75da4c0eee
Change-Id: I134d6418f1357a4d7ce37358481d6b765d93e1a5
649 lines
24 KiB
C++
649 lines
24 KiB
C++
/* Copyright (c) 2015, 2020 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
|
|
|
|
namespace loc_util {
|
|
|
|
/*
|
|
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();
|
|
};
|
|
|
|
class TimerRunnable : public LocRunnable {
|
|
const int mFd;
|
|
public:
|
|
inline TimerRunnable(const int fd) : mFd(fd) {}
|
|
// The method to be implemented by thread clients
|
|
// and be scheduled by LocThread
|
|
// This method will be repeated called until it returns false; or
|
|
// until thread is stopped.
|
|
virtual bool run() override;
|
|
|
|
// The method to wake up the potential blocking thread
|
|
// no op if not applicable
|
|
inline virtual void interrupt() { close(mFd); }
|
|
};
|
|
|
|
// 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 {
|
|
// the epoll fd
|
|
const int mFd;
|
|
// the thread that calls TimerRunnable::run() method, where
|
|
// epoll_wait() is blocking and waiting for events..
|
|
LocThread mThread;
|
|
public:
|
|
// ctor
|
|
LocTimerPollTask();
|
|
// dtor
|
|
~LocTimerPollTask() = default;
|
|
// 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);
|
|
};
|
|
|
|
// 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");
|
|
}
|
|
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;
|
|
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() {
|
|
// 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.
|
|
mThread.start("LocTimerPollTask", std::make_shared<TimerRunnable>(mFd));
|
|
}
|
|
|
|
void LocTimerPollTask::addPoll(LocTimerContainer& timerContainer) {
|
|
struct epoll_event ev;
|
|
memset(&ev, 0, sizeof(ev));
|
|
|
|
ev.events = EPOLLIN;
|
|
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 TimerRunnable::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();
|
|
}
|
|
};
|
|
|
|
} // namespace loc_util
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// This section below wraps for the C style APIs
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
using loc_util::LocTimerWrapper;
|
|
|
|
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;
|
|
}
|
|
}
|