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