sm8450-common: Import gps from LA.VENDOR.1.0.r1-12800-WAIPIO.0
Change-Id: Ia76265a8c3326f2b5f9f260ce98807276af80ee3
This commit is contained in:
648
gps/utils/LocTimer.cpp
Normal file
648
gps/utils/LocTimer.cpp
Normal file
@@ -0,0 +1,648 @@
|
||||
/* Copyright (c) 2015, 2020-2021 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;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user