blob: 915cf5404da22c58430d49d244f29bfd93824c72 [file] [log] [blame]
Michael Bestas3a0209e2023-05-04 01:15:47 +03001/* Copyright (c) 2015, 2020 The Linux Foundation. All rights reserved.
2 *
3 * Redistribution and use in source and binary forms, with or without
4 * modification, are permitted provided that the following conditions are
5 * met:
6 * * Redistributions of source code must retain the above copyright
7 * notice, this list of conditions and the following disclaimer.
8 * * Redistributions in binary form must reproduce the above
9 * copyright notice, this list of conditions and the following
10 * disclaimer in the documentation and/or other materials provided
11 * with the distribution.
12 * * Neither the name of The Linux Foundation, nor the names of its
13 * contributors may be used to endorse or promote products derived
14 * from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
23 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
25 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
26 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 */
29
30#include <unistd.h>
31#include <stdio.h>
32#include <stdlib.h>
33#include <time.h>
34#include <errno.h>
35#include <sys/timerfd.h>
36#include <sys/epoll.h>
37#include <log_util.h>
38#include <loc_timer.h>
39#include <LocTimer.h>
40#include <LocHeap.h>
41#include <LocThread.h>
42#include <LocSharedLock.h>
43#include <MsgTask.h>
44
45#ifdef __HOST_UNIT_TEST__
46#define EPOLLWAKEUP 0
47#define CLOCK_BOOTTIME CLOCK_MONOTONIC
48#define CLOCK_BOOTTIME_ALARM CLOCK_MONOTONIC
49#endif
50
51namespace loc_util {
52
53/*
54There are implementations of 5 classes in this file:
55LocTimer, LocTimerDelegate, LocTimerContainer, LocTimerPollTask, LocTimerWrapper
56
57LocTimer - client front end, interface for client to start / stop timers, also
58 to provide a callback.
59LocTimerDelegate - an internal timer entity, which also is a LocRankable obj.
60 Its life cycle is different than that of LocTimer. It gets
61 created when LocTimer::start() is called, and gets deleted
62 when it expires or clients calls the hosting LocTimer obj's
63 stop() method. When a LocTimerDelegate obj is ticking, it
64 stays in the corresponding LocTimerContainer. When expired
65 or stopped, the obj is removed from the container. Since it
66 is also a LocRankable obj, and LocTimerContainer also is a
67 heap, its ranks() implementation decides where it is placed
68 in the heap.
69LocTimerContainer - core of the timer service. It is a container (derived from
70 LocHeap) for LocTimerDelegate (implements LocRankable) objs.
71 There are 2 of such containers, one for sw timers (or Linux
72 timers) one for hw timers (or Linux alarms). It adds one of
73 each (those that expire the soonest) to kernel via services
74 provided by LocTimerPollTask. All the heap management on the
75 LocTimerDelegate objs are done in the MsgTask context, such
76 that synchronization is ensured.
77LocTimerPollTask - is a class that wraps timerfd and epoll POXIS APIs. It also
78 both implements LocRunnalbe with epoll_wait() in the run()
79 method. It is also a LocThread client, so as to loop the run
80 method.
81LocTimerWrapper - a LocTimer client itself, to implement the existing C API with
82 APIs, loc_timer_start() and loc_timer_stop().
83
84*/
85
86class LocTimerPollTask;
87
88// This is a multi-functaional class that:
89// * extends the LocHeap class for the detection of head update upon add / remove
90// events. When that happens, soonest time out changes, so timerfd needs update.
91// * contains the timers, and add / remove them into the heap
92// * provides and maps 2 of such containers, one for timers (or mSwTimers), one
93// for alarms (or mHwTimers);
94// * provides a polling thread;
95// * provides a MsgTask thread for synchronized add / remove / timer client callback.
96class LocTimerContainer : public LocHeap {
97 // mutex to synchronize getters of static members
98 static pthread_mutex_t mMutex;
99 // Container of timers
100 static LocTimerContainer* mSwTimers;
101 // Container of alarms
102 static LocTimerContainer* mHwTimers;
103 // Msg task to provider msg Q, sender and reader.
104 static MsgTask* mMsgTask;
105 // Poll task to provide epoll call and threading to poll.
106 static LocTimerPollTask* mPollTask;
107 // timer / alarm fd
108 int mDevFd;
109 // ctor
110 LocTimerContainer(bool wakeOnExpire);
111 // dtor
112 ~LocTimerContainer();
113 static MsgTask* getMsgTaskLocked();
114 static LocTimerPollTask* getPollTaskLocked();
115 // extend LocHeap and pop if the top outRanks input
116 LocTimerDelegate* popIfOutRanks(LocTimerDelegate& timer);
117 // update the timer POSIX calls with updated soonest timer spec
118 void updateSoonestTime(LocTimerDelegate* priorTop);
119
120public:
121 // factory method to control the creation of mSwTimers / mHwTimers
122 static LocTimerContainer* get(bool wakeOnExpire);
123
124 LocTimerDelegate* getSoonestTimer();
125 int getTimerFd();
126 // add a timer / alarm obj into the container
127 void add(LocTimerDelegate& timer);
128 // remove a timer / alarm obj from the container
129 void remove(LocTimerDelegate& timer);
130 // handling of timer / alarm expiration
131 void expire();
132};
133
134class TimerRunnable : public LocRunnable {
135 const int mFd;
136public:
137 inline TimerRunnable(const int fd) : mFd(fd) {}
138 // The method to be implemented by thread clients
139 // and be scheduled by LocThread
140 // This method will be repeated called until it returns false; or
141 // until thread is stopped.
142 virtual bool run() override;
143
144 // The method to wake up the potential blocking thread
145 // no op if not applicable
146 inline virtual void interrupt() { close(mFd); }
147};
148
149// This class implements the polling thread that epolls imer / alarm fds.
150// The LocRunnable::run() contains the actual polling. The other methods
151// will be run in the caller's thread context to add / remove timer / alarm
152// fds the kernel, while the polling is blocked on epoll_wait() call.
153// Since the design is that we have maximally 2 polls, one for all the
154// timers; one for all the alarms, we will poll at most on 2 fds. But it
155// is possile that all we have are only timers or alarms at one time, so we
156// allow dynamically add / remove fds we poll on. The design decision of
157// having 1 fd per container of timer / alarm is such that, we may not need
158// to make a system call each time a timer / alarm is added / removed, unless
159// that changes the "soonest" time out of that of all the timers / alarms.
160class LocTimerPollTask {
161 // the epoll fd
162 const int mFd;
163 // the thread that calls TimerRunnable::run() method, where
164 // epoll_wait() is blocking and waiting for events..
165 LocThread mThread;
166public:
167 // ctor
168 LocTimerPollTask();
169 // dtor
170 ~LocTimerPollTask() = default;
171 // add a container of timers. Each contain has a unique device fd, i.e.
172 // either timer or alarm fd, and a heap of timers / alarms. It is expected
173 // that container would have written to the device fd with the soonest
174 // time out value in the heap at the time of calling this method. So all
175 // this method does is to add the fd of the input container to the poll
176 // and also add the pointer of the container to the event data ptr, such
177 // when poll_wait wakes up on events, we know who is the owner of the fd.
178 void addPoll(LocTimerContainer& timerContainer);
179 // remove a fd that is assciated with a container. The expectation is that
180 // the atual timer would have been removed from the container.
181 void removePoll(LocTimerContainer& timerContainer);
182};
183
184// Internal class of timer obj. It gets born when client calls LocTimer::start();
185// and gets deleted when client calls LocTimer::stop() or when the it expire()'s.
186// This class implements LocRankable::ranks() so that when an obj is added into
187// the container (of LocHeap), it gets placed in sorted order.
188class LocTimerDelegate : public LocRankable {
189 friend class LocTimerContainer;
190 friend class LocTimer;
191 LocTimer* mClient;
192 LocSharedLock* mLock;
193 struct timespec mFutureTime;
194 LocTimerContainer* mContainer;
195 // not a complete obj, just ctor for LocRankable comparisons
196 inline LocTimerDelegate(struct timespec& delay)
197 : mClient(NULL), mLock(NULL), mFutureTime(delay), mContainer(NULL) {}
198 inline ~LocTimerDelegate() { if (mLock) { mLock->drop(); mLock = NULL; } }
199public:
200 LocTimerDelegate(LocTimer& client, struct timespec& futureTime, LocTimerContainer* container);
201 void destroyLocked();
202 // LocRankable virtual method
203 virtual int ranks(LocRankable& rankable);
204 void expire();
205 inline struct timespec getFutureTime() { return mFutureTime; }
206};
207
208/***************************LocTimerContainer methods***************************/
209
210// Most of these static recources are created on demand. They however are never
211// destoyed. The theory is that there are processes that link to this util lib
212// but never use timer, then these resources would never need to be created.
213// For those processes that do use timer, it will likely also need to every
214// once in a while. It might be cheaper keeping them around.
215pthread_mutex_t LocTimerContainer::mMutex = PTHREAD_MUTEX_INITIALIZER;
216LocTimerContainer* LocTimerContainer::mSwTimers = NULL;
217LocTimerContainer* LocTimerContainer::mHwTimers = NULL;
218MsgTask* LocTimerContainer::mMsgTask = NULL;
219LocTimerPollTask* LocTimerContainer::mPollTask = NULL;
220
221// ctor - initialize timer heaps
222// A container for swTimer (timer) is created, when wakeOnExpire is true; or
223// HwTimer (alarm), when wakeOnExpire is false.
224LocTimerContainer::LocTimerContainer(bool wakeOnExpire) :
225 mDevFd(timerfd_create(wakeOnExpire ? CLOCK_BOOTTIME_ALARM : CLOCK_BOOTTIME, 0)) {
226
227 if ((-1 == mDevFd) && (errno == EINVAL)) {
228 LOC_LOGW("%s: timerfd_create failure, fallback to CLOCK_MONOTONIC - %s",
229 __FUNCTION__, strerror(errno));
230 mDevFd = timerfd_create(CLOCK_MONOTONIC, 0);
231 }
232
233 if (-1 != mDevFd) {
234 // ensure we have the necessary resources created
235 LocTimerContainer::getPollTaskLocked();
236 LocTimerContainer::getMsgTaskLocked();
237 } else {
238 LOC_LOGE("%s: timerfd_create failure - %s", __FUNCTION__, strerror(errno));
239 }
240}
241
242// dtor
243// we do not ever destroy the static resources.
244inline
245LocTimerContainer::~LocTimerContainer() {
246 close(mDevFd);
247}
248
249LocTimerContainer* LocTimerContainer::get(bool wakeOnExpire) {
250 // get the reference of either mHwTimer or mSwTimers per wakeOnExpire
251 LocTimerContainer*& container = wakeOnExpire ? mHwTimers : mSwTimers;
252 // it is cheap to check pointer first than locking mutext unconditionally
253 if (!container) {
254 pthread_mutex_lock(&mMutex);
255 // let's check one more time to be safe
256 if (!container) {
257 container = new LocTimerContainer(wakeOnExpire);
258 // timerfd_create failure
259 if (-1 == container->getTimerFd()) {
260 delete container;
261 container = NULL;
262 }
263 }
264 pthread_mutex_unlock(&mMutex);
265 }
266 return container;
267}
268
269MsgTask* LocTimerContainer::getMsgTaskLocked() {
270 // it is cheap to check pointer first than locking mutext unconditionally
271 if (!mMsgTask) {
272 mMsgTask = new MsgTask("LocTimerMsgTask");
273 }
274 return mMsgTask;
275}
276
277LocTimerPollTask* LocTimerContainer::getPollTaskLocked() {
278 // it is cheap to check pointer first than locking mutext unconditionally
279 if (!mPollTask) {
280 mPollTask = new LocTimerPollTask();
281 }
282 return mPollTask;
283}
284
285inline
286LocTimerDelegate* LocTimerContainer::getSoonestTimer() {
287 return (LocTimerDelegate*)(peek());
288}
289
290inline
291int LocTimerContainer::getTimerFd() {
292 return mDevFd;
293}
294
295void LocTimerContainer::updateSoonestTime(LocTimerDelegate* priorTop) {
296 LocTimerDelegate* curTop = getSoonestTimer();
297
298 // check if top has changed
299 if (curTop != priorTop) {
300 struct itimerspec delay;
301 memset(&delay, 0, sizeof(struct itimerspec));
302 bool toSetTime = false;
303 // if tree is empty now, we remove poll and disarm timer
304 if (!curTop) {
305 mPollTask->removePoll(*this);
306 // setting the values to disarm timer
307 delay.it_value.tv_sec = 0;
308 delay.it_value.tv_nsec = 0;
309 toSetTime = true;
310 } else if (!priorTop || curTop->outRanks(*priorTop)) {
311 // do this first to avoid race condition, in case settime is called
312 // with too small an interval
313 mPollTask->addPoll(*this);
314 delay.it_value = curTop->getFutureTime();
315 toSetTime = true;
316 }
317 if (toSetTime) {
318 timerfd_settime(getTimerFd(), TFD_TIMER_ABSTIME, &delay, NULL);
319 }
320 }
321}
322
323// all the heap management is done in the MsgTask context.
324inline
325void LocTimerContainer::add(LocTimerDelegate& timer) {
326 struct MsgTimerPush : public LocMsg {
327 LocTimerContainer* mTimerContainer;
328 LocTimerDelegate* mTimer;
329 inline MsgTimerPush(LocTimerContainer& container, LocTimerDelegate& timer) :
330 LocMsg(), mTimerContainer(&container), mTimer(&timer) {}
331 inline virtual void proc() const {
332 LocTimerDelegate* priorTop = mTimerContainer->getSoonestTimer();
333 mTimerContainer->push((LocRankable&)(*mTimer));
334 mTimerContainer->updateSoonestTime(priorTop);
335 }
336 };
337
338 mMsgTask->sendMsg(new MsgTimerPush(*this, timer));
339}
340
341// all the heap management is done in the MsgTask context.
342void LocTimerContainer::remove(LocTimerDelegate& timer) {
343 struct MsgTimerRemove : public LocMsg {
344 LocTimerContainer* mTimerContainer;
345 LocTimerDelegate* mTimer;
346 inline MsgTimerRemove(LocTimerContainer& container, LocTimerDelegate& timer) :
347 LocMsg(), mTimerContainer(&container), mTimer(&timer) {}
348 inline virtual void proc() const {
349 LocTimerDelegate* priorTop = mTimerContainer->getSoonestTimer();
350
351 // update soonest timer only if mTimer is actually removed from
352 // mTimerContainer AND mTimer is not priorTop.
353 if (priorTop == ((LocHeap*)mTimerContainer)->remove((LocRankable&)*mTimer)) {
354 // if passing in NULL, we tell updateSoonestTime to update
355 // kernel with the current top timer interval.
356 mTimerContainer->updateSoonestTime(NULL);
357 }
358 // all timers are deleted here, and only here.
359 delete mTimer;
360 }
361 };
362
363 mMsgTask->sendMsg(new MsgTimerRemove(*this, timer));
364}
365
366// all the heap management is done in the MsgTask context.
367// Upon expire, we check and continuously pop the heap until
368// the top node's timeout is in the future.
369void LocTimerContainer::expire() {
370 struct MsgTimerExpire : public LocMsg {
371 LocTimerContainer* mTimerContainer;
372 inline MsgTimerExpire(LocTimerContainer& container) :
373 LocMsg(), mTimerContainer(&container) {}
374 inline virtual void proc() const {
375 struct timespec now;
376 // get time spec of now
377 clock_gettime(CLOCK_BOOTTIME, &now);
378 LocTimerDelegate timerOfNow(now);
379 // pop everything in the heap that outRanks now, i.e. has time older than now
380 // and then call expire() on that timer.
381 for (LocTimerDelegate* timer = (LocTimerDelegate*)mTimerContainer->pop();
382 NULL != timer;
383 timer = mTimerContainer->popIfOutRanks(timerOfNow)) {
384 // the timer delegate obj will be deleted before the return of this call
385 timer->expire();
386 }
387 mTimerContainer->updateSoonestTime(NULL);
388 }
389 };
390
391 struct itimerspec delay;
392 memset(&delay, 0, sizeof(struct itimerspec));
393 timerfd_settime(getTimerFd(), TFD_TIMER_ABSTIME, &delay, NULL);
394 mPollTask->removePoll(*this);
395 mMsgTask->sendMsg(new MsgTimerExpire(*this));
396}
397
398LocTimerDelegate* LocTimerContainer::popIfOutRanks(LocTimerDelegate& timer) {
399 LocTimerDelegate* poppedNode = NULL;
400 if (mTree && !timer.outRanks(*peek())) {
401 poppedNode = (LocTimerDelegate*)(pop());
402 }
403
404 return poppedNode;
405}
406
407
408/***************************LocTimerPollTask methods***************************/
409
410inline
411LocTimerPollTask::LocTimerPollTask()
412 : mFd(epoll_create(2)), mThread() {
413 // before a next call returens, a thread will be created. The run() method
414 // could already be running in parallel. Also, since each of the objs
415 // creates a thread, the container will make sure that there will be only
416 // one of such obj for our timer implementation.
417 mThread.start("LocTimerPollTask", std::make_shared<TimerRunnable>(mFd));
418}
419
420void LocTimerPollTask::addPoll(LocTimerContainer& timerContainer) {
421 struct epoll_event ev;
422 memset(&ev, 0, sizeof(ev));
423
424 ev.events = EPOLLIN;
425 ev.data.fd = timerContainer.getTimerFd();
426 // it is important that we set this context pointer with the input
427 // timer container this is how we know which container should handle
428 // which expiration.
429 ev.data.ptr = &timerContainer;
430
431 epoll_ctl(mFd, EPOLL_CTL_ADD, timerContainer.getTimerFd(), &ev);
432}
433
434inline
435void LocTimerPollTask::removePoll(LocTimerContainer& timerContainer) {
436 epoll_ctl(mFd, EPOLL_CTL_DEL, timerContainer.getTimerFd(), NULL);
437}
438
439// The polling thread context will call this method. If run() method needs to
440// be repetitvely called, it must return true from the previous call.
441bool TimerRunnable::run() {
442 struct epoll_event ev[2];
443
444 // we have max 2 descriptors to poll from
445 int fds = epoll_wait(mFd, ev, 2, -1);
446
447 // we pretty much want to continually poll until the fd is closed
448 bool rerun = (fds > 0) || (errno == EINTR);
449
450 if (fds > 0) {
451 // we may have 2 events
452 for (int i = 0; i < fds; i++) {
453 // each fd has a context pointer associated with the right timer container
454 LocTimerContainer* container = (LocTimerContainer*)(ev[i].data.ptr);
455 if (container) {
456 container->expire();
457 } else {
458 epoll_ctl(mFd, EPOLL_CTL_DEL, ev[i].data.fd, NULL);
459 }
460 }
461 }
462
463 // if rerun is true, we are requesting to be scheduled again
464 return rerun;
465}
466
467/***************************LocTimerDelegate methods***************************/
468
469inline
470LocTimerDelegate::LocTimerDelegate(LocTimer& client,
471 struct timespec& futureTime,
472 LocTimerContainer* container)
473 : mClient(&client),
474 mLock(mClient->mLock->share()),
475 mFutureTime(futureTime),
476 mContainer(container) {
477 // adding the timer into the container
478 mContainer->add(*this);
479}
480
481inline
482void LocTimerDelegate::destroyLocked() {
483 // client handle will likely be deleted soon after this
484 // method returns. Nulling this handle so that expire()
485 // won't call the callback on the dead handle any more.
486 mClient = NULL;
487
488 if (mContainer) {
489 LocTimerContainer* container = mContainer;
490 mContainer = NULL;
491 if (container) {
492 container->remove(*this);
493 }
494 } // else we do not do anything. No such *this* can be
495 // created and reached here with mContainer ever been
496 // a non NULL. So *this* must have reached the if clause
497 // once, and we want it reach there only once.
498}
499
500int LocTimerDelegate::ranks(LocRankable& rankable) {
501 int rank = -1;
502 LocTimerDelegate* timer = (LocTimerDelegate*)(&rankable);
503 if (timer) {
504 // larger time ranks lower!!!
505 // IOW, if input obj has bigger tv_sec/tv_nsec, this obj outRanks higher
506 rank = timer->mFutureTime.tv_sec - mFutureTime.tv_sec;
507 if(0 == rank)
508 {
509 //rank against tv_nsec for msec accuracy
510 rank = (int)(timer->mFutureTime.tv_nsec - mFutureTime.tv_nsec);
511 }
512 }
513 return rank;
514}
515
516inline
517void LocTimerDelegate::expire() {
518 // keeping a copy of client pointer to be safe
519 // when timeOutCallback() is called at the end of this
520 // method, *this* obj may be already deleted.
521 LocTimer* client = mClient;
522 // force a stop, which will lead to delete of this obj
523 if (client && client->stop()) {
524 // calling client callback with a pointer save on the stack
525 // only if stop() returns true, i.e. it hasn't been stopped
526 // already.
527 client->timeOutCallback();
528 }
529}
530
531
532/***************************LocTimer methods***************************/
533LocTimer::LocTimer() : mTimer(NULL), mLock(new LocSharedLock()) {
534}
535
536LocTimer::~LocTimer() {
537 stop();
538 if (mLock) {
539 mLock->drop();
540 mLock = NULL;
541 }
542}
543
544bool LocTimer::start(unsigned int timeOutInMs, bool wakeOnExpire) {
545 bool success = false;
546 mLock->lock();
547 if (!mTimer) {
548 struct timespec futureTime;
549 clock_gettime(CLOCK_BOOTTIME, &futureTime);
550 futureTime.tv_sec += timeOutInMs / 1000;
551 futureTime.tv_nsec += (timeOutInMs % 1000) * 1000000;
552 if (futureTime.tv_nsec >= 1000000000) {
553 futureTime.tv_sec += futureTime.tv_nsec / 1000000000;
554 futureTime.tv_nsec %= 1000000000;
555 }
556
557 LocTimerContainer* container;
558 container = LocTimerContainer::get(wakeOnExpire);
559 if (NULL != container) {
560 mTimer = new LocTimerDelegate(*this, futureTime, container);
561 // if mTimer is non 0, success should be 0; or vice versa
562 }
563 success = (NULL != mTimer);
564 }
565 mLock->unlock();
566 return success;
567}
568
569bool LocTimer::stop() {
570 bool success = false;
571 mLock->lock();
572 if (mTimer) {
573 LocTimerDelegate* timer = mTimer;
574 mTimer = NULL;
575 if (timer) {
576 timer->destroyLocked();
577 success = true;
578 }
579 }
580 mLock->unlock();
581 return success;
582}
583
584/***************************LocTimerWrapper methods***************************/
585//////////////////////////////////////////////////////////////////////////
586// This section below wraps for the C style APIs
587//////////////////////////////////////////////////////////////////////////
588class LocTimerWrapper : public LocTimer {
589 loc_timer_callback mCb;
590 void* mCallerData;
591 LocTimerWrapper* mMe;
592 static pthread_mutex_t mMutex;
593 inline ~LocTimerWrapper() { mCb = NULL; mMe = NULL; }
594public:
595 inline LocTimerWrapper(loc_timer_callback cb, void* callerData) :
596 mCb(cb), mCallerData(callerData), mMe(this) {
597 }
598 void destroy() {
599 pthread_mutex_lock(&mMutex);
600 if (NULL != mCb && this == mMe) {
601 delete this;
602 }
603 pthread_mutex_unlock(&mMutex);
604 }
605 virtual void timeOutCallback() {
606 loc_timer_callback cb = mCb;
607 void* callerData = mCallerData;
608 if (cb) {
609 cb(callerData, 0);
610 }
611 destroy();
612 }
613};
614
615} // namespace loc_util
616
617//////////////////////////////////////////////////////////////////////////
618// This section below wraps for the C style APIs
619//////////////////////////////////////////////////////////////////////////
620
621using loc_util::LocTimerWrapper;
622
623pthread_mutex_t LocTimerWrapper::mMutex = PTHREAD_MUTEX_INITIALIZER;
624
625void* loc_timer_start(uint64_t msec, loc_timer_callback cb_func,
626 void *caller_data, bool wake_on_expire)
627{
628 LocTimerWrapper* locTimerWrapper = NULL;
629
630 if (cb_func) {
631 locTimerWrapper = new LocTimerWrapper(cb_func, caller_data);
632
633 if (locTimerWrapper) {
634 locTimerWrapper->start(msec, wake_on_expire);
635 }
636 }
637
638 return locTimerWrapper;
639}
640
641void loc_timer_stop(void*& handle)
642{
643 if (handle) {
644 LocTimerWrapper* locTimerWrapper = (LocTimerWrapper*)(handle);
645 locTimerWrapper->destroy();
646 handle = NULL;
647 }
648}