Home | History | Annotate | Download | only in utils
      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
    182     inline LocTimerDelegate(struct timespec& delay)
    183         : mClient(NULL), mLock(NULL), mFutureTime(delay), mContainer(NULL) {}
    184     inline ~LocTimerDelegate() { if (mLock) { mLock->drop(); mLock = NULL; } }
    185 public:
    186     LocTimerDelegate(LocTimer& client, struct timespec& futureTime, bool wakeOnExpire);
    187     void destroyLocked();
    188     // LocRankable virtual method
    189     virtual int ranks(LocRankable& rankable);
    190     void expire();
    191     inline struct timespec getFutureTime() { return mFutureTime; }
    192 };
    193 
    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.
    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
    231 LocTimerContainer::~LocTimerContainer() {
    232     close(mDevFd);
    233 }
    234 
    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 
    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 
    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
    272 LocTimerDelegate* LocTimerContainer::getSoonestTimer() {
    273     return (LocTimerDelegate*)(peek());
    274 }
    275 
    276 inline
    277 int LocTimerContainer::getTimerFd() {
    278     return mDevFd;
    279 }
    280 
    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;
    287         memset(&delay, 0 ,sizeof(struct itimerspec));
    288         bool toSetTime = false;
    289         // if tree is empty now, we remove poll and disarm timer
    290         if (!curTop) {
    291             mPollTask->removePoll(*this);
    292             // setting the values to disarm timer
    293             delay.it_value.tv_sec = 0;
    294             delay.it_value.tv_nsec = 0;
    295             toSetTime = true;
    296         } else if (!priorTop || curTop->outRanks(*priorTop)) {
    297             // do this first to avoid race condition, in case settime is called
    298             // with too small an interval
    299             mPollTask->addPoll(*this);
    300             delay.it_value = curTop->getFutureTime();
    301             toSetTime = true;
    302         }
    303         if (toSetTime) {
    304             timerfd_settime(getTimerFd(), TFD_TIMER_ABSTIME, &delay, NULL);
    305         }
    306     }
    307 }
    308 
    309 // all the heap management is done in the MsgTask context.
    310 inline
    311 void LocTimerContainer::add(LocTimerDelegate& timer) {
    312     struct MsgTimerPush : public LocMsg {
    313         LocTimerContainer* mTimerContainer;
    314         LocHeapNode* mTree;
    315         LocTimerDelegate* mTimer;
    316         inline MsgTimerPush(LocTimerContainer& container, LocTimerDelegate& timer) :
    317             LocMsg(), mTimerContainer(&container), mTimer(&timer) {}
    318         inline virtual void proc() const {
    319             LocTimerDelegate* priorTop = mTimerContainer->getSoonestTimer();
    320             mTimerContainer->push((LocRankable&)(*mTimer));
    321             mTimerContainer->updateSoonestTime(priorTop);
    322         }
    323     };
    324 
    325     mMsgTask->sendMsg(new MsgTimerPush(*this, timer));
    326 }
    327 
    328 // all the heap management is done in the MsgTask context.
    329 void LocTimerContainer::remove(LocTimerDelegate& timer) {
    330     struct MsgTimerRemove : public LocMsg {
    331         LocTimerContainer* mTimerContainer;
    332         LocTimerDelegate* mTimer;
    333         inline MsgTimerRemove(LocTimerContainer& container, LocTimerDelegate& timer) :
    334             LocMsg(), mTimerContainer(&container), mTimer(&timer) {}
    335         inline virtual void proc() const {
    336             LocTimerDelegate* priorTop = mTimerContainer->getSoonestTimer();
    337 
    338             // update soonest timer only if mTimer is actually removed from
    339             // mTimerContainer AND mTimer is not priorTop.
    340             if (priorTop == ((LocHeap*)mTimerContainer)->remove((LocRankable&)*mTimer)) {
    341                 // if passing in NULL, we tell updateSoonestTime to update
    342                 // kernel with the current top timer interval.
    343                 mTimerContainer->updateSoonestTime(NULL);
    344             }
    345             // all timers are deleted here, and only here.
    346             delete mTimer;
    347         }
    348     };
    349 
    350     mMsgTask->sendMsg(new MsgTimerRemove(*this, timer));
    351 }
    352 
    353 // all the heap management is done in the MsgTask context.
    354 // Upon expire, we check and continuously pop the heap until
    355 // the top node's timeout is in the future.
    356 void LocTimerContainer::expire() {
    357     struct MsgTimerExpire : public LocMsg {
    358         LocTimerContainer* mTimerContainer;
    359         inline MsgTimerExpire(LocTimerContainer& container) :
    360             LocMsg(), mTimerContainer(&container) {}
    361         inline virtual void proc() const {
    362             struct timespec now;
    363             // get time spec of now
    364             clock_gettime(CLOCK_BOOTTIME, &now);
    365             LocTimerDelegate timerOfNow(now);
    366             // pop everything in the heap that outRanks now, i.e. has time older than now
    367             // and then call expire() on that timer.
    368             for (LocTimerDelegate* timer = (LocTimerDelegate*)mTimerContainer->pop();
    369                  NULL != timer;
    370                  timer = mTimerContainer->popIfOutRanks(timerOfNow)) {
    371                 // the timer delegate obj will be deleted before the return of this call
    372                 timer->expire();
    373             }
    374             mTimerContainer->updateSoonestTime(NULL);
    375         }
    376     };
    377 
    378     struct itimerspec delay;
    379     memset(&delay, 0 ,sizeof(struct itimerspec));
    380     timerfd_settime(getTimerFd(), TFD_TIMER_ABSTIME, &delay, NULL);
    381     mPollTask->removePoll(*this);
    382     mMsgTask->sendMsg(new MsgTimerExpire(*this));
    383 }
    384 
    385 LocTimerDelegate* LocTimerContainer::popIfOutRanks(LocTimerDelegate& timer) {
    386     LocTimerDelegate* poppedNode = NULL;
    387     if (mTree && !timer.outRanks(*peek())) {
    388         poppedNode = (LocTimerDelegate*)(pop());
    389     }
    390 
    391     return poppedNode;
    392 }
    393 
    394 
    395 /***************************LocTimerPollTask methods***************************/
    396 
    397 inline
    398 LocTimerPollTask::LocTimerPollTask()
    399     : mFd(epoll_create(2)), mThread(new LocThread()) {
    400     // before a next call returens, a thread will be created. The run() method
    401     // could already be running in parallel. Also, since each of the objs
    402     // creates a thread, the container will make sure that there will be only
    403     // one of such obj for our timer implementation.
    404     if (!mThread->start("LocTimerPollTask", this)) {
    405         delete mThread;
    406         mThread = NULL;
    407     }
    408 }
    409 
    410 inline
    411 LocTimerPollTask::~LocTimerPollTask() {
    412     // when fs is closed, epoll_wait() should fail run() should return false
    413     // and the spawned thread should exit.
    414     close(mFd);
    415 }
    416 
    417 void LocTimerPollTask::destroy() {
    418     if (mThread) {
    419         LocThread* thread = mThread;
    420         mThread = NULL;
    421         delete thread;
    422     } else {
    423         delete this;
    424     }
    425 }
    426 
    427 void LocTimerPollTask::addPoll(LocTimerContainer& timerContainer) {
    428     struct epoll_event ev;
    429     memset(&ev, 0, sizeof(ev));
    430 
    431     ev.events = EPOLLIN | EPOLLWAKEUP;
    432     ev.data.fd = timerContainer.getTimerFd();
    433     // it is important that we set this context pointer with the input
    434     // timer container this is how we know which container should handle
    435     // which expiration.
    436     ev.data.ptr = &timerContainer;
    437 
    438     epoll_ctl(mFd, EPOLL_CTL_ADD, timerContainer.getTimerFd(), &ev);
    439 }
    440 
    441 inline
    442 void LocTimerPollTask::removePoll(LocTimerContainer& timerContainer) {
    443     epoll_ctl(mFd, EPOLL_CTL_DEL, timerContainer.getTimerFd(), NULL);
    444 }
    445 
    446 // The polling thread context will call this method. If run() method needs to
    447 // be repetitvely called, it must return true from the previous call.
    448 bool LocTimerPollTask::run() {
    449     struct epoll_event ev[2];
    450 
    451     // we have max 2 descriptors to poll from
    452     int fds = epoll_wait(mFd, ev, 2, -1);
    453 
    454     // we pretty much want to continually poll until the fd is closed
    455     bool rerun = (fds > 0) || (errno == EINTR);
    456 
    457     if (fds > 0) {
    458         // we may have 2 events
    459         for (int i = 0; i < fds; i++) {
    460             // each fd has a context pointer associated with the right timer container
    461             LocTimerContainer* container = (LocTimerContainer*)(ev[i].data.ptr);
    462             if (container) {
    463                 container->expire();
    464             } else {
    465                 epoll_ctl(mFd, EPOLL_CTL_DEL, ev[i].data.fd, NULL);
    466             }
    467         }
    468     }
    469 
    470     // if rerun is true, we are requesting to be scheduled again
    471     return rerun;
    472 }
    473 
    474 /***************************LocTimerDelegate methods***************************/
    475 
    476 inline
    477 LocTimerDelegate::LocTimerDelegate(LocTimer& client, struct timespec& futureTime, bool wakeOnExpire)
    478     : mClient(&client),
    479       mLock(mClient->mLock->share()),
    480       mFutureTime(futureTime),
    481       mContainer(LocTimerContainer::get(wakeOnExpire)) {
    482     // adding the timer into the container
    483     mContainer->add(*this);
    484 }
    485 
    486 inline
    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 
    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, this obj outRanks higher
    511         rank = timer->mFutureTime.tv_sec - mFutureTime.tv_sec;
    512     }
    513     return rank;
    514 }
    515 
    516 inline
    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***************************/
    533 LocTimer::LocTimer() : mTimer(NULL), mLock(new LocSharedLock()) {
    534 }
    535 
    536 LocTimer::~LocTimer() {
    537     stop();
    538     if (mLock) {
    539         mLock->drop();
    540         mLock = NULL;
    541     }
    542 }
    543 
    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         mTimer = new LocTimerDelegate(*this, futureTime, wakeOnExpire);
    557         // if mTimer is non 0, success should be 0; or vice versa
    558         success = (NULL != mTimer);
    559     }
    560     mLock->unlock();
    561     return success;
    562 }
    563 
    564 bool LocTimer::stop() {
    565     bool success = false;
    566     mLock->lock();
    567     if (mTimer) {
    568         LocTimerDelegate* timer = mTimer;
    569         mTimer = NULL;
    570         if (timer) {
    571             timer->destroyLocked();
    572             success = true;
    573         }
    574     }
    575     mLock->unlock();
    576     return success;
    577 }
    578 
    579 /***************************LocTimerWrapper methods***************************/
    580 //////////////////////////////////////////////////////////////////////////
    581 // This section below wraps for the C style APIs
    582 //////////////////////////////////////////////////////////////////////////
    583 class LocTimerWrapper : public LocTimer {
    584     loc_timer_callback mCb;
    585     void* mCallerData;
    586     LocTimerWrapper* mMe;
    587     static pthread_mutex_t mMutex;
    588     inline ~LocTimerWrapper() { mCb = NULL; mMe = NULL; }
    589 public:
    590     inline LocTimerWrapper(loc_timer_callback cb, void* callerData) :
    591         mCb(cb), mCallerData(callerData), mMe(this) {
    592     }
    593     void destroy() {
    594         pthread_mutex_lock(&mMutex);
    595         if (NULL != mCb && this == mMe) {
    596             delete this;
    597         }
    598         pthread_mutex_unlock(&mMutex);
    599     }
    600     virtual void timeOutCallback() {
    601         loc_timer_callback cb = mCb;
    602         void* callerData = mCallerData;
    603         if (cb) {
    604             cb(callerData, 0);
    605         }
    606         destroy();
    607     }
    608 };
    609 
    610 pthread_mutex_t LocTimerWrapper::mMutex = PTHREAD_MUTEX_INITIALIZER;
    611 
    612 void* loc_timer_start(uint64_t msec, loc_timer_callback cb_func,
    613                       void *caller_data, bool wake_on_expire)
    614 {
    615     LocTimerWrapper* locTimerWrapper = NULL;
    616 
    617     if (cb_func) {
    618         locTimerWrapper = new LocTimerWrapper(cb_func, caller_data);
    619 
    620         if (locTimerWrapper) {
    621             locTimerWrapper->start(msec, wake_on_expire);
    622         }
    623     }
    624 
    625     return locTimerWrapper;
    626 }
    627 
    628 void loc_timer_stop(void*&  handle)
    629 {
    630     if (handle) {
    631         LocTimerWrapper* locTimerWrapper = (LocTimerWrapper*)(handle);
    632         locTimerWrapper->destroy();
    633         handle = NULL;
    634     }
    635 }
    636 
    637 //////////////////////////////////////////////////////////////////////////
    638 // This section above wraps for the C style APIs
    639 //////////////////////////////////////////////////////////////////////////
    640 
    641 #ifdef __LOC_DEBUG__
    642 
    643 double getDeltaSeconds(struct timespec from, struct timespec to) {
    644     return (double)to.tv_sec + (double)to.tv_nsec / 1000000000
    645         - from.tv_sec - (double)from.tv_nsec / 1000000000;
    646 }
    647 
    648 struct timespec getNow() {
    649     struct timespec now;
    650     clock_gettime(CLOCK_BOOTTIME, &now);
    651     return now;
    652 }
    653 
    654 class LocTimerTest : public LocTimer, public LocRankable {
    655     int mTimeOut;
    656     const struct timespec mTimeOfBirth;
    657     inline struct timespec getTimerWrapper(int timeout) {
    658         struct timespec now;
    659         clock_gettime(CLOCK_BOOTTIME, &now);
    660         now.tv_sec += timeout;
    661         return now;
    662     }
    663 public:
    664     inline LocTimerTest(int timeout) : LocTimer(), LocRankable(),
    665             mTimeOut(timeout), mTimeOfBirth(getTimerWrapper(0)) {}
    666     inline virtual int ranks(LocRankable& rankable) {
    667         LocTimerTest* timer = dynamic_cast<LocTimerTest*>(&rankable);
    668         return timer->mTimeOut - mTimeOut;
    669     }
    670     inline virtual void timeOutCallback() {
    671         printf("timeOutCallback() - ");
    672         deviation();
    673     }
    674     double deviation() {
    675         struct timespec now = getTimerWrapper(0);
    676         double delta = getDeltaSeconds(mTimeOfBirth, now);
    677         printf("%lf: %lf\n", delta, delta * 100 / mTimeOut);
    678         return delta / mTimeOut;
    679     }
    680 };
    681 
    682 // For Linux command line testing:
    683 // compilation:
    684 //     g++ -D__LOC_HOST_DEBUG__ -D__LOC_DEBUG__ -g -I. -I../../../../system/core/include -o LocHeap.o LocHeap.cpp
    685 //     g++ -D__LOC_HOST_DEBUG__ -D__LOC_DEBUG__ -g -std=c++0x -I. -I../../../../system/core/include -lpthread -o LocThread.o LocThread.cpp
    686 //     g++ -D__LOC_HOST_DEBUG__ -D__LOC_DEBUG__ -g -I. -I../../../../system/core/include -o LocTimer.o LocTimer.cpp
    687 int main(int argc, char** argv) {
    688     struct timespec timeOfStart=getNow();
    689     srand(time(NULL));
    690     int tries = atoi(argv[1]);
    691     int checks = tries >> 3;
    692     LocTimerTest** timerArray = new LocTimerTest*[tries];
    693     memset(timerArray, NULL, tries);
    694 
    695     for (int i = 0; i < tries; i++) {
    696         int r = rand() % tries;
    697         LocTimerTest* timer = new LocTimerTest(r);
    698         if (timerArray[r]) {
    699             if (!timer->stop()) {
    700                 printf("%lf:\n", getDeltaSeconds(timeOfStart, getNow()));
    701                 printf("ERRER: %dth timer, id %d, not running when it should be\n", i, r);
    702                 exit(0);
    703             } else {
    704                 printf("stop() - %d\n", r);
    705                 delete timer;
    706                 timerArray[r] = NULL;
    707             }
    708         } else {
    709             if (!timer->start(r, false)) {
    710                 printf("%lf:\n", getDeltaSeconds(timeOfStart, getNow()));
    711                 printf("ERRER: %dth timer, id %d, running when it should not be\n", i, r);
    712                 exit(0);
    713             } else {
    714                 printf("stop() - %d\n", r);
    715                 timerArray[r] = timer;
    716             }
    717         }
    718     }
    719 
    720     for (int i = 0; i < tries; i++) {
    721         if (timerArray[i]) {
    722             if (!timerArray[i]->stop()) {
    723                 printf("%lf:\n", getDeltaSeconds(timeOfStart, getNow()));
    724                 printf("ERRER: %dth timer, not running when it should be\n", i);
    725                 exit(0);
    726             } else {
    727                 printf("stop() - %d\n", i);
    728                 delete timerArray[i];
    729                 timerArray[i] = NULL;
    730             }
    731         }
    732     }
    733 
    734     delete[] timerArray;
    735 
    736     return 0;
    737 }
    738 
    739 #endif
    740