Refactoring for ExpiryTimer
authorcoderhyme <jhyo.kim@samsung.com>
Mon, 3 Aug 2015 10:41:28 +0000 (19:41 +0900)
committerMadan Lanka <lanka.madan@samsung.com>
Mon, 3 Aug 2015 13:20:06 +0000 (13:20 +0000)
Its unittest is also added.

Change-Id: Iecc9c841221220330372bb11807d239b36225be1
Signed-off-by: coderhyme <jhyo.kim@samsung.com>
Reviewed-on: https://gerrit.iotivity.org/gerrit/2064
Tested-by: jenkins-iotivity <jenkins-iotivity@opendaylight.org>
Reviewed-by: Madan Lanka <lanka.madan@samsung.com>
service/resource-encapsulation/src/common/SConscript
service/resource-encapsulation/src/common/expiryTimer/include/ExpiryTimer.h
service/resource-encapsulation/src/common/expiryTimer/src/ExpiryTimer.cpp
service/resource-encapsulation/src/common/expiryTimer/src/ExpiryTimerImpl.cpp
service/resource-encapsulation/src/common/expiryTimer/src/ExpiryTimerImpl.h
service/resource-encapsulation/src/common/expiryTimer/unittests/ExpiryTimerTest.cpp [new file with mode: 0644]
service/resource-encapsulation/src/resourceBroker/src/DevicePresence.cpp
service/resource-encapsulation/src/resourceBroker/src/ResourcePresence.cpp
service/resource-encapsulation/src/resourceCache/src/DataCache.cpp

index 7720213..3997142 100644 (file)
@@ -115,7 +115,10 @@ rcs_common_test_env.PrependUnique(LIBS = [
     'pthread'
     ])
 
-rcs_common_test_src = env.Glob('primitiveResource/unittests/*.cpp')
+rcs_common_test_src = [
+    env.Glob('primitiveResource/unittests/*.cpp'),
+    'expiryTimer/unittests/ExpiryTimerTest.cpp'
+    ]
 
 rcs_common_test = rcs_common_test_env.Program('rcs_common_test', rcs_common_test_src)
 Alias("rcs_common_test", rcs_common_test)
index e2d5b3b..9eb859f 100644 (file)
 #define _EXPIRY_TIMER_H_
 
 #include <functional>
-#include <list>
+#include <unordered_map>
+#include <memory>
 
-class ExpiryTimerImpl;
-
-class ExpiryTimer
+namespace OIC
 {
-public:
-    typedef unsigned int Id;
-    typedef std::function<void(Id)> CB;
-    typedef long long DelayInMilliSec;
+    namespace Service
+    {
+
+        class TimerTask;
+
+        class ExpiryTimer
+        {
+        public:
+            typedef unsigned int Id;
+            typedef std::function< void(Id) > Callback;
+            typedef long long DelayInMilliSec;
+
+        public:
+            ExpiryTimer();
+            ~ExpiryTimer();
+
+            ExpiryTimer(ExpiryTimer&&) = default;
+            ExpiryTimer& operator=(ExpiryTimer&&) = default;
+
+            ExpiryTimer(const ExpiryTimer&) = delete;
+            ExpiryTimer& operator=(const ExpiryTimer&) = delete;
+
+            Id post(DelayInMilliSec, Callback);
+            bool cancel(Id);
+            void cancelAll();
 
-public:
-    ExpiryTimer();
-    ~ExpiryTimer();
+            size_t getNumOfPending();
+            size_t getNumOfPending() const;
 
-public:
-    Id postTimer(DelayInMilliSec, CB); // will change name to post()
-    bool cancelTimer(Id); // will change name to cancel()
-    void destroyTimer(); // This function will be removed
+        private:
+            void sweep();
 
-private:
-    void cancelAll();
+        private:
+            size_t m_nextSweep;
 
-private:
-    std::list<Id> m_timerIDList;
-    ExpiryTimerImpl* timerPtr;
-};
+            std::unordered_map< Id, std::shared_ptr< TimerTask > > m_tasks;
+        };
 
+    }
+}
 #endif //_EXPIRY_TIMER_H_
index 19694bd..a3758c6 100644 (file)
 #include "ExpiryTimer.h"
 #include "ExpiryTimerImpl.h"
 
