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 182 inline LocTimerDelegate(struct timespec& delay) 183 : mClient(NULL), mLock(NULL), mFutureTime(delay), mContainer(NULL) {} 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(); 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. 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 231 LocTimerContainer::~LocTimerContainer() { 232 close(mDevFd); 233 } 234 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 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 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 272 LocTimerDelegate* LocTimerContainer::getSoonestTimer() { 273 return (LocTimerDelegate*)(peek()); 274 } 275 276 inline 277 int LocTimerContainer::getTimerFd() { 278 return mDevFd; 279 } 280 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 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. 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. 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 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 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 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 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 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 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. 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 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 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 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/tv_nsec, this obj outRanks higher 513 rank = timer->mFutureTime.tv_sec - mFutureTime.tv_sec; 514 if(0 == rank) 515 { 516 //rank against tv_nsec for msec accuracy 517 rank = (int)(timer->mFutureTime.tv_nsec - mFutureTime.tv_nsec); 518 } 519 } 520 return rank; 521 } 522 523 inline 524 void LocTimerDelegate::expire() { 525 // keeping a copy of client pointer to be safe 526 // when timeOutCallback() is called at the end of this 527 // method, *this* obj may be already deleted. 528 LocTimer* client = mClient; 529 // force a stop, which will lead to delete of this obj 530 if (client && client->stop()) { 531 // calling client callback with a pointer save on the stack 532 // only if stop() returns true, i.e. it hasn't been stopped 533 // already. 534 client->timeOutCallback(); 535 } 536 } 537 538 539 /***************************LocTimer methods***************************/ 540 LocTimer::LocTimer() : mTimer(NULL), mLock(new LocSharedLock()) { 541 } 542 543 LocTimer::~LocTimer() { 544 stop(); 545 if (mLock) { 546 mLock->drop(); 547 mLock = NULL; 548 } 549 } 550 551 bool LocTimer::start(unsigned int timeOutInMs, bool wakeOnExpire) { 552 bool success = false; 553 mLock->lock(); 554 if (!mTimer) { 555 struct timespec futureTime; 556 clock_gettime(CLOCK_BOOTTIME, &futureTime); 557 futureTime.tv_sec += timeOutInMs / 1000; 558 futureTime.tv_nsec += (timeOutInMs % 1000) * 1000000; 559 if (futureTime.tv_nsec >= 1000000000) { 560 futureTime.tv_sec += futureTime.tv_nsec / 1000000000; 561 futureTime.tv_nsec %= 1000000000; 562 } 563 564 LocTimerContainer* container; 565 container = LocTimerContainer::get(wakeOnExpire); 566 if (NULL != container) { 567 mTimer = new LocTimerDelegate(*this, futureTime, container); 568 // if mTimer is non 0, success should be 0; or vice versa 569 } 570 success = (NULL != mTimer); 571 } 572 mLock->unlock(); 573 return success; 574 } 575 576 bool LocTimer::stop() { 577 bool success = false; 578 mLock->lock(); 579 if (mTimer) { 580 LocTimerDelegate* timer = mTimer; 581 mTimer = NULL; 582 if (timer) { 583 timer->destroyLocked(); 584 success = true; 585 } 586 } 587 mLock->unlock(); 588 return success; 589 } 590 591 /***************************LocTimerWrapper methods***************************/ 592 ////////////////////////////////////////////////////////////////////////// 593 // This section below wraps for the C style APIs 594 ////////////////////////////////////////////////////////////////////////// 595 class LocTimerWrapper : public LocTimer { 596 loc_timer_callback mCb; 597 void* mCallerData; 598 LocTimerWrapper* mMe; 599 static pthread_mutex_t mMutex; 600 inline ~LocTimerWrapper() { mCb = NULL; mMe = NULL; } 601 public: 602 inline LocTimerWrapper(loc_timer_callback cb, void* callerData) : 603 mCb(cb), mCallerData(callerData), mMe(this) { 604 } 605 void destroy() { 606 pthread_mutex_lock(&mMutex); 607 if (NULL != mCb && this == mMe) { 608 delete this; 609 } 610 pthread_mutex_unlock(&mMutex); 611 } 612 virtual void timeOutCallback() { 613 loc_timer_callback cb = mCb; 614 void* callerData = mCallerData; 615 if (cb) { 616 cb(callerData, 0); 617 } 618 destroy(); 619 } 620 }; 621 622 pthread_mutex_t LocTimerWrapper::mMutex = PTHREAD_MUTEX_INITIALIZER; 623 624 void* loc_timer_start(uint64_t msec, loc_timer_callback cb_func, 625 void *caller_data, bool wake_on_expire) 626 { 627 LocTimerWrapper* locTimerWrapper = NULL; 628 629 if (cb_func) { 630 locTimerWrapper = new LocTimerWrapper(cb_func, caller_data); 631 632 if (locTimerWrapper) { 633 locTimerWrapper->start(msec, wake_on_expire); 634 } 635 } 636 637 return locTimerWrapper; 638 } 639 640 void loc_timer_stop(void*& handle) 641 { 642 if (handle) { 643 LocTimerWrapper* locTimerWrapper = (LocTimerWrapper*)(handle); 644 locTimerWrapper->destroy(); 645 handle = NULL; 646 } 647 } 648 649 ////////////////////////////////////////////////////////////////////////// 650 // This section above wraps for the C style APIs 651 ////////////////////////////////////////////////////////////////////////// 652 653 #ifdef __LOC_DEBUG__ 654 655 double getDeltaSeconds(struct timespec from, struct timespec to) { 656 return (double)to.tv_sec + (double)to.tv_nsec / 1000000000 657 - from.tv_sec - (double)from.tv_nsec / 1000000000; 658 } 659 660 struct timespec getNow() { 661 struct timespec now; 662 clock_gettime(CLOCK_BOOTTIME, &now); 663 return now; 664 } 665 666 class LocTimerTest : public LocTimer, public LocRankable { 667 int mTimeOut; 668 const struct timespec mTimeOfBirth; 669 inline struct timespec getTimerWrapper(int timeout) { 670 struct timespec now; 671 clock_gettime(CLOCK_BOOTTIME, &now); 672 now.tv_sec += timeout; 673 return now; 674 } 675 public: 676 inline LocTimerTest(int timeout) : LocTimer(), LocRankable(), 677 mTimeOut(timeout), mTimeOfBirth(getTimerWrapper(0)) {} 678 inline virtual int ranks(LocRankable& rankable) { 679 LocTimerTest* timer = dynamic_cast<LocTimerTest*>(&rankable); 680 return timer->mTimeOut - mTimeOut; 681 } 682 inline virtual void timeOutCallback() { 683 printf("timeOutCallback() - "); 684 deviation(); 685 } 686 double deviation() { 687 struct timespec now = getTimerWrapper(0); 688 double delta = getDeltaSeconds(mTimeOfBirth, now); 689 printf("%lf: %lf\n", delta, delta * 100 / mTimeOut); 690 return delta / mTimeOut; 691 } 692 }; 693 694 // For Linux command line testing: 695 // compilation: 696 // g++ -D__LOC_HOST_DEBUG__ -D__LOC_DEBUG__ -g -I. -I../../../../system/core/include -o LocHeap.o LocHeap.cpp 697 // g++ -D__LOC_HOST_DEBUG__ -D__LOC_DEBUG__ -g -std=c++0x -I. -I../../../../system/core/include -lpthread -o LocThread.o LocThread.cpp 698 // g++ -D__LOC_HOST_DEBUG__ -D__LOC_DEBUG__ -g -I. -I../../../../system/core/include -o LocTimer.o LocTimer.cpp 699 int main(int argc, char** argv) { 700 struct timespec timeOfStart=getNow(); 701 srand(time(NULL)); 702 int tries = atoi(argv[1]); 703 int checks = tries >> 3; 704 LocTimerTest** timerArray = new LocTimerTest*[tries]; 705 memset(timerArray, NULL, tries); 706 707 for (int i = 0; i < tries; i++) { 708 int r = rand() % tries; 709 LocTimerTest* timer = new LocTimerTest(r); 710 if (timerArray[r]) { 711 if (!timer->stop()) { 712 printf("%lf:\n", getDeltaSeconds(timeOfStart, getNow())); 713 printf("ERRER: %dth timer, id %d, not running when it should be\n", i, r); 714 exit(0); 715 } else { 716 printf("stop() - %d\n", r); 717 delete timer; 718 timerArray[r] = NULL; 719 } 720 } else { 721 if (!timer->start(r, false)) { 722 printf("%lf:\n", getDeltaSeconds(timeOfStart, getNow())); 723 printf("ERRER: %dth timer, id %d, running when it should not be\n", i, r); 724 exit(0); 725 } else { 726 printf("stop() - %d\n", r); 727 timerArray[r] = timer; 728 } 729 } 730 } 731 732 for (int i = 0; i < tries; i++) { 733 if (timerArray[i]) { 734 if (!timerArray[i]->stop()) { 735 printf("%lf:\n", getDeltaSeconds(timeOfStart, getNow())); 736 printf("ERRER: %dth timer, not running when it should be\n", i); 737 exit(0); 738 } else { 739 printf("stop() - %d\n", i); 740 delete timerArray[i]; 741 timerArray[i] = NULL; 742 } 743 } 744 } 745 746 delete[] timerArray; 747 748 return 0; 749 } 750 751 #endif 752