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 <time.h>
31#include <errno.h>
32#include <unistd.h>
33#include <stdio.h>
34#include <stdlib.h>
35#include <loc_timer.h>
36#include <sys/timerfd.h>
37#include <sys/epoll.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/*
51There are implementations of 5 classes in this file:
52LocTimer, LocTimerDelegate, LocTimerContainer, LocTimerPollTask, LocTimerWrapper
53
54LocTimer - client front end, interface for client to start / stop timers, also
55           to provide a callback.
56LocTimerDelegate - 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.
66LocTimerContainer - 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.
74LocTimerPollTask - 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.
78LocTimerWrapper - a LocTimer client itself, to implement the existing C API with
79                  APIs, loc_timer_start() and loc_timer_stop().
80
81*/
82
83class 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.
93class 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
117public:
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.
142class 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();
150public:
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.
175class 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; } }
186public:
187    LocTimerDelegate(LocTimer& client, struct timespec& futureTime, bool wakeOnExpire);
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.
202pthread_mutex_t LocTimerContainer::mMutex = PTHREAD_MUTEX_INITIALIZER;
203LocTimerContainer* LocTimerContainer::mSwTimers = NULL;
204LocTimerContainer* LocTimerContainer::mHwTimers = NULL;
205MsgTask* LocTimerContainer::mMsgTask = NULL;
206LocTimerPollTask* 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.
211LocTimerContainer::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.
231inline
232LocTimerContainer::~LocTimerContainer() {
233    close(mDevFd);
234}
235
236LocTimerContainer* 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
256MsgTask* 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
264LocTimerPollTask* 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
272inline
273LocTimerDelegate* LocTimerContainer::getSoonestTimer() {
274    return (LocTimerDelegate*)(peek());
275}
276
277inline
278int LocTimerContainer::getTimerFd() {
279    return mDevFd;
280}
281
282void 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.
310inline
311void 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.
329void 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.
356void 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
384LocTimerDelegate* 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
396inline
397LocTimerPollTask::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
409inline
410LocTimerPollTask::~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
416void LocTimerPollTask::destroy() {
417    if (mThread) {
418        LocThread* thread = mThread;
419        mThread = NULL;
420        delete thread;
421    } else {
422        delete this;
423    }
424}
425
426void 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
440inline
441void 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.
447bool 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
475inline
476LocTimerDelegate::LocTimerDelegate(LocTimer& client, struct timespec& futureTime, bool wakeOnExpire)
477    : mClient(&client),
478      mLock(mClient->mLock->share()),
479      mFutureTime(futureTime),
480      mContainer(LocTimerContainer::get(wakeOnExpire)) {
481    // adding the timer into the container
482    mContainer->add(*this);
483}
484
485inline
486void LocTimerDelegate::destroyLocked() {
487    // client handle will likely be deleted soon after this
488    // method returns. Nulling this handle so that expire()
489    // won't call the callback on the dead handle any more.
490    mClient = NULL;
491
492    if (mContainer) {
493        LocTimerContainer* container = mContainer;
494        mContainer = NULL;
495        if (container) {
496            container->remove(*this);
497        }
498    } // else we do not do anything. No such *this* can be
499      // created and reached here with mContainer ever been
500      // a non NULL. So *this* must have reached the if clause
501      // once, and we want it reach there only once.
502}
503
504int LocTimerDelegate::ranks(LocRankable& rankable) {
505    int rank = -1;
506    LocTimerDelegate* timer = (LocTimerDelegate*)(&rankable);
507    if (timer) {
508        // larger time ranks lower!!!
509        // IOW, if input obj has bigger tv_sec, this obj outRanks higher
510        rank = timer->mFutureTime.tv_sec - mFutureTime.tv_sec;
511    }
512    return rank;
513}
514
515inline
516void LocTimerDelegate::expire() {
517    // keeping a copy of client pointer to be safe
518    // when timeOutCallback() is called at the end of this
519    // method, *this* obj may be already deleted.
520    LocTimer* client = mClient;
521    // force a stop, which will lead to delete of this obj
522    if (client && client->stop()) {
523        // calling client callback with a pointer save on the stack
524        // only if stop() returns true, i.e. it hasn't been stopped
525        // already.
526        client->timeOutCallback();
527    }
528}
529
530
531/***************************LocTimer methods***************************/
532LocTimer::LocTimer() : mTimer(NULL), mLock(new LocSharedLock()) {
533}
534
535LocTimer::~LocTimer() {
536    stop();
537    if (mLock) {
538        mLock->drop();
539        mLock = NULL;
540    }
541}
542
543bool LocTimer::start(unsigned int timeOutInMs, bool wakeOnExpire) {
544    bool success = false;
545    mLock->lock();
546    if (!mTimer) {
547        struct timespec futureTime;
548        clock_gettime(CLOCK_BOOTTIME, &futureTime);
549        futureTime.tv_sec += timeOutInMs / 1000;
550        futureTime.tv_nsec += (timeOutInMs % 1000) * 1000000;
551        if (futureTime.tv_nsec >= 1000000000) {
552            futureTime.tv_sec += futureTime.tv_nsec / 1000000000;
553            futureTime.tv_nsec %= 1000000000;
554        }
555        mTimer = new LocTimerDelegate(*this, futureTime, wakeOnExpire);
556        // if mTimer is non 0, success should be 0; or vice versa
557        success = (NULL != mTimer);
558    }
559    mLock->unlock();
560    return success;
561}
562
563bool LocTimer::stop() {
564    bool success = false;
565    mLock->lock();
566    if (mTimer) {
567        LocTimerDelegate* timer = mTimer;
568        mTimer = NULL;
569        if (timer) {
570            timer->destroyLocked();
571            success = true;
572        }
573    }
574    mLock->unlock();
575    return success;
576}
577
578/***************************LocTimerWrapper methods***************************/
579//////////////////////////////////////////////////////////////////////////
580// This section below wraps for the C style APIs
581//////////////////////////////////////////////////////////////////////////
582class LocTimerWrapper : public LocTimer {
583    loc_timer_callback mCb;
584    void* mCallerData;
585    LocTimerWrapper* mMe;
586    static pthread_mutex_t mMutex;
587    inline ~LocTimerWrapper() { mCb = NULL; mMe = NULL; }
588public:
589    inline LocTimerWrapper(loc_timer_callback cb, void* callerData) :
590        mCb(cb), mCallerData(callerData), mMe(this) {
591    }
592    void destroy() {
593        pthread_mutex_lock(&mMutex);
594        if (NULL != mCb && this == mMe) {
595            delete this;
596        }
597        pthread_mutex_unlock(&mMutex);
598    }
599    virtual void timeOutCallback() {
600        loc_timer_callback cb = mCb;
601        void* callerData = mCallerData;
602        if (cb) {
603            cb(callerData, 0);
604        }
605        destroy();
606    }
607};
608
609pthread_mutex_t LocTimerWrapper::mMutex = PTHREAD_MUTEX_INITIALIZER;
610
611void* loc_timer_start(uint64_t msec, loc_timer_callback cb_func,
612                      void *caller_data, bool wake_on_expire)
613{
614    LocTimerWrapper* locTimerWrapper = NULL;
615
616    if (cb_func) {
617        locTimerWrapper = new LocTimerWrapper(cb_func, caller_data);
618
619        if (locTimerWrapper) {
620            locTimerWrapper->start(msec, wake_on_expire);
621        }
622    }
623
624    return locTimerWrapper;
625}
626
627void loc_timer_stop(void*&  handle)
628{
629    if (handle) {
630        LocTimerWrapper* locTimerWrapper = (LocTimerWrapper*)(handle);
631        locTimerWrapper->destroy();
632        handle = NULL;
633    }
634}
635
636//////////////////////////////////////////////////////////////////////////
637// This section above wraps for the C style APIs
638//////////////////////////////////////////////////////////////////////////
639
640#ifdef __LOC_DEBUG__
641
642double getDeltaSeconds(struct timespec from, struct timespec to) {
643    return (double)to.tv_sec + (double)to.tv_nsec / 1000000000
644        - from.tv_sec - (double)from.tv_nsec / 1000000000;
645}
646
647struct timespec getNow() {
648    struct timespec now;
649    clock_gettime(CLOCK_BOOTTIME, &now);
650    return now;
651}
652
653class LocTimerTest : public LocTimer, public LocRankable {
654    int mTimeOut;
655    const struct timespec mTimeOfBirth;
656    inline struct timespec getTimerWrapper(int timeout) {
657        struct timespec now;
658        clock_gettime(CLOCK_BOOTTIME, &now);
659        now.tv_sec += timeout;
660        return now;
661    }
662public:
663    inline LocTimerTest(int timeout) : LocTimer(), LocRankable(),
664            mTimeOut(timeout), mTimeOfBirth(getTimerWrapper(0)) {}
665    inline virtual int ranks(LocRankable& rankable) {
666        LocTimerTest* timer = dynamic_cast<LocTimerTest*>(&rankable);
667        return timer->mTimeOut - mTimeOut;
668    }
669    inline virtual void timeOutCallback() {
670        printf("timeOutCallback() - ");
671        deviation();
672    }
673    double deviation() {
674        struct timespec now = getTimerWrapper(0);
675        double delta = getDeltaSeconds(mTimeOfBirth, now);
676        printf("%lf: %lf\n", delta, delta * 100 / mTimeOut);
677        return delta / mTimeOut;
678    }
679};
680
681// For Linux command line testing:
682// compilation:
683//     g++ -D__LOC_HOST_DEBUG__ -D__LOC_DEBUG__ -g -I. -I../../../../system/core/include -o LocHeap.o LocHeap.cpp
684//     g++ -D__LOC_HOST_DEBUG__ -D__LOC_DEBUG__ -g -std=c++0x -I. -I../../../../system/core/include -lpthread -o LocThread.o LocThread.cpp
685//     g++ -D__LOC_HOST_DEBUG__ -D__LOC_DEBUG__ -g -I. -I../../../../system/core/include -o LocTimer.o LocTimer.cpp
686int main(int argc, char** argv) {
687    struct timespec timeOfStart=getNow();
688    srand(time(NULL));
689    int tries = atoi(argv[1]);
690    int checks = tries >> 3;
691    LocTimerTest** timerArray = new LocTimerTest*[tries];
692    memset(timerArray, NULL, tries);
693
694    for (int i = 0; i < tries; i++) {
695        int r = rand() % tries;
696        LocTimerTest* timer = new LocTimerTest(r);
697        if (timerArray[r]) {
698            if (!timer->stop()) {
699                printf("%lf:\n", getDeltaSeconds(timeOfStart, getNow()));
700                printf("ERRER: %dth timer, id %d, not running when it should be\n", i, r);
701                exit(0);
702            } else {
703                printf("stop() - %d\n", r);
704                delete timer;
705                timerArray[r] = NULL;
706            }
707        } else {
708            if (!timer->start(r, false)) {
709                printf("%lf:\n", getDeltaSeconds(timeOfStart, getNow()));
710                printf("ERRER: %dth timer, id %d, running when it should not be\n", i, r);
711                exit(0);
712            } else {
713                printf("stop() - %d\n", r);
714                timerArray[r] = timer;
715            }
716        }
717    }
718
719    for (int i = 0; i < tries; i++) {
720        if (timerArray[i]) {
721            if (!timerArray[i]->stop()) {
722                printf("%lf:\n", getDeltaSeconds(timeOfStart, getNow()));
723                printf("ERRER: %dth timer, not running when it should be\n", i);
724                exit(0);
725            } else {
726                printf("stop() - %d\n", i);
727                delete timerArray[i];
728                timerArray[i] = NULL;
729            }
730        }
731    }
732
733    delete[] timerArray;
734
735    return 0;
736}
737
738#endif
739