-ExpiryTimer::ExpiryTimer()
+namespace OIC
 {
-    timerPtr = ExpiryTimerImpl::getInstance();
-}
+    namespace Service
+    {
 
-ExpiryTimer::~ExpiryTimer()
-{
-    cancelAll();
-}
+        namespace
+        {
+            constexpr size_t DEFAULT_SWEEP_SIZE{ 50 };
+        }
 
-void ExpiryTimer::cancelAll()
-{
-    for(auto id : m_timerIDList)
-    {
-        timerPtr->cancel(id);
-    }
-    m_timerIDList.clear();
-}
+        ExpiryTimer::ExpiryTimer() :
+               m_nextSweep { DEFAULT_SWEEP_SIZE }
+        {
+        }
 
-ExpiryTimer::Id ExpiryTimer::postTimer(DelayInMilliSec milliSec, CB cb)
-{
-    Id retID = timerPtr->post(milliSec, std::move(cb));
-    m_timerIDList.push_back(retID);
+        ExpiryTimer::~ExpiryTimer()
+        {
+            cancelAll();
+        }
 
-    return retID;
-}
+        ExpiryTimer::Id ExpiryTimer::post(DelayInMilliSec milliSec, Callback cb)
+        {
+            auto task = ExpiryTimerImpl::getInstance()->post(milliSec, std::move(cb));
+            m_tasks[task->getId()] = task;
 
-bool ExpiryTimer::cancelTimer(Id id)
-{
-    bool ret = timerPtr->cancel(id);
-    m_timerIDList.remove(id);
+            if (m_tasks.size() == m_nextSweep) sweep();
 
-    return ret;
-}
+            return task->getId();
+        }
 
-void ExpiryTimer::destroyTimer()
-{
+        bool ExpiryTimer::cancel(Id id)
+        {
+            auto it = m_tasks.find(id);
+
+            if (it == m_tasks.end()) return false;
+
+            auto task = it->second;
+            m_tasks.erase(it);
+
+            if (task->isExecuted()) return false;
+
+            return ExpiryTimerImpl::getInstance()->cancel(id);
+        }
+
+        void ExpiryTimer::cancelAll()
+        {
+            sweep();
+
+            std::unordered_set< std::shared_ptr< TimerTask > > set;
+
+            for(const auto& p : m_tasks)
+            {
+                set.insert(p.second);
+            }
+
+            ExpiryTimerImpl::getInstance()->cancelAll(set);
+            m_tasks.clear();
+        }
+
+        size_t ExpiryTimer::getNumOfPending()
+        {
+            sweep();
+            return m_tasks.size();
+        }
+
+        size_t ExpiryTimer::getNumOfPending() const
+        {
+            size_t ret{ 0 };
+
+            for (const auto& p : m_tasks)
+            {
+                ret += p.second->isExecuted() ? 0U : 1U;
+            }
+
+            return ret;
+        }
+
+        void ExpiryTimer::sweep()
+        {
+            for (auto it = m_tasks.begin(); it != m_tasks.end();)
+            {
+                if (it->second->isExecuted())
+                {
+                    it = m_tasks.erase(it);
+                }
+                else
+                {
+                    ++it;
+                }
+            }
+
+            m_nextSweep = m_tasks.size() << 1;
+        }
+
+    }
 }
index 73a314d..ac4b088 100644 (file)
 
 #include "ExpiryTimerImpl.h"
 
-#include <unistd.h>
-#include <cstdlib>
-#include <utility>
+#include "RCSException.h"
 
