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