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