-ExpiryTimerImpl* ExpiryTimerImpl::s_instance = nullptr;
-std::once_flag* ExpiryTimerImpl::s_flag = new std::once_flag;
-
-ExpiryTimerImpl::ExpiryTimerImpl()
+namespace OIC
 {
-    m_engine = std::default_random_engine(m_device());
-    m_checkerThread = std::thread(&ExpiryTimerImpl::runChecker, this);
-}
+    namespace Service
+    {
 
-ExpiryTimerImpl::~ExpiryTimerImpl()
-{
-    m_checkerThread.join();
-}
+        namespace
+        {
+            constexpr ExpiryTimerImpl::Id INVALID_ID{ 0U };
+        }
 
-ExpiryTimerImpl* ExpiryTimerImpl::getInstance()
-{
-    std::call_once(*s_flag, [](){ s_instance = new ExpiryTimerImpl(); });
-    return s_instance;
-}
+        ExpiryTimerImpl::ExpiryTimerImpl() :
+                m_tasks{ },
+                m_thread{ std::thread(&ExpiryTimerImpl::run, this) },
+                m_mutex{ },
+                m_cond{ },
+                m_stop{ false },
+                m_mt{ std::random_device{ }() },
+                m_dist{ }
+        {
+        }
 
-ExpiryTimerImpl::Id ExpiryTimerImpl::post(DelayInMilliSec millisec, CB cb)
-{
-    Id retID = generateId();
+        ExpiryTimerImpl::~ExpiryTimerImpl()
+        {
+            {
+                std::lock_guard< std::mutex > lock{ m_mutex };
+                m_tasks.clear();
+                m_stop = true;
+            }
+            m_cond.notify_all();
+            m_thread.join();
+        }
 
-    MilliSeconds delay(millisec);
-    insertTimerCBInfo(countExpireTime(delay), cb, retID);
+        ExpiryTimerImpl* ExpiryTimerImpl::getInstance()
+        {
+            static ExpiryTimerImpl instance;
+            return &instance;
+        }
 
-    return retID;
-}
+        std::shared_ptr< TimerTask > ExpiryTimerImpl::post(DelayInMillis delay, Callback cb)
+        {
+            if (delay < 0LL)
+            {
+                throw InvalidParameterException{ "delay can't be negative." };
+            }
 
-bool ExpiryTimerImpl::cancel(Id id)
-{
-    bool ret = false;
-    std::lock_guard<std::mutex> lockf(m_mutex);
-    for(auto it: m_timerCBList)
-    {
-        if(it.second.m_id == id)
+            if (!cb)
+            {
+                throw InvalidParameterException{ "callback is empty." };
+            }
+
+            return addTask(convertToTime(Milliseconds{ delay }), std::move(cb), generateId());
+        }
+
+        bool ExpiryTimerImpl::cancel(Id id)
         {
-            if(m_timerCBList.erase(it.first)!=0)
-                ret = true;
-            else
-                ret = false;
+            if (id == INVALID_ID) return false;
+
+            std::lock_guard< std::mutex > lock{ m_mutex };
+
+            for(auto it = m_tasks.begin(); it != m_tasks.end(); ++it)
+            {
+                if(it->second->getId() == id)
+                {
+                    m_tasks.erase(it);
+                    return true;
+                }
+            }
+            return false;
         }
-    }
-    return ret;
-}
 
-void ExpiryTimerImpl::insertTimerCBInfo(ExpiredTime msec, CB cb, Id id)
-{
-    TimerCBInfo newInfo{id, cb};
-    std::lock_guard<std::mutex> lockf(m_mutex);
-    m_timerCBList.insert({msec, newInfo});
-    m_cond.notify_all();
-}
+        size_t ExpiryTimerImpl::cancelAll(
+                const std::unordered_set< std::shared_ptr<TimerTask > >& tasks)
+        {
+            std::lock_guard< std::mutex > lock{ m_mutex };
+            size_t erased { 0 };
+
+            for(auto it = m_tasks.begin(); it != m_tasks.end();)
+            {
+                if(tasks.count(it->second))
+                {
+                    it = m_tasks.erase(it);
+                    ++erased;
+                }
+                else
+                {
+                    ++it;
+                }
+            }
+            return erased;
+        }
 
-ExpiryTimerImpl::ExpiredTime ExpiryTimerImpl::countExpireTime(MilliSeconds msec)
-{
-    auto now = std::chrono::system_clock::now();
-    return std::chrono::duration_cast<MilliSeconds>(now.time_since_epoch()) + msec;
-}
+        ExpiryTimerImpl::Milliseconds ExpiryTimerImpl::convertToTime(Milliseconds delay)
+        {
+            const auto now = std::chrono::system_clock::now();
+            return std::chrono::duration_cast< Milliseconds >(now.time_since_epoch()) + delay;
+        }
 
-ExpiryTimerImpl::Id ExpiryTimerImpl::generateId()
-{
-    Id retID = m_dist(m_device);
+        std::shared_ptr< TimerTask > ExpiryTimerImpl::addTask(
+                Milliseconds delay, Callback cb, Id id)
+        {
+            std::lock_guard< std::mutex > lock{ m_mutex };
+
+            auto newTask = std::make_shared< TimerTask >(id, std::move(cb));
+            m_tasks.insert({ delay, newTask });
+            m_cond.notify_all();
+
+            return newTask;
+        }
 
-    for(auto it = m_timerCBList.begin(); it != m_timerCBList.end(); )
-     {
-       if(it->second.m_id == retID || retID == 0)
+        bool ExpiryTimerImpl::containsId(Id id) const
         {
-            retID = m_dist(m_device);
-            it = m_timerCBList.begin();
+            for (const auto& info : m_tasks)
+            {
+                if (info.second->getId() == id) return true;
+            }
+            return false;
         }
-       else
-       {
-           ++it;
-       }
-     }
 
-    return retID;
-}
+        ExpiryTimerImpl::Id ExpiryTimerImpl::generateId()
+        {
+            Id newId = m_dist(m_mt);
 
-void ExpiryTimerImpl::runChecker()
-{
-    while(true)
-    {
-        std::unique_lock<std::mutex> ul(m_mutex);
+            std::lock_guard< std::mutex > lock{ m_mutex };
+
+            while (newId == INVALID_ID || containsId(newId))
+            {
+                newId = m_dist(m_mt);
+            }
+            return newId;
+        }
 
-        if(m_timerCBList.empty())
+        void ExpiryTimerImpl::executeExpired()
         {
-            m_cond.wait(ul);
+            if (m_tasks.empty()) return;
+
+            auto now = std::chrono::system_clock::now().time_since_epoch();
+
+            auto it = m_tasks.begin();
+            for (; it != m_tasks.end() && it->first <= now; ++it)
+            {
+                it->second->execute();
+            }
+
+            m_tasks.erase(m_tasks.begin(), it);
         }
-        else
+
+        ExpiryTimerImpl::Milliseconds ExpiryTimerImpl::remainingTimeForNext() const
         {
-            ExpiredTime expireTime;
-            expireTime = m_timerCBList.begin()->first;
+            const Milliseconds& expiredTime = m_tasks.begin()->first;
 
-            auto now = std::chrono::system_clock::now();
-            MilliSeconds waitTime = expireTime - std::chrono::duration_cast<MilliSeconds>(now.time_since_epoch());
-            m_cond.wait_for(ul, waitTime);
+            return std::chrono::duration_cast< Milliseconds >(expiredTime -
+                    std::chrono::system_clock::now().time_since_epoch()) + Milliseconds{ 1 };
+        }
+
+        void ExpiryTimerImpl::run()
+        {
+            auto hasTaskOrStop = [this](){ return !m_tasks.empty() || m_stop; };
+
+            std::unique_lock< std::mutex > lock{ m_mutex };
+
+            while(!m_stop)
+            {
+                m_cond.wait(lock, hasTaskOrStop);
 
-            auto callTime = std::chrono::system_clock::now();
-            runExecutor(std::chrono::duration_cast<MilliSeconds>(callTime.time_since_epoch()));
+                if (m_stop) break;
+
+                m_cond.wait_for(lock, remainingTimeForNext());
+
+                executeExpired();
+            }
         }
-    }
-}
 
-void ExpiryTimerImpl::runExecutor(ExpiredTime expireTime)
-{
-    for(auto it = m_timerCBList.begin(); it != m_timerCBList.end(); ++it)
-    {
-        if(it->first <= expireTime)
+
+        TimerTask::TimerTask(ExpiryTimerImpl::Id id, ExpiryTimerImpl::Callback cb) :
+            m_id{ id },
+            m_callback{ std::move(cb) }
         {
-            ExecutorThread executor(it->second);
-            m_timerCBList.erase(it);
         }
-        else
+
+        void TimerTask::execute()
         {
-            break;
+            if (isExecuted()) return;
+
+            ExpiryTimerImpl::Id id { m_id };
+            m_id = INVALID_ID;
+
+            std::thread(std::move(m_callback), id).detach();
+
+            m_callback = ExpiryTimerImpl::Callback{ };
         }
-    }
-}
 
-// ExecutorThread Class
-ExpiryTimerImpl::ExecutorThread::ExecutorThread(TimerCBInfo cbInfo)
-{
-    m_executorThread = std::thread(&ExpiryTimerImpl::ExecutorThread::executorFunc, this, cbInfo);
-}
+        bool TimerTask::isExecuted() const
+        {
+            return m_id == INVALID_ID;
+        }
 
-ExpiryTimerImpl::ExecutorThread::~ExecutorThread()
-{
-    m_executorThread.detach();
-}
+        ExpiryTimerImpl::Id TimerTask::getId() const
+        {
+            return m_id;
+        }
 
-void ExpiryTimerImpl::ExecutorThread::executorFunc(TimerCBInfo cbInfo)
-{
-    cbInfo.m_cB(cbInfo.m_id);
+    }
 }
