Mercurial > projects > dwt-addons
comparison dwtx/dwtxhelper/Timer.d @ 185:987b95661bb9
Added impl for Timer/TimerTask
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Sat, 18 Oct 2008 15:02:49 +0200 |
parents | 1470d66733fa |
children | eb3414669eb0 |
comparison
equal
deleted
inserted
replaced
184:26589d623405 | 185:987b95661bb9 |
---|---|
1 module dwtx.dwtxhelper.Timer; | 1 module dwtx.dwtxhelper.Timer; |
2 | 2 |
3 import dwtx.dwtxhelper.TimerTask; | 3 import dwtx.dwtxhelper.TimerTask; |
4 import tango.util.container.CircularList; | 4 // import tango.util.container.CircularList; |
5 import dwtx.dwtxhelper.JThread; | 5 import dwtx.dwtxhelper.JThread; |
6 import tango.core.sync.Mutex; | 6 import tango.core.sync.Mutex; |
7 import tango.core.sync.Condition; | 7 import tango.core.sync.Condition; |
8 import tango.time.Time; | 8 // import tango.time.Time; |
9 import tango.time.Clock; | 9 // import tango.time.Clock; |
10 import tango.text.convert.Format; | |
11 import dwt.dwthelper.utils; | |
10 | 12 |
11 class Timer { | 13 class Timer { |
12 | 14 private static final class TaskQueue { |
13 alias CircularList!( TimerTask ) ListType; | 15 private Mutex mutex; |
16 private Condition cond; | |
17 | |
18 | |
19 private static const int DEFAULT_SIZE = 32; | |
20 private bool nullOnEmpty; | |
21 private TimerTask heap[]; | |
22 private int elements; | |
23 public this() { | |
24 mutex = new Mutex(); | |
25 cond = new Condition( mutex ); | |
26 heap = new TimerTask[DEFAULT_SIZE]; | |
27 elements = 0; | |
28 nullOnEmpty = false; | |
29 } | |
30 | |
31 private void add(TimerTask task) { | |
32 elements++; | |
33 if (elements is heap.length) { | |
34 TimerTask new_heap[] = new TimerTask[heap.length * 2]; | |
35 System.arraycopy(heap, 0, new_heap, 0, heap.length); | |
36 heap = new_heap; | |
37 } | |
38 heap[elements] = task; | |
39 } | |
40 | |
41 private void remove() { | |
42 // clear the entry first | |
43 heap[elements] = null; | |
44 elements--; | |
45 if (elements + DEFAULT_SIZE / 2 <= (heap.length / 4)) { | |
46 TimerTask new_heap[] = new TimerTask[heap.length / 2]; | |
47 System.arraycopy(heap, 0, new_heap, 0, elements + 1); | |
48 heap = new_heap; | |
49 } | |
50 } | |
51 | |
52 public void enqueue(TimerTask task) { | |
53 synchronized( mutex ){ | |
54 if (heap is null) { | |
55 throw new IllegalStateException("cannot enqueue when stop() has been called on queue"); | |
56 } | |
57 | |
58 heap[0] = task; | |
59 add(task); | |
60 int child = elements; | |
61 int parent = child / 2; | |
62 while (heap[parent].scheduled > task.scheduled) { | |
63 heap[child] = heap[parent]; | |
64 child = parent; | |
65 parent = child / 2; | |
66 } | |
67 heap[child] = task; | |
68 heap[0] = null; | |
69 cond.notify(); | |
70 } | |
71 } | |
72 | |
73 private TimerTask top() { | |
74 if (elements is 0) { | |
75 return null; | |
76 } | |
77 else { | |
78 return heap[1]; | |
79 } | |
80 } | |
81 | |
82 public TimerTask serve() { | |
83 synchronized( mutex ){ | |
84 TimerTask task = null; | |
85 while (task is null) { | |
86 task = top(); | |
87 | |
88 if ((heap is null) || (task is null && nullOnEmpty)) { | |
89 return null; | |
90 } | |
91 | |
92 if (task !is null) { | |
93 // The time to wait until the task should be served | |
94 long time = task.scheduled - System.currentTimeMillis(); | |
95 if (time > 0) { | |
96 // This task should not yet be served | |
97 // So wait until this task is ready | |
98 // or something else happens to the queue | |
99 task = null; // set to null to make sure we call top() | |
100 try { | |
101 cond.wait(time); | |
102 } | |
103 catch (InterruptedException _) { | |
104 } | |
105 } | |
106 } | |
107 else { | |
108 // wait until a task is added | |
109 // or something else happens to the queue | |
110 try { | |
111 cond.wait(); | |
112 } | |
113 catch (InterruptedException _) { | |
114 } | |
115 } | |
116 } | |
117 | |
118 TimerTask lastTask = heap[elements]; | |
119 remove(); | |
120 | |
121 int parent = 1; | |
122 int child = 2; | |
123 heap[1] = lastTask; | |
124 while (child <= elements) { | |
125 if (child < elements) { | |
126 if (heap[child].scheduled > heap[child + 1].scheduled) { | |
127 child++; | |
128 } | |
129 } | |
130 | |
131 if (lastTask.scheduled <= heap[child].scheduled) | |
132 break; | |
133 | |
134 heap[parent] = heap[child]; | |
135 parent = child; | |
136 child = parent * 2; | |
137 } | |
138 | |
139 heap[parent] = lastTask; | |
140 return task; | |
141 } | |
142 } | |
143 | |
144 public void setNullOnEmpty(bool nullOnEmpty) { | |
145 synchronized( mutex ){ | |
146 this.nullOnEmpty = nullOnEmpty; | |
147 cond.notify(); | |
148 } | |
149 } | |
150 | |
151 public void stop() { | |
152 synchronized( mutex ){ | |
153 this.heap = null; | |
154 this.elements = 0; | |
155 cond.notify(); | |
156 } | |
157 } | |
158 | |
159 } | |
160 | |
161 private static final class Scheduler : Runnable { | |
162 private TaskQueue queue; | |
163 | |
164 public this(TaskQueue queue) { | |
165 this.queue = queue; | |
166 } | |
167 | |
168 public void run() { | |
169 TimerTask task; | |
170 while ((task = queue.serve()) !is null) { | |
171 if (task.scheduled >= 0) { | |
172 task.lastExecutionTime = task.scheduled; | |
173 if (task.period < 0) { | |
174 task.scheduled = -1; | |
175 } | |
176 try { | |
177 task.run(); | |
178 } | |
179 // catch (ThreadDeath death) { | |
180 // // If an exception escapes, the Timer becomes invalid. | |
181 // queue.stop(); | |
182 // throw death; | |
183 // } | |
184 catch (Exception t) { | |
185 queue.stop(); | |
186 } | |
187 } | |
188 if (task.scheduled >= 0) { | |
189 if (task.fixed) { | |
190 task.scheduled += task.period; | |
191 } | |
192 else { | |
193 task.scheduled = task.period + System.currentTimeMillis(); | |
194 } | |
195 | |
196 try { | |
197 queue.enqueue(task); | |
198 } | |
199 catch (IllegalStateException ise) { | |
200 // Ignore. Apparently the Timer queue has been stopped. | |
201 } | |
202 } | |
203 } | |
204 } | |
205 } | |
206 | |
207 private static int nr; | |
208 private TaskQueue queue; | |
209 private Scheduler scheduler; | |
210 private JThread thread; | |
211 private bool canceled; | |
212 | |
213 public this() { | |
214 this(false); | |
215 } | |
216 | |
217 public this(bool daemon) { | |
218 this(daemon, JThread.NORM_PRIORITY); | |
219 } | |
220 | |
221 private this(bool daemon, int priority) { | |
222 this(daemon, priority, Format( "Timer-{}", ++nr)); | |
223 } | |
224 | |
225 private this(bool daemon, int priority, String name) { | |
226 canceled = false; | |
227 queue = new TaskQueue(); | |
228 scheduler = new Scheduler(queue); | |
229 thread = new JThread(scheduler, name); | |
230 thread.setDaemon(daemon); | |
231 thread.setPriority(priority); | |
232 thread.start(); | |
233 } | |
234 | |
235 public void cancel() { | |
236 canceled = true; | |
237 queue.stop(); | |
238 } | |
239 | |
240 private void schedule(TimerTask task, long time, long period, bool fixed) { | |
241 if (time < 0) | |
242 throw new IllegalArgumentException("negative time"); | |
243 | |
244 if (task.scheduled is 0 && task.lastExecutionTime is -1) { | |
245 task.scheduled = time; | |
246 task.period = period; | |
247 task.fixed = fixed; | |
248 } | |
249 else { | |
250 throw new IllegalStateException("task was already scheduled or canceled"); | |
251 } | |
252 | |
253 if (!this.canceled && this.thread !is null) { | |
254 queue.enqueue(task); | |
255 } | |
256 else { | |
257 throw new IllegalStateException("timer was canceled or scheduler thread has died"); | |
258 } | |
259 } | |
260 | |
261 private static void positiveDelay(long delay) { | |
262 if (delay < 0) { | |
263 throw new IllegalArgumentException("delay is negative"); | |
264 } | |
265 } | |
266 | |
267 private static void positivePeriod(long period) { | |
268 if (period < 0) { | |
269 throw new IllegalArgumentException("period is negative"); | |
270 } | |
271 } | |
272 | |
273 // public void schedule(TimerTask task, Date date) { | |
274 // long time = date.getTime(); | |
275 // schedule(task, time, -1, false); | |
276 // } | |
277 | |
278 // public void schedule(TimerTask task, Date date, long period) { | |
279 // positivePeriod(period); | |
280 // long time = date.getTime(); | |
281 // schedule(task, time, period, false); | |
282 // } | |
283 | |
284 public void schedule(TimerTask task, long delay) { | |
285 positiveDelay(delay); | |
286 long time = System.currentTimeMillis() + delay; | |
287 schedule(task, time, -1, false); | |
288 } | |
289 | |
290 public void schedule(TimerTask task, long delay, long period) { | |
291 positiveDelay(delay); | |
292 positivePeriod(period); | |
293 long time = System.currentTimeMillis() + delay; | |
294 schedule(task, time, period, false); | |
295 } | |
296 | |
297 // public void scheduleAtFixedRate(TimerTask task, Date date, long period) { | |
298 // positivePeriod(period); | |
299 // long time = date.getTime(); | |
300 // schedule(task, time, period, true); | |
301 // } | |
302 | |
303 public void scheduleAtFixedRate(TimerTask task, long delay, long period) { | |
304 positiveDelay(delay); | |
305 positivePeriod(period); | |
306 long time = System.currentTimeMillis() + delay; | |
307 schedule(task, time, period, true); | |
308 } | |
309 | |
310 protected void finalize() { | |
311 queue.setNullOnEmpty(true); | |
312 } | |
313 | |
314 | |
315 /////////////////////////////////////////////////// | |
316 /+ alias CircularList!( TimerTask ) ListType; | |
14 | 317 |
15 private JThread thread; | 318 private JThread thread; |
16 private ListType schedules; | 319 private ListType schedules; |
17 private Mutex mutex; | 320 private Mutex mutex; |
18 private Condition cond; | 321 private Condition cond; |
85 scheduleAtFixedRate( task, delay, 0 ); | 388 scheduleAtFixedRate( task, delay, 0 ); |
86 } | 389 } |
87 void scheduleAtFixedRate(TimerTask task, long delay, long period){ | 390 void scheduleAtFixedRate(TimerTask task, long delay, long period){ |
88 assert( task ); | 391 assert( task ); |
89 version(TANGOSVN){ | 392 version(TANGOSVN){ |
90 task.executionTime = Clock.now + TimeSpan.fromMillis(delay); | 393 task.executionTime = Clock.now + TimeSpan.fromMillis(delay); |
91 } else { | 394 } else { |
92 task.executionTime = Clock.now + TimeSpan.millis(delay); | 395 task.executionTime = Clock.now + TimeSpan.millis(delay); |
93 } | 396 } |
94 task.timer = this; | 397 task.timer = this; |
95 synchronized(mutex){ | 398 synchronized(mutex){ |
96 int index = 0; | 399 int index = 0; |
97 foreach( tt; schedules ){ | 400 if( schedules.size() > 0 ) |
98 if( tt.executionTime > task.executionTime ){ | 401 foreach( tt; schedules ){ |
99 break; | 402 if( tt.executionTime > task.executionTime ){ |
100 } | 403 break; |
101 index++; | 404 } |
102 } | 405 index++; |
406 } | |
103 schedules.addAt( index, task ); | 407 schedules.addAt( index, task ); |
104 cond.notifyAll(); | 408 cond.notifyAll(); |
105 } | 409 } |
106 } | 410 } |
107 | 411 |
108 // void schedule(TimerTask task, Date time){} | 412 // void schedule(TimerTask task, Date time){} |
109 // void schedule(TimerTask task, Date firstTime, long period){} | 413 // void schedule(TimerTask task, Date firstTime, long period){} |
110 // void schedule(TimerTask task, long delay, long period){} | 414 // void schedule(TimerTask task, long delay, long period){} |
111 // void scheduleAtFixedRate(TimerTask task, Date firstTime, long period){} | 415 // void scheduleAtFixedRate(TimerTask task, Date firstTime, long period){} |
416 +/ | |
112 } | 417 } |
113 | 418 |
114 | 419 |