view base/src/java/util/Timer.d @ 120:536e43f63c81

Comprehensive update for Win32/Linux32 dmd-2.053/dmd-1.068+Tango-r5661 ===D2=== * added [Try]Immutable/Const/Shared templates to work with differenses in D1/D2 instead of version statements used these templates to work with strict type storage rules of dmd-2.053 * com.ibm.icu now also compilable with D2, but not tested yet * small fixes Snippet288 - shared data is in TLS ===Phobos=== * fixed critical bugs in Phobos implemention completely incorrect segfault prone fromStringz (Linux's port ruthless killer) terrible, incorrect StringBuffer realization (StyledText killer) * fixed small bugs as well Snippet72 - misprint in the snippet * implemented missed functionality for Phobos ByteArrayOutputStream implemented (image loading available) formatting correctly works for all DWT's cases As a result, folowing snippets now works with Phobos (Snippet### - what is fixed): Snippet24, 42, 111, 115, 130, 235, 276 - bad string formatting Snippet48, 282 - crash on image loading Snippet163, 189, 211, 213, 217, 218, 222 - crash on copy/cut in StyledText Snippet244 - hang-up ===Tango=== * few changes for the latest Tango trunc-r5661 * few small performance improvments ===General=== * implMissing-s for only one version changed to implMissingInTango/InPhobos * incorrect calls to Format in toString-s fixed * fixed loading \uXXXX characters in ResourceBundle * added good UTF-8 support for StyledText, TextLayout (Win32) and friends UTF functions revised and tested. It is now in java.nonstandard.*Utf modules StyledText and TextLayout (Win32) modules revised for UTF-8 support * removed small diferences in most identical files in *.swt.* folders *.swt.internal.image, *.swt.events and *.swt.custom are identical in Win32/Linux32 now 179 of 576 (~31%) files in *.swt.* folders are fully identical * Win32: snippets now have right subsystem, pretty icons and native system style controls * small fixes in snippets Snippet44 - it's not Snippet44 Snippet212 - functions work with different images and offsets arrays Win32: Snippet282 - crash on close if the button has an image Snippet293 - setGrayed is commented and others Win32: As a result, folowing snippets now works Snippet68 - color doesn't change Snippet163, 189, 211, 213, 217, 218, 222 - UTF-8 issues (see above) Snippet193 - no tabel headers
author Denis Shelomovskij <verylonglogin.reg@gmail.com>
date Sat, 09 Jul 2011 15:50:20 +0300
parents 1bf55a6eb092
children
line wrap: on
line source

module java.util.Timer;

import java.lang.all;
import java.util.TimerTask;
import java.lang.Thread;

version(Tango){
    import tango.core.sync.Mutex;
    import tango.core.sync.Condition;
    import tango.text.convert.Format;
} else { // Phobos
}


class Timer {
    private static final class TaskQueue {
        version(Tango){
            private Mutex mutex;
            private Condition cond;
        } else { // Phobos
        }


        private static const int DEFAULT_SIZE = 32;
        private bool nullOnEmpty;
        private TimerTask heap[];
        private int elements;
        public this() {
            version(Tango){
                mutex = new Mutex();
                cond = new Condition( mutex );
                heap = new TimerTask[DEFAULT_SIZE];
                elements = 0;
                nullOnEmpty = false;
            } else { // Phobos
                implMissingInPhobos();
            }
        }

        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) {
            version(Tango){
                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();
                }
            } else { // Phobos
                implMissingInPhobos();
            }
        }

        private TimerTask top() {
            if (elements is 0) {
                return null;
            }
            else {
                return heap[1];
            }
        }

        public TimerTask serve() {
            version(Tango){
                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;
                }
            } else { // Phobos
                implMissingInPhobos();
                return null;
            }
        }

        public void setNullOnEmpty(bool nullOnEmpty) {
            version(Tango){
                synchronized( mutex ){
                    this.nullOnEmpty = nullOnEmpty;
                    cond.notify();
                }
            } else { // Phobos
                implMissingInPhobos();
            }
        }

        public void stop() {
            version(Tango){
                synchronized( mutex ){
                    this.heap = null;
                    this.elements = 0;
                    cond.notify();
                }
            } else { // Phobos
                implMissingInPhobos();
            }
        }

    }

    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 Thread thread;
    private bool canceled;

    public this() {
        this(false);
    }

    public this(bool daemon) {
        this(daemon, Thread.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 Thread(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 Thread 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 Thread( &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 );
        version(TANGOSVN){
            task.executionTime = Clock.now + TimeSpan.fromMillis(delay);
        } else {
            task.executionTime = Clock.now + TimeSpan.millis(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){}
    +/
}