index a1cba06..084ce3c 100644 (file)
 #include <chrono>
 #include <condition_variable>
 #include <random>
+#include <unordered_set>
+#include <atomic>
 
-class ExpiryTimerImpl
+namespace OIC
 {
-public:
-    typedef unsigned int Id;
-    typedef std::function<void(Id)> CB;
+    namespace Service
+    {
+        class TimerTask;
 
-    typedef long long DelayInMilliSec;
-    typedef std::chrono::milliseconds MilliSeconds;
-    typedef std::chrono::duration<int64_t, std::milli> MilliDelayTime;
-    typedef std::chrono::duration<int64_t, std::milli> ExpiredTime;
+        class ExpiryTimerImpl
+        {
+        public:
+            typedef unsigned int Id;
+            typedef std::function< void(Id) > Callback;
 
-private:
-    struct TimerCBInfo
-    {
-        Id m_id;
-        CB m_cB;
-    };
+            typedef long long DelayInMillis;
+
+        private:
+            typedef std::chrono::milliseconds Milliseconds;
+
+        private:
+            ExpiryTimerImpl();
+            ~ExpiryTimerImpl();
+
+            ExpiryTimerImpl(const ExpiryTimerImpl&) = delete;
+            ExpiryTimerImpl& operator=(const ExpiryTimerImpl&) = delete;
+
+        public:
+            static ExpiryTimerImpl* getInstance();
+
+            std::shared_ptr< TimerTask > post(DelayInMillis, Callback);
+
+            bool cancel(Id);
+            size_t cancelAll(const std::unordered_set< std::shared_ptr<TimerTask > >&);
+
+        private:
+            static Milliseconds convertToTime(Milliseconds);
+
+            std::shared_ptr< TimerTask > addTask(Milliseconds, Callback, Id);
+
+            /**
+             * @pre The lock must be acquired with m_mutex.
+             */
+            bool containsId(Id) const;
+            Id generateId();
 
-private:
-   ExpiryTimerImpl();
-   ExpiryTimerImpl(const ExpiryTimerImpl&) = delete;
-   ExpiryTimerImpl& operator=(const ExpiryTimerImpl&) = delete;
-   ~ExpiryTimerImpl();
+            /**
+             * @pre The lock must be acquired with m_mutex.
+             */
+            void executeExpired();
 
-public:
-    static ExpiryTimerImpl* getInstance();
-    void destroyInstance();
+            /**
+             * @pre The lock must be acquired with m_mutex.
+             */
+            Milliseconds remainingTimeForNext() const;
 
-    Id post(DelayInMilliSec, CB);
-    bool cancel(Id);
+            void run();
 
-private:
-   Id generateId();
+        private:
+            std::multimap< Milliseconds, std::shared_ptr< TimerTask > > m_tasks;
 
-   void insertTimerCBInfo(ExpiredTime, CB ,Id);
-   ExpiredTime countExpireTime(MilliSeconds);
+            std::thread m_thread;
+            std::mutex m_mutex;
+            std::condition_variable m_cond;
+            bool m_stop;
 
-   void runChecker();
+            std::mt19937 m_mt;
+            std::uniform_int_distribution< Id > m_dist;
 
-   void runExecutor(ExpiredTime);
+        };
 
-private:
-   static ExpiryTimerImpl* s_instance;
-   static std::once_flag* s_flag;
+        class TimerTask
+        {
+        public:
+            TimerTask(ExpiryTimerImpl::Id, ExpiryTimerImpl::Callback);
 
-   std::multimap<ExpiredTime, TimerCBInfo> m_timerCBList;
+            TimerTask(const TimerTask&) = delete;
+            TimerTask(TimerTask&&) = delete;
 
-   std::thread m_checkerThread;
-   std::mutex m_mutex;
-   std::condition_variable m_cond;
+            TimerTask& operator=(const TimerTask&) = delete;
+            TimerTask& operator=(TimerTask&&) = delete;
 
-   std::random_device m_device;
-   std::default_random_engine m_engine;
-   std::uniform_int_distribution<Id> m_dist;
+            bool isExecuted() const;
+            ExpiryTimerImpl::Id getId() const;
 
-public:
-   class ExecutorThread
-   {
-   public:
-       ExecutorThread(TimerCBInfo);
-       ~ExecutorThread();
+        private:
+            void execute();
 
-   public:
-       void executorFunc(TimerCBInfo);
+        private:
+            std::atomic< ExpiryTimerImpl::Id > m_id;
+            ExpiryTimerImpl::Callback m_callback;
 
-   private:
-       std::thread m_executorThread;
-   };
-};
+            friend class ExpiryTimerImpl;
+        };
 
+    }
+}
 #endif //_EXPIRY_TIMER_IMPL_H_
