summaryrefslogtreecommitdiff
path: root/gps/utils/LocTimer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gps/utils/LocTimer.cpp')
-rw-r--r--gps/utils/LocTimer.cpp753
1 files changed, 753 insertions, 0 deletions
diff --git a/gps/utils/LocTimer.cpp b/gps/utils/LocTimer.cpp
new file mode 100644
index 0000000..93775d0
--- /dev/null
+++ b/gps/utils/LocTimer.cpp
@@ -0,0 +1,753 @@
+/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation, nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <errno.h>
+#include <sys/timerfd.h>
+#include <sys/epoll.h>
+#include <log_util.h>
+#include <loc_timer.h>
+#include <LocTimer.h>
+#include <LocHeap.h>
+#include <LocThread.h>
+#include <LocSharedLock.h>
+#include <MsgTask.h>
+
+#ifdef __HOST_UNIT_TEST__
+#define EPOLLWAKEUP 0
+#define CLOCK_BOOTTIME CLOCK_MONOTONIC
+#define CLOCK_BOOTTIME_ALARM CLOCK_MONOTONIC
+#endif
+
+/*
+There are implementations of 5 classes in this file:
+LocTimer, LocTimerDelegate, LocTimerContainer, LocTimerPollTask, LocTimerWrapper
+
+LocTimer - client front end, interface for client to start / stop timers, also
+ to provide a callback.
+LocTimerDelegate - an internal timer entity, which also is a LocRankable obj.
+ Its life cycle is different than that of LocTimer. It gets
+ created when LocTimer::start() is called, and gets deleted
+ when it expires or clients calls the hosting LocTimer obj's
+ stop() method. When a LocTimerDelegate obj is ticking, it
+ stays in the corresponding LocTimerContainer. When expired
+ or stopped, the obj is removed from the container. Since it
+ is also a LocRankable obj, and LocTimerContainer also is a
+ heap, its ranks() implementation decides where it is placed
+ in the heap.
+LocTimerContainer - core of the timer service. It is a container (derived from
+ LocHeap) for LocTimerDelegate (implements LocRankable) objs.
+ There are 2 of such containers, one for sw timers (or Linux
+ timers) one for hw timers (or Linux alarms). It adds one of
+ each (those that expire the soonest) to kernel via services
+ provided by LocTimerPollTask. All the heap management on the
+ LocTimerDelegate objs are done in the MsgTask context, such
+ that synchronization is ensured.
+LocTimerPollTask - is a class that wraps timerfd and epoll POXIS APIs. It also
+ both implements LocRunnalbe with epoll_wait() in the run()
+ method. It is also a LocThread client, so as to loop the run
+ method.
+LocTimerWrapper - a LocTimer client itself, to implement the existing C API with
+ APIs, loc_timer_start() and loc_timer_stop().
+
+*/
+
+class LocTimerPollTask;
+
+// This is a multi-functaional class that:
+// * extends the LocHeap class for the detection of head update upon add / remove
+// events. When that happens, soonest time out changes, so timerfd needs update.
+// * contains the timers, and add / remove them into the heap
+// * provides and maps 2 of such containers, one for timers (or mSwTimers), one
+// for alarms (or mHwTimers);
+// * provides a polling thread;
+// * provides a MsgTask thread for synchronized add / remove / timer client callback.
+class LocTimerContainer : public LocHeap {
+ // mutex to synchronize getters of static members
+ static pthread_mutex_t mMutex;
+ // Container of timers
+ static LocTimerContainer* mSwTimers;
+ // Container of alarms
+ static LocTimerContainer* mHwTimers;
+ // Msg task to provider msg Q, sender and reader.
+ static MsgTask* mMsgTask;
+ // Poll task to provide epoll call and threading to poll.
+ static LocTimerPollTask* mPollTask;
+ // timer / alarm fd
+ int mDevFd;
+ // ctor
+ LocTimerContainer(bool wakeOnExpire);
+ // dtor
+ ~LocTimerContainer();
+ static MsgTask* getMsgTaskLocked();
+ static LocTimerPollTask* getPollTaskLocked();
+ // extend LocHeap and pop if the top outRanks input
+ LocTimerDelegate* popIfOutRanks(LocTimerDelegate& timer);
+ // update the timer POSIX calls with updated soonest timer spec
+ void updateSoonestTime(LocTimerDelegate* priorTop);
+
+public:
+ // factory method to control the creation of mSwTimers / mHwTimers
+ static LocTimerContainer* get(bool wakeOnExpire);
+
+ LocTimerDelegate* getSoonestTimer();
+ int getTimerFd();
+ // add a timer / alarm obj into the container
+ void add(LocTimerDelegate& timer);
+ // remove a timer / alarm obj from the container
+ void remove(LocTimerDelegate& timer);
+ // handling of timer / alarm expiration
+ void expire();
+};
+
+// This class implements the polling thread that epolls imer / alarm fds.
+// The LocRunnable::run() contains the actual polling. The other methods
+// will be run in the caller's thread context to add / remove timer / alarm
+// fds the kernel, while the polling is blocked on epoll_wait() call.
+// Since the design is that we have maximally 2 polls, one for all the
+// timers; one for all the alarms, we will poll at most on 2 fds. But it
+// is possile that all we have are only timers or alarms at one time, so we
+// allow dynamically add / remove fds we poll on. The design decision of
+// having 1 fd per container of timer / alarm is such that, we may not need
+// to make a system call each time a timer / alarm is added / removed, unless
+// that changes the "soonest" time out of that of all the timers / alarms.
+class LocTimerPollTask : public LocRunnable {
+ // the epoll fd
+ const int mFd;
+ // the thread that calls run() method
+ LocThread* mThread;
+ friend class LocThreadDelegate;
+ // dtor
+ ~LocTimerPollTask();
+public:
+ // ctor
+ LocTimerPollTask();
+ // this obj will be deleted once thread is deleted
+ void destroy();
+ // add a container of timers. Each contain has a unique device fd, i.e.
+ // either timer or alarm fd, and a heap of timers / alarms. It is expected
+ // that container would have written to the device fd with the soonest
+ // time out value in the heap at the time of calling this method. So all
+ // this method does is to add the fd of the input container to the poll
+ // and also add the pointer of the container to the event data ptr, such
+ // when poll_wait wakes up on events, we know who is the owner of the fd.
+ void addPoll(LocTimerContainer& timerContainer);
+ // remove a fd that is assciated with a container. The expectation is that
+ // the atual timer would have been removed from the container.
+ void removePoll(LocTimerContainer& timerContainer);
+ // The polling thread context will call this method. This is where
+ // epoll_wait() is blocking and waiting for events..
+ virtual bool run();
+};
+
+// Internal class of timer obj. It gets born when client calls LocTimer::start();
+// and gets deleted when client calls LocTimer::stop() or when the it expire()'s.
+// This class implements LocRankable::ranks() so that when an obj is added into
+// the container (of LocHeap), it gets placed in sorted order.
+class LocTimerDelegate : public LocRankable {
+ friend class LocTimerContainer;
+ friend class LocTimer;
+ LocTimer* mClient;
+ LocSharedLock* mLock;
+ struct timespec mFutureTime;
+ LocTimerContainer* mContainer;
+ // not a complete obj, just ctor for LocRankable comparisons
+ inline LocTimerDelegate(struct timespec& delay)
+ : mClient(NULL), mLock(NULL), mFutureTime(delay), mContainer(NULL) {}
+ inline ~LocTimerDelegate() { if (mLock) { mLock->drop(); mLock = NULL; } }
+public:
+ LocTimerDelegate(LocTimer& client, struct timespec& futureTime, LocTimerContainer* container);
+ void destroyLocked();
+ // LocRankable virtual method
+ virtual int ranks(LocRankable& rankable);
+ void expire();
+ inline struct timespec getFutureTime() { return mFutureTime; }
+};
+
+/***************************LocTimerContainer methods***************************/
+
+// Most of these static recources are created on demand. They however are never
+// destoyed. The theory is that there are processes that link to this util lib
+// but never use timer, then these resources would never need to be created.
+// For those processes that do use timer, it will likely also need to every
+// once in a while. It might be cheaper keeping them around.
+pthread_mutex_t LocTimerContainer::mMutex = PTHREAD_MUTEX_INITIALIZER;
+LocTimerContainer* LocTimerContainer::mSwTimers = NULL;
+LocTimerContainer* LocTimerContainer::mHwTimers = NULL;
+MsgTask* LocTimerContainer::mMsgTask = NULL;
+LocTimerPollTask* LocTimerContainer::mPollTask = NULL;
+
+// ctor - initialize timer heaps
+// A container for swTimer (timer) is created, when wakeOnExpire is true; or
+// HwTimer (alarm), when wakeOnExpire is false.
+LocTimerContainer::LocTimerContainer(bool wakeOnExpire) :
+ mDevFd(timerfd_create(wakeOnExpire ? CLOCK_BOOTTIME_ALARM : CLOCK_BOOTTIME, 0)) {
+
+ if ((-1 == mDevFd) && (errno == EINVAL)) {
+ LOC_LOGW("%s: timerfd_create failure, fallback to CLOCK_MONOTONIC - %s",
+ __FUNCTION__, strerror(errno));
+ mDevFd = timerfd_create(CLOCK_MONOTONIC, 0);
+ }
+
+ if (-1 != mDevFd) {
+ // ensure we have the necessary resources created
+ LocTimerContainer::getPollTaskLocked();
+ LocTimerContainer::getMsgTaskLocked();
+ } else {
+ LOC_LOGE("%s: timerfd_create failure - %s", __FUNCTION__, strerror(errno));
+ }
+}
+
+// dtor
+// we do not ever destroy the static resources.
+inline
+LocTimerContainer::~LocTimerContainer() {
+ close(mDevFd);
+}
+
+LocTimerContainer* LocTimerContainer::get(bool wakeOnExpire) {
+ // get the reference of either mHwTimer or mSwTimers per wakeOnExpire
+ LocTimerContainer*& container = wakeOnExpire ? mHwTimers : mSwTimers;
+ // it is cheap to check pointer first than locking mutext unconditionally
+ if (!container) {
+ pthread_mutex_lock(&mMutex);
+ // let's check one more time to be safe
+ if (!container) {
+ container = new LocTimerContainer(wakeOnExpire);
+ // timerfd_create failure
+ if (-1 == container->getTimerFd()) {
+ delete container;
+ container = NULL;
+ }
+ }
+ pthread_mutex_unlock(&mMutex);
+ }
+ return container;
+}
+
+MsgTask* LocTimerContainer::getMsgTaskLocked() {
+ // it is cheap to check pointer first than locking mutext unconditionally
+ if (!mMsgTask) {
+ mMsgTask = new MsgTask("LocTimerMsgTask", false);
+ }
+ return mMsgTask;
+}
+
+LocTimerPollTask* LocTimerContainer::getPollTaskLocked() {
+ // it is cheap to check pointer first than locking mutext unconditionally
+ if (!mPollTask) {
+ mPollTask = new LocTimerPollTask();
+ }
+ return mPollTask;
+}
+
+inline
+LocTimerDelegate* LocTimerContainer::getSoonestTimer() {
+ return (LocTimerDelegate*)(peek());
+}
+
+inline
+int LocTimerContainer::getTimerFd() {
+ return mDevFd;
+}
+
+void LocTimerContainer::updateSoonestTime(LocTimerDelegate* priorTop) {
+ LocTimerDelegate* curTop = getSoonestTimer();
+
+ // check if top has changed
+ if (curTop != priorTop) {
+ struct itimerspec delay;
+ memset(&delay, 0, sizeof(struct itimerspec));
+ bool toSetTime = false;
+ // if tree is empty now, we remove poll and disarm timer
+ if (!curTop) {
+ mPollTask->removePoll(*this);
+ // setting the values to disarm timer
+ delay.it_value.tv_sec = 0;
+ delay.it_value.tv_nsec = 0;
+ toSetTime = true;
+ } else if (!priorTop || curTop->outRanks(*priorTop)) {
+ // do this first to avoid race condition, in case settime is called
+ // with too small an interval
+ mPollTask->addPoll(*this);
+ delay.it_value = curTop->getFutureTime();
+ toSetTime = true;
+ }
+ if (toSetTime) {
+ timerfd_settime(getTimerFd(), TFD_TIMER_ABSTIME, &delay, NULL);
+ }
+ }
+}
+
+// all the heap management is done in the MsgTask context.
+inline
+void LocTimerContainer::add(LocTimerDelegate& timer) {
+ struct MsgTimerPush : public LocMsg {
+ LocTimerContainer* mTimerContainer;
+ LocHeapNode* mTree;
+ LocTimerDelegate* mTimer;
+ inline MsgTimerPush(LocTimerContainer& container, LocTimerDelegate& timer) :
+ LocMsg(), mTimerContainer(&container), mTimer(&timer) {}
+ inline virtual void proc() const {
+ LocTimerDelegate* priorTop = mTimerContainer->getSoonestTimer();
+ mTimerContainer->push((LocRankable&)(*mTimer));
+ mTimerContainer->updateSoonestTime(priorTop);
+ }
+ };
+
+ mMsgTask->sendMsg(new MsgTimerPush(*this, timer));
+}
+
+// all the heap management is done in the MsgTask context.
+void LocTimerContainer::remove(LocTimerDelegate& timer) {
+ struct MsgTimerRemove : public LocMsg {
+ LocTimerContainer* mTimerContainer;
+ LocTimerDelegate* mTimer;
+ inline MsgTimerRemove(LocTimerContainer& container, LocTimerDelegate& timer) :
+ LocMsg(), mTimerContainer(&container), mTimer(&timer) {}
+ inline virtual void proc() const {
+ LocTimerDelegate* priorTop = mTimerContainer->getSoonestTimer();
+
+ // update soonest timer only if mTimer is actually removed from
+ // mTimerContainer AND mTimer is not priorTop.
+ if (priorTop == ((LocHeap*)mTimerContainer)->remove((LocRankable&)*mTimer)) {
+ // if passing in NULL, we tell updateSoonestTime to update
+ // kernel with the current top timer interval.
+ mTimerContainer->updateSoonestTime(NULL);
+ }
+ // all timers are deleted here, and only here.
+ delete mTimer;
+ }
+ };
+
+ mMsgTask->sendMsg(new MsgTimerRemove(*this, timer));
+}
+
+// all the heap management is done in the MsgTask context.
+// Upon expire, we check and continuously pop the heap until
+// the top node's timeout is in the future.
+void LocTimerContainer::expire() {
+ struct MsgTimerExpire : public LocMsg {
+ LocTimerContainer* mTimerContainer;
+ inline MsgTimerExpire(LocTimerContainer& container) :
+ LocMsg(), mTimerContainer(&container) {}
+ inline virtual void proc() const {
+ struct timespec now;
+ // get time spec of now
+ clock_gettime(CLOCK_BOOTTIME, &now);
+ LocTimerDelegate timerOfNow(now);
+ // pop everything in the heap that outRanks now, i.e. has time older than now
+ // and then call expire() on that timer.
+ for (LocTimerDelegate* timer = (LocTimerDelegate*)mTimerContainer->pop();
+ NULL != timer;
+ timer = mTimerContainer->popIfOutRanks(timerOfNow)) {
+ // the timer delegate obj will be deleted before the return of this call
+ timer->expire();
+ }
+ mTimerContainer->updateSoonestTime(NULL);
+ }
+ };
+
+ struct itimerspec delay;
+ memset(&delay, 0, sizeof(struct itimerspec));
+ timerfd_settime(getTimerFd(), TFD_TIMER_ABSTIME, &delay, NULL);
+ mPollTask->removePoll(*this);
+ mMsgTask->sendMsg(new MsgTimerExpire(*this));
+}
+
+LocTimerDelegate* LocTimerContainer::popIfOutRanks(LocTimerDelegate& timer) {
+ LocTimerDelegate* poppedNode = NULL;
+ if (mTree && !timer.outRanks(*peek())) {
+ poppedNode = (LocTimerDelegate*)(pop());
+ }
+
+ return poppedNode;
+}
+
+
+/***************************LocTimerPollTask methods***************************/
+
+inline
+LocTimerPollTask::LocTimerPollTask()
+ : mFd(epoll_create(2)), mThread(new LocThread()) {
+ // before a next call returens, a thread will be created. The run() method
+ // could already be running in parallel. Also, since each of the objs
+ // creates a thread, the container will make sure that there will be only
+ // one of such obj for our timer implementation.
+ if (!mThread->start("LocTimerPollTask", this)) {
+ delete mThread;
+ mThread = NULL;
+ }
+}
+
+inline
+LocTimerPollTask::~LocTimerPollTask() {
+ // when fs is closed, epoll_wait() should fail run() should return false
+ // and the spawned thread should exit.
+ close(mFd);
+}
+
+void LocTimerPollTask::destroy() {
+ if (mThread) {
+ LocThread* thread = mThread;
+ mThread = NULL;
+ delete thread;
+ } else {
+ delete this;
+ }
+}
+
+void LocTimerPollTask::addPoll(LocTimerContainer& timerContainer) {
+ struct epoll_event ev;
+ memset(&ev, 0, sizeof(ev));
+
+ ev.events = EPOLLIN | EPOLLWAKEUP;
+ ev.data.fd = timerContainer.getTimerFd();
+ // it is important that we set this context pointer with the input
+ // timer container this is how we know which container should handle
+ // which expiration.
+ ev.data.ptr = &timerContainer;
+
+ epoll_ctl(mFd, EPOLL_CTL_ADD, timerContainer.getTimerFd(), &ev);
+}
+
+inline
+void LocTimerPollTask::removePoll(LocTimerContainer& timerContainer) {
+ epoll_ctl(mFd, EPOLL_CTL_DEL, timerContainer.getTimerFd(), NULL);
+}
+
+// The polling thread context will call this method. If run() method needs to
+// be repetitvely called, it must return true from the previous call.
+bool LocTimerPollTask::run() {
+ struct epoll_event ev[2];
+
+ // we have max 2 descriptors to poll from
+ int fds = epoll_wait(mFd, ev, 2, -1);
+
+ // we pretty much want to continually poll until the fd is closed
+ bool rerun = (fds > 0) || (errno == EINTR);
+
+ if (fds > 0) {
+ // we may have 2 events
+ for (int i = 0; i < fds; i++) {
+ // each fd has a context pointer associated with the right timer container
+ LocTimerContainer* container = (LocTimerContainer*)(ev[i].data.ptr);
+ if (container) {
+ container->expire();
+ } else {
+ epoll_ctl(mFd, EPOLL_CTL_DEL, ev[i].data.fd, NULL);
+ }
+ }
+ }
+
+ // if rerun is true, we are requesting to be scheduled again
+ return rerun;
+}
+
+/***************************LocTimerDelegate methods***************************/
+
+inline
+LocTimerDelegate::LocTimerDelegate(LocTimer& client,
+ struct timespec& futureTime,
+ LocTimerContainer* container)
+ : mClient(&client),
+ mLock(mClient->mLock->share()),
+ mFutureTime(futureTime),
+ mContainer(container) {
+ // adding the timer into the container
+ mContainer->add(*this);
+}
+
+inline
+void LocTimerDelegate::destroyLocked() {
+ // client handle will likely be deleted soon after this
+ // method returns. Nulling this handle so that expire()
+ // won't call the callback on the dead handle any more.
+ mClient = NULL;
+
+ if (mContainer) {
+ LocTimerContainer* container = mContainer;
+ mContainer = NULL;
+ if (container) {
+ container->remove(*this);
+ }
+ } // else we do not do anything. No such *this* can be
+ // created and reached here with mContainer ever been
+ // a non NULL. So *this* must have reached the if clause
+ // once, and we want it reach there only once.
+}
+
+int LocTimerDelegate::ranks(LocRankable& rankable) {
+ int rank = -1;
+ LocTimerDelegate* timer = (LocTimerDelegate*)(&rankable);
+ if (timer) {
+ // larger time ranks lower!!!
+ // IOW, if input obj has bigger tv_sec/tv_nsec, this obj outRanks higher
+ rank = timer->mFutureTime.tv_sec - mFutureTime.tv_sec;
+ if(0 == rank)
+ {
+ //rank against tv_nsec for msec accuracy
+ rank = (int)(timer->mFutureTime.tv_nsec - mFutureTime.tv_nsec);
+ }
+ }
+ return rank;
+}
+
+inline
+void LocTimerDelegate::expire() {
+ // keeping a copy of client pointer to be safe
+ // when timeOutCallback() is called at the end of this
+ // method, *this* obj may be already deleted.
+ LocTimer* client = mClient;
+ // force a stop, which will lead to delete of this obj
+ if (client && client->stop()) {
+ // calling client callback with a pointer save on the stack
+ // only if stop() returns true, i.e. it hasn't been stopped
+ // already.
+ client->timeOutCallback();
+ }
+}
+
+
+/***************************LocTimer methods***************************/
+LocTimer::LocTimer() : mTimer(NULL), mLock(new LocSharedLock()) {
+}
+
+LocTimer::~LocTimer() {
+ stop();
+ if (mLock) {
+ mLock->drop();
+ mLock = NULL;
+ }
+}
+
+bool LocTimer::start(unsigned int timeOutInMs, bool wakeOnExpire) {
+ bool success = false;
+ mLock->lock();
+ if (!mTimer) {
+ struct timespec futureTime;
+ clock_gettime(CLOCK_BOOTTIME, &futureTime);
+ futureTime.tv_sec += timeOutInMs / 1000;
+ futureTime.tv_nsec += (timeOutInMs % 1000) * 1000000;
+ if (futureTime.tv_nsec >= 1000000000) {
+ futureTime.tv_sec += futureTime.tv_nsec / 1000000000;
+ futureTime.tv_nsec %= 1000000000;
+ }
+
+ LocTimerContainer* container;
+ container = LocTimerContainer::get(wakeOnExpire);
+ if (NULL != container) {
+ mTimer = new LocTimerDelegate(*this, futureTime, container);
+ // if mTimer is non 0, success should be 0; or vice versa
+ }
+ success = (NULL != mTimer);
+ }
+ mLock->unlock();
+ return success;
+}
+
+bool LocTimer::stop() {
+ bool success = false;
+ mLock->lock();
+ if (mTimer) {
+ LocTimerDelegate* timer = mTimer;
+ mTimer = NULL;
+ if (timer) {
+ timer->destroyLocked();
+ success = true;
+ }
+ }
+ mLock->unlock();
+ return success;
+}
+
+/***************************LocTimerWrapper methods***************************/
+//////////////////////////////////////////////////////////////////////////
+// This section below wraps for the C style APIs
+//////////////////////////////////////////////////////////////////////////
+class LocTimerWrapper : public LocTimer {
+ loc_timer_callback mCb;
+ void* mCallerData;
+ LocTimerWrapper* mMe;
+ static pthread_mutex_t mMutex;
+ inline ~LocTimerWrapper() { mCb = NULL; mMe = NULL; }
+public:
+ inline LocTimerWrapper(loc_timer_callback cb, void* callerData) :
+ mCb(cb), mCallerData(callerData), mMe(this) {
+ }
+ void destroy() {
+ pthread_mutex_lock(&mMutex);
+ if (NULL != mCb && this == mMe) {
+ delete this;
+ }
+ pthread_mutex_unlock(&mMutex);
+ }
+ virtual void timeOutCallback() {
+ loc_timer_callback cb = mCb;
+ void* callerData = mCallerData;
+ if (cb) {
+ cb(callerData, 0);
+ }
+ destroy();
+ }
+};
+
+pthread_mutex_t LocTimerWrapper::mMutex = PTHREAD_MUTEX_INITIALIZER;
+
+void* loc_timer_start(uint64_t msec, loc_timer_callback cb_func,
+ void *caller_data, bool wake_on_expire)
+{
+ LocTimerWrapper* locTimerWrapper = NULL;
+
+ if (cb_func) {
+ locTimerWrapper = new LocTimerWrapper(cb_func, caller_data);
+
+ if (locTimerWrapper) {
+ locTimerWrapper->start(msec, wake_on_expire);
+ }
+ }
+
+ return locTimerWrapper;
+}
+
+void loc_timer_stop(void*& handle)
+{
+ if (handle) {
+ LocTimerWrapper* locTimerWrapper = (LocTimerWrapper*)(handle);
+ locTimerWrapper->destroy();
+ handle = NULL;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+// This section above wraps for the C style APIs
+//////////////////////////////////////////////////////////////////////////
+
+#ifdef __LOC_DEBUG__
+
+double getDeltaSeconds(struct timespec from, struct timespec to) {
+ return (double)to.tv_sec + (double)to.tv_nsec / 1000000000
+ - from.tv_sec - (double)from.tv_nsec / 1000000000;
+}
+
+struct timespec getNow() {
+ struct timespec now;
+ clock_gettime(CLOCK_BOOTTIME, &now);
+ return now;
+}
+
+class LocTimerTest : public LocTimer, public LocRankable {
+ int mTimeOut;
+ const struct timespec mTimeOfBirth;
+ inline struct timespec getTimerWrapper(int timeout) {
+ struct timespec now;
+ clock_gettime(CLOCK_BOOTTIME, &now);
+ now.tv_sec += timeout;
+ return now;
+ }
+public:
+ inline LocTimerTest(int timeout) : LocTimer(), LocRankable(),
+ mTimeOut(timeout), mTimeOfBirth(getTimerWrapper(0)) {}
+ inline virtual int ranks(LocRankable& rankable) {
+ LocTimerTest* timer = dynamic_cast<LocTimerTest*>(&rankable);
+ return timer->mTimeOut - mTimeOut;
+ }
+ inline virtual void timeOutCallback() {
+ printf("timeOutCallback() - ");
+ deviation();
+ }
+ double deviation() {
+ struct timespec now = getTimerWrapper(0);
+ double delta = getDeltaSeconds(mTimeOfBirth, now);
+ printf("%lf: %lf\n", delta, delta * 100 / mTimeOut);
+ return delta / mTimeOut;
+ }
+};
+
+// For Linux command line testing:
+// compilation:
+// g++ -D__LOC_HOST_DEBUG__ -D__LOC_DEBUG__ -g -I. -I../../../../system/core/include -o LocHeap.o LocHeap.cpp
+// g++ -D__LOC_HOST_DEBUG__ -D__LOC_DEBUG__ -g -std=c++0x -I. -I../../../../system/core/include -lpthread -o LocThread.o LocThread.cpp
+// g++ -D__LOC_HOST_DEBUG__ -D__LOC_DEBUG__ -g -I. -I../../../../system/core/include -o LocTimer.o LocTimer.cpp
+int main(int argc, char** argv) {
+ struct timespec timeOfStart=getNow();
+ srand(time(NULL));
+ int tries = atoi(argv[1]);
+ int checks = tries >> 3;
+ LocTimerTest** timerArray = new LocTimerTest*[tries];
+ memset(timerArray, NULL, tries);
+
+ for (int i = 0; i < tries; i++) {
+ int r = rand() % tries;
+ LocTimerTest* timer = new LocTimerTest(r);
+ if (timerArray[r]) {
+ if (!timer->stop()) {
+ printf("%lf:\n", getDeltaSeconds(timeOfStart, getNow()));
+ printf("ERRER: %dth timer, id %d, not running when it should be\n", i, r);
+ exit(0);
+ } else {
+ printf("stop() - %d\n", r);
+ delete timer;
+ timerArray[r] = NULL;
+ }
+ } else {
+ if (!timer->start(r, false)) {
+ printf("%lf:\n", getDeltaSeconds(timeOfStart, getNow()));
+ printf("ERRER: %dth timer, id %d, running when it should not be\n", i, r);
+ exit(0);
+ } else {
+ printf("stop() - %d\n", r);
+ timerArray[r] = timer;
+ }
+ }
+ }
+
+ for (int i = 0; i < tries; i++) {
+ if (timerArray[i]) {
+ if (!timerArray[i]->stop()) {
+ printf("%lf:\n", getDeltaSeconds(timeOfStart, getNow()));
+ printf("ERRER: %dth timer, not running when it should be\n", i);
+ exit(0);
+ } else {
+ printf("stop() - %d\n", i);
+ delete timerArray[i];
+ timerArray[i] = NULL;
+ }
+ }
+ }
+
+ delete[] timerArray;
+
+ return 0;
+}
+
+#endif