view dwtx/dwtxhelper/Timer.d @ 200:eb3414669eb0 default tip

fix for dmd 1.041 and tango 0.99.8
author Frank Benoit <benoit@tionex.de>
date Sat, 28 Mar 2009 03:09:57 +0100
parents 987b95661bb9
children
line wrap: on
line source

module dwtx.dwtxhelper.Timer;

import dwtx.dwtxhelper.TimerTask;
// 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.text.convert.Format;
import dwt.dwthelper.utils;

class Timer {
    private static final class TaskQueue {
        private Mutex mutex;
        private Condition cond;


        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;
    private Mutex mutex;
    private Condition cond;
    private bool isCanceled = false;

    this(){
        this(false);
    }
    this(bool isDaemon){
        mutex = new Mutex();
        cond = new Condition( mutex );

        schedules = new ListType();
        thread = new JThread( &run );
        thread.setDaemon( isDaemon );
        thread.start();
    }
    private void run(){

        while( !isCanceled ){
            TimerTask timerTask = null;
            synchronized(mutex){
                bool isReady = false;
                do{
                    if( isCanceled ){
                        return;
                    }

                    if( schedules.size() is 0 ){
                        cond.wait();
                    }
                    else{
                        timerTask = schedules.head();
                        TimeSpan toWait = timerTask.executionTime - Clock.now();
                        if( toWait.interval() > 0 ){
                            cond.wait( toWait.interval() );
                        }
                        else{
                            schedules.removeHead();
                            isReady = true;
                        }
                    }
                }while( !isReady );
            }
            if( timerTask ){
                timerTask.run();
                if( timerTask.period.millis > 0 ){
                    timerTask.executionTime += timerTask.period;
                    synchronized(mutex){
                        int index = 0;
                        foreach( tt; schedules ){
                            if( tt.executionTime > timerTask.executionTime ){
                                break;
                            }
                            index++;
                        }
                        schedules.addAt( index, timerTask );
                    }
                }
            }
        }
    }
    void   cancel(){
        synchronized(mutex){
            isCanceled = true;
            cond.notifyAll();
        }
    }
    void   schedule(TimerTask task, long delay){
        scheduleAtFixedRate( task, delay, 0 );
    }
    void   scheduleAtFixedRate(TimerTask task, long delay, long period){
        assert( task );
        task.executionTime = Clock.now + TimeSpan.fromMillis(delay);
        task.timer = this;
        synchronized(mutex){
            int index = 0;
            if( schedules.size() > 0 )
                foreach( tt; schedules ){
                    if( tt.executionTime > task.executionTime ){
                        break;
                    }
                    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){}
    +/
}