From: coderhyme Date: Mon, 3 Aug 2015 10:41:28 +0000 (+0900) Subject: Refactoring for ExpiryTimer X-Git-Tag: 1.2.0+RC1~1284 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=708bb6a49a2186c0ef217cae760018d5a3157075;p=platform%2Fupstream%2Fiotivity.git Refactoring for ExpiryTimer Its unittest is also added. Change-Id: Iecc9c841221220330372bb11807d239b36225be1 Signed-off-by: coderhyme Reviewed-on: https://gerrit.iotivity.org/gerrit/2064 Tested-by: jenkins-iotivity Reviewed-by: Madan Lanka --- diff --git a/service/resource-encapsulation/src/common/SConscript b/service/resource-encapsulation/src/common/SConscript index 7720213..3997142 100644 --- a/service/resource-encapsulation/src/common/SConscript +++ b/service/resource-encapsulation/src/common/SConscript @@ -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) diff --git a/service/resource-encapsulation/src/common/expiryTimer/include/ExpiryTimer.h b/service/resource-encapsulation/src/common/expiryTimer/include/ExpiryTimer.h index e2d5b3b..9eb859f 100644 --- a/service/resource-encapsulation/src/common/expiryTimer/include/ExpiryTimer.h +++ b/service/resource-encapsulation/src/common/expiryTimer/include/ExpiryTimer.h @@ -22,32 +22,49 @@ #define _EXPIRY_TIMER_H_ #include -#include +#include +#include -class ExpiryTimerImpl; - -class ExpiryTimer +namespace OIC { -public: - typedef unsigned int Id; - typedef std::function 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 m_timerIDList; - ExpiryTimerImpl* timerPtr; -}; + std::unordered_map< Id, std::shared_ptr< TimerTask > > m_tasks; + }; + } +} #endif //_EXPIRY_TIMER_H_ diff --git a/service/resource-encapsulation/src/common/expiryTimer/src/ExpiryTimer.cpp b/service/resource-encapsulation/src/common/expiryTimer/src/ExpiryTimer.cpp index 19694bd..a3758c6 100644 --- a/service/resource-encapsulation/src/common/expiryTimer/src/ExpiryTimer.cpp +++ b/service/resource-encapsulation/src/common/expiryTimer/src/ExpiryTimer.cpp @@ -21,41 +21,99 @@ #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; + } + + } } diff --git a/service/resource-encapsulation/src/common/expiryTimer/src/ExpiryTimerImpl.cpp b/service/resource-encapsulation/src/common/expiryTimer/src/ExpiryTimerImpl.cpp index 73a314d..ac4b088 100644 --- a/service/resource-encapsulation/src/common/expiryTimer/src/ExpiryTimerImpl.cpp +++ b/service/resource-encapsulation/src/common/expiryTimer/src/ExpiryTimerImpl.cpp @@ -20,144 +20,208 @@ #include "ExpiryTimerImpl.h" -#include -#include -#include +#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 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 lockf(m_mutex); - m_timerCBList.insert({msec, newInfo}); - m_cond.notify_all(); -} + size_t ExpiryTimerImpl::cancelAll( + const std::unordered_set< std::shared_ptr >& 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(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 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(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(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); + } } diff --git a/service/resource-encapsulation/src/common/expiryTimer/src/ExpiryTimerImpl.h b/service/resource-encapsulation/src/common/expiryTimer/src/ExpiryTimerImpl.h index a1cba06..084ce3c 100644 --- a/service/resource-encapsulation/src/common/expiryTimer/src/ExpiryTimerImpl.h +++ b/service/resource-encapsulation/src/common/expiryTimer/src/ExpiryTimerImpl.h @@ -28,75 +28,101 @@ #include #include #include +#include +#include -class ExpiryTimerImpl +namespace OIC { -public: - typedef unsigned int Id; - typedef std::function CB; + namespace Service + { + class TimerTask; - typedef long long DelayInMilliSec; - typedef std::chrono::milliseconds MilliSeconds; - typedef std::chrono::duration MilliDelayTime; - typedef std::chrono::duration 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 >&); + + 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 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 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 index 0000000..bf96c77 --- /dev/null +++ b/service/resource-encapsulation/src/common/expiryTimer/unittests/ExpiryTimerTest.cpp @@ -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 + +#include +#include + +#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(); + 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(); + mocks.OnCall(functor, FunctionObject::execute); + + timer.post(1000, std::bind(&FunctionObject::execute, functor, std::placeholders::_1)); + } + + for (size_t i=0; i(); + 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(); + 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); +} diff --git a/service/resource-encapsulation/src/resourceBroker/src/DevicePresence.cpp b/service/resource-encapsulation/src/resourceBroker/src/DevicePresence.cpp index 04f7546..6374621 100644 --- a/service/resource-encapsulation/src/resourceBroker/src/DevicePresence.cpp +++ b/service/resource-encapsulation/src/resourceBroker/src/DevicePresence.cpp @@ -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 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: diff --git a/service/resource-encapsulation/src/resourceBroker/src/ResourcePresence.cpp b/service/resource-encapsulation/src/resourceBroker/src/ResourcePresence.cpp index 04a13fa..257a577 100644 --- a/service/resource-encapsulation/src/resourceBroker/src/ResourcePresence.cpp +++ b/service/resource-encapsulation/src/resourceBroker/src/ResourcePresence.cpp @@ -83,7 +83,7 @@ namespace OIC = std::unique_ptr> (new std::list); - 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; diff --git a/service/resource-encapsulation/src/resourceCache/src/DataCache.cpp b/service/resource-encapsulation/src/resourceCache/src/DataCache.cpp index b0bc45f..e21973c 100644 --- a/service/resource-encapsulation/src/resourceCache/src/DataCache.cpp +++ b/service/resource-encapsulation/src/resourceCache/src/DataCache.cpp @@ -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; }