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