TimerTable.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 / NetFx40 / System.Activities / System / Activities / Statements / TimerTable.cs / 1305376 / TimerTable.cs

                            //------------------------------------------------------------------------------ 
// Copyright (c) Microsoft Corporation.  All rights reserved.
//-----------------------------------------------------------------------------

namespace System.Activities.Statements 
{
    using System; 
    using System.Collections.Generic; 
    using System.Linq;
    using System.Runtime; 
    using System.Runtime.Serialization;

    // This class won't be thread safe, it relies on the callers to synchronize addTimer and removeTimer
    [DataContract] 
    class TimerTable : IDisposable
    { 
        [DataMember] 
        SortedTimerList sortedTimerList;
 
        Dictionary timers;
        IOThreadTimer activeTimer;

        bool isImmutable; 

        Bookmark pendingRemoveBookmark; 
        Bookmark pendingRetryBookmark; 

        public TimerTable(DurableTimerExtension timerExtension) 
        {
            this.timers = new Dictionary();

            // Keep a sorted version of the list, so we don't have to loop through the whole list of timers everytime 
            this.sortedTimerList = new SortedTimerList();
            this.activeTimer = new IOThreadTimer(timerExtension.OnTimerFiredCallback, null, false, 0); 
        } 

        public int Count 
        {
            get
            {
                return this.sortedTimerList.Count; 
            }
        } 
 
        public void AddTimer(TimeSpan timeout, Bookmark bookmark)
        { 
            // Add timer is only called on the workflow thread,
            // It can't be racing with the persistence thread.
            // So the table MUST be mutable when this method is called
            Fx.Assert(!this.isImmutable, "Add timer is called when table is immutable"); 
            DateTime dueTime = TimeoutHelper.Add(DateTime.UtcNow, timeout);
            TimerData timerData = new TimerData(bookmark, dueTime); 
 
            this.timers.Add(bookmark, timerData);
 
            if (this.sortedTimerList.Count == 0)
            {
                this.sortedTimerList.Add(timerData);
                this.activeTimer.Set(timeout); 
            }
            else 
            { 
                TimerData latestTimer = this.sortedTimerList.First();
                this.sortedTimerList.Add(timerData); 
                if (latestTimer.ExpirationTime > dueTime)
                {
                    this.activeTimer.Set(timeout);
                } 
            }
        } 
 
        public void RemoveTimer(Bookmark bookmark)
        { 
            // When IOThread Timer calls back, it will call remove timer
            // In another thread, we may be in the middle of persistence.
            // During persisting, we will mark the table as immutable
            // After we are done writing to the database, we will buffer the remove request 
            // Meanwhile, since we are not scheduling any IOThreadTimers,
            // we can only have at most one pending Remove request 
            // We don't want to remove 
            if (!this.isImmutable)
            { 
                TimerData expirationTimeData;
                if (this.timers.TryGetValue(bookmark, out expirationTimeData))
                {
                    // Check if we need to schedule the next IOThread Timer 
                    TimerData currentActiveTimer = this.sortedTimerList.First();
                    this.timers.Remove(bookmark); 
                    this.sortedTimerList.Remove(expirationTimeData); 

                    if (currentActiveTimer.Equals(expirationTimeData)) 
                    {
                        this.activeTimer.Cancel();
                        if (this.sortedTimerList.Count > 0)
                        { 
                            TimerData newActiveTimer = this.sortedTimerList.First();
                            this.activeTimer.Set(newActiveTimer.ExpirationTime - DateTime.UtcNow); 
                        } 
                    }
                } 
            }
            else
            {
                this.pendingRemoveBookmark = bookmark; 
            }
        } 
 
        // Remove the timer from the table, and set expiration date to a new value.
        public void RetryTimer(Bookmark bookmark) 
        {
            // This value controls how many seconds do we retry
            const int retryDuration = 10;
 
            // When IOThread Timer calls back, it might call RetryTimer timer if ResumeBookmark returned notReady
            // In another thread, we may be in the middle of persistence. 
            // During persisting, we will mark the table as immutable 
            // After we are done writing to the database, we will buffer the remove request
            // Meanwhile, since we are not scheduling any IOThreadTimers, 
            // we can only have at most one pending Remove request
            // We don't want to remove
            if (!this.isImmutable)
            { 
                TimerData expirationTimeData;
                if (this.timers.TryGetValue(bookmark, out expirationTimeData)) 
                { 
                    // Check if we need to schedule the next IOThread Timer
                    TimerData currentActiveTimer = this.sortedTimerList.First(); 
                    this.timers.Remove(bookmark);
                    this.sortedTimerList.Remove(expirationTimeData);
                    // Update it to the retry time and put it back to the timer list
                    DateTime newExpirationTime = TimeoutHelper.Add(DateTime.UtcNow, TimeSpan.FromSeconds(retryDuration)); 
                    TimerData retryTimer = new TimerData(bookmark, newExpirationTime);
                    this.timers.Add(bookmark, retryTimer); 
                    this.sortedTimerList.Add(retryTimer); 
                    TimerData newActiveTimer = this.sortedTimerList.First();
                    this.activeTimer.Set(newActiveTimer.ExpirationTime - DateTime.UtcNow); 
                }
            }
            else
            { 
                this.pendingRetryBookmark = bookmark;
            } 
        } 

        public DateTime GetNextDueTime() 
        {
            DateTime nextDueTime = DateTime.MaxValue;

            if (this.sortedTimerList.Count > 0) 
            {
                nextDueTime = this.sortedTimerList.First().ExpirationTime; 
            } 

            return nextDueTime; 
        }

