context: /***************************************************************************
   *                                 Timer.cs
   *                            -------------------
   *   begin                : May 1, 2002
   *   copyright            : (C) The RunUO Software Team
   *   email                : info@runuo.com
   *
   *   $Id: Timer.cs 362 2009-08-09 02:20:57Z mark $
   *
   ***************************************************************************/
  
  /***************************************************************************
   *
   *   This program is free software; you can redistribute it and/or modify
   *   it under the terms of the GNU General Public License as published by
   *   the Free Software Foundation; either version 2 of the License, or
   *   (at your option) any later version.
   *
   ***************************************************************************/
  
  using System;
  using System.Collections;
  using System.Collections.Generic;
  using System.Diagnostics;
  using System.IO;
  using System.Threading;
  using Server.Diagnostics;
  
  namespace Server
  {
  	public enum TimerPriority
  	{
  		EveryTick,
  		TenMS,
  		TwentyFiveMS,
  		FiftyMS,
  		TwoFiftyMS,
  		OneSecond,
  		FiveSeconds,
  		OneMinute
  	}
  
  	public delegate void TimerCallback();
  	public delegate void TimerStateCallback( object state );
  	public delegate void TimerStateCallback<T>( T state );
  
  	public class Timer
  	{
  		private DateTime m_Next;
  		private TimeSpan m_Delay;
  		private TimeSpan m_Interval;
  		private bool m_Running;
  		private int m_Index, m_Count;
  		private TimerPriority m_Priority;
  		private List<Timer> m_List;
  		private bool m_PrioritySet;
  
  		private static string FormatDelegate( Delegate callback )
  		{
  			if ( callback == null )
  				return "null";
  
  			return String.Format( "{0}.{1}", callback.Method.DeclaringType.FullName, callback.Method.Name );
  		}
  
  		public static void DumpInfo( TextWriter tw )
  		{
  			TimerThread.DumpInfo( tw );
  		}
  
  		public TimerPriority Priority
  		{
  			get
  			{
  				return m_Priority;
  			}
  			set
  			{
  				if ( !m_PrioritySet )
  					m_PrioritySet = true;
  
  				if ( m_Priority != value )
  				{
  					m_Priority = value;
  
  					if ( m_Running )
  						TimerThread.PriorityChange( this, (int)m_Priority );
  				}
  			}
  		}
  
  		public DateTime Next
  		{
  			get { return m_Next; }
  		}
  
  		public TimeSpan Delay
  		{
  			get { return m_Delay; }
  			set { m_Delay = value; }
  		}
  
  		public TimeSpan Interval
  		{
  			get { return m_Interval; }
  			set { m_Interval = value; }
  		}
  
  		public bool Running
  		{
  			get { return m_Running; }
  			set {
  				if ( value ) {
  					Start();
  				} else {
  					Stop();
  				}
  			}
  		}
  
  		public TimerProfile GetProfile()
  		{
  			if ( !Core.Profiling ) {
  				return null;
  			}
  
  			string name = ToString();
  
  			if ( name == null ) {
  				name = "null";
  			}
  
  			return TimerProfile.Acquire( name );
  		}
  
  		public class TimerThread
  		{
  			private static Queue m_ChangeQueue = Queue.Synchronized( new Queue() );
  
  			private static DateTime[] m_NextPriorities = new DateTime[8];
  			private static TimeSpan[] m_PriorityDelays = new TimeSpan[8]
  			{
  				TimeSpan.Zero,
  				TimeSpan.FromMilliseconds( 10.0 ),
  				TimeSpan.FromMilliseconds( 25.0 ),
  				TimeSpan.FromMilliseconds( 50.0 ),
  				TimeSpan.FromMilliseconds( 250.0 ),
  				TimeSpan.FromSeconds( 1.0 ),
  				TimeSpan.FromSeconds( 5.0 ),
  				TimeSpan.FromMinutes( 1.0 )
  			};
  
  			private static List<Timer>[] m_Timers = new List<Timer>[8]
  			{
  				new List<Timer>(),
  				new List<Timer>(),
  				new List<Timer>(),
  				new List<Timer>(),
  				new List<Timer>(),
  				new List<Timer>(),
  				new List<Timer>(),
  				new List<Timer>(),
  			};
  
  			public static void DumpInfo( TextWriter tw )
  			{
  				for ( int i = 0; i < 8; ++i )
  				{
  					tw.WriteLine( "Priority: {0}", (TimerPriority)i );
  					tw.WriteLine();
  
  					Dictionary<string, List<Timer>> hash = new Dictionary<string, List<Timer>>();
  
  					for ( int j = 0; j < m_Timers[i].Count; ++j )
  					{
  						Timer t = m_Timers[i][j];
  
  						string key = t.ToString();
  
  						List<Timer> list;
  						hash.TryGetValue( key, out list );
  
  						if ( list == null )
  							hash[key] = list = new List<Timer>();
  
  						list.Add( t );
  					}
  
  					foreach ( KeyValuePair<string, List<Timer>> kv in hash )
  					{
  						string key = kv.Key;
  						List<Timer> list = kv.Value;
  
  						tw.WriteLine( "Type: {0}; Count: {1}; Percent: {2}%", key, list.Count, (int)(100 * (list.Count / (double)m_Timers[i].Count)) );
  					}
  
  					tw.WriteLine();
  					tw.WriteLine();
  				}
  			}
  
  			private class TimerChangeEntry
  			{
  				public Timer m_Timer;
  				public int m_NewIndex;
  				public bool m_IsAdd;
  
  				private TimerChangeEntry( Timer t, int newIndex, bool isAdd )
  				{
  					m_Timer = t;
  					m_NewIndex = newIndex;
  					m_IsAdd = isAdd;
  				}
  
  				public void Free()
  				{
  					//m_InstancePool.Enqueue( this );
  				}
  
  				private static Queue<TimerChangeEntry> m_InstancePool = new Queue<TimerChangeEntry>();
  
  				public static TimerChangeEntry GetInstance( Timer t, int newIndex, bool isAdd )
  				{
  					TimerChangeEntry e;
  
  					if ( m_InstancePool.Count > 0 )
  					{
  						e = m_InstancePool.Dequeue();
  
  						if ( e == null )
  							e = new TimerChangeEntry( t, newIndex, isAdd );
  						else
  						{
  							e.m_Timer = t;
  							e.m_NewIndex = newIndex;
  							e.m_IsAdd = isAdd;
  						}
  					}
  					else
  					{
  						e = new TimerChangeEntry( t, newIndex, isAdd );
  					}
  
  					return e;
  				}
  			}
  
  			public TimerThread()
  			{
  			}
  
  			public static void Change( Timer t, int newIndex, bool isAdd )
  			{
  				m_ChangeQueue.Enqueue( TimerChangeEntry.GetInstance( t, newIndex, isAdd ) );
  				m_Signal.Set();
  			}
  
  			public static void AddTimer( Timer t )
  			{
  				Change( t, (int)t.Priority, true );
  			}
  
  			public static void PriorityChange( Timer t, int newPrio )
  			{
  				Change( t, newPrio, false );
  			}
  
  			public static void RemoveTimer( Timer t )
  			{
  				Change( t, -1, false );
  			}
  
  			private static void ProcessChangeQueue()
  			{
  				while ( m_ChangeQueue.Count > 0 )
  				{
  					TimerChangeEntry tce = (TimerChangeEntry)m_ChangeQueue.Dequeue();
  					Timer timer = tce.m_Timer;
  					int newIndex = tce.m_NewIndex;
  
  					if ( timer.m_List != null )
  						timer.m_List.Remove( timer );
  
  					if ( tce.m_IsAdd )
  					{
  						timer.m_Next = DateTime.Now + timer.m_Delay;
  						timer.m_Index = 0;
  					}
  
  					if ( newIndex >= 0 )
  					{
  						timer.m_List = m_Timers[newIndex];
  						timer.m_List.Add( timer );
  					}
  					else
  					{
  						timer.m_List = null;
  					}
  
  					tce.Free();
  				}
  			}
  
  			private static AutoResetEvent m_Signal = new AutoResetEvent( false );
  			public static void Set() { m_Signal.Set(); }
  
  			public void TimerMain()
  			{
  				DateTime now;
  				int i, j;
  				bool loaded;
  
  				while ( !Core.Closing )
  				{
  					ProcessChangeQueue();
  
  					loaded = false;
  
  					for ( i = 0; i < m_Timers.Length; i++)
  					{
  						now = DateTime.Now;
  						if ( now < m_NextPriorities[i] )
  							break;
  
  						m_NextPriorities[i] = now + m_PriorityDelays[i];
  
  						for ( j = 0; j < m_Timers[i].Count; j++)
  						{
  							Timer t = m_Timers[i][j];
  
  							if ( !t.m_Queued && now > t.m_Next )
  							{
  								t.m_Queued = true;
  
  								lock ( m_Queue )
  									m_Queue.Enqueue( t );
  
  								loaded = true;
  									
  								if ( t.m_Count != 0 && (++t.m_Index >= t.m_Count) )
  								{
  									t.Stop();
  								}
  								else
  								{
  									t.m_Next = now + t.m_Interval;
  								}
  							}
  						}
  					}
  
  					if ( loaded )
  						Core.Set();
  
  					m_Signal.WaitOne( 10, false );
  				}
  			}
  		}
  
  		private static Queue<Timer> m_Queue = new Queue<Timer>();
  		private static int m_BreakCount = 20000;
  
  		public static int BreakCount{ get{ return m_BreakCount; } set{ m_BreakCount = value; } }
  
  		private static int m_QueueCountAtSlice;
  
  		private bool m_Queued;
  
  		public static void Slice()
  		{
  			lock ( m_Queue )
  			{
  				m_QueueCountAtSlice = m_Queue.Count;
  
  				int index = 0;
  
  				while ( index < m_BreakCount && m_Queue.Count != 0 )
  				{
  					Timer t = m_Queue.Dequeue();
  					TimerProfile prof = t.GetProfile();
  
  					if ( prof != null ) {
  						prof.Start();
  					}
  
  					t.OnTick();
  					t.m_Queued = false;
  					++index;
  
  					if ( prof != null ) {
  						prof.Finish();
  					}
  				}
  			}
  		}
  
  		public Timer( TimeSpan delay ) : this( delay, TimeSpan.Zero, 1 )
  		{
  		}
  
  		public Timer( TimeSpan delay, TimeSpan interval ) : this( delay, interval, 0 )
  		{
  		}
  
  		public virtual bool DefRegCreation
  		{
  			get{ return true; }
  		}
  
  		public virtual void RegCreation()
  		{
  			TimerProfile prof = GetProfile();
  
  			if ( prof != null ) {
  				prof.Created++;
  			}
  		}
  
  		public Timer( TimeSpan delay, TimeSpan interval, int count )
  		{
  			m_Delay = delay;
  			m_Interval = interval;
  			m_Count = count;
  
  			if ( !m_PrioritySet ) {
  				if ( count == 1 ) {
  					m_Priority = ComputePriority( delay );
  				} else {
  					m_Priority = ComputePriority( interval );
  				}
  				m_PrioritySet = true;
  			}
  
  			if ( DefRegCreation )
  				RegCreation();
  		}
  
  		public override string ToString()
  		{
  			return GetType().FullName;
  		}
  
  		public static TimerPriority ComputePriority( TimeSpan ts )
  		{
  			if ( ts >= TimeSpan.FromMinutes( 1.0 ) )
  				return TimerPriority.FiveSeconds;
  
  			if ( ts >= TimeSpan.FromSeconds( 10.0 ) )
  				return TimerPriority.OneSecond;
  
  			if ( ts >= TimeSpan.FromSeconds( 5.0 ) )
  				return TimerPriority.TwoFiftyMS;
  
  			if ( ts >= TimeSpan.FromSeconds( 2.5 ) )
  				return TimerPriority.FiftyMS;
  
  			if ( ts >= TimeSpan.FromSeconds( 1.0 ) )
  				return TimerPriority.TwentyFiveMS;
  
  			if ( ts >= TimeSpan.FromSeconds( 0.5 ) )
  				return TimerPriority.TenMS;
  
  			return TimerPriority.EveryTick;
  		}
  
  		#region DelayCall(..)
  
  		public static Timer DelayCall( TimeSpan delay, TimerCallback callback )
  		{
  			return DelayCall( delay, TimeSpan.Zero, 1, callback );
  		}
  
  		public static Timer DelayCall( TimeSpan delay, TimeSpan interval, TimerCallback callback )
  		{
  			return DelayCall( delay, interval, 0, callback );
  		}
  
  		public static Timer DelayCall( TimeSpan delay, TimeSpan interval, int count, TimerCallback callback )
  		{
  			Timer t = new DelayCallTimer( delay, interval, count, callback );
  
  			if ( count == 1 )
  				t.Priority = ComputePriority( delay );
  			else
  				t.Priority = ComputePriority( interval );
  
  			t.Start();
  
  			return t;
  		}
  
  		public static Timer DelayCall( TimeSpan delay, TimerStateCallback callback, object state )
  		{
  			return DelayCall( delay, TimeSpan.Zero, 1, callback, state );
  		}
  
  		public static Timer DelayCall( TimeSpan delay, TimeSpan interval, TimerStateCallback callback, object state )
  		{
  			return DelayCall( delay, interval, 0, callback, state );
  		}
  
  		public static Timer DelayCall( TimeSpan delay, TimeSpan interval, int count, TimerStateCallback callback, object state )
  		{
  			Timer t = new DelayStateCallTimer( delay, interval, count, callback, state );
  
  			if ( count == 1 )
  				t.Priority = ComputePriority( delay );
  			else
  				t.Priority = ComputePriority( interval );
  
  			t.Start();
  
  			return t;
  		}
  		#endregion
  
  		#region DelayCall<T>(..)
  		public static Timer DelayCall<T>( TimeSpan delay, TimerStateCallback<T> callback, T state )
  		{
  			return DelayCall( delay, TimeSpan.Zero, 1, callback, state );
  		}
  
  		public static Timer DelayCall<T>( TimeSpan delay, TimeSpan interval, TimerStateCallback<T> callback, T state )
  		{
  			return DelayCall( delay, interval, 0, callback, state );
  		}
  
  		public static Timer DelayCall<T>( TimeSpan delay, TimeSpan interval, int count, TimerStateCallback<T> callback, T state )
  		{
  			Timer t = new DelayStateCallTimer<T>( delay, interval, count, callback, state );
  
  			if( count == 1 )
  
gt: 				t.Priority = ComputePriority( delay );