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