1/* 2 File: CTimer.cpp 3 4 Contains: Timer object 5 6 7*/ 8 9#include "CTimer.h" 10#include "IrDALog.h" 11 12#if defined(hasTracing) && defined(hasCTimerTracing) 13 14enum CTimerTraceCodes 15{ 16 kLogNew = 1, 17 kLogInit, 18 kLogFree1, 19 kLogFree2, 20 kStartTimerDelay, 21 kStartTimerSignal, 22 23 kLogStopTimer1, 24 kLogStopTimer2, 25 kLogStopTimer3, 26 kLogStopTimer4, 27 kLogStopTimer5, 28 29 kLogTimeout1, 30 kLogTimeout2, 31 kLogTimeout3, 32 kLogTimeout4, 33 kLogTimeout5, 34 kLogGrimReaper 35}; 36 37static 38EventTraceCauseDesc TraceEvents[] = { 39 {kLogNew, "CTimer: new, obj="}, 40 {kLogInit, "CTimer: init, fTimerSrc="}, 41 {kLogFree1, "CTimer: free, obj="}, 42 {kLogFree2, "CTimer: free, fTimerSrc="}, 43 {kStartTimerDelay, "CTimer: Start timer, delay="}, 44 {kStartTimerSignal, "CTimer: Start timer, signal=, new retain="}, 45 46 {kLogStopTimer1, "CTimer: Stop timer, obj="}, 47 {kLogStopTimer2, "CTimer: Stop timer, idle so nop. retain stays="}, 48 {kLogStopTimer3, "CTimer: Stop timer, cancel worked, retain before release="}, 49 {kLogStopTimer4, "CTimer: Stop timer, cancel failed, retain still="}, 50 {kLogStopTimer5, "CTimer: Stop timer, exit, retain count="}, 51 52 {kLogTimeout1, "CTimer: timeout called, obj="}, 53 {kLogTimeout2, "CTimer: timeout called, retain="}, 54 {kLogTimeout3, "CTimer: timeout called, owner="}, 55 {kLogTimeout4, "CTimer: timeout called, sender="}, 56 {kLogTimeout5, "CTimer: timeout back from calling owner"}, 57 {kLogGrimReaper, "CTimer: grim reaper"} 58 59}; 60 #define XTRACE(x, y, z) IrDALogAdd ( x, y, (uintptr_t)z & 0xffff, TraceEvents, true ) 61#else 62 #define XTRACE(x, y, z) ((void)0) 63#endif 64 65 66#define super OSObject 67 OSDefineMetaClassAndStructors(CTimer, OSObject); 68 69CTimer * 70CTimer::cTimer(IOWorkLoop *work, OSObject *owner, Action callback) 71{ 72 CTimer * obj = new CTimer; 73 74 XTRACE(kLogNew, 0, obj); 75 76 if (obj && !obj->init(work, owner, callback)) { 77 obj->release(); 78 obj = nil; 79 } 80 return obj; 81} 82 83Boolean CTimer::init(IOWorkLoop *work, OSObject *owner, Action callback) 84{ 85 IOReturn rc; 86 87 fTimerSrc = nil; 88 89 require(work, Failed); 90 require(owner, Failed); 91 require(callback, Failed); 92 93 if (!super::init()) return false; 94 95 fOwner = owner; // save backpointer 96 fCallback = callback; // save the real callback, we're going to get it first 97 fWorkLoop = work; // save workloop for free later 98// fTimerSrc = IOTimerEventSource::timerEventSource(owner, callback); // make a timer 99 fTimerSrc = IrDATimerEventSource::timerEventSource(this, &CTimer::timeout); 100 require(fTimerSrc, Failed); 101 102 fBusy = false; 103 104 fGrimReaper = thread_call_allocate(grim_reaper, this); 105 require(fGrimReaper, Failed); 106 107 rc = work->addEventSource(fTimerSrc); 108 require(rc == kIOReturnSuccess, Failed); 109 110 XTRACE(kLogInit, 0, fTimerSrc); 111 return true; 112 113Failed: 114 return false; 115} 116 117// 118// free - this should now be impossible to have invoked while a timer is pending. 119// 120 121void CTimer::free() 122{ 123 XTRACE(kLogFree1, 0, this); 124 Boolean ok; 125 126 check(fBusy == false); 127 128 if (fGrimReaper) { 129 ok = thread_call_cancel(fGrimReaper); 130 check(ok == false); // should *never* have work! 131 thread_call_free(fGrimReaper); 132 fGrimReaper = nil; 133 } 134 135 if (fTimerSrc) { 136 XTRACE(kLogFree2, 0, fTimerSrc); 137 ok = fTimerSrc->SafeCancelTimeout(); 138 check(ok == false); // should *not* have worked (no timer pending) 139 140 fWorkLoop->removeEventSource(fTimerSrc); 141 142 fTimerSrc->release(); 143 fTimerSrc = nil; 144 } 145 146 super::free(); 147} 148 149// 150// Start a timer to fire up after a while. 151// Note the retain on self to prevent zombie 152// timers getting invoked after they've been freed. 153// 154 155void CTimer::StartTimer(TTimeout delay, UInt32 sig) 156{ 157 IOReturn rc; 158 159 require(fTimerSrc, Fail); 160 161 XTRACE (kStartTimerDelay, delay>>16, delay); 162 163 if (fBusy) StopTimer(); // cleanup 164 require(fBusy == false, Fail); // sanity 165 166 fBusy = true; // mark timer as in-use 167 this->retain(); // retain to prevent dangling 168 169 fSig = sig; 170 171 if (delay < 0) // microsecond timer 172 rc = fTimerSrc->setTimeoutUS(-delay); 173 else 174 rc = fTimerSrc->setTimeoutMS(delay); // millisecond timer 175 176 check(rc == kIOReturnSuccess); 177 XTRACE (kStartTimerSignal, sig, this->getRetainCount()); 178 179Fail: 180 return; 181} 182 183void CTimer::StopTimer() 184{ 185 Boolean ok; 186 XTRACE(kLogStopTimer1, 0, this); 187 188 if (fBusy == false) { // stopped, but we're already stopped (nop) 189 XTRACE(kLogStopTimer2, 0, this->getRetainCount()); 190 return; 191 } 192 193 if (fTimerSrc) { 194 ok = fTimerSrc->SafeCancelTimeout(); // returns ok if it worked 195 if (ok) { // if cancel timer worked, then 196 XTRACE(kLogStopTimer3, 0, this->getRetainCount()); 197 fBusy = false; // mark timer as available and 198 this->release(); // we can release self safely 199 } else { 200 XTRACE(kLogStopTimer4, 0, this->getRetainCount()); 201 } 202 } 203 XTRACE(kLogStopTimer5, 0, this->getRetainCount()); 204 return; 205} 206 207void 208CTimer::timeout(OSObject *owner, IrDATimerEventSource *sender) 209{ 210 CTimer *obj; 211 212 XTRACE(kLogTimeout1, 0, owner); 213 obj = OSDynamicCast(CTimer, owner); 214 require(obj, Fail); 215 require(obj->fOwner, Fail); 216 require(obj->fCallback, Fail); 217 require(obj->fBusy, Fail); 218 219 XTRACE(kLogTimeout2, 0, obj->getRetainCount()); 220 221 if (obj->getRetainCount() == 1) { // if it's down to one, then we're been deleted but for our start timer 222 thread_call_enter1(obj->fGrimReaper, 0); 223 return; 224 } 225 obj->fBusy = false; 226 obj->release(); 227 // temp debugging 228 XTRACE(kLogTimeout3, 0, obj->fOwner); 229 XTRACE(kLogTimeout4, 0, sender); 230 (*obj->fCallback)(obj->fOwner, sender); 231 XTRACE(kLogTimeout5, 0xffff, 0xffff); 232 233Fail: 234 return; 235} 236 237/*static*/ 238void CTimer::grim_reaper(thread_call_param_t param0, thread_call_param_t param1) 239{ 240 CTimer *obj; 241 242 XTRACE(kLogGrimReaper, 0x1111, 0x1111); 243 244 obj = OSDynamicCast(CTimer, (OSObject *)param0); 245 require(obj, Fail); 246 require(obj->fBusy, Fail); 247 248 obj->fBusy = false; 249 obj->release(); 250 251 XTRACE(kLogGrimReaper, 0xffff, 0xffff); 252 return; 253 254Fail: 255 XTRACE(kLogGrimReaper, 0xdead, 0xbeef); 256 return; 257} 258