networkprotocols/iphook/inhook6/src/timeout.cpp
changeset 0 af10295192d8
equal deleted inserted replaced
-1:000000000000 0:af10295192d8
       
     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