diff --git a/service/resource-encapsulation/src/common/expiryTimer/unittests/ExpiryTimerTest.cpp b/service/resource-encapsulation/src/common/expiryTimer/unittests/ExpiryTimerTest.cpp
new file mode 100644 (file)
index 0000000..bf96c77
--- /dev/null
@@ -0,0 +1,332 @@
+//******************************************************************
+//
+// Copyright 2015 Samsung Electronics All Rights Reserved.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+
+#include <UnitTestHelper.h>
+
+#include <mutex>
+#include <atomic>
+
+#include "RCSException.h"
+#include "ExpiryTimer.h"
+#include "ExpiryTimerImpl.h"
+
+using namespace OIC::Service;
+
+constexpr int TOLERANCE_IN_MILLIS{ 50 };
+
+class FunctionObject
+{
+public:
+    virtual ~FunctionObject() { }
+
+    virtual void execute(ExpiryTimerImpl::Id) { }
+};
+
+class ExpiryTimerImplTest: public TestWithMock
+{
+public:
+    void Proceed()
+    {
+        cond.notify_all();
+    }
+
+    void Wait(int waitingTime = TOLERANCE_IN_MILLIS)
+    {
+        std::unique_lock< std::mutex > lock{ mutex };
+        cond.wait_for(lock, std::chrono::milliseconds{ waitingTime });
+    }
+
+private:
+    std::condition_variable cond;
+    std::mutex mutex;
+};
+
+
+TEST_F(ExpiryTimerImplTest, PostThrowsIfDelayIsNegative)
+{
+    ASSERT_THROW(ExpiryTimerImpl::getInstance()->post(-1, [](ExpiryTimerImpl::Id){}), RCSException);
+}
+
+TEST_F(ExpiryTimerImplTest, PostThrowsIfCallbackIsEmpty)
+{
+    ASSERT_THROW(ExpiryTimerImpl::getInstance()->post(1, { }), RCSException);
+}
+
+TEST_F(ExpiryTimerImplTest, CallbackBeInvokedWithinTolerance)
+{
+    FunctionObject* functor = mocks.Mock< FunctionObject >();
+
+    mocks.ExpectCall(functor, FunctionObject::execute).Do(
+            [this](ExpiryTimerImpl::Id){
+                Proceed();
+            }
+    );
+
+    ExpiryTimerImpl::getInstance()->post(10,
+            std::bind(&FunctionObject::execute, functor, std::placeholders::_1));
+
+    Wait();
+}
+
+TEST_F(ExpiryTimerImplTest, CallbackBeInvokedWithTimerId)
+{
+    ExpiryTimerImpl::Id returnedId;
+    FunctionObject* functor = mocks.Mock< FunctionObject >();
+
+    mocks.ExpectCall(functor, FunctionObject::execute).Match(
+            [this, &returnedId](ExpiryTimerImpl::Id id){
+                return returnedId == id;
+            }
+    ).Do(
+            [this](ExpiryTimerImpl::Id){
+                Proceed();
+            }
+    );
+
+    returnedId = ExpiryTimerImpl::getInstance()->post(1,
+            std::bind(&FunctionObject::execute, functor, std::placeholders::_1))->getId();
+
+    Wait();
+}
+
+TEST_F(ExpiryTimerImplTest, CanceledTaskBeNotCalled)
+{
+    FunctionObject* functor = mocks.Mock< FunctionObject >();
+
+    mocks.NeverCall(functor, FunctionObject::execute);
+
+    ExpiryTimerImpl::Id id = ExpiryTimerImpl::getInstance()->post(10,
+            std::bind(&FunctionObject::execute, functor, std::placeholders::_1))->getId();
+    ExpiryTimerImpl::getInstance()->cancel(id);
+    Wait(100);
+}
+
+TEST_F(ExpiryTimerImplTest, CancelReturnsTrueIfCanceledCorrectly)
+{
+    FunctionObject* functor = mocks.Mock< FunctionObject >();
+
+    ExpiryTimerImpl::Id id = ExpiryTimerImpl::getInstance()->post(10,
+            std::bind(&FunctionObject::execute, functor, std::placeholders::_1))->getId();
+
+    ASSERT_TRUE(ExpiryTimerImpl::getInstance()->cancel(id));
+}
+
+TEST_F(ExpiryTimerImplTest, CancelReturnsFalseIfAlreadyExecuted)
+{
+    FunctionObject* functor = mocks.Mock< FunctionObject >();
+
+    mocks.ExpectCall(functor, FunctionObject::execute).Do(
+        [this](ExpiryTimerImpl::Id){
+            Proceed();
+        }
+    );
+
+    ExpiryTimerImpl::Id id = ExpiryTimerImpl::getInstance()->post(1,
+            std::bind(&FunctionObject::execute, functor, std::placeholders::_1))->getId();
+    Wait();
+
+    ASSERT_FALSE(ExpiryTimerImpl::getInstance()->cancel(id));
+}
+
+TEST_F(ExpiryTimerImplTest, CallbackBeInvokedWithinToleranceWithMultiplePost)
+{
+    constexpr int NUM_OF_POST{ 10000 };
+    std::atomic_int called{ 0 };
+
+    for (int i=0; i<NUM_OF_POST; ++i)
+    {
+        FunctionObject* functor = mocks.Mock< FunctionObject >();
+        mocks.OnCall(functor, FunctionObject::execute).Do(
+                [&called](ExpiryTimerImpl::Id)
+                {
+                    ++called;
+                }
+        );
+
+        ExpiryTimerImpl::getInstance()->post(rand() % 20 + 5,
+                std::bind(&FunctionObject::execute, functor, std::placeholders::_1));
+    }
+
+    Wait(TOLERANCE_IN_MILLIS + 25);
+
+    ASSERT_EQ(NUM_OF_POST, called);
+}
+
+class ExpiryTimerTest: public TestWithMock
+{
+public:
+    ExpiryTimer timer;
+
+public:
+    void Proceed()
+    {
+        cond.notify_all();
+    }
+
+    void Wait(int waitingTime = TOLERANCE_IN_MILLIS)
+    {
+        std::unique_lock< std::mutex > lock{ mutex };
+        cond.wait_for(lock, std::chrono::milliseconds{ waitingTime });
+    }
+
+private:
+    std::condition_variable cond;
+    std::mutex mutex;
+};
+
+TEST_F(ExpiryTimerTest, PostThrowsIfDelayIsNegative)
+{
+    ASSERT_THROW(timer.post(-1, [](ExpiryTimer::Id){}), RCSException);
+}
+
+TEST_F(ExpiryTimerTest, PostThrowsIfCallbackIsEmpty)
+{
+    ASSERT_THROW(timer.post(1, { }), RCSException);
+}
+
+TEST_F(ExpiryTimerTest, CallbackBeInvokedWithinTolerance)
+{
+    FunctionObject* functor = mocks.Mock< FunctionObject >();
+
+    mocks.ExpectCall(functor, FunctionObject::execute).Do(
+            [this](ExpiryTimer::Id){
+                Proceed();
+            }
+    );
+
+    timer.post(10,
+            std::bind(&FunctionObject::execute, functor, std::placeholders::_1));
+
+    Wait();
+}
+
+TEST_F(ExpiryTimerTest, CallbackBeInvokedWithTimerId)
+{
+    ExpiryTimer::Id returnedId;
+    FunctionObject* functor = mocks.Mock< FunctionObject >();
+
+    mocks.ExpectCall(functor, FunctionObject::execute).Match(
+            [this, &returnedId](ExpiryTimer::Id id){
+                return returnedId == id;
+            }
+    ).Do(
+            [this](ExpiryTimer::Id){
+                Proceed();
+            }
+    );
+
+    returnedId = timer.post(1, std::bind(&FunctionObject::execute, functor, std::placeholders::_1));
+
+    Wait();
+}
+
+TEST_F(ExpiryTimerTest, CanceledTaskBeNotCalled)
+{
+    FunctionObject* functor = mocks.Mock< FunctionObject >();
+
+    mocks.NeverCall(functor, FunctionObject::execute);
+
+    auto id = timer.post(10, std::bind(&FunctionObject::execute, functor, std::placeholders::_1));
+    timer.cancel(id);
+    Wait(100);
+}
+
+TEST_F(ExpiryTimerTest, CancelReturnsTrueIfCanceledCorrectly)
+{
+    FunctionObject* functor = mocks.Mock< FunctionObject >();
+
+    auto id = timer.post(10, std::bind(&FunctionObject::execute, functor, std::placeholders::_1));
+
+    ASSERT_TRUE(timer.cancel(id));
+}
+
+TEST_F(ExpiryTimerTest, CancelReturnsFalseIfAlreadyExecuted)
+{
+    FunctionObject* functor = mocks.Mock< FunctionObject >();
+
+    mocks.ExpectCall(functor, FunctionObject::execute).Do(
+        [this](ExpiryTimer::Id){
+            Proceed();
+        }
+    );
+
+    auto id = timer.post(1, std::bind(&FunctionObject::execute, functor, std::placeholders::_1));
+    Wait();
+
+    ASSERT_FALSE(timer.cancel(id));
+}
+
+TEST_F(ExpiryTimerTest, NumOfPendingReturnsNumberOfNotExecuted)
+{
+    constexpr size_t numOfFutureTask{ 100 };
+    constexpr size_t numOfShortDelayTask{ 100 };
+
+    for (size_t i=0; i<numOfFutureTask; ++i)
+    {
+        FunctionObject* functor = mocks.Mock< FunctionObject >();
+        mocks.OnCall(functor, FunctionObject::execute);
+
+        timer.post(1000, std::bind(&FunctionObject::execute, functor, std::placeholders::_1));
+    }
+
+    for (size_t i=0; i<numOfShortDelayTask; ++i)
+     {
+         FunctionObject* functor = mocks.Mock< FunctionObject >();
+         mocks.OnCall(functor, FunctionObject::execute);
+
+         timer.post(i, std::bind(&FunctionObject::execute, functor, std::placeholders::_1));
+     }
+
+    Wait(numOfShortDelayTask + TOLERANCE_IN_MILLIS);
+
+    ASSERT_EQ(timer.getNumOfPending(), numOfFutureTask);
+}
+
+TEST_F(ExpiryTimerTest, CancelAllCancelsAllTasks)
+{
+    constexpr size_t numOfTask{ 100 };
+
+    for (size_t i=0; i<numOfTask; ++i)
+    {
+        FunctionObject* functor = mocks.Mock< FunctionObject >();
+        mocks.NeverCall(functor, FunctionObject::execute);
+
+        timer.post(50 + i, std::bind(&FunctionObject::execute, functor, std::placeholders::_1));
+    }
+
+    timer.cancelAll();
+
+    Wait(200);
+}
+
+TEST_F(ExpiryTimerTest, AllTasksAreCancelledAfterTimerDestroyed)
+{
+    {
+        ExpiryTimer localTimer;
+        FunctionObject* functor = mocks.Mock< FunctionObject >();
+
+        mocks.NeverCall(functor, FunctionObject::execute);
+
+        localTimer.post(50,
+                std::bind(&FunctionObject::execute, functor, std::placeholders::_1));
+    }
+
+    Wait(200);
+}
index 04f7546..6374621 100644 (file)
@@ -46,7 +46,6 @@ namespace OIC
             }
             resourcePresenceList.clear();
             OC_LOG_V(DEBUG,BROKER_TAG,"destroy Timer.");
