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/*
50There are implementations of 5 classes in this file:
51LocTimer, LocTimerDelegate, LocTimerContainer, LocTimerPollTask, LocTimerWrapper
52
53LocTimer - client front end, interface for client to start / stop timers, also
54           to provide a callback.
55LocTimerDelegate - 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.
65LocTimerContainer - 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.
73LocTimerPollTask - 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.
77LocTimerWrapper - a LocTimer client itself, to implement the existing C API with
78                  APIs, loc_timer_start() and loc_timer_stop().
79
80*/
81
82class 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.
92class 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
116public:
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.
141class 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();
149public:
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.
174class 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; } }
185public:
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.
201pthread_mutex_t LocTimerContainer::mMutex = PTHREAD_MUTEX_INITIALIZER;
202LocTimerContainer* LocTimerContainer::mSwTimers = NULL;
203LocTimerContainer* LocTimerContainer::mHwTimers = NULL;
204MsgTask* LocTimerContainer::mMsgTask = NULL;
205LocTimerPollTask* 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.
210LocTimerContainer::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.
230inline
231LocTimerContainer::~LocTimerContainer() {
232    close(mDevFd);
233}
234
235LocTimerContainer* 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
255MsgTask* 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
263LocTimerPollTask* 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
271inline
272LocTimerDelegate* LocTimerContainer::getSoonestTimer() {
273    return (LocTimerDelegate*)(peek());
274}
275
276inline
277int LocTimerContainer::getTimerFd() {
278    return mDevFd;
279}
280
281void 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.
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;
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
385LocTimerDelegate* 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
397inline
398LocTimerPollTask::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
410inline
411LocTimerPollTask::~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
417void LocTimerPollTask::destroy() {
418    if (mThread) {
419        LocThread* thread = mThread;
420        mThread = NULL;
421        delete thread;
422    } else {
423        delete this;
424    }
425}
426
427void 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
441inline
442void 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.
448bool 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
476inline
477LocTimerDelegate::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
486inline
487void 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
505int 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
516inline
517void 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***************************/
533LocTimer::LocTimer() : mTimer(NULL), mLock(new LocSharedLock()) {
534}
535
536LocTimer::~LocTimer() {
537    stop();
538    if (mLock) {
539        mLock->drop();
540        mLock = NULL;
541    }
542}
543
544bool 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
564bool 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//////////////////////////////////////////////////////////////////////////
583class 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; }
589public:
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
610pthread_mutex_t LocTimerWrapper::mMutex = PTHREAD_MUTEX_INITIALIZER;
611
612void* 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
628void 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
643double 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
648struct timespec getNow() {
649    struct timespec now;
650    clock_gettime(CLOCK_BOOTTIME, &now);
651    return now;
652}
653
654class 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    }
663public:
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
687int 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