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