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