networkprotocols/iphook/inhook6/src/timeout.cpp
changeset 0 af10295192d8
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/networkprotocols/iphook/inhook6/src/timeout.cpp	Tue Jan 26 15:23:49 2010 +0200
@@ -0,0 +1,389 @@
+// Copyright (c) 2004-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of "Eclipse Public License v1.0"
+// which accompanies this distribution, and is available
+// at the URL "http://www.eclipse.org/legal/epl-v10.html".
+//
+// Initial Contributors:
+// Nokia Corporation - initial contribution.
+//
+// Contributors:
+//
+// Description:
+// timeout.cpp - timer manager
+//
+
+#include <e32std.h>
+#include "timeout.h"
+#include "inet6log.h"
+
+// Turn off logging for this module
+#undef _LOG
+#undef LOG
+#define LOG(s)
+
+#ifdef NONSHARABLE_CLASS
+	NONSHARABLE_CLASS(CTimeoutManager);
+#endif
+
+class CTimeoutManager : public CActive, public MTimeoutManager
+	{
+	friend MTimeoutManager *TimeoutFactory::NewL(TUint,TAny *, TInt);
+	CTimeoutManager(TUint aUnit, TAny *aPtr, TInt aPriority);
+public:
+	~CTimeoutManager();
+	void Set(RTimeout &aLink, TUint aTime);
+private:
+	void DoCancel();
+	void RunL();
+	TUint Elapsed(const TTime &aStamp);
+	inline TTimeIntervalMicroSeconds32 AfterTime(TUint aDelta) const
+		{
+		return TTimeIntervalMicroSeconds32((aDelta > iMaxDelta) ? KMaxTInt : aDelta * iMultiplier);
+		}
+	TAny *const iPtr;
+	const TUint iMultiplier;						//< Units to microseconds multiplier.
+	const TUint iMaxDelta;							//< KMaxTInt microseconds in units.
+	const TTimeIntervalMicroSeconds iMaxInterval;	//< KMaxTUint units in microseconds
+	RTimer iTimer;
+	RTimeout iHead;			//< Use the RTimeout as head, queue is empty , when iNext == iPrev == 0
+	TTime iTimeStamp;		//< The base time
+	TInt iRun;				//< > 0, when inside RunL, < 0, iTimer is not open
+	};
+
+//
+// CTimeoutManager::CTimeoutManager()
+//
+CTimeoutManager::CTimeoutManager(TUint aUnit, TAny *aPtr, TInt aPriority) : CActive(aPriority),
+	iPtr(aPtr),
+	iMultiplier(1000000 / aUnit),
+	iMaxDelta(KMaxTInt / iMultiplier),
+	iMaxInterval(TInt64(KMaxTUint) * iMultiplier),
+	iHead(NULL)
+	{
+	LOG(Log::Printf(_L("CTimeoutManager[%u]::CTimeoutManager() aUnit=%u, iMultiplier=%u, iMaxDelta=%u\r\n"),
+		this, aUnit, iMultiplier, iMaxDelta));
+	// If CreateLocal fails, the iRun will be negative. This
+	// will silently disable the timeout activation,
+	// there will be no timeout callbacks.
+	iRun = iTimer.CreateLocal();
+	ASSERT(iRun <= 0);		// should never be > 0!
+	CActiveScheduler::Add(this);
+	}
+
+//
+//	CTimeoutManager::~CTimeoutManager()
+//
+CTimeoutManager::~CTimeoutManager()
+	{
+	if (IsActive())
+		Cancel();
+	if (iRun >= 0)	// Has iTimer been successfully opened?
+		iTimer.Close();
+	//
+	// *NOTE*
+	//		Leaves iHead in incorrect state for iPrev,
+	//		but as this is a desctuctor, ignore this
+	//		for now (beware of using this loop as a
+	//		model in elsewhere! -- msa)
+	//
+	RTimeout *p;
+	while ((p = iHead.iNext) != &iHead)
+		{
+		iHead.iNext = p->iNext;
+		p->iNext = p;
+		p->iPrev = p;
+		}
+	}
+
+TUint CTimeoutManager::Elapsed(const TTime &aStamp)
+	{
+	// If the clock does not behave normally (i.e turned backward or forward),
+	// the base time will be set to current time without adjusting any timers
+	// This will make all timers in the queue be scheduled longer in actual time
+	// by the delta time of the first timer in the queue.
+	const TTimeIntervalMicroSeconds delta = aStamp.MicroSecondsFrom(iTimeStamp);
+	
+#ifdef _DEBUG
+	if (delta < TTimeIntervalMicroSeconds(0))
+		{
+		// Someone has turned the clock backwards!
+		LOG(Log::Printf(_L("CTimeoutManager[%u]::Elapsed() *** Clock has been turned back! ***"), this));
+		}
+	else if (delta > iMaxInterval)
+		{
+		// Too much time has elapsed
+		LOG(Log::Printf(_L("CTimeoutManager[%u]::Elapsed() *** Too much time has elapsed! ***"), this));
+		}
+#endif
+
+	if (delta < TTimeIntervalMicroSeconds(0) || delta > iMaxInterval)
+		{
+		iTimeStamp = aStamp;	// Reset base time 
+		return 0;
+		}
+
+#ifdef I64LOW
+	return I64LOW(delta.Int64() / iMultiplier); 
+#else
+	return (delta.Int64() / iMultiplier).Low(); 
+#endif
+	}
+
+
+//
+// CTimeoutManager::Set
+//	Add an object to the timer queue
+//
+void CTimeoutManager::Set(RTimeout &aHandle, TUint aTime)
+	{
+	if (aHandle.IsActive())		// Already Queued?
+		aHandle.Cancel();
+
+	TTime stamp;
+	stamp.UniversalTime();
+	//
+	// Initial iTimer.iDelta is the expiration time from the
+	// last time stamp. Should always be > 0.
+	//
+	aHandle.iDelta = aTime;
+	if (iHead.iNext != &iHead)
+		{
+		// Non-empty queue has a valid time stamp.
+		// Compute elapsed time from the last time stamp
+		// This is always >= 0!
+		//
+		TUint elapsed = Elapsed(stamp); 
+		if (KMaxTUint - aHandle.iDelta < elapsed)
+			aHandle.iDelta = KMaxTUint;
+		else
+			aHandle.iDelta += elapsed;
+		}
+	else
+		{
+		//
+		// Empty queue does not have a valid time stamp,
+		// initialize it to the current time.
+		iTimeStamp = stamp;
+		}
+
+	LOG(TInt pos = 0);
+	RTimeout *p, *prev;
+	for (prev = &iHead; ;prev = p)
+		{
+		LOG(pos++);
+		if ((p = prev->iNext) == &iHead)
+			{
+			//
+			// Add to last (and possibly first)
+			// Note: prev == some node or &iHead
+			//
+			aHandle.iPrev = prev;
+			aHandle.iNext = &iHead;
+			prev->iNext = &aHandle;
+			iHead.iPrev = &aHandle;
+			LOG(Log::Printf(_L("\tCTimeoutManager[%u]::Set(RTimeout[%u], %u) iDelta=%u, pos=%u. (last)\r\n"),
+				this, &aHandle, aTime, aHandle.iDelta, pos));
+			break;
+			}
+		else if (aHandle.iDelta < p->iDelta)
+			{
+			//
+			//	Need to add before this element (p)
+			//	(note, p could be == &iHead!)
+			//
+			p->iDelta -= aHandle.iDelta;
+			aHandle.iNext = p;
+			aHandle.iPrev = prev;
+			p->iPrev = &aHandle;
+			prev->iNext = &aHandle;
+			LOG(Log::Printf(_L("\tCTimeoutManager[%u]::Set(RTimeout[%u], %u) iDelta=%u, pos=%u. (not last)\r\n"),
+				this, &aHandle, aTime, aHandle.iDelta, pos));
+			break;
+			}
+		else
+			aHandle.iDelta -= p->iDelta;
+		}
+
+	if (iHead.iNext == &aHandle && !iRun)
+		{
+		// Inserted to the beginning, need to shorten the
+		// timer event! (If the insert was not to the beginning,
+		// the timer must already be active or this is called
+		// from RunL).
+		//
+		if (IsActive())
+			{
+			if (iStatus.Int() != KRequestPending)
+				{
+				LOG(Log::Printf(_L("\tCTimeoutManager[%u] RunL() pending (%d)\r\n"), this, iStatus.Int()));
+				return;	// RunL will be called, no need to do anything here.
+				}
+			Cancel();
+			}
+		//
+		// Activate timer, Set NEVER expires directly -- assume After(0)
+		// works and calls RunL() as soon as possible.
+		//
+		LOG(Log::Printf(_L("\tCTimeoutManager[%u] event after = %u units [%uus]\r\n"), this, aTime, AfterTime(aTime).Int()));
+		iTimer.After(iStatus, AfterTime(aTime));
+		SetActive();
+		}
+	}
+
+
+//
+// CTimeoutManager::RunL
+//	Called when the timer expires, but it makes no assumptions about
+//	the time elapsed, instead it refers to the current real time.
+//
+void CTimeoutManager::RunL()
+	{
+	LOG(Log::Printf(_L("-->\tCTimeoutManager[%u]::RunL()\r\n"), this));
+
+	// If iRun < 0, then iTimer.CreateLocal() has failed
+	// and RunL should never get called, because timer
+	// has never been activated. However, just return
+	// if this happens for some reason...
+	if (iRun < 0)
+		return;
+
+	ASSERT(iRun == 0);
+
+	iRun++;
+
+	for (;;)
+		{
+		RTimeout *const p = iHead.iNext;
+		if (p == &iHead)
+			{
+			LOG(Log::Printf(_L("<--\tCTimeoutManager[%u] queue is empty\r\n"), this));
+			break;
+			}
+		TTime stamp;
+		stamp.UniversalTime();
+		// Number of units elapsed since the last time stamp. Always >= 0.
+		const TUint elapsed = Elapsed(stamp);
+
+		if (p->iDelta > elapsed)
+			{
+			iTimeStamp = stamp;
+			p->iDelta -= elapsed;			// iDelta > 0, always!
+			LOG(Log::Printf(_L("<--\tCTimeoutManager[%u] elapsed=%u next event=%u [%uus]\r\n"),
+				this, elapsed, p->iDelta, AfterTime(p->iDelta).Int()));
+			iTimer.After(iStatus, AfterTime(p->iDelta));
+			SetActive();
+			break;
+			}
+		LOG(Log::Printf(_L("\tCTimeoutManager[%u] elapsed=%u, expire %u [overdue=%u units] RTimeout[%u]\r\n"),
+			this, elapsed, p->iDelta, elapsed - p->iDelta, p));
+		// Pass the delay to next in queue (as
+		// iTimeStamp is not yet changed)
+		p->iNext->iDelta += p->iDelta;
+		// The time has passed for this TTimeout. Remove
+		// from the queue and call the Expired callback
+		// (if last, iHead will end up pointing to itself)
+		iHead.iNext = p->iNext;
+		iHead.iNext->iPrev = &iHead;
+		// Mark as inactive (link element to self)
+		p->iPrev = p;
+		p->iNext = p;
+		(p->iExpired)(*p, stamp, iPtr);
+		// After above, the iQueue content may have changed
+		// totally, one needs to examine only the first entry
+		// on each loop.
+		}
+	--iRun;
+	}
+
+void CTimeoutManager::DoCancel()
+	{
+	iTimer.Cancel();
+	}
+
+
+
+EXPORT_C MTimeoutManager *TimeoutFactory::NewL(TUint aUnit, TAny *aPtr, TInt aPriority)
+	/**
+	* Create a new instance of timeout manager.
+	*
+	* aUnit specifies the unit of the aTime parameter for the timeout
+	* setting as fractions of a second. Valid range is [1..1000000].
+	* aPtr is additional parameter  which is passed to each Expired()
+	* call. aPriority is used for the CActive instantiation.
+	*
+	* The chosen unit also contrains the maximum time that can
+	* be specified with the manager. The time corresponding the
+	* KMaxTUint units in seconds is:
+@verbatim
+maxtime = KMaxTUint / unit
+@endverbatim
+	*
+	* @param	aUnit
+	*		The unit in fractions of a second (1/aUnit sec).
+	* @param	aPtr
+	*		The ptr parameter for every callback (TimeoutCallback)
+	*		generated by this manager.
+	* @param	aPriority
+	*		The CActive priority value for the timeout manager.
+	* @return
+	*		The MTimeoutManager instance.
+	*
+	* @leave KErrArgument, if aUnit is invalid
+	* @leave Other system wide reasons, if creation fails.
+	*/
+	{
+	//
+	// Sanity check and constrain parameters
+	//
+	if (aUnit < 1 || aUnit > 1000000)
+		User::Leave(KErrArgument);
+	return new (ELeave) CTimeoutManager(aUnit, aPtr, aPriority);
+	}
+
+//
+//	CTimeoutFactory::IsActive
+//
+EXPORT_C TBool TimeoutFactory::IsActive(const RTimeout &aHandle)
+	/**
+	* Tests if a timeout is active on specified handle.
+	*
+	* @param	aHandle	The timeout handle
+	* @return	ETrue, if active, and EFalse otherwise.
+	*/
+	{
+	return (aHandle.iNext != &aHandle);
+	}
+//
+//	CTimeoutFactory::Cancel
+//
+EXPORT_C void TimeoutFactory::Cancel(RTimeout &aHandle)
+	/**
+	* Cancels timeout, if active.
+	*
+	* This is safe to call, even if handle is inactive.
+	*
+	* @param	aHandle	The timeout handle
+	*/
+	{
+	LOG(if (aHandle.IsActive()) Log::Printf(_L("\tRTimeout[%u] canceled iDelta=%u\r\n"), &aHandle, aHandle.iDelta));
+
+	//
+	// (no need to check for IsActive(), code does not
+	// crash even if called for unlinked handle! -- msa)
+	//
+	// Pass the delay to the next in queue
+	aHandle.iNext->iDelta += aHandle.iDelta;
+	// Remove element from the old queue
+	aHandle.iPrev->iNext = aHandle.iNext;
+	aHandle.iNext->iPrev = aHandle.iPrev;
+	// Link to self, (== InActive status)
+	aHandle.iNext = &aHandle;
+	aHandle.iPrev = &aHandle;
+
+	// ** Don't have access to the actual manager, cannot
+	// ** cancel timer. Timer event must deal with queue
+	// ** being empty!
+	}
+