diff --git a/Projects/Server/Timer/Timer.DelayCall.cs b/Projects/Server/Timer/Timer.DelayCall.cs index dd2a75cd0..312801e64 100644 --- a/Projects/Server/Timer/Timer.DelayCall.cs +++ b/Projects/Server/Timer/Timer.DelayCall.cs @@ -20,211 +20,207 @@ using System.Collections.Generic; using System.Diagnostics; using System.Runtime.CompilerServices; -namespace Server +namespace Server; + +public partial class Timer { - public partial class Timer + private static string FormatDelegate(Delegate callback) => + callback == null ? "null" : $"{callback.Method.DeclaringType?.FullName ?? ""}.{callback.Method.Name}"; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static DelayCallTimer DelayCall(Action callback) => DelayCall(TimeSpan.Zero, TimeSpan.Zero, 1, callback); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static DelayCallTimer DelayCall(TimeSpan delay, Action callback) => DelayCall(delay, TimeSpan.Zero, 1, callback); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static DelayCallTimer DelayCall(TimeSpan delay, TimeSpan interval, Action callback) => + DelayCall(delay, interval, 0, callback); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static DelayCallTimer DelayCall(TimeSpan interval, int count, Action callback) => + DelayCall(TimeSpan.Zero, interval, count, callback); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static DelayCallTimer DelayCall(TimeSpan delay, TimeSpan interval, int count, Action callback) { - private static string FormatDelegate(Delegate callback) => - callback == null ? "null" : $"{callback.Method.DeclaringType?.FullName ?? ""}.{callback.Method.Name}"; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static DelayCallTimer DelayCall(Action callback) => DelayCall(TimeSpan.Zero, TimeSpan.Zero, 1, callback); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static DelayCallTimer DelayCall(TimeSpan delay, Action callback) => DelayCall(delay, TimeSpan.Zero, 1, callback); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static DelayCallTimer DelayCall(TimeSpan delay, TimeSpan interval, Action callback) => - DelayCall(delay, interval, 0, callback); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static DelayCallTimer DelayCall(TimeSpan interval, int count, Action callback) => - DelayCall(TimeSpan.Zero, interval, count, callback); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static DelayCallTimer DelayCall(TimeSpan delay, TimeSpan interval, int count, Action callback) - { - DelayCallTimer t = new DelayCallTimer(delay, interval, count, callback); - t.Start(); + DelayCallTimer t = new DelayCallTimer(delay, interval, count, callback); + t.Start(); #if DEBUG_TIMERS - t._allowFinalization = true; + t._allowFinalization = true; #endif - return t; + return t; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void StartTimer(Action callback) => StartTimer(TimeSpan.Zero, TimeSpan.Zero, 1, callback); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void StartTimer(TimeSpan delay, Action callback) => StartTimer(delay, TimeSpan.Zero, 1, callback); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void StartTimer(TimeSpan delay, TimeSpan interval, Action callback) => + StartTimer(delay, interval, 0, callback); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void StartTimer(TimeSpan interval, int count, Action callback) => + StartTimer(TimeSpan.Zero, interval, count, callback); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void StartTimer(TimeSpan delay, TimeSpan interval, int count, Action callback) + { + DelayCallTimer t = DelayCallTimer.GetTimer(delay, interval, count, callback); + t._returnOnDetach = true; + t.Start(); + +#if DEBUG_TIMERS + DelayCallTimer._stackTraces[t.GetHashCode()] = $"{callback.Method.Name}\n{new StackTrace()}"; +#endif + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void StartTimer(Action callback, out TimerExecutionToken token) => + StartTimer(TimeSpan.Zero, TimeSpan.Zero, 1, callback, out token); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void StartTimer(TimeSpan delay, Action callback, out TimerExecutionToken token) => + StartTimer(delay, TimeSpan.Zero, 1, callback, out token); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void StartTimer(TimeSpan delay, TimeSpan interval, Action callback, out TimerExecutionToken token) => + StartTimer(delay, interval, 0, callback, out token); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void StartTimer(TimeSpan interval, int count, Action callback, out TimerExecutionToken token) => + StartTimer(TimeSpan.Zero, interval, count, callback, out token); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void StartTimer(TimeSpan delay, TimeSpan interval, int count, Action callback, out TimerExecutionToken token) + { + DelayCallTimer t = DelayCallTimer.GetTimer(delay, interval, count, callback); + t.Start(); + +#if DEBUG_TIMERS + DelayCallTimer._stackTraces[t.GetHashCode()] = $"{callback.Method.Name}\n{new StackTrace()}"; +#endif + token = new TimerExecutionToken(t); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static DelayCallTimer Pause(TimeSpan ms) => new(ms); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static DelayCallTimer Pause(int ms) => Pause(TimeSpan.FromMilliseconds(ms)); + + public sealed class DelayCallTimer : Timer, INotifyCompletion + { + internal bool _returnOnDetach; +#if DEBUG_TIMERS + internal bool _allowFinalization; +#endif + private Action _continuation; + private bool _complete; + + internal DelayCallTimer(TimeSpan delay, TimeSpan interval, int count, Action callback) : base( + delay, + interval, + count + ) => _continuation = callback; + + internal DelayCallTimer(TimeSpan delay) : base(delay) + { +#if DEBUG_TIMERS + _allowFinalization = true; +#endif + Start(); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void StartTimer(Action callback) => StartTimer(TimeSpan.Zero, TimeSpan.Zero, 1, callback); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void StartTimer(TimeSpan delay, Action callback) => StartTimer(delay, TimeSpan.Zero, 1, callback); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void StartTimer(TimeSpan delay, TimeSpan interval, Action callback) => - StartTimer(delay, interval, 0, callback); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void StartTimer(TimeSpan interval, int count, Action callback) => - StartTimer(TimeSpan.Zero, interval, count, callback); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void StartTimer(TimeSpan delay, TimeSpan interval, int count, Action callback) + protected override void OnTick() { - DelayCallTimer t = DelayCallTimer.GetTimer(delay, interval, count, callback); - t._returnOnDetach = true; - t.Start(); - -#if DEBUG_TIMERS - DelayCallTimer._stackTraces[t.GetHashCode()] = new StackTrace().ToString(); -#endif + _complete = true; + _continuation?.Invoke(); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void StartTimer(Action callback, out TimerExecutionToken token) => - StartTimer(TimeSpan.Zero, TimeSpan.Zero, 1, callback, out token); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void StartTimer(TimeSpan delay, Action callback, out TimerExecutionToken token) => - StartTimer(delay, TimeSpan.Zero, 1, callback, out token); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void StartTimer(TimeSpan delay, TimeSpan interval, Action callback, out TimerExecutionToken token) => - StartTimer(delay, interval, 0, callback, out token); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void StartTimer(TimeSpan interval, int count, Action callback, out TimerExecutionToken token) => - StartTimer(TimeSpan.Zero, interval, count, callback, out token); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void StartTimer(TimeSpan delay, TimeSpan interval, int count, Action callback, out TimerExecutionToken token) + internal override void OnDetach() { - DelayCallTimer t = DelayCallTimer.GetTimer(delay, interval, count, callback); - t.Start(); - -#if DEBUG_TIMERS - DelayCallTimer._stackTraces[t.GetHashCode()] = new StackTrace().ToString(); -#endif - token = new TimerExecutionToken(t); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static DelayCallTimer Pause(TimeSpan ms) => new(ms); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static DelayCallTimer Pause(int ms) => Pause(TimeSpan.FromMilliseconds(ms)); - - public sealed class DelayCallTimer : Timer, INotifyCompletion - { - internal bool _returnOnDetach; -#if DEBUG_TIMERS - internal bool _allowFinalization; -#endif - private Action _continuation; - private bool _complete; - - internal DelayCallTimer(TimeSpan delay, TimeSpan interval, int count, Action callback) : base( - delay, - interval, - count - ) => - _continuation = callback; - - internal DelayCallTimer(TimeSpan delay) : base(delay) + if (Running) { -#if DEBUG_TIMERS - _allowFinalization = true; -#endif - Start(); + logger.Error("Timer is returned while still running!\n{StackTrace}", new StackTrace()); + return; } - protected override void OnTick() + if (_returnOnDetach) { - _complete = true; - _continuation?.Invoke(); - } - internal override void OnDetach() - { - if (Running) +#if DEBUG_TIMERS + _stackTraces.Remove(GetHashCode()); +#endif + + if (_poolCount >= _poolCapacity) { - logger.Error("Timer is returned while still running!\n{StackTrace}", new StackTrace()); +#if DEBUG_TIMERS + logger.Warning("DelayCallTimer pool reached maximum of {Capacity} timers", _poolCapacity); + _allowFinalization = true; +#endif return; } - if (_returnOnDetach) - { - // Increment the version so if this is called from OnTick() and another timer is started, we don't have a problem - // Version++; - -#if DEBUG_TIMERS - _stackTraces.Remove(GetHashCode()); -#endif - - if (_poolCount >= _poolCapacity) - { -#if DEBUG_TIMERS - logger.Warning("DelayCallTimer pool reached maximum of {Capacity} timers", _poolCapacity); - _allowFinalization = true; -#endif - return; - } - - _continuation = null; - _returnOnDetach = false; - ReturnToPool(1, this, this); - } + _continuation = null; + _returnOnDetach = false; + ReturnToPool(1, this, this); } + } - public static DelayCallTimer GetTimer(TimeSpan delay, TimeSpan interval, int count, Action callback) + public static DelayCallTimer GetTimer(TimeSpan delay, TimeSpan interval, int count, Action callback) + { + var timer = GetFromPool(); + if (timer != null) { - var timer = GetFromPool(); - if (timer != null) - { #if DEBUG_TIMERS - logger.Information("Getting from pool: ({Count} / {Capacity})", _poolCount, _poolCapacity); + logger.Information("Getting from pool: ({Count} / {Capacity})", _poolCount, _poolCapacity); #endif - timer.Init(delay, interval, count); - timer._continuation = callback; - timer._returnOnDetach = false; + timer.Init(delay, interval, count); + timer._continuation = callback; + timer._returnOnDetach = false; #if DEBUG_TIMERS - timer._allowFinalization = false; + timer._allowFinalization = false; #endif - return timer; - } - - _timerPoolDepletionAmount++; - -#if DEBUG_TIMERS - logger.Warning("Timer pool depleted and timer was allocated.\n{StackTrace}", new StackTrace()); -#endif - return new DelayCallTimer(delay, interval, count, callback); + return timer; } - public override string ToString() => $"DelayCallTimer[{FormatDelegate(_continuation)}]"; + _timerPoolDepletionAmount++; #if DEBUG_TIMERS - internal static Dictionary _stackTraces = new(); + logger.Warning("Timer pool depleted and timer was allocated.\n{StackTrace}", new StackTrace()); +#endif + return new DelayCallTimer(delay, interval, count, callback); + } - ~DelayCallTimer() + public override string ToString() => $"DelayCallTimer[{FormatDelegate(_continuation)}]"; + +#if DEBUG_TIMERS + internal static Dictionary _stackTraces = new(); + + ~DelayCallTimer() + { + if (!_allowFinalization) { - if (!_allowFinalization) - { - logger.Warning("Pooled timer was not returned to the pool.\n{StackTrace}", _stackTraces[GetHashCode()]); - } + logger.Warning("Pooled timer was not returned to the pool.\n{StackTrace}", _stackTraces[GetHashCode()]); } + } #endif - public DelayCallTimer GetAwaiter() => this; + public DelayCallTimer GetAwaiter() => this; - public bool IsCompleted => _complete; + public bool IsCompleted => _complete; - public void OnCompleted(Action continuation) => _continuation = continuation; + public void OnCompleted(Action continuation) => _continuation = continuation; - public void GetResult() - { - } + public void GetResult() + { } } } diff --git a/Projects/Server/Timer/Timer.DelayStateCall.cs b/Projects/Server/Timer/Timer.DelayStateCall.cs index 5c7712e55..9ba6557d1 100644 --- a/Projects/Server/Timer/Timer.DelayStateCall.cs +++ b/Projects/Server/Timer/Timer.DelayStateCall.cs @@ -16,227 +16,226 @@ using System; using System.Runtime.CompilerServices; -namespace Server +namespace Server; + +public partial class Timer { - public partial class Timer + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Timer DelayCall(Action callback, T state) => + DelayCall(TimeSpan.Zero, TimeSpan.Zero, 1, callback, state); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Timer DelayCall(TimeSpan delay, Action callback, T state) => + DelayCall(delay, TimeSpan.Zero, 1, callback, state); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Timer DelayCall(TimeSpan delay, TimeSpan interval, Action callback, T state) => + DelayCall(delay, interval, 0, callback, state); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Timer DelayCall(TimeSpan delay, TimeSpan interval, int count, Action callback, T state) => + new DelayStateCallTimer(delay, interval, count, callback, state).Start(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Timer DelayCall(Action callback, T1 t1, T2 t2) => + DelayCall(TimeSpan.Zero, TimeSpan.Zero, 1, callback, t1, t2); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Timer DelayCall(TimeSpan delay, Action callback, T1 t1, T2 t2) => + DelayCall(delay, TimeSpan.Zero, 1, callback, t1, t2); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Timer DelayCall(TimeSpan delay, TimeSpan interval, Action callback, T1 t1, T2 t2) => + DelayCall(delay, interval, 0, callback, t1, t2); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Timer DelayCall( + TimeSpan delay, TimeSpan interval, int count, Action callback, + T1 t1, T2 t2 + ) => new DelayStateCallTimer(delay, interval, count, callback, t1, t2).Start(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Timer DelayCall(Action callback, T1 t1, T2 t2, T3 t3) => + DelayCall(TimeSpan.Zero, TimeSpan.Zero, 1, callback, t1, t2, t3); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Timer DelayCall( + TimeSpan delay, Action callback, T1 t1, T2 t2, T3 t3 + ) => DelayCall(delay, TimeSpan.Zero, 1, callback, t1, t2, t3); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Timer DelayCall( + TimeSpan delay, TimeSpan interval, Action callback, + T1 t1, T2 t2, T3 t3 + ) => DelayCall(delay, interval, 0, callback, t1, t2, t3); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Timer DelayCall( + TimeSpan delay, TimeSpan interval, int count, + Action callback, T1 t1, T2 t2, T3 t3 + ) => new DelayStateCallTimer(delay, interval, count, callback, t1, t2, t3).Start(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Timer DelayCall( + Action callback, T1 t1, T2 t2, T3 t3, T4 t4 + ) => DelayCall(TimeSpan.Zero, TimeSpan.Zero, 1, callback, t1, t2, t3, t4); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Timer DelayCall( + TimeSpan delay, Action callback, + T1 t1, T2 t2, T3 t3, T4 t4 + ) => DelayCall(delay, TimeSpan.Zero, 1, callback, t1, t2, t3, t4); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Timer DelayCall( + TimeSpan delay, TimeSpan interval, + Action callback, T1 t1, T2 t2, T3 t3, T4 t4 + ) => DelayCall(delay, interval, 0, callback, t1, t2, t3, t4); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Timer DelayCall( + TimeSpan delay, TimeSpan interval, int count, + Action callback, T1 t1, T2 t2, T3 t3, T4 t4 + ) => new DelayStateCallTimer(delay, interval, count, callback, t1, t2, t3, t4).Start(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Timer DelayCall( + Action callback, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5 + ) => new DelayStateCallTimer(TimeSpan.Zero, TimeSpan.Zero, 1, callback, t1, t2, t3, t4, t5).Start(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Timer DelayCall( + TimeSpan delay, + Action callback, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5 + ) => new DelayStateCallTimer(delay, TimeSpan.Zero, 1, callback, t1, t2, t3, t4, t5).Start(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Timer DelayCall( + TimeSpan delay, TimeSpan interval, + Action callback, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5 + ) => new DelayStateCallTimer(delay, interval, 0, callback, t1, t2, t3, t4, t5).Start(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Timer DelayCall( + TimeSpan delay, TimeSpan interval, int count, + Action callback, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5 + ) => new DelayStateCallTimer(delay, interval, count, callback, t1, t2, t3, t4, t5).Start(); + + private class DelayStateCallTimer : Timer { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Timer DelayCall(Action callback, T state) => - DelayCall(TimeSpan.Zero, TimeSpan.Zero, 1, callback, state); + private readonly T _t1; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Timer DelayCall(TimeSpan delay, Action callback, T state) => - DelayCall(delay, TimeSpan.Zero, 1, callback, state); + public DelayStateCallTimer(TimeSpan delay, TimeSpan interval, int count, Action callback, T state) + : base(delay, interval, count) + { + Callback = callback; + _t1 = state; + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Timer DelayCall(TimeSpan delay, TimeSpan interval, Action callback, T state) => - DelayCall(delay, interval, 0, callback, state); + public Action Callback { get; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Timer DelayCall(TimeSpan delay, TimeSpan interval, int count, Action callback, T state) => - new DelayStateCallTimer(delay, interval, count, callback, state).Start(); + protected override void OnTick() => Callback?.Invoke(_t1); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Timer DelayCall(Action callback, T1 t1, T2 t2) => - DelayCall(TimeSpan.Zero, TimeSpan.Zero, 1, callback, t1, t2); + public override string ToString() => $"DelayStateCall[{FormatDelegate(Callback)}]"; + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Timer DelayCall(TimeSpan delay, Action callback, T1 t1, T2 t2) => - DelayCall(delay, TimeSpan.Zero, 1, callback, t1, t2); + private class DelayStateCallTimer : Timer + { + private readonly T1 _t1; + private readonly T2 _t2; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Timer DelayCall(TimeSpan delay, TimeSpan interval, Action callback, T1 t1, T2 t2) => - DelayCall(delay, interval, 0, callback, t1, t2); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Timer DelayCall( + public DelayStateCallTimer( TimeSpan delay, TimeSpan interval, int count, Action callback, T1 t1, T2 t2 - ) => new DelayStateCallTimer(delay, interval, count, callback, t1, t2).Start(); + ) : base(delay, interval, count) + { + Callback = callback; + _t1 = t1; + _t2 = t2; + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Timer DelayCall(Action callback, T1 t1, T2 t2, T3 t3) => - DelayCall(TimeSpan.Zero, TimeSpan.Zero, 1, callback, t1, t2, t3); + public Action Callback { get; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Timer DelayCall( - TimeSpan delay, Action callback, T1 t1, T2 t2, T3 t3 - ) => DelayCall(delay, TimeSpan.Zero, 1, callback, t1, t2, t3); + protected override void OnTick() => Callback?.Invoke(_t1, _t2); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Timer DelayCall( - TimeSpan delay, TimeSpan interval, Action callback, + public override string ToString() => $"DelayStateCall[{FormatDelegate(Callback)}]"; + } + + private class DelayStateCallTimer : Timer + { + private readonly T1 _t1; + private readonly T2 _t2; + private readonly T3 _t3; + + public DelayStateCallTimer( + TimeSpan delay, TimeSpan interval, int count, Action callback, T1 t1, T2 t2, T3 t3 - ) => DelayCall(delay, interval, 0, callback, t1, t2, t3); + ) : base(delay, interval, count) + { + Callback = callback; + _t1 = t1; + _t2 = t2; + _t3 = t3; + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Timer DelayCall( - TimeSpan delay, TimeSpan interval, int count, - Action callback, T1 t1, T2 t2, T3 t3 - ) => new DelayStateCallTimer(delay, interval, count, callback, t1, t2, t3).Start(); + public Action Callback { get; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Timer DelayCall( - Action callback, T1 t1, T2 t2, T3 t3, T4 t4 - ) => DelayCall(TimeSpan.Zero, TimeSpan.Zero, 1, callback, t1, t2, t3, t4); + protected override void OnTick() => Callback?.Invoke(_t1, _t2, _t3); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Timer DelayCall( - TimeSpan delay, Action callback, + public override string ToString() => $"DelayStateCall[{FormatDelegate(Callback)}]"; + } + + private class DelayStateCallTimer : Timer + { + private readonly T1 _t1; + private readonly T2 _t2; + private readonly T3 _t3; + private readonly T4 _t4; + + public DelayStateCallTimer( + TimeSpan delay, TimeSpan interval, int count, Action callback, T1 t1, T2 t2, T3 t3, T4 t4 - ) => DelayCall(delay, TimeSpan.Zero, 1, callback, t1, t2, t3, t4); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Timer DelayCall( - TimeSpan delay, TimeSpan interval, - Action callback, T1 t1, T2 t2, T3 t3, T4 t4 - ) => DelayCall(delay, interval, 0, callback, t1, t2, t3, t4); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Timer DelayCall( - TimeSpan delay, TimeSpan interval, int count, - Action callback, T1 t1, T2 t2, T3 t3, T4 t4 - ) => new DelayStateCallTimer(delay, interval, count, callback, t1, t2, t3, t4).Start(); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Timer DelayCall( - Action callback, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5 - ) => new DelayStateCallTimer(TimeSpan.Zero, TimeSpan.Zero, 1, callback, t1, t2, t3, t4, t5).Start(); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Timer DelayCall( - TimeSpan delay, - Action callback, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5 - ) => new DelayStateCallTimer(delay, TimeSpan.Zero, 1, callback, t1, t2, t3, t4, t5).Start(); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Timer DelayCall( - TimeSpan delay, TimeSpan interval, - Action callback, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5 - ) => new DelayStateCallTimer(delay, interval, 0, callback, t1, t2, t3, t4, t5).Start(); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Timer DelayCall( - TimeSpan delay, TimeSpan interval, int count, - Action callback, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5 - ) => new DelayStateCallTimer(delay, interval, count, callback, t1, t2, t3, t4, t5).Start(); - - private class DelayStateCallTimer : Timer + ) : base(delay, interval, count) { - private readonly T _t1; - - public DelayStateCallTimer(TimeSpan delay, TimeSpan interval, int count, Action callback, T state) - : base(delay, interval, count) - { - Callback = callback; - _t1 = state; - } - - public Action Callback { get; } - - protected override void OnTick() => Callback?.Invoke(_t1); - - public override string ToString() => $"DelayStateCall[{FormatDelegate(Callback)}]"; + Callback = callback; + _t1 = t1; + _t2 = t2; + _t3 = t3; + _t4 = t4; } - private class DelayStateCallTimer : Timer + public Action Callback { get; } + + protected override void OnTick() => Callback?.Invoke(_t1, _t2, _t3, _t4); + + public override string ToString() => $"DelayStateCall[{FormatDelegate(Callback)}]"; + } + + private class DelayStateCallTimer : Timer + { + private readonly T1 _t1; + private readonly T2 _t2; + private readonly T3 _t3; + private readonly T4 _t4; + private readonly T5 _t5; + + public DelayStateCallTimer( + TimeSpan delay, TimeSpan interval, int count, Action callback, + T1 t1, T2 t2, T3 t3, T4 t4, T5 t5 + ) : base(delay, interval, count) { - private readonly T1 _t1; - private readonly T2 _t2; - - public DelayStateCallTimer( - TimeSpan delay, TimeSpan interval, int count, Action callback, - T1 t1, T2 t2 - ) : base(delay, interval, count) - { - Callback = callback; - _t1 = t1; - _t2 = t2; - } - - public Action Callback { get; } - - protected override void OnTick() => Callback?.Invoke(_t1, _t2); - - public override string ToString() => $"DelayStateCall[{FormatDelegate(Callback)}]"; + Callback = callback; + _t1 = t1; + _t2 = t2; + _t3 = t3; + _t4 = t4; + _t5 = t5; } - private class DelayStateCallTimer : Timer - { - private readonly T1 _t1; - private readonly T2 _t2; - private readonly T3 _t3; + public Action Callback { get; } - public DelayStateCallTimer( - TimeSpan delay, TimeSpan interval, int count, Action callback, - T1 t1, T2 t2, T3 t3 - ) : base(delay, interval, count) - { - Callback = callback; - _t1 = t1; - _t2 = t2; - _t3 = t3; - } + protected override void OnTick() => Callback?.Invoke(_t1, _t2, _t3, _t4, _t5); - public Action Callback { get; } - - protected override void OnTick() => Callback?.Invoke(_t1, _t2, _t3); - - public override string ToString() => $"DelayStateCall[{FormatDelegate(Callback)}]"; - } - - private class DelayStateCallTimer : Timer - { - private readonly T1 _t1; - private readonly T2 _t2; - private readonly T3 _t3; - private readonly T4 _t4; - - public DelayStateCallTimer( - TimeSpan delay, TimeSpan interval, int count, Action callback, - T1 t1, T2 t2, T3 t3, T4 t4 - ) : base(delay, interval, count) - { - Callback = callback; - _t1 = t1; - _t2 = t2; - _t3 = t3; - _t4 = t4; - } - - public Action Callback { get; } - - protected override void OnTick() => Callback?.Invoke(_t1, _t2, _t3, _t4); - - public override string ToString() => $"DelayStateCall[{FormatDelegate(Callback)}]"; - } - - private class DelayStateCallTimer : Timer - { - private readonly T1 _t1; - private readonly T2 _t2; - private readonly T3 _t3; - private readonly T4 _t4; - private readonly T5 _t5; - - public DelayStateCallTimer( - TimeSpan delay, TimeSpan interval, int count, Action callback, - T1 t1, T2 t2, T3 t3, T4 t4, T5 t5 - ) : base(delay, interval, count) - { - Callback = callback; - _t1 = t1; - _t2 = t2; - _t3 = t3; - _t4 = t4; - _t5 = t5; - } - - public Action Callback { get; } - - protected override void OnTick() => Callback?.Invoke(_t1, _t2, _t3, _t4, _t5); - - public override string ToString() => $"DelayStateCall[{FormatDelegate(Callback)}]"; - } + public override string ToString() => $"DelayStateCall[{FormatDelegate(Callback)}]"; } } diff --git a/Projects/Server/Timer/Timer.Pool.cs b/Projects/Server/Timer/Timer.Pool.cs index fbdc0d91e..79f467b86 100644 --- a/Projects/Server/Timer/Timer.Pool.cs +++ b/Projects/Server/Timer/Timer.Pool.cs @@ -15,122 +15,115 @@ using System; using System.Threading; +using System.Threading.Tasks; -namespace Server +namespace Server; + +public partial class Timer { - public partial class Timer + private const int _timerPoolDepletionThreshold = 128; // Maximum timers allocated in a single tick before we force adjust + private static int _timerPoolDepletionAmount; // Amount the pool has been depleted by + private static int _maxPoolCapacity; + private static int _poolCapacity; + private static int _poolCount; + private static DelayCallTimer _poolHead; + private static int _isRefilling; + + public static void CheckTimerPool() { - private const int _timerPoolDepletionThreshold = 128; // Maximum timers allocated in a single tick before we force adjust - private static int _timerPoolDepletionAmount; // Amount the pool has been depleted by - private static int _maxPoolCapacity; - private static int _poolCapacity; - private static int _poolCount; - private static DelayCallTimer _poolHead; - - public static void CheckTimerPool() + // Anything less than this threshold and we are ok with the number of allocations. + if (_timerPoolDepletionAmount < _timerPoolDepletionThreshold) { - // Anything less than this threshold and we are ok with the number of allocations. - if (_timerPoolDepletionAmount < _timerPoolDepletionThreshold) - { - _timerPoolDepletionAmount = 0; - return; - } - - var growthFactor = Math.DivRem(_timerPoolDepletionAmount, _poolCapacity, out var rem); - var amountToGrow = _poolCapacity * (growthFactor + (rem > 0 ? 1 : 0)); - var amountToRefill = Math.Min(_maxPoolCapacity, amountToGrow); - - var maximumHit = amountToGrow > amountToRefill ? " Maximum pool size has been reached." : ""; - var warningMessage = $"Timer pool depleted by {{Amount}}. Refilling with {{AmountRefill}}.{maximumHit}"; - - logger.Warning(warningMessage, _timerPoolDepletionAmount, amountToRefill); - RefillPoolAsync(amountToRefill); _timerPoolDepletionAmount = 0; + return; } - public static void ConfigureTimerPool() - { - _poolCapacity = ServerConfiguration.GetOrUpdateSetting("timer.initialPoolCapacity", 1024); - _maxPoolCapacity = ServerConfiguration.GetOrUpdateSetting("timer.maxPoolCapacity", _poolCapacity * 16); + var growthFactor = Math.DivRem(_timerPoolDepletionAmount, _poolCapacity, out var rem); + var amountToGrow = _poolCapacity * (growthFactor + (rem > 0 ? 1 : 0)); + var amountToRefill = Math.Min(_maxPoolCapacity, amountToGrow); - RefillPool(_poolCapacity, out var head, out var tail); - ReturnToPool(_poolCapacity, head, tail); - } + var maximumHit = amountToGrow > amountToRefill ? " Maximum pool size has been reached." : ""; + var warningMessage = $"Timer pool depleted by {{Amount}}. Refilling with {{AmountRefill}}.{maximumHit}"; - private static void ReturnToPool(int amount, DelayCallTimer head, DelayCallTimer tail) - { - tail.Attach(_poolHead); - _poolHead = head; - _poolCount += amount; + logger.Warning(warningMessage, _timerPoolDepletionAmount, amountToRefill); + RefillPoolAsync(amountToRefill); + _timerPoolDepletionAmount = 0; + } + + public static void ConfigureTimerPool() + { + _poolCapacity = ServerConfiguration.GetOrUpdateSetting("timer.initialPoolCapacity", 1024); + _maxPoolCapacity = ServerConfiguration.GetOrUpdateSetting("timer.maxPoolCapacity", _poolCapacity * 16); + + RefillPool(_poolCapacity, out var head, out var tail); + ReturnToPool(_poolCapacity, head, tail); + } + + private static void ReturnToPool(int amount, DelayCallTimer head, DelayCallTimer tail) + { + tail.Attach(_poolHead); + _poolHead = head; + _poolCount += amount; #if DEBUG_TIMERS - logger.Information("Returning to pool. ({Count} / {Capacity})", _poolCount, _poolCapacity); + logger.Information("Returning to pool. ({Count} / {Capacity})", _poolCount, _poolCapacity); #endif + } + + private static DelayCallTimer GetFromPool() + { + if (_poolHead == null) + { + return null; } - private static DelayCallTimer GetFromPool() - { - if (_poolHead == null) - { - return null; - } + var timer = _poolHead; + _poolHead = _poolHead._nextTimer as DelayCallTimer; + timer.Detach(); + _poolCount--; - var timer = _poolHead; - _poolHead = _poolHead._nextTimer as DelayCallTimer; - timer.Detach(); - _poolCount--; + return timer; + } - return timer; - } - - internal static void RefillPool(int amount, out DelayCallTimer head, out DelayCallTimer tail) - { + internal static void RefillPool(int amount, out DelayCallTimer head, out DelayCallTimer tail) + { #if DEBUG_TIMERS - logger.Information("Filling pool with {Amount} timers.", amount); + logger.Information("Filling pool with {Amount} timers.", amount); #endif - head = null; - tail = null; + head = null; + tail = null; - for (var i = 0; i < amount; i++) - { - var timer = new DelayCallTimer(TimeSpan.Zero, TimeSpan.Zero, 0, null); - timer.Attach(head); - - if (i == 0) - { - tail = timer; - } - - head = timer; - } - } - - internal static void RefillPoolAsync(int amountToRefill) + for (var i = 0; i < amount; i++) { - ThreadPool.UnsafeQueueUserWorkItem( - static amount => - { - RefillPool(amount, out var head, out var tail); + var timer = new DelayCallTimer(TimeSpan.Zero, TimeSpan.Zero, 0, null); + timer.Attach(head); - // Run this on the core thread - Core.LoopContext.Post( - state => - { - if (state == null) - { - return; - } + if (i == 0) + { + tail = timer; + } - var (listHead, listTail) = ((DelayCallTimer, DelayCallTimer))state; - ReturnToPool(amount, listHead, listTail); - _poolCapacity = amount; - }, - (head, tail) - ); - }, - amountToRefill, - false - ); + head = timer; } } + + internal static async void RefillPoolAsync(int amountToRefill) + { + if (Interlocked.CompareExchange(ref _isRefilling, 0, 1) == 1) + { + return; + } + + var (headTimer, tailTimer) = await Task.Run( + () => + { + RefillPool(amountToRefill, out var head, out var tail); + return (head, tail); + } + ); + + ReturnToPool(amountToRefill, headTimer, tailTimer); + _poolCapacity = amountToRefill; + _isRefilling = 0; + } } diff --git a/Projects/Server/Timer/Timer.TimerWheel.cs b/Projects/Server/Timer/Timer.TimerWheel.cs index 8b0621b21..fcd0450e1 100644 --- a/Projects/Server/Timer/Timer.TimerWheel.cs +++ b/Projects/Server/Timer/Timer.TimerWheel.cs @@ -18,270 +18,269 @@ using System.Collections.Generic; using System.IO; using System.Linq; -namespace Server +namespace Server; + +public partial class Timer { - public partial class Timer + private const int _ringSizePowerOf2 = 12; + private const int _ringSize = 1 << _ringSizePowerOf2; // 4096 + private const int _ringLayers = 3; + private const int _tickRatePowerOf2 = 3; + private const int _tickRate = 1 << _tickRatePowerOf2; // 8ms + + private static readonly Timer[][] _rings = new Timer[_ringLayers][]; + private static readonly int[] _ringIndexes = new int[_ringLayers]; + + private static long _lastTickTurned = -1; + private static bool _timerWheelExecuting; + + public static void Init(long tickCount) { - private const int _ringSizePowerOf2 = 12; - private const int _ringSize = 1 << _ringSizePowerOf2; // 4096 - private const int _ringLayers = 3; - private const int _tickRatePowerOf2 = 3; - private const int _tickRate = 1 << _tickRatePowerOf2; // 8ms + _lastTickTurned = tickCount; - private static readonly Timer[][] _rings = new Timer[_ringLayers][]; - private static readonly int[] _ringIndexes = new int[_ringLayers]; - - private static long _lastTickTurned = -1; - private static bool _timerWheelExecuting; - - public static void Init(long tickCount) + for (int i = 0; i < _rings.Length; i++) { - _lastTickTurned = tickCount; - - for (int i = 0; i < _rings.Length; i++) - { - _rings[i] = new Timer[_ringSize]; - _ringIndexes[i] = 0; - } + _rings[i] = new Timer[_ringSize]; + _ringIndexes[i] = 0; } + } - public static void Slice(long tickCount) + public static void Slice(long tickCount) + { + var deltaSinceTurn = tickCount - _lastTickTurned; + while (deltaSinceTurn >= _tickRate) { - var deltaSinceTurn = tickCount - _lastTickTurned; - while (deltaSinceTurn >= _tickRate) - { - deltaSinceTurn -= _tickRate; - _lastTickTurned += _tickRate; - Turn(); - } + deltaSinceTurn -= _tickRate; + _lastTickTurned += _tickRate; + Turn(); } + } - private static void Turn() + private static void Turn() + { + _timerWheelExecuting = true; + var turnNextWheel = false; + + var _executingRings = new Timer[_ringLayers]; + + // Detach the chain from the timer wheel. This allows adding timers to the same slot during execution. + for (var i = 0; i < _ringLayers; i++) { - _timerWheelExecuting = true; - var turnNextWheel = false; - - var _executingRings = new Timer[_ringLayers]; - - // Detach the chain from the timer wheel. This allows adding timers to the same slot during execution. - for (var i = 0; i < _ringLayers; i++) + if (i == 0 || turnNextWheel) { - if (i == 0 || turnNextWheel) + var ringIndex = ++_ringIndexes[i]; + turnNextWheel = ringIndex >= _ringSize; + + if (turnNextWheel) { - var ringIndex = ++_ringIndexes[i]; - turnNextWheel = ringIndex >= _ringSize; + ringIndex = _ringIndexes[i] = 0; + } - if (turnNextWheel) + _executingRings[i] = _rings[i][ringIndex]; + _rings[i][ringIndex] = null; + } + else + { + _executingRings[i] = null; + } + } + + for (var i = 0; i < _ringLayers; i++) + { + var timer = _executingRings[i]; + if (timer == null) + { + continue; + } + + do + { + var next = timer._nextTimer; + + timer.Detach(); + + // Check to see if it's running just in case it was stopped by another timer + if (timer.Running) + { + if (i > 0 && timer._remaining > 0) { - ringIndex = _ringIndexes[i] = 0; + // Promote + AddTimer(timer, timer._remaining); } - - _executingRings[i] = _rings[i][ringIndex]; - _rings[i][ringIndex] = null; - } - else - { - _executingRings[i] = null; - } - } - - for (var i = 0; i < _ringLayers; i++) - { - var timer = _executingRings[i]; - if (timer == null) - { - continue; - } - - do - { - var next = timer._nextTimer; - - timer.Detach(); - - // Check to see if it's running just in case it was stopped by another timer - if (timer.Running) + else { - if (i > 0 && timer._remaining > 0) - { - // Promote - AddTimer(timer, timer._remaining); - } - else - { - Execute(timer); - } + Execute(timer); } + } - if (!timer.Running) - { - timer.OnDetach(); - } + if (!timer.Running) + { + timer.OnDetach(); + } - timer = next; - } while (timer != null); - } - - _timerWheelExecuting = false; + timer = next; + } while (timer != null); } - private static void Execute(Timer timer) + _timerWheelExecuting = false; + } + + private static void Execute(Timer timer) + { + var finished = timer.Count != 0 && ++timer.Index >= timer.Count; + + var version = timer.Version; + + var prof = timer.GetProfile(); + prof?.Start(); + timer.OnTick(); + prof?.Finish(); + + // If the timer has not been stopped, and it has not been altered (restarted, returned etc) + if (timer.Running && timer.Version == version) { - var finished = timer.Count != 0 && ++timer.Index >= timer.Count; - - var version = timer.Version; - - var prof = timer.GetProfile(); - prof?.Start(); - timer.OnTick(); - prof?.Finish(); - - // If the timer has not been stopped, and it has not been altered (restarted, returned etc) - if (timer.Running && timer.Version == version) + if (finished) { - if (finished) - { - timer.Stop(); - } - else - { - timer.Delay = timer.Interval; - timer.Next = Core.Now + timer.Interval; - AddTimer(timer, (long)timer.Delay.TotalMilliseconds); - } + timer.Stop(); + } + else + { + timer.Delay = timer.Interval; + timer.Next = Core.Now + timer.Interval; + AddTimer(timer, (long)timer.Delay.TotalMilliseconds); } } + } - private static void AddTimer(Timer timer, long delay) - { + private static void AddTimer(Timer timer, long delay) + { #if DEBUG_TIMERS - var originalDelay = delay; + var originalDelay = delay; #endif - delay = Math.Max(0, delay); + delay = Math.Max(0, delay); - var resolutionPowerOf2 = _tickRatePowerOf2; - for (var i = 0; i < _ringLayers; i++) + var resolutionPowerOf2 = _tickRatePowerOf2; + for (var i = 0; i < _ringLayers; i++) + { + var resolution = 1L << resolutionPowerOf2; + var nextResolutionPowerOf2 = resolutionPowerOf2 + _ringSizePowerOf2; + var max = 1L << nextResolutionPowerOf2; + if (delay < max) { - var resolution = 1L << resolutionPowerOf2; - var nextResolutionPowerOf2 = resolutionPowerOf2 + _ringSizePowerOf2; - var max = 1L << nextResolutionPowerOf2; - if (delay < max) + var remaining = delay & (resolution - 1); + var slot = (delay >> resolutionPowerOf2) + _ringIndexes[i] + (remaining > 0 ? 1 : 0); + + // Round up if we have a delay of 0 + if (delay == 0) { - var remaining = delay & (resolution - 1); - var slot = (delay >> resolutionPowerOf2) + _ringIndexes[i] + (remaining > 0 ? 1 : 0); - - // Round up if we have a delay of 0 - if (delay == 0) - { - slot++; - remaining = 0; - } - - if (slot >= _ringSize) - { - slot -= _ringSize; - } - - timer.Attach(_rings[i][slot]); - timer._remaining = remaining; - timer._ring = i; - timer._slot = (int)slot; - - _rings[i][slot] = timer; - - return; + slot++; + remaining = 0; } - // The remaining amount until we turn this ring - delay -= resolution * (_ringSize - _ringIndexes[i]); - resolutionPowerOf2 = nextResolutionPowerOf2; - } - - // TODO: Handle timers > 17yrs -#if DEBUG_TIMERS - logger.Error("Timer is more than max duration. ({Duration})", originalDelay); -#endif - } - - public static void DumpInfo(TextWriter tw) - { - tw.WriteLine("Date: {0}\n", Core.Now.ToLocalTime()); - tw.WriteLine("Pool - Count: {0}; Size {1}\n", _poolCount - _timerPoolDepletionAmount, _poolCapacity); - - var total = 0.0; - var hash = new Dictionary(); - - for (var i = 0; i < _ringLayers; i++) - { - for (var j = 0; j < _ringSize; j++) + if (slot >= _ringSize) { - var t = _rings[i][j]; - if (t == null) - { - continue; - } - - while (t != null) - { - var name = t.ToString(); - - hash.TryGetValue(name, out var count); - hash[name] = count + 1; - - total++; - - t = t?._nextTimer; - } + slot -= _ringSize; } + + timer.Attach(_rings[i][slot]); + timer._remaining = remaining; + timer._ring = i; + timer._slot = (int)slot; + + _rings[i][slot] = timer; + + return; } - tw.WriteLine("Timers:"); - - foreach (var (name, count) in hash.OrderByDescending(o => o.Value)) - { - var percent = count / total; - var line = $"{count:#,0} ({percent:P1})"; - // 6 - 15 / 8 = 1 - var tabs = new string('\t', line.Length < 12 ? 2 : 1); - tw.WriteLine($"{line}{tabs}{name}"); - } - -#if DEBUG_TIMERS - tw.WriteLine($"{Environment.NewLine}Stack Traces:"); - foreach (var kvp in DelayCallTimer._stackTraces) - { - tw.WriteLine(kvp.Value); - tw.WriteLine(); - } -#endif - - tw.WriteLine(); - tw.WriteLine(); + // The remaining amount until we turn this ring + delay -= resolution * (_ringSize - _ringIndexes[i]); + resolutionPowerOf2 = nextResolutionPowerOf2; } - public static void ClearAllTimers(long tickCount) - { - _lastTickTurned = tickCount; + // TODO: Handle timers > 17yrs +#if DEBUG_TIMERS + logger.Error("Timer is more than max duration. ({Duration})", originalDelay); +#endif + } - foreach (var t in _rings) + public static void DumpInfo(TextWriter tw) + { + tw.WriteLine($"Date: {Core.Now.ToLocalTime()}\n"); + tw.WriteLine($"Pool - Count: {_poolCount - _timerPoolDepletionAmount}; Size {_poolCapacity}\n"); + + var total = 0.0; + var hash = new Dictionary(); + + for (var i = 0; i < _ringLayers; i++) + { + for (var j = 0; j < _ringSize; j++) { + var t = _rings[i][j]; if (t == null) { continue; } - for (var i = 0; i < _ringSize; i++) + while (t != null) { - var node = t[i]; - Timer next; + var name = t.ToString(); - do - { - next = node?._nextTimer; - node?.Stop(); - } while (next != null); + hash.TryGetValue(name, out var count); + hash[name] = count + 1; + + total++; + + t = t?._nextTimer; } } } + + tw.WriteLine("Timers:"); + + foreach (var (name, count) in hash.OrderByDescending(o => o.Value)) + { + var percent = count / total; + var line = $"{count:#,0} ({percent:P1})"; + // 6 - 15 / 8 = 1 + var tabs = new string('\t', line.Length < 12 ? 2 : 1); + tw.WriteLine($"{line}{tabs}{name}"); + } + +#if DEBUG_TIMERS + tw.WriteLine($"{Environment.NewLine}Stack Traces:"); + foreach (var kvp in DelayCallTimer._stackTraces) + { + tw.WriteLine(kvp.Value); + tw.WriteLine(); + } +#endif + + tw.WriteLine(); + tw.WriteLine(); + } + + public static void ClearAllTimers(long tickCount) + { + _lastTickTurned = tickCount; + + foreach (var t in _rings) + { + if (t == null) + { + continue; + } + + for (var i = 0; i < _ringSize; i++) + { + var node = t[i]; + Timer next; + + do + { + next = node?._nextTimer; + node?.Stop(); + } while (next != null); + } + } } } diff --git a/Projects/Server/Timer/Timer.cs b/Projects/Server/Timer/Timer.cs index 786d1c86c..3743b7b3e 100644 --- a/Projects/Server/Timer/Timer.cs +++ b/Projects/Server/Timer/Timer.cs @@ -17,144 +17,145 @@ using System; using Server.Diagnostics; using Server.Logging; -namespace Server +namespace Server; + +public partial class Timer { - public partial class Timer + protected internal static readonly ILogger logger = LogFactory.GetLogger(typeof(Timer)); + + public static void Configure() { - protected internal static readonly ILogger logger = LogFactory.GetLogger(typeof(Timer)); + ConfigureTimerPool(); + } - public static void Configure() + // We need to know what ring/slot we are in so we can be removed if we are "head" of the link list. + private int _ring; + private int _slot; + private long _remaining; + private Timer _nextTimer; + private Timer _prevTimer; + + public Timer(TimeSpan delay) => Init(delay, TimeSpan.Zero, 1); + + public Timer(TimeSpan interval, int count) => Init(interval, interval, count); + + public Timer(TimeSpan delay, TimeSpan interval, int count = 0) => Init(delay, interval, count); + + protected void Init(TimeSpan delay, TimeSpan interval, int count) + { + Running = false; + Delay = delay; + Index = 0; + Interval = interval; + Count = count; + _nextTimer = null; + _prevTimer = null; + Next = Core.Now + Delay; + + var prof = GetProfile(); + + if (prof != null) { - ConfigureTimerPool(); + prof.Created++; } + } - // We need to know what ring/slot we are in so we can be removed if we are "head" of the link list. - private int _ring; - private int _slot; - private long _remaining; - private Timer _nextTimer; - private Timer _prevTimer; + protected int Version { get; set; } // Used to determine if a timer was altered and we should abandon it. - public Timer(TimeSpan delay) => Init(delay, TimeSpan.Zero, 1); + public DateTime Next { get; private set; } + public TimeSpan Delay { get; set; } + public TimeSpan Interval { get; set; } + public int Index { get; private set; } + public int Count { get; private set; } + public int RemainingCount => Count - Index; + public bool Running { get; private set; } - public Timer(TimeSpan interval, int count) => Init(interval, interval, count); + public TimerProfile GetProfile() => !Core.Profiling ? null : TimerProfile.Acquire(ToString() ?? "null"); - public Timer(TimeSpan delay, TimeSpan interval, int count = 0) => Init(delay, interval, count); + public override string ToString() => GetType().FullName; - protected void Init(TimeSpan delay, TimeSpan interval, int count) + public Timer Start() + { + if (Running) { - Running = false; - Delay = delay; - Index = 0; - Interval = interval; - Count = count; - _nextTimer = null; - _prevTimer = null; - Next = Core.Now + Delay; - - var prof = GetProfile(); - - if (prof != null) - { - prof.Created++; - } - } - - protected int Version { get; set; } // Used to determine if a timer was altered and we should abandon it. - - public DateTime Next { get; private set; } - public TimeSpan Delay { get; set; } - public TimeSpan Interval { get; set; } - public int Index { get; private set; } - public int Count { get; private set; } - public int RemainingCount => Count - Index; - public bool Running { get; private set; } - - public TimerProfile GetProfile() => !Core.Profiling ? null : TimerProfile.Acquire(ToString() ?? "null"); - - public override string ToString() => GetType().FullName; - - public Timer Start() - { - if (Running) - { - return this; - } - - Index = 0; - Running = true; - AddTimer(this, (long)Delay.TotalMilliseconds); - - var prof = GetProfile(); - - if (prof != null) - { - prof.Started++; - } - return this; } - public virtual void Stop() + Index = 0; + Running = true; + AddTimer(this, (long)Delay.TotalMilliseconds); + + var prof = GetProfile(); + + if (prof != null) { - if (!Running) - { - return; - } - - // Do not detach if we are in the middle of executing the timer wheel for this ring/slot - if (!_timerWheelExecuting || _ringIndexes[_ring] != _slot) - { - // We are at the head - if (_rings[_ring][_slot] == this) - { - _rings[_ring][_slot] = _nextTimer; - } - - Detach(); - } - - Running = false; - Version++; - - var prof = GetProfile(); - if (prof != null) - { - prof.Stopped++; - } + prof.Started++; } - protected virtual void OnTick() + return this; + } + + public void Stop() + { + if (!Running) { + return; } - private void Attach(Timer timer) + Running = false; + + // Do not detach if we are in the middle of executing the timer wheel for this ring/slot + if (!_timerWheelExecuting || _ringIndexes[_ring] != _slot) { - _nextTimer = timer; - if (timer != null) + // We are at the head + if (_rings[_ring][_slot] == this) { - timer._prevTimer = this; + _rings[_ring][_slot] = _nextTimer; } + + Detach(); + OnDetach(); } - private void Detach() - { - if (_prevTimer != null) - { - _prevTimer._nextTimer = _nextTimer; - } - - if (_nextTimer != null) - { - _nextTimer._prevTimer = _prevTimer; - } - - _nextTimer = null; - _prevTimer = null; - } - - internal virtual void OnDetach() + Version++; + + var prof = GetProfile(); + if (prof != null) { + prof.Stopped++; } } + + protected virtual void OnTick() + { + } + + private void Attach(Timer timer) + { + _nextTimer = timer; + if (timer != null) + { + timer._prevTimer = this; + } + } + + private void Detach() + { + if (_prevTimer != null) + { + _prevTimer._nextTimer = _nextTimer; + } + + if (_nextTimer != null) + { + _nextTimer._prevTimer = _prevTimer; + } + + _nextTimer = null; + _prevTimer = null; + } + + internal virtual void OnDetach() + { + } } diff --git a/Projects/Server/Timer/TimerExecutionToken.cs b/Projects/Server/Timer/TimerExecutionToken.cs index 9e9992e3b..bcb54e005 100644 --- a/Projects/Server/Timer/TimerExecutionToken.cs +++ b/Projects/Server/Timer/TimerExecutionToken.cs @@ -16,48 +16,47 @@ using System; using System.Runtime.CompilerServices; -namespace Server +namespace Server; + +public struct TimerExecutionToken { - public struct TimerExecutionToken + private Timer.DelayCallTimer _timer; + + internal TimerExecutionToken(Timer.DelayCallTimer timer) => _timer = timer; + + public bool Running { - private Timer.DelayCallTimer _timer; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _timer?.Running == true; + } - internal TimerExecutionToken(Timer.DelayCallTimer timer) => _timer = timer; + public int Index + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _timer?.Index ?? 0; + } - public bool Running + public int RemainingCount + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _timer?.RemainingCount ?? 0; + } + + public DateTime Next + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _timer?.Next ?? DateTime.MinValue; + } + + public void Cancel() + { + if (_timer != null) { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => _timer?.Running == true; + _timer._returnOnDetach = true; + _timer?.Stop(); + _timer = null; } - public int Index - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => _timer?.Index ?? 0; - } - - public int RemainingCount - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => _timer?.RemainingCount ?? 0; - } - - public DateTime Next - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => _timer?.Next ?? DateTime.MinValue; - } - - public void Cancel() - { - if (_timer != null) - { - _timer._returnOnDetach = true; - _timer?.Stop(); - _timer = null; - } - - this = default; - } + this = default; } } diff --git a/Projects/UOContent/Mobiles/Monsters/ML/Special/Ilhenir.cs b/Projects/UOContent/Mobiles/Monsters/ML/Special/Ilhenir.cs index 073c4729c..4d8218697 100644 --- a/Projects/UOContent/Mobiles/Monsters/ML/Special/Ilhenir.cs +++ b/Projects/UOContent/Mobiles/Monsters/ML/Special/Ilhenir.cs @@ -322,6 +322,7 @@ namespace Server.Mobiles public override void OnAfterDelete() { + base.OnAfterDelete(); _timerToken.Cancel(); }