| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654 |
- //------------------------------------------------------------
- // Copyright (c) Microsoft Corporation. All rights reserved.
- //------------------------------------------------------------
- namespace System.Runtime
- {
- using System;
- using System.ComponentModel;
- using System.Runtime.Interop;
- using System.Security;
- using System.Threading;
- using Microsoft.Win32.SafeHandles;
- // IOThreadTimer has several characterstics that are important for performance:
- // - Timers that expire benefit from being scheduled to run on IO threads using IOThreadScheduler.Schedule.
- // - The timer "waiter" thread thread is only allocated if there are set timers.
- // - The timer waiter thread itself is an IO thread, which allows it to go away if there is no need for it,
- // and allows it to be reused for other purposes.
- // - After the timer count goes to zero, the timer waiter thread remains active for a bounded amount
- // of time to wait for additional timers to be set.
- // - Timers are stored in an array-based priority queue to reduce the amount of time spent in updates, and
- // to always provide O(1) access to the minimum timer (the first one that will expire).
- // - The standard textbook priority queue data structure is extended to allow efficient Delete in addition to
- // DeleteMin for efficient handling of canceled timers.
- // - Timers that are typically set, then immediately canceled (such as a retry timer,
- // or a flush timer), are tracked separately from more stable timers, to avoid having
- // to update the waitable timer in the typical case when a timer is canceled. Whether
- // a timer instance follows this pattern is specified when the timer is constructed.
- // - Extending a timer by a configurable time delta (maxSkew) does not involve updating the
- // waitable timer, or taking a lock.
- // - Timer instances are relatively cheap. They share "heavy" resources like the waiter thread and
- // waitable timer handle.
- // - Setting or canceling a timer does not typically involve any allocations.
- class IOThreadTimer
- {
- const int maxSkewInMillisecondsDefault = 100;
- static long systemTimeResolutionTicks = -1;
- Action<object> callback;
- object callbackState;
- long dueTime;
- int index;
- long maxSkew;
- TimerGroup timerGroup;
- public IOThreadTimer(Action<object> callback, object callbackState, bool isTypicallyCanceledShortlyAfterBeingSet)
- : this(callback, callbackState, isTypicallyCanceledShortlyAfterBeingSet, maxSkewInMillisecondsDefault)
- {
- }
- public IOThreadTimer(Action<object> callback, object callbackState, bool isTypicallyCanceledShortlyAfterBeingSet, int maxSkewInMilliseconds)
- {
- this.callback = callback;
- this.callbackState = callbackState;
- this.maxSkew = Ticks.FromMilliseconds(maxSkewInMilliseconds);
- this.timerGroup =
- (isTypicallyCanceledShortlyAfterBeingSet ? TimerManager.Value.VolatileTimerGroup : TimerManager.Value.StableTimerGroup);
- }
- public static long SystemTimeResolutionTicks
- {
- get
- {
- if (IOThreadTimer.systemTimeResolutionTicks == -1)
- {
- IOThreadTimer.systemTimeResolutionTicks = GetSystemTimeResolution();
- }
- return IOThreadTimer.systemTimeResolutionTicks;
- }
- }
- [Fx.Tag.SecurityNote(Critical = "Calls critical method GetSystemTimeAdjustment", Safe = "method is a SafeNativeMethod")]
- [SecuritySafeCritical]
- static long GetSystemTimeResolution()
- {
- int dummyAdjustment;
- uint increment;
- uint dummyAdjustmentDisabled;
- if (UnsafeNativeMethods.GetSystemTimeAdjustment(out dummyAdjustment, out increment, out dummyAdjustmentDisabled) != 0)
- {
- return (long)increment;
- }
- // Assume the default, which is around 15 milliseconds.
- return 15 * TimeSpan.TicksPerMillisecond;
- }
- public bool Cancel()
- {
- return TimerManager.Value.Cancel(this);
- }
- public void Set(TimeSpan timeFromNow)
- {
- if (timeFromNow != TimeSpan.MaxValue)
- {
- SetAt(Ticks.Add(Ticks.Now, Ticks.FromTimeSpan(timeFromNow)));
- }
- }
- public void Set(int millisecondsFromNow)
- {
- SetAt(Ticks.Add(Ticks.Now, Ticks.FromMilliseconds(millisecondsFromNow)));
- }
- public void SetAt(long dueTime)
- {
- TimerManager.Value.Set(this, dueTime);
- }
- [Fx.Tag.SynchronizationObject(Blocking = false, Scope = Fx.Tag.Strings.AppDomain)]
- class TimerManager
- {
- const long maxTimeToWaitForMoreTimers = 1000 * TimeSpan.TicksPerMillisecond;
- [Fx.Tag.Queue(typeof(IOThreadTimer), Scope = Fx.Tag.Strings.AppDomain, StaleElementsRemovedImmediately = true)]
- static TimerManager value = new TimerManager();
- Action<object> onWaitCallback;
- TimerGroup stableTimerGroup;
- TimerGroup volatileTimerGroup;
- [Fx.Tag.SynchronizationObject(Blocking = false)]
- WaitableTimer[] waitableTimers;
- bool waitScheduled;
- public TimerManager()
- {
- this.onWaitCallback = new Action<object>(OnWaitCallback);
- this.stableTimerGroup = new TimerGroup();
- this.volatileTimerGroup = new TimerGroup();
- this.waitableTimers = new WaitableTimer[] { this.stableTimerGroup.WaitableTimer, this.volatileTimerGroup.WaitableTimer };
- }
- object ThisLock
- {
- get { return this; }
- }
- public static TimerManager Value
- {
- get
- {
- return TimerManager.value;
- }
- }
- public TimerGroup StableTimerGroup
- {
- get
- {
- return this.stableTimerGroup;
- }
- }
- public TimerGroup VolatileTimerGroup
- {
- get
- {
- return this.volatileTimerGroup;
- }
- }
- public void Set(IOThreadTimer timer, long dueTime)
- {
- long timeDiff = dueTime - timer.dueTime;
- if (timeDiff < 0)
- {
- timeDiff = -timeDiff;
- }
- if (timeDiff > timer.maxSkew)
- {
- lock (ThisLock)
- {
- TimerGroup timerGroup = timer.timerGroup;
- TimerQueue timerQueue = timerGroup.TimerQueue;
- if (timer.index > 0)
- {
- if (timerQueue.UpdateTimer(timer, dueTime))
- {
- UpdateWaitableTimer(timerGroup);
- }
- }
- else
- {
- if (timerQueue.InsertTimer(timer, dueTime))
- {
- UpdateWaitableTimer(timerGroup);
- if (timerQueue.Count == 1)
- {
- EnsureWaitScheduled();
- }
- }
- }
- }
- }
- }
- public bool Cancel(IOThreadTimer timer)
- {
- lock (ThisLock)
- {
- if (timer.index > 0)
- {
- TimerGroup timerGroup = timer.timerGroup;
- TimerQueue timerQueue = timerGroup.TimerQueue;
- timerQueue.DeleteTimer(timer);
- if (timerQueue.Count > 0)
- {
- UpdateWaitableTimer(timerGroup);
- }
- else
- {
- TimerGroup otherTimerGroup = GetOtherTimerGroup(timerGroup);
- if (otherTimerGroup.TimerQueue.Count == 0)
- {
- long now = Ticks.Now;
- long thisGroupRemainingTime = timerGroup.WaitableTimer.DueTime - now;
- long otherGroupRemainingTime = otherTimerGroup.WaitableTimer.DueTime - now;
- if (thisGroupRemainingTime > maxTimeToWaitForMoreTimers &&
- otherGroupRemainingTime > maxTimeToWaitForMoreTimers)
- {
- timerGroup.WaitableTimer.Set(Ticks.Add(now, maxTimeToWaitForMoreTimers));
- }
- }
- }
- return true;
- }
- else
- {
- return false;
- }
- }
- }
- void EnsureWaitScheduled()
- {
- if (!this.waitScheduled)
- {
- ScheduleWait();
- }
- }
- TimerGroup GetOtherTimerGroup(TimerGroup timerGroup)
- {
- if (object.ReferenceEquals(timerGroup, this.volatileTimerGroup))
- {
- return this.stableTimerGroup;
- }
- else
- {
- return this.volatileTimerGroup;
- }
- }
- void OnWaitCallback(object state)
- {
- WaitHandle.WaitAny(this.waitableTimers);
- long now = Ticks.Now;
- lock (ThisLock)
- {
- this.waitScheduled = false;
- ScheduleElapsedTimers(now);
- ReactivateWaitableTimers();
- ScheduleWaitIfAnyTimersLeft();
- }
- }
- void ReactivateWaitableTimers()
- {
- ReactivateWaitableTimer(this.stableTimerGroup);
- ReactivateWaitableTimer(this.volatileTimerGroup);
- }
- void ReactivateWaitableTimer(TimerGroup timerGroup)
- {
- TimerQueue timerQueue = timerGroup.TimerQueue;
- if (timerQueue.Count > 0)
- {
- timerGroup.WaitableTimer.Set(timerQueue.MinTimer.dueTime);
- }
- else
- {
- timerGroup.WaitableTimer.Set(long.MaxValue);
- }
- }
- void ScheduleElapsedTimers(long now)
- {
- ScheduleElapsedTimers(this.stableTimerGroup, now);
- ScheduleElapsedTimers(this.volatileTimerGroup, now);
- }
- void ScheduleElapsedTimers(TimerGroup timerGroup, long now)
- {
- TimerQueue timerQueue = timerGroup.TimerQueue;
- while (timerQueue.Count > 0)
- {
- IOThreadTimer timer = timerQueue.MinTimer;
- long timeDiff = timer.dueTime - now;
- if (timeDiff <= timer.maxSkew)
- {
- timerQueue.DeleteMinTimer();
- ActionItem.Schedule(timer.callback, timer.callbackState);
- }
- else
- {
- break;
- }
- }
- }
- void ScheduleWait()
- {
- ActionItem.Schedule(this.onWaitCallback, null);
- this.waitScheduled = true;
- }
- void ScheduleWaitIfAnyTimersLeft()
- {
- if (this.stableTimerGroup.TimerQueue.Count > 0 ||
- this.volatileTimerGroup.TimerQueue.Count > 0)
- {
- ScheduleWait();
- }
- }
- void UpdateWaitableTimer(TimerGroup timerGroup)
- {
- WaitableTimer waitableTimer = timerGroup.WaitableTimer;
- IOThreadTimer minTimer = timerGroup.TimerQueue.MinTimer;
- long timeDiff = waitableTimer.DueTime - minTimer.dueTime;
- if (timeDiff < 0)
- {
- timeDiff = -timeDiff;
- }
- if (timeDiff > minTimer.maxSkew)
- {
- waitableTimer.Set(minTimer.dueTime);
- }
- }
- }
- class TimerGroup
- {
- TimerQueue timerQueue;
- WaitableTimer waitableTimer;
- public TimerGroup()
- {
- this.waitableTimer = new WaitableTimer();
- this.waitableTimer.Set(long.MaxValue);
- this.timerQueue = new TimerQueue();
- }
- public TimerQueue TimerQueue
- {
- get
- {
- return this.timerQueue;
- }
- }
- public WaitableTimer WaitableTimer
- {
- get
- {
- return this.waitableTimer;
- }
- }
- }
- class TimerQueue
- {
- int count;
- IOThreadTimer[] timers;
- public TimerQueue()
- {
- this.timers = new IOThreadTimer[4];
- }
- public int Count
- {
- get { return count; }
- }
- public IOThreadTimer MinTimer
- {
- get
- {
- Fx.Assert(this.count > 0, "Should have at least one timer in our queue.");
- return timers[1];
- }
- }
- public void DeleteMinTimer()
- {
- IOThreadTimer minTimer = this.MinTimer;
- DeleteMinTimerCore();
- minTimer.index = 0;
- minTimer.dueTime = 0;
- }
- public void DeleteTimer(IOThreadTimer timer)
- {
- int index = timer.index;
- Fx.Assert(index > 0, "");
- Fx.Assert(index <= this.count, "");
- IOThreadTimer[] timers = this.timers;
- for (;;)
- {
- int parentIndex = index / 2;
- if (parentIndex >= 1)
- {
- IOThreadTimer parentTimer = timers[parentIndex];
- timers[index] = parentTimer;
- parentTimer.index = index;
- }
- else
- {
- break;
- }
- index = parentIndex;
- }
- timer.index = 0;
- timer.dueTime = 0;
- timers[1] = null;
- DeleteMinTimerCore();
- }
- public bool InsertTimer(IOThreadTimer timer, long dueTime)
- {
- Fx.Assert(timer.index == 0, "Timer should not have an index.");
- IOThreadTimer[] timers = this.timers;
- int index = this.count + 1;
- if (index == timers.Length)
- {
- timers = new IOThreadTimer[timers.Length * 2];
- Array.Copy(this.timers, timers, this.timers.Length);
- this.timers = timers;
- }
- this.count = index;
- if (index > 1)
- {
- for (;;)
- {
- int parentIndex = index / 2;
- if (parentIndex == 0)
- {
- break;
- }
- IOThreadTimer parent = timers[parentIndex];
- if (parent.dueTime > dueTime)
- {
- timers[index] = parent;
- parent.index = index;
- index = parentIndex;
- }
- else
- {
- break;
- }
- }
- }
- timers[index] = timer;
- timer.index = index;
- timer.dueTime = dueTime;
- return index == 1;
- }
- public bool UpdateTimer(IOThreadTimer timer, long dueTime)
- {
- int index = timer.index;
- IOThreadTimer[] timers = this.timers;
- int count = this.count;
- Fx.Assert(index > 0, "");
- Fx.Assert(index <= count, "");
- int parentIndex = index / 2;
- if (parentIndex == 0 ||
- timers[parentIndex].dueTime <= dueTime)
- {
- int leftChildIndex = index * 2;
- if (leftChildIndex > count ||
- timers[leftChildIndex].dueTime >= dueTime)
- {
- int rightChildIndex = leftChildIndex + 1;
- if (rightChildIndex > count ||
- timers[rightChildIndex].dueTime >= dueTime)
- {
- timer.dueTime = dueTime;
- return index == 1;
- }
- }
- }
- DeleteTimer(timer);
- InsertTimer(timer, dueTime);
- return true;
- }
- void DeleteMinTimerCore()
- {
- int count = this.count;
- if (count == 1)
- {
- this.count = 0;
- this.timers[1] = null;
- }
- else
- {
- IOThreadTimer[] timers = this.timers;
- IOThreadTimer lastTimer = timers[count];
- this.count = --count;
- int index = 1;
- for (;;)
- {
- int leftChildIndex = index * 2;
- if (leftChildIndex > count)
- {
- break;
- }
- int childIndex;
- IOThreadTimer child;
- if (leftChildIndex < count)
- {
- IOThreadTimer leftChild = timers[leftChildIndex];
- int rightChildIndex = leftChildIndex + 1;
- IOThreadTimer rightChild = timers[rightChildIndex];
- if (rightChild.dueTime < leftChild.dueTime)
- {
- child = rightChild;
- childIndex = rightChildIndex;
- }
- else
- {
- child = leftChild;
- childIndex = leftChildIndex;
- }
- }
- else
- {
- childIndex = leftChildIndex;
- child = timers[childIndex];
- }
- if (lastTimer.dueTime > child.dueTime)
- {
- timers[index] = child;
- child.index = index;
- }
- else
- {
- break;
- }
- index = childIndex;
- if (leftChildIndex >= count)
- {
- break;
- }
- }
- timers[index] = lastTimer;
- lastTimer.index = index;
- timers[count + 1] = null;
- }
- }
- }
- [Fx.Tag.SynchronizationPrimitive(Fx.Tag.BlocksUsing.NonBlocking)]
- class WaitableTimer : WaitHandle
- {
- long dueTime;
- [Fx.Tag.SecurityNote(Critical = "Call the critical CreateWaitableTimer method in TimerHelper",
- Safe = "Doesn't leak information or resources")]
- [SecuritySafeCritical]
- public WaitableTimer()
- {
- this.SafeWaitHandle = TimerHelper.CreateWaitableTimer();
- }
- public long DueTime
- {
- get { return this.dueTime; }
- }
- [Fx.Tag.SecurityNote(Critical = "Call the critical Set method in TimerHelper",
- Safe = "Doesn't leak information or resources")]
- [SecuritySafeCritical]
- public void Set(long dueTime)
- {
- this.dueTime = TimerHelper.Set(this.SafeWaitHandle, dueTime);
- }
- [Fx.Tag.SecurityNote(Critical = "Provides a set of unsafe methods used to work with the WaitableTimer")]
- [SecurityCritical]
- static class TimerHelper
- {
- public static unsafe SafeWaitHandle CreateWaitableTimer()
- {
- SafeWaitHandle handle = UnsafeNativeMethods.CreateWaitableTimer(IntPtr.Zero, false, null);
- if (handle.IsInvalid)
- {
- Exception exception = new Win32Exception();
- handle.SetHandleAsInvalid();
- throw Fx.Exception.AsError(exception);
- }
- return handle;
- }
- public static unsafe long Set(SafeWaitHandle timer, long dueTime)
- {
- if (!UnsafeNativeMethods.SetWaitableTimer(timer, ref dueTime, 0, IntPtr.Zero, IntPtr.Zero, false))
- {
- throw Fx.Exception.AsError(new Win32Exception());
- }
- return dueTime;
- }
- }
- }
- }
- }
|