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