IOThreadTimer.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / cdf / src / System.Runtime.DurableInstancing / System / Runtime / IOThreadTimer.cs / 1305376 / IOThreadTimer.cs

                            //------------------------------------------------------------ 
// 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 callback;
        object callbackState;
        long dueTime; 

        int index; 
        long maxSkew; 
        TimerGroup timerGroup;
 
        public IOThreadTimer(Action callback, object callbackState, bool isTypicallyCanceledShortlyAfterBeingSet)
            : this(callback, callbackState, isTypicallyCanceledShortlyAfterBeingSet, maxSkewInMillisecondsDefault)
        {
        } 

        public IOThreadTimer(Action 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 onWaitCallback;
            TimerGroup stableTimerGroup; 
            TimerGroup volatileTimerGroup;
            [Fx.Tag.SynchronizationObject(Blocking = false)] 
            WaitableTimer[] waitableTimers; 

            bool waitScheduled; 

            public TimerManager()
            {
                this.onWaitCallback = new Action(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; 
                }
            } 
        }
    }
}
 


// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
                        

                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK