changeset 185:987b95661bb9

Added impl for Timer/TimerTask
author Frank Benoit <benoit@tionex.de>
date Sat, 18 Oct 2008 15:02:49 +0200
parents 26589d623405
children 0e2944da7cd0
files dwtx/dwtxhelper/JThread.d dwtx/dwtxhelper/Timer.d dwtx/dwtxhelper/TimerTask.d
diffstat 3 files changed, 340 insertions(+), 22 deletions(-) [+]
line wrap: on
line diff
--- 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(){
--- 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){}
+    +/
 }
 
 
--- 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;
     }
 }