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[] 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[m_sectorCount]; for (int i = 0; i < m_sectorCount; i++) { m_sectors[i] = new List(); } 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 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; } } }