/////////////////////////////////////////////////////////////////////////// // Copyright (C) Wizardry and Steamworks 2013 - License: GNU GPLv3 // // Please see: http://www.gnu.org/licenses/gpl.html for legal details, // // rights of fair usage, the disclaimer and warranty conditions. // /////////////////////////////////////////////////////////////////////////// using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading; using System.Xml.Serialization; namespace wasSharp.Timers { /////////////////////////////////////////////////////////////////////////// // Copyright (C) Wizardry and Steamworks 2013 - License: GNU GPLv3 // /////////////////////////////////////////////////////////////////////////// /// /// An alarm class similar to the UNIX alarm with the added benefit /// of a decaying timer that tracks the time between rescheduling. /// /// /// (C) Wizardry and Steamworks 2013 - License: GNU GPLv3 /// public class DecayingAlarm : IDisposable { [Flags] public enum DECAY_TYPE { [Reflection.NameAttribute("none")] [XmlEnum(Name = "none")] NONE = 0, [Reflection.NameAttribute("arithmetic")] [XmlEnum(Name = "arithmetic")] ARITHMETIC = 1, [Reflection.NameAttribute("geometric")] [XmlEnum(Name = "geometric")] GEOMETRIC = 2, [Reflection.NameAttribute("harmonic")] [XmlEnum(Name = "harmonic")] HARMONIC = 4, [Reflection.NameAttribute("weighted")] [XmlEnum(Name = "weighted")] WEIGHTED = 5 } private readonly DECAY_TYPE decay = DECAY_TYPE.NONE; private readonly Stopwatch elapsed = new Stopwatch(); private readonly object LockObject = new object(); private readonly HashSet times = new HashSet(); private Timer alarm; /// /// The default constructor using no decay. /// public DecayingAlarm() { Signal = new ManualResetEvent(false); } /// /// The constructor for the DecayingAlarm class taking as parameter a decay type. /// /// the type of decay: arithmetic, geometric, harmonic, heronian or quadratic public DecayingAlarm(DECAY_TYPE decay) { Signal = new ManualResetEvent(false); this.decay = decay; } public ManualResetEvent Signal { get; set; } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } ~DecayingAlarm() { Dispose(false); } public void Alarm(double deadline) { lock (LockObject) { switch (alarm == null) { case true: elapsed.Start(); alarm = new Timer(() => { lock (LockObject) { Signal.Set(); elapsed.Stop(); times.Clear(); alarm.Dispose(); alarm = null; } }, deadline, 0); return; case false: elapsed.Stop(); times.Add(elapsed.ElapsedMilliseconds); switch (decay) { case DECAY_TYPE.ARITHMETIC: alarm?.Change( (int)((deadline + times.Aggregate((a, b) => b + a)) / (1f + times.Count())), 0); break; case DECAY_TYPE.GEOMETRIC: alarm?.Change((int)Math.Pow(deadline * times.Aggregate((a, b) => b * a), 1f / (1f + times.Count())), 0); break; case DECAY_TYPE.HARMONIC: alarm?.Change((int)((1f + times.Count()) / (1f / deadline + times.Aggregate((a, b) => 1f / b + 1f / a))), 0); break; case DECAY_TYPE.WEIGHTED: var d = new HashSet(times) { deadline }; var total = d.Aggregate((a, b) => b + a); alarm?.Change( (int)d.Aggregate((a, b) => Math.Pow(a, 2) / total + Math.Pow(b, 2) / total), 0); break; default: alarm?.Change((int)deadline, 0); break; } elapsed.Reset(); elapsed.Start(); break; } } } private void Dispose(bool dispose) { if (alarm != null) { alarm.Dispose(); alarm = null; } } public DecayingAlarm Clone() { return new DecayingAlarm(decay); } } }