--- /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!
+ }
+