        public Bookmark GetNextExpiredBookmark()
        { 
            Bookmark bookmark = null;
 
            if (this.sortedTimerList.Count > 0) 
            {
                bookmark = this.sortedTimerList.First().Bookmark; 
            }

            return bookmark;
 
        }
 
        public void OnLoad(DurableTimerExtension timerExtension) 
        {
            this.timers = new Dictionary(); 
            this.activeTimer = new IOThreadTimer(timerExtension.OnTimerFiredCallback, null, false, 0);

            foreach (TimerData timeData in this.sortedTimerList.Timers)
            { 
                this.timers.Add(timeData.Bookmark, timeData);
            } 
 
            if (this.sortedTimerList.Count > 0)
            { 
                TimerData currentActiveTimer = this.sortedTimerList.First();
                if (currentActiveTimer.ExpirationTime <= DateTime.UtcNow)
                {
                    // If the timer expired, we want to fire it immediately to win the ---- against UnloadOnIdle policy 
                    timerExtension.OnTimerFiredCallback(currentActiveTimer.Bookmark);
                } 
                else 
                {
                    this.activeTimer.Set(currentActiveTimer.ExpirationTime - DateTime.UtcNow); 
                }
            }
        }
 
        public void MarkAsImmutable()
        { 
            this.isImmutable = true; 
        }
 
        public void MarkAsMutable()
        {
            if (this.isImmutable)
            { 
                this.isImmutable = false;
                if (this.pendingRemoveBookmark != null) 
                { 
                    this.RemoveTimer(this.pendingRemoveBookmark);
                    this.pendingRemoveBookmark = null; 
                }

                if (this.pendingRetryBookmark != null)
                { 
                    this.RetryTimer(this.pendingRetryBookmark);
                    this.pendingRetryBookmark = null; 
                } 
            }
        } 

        public void Dispose()
        {
            // Cancel the active timer so we stop retrying 
            this.activeTimer.Cancel();
            // And we clear the table and other member variables that might cause the retry logic 
            this.timers.Clear(); 
            this.sortedTimerList.Clear();
            this.pendingRemoveBookmark = null; 
            this.pendingRetryBookmark = null;
        }

        [DataContract] 
        class SortedTimerList
        { 
            [DataMember] 
            List list;
            TimerComparer timerComparer; 

            public SortedTimerList()
            {
                this.list = new List(); 
            }
 
            public List Timers 
            {
                get 
                {
                    return this.list;
                }
            } 

            // Assume that the caller would synchronize 
            TimerComparer TimerComparer 
            {
                get 
                {
                    if (this.timerComparer == null)
                    {
                        this.timerComparer = new TimerComparer(); 
                    }
                    return this.timerComparer; 
                } 
            }
 
            public int Count
            {
                get
                { 
                    return this.list.Count;
                } 
            } 

            public void Add(TimerData timerData) 
            {
                int index = this.list.BinarySearch(timerData, this.TimerComparer);
                if (index < 0)
                { 
                    this.list.Insert(~index, timerData);
                } 
            } 

            public void Remove(TimerData timerData) 
            {
                int index = this.list.BinarySearch(timerData, this.TimerComparer);
                if (index >= 0)
                { 
                    this.list.RemoveAt(index);
                } 
            } 

            public TimerData First() 
            {
                return this.list.First();
            }
 
            public void Clear()
            { 
                this.list.Clear(); 
            }
        } 

        class TimerComparer : IComparer
        {
            public int Compare(TimerData x, TimerData y) 
            {
                if (object.ReferenceEquals(x, y)) 
                { 
                    return 0;
                } 
                else
                {
                    if (x == null)
                    { 
                        return -1;
                    } 
                    else 
                    {
                        if (y == null) 
                        {
                            return 1;
                        }
                        else 
                        {
                            if (x.ExpirationTime == y.ExpirationTime) 
                            { 
                                if (x.Bookmark.IsNamed)
                                { 
                                    if (y.Bookmark.IsNamed)
                                    {
                                        return string.Compare(x.Bookmark.Name, y.Bookmark.Name, StringComparison.OrdinalIgnoreCase);
                                    } 
                                    else
                                    { 
                                        return 1; 
                                    }
                                } 
                                else
                                {
                                    if (y.Bookmark.IsNamed)
                                    { 
                                        return -1;
                                    } 
                                    else 
                                    {
                                        return x.Bookmark.Id.CompareTo(y.Bookmark.Id); 
                                    }
                                }
                            }
                            else 
                            {
                                return x.ExpirationTime.CompareTo(y.ExpirationTime); 
                            } 
                        }
                    } 
                }
            }
        }
 
        [DataContract]
        class TimerData : IEquatable 
        { 
            public TimerData(Bookmark bookmark, DateTime expirationTime)
            { 
                this.Bookmark = bookmark;
                this.ExpirationTime = expirationTime;
            }
 
            [DataMember]
            public Bookmark Bookmark 
            { 
                get;
                private set; 
            }

            [DataMember]
            public DateTime ExpirationTime 
            {
                get; 
                private set; 
            }
 
            public bool Equals(TimerData other)
            {
                if (this.ExpirationTime == other.ExpirationTime)
                { 
                    return this.Bookmark.Equals(other.Bookmark);
                } 
                else 
                {
                    return this.ExpirationTime.Equals(other.ExpirationTime); 
                }
            }
        }
    } 
}

// 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