corrade-vassal – Blame information for rev 13

Subversion Repositories:
Rev:
Rev Author Line No. Line
13 eva 1 ///////////////////////////////////////////////////////////////////////////
2 // Copyright (C) Wizardry and Steamworks 2013 - License: GNU GPLv3 //
3 // Please see: http://www.gnu.org/licenses/gpl.html for legal details, //
4 // rights of fair usage, the disclaimer and warranty conditions. //
5 ///////////////////////////////////////////////////////////////////////////
6  
7 using System;
8 using System.Collections.Generic;
9 using System.Diagnostics;
10 using System.Linq;
11 using System.Threading;
12 using System.Threading.Tasks;
13 using System.Xml.Serialization;
14  
15 namespace wasSharp
16 {
17 public class Time
18 {
19 public delegate void TimerCallback(object state);
20  
21 public sealed class Timer : IDisposable
22 {
23 private static readonly Task CompletedTask = Task.FromResult(false);
24 private readonly TimerCallback Callback;
25 private readonly object State;
26 private Task Delay;
27 private bool Disposed;
28 private int Period;
29 private CancellationTokenSource TokenSource;
30  
31 public Timer(TimerCallback callback, object state, int dueTime, int period)
32 {
33 Callback = callback;
34 State = state;
35 Period = period;
36 Reset(dueTime);
37 }
38  
39 public Timer(TimerCallback callback, object state, TimeSpan dueTime, TimeSpan period)
40 : this(callback, state, (int) dueTime.TotalMilliseconds, (int) period.TotalMilliseconds)
41 {
42 }
43  
44 public void Dispose()
45 {
46 Dispose(true);
47 GC.SuppressFinalize(this);
48 }
49  
50 ~Timer()
51 {
52 Dispose(false);
53 }
54  
55 private void Dispose(bool cleanUpManagedObjects)
56 {
57 if (cleanUpManagedObjects)
58 Cancel();
59 Disposed = true;
60 }
61  
62 public void Change(int dueTime, int period)
63 {
64 Period = period;
65 Reset(dueTime);
66 }
67  
68 public void Change(TimeSpan dueTime, TimeSpan period)
69 {
70 Change((int) dueTime.TotalMilliseconds, (int) period.TotalMilliseconds);
71 }
72  
73 private void Reset(int due)
74 {
75 Cancel();
76 if (due >= 0)
77 {
78 TokenSource = new CancellationTokenSource();
79 Action tick = null;
80 tick = () =>
81 {
82 Task.Run(() => Callback(State));
83 if (Disposed || Period < 0) return;
84 Delay = Period > 0 ? Task.Delay(Period, TokenSource.Token) : CompletedTask;
85 Delay.ContinueWith(t => tick(), TokenSource.Token);
86 };
87 Delay = due > 0 ? Task.Delay(due, TokenSource.Token) : CompletedTask;
88 Delay.ContinueWith(t => tick(), TokenSource.Token);
89 }
90 }
91  
92 private void Cancel()
93 {
94 if (TokenSource != null)
95 {
96 TokenSource.Cancel();
97 TokenSource.Dispose();
98 TokenSource = null;
99 }
100 }
101 }
102  
103 ///////////////////////////////////////////////////////////////////////////
104 // Copyright (C) Wizardry and Steamworks 2015 - License: GNU GPLv3 //
105 ///////////////////////////////////////////////////////////////////////////
106 /// <summary>
107 /// Given a number of allowed events per seconds, this class allows you
108 /// to determine via the IsSafe property whether it is safe to trigger
109 /// another lined-up event. This is mostly used to check that throttles
110 /// are being respected.
111 /// </summary>
112 public class TimedThrottle : IDisposable
113 {
114 private readonly uint EventsAllowed;
115 private readonly object LockObject = new object();
116 private Timer timer;
117 private uint TriggeredEvents;
118  
119 public TimedThrottle(uint events, uint seconds)
120 {
121 EventsAllowed = events;
122 if (timer == null)
123 {
124 timer = new Timer(o =>
125 {
126 lock (LockObject)
127 {
128 TriggeredEvents = 0;
129 }
130 }, null, (int) seconds, (int) seconds);
131 }
132 }
133  
134 public bool IsSafe
135 {
136 get
137 {
138 lock (LockObject)
139 {
140 return ++TriggeredEvents <= EventsAllowed;
141 }
142 }
143 }
144  
145 public void Dispose()
146 {
147 Dispose(true);
148 GC.SuppressFinalize(this);
149 }
150  
151 protected virtual void Dispose(bool dispose)
152 {
153 if (timer != null)
154 {
155 timer.Dispose();
156 timer = null;
157 }
158 }
159 }
160  
161 ///////////////////////////////////////////////////////////////////////////
162 // Copyright (C) Wizardry and Steamworks 2013 - License: GNU GPLv3 //
163 ///////////////////////////////////////////////////////////////////////////
164 /// <summary>
165 /// An alarm class similar to the UNIX alarm with the added benefit
166 /// of a decaying timer that tracks the time between rescheduling.
167 /// </summary>
168 /// <remarks>
169 /// (C) Wizardry and Steamworks 2013 - License: GNU GPLv3
170 /// </remarks>
171 public class DecayingAlarm : IDisposable
172 {
173 [Flags]
174 public enum DECAY_TYPE
175 {
176 [XmlEnum(Name = "none")] NONE = 0,
177 [XmlEnum(Name = "arithmetic")] ARITHMETIC = 1,
178 [XmlEnum(Name = "geometric")] GEOMETRIC = 2,
179 [XmlEnum(Name = "harmonic")] HARMONIC = 4,
180 [XmlEnum(Name = "weighted")] WEIGHTED = 5
181 }
182  
183 private readonly DECAY_TYPE decay = DECAY_TYPE.NONE;
184 private readonly Stopwatch elapsed = new Stopwatch();
185 private readonly object LockObject = new object();
186 private readonly HashSet<double> times = new HashSet<double>();
187 private Timer alarm;
188  
189 /// <summary>
190 /// The default constructor using no decay.
191 /// </summary>
192 public DecayingAlarm()
193 {
194 Signal = new ManualResetEvent(false);
195 }
196  
197 /// <summary>
198 /// The constructor for the DecayingAlarm class taking as parameter a decay type.
199 /// </summary>
200 /// <param name="decay">the type of decay: arithmetic, geometric, harmonic, heronian or quadratic</param>
201 public DecayingAlarm(DECAY_TYPE decay)
202 {
203 Signal = new ManualResetEvent(false);
204 this.decay = decay;
205 }
206  
207 public ManualResetEvent Signal { get; set; }
208  
209 public void Dispose()
210 {
211 Dispose(true);
212 GC.SuppressFinalize(this);
213 }
214  
215 public void Alarm(double deadline)
216 {
217 lock (LockObject)
218 {
219 switch (alarm == null)
220 {
221 case true:
222 elapsed.Start();
223 alarm = new Timer(o =>
224 {
225 lock (LockObject)
226 {
227 Signal.Set();
228 elapsed.Stop();
229 times.Clear();
230 alarm.Dispose();
231 alarm = null;
232 }
233 }, null, (int) deadline, 0);
234 return;
235 case false:
236 elapsed.Stop();
237 times.Add(elapsed.ElapsedMilliseconds);
238 switch (decay)
239 {
240 case DECAY_TYPE.ARITHMETIC:
241 alarm?.Change(
242 (int) ((deadline + times.Aggregate((a, b) => b + a))/(1f + times.Count)), 0);
243 break;
244 case DECAY_TYPE.GEOMETRIC:
245 alarm?.Change((int) (Math.Pow(deadline*times.Aggregate((a, b) => b*a),
246 1f/(1f + times.Count))), 0);
247 break;
248 case DECAY_TYPE.HARMONIC:
249 alarm?.Change((int) ((1f + times.Count)/
250 (1f/deadline + times.Aggregate((a, b) => 1f/b + 1f/a))), 0);
251 break;
252 case DECAY_TYPE.WEIGHTED:
253 HashSet<double> d = new HashSet<double>(times) {deadline};
254 double total = d.Aggregate((a, b) => b + a);
255 alarm?.Change(
256 (int) (d.Aggregate((a, b) => Math.Pow(a, 2)/total + Math.Pow(b, 2)/total)), 0);
257 break;
258 default:
259 alarm?.Change((int) deadline, 0);
260 break;
261 }
262 elapsed.Reset();
263 elapsed.Start();
264 break;
265 }
266 }
267 }
268  
269 protected virtual void Dispose(bool dispose)
270 {
271 if (alarm != null)
272 {
273 alarm.Dispose();
274 alarm = null;
275 }
276 }
277 }
278 }
279 }