All pastes #562325 Raw Edit

Unnamed

public text v1 · immutable
·published 2007-06-12 19:18 UTC
rendered paste body
public class TimerWheel : IDisposable
    {
        public const int DefaultSectorCount = 0x200;
        public const int DefaultSectorSpan = 1;
        private int m_currentSectorIndex;
        private bool m_disposed;
        private int m_sectorCount;
        private List<TimerItem>[] m_sectors;
        private int m_sectorSpan;
        private Timer m_timer;
        public const int MaxCurrentCount = 0xFF;
        public const int MaxSectors = 0xFFFF;
        public const int MaxSpanSeconds = 0xFFFF;

        public TimerWheel()
            : this(0x200, new TimeSpan(0, 0, 1))
        {
        }

        public TimerWheel(int sectorCount, TimeSpan sectorSpan)
        {
            if ((sectorCount <= 0) || (sectorCount >= 0xFFFF))
                throw new ArgumentOutOfRangeException("sectorCount");
            
            int totalSeconds = TimerItem.TotalSeconds(sectorSpan);
            if (totalSeconds < 1)
                throw new ArgumentOutOfRangeException("sectorSpan");
            
            m_sectorCount = sectorCount;
            m_sectorSpan = totalSeconds;
            m_sectors = new List<TimerItem>[m_sectorCount];
            for (int i = 0; i < m_sectorCount; i++)
            {
                m_sectors[i] = new List<TimerItem>();
            }
            m_timer = new Timer((double)(m_sectorSpan * 0x3E8));
            m_timer.AutoReset = true;
            m_timer.Elapsed += Timer_Elapsed;
            m_timer.Start();
        }

        public void Add(TimerItem timerItem)
        {
            if (timerItem == null)
                throw new ArgumentNullException("timerItem");
            if (timerItem.Wheel != this)
                throw new InvalidOperationException("Invalid Timer Addition");
            
            if (!timerItem.IsStarted)
            {
                int currentSectorIndex = m_currentSectorIndex;
                int num2 = timerItem.SpanSeconds / m_sectorSpan;
                timerItem.CurrentCount = (num2 / m_sectorCount) + 1;
                int index = (num2 + currentSectorIndex) % m_sectorCount;
                if (index == currentSectorIndex)
                {
                    index = (index + 1) % m_sectorCount;
                }
                timerItem.SectorIndex = index;
                lock (m_sectors[index])
                {
                    m_sectors[index].Add(timerItem);
                    timerItem.SectorListIndex = m_sectors[index].Count - 1;
                }
            }
        }

        protected void CheckDisposed()
        {
            if (m_disposed)
            {
                throw new ObjectDisposedException(GetType().ToString());
            }
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (!m_disposed)
            {
                m_timer.Elapsed -= Timer_Elapsed;
                m_timer.Dispose();
                m_disposed = true;
            }
        }

        ~TimerWheel()
        {
            Dispose(true);
        }

        public TimeSpan GetRemainingTimeToExpire(TimerItem item)
        {
            if (item == null)
                throw new ArgumentNullException("item");
            if (item.Wheel != this)
                throw new ArgumentException("Invalid Timer Item");
            
            lock (m_sectors)
            {
                int num = (item.CurrentCount - 1) * m_sectorCount;
                if (item.SectorIndex < m_currentSectorIndex)
                {
                    num += m_currentSectorIndex - item.SectorIndex;
                }
                else
                {
                    num += m_sectorCount - (m_currentSectorIndex - item.SectorIndex);
                }
                return new TimeSpan(0, 0, num * m_sectorSpan);
            }
        }

        public bool Remove(TimerItem timerItem)
        {
            if (timerItem == null)
                throw new ArgumentNullException("timerItem");

            if (timerItem.Wheel != this)
                throw new InvalidOperationException("Invalid Timer Removal");

            if (timerItem.IsStarted)
            {
                int index = timerItem.SectorIndex;
                if ((index < 0) || (index > m_sectors.Length))
                {
                    return false;
                }
                lock (m_sectors[index])
                {
                    int sectorListIndex = timerItem.SectorListIndex;
                    if ((sectorListIndex < m_sectors[index].Count) && (m_sectors[index][sectorListIndex] == timerItem))
                    {
                        if (sectorListIndex != (m_sectors[index].Count - 1))
                        {
                            TimerItem item = m_sectors[index][m_sectors[index].Count - 1];
                            m_sectors[index][sectorListIndex] = item;
                            item.SectorListIndex = sectorListIndex;
                        }

                        m_sectors[index].RemoveAt(m_sectors[index].Count - 1);
                        timerItem.SectorIndex = 0xFFFF;

                        return true;
                    }
                    return false;
                }
            }
            return false;
        }

        private void Timer_Elapsed(object sender, ElapsedEventArgs e)
        {
            int index;
            lock (m_sectors)
            {
                index = m_currentSectorIndex;
                m_currentSectorIndex = (m_currentSectorIndex + 1) % m_sectorCount;
            }

            List<TimerItem> timersToRemove = null;
            int count = 0;
            lock (m_sectors[index])
            {
                int listCount = m_sectors[index].Count;
                int num4 = listCount - 1;

                for (int i = num4; i >= 0; i--)
                {
                    // handles moving timers down the list
                    TimerItem item = m_sectors[index][i];
                    if (item.DecrementCurrentCount() == 0)
                    {
                        item.SectorIndex = 0xFFFF;
                        TimerItem item2 = m_sectors[index][num4];
                        m_sectors[index][i] = item2;
                        item2.SectorListIndex = i;
                        m_sectors[index][num4--] = item;
                    }
                }

                count = (listCount - num4) - 1;
                if (count > 0)
                {
                    timersToRemove = m_sectors[index].GetRange(num4 + 1, count);

                    m_sectors[index].RemoveRange(num4 + 1, count);
                }
            }
            if (timersToRemove != null)
            {
                foreach (TimerItem timerItem in timersToRemove)
                {
                    System.Threading.ThreadPool.QueueUserWorkItem(timerItem.OnExpired, null);
                }
            }
        }
    }

    public class TimerItem
    {
        private const ulong SpanSecondsMask = 0xFFFF000000000000;
        private const ulong SectorIndexMask = 0x0000FFFF00000000;
        private const ulong SectorListIndexMask = 0x0000000000FFFFFF;
        private const ulong CurrentCountMask = 0x00000000FF000000;
        private const int SectorListIndexShift = 0;
        private const int CurrentCountShift = 0x18;
        private const int SectorIndexShift = 0x20;      
        private const int SpanSecondsShift = 0x30;

        private ulong m_state;
        private TimerWheel m_timerWheel;
        public event EventHandler Expired;

        public TimerItem(TimerWheel timerWheel, TimeSpan timeSpan)
        {
            if (timerWheel == null)
                throw new ArgumentNullException("timerWheel");

            int totalSeconds = TotalSeconds(timeSpan);

            if ((totalSeconds > 0xFFFF) || (totalSeconds < 1))
                throw new ArgumentOutOfRangeException("timeSpan");

            m_timerWheel = timerWheel;
            SpanSeconds = totalSeconds;
            SectorIndex = 0xFFFF;
        }

        public int DecrementCurrentCount()
        {
            int currentCount = (int)((m_state & CurrentCountMask) >> CurrentCountShift);
            --currentCount;
            m_state &= ~CurrentCountMask;
            m_state |= (ulong)(currentCount << CurrentCountShift) & CurrentCountMask;
            return currentCount;
        }

        internal void OnExpired(object state)
        {
            if (Expired != null)
            {
                Expired(this, EventArgs.Empty);
            }
        }

        public void Reset()
        {
            Reset(TimerSpan);
        }

        public void Reset(TimeSpan newTimerSpan)
        {
            int totalSeconds = TotalSeconds(newTimerSpan);
            if ((totalSeconds > 0xFFFF) || (totalSeconds < 1))
                throw new ArgumentOutOfRangeException("newTimerSpan");

            lock (this)
            {
                if (IsStarted)
                {
                    m_timerWheel.Remove(this);
                }
                SpanSeconds = totalSeconds;
                SectorIndex = 0xFFFF;
                m_timerWheel.Add(this);
            }
        }

        public void Start()
        {
            lock (this)
            {
                if (!IsStarted)
                {
                    m_timerWheel.Add(this);
                }
            }
        }

        public void Stop()
        {
            lock (this)
            {
                if (IsStarted)
                {
                    m_timerWheel.Remove(this);
                }
            }
        }

        internal static int TotalSeconds(TimeSpan timeSpan)
        {
            return (int)(timeSpan.Ticks / ((long)0x989680));
        }

        public int CurrentCount
        {
            get
            {
                return (int)((m_state & CurrentCountMask) >> CurrentCountShift);
            }
            set
            {
                if (value > 0xFF)
                    throw new ArgumentOutOfRangeException("value");

                m_state &= ~CurrentCountMask;
                m_state |= (((ulong)value) << CurrentCountShift) & CurrentCountMask;
            }
        }

        public bool IsStarted
        {
            get { return (SectorIndex != 0xFFFF); }
        }

        public TimeSpan RemainingTime
        {
            get { return m_timerWheel.GetRemainingTimeToExpire(this); }
        }

        public int SectorIndex
        {
            get
            {
                return (int)((m_state & SectorIndexMask) >> SectorIndexShift);
            }
            set
            {
                if (value > 0xFFFF)
                    throw new ArgumentOutOfRangeException("value");

                m_state &= ~SectorIndexMask;//0xFFFF0000FFFFFFFF;
                m_state |= (ulong)(((ulong)value << SectorIndexShift) & SectorIndexMask);
            }
        }

        public int SectorListIndex
        {
            get
            {
                //return (((int)m_state) & 0xFFFFFF);
                return (int)(m_state & SectorListIndexMask);
            }
            set
            {
                //m_state &= 0xFFFFFFFFFF000000;
                m_state &= ~SectorListIndexMask;
                //m_state |= (ulong)(value & 0xFFFFFF);
                m_state |= (ulong)value & SectorListIndexMask;
            }
        }

        public int SpanSeconds
        {
            get
            {
                return (int)((m_state & SpanSecondsMask) >> SpanSecondsShift);
            }
            private set
            {
                if (value > 0xFFFF)
                    throw new ArgumentOutOfRangeException("value");

                m_state &= ~SpanSecondsMask;
                m_state |= (ulong)((ulong)(value << SpanSecondsShift) & SpanSecondsMask);
            }
        }

        internal ulong State
        {
            get { return m_state; }
        }

        public TimeSpan TimerSpan
        {
            get { return new TimeSpan(0, 0, SpanSeconds); }
        }

        public TimerWheel Wheel
        {
            get { return m_timerWheel; }
        }
    }