-            presenceTimer.destroyTimer();
         }
 
         void DevicePresence::initializeDevicePresence(PrimitiveResourcePtr pResource)
@@ -68,7 +67,7 @@ namespace OIC
                 throw;
             }
             presenceTimerHandle
-            = presenceTimer.postTimer(BROKER_DEVICE_PRESENCE_TIMEROUT, pTimeoutCB);
+            = presenceTimer.post(BROKER_DEVICE_PRESENCE_TIMEROUT, pTimeoutCB);
         }
         DEVICE_STATE DevicePresence::getDeviceState() const
         {
@@ -122,7 +121,7 @@ namespace OIC
                 std::unique_lock<std::mutex> lock(timeoutMutex);
                 condition.wait(lock);
             }
-            presenceTimer.cancelTimer(presenceTimerHandle);
+            presenceTimer.cancel(presenceTimerHandle);
 
             switch(ret)
             {
@@ -136,7 +135,7 @@ namespace OIC
                             (int)(state.load(boost::memory_order_consume)));
                     changeAllPresenceMode(BROKER_MODE::DEVICE_PRESENCE_MODE);
                     presenceTimerHandle
-                    = presenceTimer.postTimer(BROKER_DEVICE_PRESENCE_TIMEROUT, pTimeoutCB);
+                    = presenceTimer.post(BROKER_DEVICE_PRESENCE_TIMEROUT, pTimeoutCB);
                     break;
                 }
                 case OC_STACK_INVALID_REQUEST_HANDLE:
