|
1 // Copyright (c) 2004-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
2 // All rights reserved. |
|
3 // This component and the accompanying materials are made available |
|
4 // under the terms of "Eclipse Public License v1.0" |
|
5 // which accompanies this distribution, and is available |
|
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
7 // |
|
8 // Initial Contributors: |
|
9 // Nokia Corporation - initial contribution. |
|
10 // |
|
11 // Contributors: |
|
12 // |
|
13 // Description: |
|
14 // timeout.cpp - timer manager |
|
15 // |
|
16 |
|
17 #include <e32std.h> |
|
18 #include "timeout.h" |
|
19 #include "inet6log.h" |
|
20 |
|
21 // Turn off logging for this module |
|
22 #undef _LOG |
|
23 #undef LOG |
|
24 #define LOG(s) |
|
25 |
|
26 #ifdef NONSHARABLE_CLASS |
|
27 NONSHARABLE_CLASS(CTimeoutManager); |
|
28 #endif |
|
29 |
|
30 class CTimeoutManager : public CActive, public MTimeoutManager |
|
31 { |
|
32 friend MTimeoutManager *TimeoutFactory::NewL(TUint,TAny *, TInt); |
|
33 CTimeoutManager(TUint aUnit, TAny *aPtr, TInt aPriority); |
|
34 public: |
|
35 ~CTimeoutManager(); |
|
36 void Set(RTimeout &aLink, TUint aTime); |
|
37 private: |
|
38 void DoCancel(); |
|
39 void RunL(); |
|
40 TUint Elapsed(const TTime &aStamp); |
|
41 inline TTimeIntervalMicroSeconds32 AfterTime(TUint aDelta) const |
|
42 { |
|
43 return TTimeIntervalMicroSeconds32((aDelta > iMaxDelta) ? KMaxTInt : aDelta * iMultiplier); |
|
44 } |
|
45 TAny *const iPtr; |
|
46 const TUint iMultiplier; //< Units to microseconds multiplier. |
|
47 const TUint iMaxDelta; //< KMaxTInt microseconds in units. |
|
48 const TTimeIntervalMicroSeconds iMaxInterval; //< KMaxTUint units in microseconds |
|
49 RTimer iTimer; |
|
50 RTimeout iHead; //< Use the RTimeout as head, queue is empty , when iNext == iPrev == 0 |
|
51 TTime iTimeStamp; //< The base time |
|
52 TInt iRun; //< > 0, when inside RunL, < 0, iTimer is not open |
|
53 }; |
|
54 |
|
55 // |
|
56 // CTimeoutManager::CTimeoutManager() |
|
57 // |
|
58 CTimeoutManager::CTimeoutManager(TUint aUnit, TAny *aPtr, TInt aPriority) : CActive(aPriority), |
|
59 iPtr(aPtr), |
|
60 iMultiplier(1000000 / aUnit), |
|
61 iMaxDelta(KMaxTInt / iMultiplier), |
|
62 iMaxInterval(TInt64(KMaxTUint) * iMultiplier), |
|
63 iHead(NULL) |
|
64 { |
|
65 LOG(Log::Printf(_L("CTimeoutManager[%u]::CTimeoutManager() aUnit=%u, iMultiplier=%u, iMaxDelta=%u\r\n"), |
|
66 this, aUnit, iMultiplier, iMaxDelta)); |
|
67 // If CreateLocal fails, the iRun will be negative. This |
|
68 // will silently disable the timeout activation, |
|
69 // there will be no timeout callbacks. |
|
70 iRun = iTimer.CreateLocal(); |
|
71 ASSERT(iRun <= 0); // should never be > 0! |
|
72 CActiveScheduler::Add(this); |
|
73 } |
|
74 |
|
75 // |
|
76 // CTimeoutManager::~CTimeoutManager() |
|
77 // |
|
78 CTimeoutManager::~CTimeoutManager() |
|
79 { |
|
80 if (IsActive()) |
|
81 Cancel(); |
|
82 if (iRun >= 0) // Has iTimer been successfully opened? |
|
83 iTimer.Close(); |
|
84 // |
|
85 // *NOTE* |
|
86 // Leaves iHead in incorrect state for iPrev, |
|
87 // but as this is a desctuctor, ignore this |
|
88 // for now (beware of using this loop as a |
|
89 // model in elsewhere! -- msa) |
|
90 // |
|
91 RTimeout *p; |
|
92 while ((p = iHead.iNext) != &iHead) |
|
93 { |
|
94 iHead.iNext = p->iNext; |
|
95 p->iNext = p; |
|
96 p->iPrev = p; |
|
97 } |
|
98 } |
|
99 |
|
100 TUint CTimeoutManager::Elapsed(const TTime &aStamp) |
|
101 { |
|
102 // If the clock does not behave normally (i.e turned backward or forward), |
|
103 // the base time will be set to current time without adjusting any timers |
|
104 // This will make all timers in the queue be scheduled longer in actual time |
|
105 // by the delta time of the first timer in the queue. |
|
106 const TTimeIntervalMicroSeconds delta = aStamp.MicroSecondsFrom(iTimeStamp); |
|
107 |
|
108 #ifdef _DEBUG |
|
109 if (delta < TTimeIntervalMicroSeconds(0)) |
|
110 { |
|
111 // Someone has turned the clock backwards! |
|
112 LOG(Log::Printf(_L("CTimeoutManager[%u]::Elapsed() *** Clock has been turned back! ***"), this)); |
|
113 } |
|
114 else if (delta > iMaxInterval) |
|
115 { |
|
116 // Too much time has elapsed |
|
117 LOG(Log::Printf(_L("CTimeoutManager[%u]::Elapsed() *** Too much time has elapsed! ***"), this)); |
|
118 } |
|
119 #endif |
|
120 |
|
121 if (delta < TTimeIntervalMicroSeconds(0) || delta > iMaxInterval) |
|
122 { |
|
123 iTimeStamp = aStamp; // Reset base time |
|
124 return 0; |
|
125 } |
|
126 |
|
127 #ifdef I64LOW |
|
128 return I64LOW(delta.Int64() / iMultiplier); |
|
129 #else |
|
130 return (delta.Int64() / iMultiplier).Low(); |
|
131 #endif |
|
132 } |
|
133 |
|
134 |
|
135 // |
|
136 // CTimeoutManager::Set |
|
137 // Add an object to the timer queue |
|
138 // |
|
139 void CTimeoutManager::Set(RTimeout &aHandle, TUint aTime) |
|
140 { |
|
141 if (aHandle.IsActive()) // Already Queued? |
|
142 aHandle.Cancel(); |
|
143 |
|
144 TTime stamp; |
|
145 stamp.UniversalTime(); |
|
146 // |
|
147 // Initial iTimer.iDelta is the expiration time from the |
|
148 // last time stamp. Should always be > 0. |
|
149 // |
|
150 aHandle.iDelta = aTime; |
|
151 if (iHead.iNext != &iHead) |
|
152 { |
|
153 // Non-empty queue has a valid time stamp. |
|
154 // Compute elapsed time from the last time stamp |
|
155 // This is always >= 0! |
|
156 // |
|
157 TUint elapsed = Elapsed(stamp); |
|
158 if (KMaxTUint - aHandle.iDelta < elapsed) |
|
159 aHandle.iDelta = KMaxTUint; |
|
160 else |
|
161 aHandle.iDelta += elapsed; |
|
162 } |
|
163 else |
|
164 { |
|
165 // |
|
166 // Empty queue does not have a valid time stamp, |
|
167 // initialize it to the current time. |
|
168 iTimeStamp = stamp; |
|
169 } |
|
170 |
|
171 LOG(TInt pos = 0); |
|
172 RTimeout *p, *prev; |
|
173 for (prev = &iHead; ;prev = p) |
|
174 { |
|
175 LOG(pos++); |
|
176 if ((p = prev->iNext) == &iHead) |
|
177 { |
|
178 // |
|
179 // Add to last (and possibly first) |
|
180 // Note: prev == some node or &iHead |
|
181 // |
|
182 aHandle.iPrev = prev; |
|
183 aHandle.iNext = &iHead; |
|
184 prev->iNext = &aHandle; |
|
185 iHead.iPrev = &aHandle; |
|
186 LOG(Log::Printf(_L("\tCTimeoutManager[%u]::Set(RTimeout[%u], %u) iDelta=%u, pos=%u. (last)\r\n"), |
|
187 this, &aHandle, aTime, aHandle.iDelta, pos)); |
|
188 break; |
|
189 } |
|
190 else if (aHandle.iDelta < p->iDelta) |
|
191 { |
|
192 // |
|
193 // Need to add before this element (p) |
|
194 // (note, p could be == &iHead!) |
|
195 // |
|
196 p->iDelta -= aHandle.iDelta; |
|
197 aHandle.iNext = p; |
|
198 aHandle.iPrev = prev; |
|
199 p->iPrev = &aHandle; |
|
200 prev->iNext = &aHandle; |
|
201 LOG(Log::Printf(_L("\tCTimeoutManager[%u]::Set(RTimeout[%u], %u) iDelta=%u, pos=%u. (not last)\r\n"), |
|
202 this, &aHandle, aTime, aHandle.iDelta, pos)); |
|
203 break; |
|
204 } |
|
205 else |
|
206 aHandle.iDelta -= p->iDelta; |
|
207 } |
|
208 |
|
209 if (iHead.iNext == &aHandle && !iRun) |
|
210 { |
|
211 // Inserted to the beginning, need to shorten the |
|
212 // timer event! (If the insert was not to the beginning, |
|
213 // the timer must already be active or this is called |
|
214 // from RunL). |
|
215 // |
|
216 if (IsActive()) |
|
217 { |
|
218 if (iStatus.Int() != KRequestPending) |
|
219 { |
|
220 LOG(Log::Printf(_L("\tCTimeoutManager[%u] RunL() pending (%d)\r\n"), this, iStatus.Int())); |
|
221 return; // RunL will be called, no need to do anything here. |
|
222 } |
|
223 Cancel(); |
|
224 } |
|
225 // |
|
226 // Activate timer, Set NEVER expires directly -- assume After(0) |
|
227 // works and calls RunL() as soon as possible. |
|
228 // |
|
229 LOG(Log::Printf(_L("\tCTimeoutManager[%u] event after = %u units [%uus]\r\n"), this, aTime, AfterTime(aTime).Int())); |
|
230 iTimer.After(iStatus, AfterTime(aTime)); |
|
231 SetActive(); |
|
232 } |
|
233 } |
|
234 |
|
235 |
|
236 // |
|
237 // CTimeoutManager::RunL |
|
238 // Called when the timer expires, but it makes no assumptions about |
|
239 // the time elapsed, instead it refers to the current real time. |
|
240 // |
|
241 void CTimeoutManager::RunL() |
|
242 { |
|
243 LOG(Log::Printf(_L("-->\tCTimeoutManager[%u]::RunL()\r\n"), this)); |
|
244 |
|
245 // If iRun < 0, then iTimer.CreateLocal() has failed |
|
246 // and RunL should never get called, because timer |
|
247 // has never been activated. However, just return |
|
248 // if this happens for some reason... |
|
249 if (iRun < 0) |
|
250 return; |
|
251 |
|
252 ASSERT(iRun == 0); |
|
253 |
|
254 iRun++; |
|
255 |
|
256 for (;;) |
|
257 { |
|
258 RTimeout *const p = iHead.iNext; |
|
259 if (p == &iHead) |
|
260 { |
|
261 LOG(Log::Printf(_L("<--\tCTimeoutManager[%u] queue is empty\r\n"), this)); |
|
262 break; |
|
263 } |
|
264 TTime stamp; |
|
265 stamp.UniversalTime(); |
|
266 // Number of units elapsed since the last time stamp. Always >= 0. |
|
267 const TUint elapsed = Elapsed(stamp); |
|
268 |
|
269 if (p->iDelta > elapsed) |
|
270 { |
|
271 iTimeStamp = stamp; |
|
272 p->iDelta -= elapsed; // iDelta > 0, always! |
|
273 LOG(Log::Printf(_L("<--\tCTimeoutManager[%u] elapsed=%u next event=%u [%uus]\r\n"), |
|
274 this, elapsed, p->iDelta, AfterTime(p->iDelta).Int())); |
|
275 iTimer.After(iStatus, AfterTime(p->iDelta)); |
|
276 SetActive(); |
|
277 break; |
|
278 } |
|
279 LOG(Log::Printf(_L("\tCTimeoutManager[%u] elapsed=%u, expire %u [overdue=%u units] RTimeout[%u]\r\n"), |
|
280 this, elapsed, p->iDelta, elapsed - p->iDelta, p)); |
|
281 // Pass the delay to next in queue (as |
|
282 // iTimeStamp is not yet changed) |
|
283 p->iNext->iDelta += p->iDelta; |
|
284 // The time has passed for this TTimeout. Remove |
|
285 // from the queue and call the Expired callback |
|
286 // (if last, iHead will end up pointing to itself) |
|
287 iHead.iNext = p->iNext; |
|
288 iHead.iNext->iPrev = &iHead; |
|
289 // Mark as inactive (link element to self) |
|
290 p->iPrev = p; |
|
291 p->iNext = p; |
|
292 (p->iExpired)(*p, stamp, iPtr); |
|
293 // After above, the iQueue content may have changed |
|
294 // totally, one needs to examine only the first entry |
|
295 // on each loop. |
|
296 } |
|
297 --iRun; |
|
298 } |
|
299 |
|
300 void CTimeoutManager::DoCancel() |
|
301 { |
|
302 iTimer.Cancel(); |
|
303 } |
|
304 |
|
305 |
|
306 |
|
307 EXPORT_C MTimeoutManager *TimeoutFactory::NewL(TUint aUnit, TAny *aPtr, TInt aPriority) |
|
308 /** |
|
309 * Create a new instance of timeout manager. |
|
310 * |
|
311 * aUnit specifies the unit of the aTime parameter for the timeout |
|
312 * setting as fractions of a second. Valid range is [1..1000000]. |
|
313 * aPtr is additional parameter which is passed to each Expired() |
|
314 * call. aPriority is used for the CActive instantiation. |
|
315 * |
|
316 * The chosen unit also contrains the maximum time that can |
|
317 * be specified with the manager. The time corresponding the |
|
318 * KMaxTUint units in seconds is: |
|
319 @verbatim |
|
320 maxtime = KMaxTUint / unit |
|
321 @endverbatim |
|
322 * |
|
323 * @param aUnit |
|
324 * The unit in fractions of a second (1/aUnit sec). |
|
325 * @param aPtr |
|
326 * The ptr parameter for every callback (TimeoutCallback) |
|
327 * generated by this manager. |
|
328 * @param aPriority |
|
329 * The CActive priority value for the timeout manager. |
|
330 * @return |
|
331 * The MTimeoutManager instance. |
|
332 * |
|
333 * @leave KErrArgument, if aUnit is invalid |
|
334 * @leave Other system wide reasons, if creation fails. |
|
335 */ |
|
336 { |
|
337 // |
|
338 // Sanity check and constrain parameters |
|
339 // |
|
340 if (aUnit < 1 || aUnit > 1000000) |
|
341 User::Leave(KErrArgument); |
|
342 return new (ELeave) CTimeoutManager(aUnit, aPtr, aPriority); |
|
343 } |
|
344 |
|
345 // |
|
346 // CTimeoutFactory::IsActive |
|
347 // |
|
348 EXPORT_C TBool TimeoutFactory::IsActive(const RTimeout &aHandle) |
|
349 /** |
|
350 * Tests if a timeout is active on specified handle. |
|
351 * |
|
352 * @param aHandle The timeout handle |
|
353 * @return ETrue, if active, and EFalse otherwise. |
|
354 */ |
|
355 { |
|
356 return (aHandle.iNext != &aHandle); |
|
357 } |
|
358 // |
|
359 // CTimeoutFactory::Cancel |
|
360 // |
|
361 EXPORT_C void TimeoutFactory::Cancel(RTimeout &aHandle) |
|
362 /** |
|
363 * Cancels timeout, if active. |
|
364 * |
|
365 * This is safe to call, even if handle is inactive. |
|
366 * |
|
367 * @param aHandle The timeout handle |
|
368 */ |
|
369 { |
|
370 LOG(if (aHandle.IsActive()) Log::Printf(_L("\tRTimeout[%u] canceled iDelta=%u\r\n"), &aHandle, aHandle.iDelta)); |
|
371 |
|
372 // |
|
373 // (no need to check for IsActive(), code does not |
|
374 // crash even if called for unlinked handle! -- msa) |
|
375 // |
|
376 // Pass the delay to the next in queue |
|
377 aHandle.iNext->iDelta += aHandle.iDelta; |
|
378 // Remove element from the old queue |
|
379 aHandle.iPrev->iNext = aHandle.iNext; |
|
380 aHandle.iNext->iPrev = aHandle.iPrev; |
|
381 // Link to self, (== InActive status) |
|
382 aHandle.iNext = &aHandle; |
|
383 aHandle.iPrev = &aHandle; |
|
384 |
|
385 // ** Don't have access to the actual manager, cannot |
|
386 // ** cancel timer. Timer event must deal with queue |
|
387 // ** being empty! |
|
388 } |
|
389 |