LocTimer.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648
  1. /* Copyright (c) 2015, 2020-2021 The Linux Foundation. All rights reserved.
  2. *
  3. * Redistribution and use in source and binary forms, with or without
  4. * modification, are permitted provided that the following conditions are
  5. * met:
  6. * * Redistributions of source code must retain the above copyright
  7. * notice, this list of conditions and the following disclaimer.
  8. * * Redistributions in binary form must reproduce the above
  9. * copyright notice, this list of conditions and the following
  10. * disclaimer in the documentation and/or other materials provided
  11. * with the distribution.
  12. * * Neither the name of The Linux Foundation, nor the names of its
  13. * contributors may be used to endorse or promote products derived
  14. * from this software without specific prior written permission.
  15. *
  16. * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
  17. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  18. * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
  19. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
  20. * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  21. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  22. * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
  23. * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  24. * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
  25. * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
  26. * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  27. *
  28. */
  29. #include <unistd.h>
  30. #include <stdio.h>
  31. #include <stdlib.h>
  32. #include <time.h>
  33. #include <errno.h>
  34. #include <sys/timerfd.h>
  35. #include <sys/epoll.h>
  36. #include <log_util.h>
  37. #include <loc_timer.h>
  38. #include <LocTimer.h>
  39. #include <LocHeap.h>
  40. #include <LocThread.h>
  41. #include <LocSharedLock.h>
  42. #include <MsgTask.h>
  43. #ifdef __HOST_UNIT_TEST__
  44. #define EPOLLWAKEUP 0
  45. #define CLOCK_BOOTTIME CLOCK_MONOTONIC
  46. #define CLOCK_BOOTTIME_ALARM CLOCK_MONOTONIC
  47. #endif
  48. namespace loc_util {
  49. /*
  50. There are implementations of 5 classes in this file:
  51. LocTimer, LocTimerDelegate, LocTimerContainer, LocTimerPollTask, LocTimerWrapper
  52. LocTimer - client front end, interface for client to start / stop timers, also
  53. to provide a callback.
  54. LocTimerDelegate - an internal timer entity, which also is a LocRankable obj.
  55. Its life cycle is different than that of LocTimer. It gets
  56. created when LocTimer::start() is called, and gets deleted
  57. when it expires or clients calls the hosting LocTimer obj's
  58. stop() method. When a LocTimerDelegate obj is ticking, it
  59. stays in the corresponding LocTimerContainer. When expired
  60. or stopped, the obj is removed from the container. Since it
  61. is also a LocRankable obj, and LocTimerContainer also is a
  62. heap, its ranks() implementation decides where it is placed
  63. in the heap.
  64. LocTimerContainer - core of the timer service. It is a container (derived from
  65. LocHeap) for LocTimerDelegate (implements LocRankable) objs.
  66. There are 2 of such containers, one for sw timers (or Linux
  67. timers) one for hw timers (or Linux alarms). It adds one of
  68. each (those that expire the soonest) to kernel via services
  69. provided by LocTimerPollTask. All the heap management on the
  70. LocTimerDelegate objs are done in the MsgTask context, such
  71. that synchronization is ensured.
  72. LocTimerPollTask - is a class that wraps timerfd and epoll POXIS APIs. It also
  73. both implements LocRunnalbe with epoll_wait() in the run()
  74. method. It is also a LocThread client, so as to loop the run
  75. method.
  76. LocTimerWrapper - a LocTimer client itself, to implement the existing C API with
  77. APIs, loc_timer_start() and loc_timer_stop().
  78. */
  79. class LocTimerPollTask;
  80. // This is a multi-functaional class that:
  81. // * extends the LocHeap class for the detection of head update upon add / remove
  82. // events. When that happens, soonest time out changes, so timerfd needs update.
  83. // * contains the timers, and add / remove them into the heap
  84. // * provides and maps 2 of such containers, one for timers (or mSwTimers), one
  85. // for alarms (or mHwTimers);
  86. // * provides a polling thread;
  87. // * provides a MsgTask thread for synchronized add / remove / timer client callback.
  88. class LocTimerContainer : public LocHeap {
  89. // mutex to synchronize getters of static members
  90. static pthread_mutex_t mMutex;
  91. // Container of timers
  92. static LocTimerContainer* mSwTimers;
  93. // Container of alarms
  94. static LocTimerContainer* mHwTimers;
  95. // Msg task to provider msg Q, sender and reader.
  96. static MsgTask* mMsgTask;
  97. // Poll task to provide epoll call and threading to poll.
  98. static LocTimerPollTask* mPollTask;
  99. // timer / alarm fd
  100. int mDevFd;
  101. // ctor
  102. LocTimerContainer(bool wakeOnExpire);
  103. // dtor
  104. ~LocTimerContainer();
  105. static MsgTask* getMsgTaskLocked();
  106. static LocTimerPollTask* getPollTaskLocked();
  107. // extend LocHeap and pop if the top outRanks input
  108. LocTimerDelegate* popIfOutRanks(LocTimerDelegate& timer);
  109. // update the timer POSIX calls with updated soonest timer spec
  110. void updateSoonestTime(LocTimerDelegate* priorTop);
  111. public:
  112. // factory method to control the creation of mSwTimers / mHwTimers
  113. static LocTimerContainer* get(bool wakeOnExpire);
  114. LocTimerDelegate* getSoonestTimer();
  115. int getTimerFd();
  116. // add a timer / alarm obj into the container
  117. void add(LocTimerDelegate& timer);
  118. // remove a timer / alarm obj from the container
  119. void remove(LocTimerDelegate& timer);
  120. // handling of timer / alarm expiration
  121. void expire();
  122. };
  123. class TimerRunnable : public LocRunnable {
  124. const int mFd;
  125. public:
  126. inline TimerRunnable(const int fd) : mFd(fd) {}
  127. // The method to be implemented by thread clients
  128. // and be scheduled by LocThread
  129. // This method will be repeated called until it returns false; or
  130. // until thread is stopped.
  131. virtual bool run() override;
  132. // The method to wake up the potential blocking thread
  133. // no op if not applicable
  134. inline virtual void interrupt() { close(mFd); }
  135. };
  136. // This class implements the polling thread that epolls imer / alarm fds.
  137. // The LocRunnable::run() contains the actual polling. The other methods
  138. // will be run in the caller's thread context to add / remove timer / alarm
  139. // fds the kernel, while the polling is blocked on epoll_wait() call.
  140. // Since the design is that we have maximally 2 polls, one for all the
  141. // timers; one for all the alarms, we will poll at most on 2 fds. But it
  142. // is possile that all we have are only timers or alarms at one time, so we
  143. // allow dynamically add / remove fds we poll on. The design decision of
  144. // having 1 fd per container of timer / alarm is such that, we may not need
  145. // to make a system call each time a timer / alarm is added / removed, unless
  146. // that changes the "soonest" time out of that of all the timers / alarms.
  147. class LocTimerPollTask {
  148. // the epoll fd
  149. const int mFd;
  150. // the thread that calls TimerRunnable::run() method, where
  151. // epoll_wait() is blocking and waiting for events..
  152. LocThread mThread;
  153. public:
  154. // ctor
  155. LocTimerPollTask();
  156. // dtor
  157. ~LocTimerPollTask() = default;
  158. // add a container of timers. Each contain has a unique device fd, i.e.
  159. // either timer or alarm fd, and a heap of timers / alarms. It is expected
  160. // that container would have written to the device fd with the soonest
  161. // time out value in the heap at the time of calling this method. So all
  162. // this method does is to add the fd of the input container to the poll
  163. // and also add the pointer of the container to the event data ptr, such
  164. // when poll_wait wakes up on events, we know who is the owner of the fd.
  165. void addPoll(LocTimerContainer& timerContainer);
  166. // remove a fd that is assciated with a container. The expectation is that
  167. // the atual timer would have been removed from the container.
  168. void removePoll(LocTimerContainer& timerContainer);
  169. };
  170. // Internal class of timer obj. It gets born when client calls LocTimer::start();
  171. // and gets deleted when client calls LocTimer::stop() or when the it expire()'s.
  172. // This class implements LocRankable::ranks() so that when an obj is added into
  173. // the container (of LocHeap), it gets placed in sorted order.
  174. class LocTimerDelegate : public LocRankable {
  175. friend class LocTimerContainer;
  176. friend class LocTimer;
  177. LocTimer* mClient;
  178. LocSharedLock* mLock;
  179. struct timespec mFutureTime;
  180. LocTimerContainer* mContainer;
  181. // not a complete obj, just ctor for LocRankable comparisons
  182. inline LocTimerDelegate(struct timespec& delay)
  183. : mClient(NULL), mLock(NULL), mFutureTime(delay), mContainer(NULL) {}
  184. inline ~LocTimerDelegate() { if (mLock) { mLock->drop(); mLock = NULL; } }
  185. public:
  186. LocTimerDelegate(LocTimer& client, struct timespec& futureTime, LocTimerContainer* container);
  187. void destroyLocked();
  188. // LocRankable virtual method
  189. virtual int ranks(LocRankable& rankable);
  190. void expire();
  191. inline struct timespec getFutureTime() { return mFutureTime; }
  192. };
  193. /***************************LocTimerContainer methods***************************/
  194. // Most of these static recources are created on demand. They however are never
  195. // destoyed. The theory is that there are processes that link to this util lib
  196. // but never use timer, then these resources would never need to be created.
  197. // For those processes that do use timer, it will likely also need to every
  198. // once in a while. It might be cheaper keeping them around.
  199. pthread_mutex_t LocTimerContainer::mMutex = PTHREAD_MUTEX_INITIALIZER;
  200. LocTimerContainer* LocTimerContainer::mSwTimers = NULL;
  201. LocTimerContainer* LocTimerContainer::mHwTimers = NULL;
  202. MsgTask* LocTimerContainer::mMsgTask = NULL;
  203. LocTimerPollTask* LocTimerContainer::mPollTask = NULL;
  204. // ctor - initialize timer heaps
  205. // A container for swTimer (timer) is created, when wakeOnExpire is true; or
  206. // HwTimer (alarm), when wakeOnExpire is false.
  207. LocTimerContainer::LocTimerContainer(bool wakeOnExpire) :
  208. mDevFd(timerfd_create(wakeOnExpire ? CLOCK_BOOTTIME_ALARM : CLOCK_BOOTTIME, 0)) {
  209. if ((-1 == mDevFd) && (errno == EINVAL)) {
  210. LOC_LOGW("%s: timerfd_create failure, fallback to CLOCK_MONOTONIC - %s",
  211. __FUNCTION__, strerror(errno));
  212. mDevFd = timerfd_create(CLOCK_MONOTONIC, 0);
  213. }
  214. if (-1 != mDevFd) {
  215. // ensure we have the necessary resources created
  216. LocTimerContainer::getPollTaskLocked();
  217. LocTimerContainer::getMsgTaskLocked();
  218. } else {
  219. LOC_LOGE("%s: timerfd_create failure - %s", __FUNCTION__, strerror(errno));
  220. }
  221. }
  222. // dtor
  223. // we do not ever destroy the static resources.
  224. inline
  225. LocTimerContainer::~LocTimerContainer() {
  226. close(mDevFd);
  227. }
  228. LocTimerContainer* LocTimerContainer::get(bool wakeOnExpire) {
  229. // get the reference of either mHwTimer or mSwTimers per wakeOnExpire
  230. LocTimerContainer*& container = wakeOnExpire ? mHwTimers : mSwTimers;
  231. // it is cheap to check pointer first than locking mutext unconditionally
  232. if (!container) {
  233. pthread_mutex_lock(&mMutex);
  234. // let's check one more time to be safe
  235. if (!container) {
  236. container = new LocTimerContainer(wakeOnExpire);
  237. // timerfd_create failure
  238. if (-1 == container->getTimerFd()) {
  239. delete container;
  240. container = NULL;
  241. }
  242. }
  243. pthread_mutex_unlock(&mMutex);
  244. }
  245. return container;
  246. }
  247. MsgTask* LocTimerContainer::getMsgTaskLocked() {
  248. // it is cheap to check pointer first than locking mutext unconditionally
  249. if (!mMsgTask) {
  250. mMsgTask = new MsgTask("LocTimerMsgTask");
  251. }
  252. return mMsgTask;
  253. }
  254. LocTimerPollTask* LocTimerContainer::getPollTaskLocked() {
  255. // it is cheap to check pointer first than locking mutext unconditionally
  256. if (!mPollTask) {
  257. mPollTask = new LocTimerPollTask();
  258. }
  259. return mPollTask;
  260. }
  261. inline
  262. LocTimerDelegate* LocTimerContainer::getSoonestTimer() {
  263. return (LocTimerDelegate*)(peek());
  264. }
  265. inline
  266. int LocTimerContainer::getTimerFd() {
  267. return mDevFd;
  268. }
  269. void LocTimerContainer::updateSoonestTime(LocTimerDelegate* priorTop) {
  270. LocTimerDelegate* curTop = getSoonestTimer();
  271. // check if top has changed
  272. if (curTop != priorTop) {
  273. struct itimerspec delay;
  274. memset(&delay, 0, sizeof(struct itimerspec));
  275. bool toSetTime = false;
  276. // if tree is empty now, we remove poll and disarm timer
  277. if (!curTop) {
  278. mPollTask->removePoll(*this);
  279. // setting the values to disarm timer
  280. delay.it_value.tv_sec = 0;
  281. delay.it_value.tv_nsec = 0;
  282. toSetTime = true;
  283. } else if (!priorTop || curTop->outRanks(*priorTop)) {
  284. // do this first to avoid race condition, in case settime is called
  285. // with too small an interval
  286. mPollTask->addPoll(*this);
  287. delay.it_value = curTop->getFutureTime();
  288. toSetTime = true;
  289. }
  290. if (toSetTime) {
  291. timerfd_settime(getTimerFd(), TFD_TIMER_ABSTIME, &delay, NULL);
  292. }
  293. }
  294. }
  295. // all the heap management is done in the MsgTask context.
  296. inline
  297. void LocTimerContainer::add(LocTimerDelegate& timer) {
  298. struct MsgTimerPush : public LocMsg {
  299. LocTimerContainer* mTimerContainer;
  300. LocTimerDelegate* mTimer;
  301. inline MsgTimerPush(LocTimerContainer& container, LocTimerDelegate& timer) :
  302. LocMsg(), mTimerContainer(&container), mTimer(&timer) {}
  303. inline virtual void proc() const {
  304. LocTimerDelegate* priorTop = mTimerContainer->getSoonestTimer();
  305. mTimerContainer->push((LocRankable&)(*mTimer));
  306. mTimerContainer->updateSoonestTime(priorTop);
  307. }
  308. };
  309. mMsgTask->sendMsg(new MsgTimerPush(*this, timer));
  310. }
  311. // all the heap management is done in the MsgTask context.
  312. void LocTimerContainer::remove(LocTimerDelegate& timer) {
  313. struct MsgTimerRemove : public LocMsg {
  314. LocTimerContainer* mTimerContainer;
  315. LocTimerDelegate* mTimer;
  316. inline MsgTimerRemove(LocTimerContainer& container, LocTimerDelegate& timer) :
  317. LocMsg(), mTimerContainer(&container), mTimer(&timer) {}
  318. inline virtual void proc() const {
  319. LocTimerDelegate* priorTop = mTimerContainer->getSoonestTimer();
  320. // update soonest timer only if mTimer is actually removed from
  321. // mTimerContainer AND mTimer is not priorTop.
  322. if (priorTop == ((LocHeap*)mTimerContainer)->remove((LocRankable&)*mTimer)) {
  323. // if passing in NULL, we tell updateSoonestTime to update
  324. // kernel with the current top timer interval.
  325. mTimerContainer->updateSoonestTime(NULL);
  326. }
  327. // all timers are deleted here, and only here.
  328. delete mTimer;
  329. }
  330. };
  331. mMsgTask->sendMsg(new MsgTimerRemove(*this, timer));
  332. }
  333. // all the heap management is done in the MsgTask context.
  334. // Upon expire, we check and continuously pop the heap until
  335. // the top node's timeout is in the future.
  336. void LocTimerContainer::expire() {
  337. struct MsgTimerExpire : public LocMsg {
  338. LocTimerContainer* mTimerContainer;
  339. inline MsgTimerExpire(LocTimerContainer& container) :
  340. LocMsg(), mTimerContainer(&container) {}
  341. inline virtual void proc() const {
  342. struct timespec now = {};
  343. // get time spec of now
  344. clock_gettime(CLOCK_BOOTTIME, &now);
  345. LocTimerDelegate timerOfNow(now);
  346. // pop everything in the heap that outRanks now, i.e. has time older than now
  347. // and then call expire() on that timer.
  348. for (LocTimerDelegate* timer = (LocTimerDelegate*)mTimerContainer->pop();
  349. NULL != timer;
  350. timer = mTimerContainer->popIfOutRanks(timerOfNow)) {
  351. // the timer delegate obj will be deleted before the return of this call
  352. timer->expire();
  353. }
  354. mTimerContainer->updateSoonestTime(NULL);
  355. }
  356. };
  357. struct itimerspec delay;
  358. memset(&delay, 0, sizeof(struct itimerspec));
  359. timerfd_settime(getTimerFd(), TFD_TIMER_ABSTIME, &delay, NULL);
  360. mPollTask->removePoll(*this);
  361. mMsgTask->sendMsg(new MsgTimerExpire(*this));
  362. }
  363. LocTimerDelegate* LocTimerContainer::popIfOutRanks(LocTimerDelegate& timer) {
  364. LocTimerDelegate* poppedNode = NULL;
  365. if (mTree && !timer.outRanks(*peek())) {
  366. poppedNode = (LocTimerDelegate*)(pop());
  367. }
  368. return poppedNode;
  369. }
  370. /***************************LocTimerPollTask methods***************************/
  371. inline
  372. LocTimerPollTask::LocTimerPollTask()
  373. : mFd(epoll_create(2)), mThread() {
  374. // before a next call returens, a thread will be created. The run() method
  375. // could already be running in parallel. Also, since each of the objs
  376. // creates a thread, the container will make sure that there will be only
  377. // one of such obj for our timer implementation.
  378. mThread.start("LocTimerPollTask", std::make_shared<TimerRunnable>(mFd));
  379. }
  380. void LocTimerPollTask::addPoll(LocTimerContainer& timerContainer) {
  381. struct epoll_event ev;
  382. memset(&ev, 0, sizeof(ev));
  383. ev.events = EPOLLIN;
  384. ev.data.fd = timerContainer.getTimerFd();
  385. // it is important that we set this context pointer with the input
  386. // timer container this is how we know which container should handle
  387. // which expiration.
  388. ev.data.ptr = &timerContainer;
  389. epoll_ctl(mFd, EPOLL_CTL_ADD, timerContainer.getTimerFd(), &ev);
  390. }
  391. inline
  392. void LocTimerPollTask::removePoll(LocTimerContainer& timerContainer) {
  393. epoll_ctl(mFd, EPOLL_CTL_DEL, timerContainer.getTimerFd(), NULL);
  394. }
  395. // The polling thread context will call this method. If run() method needs to
  396. // be repetitvely called, it must return true from the previous call.
  397. bool TimerRunnable::run() {
  398. struct epoll_event ev[2];
  399. // we have max 2 descriptors to poll from
  400. int fds = epoll_wait(mFd, ev, 2, -1);
  401. // we pretty much want to continually poll until the fd is closed
  402. bool rerun = (fds > 0) || (errno == EINTR);
  403. if (fds > 0) {
  404. // we may have 2 events
  405. for (int i = 0; i < fds; i++) {
  406. // each fd has a context pointer associated with the right timer container
  407. LocTimerContainer* container = (LocTimerContainer*)(ev[i].data.ptr);
  408. if (container) {
  409. container->expire();
  410. } else {
  411. epoll_ctl(mFd, EPOLL_CTL_DEL, ev[i].data.fd, NULL);
  412. }
  413. }
  414. }
  415. // if rerun is true, we are requesting to be scheduled again
  416. return rerun;
  417. }
  418. /***************************LocTimerDelegate methods***************************/
  419. inline
  420. LocTimerDelegate::LocTimerDelegate(LocTimer& client,
  421. struct timespec& futureTime,
  422. LocTimerContainer* container)
  423. : mClient(&client),
  424. mLock(mClient->mLock->share()),
  425. mFutureTime(futureTime),
  426. mContainer(container) {
  427. // adding the timer into the container
  428. mContainer->add(*this);
  429. }
  430. inline
  431. void LocTimerDelegate::destroyLocked() {
  432. // client handle will likely be deleted soon after this
  433. // method returns. Nulling this handle so that expire()
  434. // won't call the callback on the dead handle any more.
  435. mClient = NULL;
  436. if (mContainer) {
  437. LocTimerContainer* container = mContainer;
  438. mContainer = NULL;
  439. if (container) {
  440. container->remove(*this);
  441. }
  442. } // else we do not do anything. No such *this* can be
  443. // created and reached here with mContainer ever been
  444. // a non NULL. So *this* must have reached the if clause
  445. // once, and we want it reach there only once.
  446. }
  447. int LocTimerDelegate::ranks(LocRankable& rankable) {
  448. int rank = -1;
  449. LocTimerDelegate* timer = (LocTimerDelegate*)(&rankable);
  450. if (timer) {
  451. // larger time ranks lower!!!
  452. // IOW, if input obj has bigger tv_sec/tv_nsec, this obj outRanks higher
  453. rank = timer->mFutureTime.tv_sec - mFutureTime.tv_sec;
  454. if(0 == rank)
  455. {
  456. //rank against tv_nsec for msec accuracy
  457. rank = (int)(timer->mFutureTime.tv_nsec - mFutureTime.tv_nsec);
  458. }
  459. }
  460. return rank;
  461. }
  462. inline
  463. void LocTimerDelegate::expire() {
  464. // keeping a copy of client pointer to be safe
  465. // when timeOutCallback() is called at the end of this
  466. // method, *this* obj may be already deleted.
  467. LocTimer* client = mClient;
  468. // force a stop, which will lead to delete of this obj
  469. if (client && client->stop()) {
  470. // calling client callback with a pointer save on the stack
  471. // only if stop() returns true, i.e. it hasn't been stopped
  472. // already.
  473. client->timeOutCallback();
  474. }
  475. }
  476. /***************************LocTimer methods***************************/
  477. LocTimer::LocTimer() : mTimer(NULL), mLock(new LocSharedLock()) {
  478. }
  479. LocTimer::~LocTimer() {
  480. stop();
  481. if (mLock) {
  482. mLock->drop();
  483. mLock = NULL;
  484. }
  485. }
  486. bool LocTimer::start(unsigned int timeOutInMs, bool wakeOnExpire) {
  487. bool success = false;
  488. mLock->lock();
  489. if (!mTimer) {
  490. struct timespec futureTime = {};
  491. clock_gettime(CLOCK_BOOTTIME, &futureTime);
  492. futureTime.tv_sec += timeOutInMs / 1000;
  493. futureTime.tv_nsec += (timeOutInMs % 1000) * 1000000;
  494. if (futureTime.tv_nsec >= 1000000000) {
  495. futureTime.tv_sec += futureTime.tv_nsec / 1000000000;
  496. futureTime.tv_nsec %= 1000000000;
  497. }
  498. LocTimerContainer* container;
  499. container = LocTimerContainer::get(wakeOnExpire);
  500. if (NULL != container) {
  501. mTimer = new LocTimerDelegate(*this, futureTime, container);
  502. // if mTimer is non 0, success should be 0; or vice versa
  503. }
  504. success = (NULL != mTimer);
  505. }
  506. mLock->unlock();
  507. return success;
  508. }
  509. bool LocTimer::stop() {
  510. bool success = false;
  511. mLock->lock();
  512. if (mTimer) {
  513. LocTimerDelegate* timer = mTimer;
  514. mTimer = NULL;
  515. if (timer) {
  516. timer->destroyLocked();
  517. success = true;
  518. }
  519. }
  520. mLock->unlock();
  521. return success;
  522. }
  523. /***************************LocTimerWrapper methods***************************/
  524. //////////////////////////////////////////////////////////////////////////
  525. // This section below wraps for the C style APIs
  526. //////////////////////////////////////////////////////////////////////////
  527. class LocTimerWrapper : public LocTimer {
  528. loc_timer_callback mCb;
  529. void* mCallerData;
  530. LocTimerWrapper* mMe;
  531. static pthread_mutex_t mMutex;
  532. inline ~LocTimerWrapper() { mCb = NULL; mMe = NULL; }
  533. public:
  534. inline LocTimerWrapper(loc_timer_callback cb, void* callerData) :
  535. mCb(cb), mCallerData(callerData), mMe(this) {
  536. }
  537. void destroy() {
  538. pthread_mutex_lock(&mMutex);
  539. if (NULL != mCb && this == mMe) {
  540. delete this;
  541. }
  542. pthread_mutex_unlock(&mMutex);
  543. }
  544. virtual void timeOutCallback() {
  545. loc_timer_callback cb = mCb;
  546. void* callerData = mCallerData;
  547. if (cb) {
  548. cb(callerData, 0);
  549. }
  550. destroy();
  551. }
  552. };
  553. } // namespace loc_util
  554. //////////////////////////////////////////////////////////////////////////
  555. // This section below wraps for the C style APIs
  556. //////////////////////////////////////////////////////////////////////////
  557. using loc_util::LocTimerWrapper;
  558. pthread_mutex_t LocTimerWrapper::mMutex = PTHREAD_MUTEX_INITIALIZER;
  559. void* loc_timer_start(uint64_t msec, loc_timer_callback cb_func,
  560. void *caller_data, bool wake_on_expire)
  561. {
  562. LocTimerWrapper* locTimerWrapper = NULL;
  563. if (cb_func) {
  564. locTimerWrapper = new LocTimerWrapper(cb_func, caller_data);
  565. if (locTimerWrapper) {
  566. locTimerWrapper->start(msec, wake_on_expire);
  567. }
  568. }
  569. return locTimerWrapper;
  570. }
  571. void loc_timer_stop(void*& handle)
  572. {
  573. if (handle) {
  574. LocTimerWrapper* locTimerWrapper = (LocTimerWrapper*)(handle);
  575. locTimerWrapper->destroy();
  576. handle = NULL;
  577. }
  578. }