index 04a13fa..257a577 100644 (file)
@@ -83,7 +83,7 @@ namespace OIC
             = std::unique_ptr<std::list<BrokerRequesterInfoPtr>>
             (new std::list<BrokerRequesterInfoPtr>);
 
-            timeoutHandle = expiryTimer.postTimer(BROKER_SAFE_MILLISECOND, pTimeoutCB);
+            timeoutHandle = expiryTimer.post(BROKER_SAFE_MILLISECOND, pTimeoutCB);
             OC_LOG_V(DEBUG,BROKER_TAG,"initializeResourcePresence::requestGet.\n");
             primitiveResource->requestGet(pGetCB);
 
@@ -238,7 +238,7 @@ namespace OIC
             if(this->requesterList->size() != 0)
             {
                 this->requestResourceState();
-                timeoutHandle = expiryTimer.postTimer(BROKER_SAFE_MILLISECOND,pTimeoutCB);
+                timeoutHandle = expiryTimer.post(BROKER_SAFE_MILLISECOND,pTimeoutCB);
             }
         }
 
@@ -257,13 +257,13 @@ namespace OIC
 
             if(isWithinTime)
             {
-                expiryTimer.cancelTimer(timeoutHandle);
+                expiryTimer.cancel(timeoutHandle);
                 isWithinTime = true;
             }
 
             if(mode == BROKER_MODE::NON_PRESENCE_MODE)
             {
-                expiryTimer.postTimer(BROKER_SAFE_MILLISECOND,pPollingCB);
+                expiryTimer.post(BROKER_SAFE_MILLISECOND,pPollingCB);
             }
 
         }
