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