1 /* Copyright (c) 2015, 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 <stdio.h>
31 #include <stdlib.h>
32 #include <time.h>
33 #include <errno.h>
34 #include <loc_timer.h>
35 #include <sys/timerfd.h>
36 #include <sys/epoll.h>
37 #include <unistd.h>
38 #include <LocTimer.h>
39 #include <LocHeap.h>
40 #include <LocThread.h>
41 #include <LocSharedLock.h>
42 #include <MsgTask.h>
43
44 #ifdef __HOST_UNIT_TEST__
45 #define EPOLLWAKEUP 0
46 #define CLOCK_BOOTTIME CLOCK_MONOTONIC
47 #define CLOCK_BOOTTIME_ALARM CLOCK_MONOTONIC
48 #endif
49
50 /*
51 There are implementations of 5 classes in this file:
52 LocTimer, LocTimerDelegate, LocTimerContainer, LocTimerPollTask, LocTimerWrapper
53
54 LocTimer - client front end, interface for client to start / stop timers, also
55 to provide a callback.
56 LocTimerDelegate - an internal timer entity, which also is a LocRankable obj.
57 Its life cycle is different than that of LocTimer. It gets
58 created when LocTimer::start() is called, and gets deleted
59 when it expires or clients calls the hosting LocTimer obj's
60 stop() method. When a LocTimerDelegate obj is ticking, it
61 stays in the corresponding LocTimerContainer. When expired
62 or stopped, the obj is removed from the container. Since it
63 is also a LocRankable obj, and LocTimerContainer also is a
64 heap, its ranks() implementation decides where it is placed
65 in the heap.
66 LocTimerContainer - core of the timer service. It is a container (derived from
67 LocHeap) for LocTimerDelegate (implements LocRankable) objs.
68 There are 2 of such containers, one for sw timers (or Linux
69 timers) one for hw timers (or Linux alarms). It adds one of
70 each (those that expire the soonest) to kernel via services
71 provided by LocTimerPollTask. All the heap management on the
72 LocTimerDelegate objs are done in the MsgTask context, such
73 that synchronization is ensured.
74 LocTimerPollTask - is a class that wraps timerfd and epoll POXIS APIs. It also
75 both implements LocRunnalbe with epoll_wait() in the run()
76 method. It is also a LocThread client, so as to loop the run
77 method.
78 LocTimerWrapper - a LocTimer client itself, to implement the existing C API with
79 APIs, loc_timer_start() and loc_timer_stop().
80
81 */
82
83 class LocTimerPollTask;
84
85 // This is a multi-functaional class that:
86 // * extends the LocHeap class for the detection of head update upon add / remove
87 // events. When that happens, soonest time out changes, so timerfd needs update.
88 // * contains the timers, and add / remove them into the heap
89 // * provides and maps 2 of such containers, one for timers (or mSwTimers), one
90 // for alarms (or mHwTimers);
91 // * provides a polling thread;
92 // * provides a MsgTask thread for synchronized add / remove / timer client callback.
93 class LocTimerContainer : public LocHeap {
94 // mutex to synchronize getters of static members
95 static pthread_mutex_t mMutex;
96 // Container of timers
97 static LocTimerContainer* mSwTimers;
98 // Container of alarms
99 static LocTimerContainer* mHwTimers;
100 // Msg task to provider msg Q, sender and reader.
101 static MsgTask* mMsgTask;
102 // Poll task to provide epoll call and threading to poll.
103 static LocTimerPollTask* mPollTask;
104 // timer / alarm fd
105 int mDevFd;
106 // ctor
107 LocTimerContainer(bool wakeOnExpire);
108 // dtor
109 ~LocTimerContainer();
110 static MsgTask* getMsgTaskLocked();
111 static LocTimerPollTask* getPollTaskLocked();
112 // extend LocHeap and pop if the top outRanks input
113 LocTimerDelegate* popIfOutRanks(LocTimerDelegate& timer);
114 // update the timer POSIX calls with updated soonest timer spec
115 void updateSoonestTime(LocTimerDelegate* priorTop);
116
117 public:
118 // factory method to control the creation of mSwTimers / mHwTimers
119 static LocTimerContainer* get(bool wakeOnExpire);
120
121 LocTimerDelegate* getSoonestTimer();
122 int getTimerFd();
123 // add a timer / alarm obj into the container
124 void add(LocTimerDelegate& timer);
125 // remove a timer / alarm obj from the container
126 void remove(LocTimerDelegate& timer);
127 // handling of timer / alarm expiration
128 void expire();
129 };
130
131 // This class implements the polling thread that epolls imer / alarm fds.
132 // The LocRunnable::run() contains the actual polling. The other methods
133 // will be run in the caller's thread context to add / remove timer / alarm
134 // fds the kernel, while the polling is blocked on epoll_wait() call.
135 // Since the design is that we have maximally 2 polls, one for all the
136 // timers; one for all the alarms, we will poll at most on 2 fds. But it
137 // is possile that all we have are only timers or alarms at one time, so we
138 // allow dynamically add / remove fds we poll on. The design decision of
139 // having 1 fd per container of timer / alarm is such that, we may not need
140 // to make a system call each time a timer / alarm is added / removed, unless
141 // that changes the "soonest" time out of that of all the timers / alarms.
142 class LocTimerPollTask : public LocRunnable {
143 // the epoll fd
144 const int mFd;
145 // the thread that calls run() method
146 LocThread* mThread;
147 friend class LocThreadDelegate;
148 // dtor
149 ~LocTimerPollTask();
150 public:
151 // ctor
152 LocTimerPollTask();
153 // this obj will be deleted once thread is deleted
154 void destroy();
155 // add a container of timers. Each contain has a unique device fd, i.e.
156 // either timer or alarm fd, and a heap of timers / alarms. It is expected
157 // that container would have written to the device fd with the soonest
158 // time out value in the heap at the time of calling this method. So all
159 // this method does is to add the fd of the input container to the poll
160 // and also add the pointer of the container to the event data ptr, such
161 // when poll_wait wakes up on events, we know who is the owner of the fd.
162 void addPoll(LocTimerContainer& timerContainer);
163 // remove a fd that is assciated with a container. The expectation is that
164 // the atual timer would have been removed from the container.
165 void removePoll(LocTimerContainer& timerContainer);
166 // The polling thread context will call this method. This is where
167 // epoll_wait() is blocking and waiting for events..
168 virtual bool run();
169 };
170
171 // Internal class of timer obj. It gets born when client calls LocTimer::start();
172 // and gets deleted when client calls LocTimer::stop() or when the it expire()'s.
173 // This class implements LocRankable::ranks() so that when an obj is added into
174 // the container (of LocHeap), it gets placed in sorted order.
175 class LocTimerDelegate : public LocRankable {
176 friend class LocTimerContainer;
177 friend class LocTimer;
178 LocTimer* mClient;
179 LocSharedLock* mLock;
180 struct timespec mFutureTime;
181 LocTimerContainer* mContainer;
182 // not a complete obj, just ctor for LocRankable comparisons
LocTimerDelegate(struct timespec & delay)183 inline LocTimerDelegate(struct timespec& delay)
184 : mClient(NULL), mLock(NULL), mFutureTime(delay), mContainer(NULL) {}
~LocTimerDelegate()185 inline ~LocTimerDelegate() { if (mLock) { mLock->drop(); mLock = NULL; } }
186 public:
187 LocTimerDelegate(LocTimer& client, struct timespec& futureTime, LocTimerContainer* container);
188 void destroyLocked();
189 // LocRankable virtual method
190 virtual int ranks(LocRankable& rankable);
191 void expire();
getFutureTime()192 inline struct timespec getFutureTime() { return mFutureTime; }
193 };
194
195 /***************************LocTimerContainer methods***************************/
196
197 // Most of these static recources are created on demand. They however are never
198 // destoyed. The theory is that there are processes that link to this util lib
199 // but never use timer, then these resources would never need to be created.
200 // For those processes that do use timer, it will likely also need to every
201 // once in a while. It might be cheaper keeping them around.
202 pthread_mutex_t LocTimerContainer::mMutex = PTHREAD_MUTEX_INITIALIZER;
203 LocTimerContainer* LocTimerContainer::mSwTimers = NULL;
204 LocTimerContainer* LocTimerContainer::mHwTimers = NULL;
205 MsgTask* LocTimerContainer::mMsgTask = NULL;
206 LocTimerPollTask* LocTimerContainer::mPollTask = NULL;
207
208 // ctor - initialize timer heaps
209 // A container for swTimer (timer) is created, when wakeOnExpire is true; or
210 // HwTimer (alarm), when wakeOnExpire is false.
LocTimerContainer(bool wakeOnExpire)211 LocTimerContainer::LocTimerContainer(bool wakeOnExpire) :
212 mDevFd(timerfd_create(wakeOnExpire ? CLOCK_BOOTTIME_ALARM : CLOCK_BOOTTIME, 0)) {
213
214 if ((-1 == mDevFd) && (errno == EINVAL)) {
215 LOC_LOGW("%s: timerfd_create failure, fallback to CLOCK_MONOTONIC - %s",
216 __FUNCTION__, strerror(errno));
217 mDevFd = timerfd_create(CLOCK_MONOTONIC, 0);
218 }
219
220 if (-1 != mDevFd) {
221 // ensure we have the necessary resources created
222 LocTimerContainer::getPollTaskLocked();
223 LocTimerContainer::getMsgTaskLocked();
224 } else {
225 LOC_LOGE("%s: timerfd_create failure - %s", __FUNCTION__, strerror(errno));
226 }
227 }
228
229 // dtor
230 // we do not ever destroy the static resources.
231 inline
~LocTimerContainer()232 LocTimerContainer::~LocTimerContainer() {
233 close(mDevFd);
234 }
235
get(bool wakeOnExpire)236 LocTimerContainer* LocTimerContainer::get(bool wakeOnExpire) {
237 // get the reference of either mHwTimer or mSwTimers per wakeOnExpire
238 LocTimerContainer*& container = wakeOnExpire ? mHwTimers : mSwTimers;
239 // it is cheap to check pointer first than locking mutext unconditionally
240 if (!container) {
241 pthread_mutex_lock(&mMutex);
242 // let's check one more time to be safe
243 if (!container) {
244 container = new LocTimerContainer(wakeOnExpire);
245 // timerfd_create failure
246 if (-1 == container->getTimerFd()) {
247 delete container;
248 container = NULL;
249 }
250 }
251 pthread_mutex_unlock(&mMutex);
252 }
253 return container;
254 }
255
getMsgTaskLocked()256 MsgTask* LocTimerContainer::getMsgTaskLocked() {
257 // it is cheap to check pointer first than locking mutext unconditionally
258 if (!mMsgTask) {
259 mMsgTask = new MsgTask("LocTimerMsgTask", false);
260 }
261 return mMsgTask;
262 }
263
getPollTaskLocked()264 LocTimerPollTask* LocTimerContainer::getPollTaskLocked() {
265 // it is cheap to check pointer first than locking mutext unconditionally
266 if (!mPollTask) {
267 mPollTask = new LocTimerPollTask();
268 }
269 return mPollTask;
270 }
271
272 inline
getSoonestTimer()273 LocTimerDelegate* LocTimerContainer::getSoonestTimer() {
274 return (LocTimerDelegate*)(peek());
275 }
276
277 inline
getTimerFd()278 int LocTimerContainer::getTimerFd() {
279 return mDevFd;
280 }
281
updateSoonestTime(LocTimerDelegate * priorTop)282 void LocTimerContainer::updateSoonestTime(LocTimerDelegate* priorTop) {
283 LocTimerDelegate* curTop = getSoonestTimer();
284
285 // check if top has changed
286 if (curTop != priorTop) {
287 struct itimerspec delay = {0};
288 bool toSetTime = false;
289 // if tree is empty now, we remove poll and disarm timer
290 if (!curTop) {
291 mPollTask->removePoll(*this);
292 // setting the values to disarm timer
293 delay.it_value.tv_sec = 0;
294 delay.it_value.tv_nsec = 0;
295 toSetTime = true;
296 } else if (!priorTop || curTop->outRanks(*priorTop)) {
297 // do this first to avoid race condition, in case settime is called
298 // with too small an interval
299 mPollTask->addPoll(*this);
300 delay.it_value = curTop->getFutureTime();
301 toSetTime = true;
302 }
303 if (toSetTime) {
304 timerfd_settime(getTimerFd(), TFD_TIMER_ABSTIME, &delay, NULL);
305 }
306 }
307 }
308
309 // all the heap management is done in the MsgTask context.
310 inline
add(LocTimerDelegate & timer)311 void LocTimerContainer::add(LocTimerDelegate& timer) {
312 struct MsgTimerPush : public LocMsg {
313 LocTimerContainer* mTimerContainer;
314 LocHeapNode* mTree;
315 LocTimerDelegate* mTimer;
316 inline MsgTimerPush(LocTimerContainer& container, LocTimerDelegate& timer) :
317 LocMsg(), mTimerContainer(&container), mTimer(&timer) {}
318 inline virtual void proc() const {
319 LocTimerDelegate* priorTop = mTimerContainer->getSoonestTimer();
320 mTimerContainer->push((LocRankable&)(*mTimer));
321 mTimerContainer->updateSoonestTime(priorTop);
322 }
323 };
324
325 mMsgTask->sendMsg(new MsgTimerPush(*this, timer));
326 }
327
328 // all the heap management is done in the MsgTask context.
remove(LocTimerDelegate & timer)329 void LocTimerContainer::remove(LocTimerDelegate& timer) {
330 struct MsgTimerRemove : public LocMsg {
331 LocTimerContainer* mTimerContainer;
332 LocTimerDelegate* mTimer;
333 inline MsgTimerRemove(LocTimerContainer& container, LocTimerDelegate& timer) :
334 LocMsg(), mTimerContainer(&container), mTimer(&timer) {}
335 inline virtual void proc() const {
336 LocTimerDelegate* priorTop = mTimerContainer->getSoonestTimer();
337
338 // update soonest timer only if mTimer is actually removed from
339 // mTimerContainer AND mTimer is not priorTop.
340 if (priorTop == ((LocHeap*)mTimerContainer)->remove((LocRankable&)*mTimer)) {
341 // if passing in NULL, we tell updateSoonestTime to update
342 // kernel with the current top timer interval.
343 mTimerContainer->updateSoonestTime(NULL);
344 }
345 // all timers are deleted here, and only here.
346 delete mTimer;
347 }
348 };
349
350 mMsgTask->sendMsg(new MsgTimerRemove(*this, timer));
351 }
352
353 // all the heap management is done in the MsgTask context.
354 // Upon expire, we check and continuously pop the heap until
355 // the top node's timeout is in the future.
expire()356 void LocTimerContainer::expire() {
357 struct MsgTimerExpire : public LocMsg {
358 LocTimerContainer* mTimerContainer;
359 inline MsgTimerExpire(LocTimerContainer& container) :
360 LocMsg(), mTimerContainer(&container) {}
361 inline virtual void proc() const {
362 struct timespec now;
363 // get time spec of now
364 clock_gettime(CLOCK_BOOTTIME, &now);
365 LocTimerDelegate timerOfNow(now);
366 // pop everything in the heap that outRanks now, i.e. has time older than now
367 // and then call expire() on that timer.
368 for (LocTimerDelegate* timer = (LocTimerDelegate*)mTimerContainer->pop();
369 NULL != timer;
370 timer = mTimerContainer->popIfOutRanks(timerOfNow)) {
371 // the timer delegate obj will be deleted before the return of this call
372 timer->expire();
373 }
374 mTimerContainer->updateSoonestTime(NULL);
375 }
376 };
377
378 struct itimerspec delay = {0};
379 timerfd_settime(getTimerFd(), TFD_TIMER_ABSTIME, &delay, NULL);
380 mPollTask->removePoll(*this);
381 mMsgTask->sendMsg(new MsgTimerExpire(*this));
382 }
383
popIfOutRanks(LocTimerDelegate & timer)384 LocTimerDelegate* LocTimerContainer::popIfOutRanks(LocTimerDelegate& timer) {
385 LocTimerDelegate* poppedNode = NULL;
386 if (mTree && !timer.outRanks(*peek())) {
387 poppedNode = (LocTimerDelegate*)(pop());
388 }
389
390 return poppedNode;
391 }
392
393
394 /***************************LocTimerPollTask methods***************************/
395
396 inline
LocTimerPollTask()397 LocTimerPollTask::LocTimerPollTask()
398 : mFd(epoll_create(2)), mThread(new LocThread()) {
399 // before a next call returens, a thread will be created. The run() method
400 // could already be running in parallel. Also, since each of the objs
401 // creates a thread, the container will make sure that there will be only
402 // one of such obj for our timer implementation.
403 if (!mThread->start("LocTimerPollTask", this)) {
404 delete mThread;
405 mThread = NULL;
406 }
407 }
408
409 inline
~LocTimerPollTask()410 LocTimerPollTask::~LocTimerPollTask() {
411 // when fs is closed, epoll_wait() should fail run() should return false
412 // and the spawned thread should exit.
413 close(mFd);
414 }
415
destroy()416 void LocTimerPollTask::destroy() {
417 if (mThread) {
418 LocThread* thread = mThread;
419 mThread = NULL;
420 delete thread;
421 } else {
422 delete this;
423 }
424 }
425
addPoll(LocTimerContainer & timerContainer)426 void LocTimerPollTask::addPoll(LocTimerContainer& timerContainer) {
427 struct epoll_event ev;
428 memset(&ev, 0, sizeof(ev));
429
430 ev.events = EPOLLIN | EPOLLWAKEUP;
431 ev.data.fd = timerContainer.getTimerFd();
432 // it is important that we set this context pointer with the input
433 // timer container this is how we know which container should handle
434 // which expiration.
435 ev.data.ptr = &timerContainer;
436
437 epoll_ctl(mFd, EPOLL_CTL_ADD, timerContainer.getTimerFd(), &ev);
438 }
439
440 inline
removePoll(LocTimerContainer & timerContainer)441 void LocTimerPollTask::removePoll(LocTimerContainer& timerContainer) {
442 epoll_ctl(mFd, EPOLL_CTL_DEL, timerContainer.getTimerFd(), NULL);
443 }
444
445 // The polling thread context will call this method. If run() method needs to
446 // be repetitvely called, it must return true from the previous call.
run()447 bool LocTimerPollTask::run() {
448 struct epoll_event ev[2];
449
450 // we have max 2 descriptors to poll from
451 int fds = epoll_wait(mFd, ev, 2, -1);
452
453 // we pretty much want to continually poll until the fd is closed
454 bool rerun = (fds > 0) || (errno == EINTR);
455
456 if (fds > 0) {
457 // we may have 2 events
458 for (int i = 0; i < fds; i++) {
459 // each fd has a context pointer associated with the right timer container
460 LocTimerContainer* container = (LocTimerContainer*)(ev[i].data.ptr);
461 if (container) {
462 container->expire();
463 } else {
464 epoll_ctl(mFd, EPOLL_CTL_DEL, ev[i].data.fd, NULL);
465 }
466 }
467 }
468
469 // if rerun is true, we are requesting to be scheduled again
470 return rerun;
471 }
472
473 /***************************LocTimerDelegate methods***************************/
474
475 inline
LocTimerDelegate(LocTimer & client,struct timespec & futureTime,LocTimerContainer * container)476 LocTimerDelegate::LocTimerDelegate(LocTimer& client,
477 struct timespec& futureTime,
478 LocTimerContainer* container)
479 : mClient(&client),
480 mLock(mClient->mLock->share()),
481 mFutureTime(futureTime),
482 mContainer(container) {
483 // adding the timer into the container
484 mContainer->add(*this);
485 }
486
487 inline
destroyLocked()488 void LocTimerDelegate::destroyLocked() {
489 // client handle will likely be deleted soon after this
490 // method returns. Nulling this handle so that expire()
491 // won't call the callback on the dead handle any more.
492 mClient = NULL;
493
494 if (mContainer) {
495 LocTimerContainer* container = mContainer;
496 mContainer = NULL;
497 if (container) {
498 container->remove(*this);
499 }
500 } // else we do not do anything. No such *this* can be
501 // created and reached here with mContainer ever been
502 // a non NULL. So *this* must have reached the if clause
503 // once, and we want it reach there only once.
504 }
505
ranks(LocRankable & rankable)506 int LocTimerDelegate::ranks(LocRankable& rankable) {
507 int rank = -1;
508 LocTimerDelegate* timer = (LocTimerDelegate*)(&rankable);
509 if (timer) {
510 // larger time ranks lower!!!
511 // IOW, if input obj has bigger tv_sec/tv_nsec, this obj outRanks higher
512 rank = timer->mFutureTime.tv_sec - mFutureTime.tv_sec;
513 if(0 == rank)
514 {
515 //rank against tv_nsec for msec accuracy
516 rank = (int)(timer->mFutureTime.tv_nsec - mFutureTime.tv_nsec);
517 }
518 }
519 return rank;
520 }
521
522 inline
expire()523 void LocTimerDelegate::expire() {
524 // keeping a copy of client pointer to be safe
525 // when timeOutCallback() is called at the end of this
526 // method, *this* obj may be already deleted.
527 LocTimer* client = mClient;
528 // force a stop, which will lead to delete of this obj
529 if (client && client->stop()) {
530 // calling client callback with a pointer save on the stack
531 // only if stop() returns true, i.e. it hasn't been stopped
532 // already.
533 client->timeOutCallback();
534 }
535 }
536
537
538 /***************************LocTimer methods***************************/
LocTimer()539 LocTimer::LocTimer() : mTimer(NULL), mLock(new LocSharedLock()) {
540 }
541
~LocTimer()542 LocTimer::~LocTimer() {
543 stop();
544 if (mLock) {
545 mLock->drop();
546 mLock = NULL;
547 }
548 }
549
start(unsigned int timeOutInMs,bool wakeOnExpire)550 bool LocTimer::start(unsigned int timeOutInMs, bool wakeOnExpire) {
551 bool success = false;
552 mLock->lock();
553 if (!mTimer) {
554 struct timespec futureTime;
555 clock_gettime(CLOCK_BOOTTIME, &futureTime);
556 futureTime.tv_sec += timeOutInMs / 1000;
557 futureTime.tv_nsec += (timeOutInMs % 1000) * 1000000;
558 if (futureTime.tv_nsec >= 1000000000) {
559 futureTime.tv_sec += futureTime.tv_nsec / 1000000000;
560 futureTime.tv_nsec %= 1000000000;
561 }
562
563 LocTimerContainer* container;
564 container = LocTimerContainer::get(wakeOnExpire);
565 if (NULL != container) {
566 mTimer = new LocTimerDelegate(*this, futureTime, container);
567 // if mTimer is non 0, success should be 0; or vice versa
568 }
569 success = (NULL != mTimer);
570 }
571 mLock->unlock();
572 return success;
573 }
574
stop()575 bool LocTimer::stop() {
576 bool success = false;
577 mLock->lock();
578 if (mTimer) {
579 LocTimerDelegate* timer = mTimer;
580 mTimer = NULL;
581 if (timer) {
582 timer->destroyLocked();
583 success = true;
584 }
585 }
586 mLock->unlock();
587 return success;
588 }
589
590 /***************************LocTimerWrapper methods***************************/
591 //////////////////////////////////////////////////////////////////////////
592 // This section below wraps for the C style APIs
593 //////////////////////////////////////////////////////////////////////////
594 class LocTimerWrapper : public LocTimer {
595 loc_timer_callback mCb;
596 void* mCallerData;
597 LocTimerWrapper* mMe;
598 static pthread_mutex_t mMutex;
~LocTimerWrapper()599 inline ~LocTimerWrapper() { mCb = NULL; mMe = NULL; }
600 public:
LocTimerWrapper(loc_timer_callback cb,void * callerData)601 inline LocTimerWrapper(loc_timer_callback cb, void* callerData) :
602 mCb(cb), mCallerData(callerData), mMe(this) {
603 }
destroy()604 void destroy() {
605 pthread_mutex_lock(&mMutex);
606 if (NULL != mCb && this == mMe) {
607 delete this;
608 }
609 pthread_mutex_unlock(&mMutex);
610 }
timeOutCallback()611 virtual void timeOutCallback() {
612 loc_timer_callback cb = mCb;
613 void* callerData = mCallerData;
614 if (cb) {
615 cb(callerData, 0);
616 }
617 destroy();
618 }
619 };
620
621 pthread_mutex_t LocTimerWrapper::mMutex = PTHREAD_MUTEX_INITIALIZER;
622
loc_timer_start(uint64_t msec,loc_timer_callback cb_func,void * caller_data,bool wake_on_expire)623 void* loc_timer_start(uint64_t msec, loc_timer_callback cb_func,
624 void *caller_data, bool wake_on_expire)
625 {
626 LocTimerWrapper* locTimerWrapper = NULL;
627
628 if (cb_func) {
629 locTimerWrapper = new LocTimerWrapper(cb_func, caller_data);
630
631 if (locTimerWrapper) {
632 locTimerWrapper->start(msec, wake_on_expire);
633 }
634 }
635
636 return locTimerWrapper;
637 }
638
loc_timer_stop(void * & handle)639 void loc_timer_stop(void*& handle)
640 {
641 if (handle) {
642 LocTimerWrapper* locTimerWrapper = (LocTimerWrapper*)(handle);
643 locTimerWrapper->destroy();
644 handle = NULL;
645 }
646 }
647
648 //////////////////////////////////////////////////////////////////////////
649 // This section above wraps for the C style APIs
650 //////////////////////////////////////////////////////////////////////////
651
652 #ifdef __LOC_DEBUG__
653
getDeltaSeconds(struct timespec from,struct timespec to)654 double getDeltaSeconds(struct timespec from, struct timespec to) {
655 return (double)to.tv_sec + (double)to.tv_nsec / 1000000000
656 - from.tv_sec - (double)from.tv_nsec / 1000000000;
657 }
658
getNow()659 struct timespec getNow() {
660 struct timespec now;
661 clock_gettime(CLOCK_BOOTTIME, &now);
662 return now;
663 }
664
665 class LocTimerTest : public LocTimer, public LocRankable {
666 int mTimeOut;
667 const struct timespec mTimeOfBirth;
getTimerWrapper(int timeout)668 inline struct timespec getTimerWrapper(int timeout) {
669 struct timespec now;
670 clock_gettime(CLOCK_BOOTTIME, &now);
671 now.tv_sec += timeout;
672 return now;
673 }
674 public:
LocTimerTest(int timeout)675 inline LocTimerTest(int timeout) : LocTimer(), LocRankable(),
676 mTimeOut(timeout), mTimeOfBirth(getTimerWrapper(0)) {}
ranks(LocRankable & rankable)677 inline virtual int ranks(LocRankable& rankable) {
678 LocTimerTest* timer = dynamic_cast<LocTimerTest*>(&rankable);
679 return timer->mTimeOut - mTimeOut;
680 }
timeOutCallback()681 inline virtual void timeOutCallback() {
682 printf("timeOutCallback() - ");
683 deviation();
684 }
deviation()685 double deviation() {
686 struct timespec now = getTimerWrapper(0);
687 double delta = getDeltaSeconds(mTimeOfBirth, now);
688 printf("%lf: %lf\n", delta, delta * 100 / mTimeOut);
689 return delta / mTimeOut;
690 }
691 };
692
693 // For Linux command line testing:
694 // compilation:
695 // g++ -D__LOC_HOST_DEBUG__ -D__LOC_DEBUG__ -g -I. -I../../../../system/core/include -o LocHeap.o LocHeap.cpp
696 // g++ -D__LOC_HOST_DEBUG__ -D__LOC_DEBUG__ -g -std=c++0x -I. -I../../../../system/core/include -lpthread -o LocThread.o LocThread.cpp
697 // g++ -D__LOC_HOST_DEBUG__ -D__LOC_DEBUG__ -g -I. -I../../../../system/core/include -o LocTimer.o LocTimer.cpp
main(int argc,char ** argv)698 int main(int argc, char** argv) {
699 struct timespec timeOfStart=getNow();
700 srand(time(NULL));
701 int tries = atoi(argv[1]);
702 int checks = tries >> 3;
703 LocTimerTest** timerArray = new LocTimerTest*[tries];
704 memset(timerArray, NULL, tries);
705
706 for (int i = 0; i < tries; i++) {
707 int r = rand() % tries;
708 LocTimerTest* timer = new LocTimerTest(r);
709 if (timerArray[r]) {
710 if (!timer->stop()) {
711 printf("%lf:\n", getDeltaSeconds(timeOfStart, getNow()));
712 printf("ERRER: %dth timer, id %d, not running when it should be\n", i, r);
713 exit(0);
714 } else {
715 printf("stop() - %d\n", r);
716 delete timer;
717 timerArray[r] = NULL;
718 }
719 } else {
720 if (!timer->start(r, false)) {
721 printf("%lf:\n", getDeltaSeconds(timeOfStart, getNow()));
722 printf("ERRER: %dth timer, id %d, running when it should not be\n", i, r);
723 exit(0);
724 } else {
725 printf("stop() - %d\n", r);
726 timerArray[r] = timer;
727 }
728 }
729 }
730
731 for (int i = 0; i < tries; i++) {
732 if (timerArray[i]) {
733 if (!timerArray[i]->stop()) {
734 printf("%lf:\n", getDeltaSeconds(timeOfStart, getNow()));
735 printf("ERRER: %dth timer, not running when it should be\n", i);
736 exit(0);
737 } else {
738 printf("stop() - %d\n", i);
739 delete timerArray[i];
740 timerArray[i] = NULL;
741 }
742 }
743 }
744
745 delete[] timerArray;
746
747 return 0;
748 }
749
750 #endif
751