@@ -314,10 +314,10 @@ namespace OIC
             OC_LOG_V(DEBUG, BROKER_TAG, "changePresenceMode()\n");
             if(newMode != mode)
             {
-                expiryTimer.cancelTimer(timeoutHandle);
+                expiryTimer.cancel(timeoutHandle);
                 if(newMode == BROKER_MODE::NON_PRESENCE_MODE)
                 {
-                    timeoutHandle = expiryTimer.postTimer(BROKER_SAFE_MILLISECOND,pTimeoutCB);
+                    timeoutHandle = expiryTimer.post(BROKER_SAFE_MILLISECOND,pTimeoutCB);
                     requestResourceState();
                 }
                 mode = newMode;
index b0bc45f..e21973c 100644 (file)
@@ -126,7 +126,7 @@ namespace OIC
             {
                 sResource->requestObserve(pObserveCB);
             }
-            networkTimeOutHandle = networkTimer.postTimer(CACHE_DEFAULT_EXPIRED_MILLITIME, pTimerCB);
+            networkTimeOutHandle = networkTimer.post(CACHE_DEFAULT_EXPIRED_MILLITIME, pTimerCB);
         }
 
         CacheID DataCache::addSubscriber(CacheCB func, REPORT_FREQUENCY rf, long repeatTime)
@@ -226,8 +226,8 @@ namespace OIC
                 mode = CACHE_MODE::OBSERVE;
             }
 
-            networkTimer.cancelTimer(networkTimeOutHandle);
-            networkTimeOutHandle = networkTimer.postTimer(CACHE_DEFAULT_EXPIRED_MILLITIME, pTimerCB);
+            networkTimer.cancel(networkTimeOutHandle);
+            networkTimeOutHandle = networkTimer.post(CACHE_DEFAULT_EXPIRED_MILLITIME, pTimerCB);
 
             notifyObservers(_rep.getAttributes());
         }
@@ -248,11 +248,11 @@ namespace OIC
 
             if (mode != CACHE_MODE::OBSERVE)
             {
-                networkTimer.cancelTimer(networkTimeOutHandle);
-                networkTimeOutHandle = networkTimer.postTimer(
+                networkTimer.cancel(networkTimeOutHandle);
+                networkTimeOutHandle = networkTimer.post(
                                            CACHE_DEFAULT_EXPIRED_MILLITIME, pTimerCB);
 
-                pollingHandle = pollingTimer.postTimer(CACHE_DEFAULT_REPORT_MILLITIME, pPollingCB);
+                pollingHandle = pollingTimer.post(CACHE_DEFAULT_REPORT_MILLITIME, pPollingCB);
             }
 
             notifyObservers(_rep.getAttributes());
@@ -291,11 +291,11 @@ namespace OIC
                 sResource->cancelObserve();
                 mode = CACHE_MODE::FREQUENCY;
 
-                networkTimer.cancelTimer(networkTimeOutHandle);
-                networkTimeOutHandle = networkTimer.postTimer(
+                networkTimer.cancel(networkTimeOutHandle);
+                networkTimeOutHandle = networkTimer.post(
                                            CACHE_DEFAULT_EXPIRED_MILLITIME, pTimerCB);
 
-                pollingHandle = pollingTimer.postTimer(CACHE_DEFAULT_REPORT_MILLITIME, pPollingCB);
+                pollingHandle = pollingTimer.post(CACHE_DEFAULT_REPORT_MILLITIME, pPollingCB);
                 return;
             }