corrade-vassal – Blame information for rev 16

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