javacommons/jvms/cldc_1.1.1/javasrc/java/util/Timer.java
branchRCL_3
changeset 19 04becd199f91
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javacommons/jvms/cldc_1.1.1/javasrc/java/util/Timer.java	Tue Apr 27 16:30:29 2010 +0300
@@ -0,0 +1,681 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/**
+ * Timer implementation adapted to Java ME from Apache Harmony (Open Source Java SE)
+ * Main changes:
+ *  - Java ME finalizer used
+ *  - signature test related changes (API to Java 1.3 level)
+ *      - some methods made private
+ *      - some unsupported method calls commented out
+ *  - exception strings hardcoded instead of properties
+ */
+
+package java.util;
+
+import com.nokia.mj.impl.rt.support.Finalizer;
+
+
+/**
+ * {@code Timer}s are used to schedule jobs for execution in a background process. A
+ * single thread is used for the scheduling and this thread has the option of
+ * being a daemon thread. By calling {@code cancel} you can terminate a
+ * {@code Timer} and its associated thread. All tasks which are scheduled to run after
+ * this point are cancelled. Tasks are executed sequentially but are subject to
+ * the delays from other tasks run methods. If a specific task takes an
+ * excessive amount of time to run it may impact the time at which subsequent
+ * tasks may run.
+ * <p>
+ *
+ * The {@code TimerTask} does not offer any guarantees about the real-time nature of
+ * scheduling tasks as its underlying implementation relies on the
+ * {@code Object.wait(long)} method.
+ * <p>
+ * Multiple threads can share a single {@code Timer} without the need for their own
+ * synchronization.
+ * <p>
+ * A {@code Timer} can be set to schedule tasks either at a fixed rate or
+ * with a fixed period. Fixed-period execution is the default.
+ * <p>
+ * The difference between fixed-rate and fixed-period execution
+ * is the following:  With fixed-rate execution, the start time of each
+ * successive run of the task is scheduled in absolute terms without regard for when the previous
+ * task run actually took place. This can result in a series of bunched-up runs
+ * (one launched immediately after another) if busy resources or other
+ * system delays prevent the {@code Timer} from firing for an extended time.
+ * With fixed-period execution, each successive run of the
+ * task is scheduled relative to the start time of the previous run of the
+ * task, so two runs of the task are never fired closer together in time than
+ * the specified {@code period}.
+ *
+ * @see TimerTask
+ * @see java.lang.Object#wait(long)
+ */
+public class Timer
+{
+
+    private static final class TimerImpl extends Thread
+    {
+
+        private static final class TimerHeap
+        {
+            private int DEFAULT_HEAP_SIZE = 256;
+
+            private TimerTask[] timers = new TimerTask[DEFAULT_HEAP_SIZE];
+
+            private int size = 0;
+
+            private int deletedCancelledNumber = 0;
+
+            public TimerTask minimum()
+            {
+                return timers[0];
+            }
+
+            public boolean isEmpty()
+            {
+                return size == 0;
+            }
+
+            public void insert(TimerTask task)
+            {
+                if (timers.length == size)
+                {
+                    TimerTask[] appendedTimers = new TimerTask[size * 2];
+                    System.arraycopy(timers, 0, appendedTimers, 0, size);
+                    timers = appendedTimers;
+                }
+                timers[size++] = task;
+                upHeap();
+            }
+
+            public void delete(int pos)
+            {
+                // posible to delete any position of the heap
+                if (pos >= 0 && pos < size)
+                {
+                    timers[pos] = timers[--size];
+                    timers[size] = null;
+                    downHeap(pos);
+                }
+            }
+
+            private void upHeap()
+            {
+                int current = size - 1;
+                int parent = (current - 1) / 2;
+
+                while (timers[current].when < timers[parent].when)
+                {
+                    // swap the two
+                    TimerTask tmp = timers[current];
+                    timers[current] = timers[parent];
+                    timers[parent] = tmp;
+
+                    // update pos and current
+                    current = parent;
+                    parent = (current - 1) / 2;
+                }
+            }
+
+            private void downHeap(int pos)
+            {
+                int current = pos;
+                int child = 2 * current + 1;
+
+                while (child < size && size > 0)
+                {
+                    // compare the children if they exist
+                    if (child + 1 < size
+                            && timers[child + 1].when < timers[child].when)
+                    {
+                        child++;
+                    }
+
+                    // compare selected child with parent
+                    if (timers[current].when < timers[child].when)
+                    {
+                        break;
+                    }
+
+                    // swap the two
+                    TimerTask tmp = timers[current];
+                    timers[current] = timers[child];
+                    timers[child] = tmp;
+
+                    // update pos and current
+                    current = child;
+                    child = 2 * current + 1;
+                }
+            }
+
+            public void reset()
+            {
+                timers = new TimerTask[DEFAULT_HEAP_SIZE];
+                size = 0;
+            }
+
+            public void adjustMinimum()
+            {
+                downHeap(0);
+            }
+
+            public void deleteIfCancelled()
+            {
+                for (int i = 0; i < size; i++)
+                {
+                    if (timers[i].cancelled)
+                    {
+                        deletedCancelledNumber++;
+                        delete(i);
+                        // re-try this point
+                        i--;
+                    }
+                }
+            }
+
+            private int getTask(TimerTask task)
+            {
+                for (int i = 0; i < timers.length; i++)
+                {
+                    if (timers[i] == task)
+                    {
+                        return i;
+                    }
+                }
+                return -1;
+            }
+
+        }
+
+        /**
+         * True if the method cancel() of the Timer was called or the !!!stop()
+         * method was invoked
+         */
+        private boolean cancelled;
+
+        /**
+         * True if the Timer has become garbage
+         */
+        private boolean finished;
+
+        /**
+         * Vector consists of scheduled events, sorted according to
+         * {@code when} field of TaskScheduled object.
+         */
+        private TimerHeap tasks = new TimerHeap();
+
+        /**
+         * Starts a new timer.
+         *
+         * @param name thread's name
+         * @param isDaemon daemon thread or not
+         */
+        TimerImpl(String name, boolean isDaemon)
+        {
+            // not supported in Java ME
+            /*
+            this.setName(name);
+            this.setDaemon(isDaemon);
+            */
+            this.start();
+        }
+
+        /**
+         * This method will be launched on separate thread for each Timer
+         * object.
+         */
+        //@Override
+        public void run()
+        {
+            while (true)
+            {
+                TimerTask task;
+                synchronized (this)
+                {
+                    // need to check cancelled inside the synchronized block
+                    if (cancelled)
+                    {
+                        return;
+                    }
+                    if (tasks.isEmpty())
+                    {
+                        if (finished)
+                        {
+                            return;
+                        }
+                        // no tasks scheduled -- sleep until any task appear
+                        try
+                        {
+                            this.wait();
+                        }
+                        catch (InterruptedException e)
+                        {
+                        }
+                        continue;
+                    }
+
+                    long currentTime = System.currentTimeMillis();
+
+                    task = tasks.minimum();
+                    long timeToSleep;
+
+                    synchronized (task.lock)
+                    {
+                        if (task.cancelled)
+                        {
+                            tasks.delete(0);
+                            continue;
+                        }
+
+                        // check the time to sleep for the first task scheduled
+                        timeToSleep = task.when - currentTime;
+                    }
+
+                    if (timeToSleep > 0)
+                    {
+                        // sleep!
+                        try
+                        {
+                            this.wait(timeToSleep);
+                        }
+                        catch (InterruptedException e)
+                        {
+                            // Ignored
+                        }
+                        continue;
+                    }
+
+                    // no sleep is necessary before launching the task
+
+                    synchronized (task.lock)
+                    {
+                        int pos = 0;
+                        if (tasks.minimum().when != task.when)
+                        {
+                            pos = tasks.getTask(task);
+                        }
+                        if (task.cancelled)
+                        {
+                            tasks.delete(tasks.getTask(task));
+                            continue;
+                        }
+
+                        // set time to schedule
+                        task.setScheduledTime(task.when);
+
+                        // remove task from queue
+                        tasks.delete(pos);
+
+                        // set when the next task should be launched
+                        if (task.period >= 0)
+                        {
+                            // this is a repeating task,
+                            if (task.fixedRate)
+                            {
+                                // task is scheduled at fixed rate
+                                task.when = task.when + task.period;
+                            }
+                            else
+                            {
+                                // task is scheduled at fixed delay
+                                task.when = System.currentTimeMillis()
+                                            + task.period;
+                            }
+
+                            // insert this task into queue
+                            insertTask(task);
+                        }
+                        else
+                        {
+                            task.when = 0;
+                        }
+                    }
+                }
+
+                // run the task
+                task.run(); // we want that unhandled exception goes through
+                /*
+                                try {
+                                    task.run();
+                                } catch (Exception e) {
+                                    // Ignored
+                                }
+                */
+            }
+        }
+
+        private void insertTask(TimerTask newTask)
+        {
+            // callers are synchronized
+            tasks.insert(newTask);
+            this.notify();
+        }
+
+        /**
+         * Cancels timer.
+         */
+        public synchronized void cancel()
+        {
+            cancelled = true;
+            tasks.reset();
+            this.notify();
+        }
+
+        public int purge()
+        {
+            if (tasks.isEmpty())
+            {
+                return 0;
+            }
+            // callers are synchronized
+            tasks.deletedCancelledNumber = 0;
+            tasks.deleteIfCancelled();
+            return tasks.deletedCancelledNumber;
+        }
+
+    }
+
+    // Used to finalize thread
+    private Finalizer finalizer = new Finalizer()
+    {
+        public void finalizeImpl()
+        {
+            doFinalize();
+        }
+    };
+    private void doFinalize()
+    {
+        synchronized (impl)
+        {
+            impl.finished = true;
+            impl.notify();
+        }
+    }
+
+    private static long timerId;
+
+    private synchronized static long nextId()
+    {
+        return timerId++;
+    }
+
+    /* This object will be used in synchronization purposes */
+    private final TimerImpl impl;
+
+    /**
+     * Creates a new named {@code Timer} which may be specified to be run as a
+     * daemon thread.
+     *
+     * @param name the name of the {@code Timer}.
+     * @param isDaemon true if {@code Timer}'s thread should be a daemon thread.
+     * @throws NullPointerException is {@code name} is {@code null}
+     */
+    private Timer(String name, boolean isDaemon)
+    {
+        super();
+        if (name == null)
+        {
+            throw new NullPointerException("name is null");
+        }
+        this.impl = new TimerImpl(name, isDaemon);
+    }
+
+    /**
+     * Creates a new named {@code Timer} which does not run as a daemon thread.
+     *
+     * @param name the name of the Timer.
+     * @throws NullPointerException is {@code name} is {@code null}
+     */
+    private Timer(String name)
+    {
+        this(name, false);
+    }
+
+    /**
+     * Creates a new {@code Timer} which may be specified to be run as a daemon thread.
+     *
+     * @param isDaemon {@code true} if the {@code Timer}'s thread should be a daemon thread.
+     */
+    private Timer(boolean isDaemon)
+    {
+        this("Timer-" + Timer.nextId(), isDaemon);
+    }
+
+    /**
+     * Creates a new non-daemon {@code Timer}.
+     */
+    public Timer()
+    {
+        this(false);
+    }
+
+    /**
+     * Cancels the {@code Timer} and removes any scheduled tasks. If there is a
+     * currently running task it is not affected. No more tasks may be scheduled
+     * on this {@code Timer}. Subsequent calls do nothing.
+     */
+    public void cancel()
+    {
+        impl.cancel();
+    }
+
+    /**
+     * Removes all canceled tasks from the task queue. If there are no
+     * other references on the tasks, then after this call they are free
+     * to be garbage collected.
+     *
+     * @return the number of canceled tasks that were removed from the task
+     *         queue.
+     */
+    private int purge()
+    {
+        synchronized (impl)
+        {
+            return impl.purge();
+        }
+    }
+
+    /**
+     * Schedule a task for single execution. If {@code when} is less than the
+     * current time, it will be scheduled to be executed as soon as possible.
+     *
+     * @param task
+     *            the task to schedule.
+     * @param when
+     *            time of execution.
+     * @throws IllegalArgumentException
+     *                if {@code when.getTime() < 0}.
+     * @throws IllegalStateException
+     *                if the {@code Timer} has been canceled, or if the task has been
+     *                scheduled or canceled.
+     */
+    public void schedule(TimerTask task, Date when)
+    {
+        if (when.getTime() < 0)
+        {
+            throw new IllegalArgumentException();
+        }
+        long delay = when.getTime() - System.currentTimeMillis();
+        scheduleImpl(task, delay < 0 ? 0 : delay, -1, false);
+    }
+
+    /**
+     * Schedule a task for single execution after a specified delay.
+     *
+     * @param task
+     *            the task to schedule.
+     * @param delay
+     *            amount of time in milliseconds before execution.
+     * @throws IllegalArgumentException
+     *                if {@code delay < 0}.
+     * @throws IllegalStateException
+     *                if the {@code Timer} has been canceled, or if the task has been
+     *                scheduled or canceled.
+     */
+    public void schedule(TimerTask task, long delay)
+    {
+        if (delay < 0)
+        {
+            throw new IllegalArgumentException();
+        }
+        scheduleImpl(task, delay, -1, false);
+    }
+
+    /**
+     * Schedule a task for repeated fixed-delay execution after a specific delay.
+     *
+     * @param task
+     *            the task to schedule.
+     * @param delay
+     *            amount of time in milliseconds before first execution.
+     * @param period
+     *            amount of time in milliseconds between subsequent executions.
+     * @throws IllegalArgumentException
+     *                if {@code delay < 0} or {@code period < 0}.
+     * @throws IllegalStateException
+     *                if the {@code Timer} has been canceled, or if the task has been
+     *                scheduled or canceled.
+     */
+    public void schedule(TimerTask task, long delay, long period)
+    {
+        if (delay < 0 || period <= 0)
+        {
+            throw new IllegalArgumentException();
+        }
+        scheduleImpl(task, delay, period, false);
+    }
+
+    /**
+     * Schedule a task for repeated fixed-delay execution after a specific time
+     * has been reached.
+     *
+     * @param task
+     *            the task to schedule.
+     * @param when
+     *            time of first execution.
+     * @param period
+     *            amount of time in milliseconds between subsequent executions.
+     * @throws IllegalArgumentException
+     *                if {@code when.getTime() < 0} or {@code period < 0}.
+     * @throws IllegalStateException
+     *                if the {@code Timer} has been canceled, or if the task has been
+     *                scheduled or canceled.
+     */
+    public void schedule(TimerTask task, Date when, long period)
+    {
+        if (period <= 0 || when.getTime() < 0)
+        {
+            throw new IllegalArgumentException();
+        }
+        long delay = when.getTime() - System.currentTimeMillis();
+        scheduleImpl(task, delay < 0 ? 0 : delay, period, false);
+    }
+
+    /**
+     * Schedule a task for repeated fixed-rate execution after a specific delay
+     * has passed.
+     *
+     * @param task
+     *            the task to schedule.
+     * @param delay
+     *            amount of time in milliseconds before first execution.
+     * @param period
+     *            amount of time in milliseconds between subsequent executions.
+     * @throws IllegalArgumentException
+     *                if {@code delay < 0} or {@code period < 0}.
+     * @throws IllegalStateException
+     *                if the {@code Timer} has been canceled, or if the task has been
+     *                scheduled or canceled.
+     */
+    public void scheduleAtFixedRate(TimerTask task, long delay, long period)
+    {
+        if (delay < 0 || period <= 0)
+        {
+            throw new IllegalArgumentException();
+        }
+        scheduleImpl(task, delay, period, true);
+    }
+
+    /**
+     * Schedule a task for repeated fixed-rate execution after a specific time
+     * has been reached.
+     *
+     * @param task
+     *            the task to schedule.
+     * @param when
+     *            time of first execution.
+     * @param period
+     *            amount of time in milliseconds between subsequent executions.
+     * @throws IllegalArgumentException
+     *                if {@code when.getTime() < 0} or {@code period < 0}.
+     * @throws IllegalStateException
+     *                if the {@code Timer} has been canceled, or if the task has been
+     *                scheduled or canceled.
+     */
+    public void scheduleAtFixedRate(TimerTask task, Date when, long period)
+    {
+        if (period <= 0 || when.getTime() < 0)
+        {
+            throw new IllegalArgumentException();
+        }
+        long delay = when.getTime() - System.currentTimeMillis();
+        scheduleImpl(task, delay < 0 ? 0 : delay, period, true);
+    }
+
+    /*
+     * Schedule a task.
+     */
+    private void scheduleImpl(TimerTask task, long delay, long period,
+                              boolean fixed)
+    {
+        synchronized (impl)
+        {
+            if (impl.cancelled)
+            {
+                throw new IllegalStateException("Timer is cancelled");
+            }
+
+            long when = delay + System.currentTimeMillis();
+
+            if (when < 0)
+            {
+                throw new IllegalArgumentException("Illegal delay to start TimerTask");
+            }
+
+            synchronized (task.lock)
+            {
+                if (task.isScheduled())
+                {
+                    throw new IllegalStateException("TimerTask is already scheduled");
+                }
+
+                if (task.cancelled)
+                {
+                    throw new IllegalStateException("TimerTask is cancelled");
+                }
+
+                task.when = when;
+                task.period = period;
+                task.fixedRate = fixed;
+            }
+
+            // insert the newTask into queue
+            impl.insertTask(task);
+        }
+    }
+}