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>
'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)
#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_
#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;
+ }
+
+ }
}
#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);
+ }
}
#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_
--- /dev/null
+//******************************************************************
+//
+// 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);
+}
}
resourcePresenceList.clear();
OC_LOG_V(DEBUG,BROKER_TAG,"destroy Timer.");
- presenceTimer.destroyTimer();
}
void DevicePresence::initializeDevicePresence(PrimitiveResourcePtr pResource)
throw;
}
presenceTimerHandle
- = presenceTimer.postTimer(BROKER_DEVICE_PRESENCE_TIMEROUT, pTimeoutCB);
+ = presenceTimer.post(BROKER_DEVICE_PRESENCE_TIMEROUT, pTimeoutCB);
}
DEVICE_STATE DevicePresence::getDeviceState() const
{
std::unique_lock<std::mutex> lock(timeoutMutex);
condition.wait(lock);
}
- presenceTimer.cancelTimer(presenceTimerHandle);
+ presenceTimer.cancel(presenceTimerHandle);
switch(ret)
{
(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:
= 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);
if(this->requesterList->size() != 0)
{
this->requestResourceState();
- timeoutHandle = expiryTimer.postTimer(BROKER_SAFE_MILLISECOND,pTimeoutCB);
+ timeoutHandle = expiryTimer.post(BROKER_SAFE_MILLISECOND,pTimeoutCB);
}
}
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);
}
}
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;
{
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)
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());
}
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());
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;
}