From 932670198f01eab28395bc628962f0e339bd2982 Mon Sep 17 00:00:00 2001 From: Tom Tromey Date: Fri, 9 Feb 2001 23:28:55 +0000 Subject: [PATCH] * java/util/Timer.java: New version from Classpath. From-SVN: r39573 --- libjava/ChangeLog | 4 + libjava/java/util/Timer.java | 921 +++++++++++++++++++---------------- 2 files changed, 496 insertions(+), 429 deletions(-) diff --git a/libjava/ChangeLog b/libjava/ChangeLog index e1002bffdfc..93a23bce13d 100644 --- a/libjava/ChangeLog +++ b/libjava/ChangeLog @@ -1,3 +1,7 @@ +2001-02-09 Tom Tromey + + * java/util/Timer.java: New version from Classpath. + 2001-02-09 Bryce McKinlay * java/lang/Double.java (doubleToRawLongBits): Now native. diff --git a/libjava/java/util/Timer.java b/libjava/java/util/Timer.java index 8c3e993833a..d00c93768cb 100644 --- a/libjava/java/util/Timer.java +++ b/libjava/java/util/Timer.java @@ -1,5 +1,5 @@ /* Timer.java -- Timer that runs TimerTasks at a later time. - Copyright (C) 2000 Free Software Foundation, Inc. + Copyright (C) 2000, 2001 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -7,7 +7,7 @@ GNU Classpath is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. - + GNU Classpath is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU @@ -50,476 +50,539 @@ package java.util; * @since 1.3 * @author Mark Wielaard (mark@klomp.org) */ -public class Timer { - - /** - * Priority Task Queue. - * TimerTasks are kept in a binary heap. - * The scheduler calls sleep() on the queue when it has nothing to do or - * has to wait. A sleeping scheduler can be notified by calling interrupt() - * which is automatically called by the enqueue(), cancel() and - * timerFinalized() methods. - */ - private static final class TaskQueue { - - /** Default size of this queue */ - private final int DEFAULT_SIZE = 32; - - /** Wheter to return null when there is nothing in the queue */ - private boolean nullOnEmpty; - - /** - * The heap containing all the scheduled TimerTasks - * sorted by the TimerTask.scheduled field. - * Null when the stop() method has been called. - */ - private TimerTask heap[]; - - /** - * The actual number of elements in the heap - * Can be less then heap.length. - * Note that heap[0] is used as a sentinel. - */ - private int elements; - - /** - * Creates a TaskQueue of default size without any elements in it. - */ - public TaskQueue() { - heap = new TimerTask[DEFAULT_SIZE]; - elements = 0; - nullOnEmpty = false; - } - - /** - * Adds a TimerTask at the end of the heap. - * Grows the heap if necessary by doubling the heap in size. - */ - private void add(TimerTask task) { - elements++; - if (elements == 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; - } - - /** - * Removes the last element from the heap. - * Shrinks the heap in half if - * elements+DEFAULT_SIZE/2 <= heap.length/4. - */ - 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); - } - } - - /** - * Adds a task to the queue and puts it at the correct place - * in the heap. - */ - public synchronized void enqueue(TimerTask task) { - - // Check if it is legal to add another element - if (heap == null) { - throw new IllegalStateException - ("cannot enqueue when stop() has been called on queue"); - } - - heap[0] = task; // sentinel - add(task); // put the new task at the end - // Now push the task up in the heap until it has reached its place - int child = elements; - int parent = child / 2; - while (heap[parent].scheduled > task.scheduled) { - heap[child] = heap[parent]; - child = parent; - parent = child / 2; - } - // This is the correct place for the new task - heap[child] = task; - heap[0] = null; // clear sentinel - // Maybe sched() is waiting for a new element - this.notify(); - } - - /** - * Returns the top element of the queue. - * Can return null when no task is in the queue. - */ - private TimerTask top() { - if (elements == 0) { - return null; - } else { - return heap[1]; - } - } - - /** - * Returns the top task in the Queue. - * Removes the element from the heap and reorders the heap first. - * Can return null when there is nothing in the queue. - */ - public synchronized TimerTask serve() { - // The task to return - TimerTask task = null; - - while (task == null) { - // Get the next task - task = top(); - - // return null when asked to stop - // or if asked to return null when the queue is empty - if ((heap == null) || (task == null && nullOnEmpty)) { - return null; - } - - // Do we have a task? - if (task != 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 { - this.wait(time); - } catch (InterruptedException _) {} - } - } else { - // wait until a task is added - // or something else happens to the queue - try { - this.wait(); - } catch (InterruptedException _) {} - } - } - - // reconstruct the heap - TimerTask lastTask = heap[elements]; - remove(); - - // drop lastTask at the beginning and move it down the heap - 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; // found the correct place (the parent) - done - - heap[parent] = heap[child]; - parent = child; - child = parent*2; - } - - // this is the correct new place for the lastTask - heap[parent] = lastTask; - - // return the task - return task; - } - - - /** - * When nullOnEmpty is true the serve() method will return null when - * there are no tasks in the queue, otherwise it will wait until - * a new element is added to the queue. It is used to indicate to - * the scheduler that no new tasks will ever be added to the queue. - */ - public synchronized void setNullOnEmpty(boolean nullOnEmpty) { - this.nullOnEmpty = nullOnEmpty; - this.notify(); - } - - /** - * When this method is called the current and all future calls to - * serve() will return null. It is used to indicate to the Scheduler - * that it should stop executing since no more tasks will come. - */ - public synchronized void stop() { - this.heap = null; - this.notify(); - } - - } // TaskQueue +public class Timer +{ + /** + * Priority Task Queue. + * TimerTasks are kept in a binary heap. + * The scheduler calls sleep() on the queue when it has nothing to do or + * has to wait. A sleeping scheduler can be notified by calling interrupt() + * which is automatically called by the enqueue(), cancel() and + * timerFinalized() methods. + */ + private static final class TaskQueue + { + /** Default size of this queue */ + private final int DEFAULT_SIZE = 32; + + /** Wheter to return null when there is nothing in the queue */ + private boolean nullOnEmpty; /** - * The scheduler that executes all the tasks on a particular TaskQueue, - * reschedules any repeating tasks and that waits when no task has to be - * executed immediatly. Stops running when canceled or when the parent - * Timer has been finalized and no more tasks have to be executed. + * The heap containing all the scheduled TimerTasks + * sorted by the TimerTask.scheduled field. + * Null when the stop() method has been called. */ - private static final class Scheduler implements Runnable { - - // The priority queue containing all the TimerTasks. - private TaskQueue queue; - - /** - * Creates a new Scheduler that will schedule the tasks on the - * given TaskQueue. - */ - public Scheduler(TaskQueue queue) { - this.queue = queue; - } - - public void run() { - TimerTask task; - while((task = queue.serve()) != null) { - // If this task has not been canceled - if (task.scheduled >= 0) { - - // Mark execution time - task.lastExecutionTime = task.scheduled; - - // Repeatable task? - if (task.period < 0) { - // Last time this task is executed - task.scheduled = -1; - } - - // Run the task - try { - task.run(); - } catch (Throwable t) {/* ignore all errors */} - } - - // Calculate next time and possibly re-enqueue - if (task.scheduled >= 0) { - if (task.fixed) { - task.scheduled += task.period; - } else { - task.scheduled = task.period + - System.currentTimeMillis(); - } - queue.enqueue(task); - } - } - } - } // Scheduler - - // Number of Timers created. - // Used for creating nice Thread names. - private static int nr = 0; - - // The queue that all the tasks are put in. - // Given to the scheduler - private TaskQueue queue; - - // The Scheduler that does all the real work - private Scheduler scheduler; - - // Used to run the scheduler. - // Also used to checked if the Thread is still running by calling - // thread.isAlive(). Sometimes a Thread is suddenly killed by the system - // (if it belonged to an Applet). - private Thread thread; - - // When cancelled we don't accept any more TimerTasks. - private boolean canceled; - - /** - * Creates a new Timer with a non deamon Thread as Scheduler, with normal - * priority and a default name. - */ - public Timer() { - this(false); - } + private TimerTask heap[]; /** - * Creates a new Timer with a deamon Thread as scheduler if deamon is true, - * with normal priority and a default name. + * The actual number of elements in the heap + * Can be less then heap.length. + * Note that heap[0] is used as a sentinel. */ - public Timer(boolean daemon) { - this(daemon, Thread.NORM_PRIORITY); - } + private int elements; /** - * Creates a new Timer with a deamon Thread as scheduler if deamon is true, - * with the priority given and a default name. + * Creates a TaskQueue of default size without any elements in it. */ - private Timer(boolean daemon, int priority) { - this(daemon, priority, "Timer-" + (++nr)); + public TaskQueue() + { + heap = new TimerTask[DEFAULT_SIZE]; + elements = 0; + nullOnEmpty = false; } /** - * Creates a new Timer with a deamon Thread as scheduler if deamon is true, - * with the priority and name given.E + * Adds a TimerTask at the end of the heap. + * Grows the heap if necessary by doubling the heap in size. */ - private Timer(boolean 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(); + private void add(TimerTask task) + { + elements++; + if (elements == 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; } /** - * Cancels the execution of the scheduler. If a task is executing it will - * normally finish execution, but no other tasks will be executed and no - * more tasks can be scheduled. + * Removes the last element from the heap. + * Shrinks the heap in half if + * elements+DEFAULT_SIZE/2 <= heap.length/4. */ - public void cancel() { - canceled = true; - queue.stop(); + 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; + } } /** - * Schedules the task at Time time, repeating every period - * milliseconds if period is positive and at a fixed rate if fixed is true. - * - * @exception IllegalArgumentException if time is negative - * @exception IllegalStateException if the task was already scheduled or - * canceled or this Timer is canceled or the scheduler thread has died + * Adds a task to the queue and puts it at the correct place + * in the heap. */ - private void schedule(TimerTask task, - long time, - long period, - boolean fixed) { - - if (time < 0) - throw new IllegalArgumentException("negative time"); - - if (task.scheduled == 0 && task.lastExecutionTime == -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 != 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 synchronized void enqueue(TimerTask task) + { + // Check if it is legal to add another element + if (heap == null) + { + throw new IllegalStateException + ("cannot enqueue when stop() has been called on queue"); + } + + heap[0] = task; // sentinel + add(task); // put the new task at the end + // Now push the task up in the heap until it has reached its place + int child = elements; + int parent = child / 2; + while (heap[parent].scheduled > task.scheduled) + { + heap[child] = heap[parent]; + child = parent; + parent = child / 2; + } + // This is the correct place for the new task + heap[child] = task; + heap[0] = null; // clear sentinel + // Maybe sched() is waiting for a new element + this.notify(); } /** - * Schedules the task at the specified data for one time execution. - * - * @exception IllegalArgumentException if date.getTime() is negative - * @exception IllegalStateException if the task was already scheduled or - * canceled or this Timer is canceled or the scheduler thread has died + * Returns the top element of the queue. + * Can return null when no task is in the queue. */ - public void schedule(TimerTask task, Date date) { - long time = date.getTime(); - schedule(task, time, -1, false); + private TimerTask top() + { + if (elements == 0) + { + return null; + } + else + { + return heap[1]; + } } /** - * Schedules the task at the specified date and reschedules the task every - * period milliseconds after the last execution of the task finishes until - * this timer or the task is canceled. - * - * @exception IllegalArgumentException if period or date.getTime() is - * negative - * @exception IllegalStateException if the task was already scheduled or - * canceled or this Timer is canceled or the scheduler thread has died + * Returns the top task in the Queue. + * Removes the element from the heap and reorders the heap first. + * Can return null when there is nothing in the queue. */ - public void schedule(TimerTask task, Date date, long period) { - positivePeriod(period); - long time = date.getTime(); - schedule(task, time, period, false); + public synchronized TimerTask serve() + { + // The task to return + TimerTask task = null; + + while (task == null) + { + // Get the next task + task = top(); + + // return null when asked to stop + // or if asked to return null when the queue is empty + if ((heap == null) || (task == null && nullOnEmpty)) + { + return null; + } + + // Do we have a task? + if (task != 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 + { + this.wait(time); + } + catch (InterruptedException _) + { + } + } + } + else + { + // wait until a task is added + // or something else happens to the queue + try + { + this.wait(); + } + catch (InterruptedException _) + { + } + } + } + + // reconstruct the heap + TimerTask lastTask = heap[elements]; + remove(); + + // drop lastTask at the beginning and move it down the heap + 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; // found the correct place (the parent) - done + + heap[parent] = heap[child]; + parent = child; + child = parent * 2; + } + + // this is the correct new place for the lastTask + heap[parent] = lastTask; + + // return the task + return task; } /** - * Schedules the task after the specified delay milliseconds for one time - * execution. - * - * @exception IllegalArgumentException if delay or - * System.currentTimeMillis + delay is negative - * @exception IllegalStateException if the task was already scheduled or - * canceled or this Timer is canceled or the scheduler thread has died + * When nullOnEmpty is true the serve() method will return null when + * there are no tasks in the queue, otherwise it will wait until + * a new element is added to the queue. It is used to indicate to + * the scheduler that no new tasks will ever be added to the queue. */ - public void schedule(TimerTask task, long delay) { - positiveDelay(delay); - long time = System.currentTimeMillis() + delay; - schedule(task, time, -1, false); + public synchronized void setNullOnEmpty(boolean nullOnEmpty) + { + this.nullOnEmpty = nullOnEmpty; + this.notify(); } /** - * Schedules the task after the delay milliseconds and reschedules the - * task every period milliseconds after the last execution of the task - * finishes until this timer or the task is canceled. - * - * @exception IllegalArgumentException if delay or period is negative - * @exception IllegalStateException if the task was already scheduled or - * canceled or this Timer is canceled or the scheduler thread has died + * When this method is called the current and all future calls to + * serve() will return null. It is used to indicate to the Scheduler + * that it should stop executing since no more tasks will come. */ - public void schedule(TimerTask task, long delay, long period) { - positiveDelay(delay); - positivePeriod(period); - long time = System.currentTimeMillis() + delay; - schedule(task, time, period, false); + public synchronized void stop() + { + this.heap = null; + this.notify(); } - /** - * Schedules the task at the specified date and reschedules the task at a - * fixed rate every period milliseconds until this timer or the task is - * canceled. - * - * @exception IllegalArgumentException if period or date.getTime() is - * negative - * @exception IllegalStateException if the task was already scheduled or - * canceled or this Timer is canceled or the scheduler thread has died - */ - public void scheduleAtFixedRate(TimerTask task, Date date, long period) { - positivePeriod(period); - long time = date.getTime(); - schedule(task, time, period, true); - } + } // TaskQueue + + /** + * The scheduler that executes all the tasks on a particular TaskQueue, + * reschedules any repeating tasks and that waits when no task has to be + * executed immediatly. Stops running when canceled or when the parent + * Timer has been finalized and no more tasks have to be executed. + */ + private static final class Scheduler implements Runnable + { + // The priority queue containing all the TimerTasks. + private TaskQueue queue; /** - * Schedules the task after the delay milliseconds and reschedules the task - * at a fixed rate every period milliseconds until this timer or the task - * is canceled. - * - * @exception IllegalArgumentException if delay or - * System.currentTimeMillis + delay is negative - * @exception IllegalStateException if the task was already scheduled or - * canceled or this Timer is canceled or the scheduler thread has died + * Creates a new Scheduler that will schedule the tasks on the + * given TaskQueue. */ - public void scheduleAtFixedRate(TimerTask task, long delay, long period) { - positiveDelay(delay); - positivePeriod(period); - long time = System.currentTimeMillis() + delay; - schedule(task, time, period, true); + public Scheduler(TaskQueue queue) + { + this.queue = queue; } - /** - * Tells the scheduler that the Timer task died - * so there will be no more new tasks scheduled. - */ - protected void finalize() { - queue.setNullOnEmpty(true); + public void run() + { + TimerTask task; + while ((task = queue.serve()) != null) + { + // If this task has not been canceled + if (task.scheduled >= 0) + { + + // Mark execution time + task.lastExecutionTime = task.scheduled; + + // Repeatable task? + if (task.period < 0) + { + // Last time this task is executed + task.scheduled = -1; + } + + // Run the task + try + { + task.run(); + } + catch (Throwable t) + { + /* ignore all errors */ + } + } + + // Calculate next time and possibly re-enqueue + if (task.scheduled >= 0) + { + if (task.fixed) + { + task.scheduled += task.period; + } + else + { + task.scheduled = task.period + System.currentTimeMillis(); + } + queue.enqueue(task); + } + } } + } // Scheduler + + // Number of Timers created. + // Used for creating nice Thread names. + private static int nr = 0; + + // The queue that all the tasks are put in. + // Given to the scheduler + private TaskQueue queue; + + // The Scheduler that does all the real work + private Scheduler scheduler; + + // Used to run the scheduler. + // Also used to checked if the Thread is still running by calling + // thread.isAlive(). Sometimes a Thread is suddenly killed by the system + // (if it belonged to an Applet). + private Thread thread; + + // When cancelled we don't accept any more TimerTasks. + private boolean canceled; + + /** + * Creates a new Timer with a non deamon Thread as Scheduler, with normal + * priority and a default name. + */ + public Timer() + { + this(false); + } + + /** + * Creates a new Timer with a deamon Thread as scheduler if deamon is true, + * with normal priority and a default name. + */ + public Timer(boolean daemon) + { + this(daemon, Thread.NORM_PRIORITY); + } + + /** + * Creates a new Timer with a deamon Thread as scheduler if deamon is true, + * with the priority given and a default name. + */ + private Timer(boolean daemon, int priority) + { + this(daemon, priority, "Timer-" + (++nr)); + } + + /** + * Creates a new Timer with a deamon Thread as scheduler if deamon is true, + * with the priority and name given.E + */ + private Timer(boolean 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(); + } + + /** + * Cancels the execution of the scheduler. If a task is executing it will + * normally finish execution, but no other tasks will be executed and no + * more tasks can be scheduled. + */ + public void cancel() + { + canceled = true; + queue.stop(); + } + + /** + * Schedules the task at Time time, repeating every period + * milliseconds if period is positive and at a fixed rate if fixed is true. + * + * @exception IllegalArgumentException if time is negative + * @exception IllegalStateException if the task was already scheduled or + * canceled or this Timer is canceled or the scheduler thread has died + */ + private void schedule(TimerTask task, long time, long period, boolean fixed) + { + if (time < 0) + throw new IllegalArgumentException("negative time"); + + if (task.scheduled == 0 && task.lastExecutionTime == -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 != 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"); + } + } + + /** + * Schedules the task at the specified data for one time execution. + * + * @exception IllegalArgumentException if date.getTime() is negative + * @exception IllegalStateException if the task was already scheduled or + * canceled or this Timer is canceled or the scheduler thread has died + */ + public void schedule(TimerTask task, Date date) + { + long time = date.getTime(); + schedule(task, time, -1, false); + } + + /** + * Schedules the task at the specified date and reschedules the task every + * period milliseconds after the last execution of the task finishes until + * this timer or the task is canceled. + * + * @exception IllegalArgumentException if period or date.getTime() is + * negative + * @exception IllegalStateException if the task was already scheduled or + * canceled or this Timer is canceled or the scheduler thread has died + */ + public void schedule(TimerTask task, Date date, long period) + { + positivePeriod(period); + long time = date.getTime(); + schedule(task, time, period, false); + } + + /** + * Schedules the task after the specified delay milliseconds for one time + * execution. + * + * @exception IllegalArgumentException if delay or + * System.currentTimeMillis + delay is negative + * @exception IllegalStateException if the task was already scheduled or + * canceled or this Timer is canceled or the scheduler thread has died + */ + public void schedule(TimerTask task, long delay) + { + positiveDelay(delay); + long time = System.currentTimeMillis() + delay; + schedule(task, time, -1, false); + } + + /** + * Schedules the task after the delay milliseconds and reschedules the + * task every period milliseconds after the last execution of the task + * finishes until this timer or the task is canceled. + * + * @exception IllegalArgumentException if delay or period is negative + * @exception IllegalStateException if the task was already scheduled or + * canceled or this Timer is canceled or the scheduler thread has died + */ + public void schedule(TimerTask task, long delay, long period) + { + positiveDelay(delay); + positivePeriod(period); + long time = System.currentTimeMillis() + delay; + schedule(task, time, period, false); + } + + /** + * Schedules the task at the specified date and reschedules the task at a + * fixed rate every period milliseconds until this timer or the task is + * canceled. + * + * @exception IllegalArgumentException if period or date.getTime() is + * negative + * @exception IllegalStateException if the task was already scheduled or + * canceled or this Timer is canceled or the scheduler thread has died + */ + public void scheduleAtFixedRate(TimerTask task, Date date, long period) + { + positivePeriod(period); + long time = date.getTime(); + schedule(task, time, period, true); + } + + /** + * Schedules the task after the delay milliseconds and reschedules the task + * at a fixed rate every period milliseconds until this timer or the task + * is canceled. + * + * @exception IllegalArgumentException if delay or + * System.currentTimeMillis + delay is negative + * @exception IllegalStateException if the task was already scheduled or + * canceled or this Timer is canceled or the scheduler thread has died + */ + public void scheduleAtFixedRate(TimerTask task, long delay, long period) + { + positiveDelay(delay); + positivePeriod(period); + long time = System.currentTimeMillis() + delay; + schedule(task, time, period, true); + } + + /** + * Tells the scheduler that the Timer task died + * so there will be no more new tasks scheduled. + */ + protected void finalize() + { + queue.setNullOnEmpty(true); + } } -- 2.30.2