# HG changeset patch # User Frank Benoit # Date 1224334969 -7200 # Node ID 987b95661bb9d9d5e85ba4c7cf7650119c1e75ba # Parent 26589d623405fd48519e84771bea3eec429d2eb9 Added impl for Timer/TimerTask diff -r 26589d623405 -r 987b95661bb9 dwtx/dwtxhelper/JThread.d --- a/dwtx/dwtxhelper/JThread.d Sat Oct 18 15:02:28 2008 +0200 +++ b/dwtx/dwtxhelper/JThread.d Sat Oct 18 15:02:49 2008 +0200 @@ -2,6 +2,7 @@ import tango.core.Thread; import dwt.dwthelper.utils; +import tango.util.log.Trace; class JThread { @@ -66,7 +67,11 @@ return (thread.priority-Thread.PRIORITY_MIN) * (MAX_PRIORITY-MIN_PRIORITY) / (Thread.PRIORITY_MAX-Thread.PRIORITY_MIN) + MIN_PRIORITY; } public void setPriority( int newPriority ) { - thread.priority( (newPriority-MIN_PRIORITY) * (Thread.PRIORITY_MAX-Thread.PRIORITY_MIN) / (MAX_PRIORITY-MIN_PRIORITY) +Thread.PRIORITY_MIN ); +// assert( MIN_PRIORITY < MAX_PRIORITY ); +// assert( Thread.PRIORITY_MIN < Thread.PRIORITY_MAX ); + auto scaledPrio = (newPriority-MIN_PRIORITY) * (Thread.PRIORITY_MAX-Thread.PRIORITY_MIN) / (MAX_PRIORITY-MIN_PRIORITY) +Thread.PRIORITY_MIN; + Trace.formatln( "JThread.setPriority: scale ({} {} {}) -> ({} {} {})", MIN_PRIORITY, newPriority, MAX_PRIORITY, Thread.PRIORITY_MIN, scaledPrio, Thread.PRIORITY_MAX); +// thread.priority( scaledPrio ); } private void internalRun(){ diff -r 26589d623405 -r 987b95661bb9 dwtx/dwtxhelper/Timer.d --- a/dwtx/dwtxhelper/Timer.d Sat Oct 18 15:02:28 2008 +0200 +++ b/dwtx/dwtxhelper/Timer.d Sat Oct 18 15:02:49 2008 +0200 @@ -1,16 +1,319 @@ module dwtx.dwtxhelper.Timer; import dwtx.dwtxhelper.TimerTask; -import tango.util.container.CircularList; +// import tango.util.container.CircularList; import dwtx.dwtxhelper.JThread; import tango.core.sync.Mutex; import tango.core.sync.Condition; -import tango.time.Time; -import tango.time.Clock; +// import tango.time.Time; +// import tango.time.Clock; +import tango.text.convert.Format; +import dwt.dwthelper.utils; class Timer { + private static final class TaskQueue { + private Mutex mutex; + private Condition cond; - alias CircularList!( TimerTask ) ListType; + + private static const int DEFAULT_SIZE = 32; + private bool nullOnEmpty; + private TimerTask heap[]; + private int elements; + public this() { + mutex = new Mutex(); + cond = new Condition( mutex ); + heap = new TimerTask[DEFAULT_SIZE]; + elements = 0; + nullOnEmpty = false; + } + + private void add(TimerTask task) { + elements++; + if (elements is heap.length) { + TimerTask new_heap[] = new TimerTask[heap.length * 2]; + System.arraycopy(heap, 0, new_heap, 0, heap.length); + heap = new_heap; + } + heap[elements] = task; + } + + private void remove() { + // clear the entry first + heap[elements] = null; + elements--; + if (elements + DEFAULT_SIZE / 2 <= (heap.length / 4)) { + TimerTask new_heap[] = new TimerTask[heap.length / 2]; + System.arraycopy(heap, 0, new_heap, 0, elements + 1); + heap = new_heap; + } + } + + public void enqueue(TimerTask task) { + synchronized( mutex ){ + if (heap is null) { + throw new IllegalStateException("cannot enqueue when stop() has been called on queue"); + } + + heap[0] = task; + add(task); + int child = elements; + int parent = child / 2; + while (heap[parent].scheduled > task.scheduled) { + heap[child] = heap[parent]; + child = parent; + parent = child / 2; + } + heap[child] = task; + heap[0] = null; + cond.notify(); + } + } + + private TimerTask top() { + if (elements is 0) { + return null; + } + else { + return heap[1]; + } + } + + public TimerTask serve() { + synchronized( mutex ){ + TimerTask task = null; + while (task is null) { + task = top(); + + if ((heap is null) || (task is null && nullOnEmpty)) { + return null; + } + + if (task !is null) { + // The time to wait until the task should be served + long time = task.scheduled - System.currentTimeMillis(); + if (time > 0) { + // This task should not yet be served + // So wait until this task is ready + // or something else happens to the queue + task = null; // set to null to make sure we call top() + try { + cond.wait(time); + } + catch (InterruptedException _) { + } + } + } + else { + // wait until a task is added + // or something else happens to the queue + try { + cond.wait(); + } + catch (InterruptedException _) { + } + } + } + + TimerTask lastTask = heap[elements]; + remove(); + + int parent = 1; + int child = 2; + heap[1] = lastTask; + while (child <= elements) { + if (child < elements) { + if (heap[child].scheduled > heap[child + 1].scheduled) { + child++; + } + } + + if (lastTask.scheduled <= heap[child].scheduled) + break; + + heap[parent] = heap[child]; + parent = child; + child = parent * 2; + } + + heap[parent] = lastTask; + return task; + } + } + + public void setNullOnEmpty(bool nullOnEmpty) { + synchronized( mutex ){ + this.nullOnEmpty = nullOnEmpty; + cond.notify(); + } + } + + public void stop() { + synchronized( mutex ){ + this.heap = null; + this.elements = 0; + cond.notify(); + } + } + + } + + private static final class Scheduler : Runnable { + private TaskQueue queue; + + public this(TaskQueue queue) { + this.queue = queue; + } + + public void run() { + TimerTask task; + while ((task = queue.serve()) !is null) { + if (task.scheduled >= 0) { + task.lastExecutionTime = task.scheduled; + if (task.period < 0) { + task.scheduled = -1; + } + try { + task.run(); + } +// catch (ThreadDeath death) { +// // If an exception escapes, the Timer becomes invalid. +// queue.stop(); +// throw death; +// } + catch (Exception t) { + queue.stop(); + } + } + if (task.scheduled >= 0) { + if (task.fixed) { + task.scheduled += task.period; + } + else { + task.scheduled = task.period + System.currentTimeMillis(); + } + + try { + queue.enqueue(task); + } + catch (IllegalStateException ise) { + // Ignore. Apparently the Timer queue has been stopped. + } + } + } + } + } + + private static int nr; + private TaskQueue queue; + private Scheduler scheduler; + private JThread thread; + private bool canceled; + + public this() { + this(false); + } + + public this(bool daemon) { + this(daemon, JThread.NORM_PRIORITY); + } + + private this(bool daemon, int priority) { + this(daemon, priority, Format( "Timer-{}", ++nr)); + } + + private this(bool daemon, int priority, String name) { + canceled = false; + queue = new TaskQueue(); + scheduler = new Scheduler(queue); + thread = new JThread(scheduler, name); + thread.setDaemon(daemon); + thread.setPriority(priority); + thread.start(); + } + + public void cancel() { + canceled = true; + queue.stop(); + } + + private void schedule(TimerTask task, long time, long period, bool fixed) { + if (time < 0) + throw new IllegalArgumentException("negative time"); + + if (task.scheduled is 0 && task.lastExecutionTime is -1) { + task.scheduled = time; + task.period = period; + task.fixed = fixed; + } + else { + throw new IllegalStateException("task was already scheduled or canceled"); + } + + if (!this.canceled && this.thread !is null) { + queue.enqueue(task); + } + else { + throw new IllegalStateException("timer was canceled or scheduler thread has died"); + } + } + + private static void positiveDelay(long delay) { + if (delay < 0) { + throw new IllegalArgumentException("delay is negative"); + } + } + + private static void positivePeriod(long period) { + if (period < 0) { + throw new IllegalArgumentException("period is negative"); + } + } + +// public void schedule(TimerTask task, Date date) { +// long time = date.getTime(); +// schedule(task, time, -1, false); +// } + +// public void schedule(TimerTask task, Date date, long period) { +// positivePeriod(period); +// long time = date.getTime(); +// schedule(task, time, period, false); +// } + + public void schedule(TimerTask task, long delay) { + positiveDelay(delay); + long time = System.currentTimeMillis() + delay; + schedule(task, time, -1, false); + } + + public void schedule(TimerTask task, long delay, long period) { + positiveDelay(delay); + positivePeriod(period); + long time = System.currentTimeMillis() + delay; + schedule(task, time, period, false); + } + +// public void scheduleAtFixedRate(TimerTask task, Date date, long period) { +// positivePeriod(period); +// long time = date.getTime(); +// schedule(task, time, period, true); +// } + + public void scheduleAtFixedRate(TimerTask task, long delay, long period) { + positiveDelay(delay); + positivePeriod(period); + long time = System.currentTimeMillis() + delay; + schedule(task, time, period, true); + } + + protected void finalize() { + queue.setNullOnEmpty(true); + } + + + /////////////////////////////////////////////////// + /+ alias CircularList!( TimerTask ) ListType; private JThread thread; private ListType schedules; @@ -87,28 +390,30 @@ void scheduleAtFixedRate(TimerTask task, long delay, long period){ assert( task ); version(TANGOSVN){ - task.executionTime = Clock.now + TimeSpan.fromMillis(delay); + task.executionTime = Clock.now + TimeSpan.fromMillis(delay); } else { - task.executionTime = Clock.now + TimeSpan.millis(delay); + task.executionTime = Clock.now + TimeSpan.millis(delay); } task.timer = this; synchronized(mutex){ int index = 0; - foreach( tt; schedules ){ - if( tt.executionTime > task.executionTime ){ - break; + if( schedules.size() > 0 ) + foreach( tt; schedules ){ + if( tt.executionTime > task.executionTime ){ + break; + } + index++; } - index++; - } schedules.addAt( index, task ); cond.notifyAll(); } } -// void schedule(TimerTask task, Date time){} -// void schedule(TimerTask task, Date firstTime, long period){} -// void schedule(TimerTask task, long delay, long period){} -// void scheduleAtFixedRate(TimerTask task, Date firstTime, long period){} + // void schedule(TimerTask task, Date time){} + // void schedule(TimerTask task, Date firstTime, long period){} + // void schedule(TimerTask task, long delay, long period){} + // void scheduleAtFixedRate(TimerTask task, Date firstTime, long period){} + +/ } diff -r 26589d623405 -r 987b95661bb9 dwtx/dwtxhelper/TimerTask.d --- a/dwtx/dwtxhelper/TimerTask.d Sat Oct 18 15:02:28 2008 +0200 +++ b/dwtx/dwtxhelper/TimerTask.d Sat Oct 18 15:02:49 2008 +0200 @@ -6,19 +6,27 @@ import dwtx.dwtxhelper.Timer; class TimerTask : Runnable { - package TimeSpan period; - package Time executionTime; - package Timer timer; + + package long scheduled; + package long lastExecutionTime; + package long period; + package bool fixed; + + this(){ + this.scheduled = 0; + this.lastExecutionTime = -1; + } bool cancel(){ - implMissing( __FILE__, __LINE__ ); - return false; + bool prevented_execution = (this.scheduled >= 0); + this.scheduled = -1; + return prevented_execution; } abstract void run(); long scheduledExcecutionTime(){ - return ( executionTime - Time.epoch1970 ).millis; + return lastExecutionTime; } }