You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
749 lines
27 KiB
749 lines
27 KiB
/* 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 <stdio.h> |
|
#include <stdlib.h> |
|
#include <time.h> |
|
#include <errno.h> |
|
#include <loc_timer.h> |
|
#include <sys/timerfd.h> |
|
#include <sys/epoll.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 = {0}; |
|
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 = {0}; |
|
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
|
|
|