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