All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.hsqldb.lib.HsqlTimer Maven / Gradle / Ivy

The newest version!
/* Copyright (c) 2001-2008, The HSQL Development Group
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * Neither the name of the HSQL Development Group nor the names of its
 * contributors may be used to endorse or promote products derived from this
 * software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


package org.hsqldb.lib;

import java.util.Date;

/**
 * Facility to schedule tasks for future execution in a background thread.  

* * Tasks may be scheduled for one-time execution or for repeated execution at * regular intervals, using either fixed rate or fixed delay policy.

* * This class is a JDK 1.1 compatible implementation required by HSQLDB both * because the java.util.Timer class is available only in JDK 1.3+ and because * java.util.Timer starves least recently added tasks under high load and * fixed rate scheduling, especially when the average actual task duration is * greater than the average requested task periodicity.

* * An additional (minor) advantage over java.util.Timer is that this class does * not retain a live background thread during periods when the task queue is * empty. * @author boucherb@users * @version 1.8.0.10 * @since 1.7.2 */ public final class HsqlTimer implements ObjectComparator, ThreadFactory { /** The priority queue for the scheduled tasks. */ protected final TaskQueue taskQueue = new TaskQueue(16, (ObjectComparator) this); /** The inner runnable that executes tasks in the background thread. */ protected final TaskRunner taskRunner = new TaskRunner(); /** The background thread. */ protected Thread taskRunnerThread; /** The factory that procduces the background threads. */ protected final ThreadFactory threadFactory; /** * Whether this timer should disallow all further processing. * * Once set true, stays true forever. */ protected volatile boolean isShutdown; /** * Constructs a new HsqlTimer using the default thread factory * implementation. */ public HsqlTimer() { this(null); } /** * Constructs a new HsqlTimer. * * Uses the specified thread factory implementation. * * @param threadFactory the ThreadFactory used to produce this timer's * background threads. If null, the default implementation supplied * by this class will be used. */ public HsqlTimer(final ThreadFactory threadFactory) { this.threadFactory = (threadFactory == null) ? this : threadFactory; } /** * Required to back the priority queue for scheduled tasks. * * @param a the first Task * @param b the second Task * @return 0 if equal, < 0 if a < b, > 0 if a > b */ public int compare(final Object a, final Object b) { final long awhen = ((Task) (a)).getNextScheduled(); final long bwhen = ((Task) (b)).getNextScheduled(); return (awhen < bwhen) ? -1 : (awhen == bwhen) ? 0 : 1; } /** * Default ThreadFactory implementation.

* * Contructs a new Thread from the designated runnable, sets its * name to "HSQLDB Timer @" + Integer.toHexString(hashCode()), * and sets it as a daemon thread.

* * @param runnable used to construct the new Thread. * @return a new Thread constructed from the designated runnable. */ public Thread newThread(final Runnable runnable) { final Thread thread = new Thread(runnable); thread.setName("HSQLDB Timer @" + Integer.toHexString(hashCode())); thread.setDaemon(true); return thread; } /** * Retrieves the background execution thread.

* * null is returned if there is no such thread.

* * @return the current background thread (may be null) */ public synchronized Thread getThread() { return this.taskRunnerThread; } /** * (Re)starts background processing of the task queue. * * @throws IllegalStateException if this timer is shut down. * @see #shutdown() * @see #shutdownImmediately() */ public synchronized void restart() throws IllegalStateException { if (this.isShutdown) { throw new IllegalStateException("isShutdown==true"); } else if (this.taskRunnerThread == null) { this.taskRunnerThread = this.threadFactory.newThread(this.taskRunner); this.taskRunnerThread.start(); } else { this.taskQueue.unpark(); } } /** * Causes the specified Runnable to be executed once in the background * after the specified delay. * * @param delay in milliseconds * @param runnable the Runnable to execute. * @return opaque reference to the internal task * @throws IllegalArgumentException if runnable is null */ public Object scheduleAfter(final long delay, final Runnable runnable) throws IllegalArgumentException { if (runnable == null) { throw new IllegalArgumentException("runnable == null"); } return this.addTask(now() + delay, runnable, 0, false); } /** * Causes the specified Runnable to be executed once in the background * at the specified time. * * @param date time at which to execute the specified Runnable * @param runnable the Runnable to execute. * @return opaque reference to the internal task * @throws IllegalArgumentException if date or runnable is null */ public Object scheduleAt(final Date date, final Runnable runnable) throws IllegalArgumentException { if (date == null) { throw new IllegalArgumentException("date == null"); } else if (runnable == null) { throw new IllegalArgumentException("runnable == null"); } return this.addTask(date.getTime(), runnable, 0, false); } /** * Causes the specified Runnable to be executed periodically in the * background, starting at the specified time. * * @return opaque reference to the internal task * @param period the cycle period * @param relative if true, fixed rate sheduling else fixed delay scheduling * @param date time at which to execute the specified Runnable * @param runnable the Runnable to execute * @throws IllegalArgumentException if date or runnable is null, or * period is <= 0 */ public Object schedulePeriodicallyAt(final Date date, final long period, final Runnable runnable, final boolean relative) throws IllegalArgumentException { if (date == null) { throw new IllegalArgumentException("date == null"); } else if (period <= 0) { throw new IllegalArgumentException("period <= 0"); } else if (runnable == null) { throw new IllegalArgumentException("runnable == null"); } return addTask(date.getTime(), runnable, period, relative); } /** * Causes the specified Runnable to be executed periodically in the * background, starting after the specified delay. * * @return opaque reference to the internal task * @param period the cycle period * @param relative if true, fixed rate sheduling else fixed delay scheduling * @param delay in milliseconds * @param runnable the Runnable to execute. * @throws IllegalArgumentException if runnable is null or period is <= 0 */ public Object schedulePeriodicallyAfter(final long delay, final long period, final Runnable runnable, final boolean relative) throws IllegalArgumentException { if (period <= 0) { throw new IllegalArgumentException("period <= 0"); } else if (runnable == null) { throw new IllegalArgumentException("runnable == null"); } return addTask(now() + delay, runnable, period, relative); } /** * Shuts down this timer after the current task (if any) completes.

* * After this call, the timer has permanently entered the shutdown state; * attempting to schedule any new task or directly restart this timer will * result in an IllegalStateException.

* */ public synchronized void shutdown() { if (!this.isShutdown) { this.isShutdown = true; this.taskQueue.cancelAllTasks(); } } /** for compatiblity with previous version */ public synchronized void shutDown() { shutdown(); } /** * Shuts down this timer immediately, interrupting the wait state associated * with the current head of the task queue or the wait state internal to * the currently executing task, if any such state is currently in effect. * * After this call, the timer has permanently entered the shutdown state; * attempting to schedule any new task or directly restart this timer will * result in an IllegalStateException.

* * Note: If the integrity of work performed by a scheduled task * may be adversely affected by an unplanned interruption, it is the * responsibility of the task's implementation to deal correctly with the * possibility that this method is called while such work is in progress, * for instance by catching the InterruptedException, completing the work, * and then rethrowing the exception. */ public synchronized void shutdownImmediately() { if (!this.isShutdown) { final Thread runner = this.taskRunnerThread; this.isShutdown = true; if (runner != null && runner.isAlive()) { runner.interrupt(); } this.taskQueue.cancelAllTasks(); } } /** * Causes the task referenced by the supplied argument to be cancelled. * If the referenced task is currently executing, it will continue until * finished but will not be rescheduled. * * @param task a task reference */ public static void cancel(final Object task) { if (task instanceof Task) { ((Task) task).cancel(); } } /** * Retrieves whether the specified argument references a cancelled task. * * @param task a task reference * @return true if referenced task is cancelled */ public static boolean isCancelled(final Object task) { return (task instanceof Task) ? ((Task) task).isCancelled() : true; } /** * Retrieves whether the specified argument references a task scheduled * periodically using fixed rate scheduling. * * @param task a task reference * @return true if the task is scheduled at a fixed rate */ public static boolean isFixedRate(final Object task) { if (task instanceof Task) { final Task ltask = (Task) task; return (ltask.relative && ltask.period > 0); } else { return false; } } /** * Retrieves whether the specified argument references a task scheduled * periodically using fixed delay scheduling. * * @param task a task reference * @return true if the reference is scheduled using a fixed delay */ public static boolean isFixedDelay(final Object task) { if (task instanceof Task) { final Task ltask = (Task) task; return (!ltask.relative && ltask.period > 0); } else { return false; } } /** * Retrieves whether the specified argument references a task scheduled * for periodic execution. * * @param task a task reference * @return true if the task is scheduled for periodic execution */ public static boolean isPeriodic(final Object task) { return (task instanceof Task) ? (((Task) task).period > 0) : false; } /** * Retrieves the last time the referenced task was executed, as a * Date object. If the task has never been executed, null is returned. * * @param task a task reference * @return the last time the referenced task was executed; null if never */ public static Date getLastScheduled(Object task) { if (task instanceof Task) { final Task ltask = (Task) task; final long last = ltask.getLastScheduled(); return (last == 0) ? null : new Date(last); } else { return null; } } /** * Sets the periodicity of the designated task to a new value.

* * If the designated task is cancelled or the new period is identical to the * task's current period, then this invocation has essentially no effect * and the submitted object is returned.

* * Otherwise, if the new period is greater than the designated task's * current period, then a simple assignment occurs and the submittted * object is returned.

* * If neither case holds, then the designated task is cancelled and a new, * equivalent task with the new period is scheduled for immediate first * execution and returned to the caller.

* * @return a task reference, as per the rules stated above. * @param task the task whose periodicity is to be set * @param period the new period */ public static Object setPeriod(final Object task, final long period) { return (task instanceof Task) ? ((Task) task).setPeriod(period) : task; } /** * Retrieves the next time the referenced task is due to be executed, as a * Date object. If the referenced task is cancelled, null is returned. * * @param task a task reference * @return the next time the referenced task is due to be executed */ public static Date getNextScheduled(Object task) { if (task instanceof Task) { final Task ltask = (Task) task; final long next = ltask.isCancelled() ? 0 : ltask.getNextScheduled(); return next == 0 ? null : new Date(next); } else { return null; } } /** * Adds to the task queue a new Task object encapsulating the supplied * Runnable and scheduling arguments. * * @param first the time of the task's first execution * @param runnable the Runnable to execute * @param period the task's periodicity * @param relative if true, use fixed rate else use fixed delay scheduling * @return an opaque reference to the internal task */ protected Task addTask(final long first, final Runnable runnable, final long period, boolean relative) { if (this.isShutdown) { throw new IllegalStateException("shutdown"); } final Task task = new Task(first, runnable, period, relative); // sychronized this.taskQueue.addTask(task); // sychronized this.restart(); return task; } /** Sets the background thread to null. */ protected synchronized void clearThread() { //#ifdef JAVA2FULL try { taskRunnerThread.setContextClassLoader(null); } catch (Throwable t) {} //#endif JAVA2FULL taskRunnerThread = null; } /** * Retrieves the next task to execute, or null if this timer is shutdown, * the current thread is interrupted, or there are no queued tasks. * * @return the next task to execute, or null */ protected Task nextTask() { try { while (!this.isShutdown || Thread.interrupted()) { long now; long next; long wait; Task task; // synchronized to ensure removeTask // applies only to the peeked task, // when the computed wait <= 0 synchronized (this.taskQueue) { task = this.taskQueue.peekTask(); if (task == null) { // queue is empty break; } now = System.currentTimeMillis(); next = task.next; wait = (next - now); if (wait > 0) { // release ownership of taskQueue monitor and await // notification of task addition or cancellation, // at most until the time when the peeked task is // next supposed to execute this.taskQueue.park(wait); continue; // to top of loop } else { this.taskQueue.removeTask(); } } long period = task.period; if (period > 0) { // repeated task if (task.relative) { // using fixed rate shceduling final long late = (now - next); if (late > period) { // ensure that really late tasks don't // completely saturate the head of the // task queue period = 0; /** @todo : is -1, -2 ... fairer? */ } else if (late > 0) { // compensate for scheduling overruns period -= late; } } task.updateSchedule(now, now + period); this.taskQueue.addTask(task); } return task; } } catch (InterruptedException e) { //e.printStackTrace(); } return null; } /** * stats var */ static int nowCount = 0; /** * Convenience method replacing the longer incantation: * System.currentTimeMillis() * * @return System.currentTimeMillis() */ private static long now() { nowCount++; return System.currentTimeMillis(); } /** * The Runnable that the background thread uses to execute * scheduled tasks.

* * Note: Outer class could simply implement Runnable, * but using an inner class protects the public run method * from potential abuse. */ protected class TaskRunner implements Runnable { /** * Runs the next available task in the background thread.

* * When there are no available tasks, the background * thread dies and its instance field is cleared until * tasks once again become available. */ public void run() { try { do { final Task task = HsqlTimer.this.nextTask(); if (task == null) { break; } // PROBLEM: If the runnable throws an exception other // than InterruptedException (which likely stems // naturally from calling shutdownImmediately() // or getThread().interrupt()), this will still // cause the loop to exit, which is to say that // task scheduling will stop until a new task is // added or the timer is restarted directly, even // though there may still be uncancelled tasks // left on the queue. // // TODO: Clarify and establish a contract regarding // the difference between InterruptedException, // RuntimeException and other things, like // UndeclaredThrowableException. // // SOL'N: At present, we simply require each runnable to // understand its part of the implicit contract, // which is to deal with exceptions internally // (not throw them up to the timer), with the // possible exception of InterruptedException. // // If the integrity of work performed by the // runnable may be adversely affected by an // unplanned interruption, the runnable should // deal with this directly, for instance by // catching the InterruptedException, ensuring // that some integrity preserving state is // attained, and then rethrowing the exception. task.runnable.run(); } while (true); } finally { HsqlTimer.this.clearThread(); } } } /** * Encapsulates a Runnable and its scheduling attributes. * * Essentially, a wrapper class used to schedule a Runnable object * for execution by the enclosing HsqlTimer's TaskRunner in a * background thread. */ protected class Task { /** What to run. */ Runnable runnable; /** The periodic interval, or 0 if one-shot. */ long period; /** The time this task was last executed, or 0 if never. */ long last; /** The next time this task is scheduled to execute. */ long next; /** * Whether to silently remove this task instead of running it, * the next time (if ever) it makes its way to the head of the * timer queue. */ boolean cancelled = false; /** Serializes concurrent access to the cancelled field. */ private Object cancel_mutex = new Object(); /** * Scheduling policy flag.

* * When true, scheduling is fixed rate (as opposed to fixed delay), * and schedule updates are calculated relative to when the task was * was last run rather than a fixed delay starting from the current * wall-clock time provided by System.currentTimeMillis().

* * This helps normalize scheduling for tasks that must attempt to * maintain a fixed rate of execution. */ final boolean relative; /** * Constructs a new Task object encapulating the specified Runnable * and scheduling arguments. * * @param first the first time to execute * @param runnable the Runnable to execute * @param period the periodicity of execution * @param relative if true, use fixed rate scheduling else fixed delay */ Task(final long first, final Runnable runnable, final long period, final boolean relative) { this.next = first; this.runnable = runnable; this.period = period; this.relative = relative; } // fixed reported race condition /** Sets this task's cancelled flag true and signals its taskQueue. */ void cancel() { boolean signalCancelled = false; synchronized (cancel_mutex) { if (!cancelled) { cancelled = signalCancelled = true; } } if (signalCancelled) { HsqlTimer.this.taskQueue.signalTaskCancelled(this); } } /** * Retrieves whether this task is cancelled. * * @return true if cancelled, else false */ boolean isCancelled() { synchronized (cancel_mutex) { return cancelled; } } /** * Retrieves the instant in time just before this task was * last executed by the background thread. A value of zero * indicates that this task has never been executed. * * @return the last time this task was executed or zero if never */ synchronized long getLastScheduled() { return last; } /** * Retrieves the time at which this task is next scheduled for * execution. * * @return the time at which this task is next scheduled for * execution */ synchronized long getNextScheduled() { return next; } /** * Updates the last and next scheduled execution times. * * @param last when this task was last executed * @param next when this task is to be next executed */ synchronized void updateSchedule(final long last, final long next) { this.last = last; this.next = next; } /** * Sets the new periodicity of this task in milliseconds.

* * If this task is cancelled or the new period is identical to the * current period, then this invocation has essentailly no effect * and this object is returned.

* * Otherwise, if the new period is greater than the current period, then * a simple field assignment occurs and this object is returned.

* * If none of the previous cases hold, then this task is cancelled and * a new, equivalent task with the new period is scheduled for * immediate first execution and returned to the caller.

* * @param newPeriod the new period * @return a task reference, as per the rules stated above. */ synchronized Object setPeriod(final long newPeriod) { if (this.period == newPeriod || this.isCancelled()) { return this; } else if (newPeriod > this.period) { this.period = newPeriod; return this; } else { this.cancel(); return HsqlTimer.this.addTask(now(), this.runnable, newPeriod, this.relative); } } } /** * Heap-based priority queue. * * Provides extensions to facilitate and simplify implementing * timer functionality. */ protected static class TaskQueue extends HsqlArrayHeap { /** * Constructs a new TaskQueue with the specified initial capacity and * ObjectComparator. * * @param capacity the initial capacity of the queue * @param oc The ObjectComparator this queue uses to maintain its * Heap invariant. */ TaskQueue(final int capacity, final ObjectComparator oc) { super(capacity, oc); } /** * Type-safe add method.

* * Can be used to inject debugging or accounting behaviour.

* * @param task the task to add */ void addTask(final Task task) { // System.out.println("task added: " + task); super.add(task); } /** * Atomically removes all tasks in this queue and then and cancels * them. */ void cancelAllTasks() { Object[] oldHeap; int oldCount; synchronized (this) { oldHeap = this.heap; oldCount = this.count; // 1 instead of 0 to avoid unintended aoob exceptions this.heap = new Object[1]; this.count = 0; } for (int i = 0; i < oldCount; i++) { ((Task) oldHeap[i]).cancelled = true; } } /** * Causes the calling thread to wait until another thread invokes * {@link #unpark() unpark} or the specified amount of time has * elapsed. * * Implements the sync & wait(n) half of this queue's availability * condition.

* * @param timeout the maximum time to wait in milliseconds. * @throws java.lang.InterruptedException if another thread has * interrupted the current thread. The interrupted status of * the current thread is cleared when this exception is thrown. */ synchronized void park(final long timeout) throws InterruptedException { this.wait(timeout); } /** * Retrieves the head of this queue, without removing it.

* * This method has the side-effect of removing tasks from the * head of this queue until a non-cancelled task is encountered * or this queue is empty.

* * If this queue is initially empty or is emptied in the process * of finding the earliest scheduled non-cancelled task, * then null is returned.

* * @return the earliest scheduled non-cancelled task, or null if no such * task exists */ synchronized Task peekTask() { while (super.heap[0] != null && ((Task) super.heap[0]).isCancelled()) { super.remove(); } return (Task) super.heap[0]; } /** * Informs this queue that the given task is supposedly cancelled.

* * If the indicated task is identical to the current head of * this queue, then it is removed and this queue is * {@link #unpark() unparked}.

* * The cancelled status of the given task is not verified; it is * assumed that the caller is well-behaved (always passes a * non-null reference to a cancelled task). * * @param task a supposedly cancelled task */ synchronized void signalTaskCancelled(Task task) { // We only care about the case where HsqlTimer.nextTask // might be parked momentarily on this task. if (task == super.heap[0]) { super.remove(); this.notify(); } } /** * Type-safe remove method.

* * Removes the head task from this queue.

* * Can be used to inject debugging or accounting behaviour.

* * @return this queue's head task or null if no such task exists */ Task removeTask() { // System.out.println("removing task..."); return (Task) super.remove(); } /** * Wakes up a single thread (if any) that is waiting on this queue's * {@link #park(long) park} method. * * Implements the sync & notify half of this queue's availability * condition. */ synchronized void unpark() { this.notify(); } } // ---------------------------------- tests ------------------------------------ // /** // * Computes the system-specific average {@link java.io.FileDescriptor#sync() // * sync} time.

// * // * @param runs iterations to perform when computing the average // * @param buff the data to write before each sync call // * @return the total time to write buff and call sync runs times, // * divided by runs // */ // static double avgSyncTime(int runs, byte[] buff) { // java.io.File file = null; // java.io.FileOutputStream fos; // java.io.FileDescriptor fd; // long start = System.currentTimeMillis(); // // try { // file = java.io.File.createTempFile("SyncTest", ".tmp"); // fos = new java.io.FileOutputStream(file); // fd = fos.getFD(); // // for (int i = 0; i < runs; i++) { // fos.write(buff); // fos.flush(); // fd.sync(); // } // // long elapsed = System.currentTimeMillis() - start; // // return (elapsed/(double)runs); // } catch (Exception e) { // throw new RuntimeException(e); // } finally { // if (file != null) { // file.delete(); // } // } // } // // /** // * WRITE_DELAY simulation task. // * // * Writes a given buffer to disk, sync's the associated file // * descriptor and maintains an account of the average period // * between executions. // */ // static class WriteAndSyncTask extends java.util.TimerTask { // // static // /** Used to make the name of each task unique. */ // static int serial; // /** The data to write. */ // static final byte[] buf = new byte[256]; // // // instance // /** Identifes this task. */ // String name; // /** The time at which this task was last executed. */ // long last; // /** A running sum of the periods between executions. */ // long total; // /** The number of times this task has been executed. */ // int runs; // /** True until this task is the first time. */ // boolean firstTime = true; // /** The file to write. */ // java.io.File file; // /** The FileOutputStream to write. */ // java.io.FileOutputStream fos; // /** The FileDescriptor to sync. */ // java.io.FileDescriptor fd; // // /** Constructs a new WriteAndSyncTask */ // WriteAndSyncTask() { // this.name = "Task." + serial++; // // try { // this.file = java.io.File.createTempFile(name, ".tmp"); // this.fos = new java.io.FileOutputStream(file); // this.fd = fos.getFD(); // } catch(java.io.IOException ioe) { // throw new RuntimeException(ioe); // } // } // // /** // * Runnable implementation.

// * // * Does the average period accounting and // * invokes the writeAndSync method. // */ // public void run() { // final long now = System.currentTimeMillis(); // // if (this.firstTime) { // this.firstTime = false; // } else { // this.total += (now - this.last); // } // // this.last = now; // // writeAndSync(); // // this.runs++; // } // // /** // * Writes a given buffer to disk and syncs the associated file // * descriptor. // */ // void writeAndSync() { // try { // this.fos.write(buf); // this.fos.flush(); // this.fd.sync(); // Thread.sleep(1); // } catch(Exception e) { // e.printStackTrace(); // } // } // // /** // * Closes the FileOutputStream, deletes the file // * and nullifies Object fields. // */ // public void release() { // try { // this.fos.close(); // } catch(Exception e) { // e.printStackTrace(); // } // try { // this.file.delete(); // } catch (Exception e) { // e.printStackTrace(); // } // // this.fos = null; // this.file = null; // this.fd = null; // } // // /** // * Retrieves the computed moment of actual average periodicity // * experienced by this task. // */ // public float getAveragePeriod() { // return (this.runs < 2) ? Float.NaN // : (this.total/(float)(this.runs - 1)); // } // // // /** // * @return the String representation of this task, indicating // * its name, the number of runs so far and the // * computed moment of actual average periodicity // * experienced so far. // */ // public String toString() { // return this.name // + "[" // + "runs: " + runs + ", " // + "actual avg. period: " + getAveragePeriod() // + "]"; // } // } // // static class Stats { // double min; // double max; // double pk; // double sk; // double vk; // long n; // boolean initialized; // boolean sample; // // void addDataPoint(double x) { // // double xi; // double xsi; // long nm1; // // xi = x; // // if (!initialized) { // n = 1; // pk = xi; // sk = xi; // min = xi; // max = xi; // vk = 0.0; // initialized = true; // // return; // } // // n++; // // nm1 = (n - 1); // xsi = (sk - (xi * nm1)); // vk += ((xsi * xsi) / n) / nm1; // sk += xi; // // if (xi != 0) { // pk *= xi; // } // // max = Math.max(max, xi); // min = Math.min(min, xi); // } // // double getMin() { // return initialized ? min : Double.NaN; // } // // double getMax() { // return initialized ? max : Double.NaN; // } // // double getGeometricMean() { // return initialized ? Math.pow(pk, 1/(double)n) : Double.NaN; // } // // double getVariance() { // // if (!initialized) { // return Double.NaN; // } // // return sample ? (n == 1) ? Double.NaN // : (vk / (double) (n - 1)) // : (vk / (double) (n)); // } // // double getStdDev() { // // if (!initialized) { // return Double.NaN; // } // // return sample ? (n == 1) ? Double.NaN // : (Math.sqrt(vk // / (double) (n - 1))) // : (Math.sqrt(vk / (double) (n))); // } // } // // /** // * Runs the HsqlTimer tests. // * @param args Currently unused // */ // public static void main(String[] args) { // // number of tasks to queue // int taskCount = 10; // // period, as a multiple of computed system-specific avg. sync time // double periodMultiplier = 1.4D; // // how long to run the timer, in milliseconds // long duration = 2800; // // test(taskCount, periodMultiplier, duration); // } // // /** // * Runs the HsqlTimer and java.util.Timer tests using the given // * arguments.

// * // * @param taskCount the number of WriteAndSync tasks to add // * @param periodMultiplier the period with with to schedule // * the tasks, as a multiple of the computed, system-specific // * average sync time. // * @param duration The number of milliseconds that the foreground // * Thread should sleep while the specified number of WriteAndSync // * tasks are running in the background thread // */ // public static void test(final int taskCount, // final double periodMultiplier, // final long duration) { // // System.out.println(); // System.out.println("****************************************"); // System.out.println("* org.hsqldb.lib.HsqlTimer tests *"); // System.out.println("****************************************"); // System.out.println(); // // System.out.println("Computing system-specific avg. sync time."); // System.out.println("Please wait..."); // // double avgSyncTime = avgSyncTime(500, new byte[256]); // double minAvgPeriod = (taskCount * avgSyncTime); // long period = Math.round(avgSyncTime * periodMultiplier); // // System.out.println(); // System.out.println("System-specific avg. sync time : " + avgSyncTime + " ms."); // System.out.println("Requested task count : " + taskCount); // System.out.println("Requested task period : " + period + " ms." ); // System.out.println("Min. avg. period (0 starved) : " + minAvgPeriod + " ms." ); // System.out.println("Requested test duration : " + duration + " ms."); // // if (period <= minAvgPeriod || minAvgPeriod >= duration) { // double idealAvgRuns = (duration / minAvgPeriod); // // System.out.println("Idealized avg. runs / task : " + (float)idealAvgRuns); // } else { // double remainingDuration = (duration - minAvgPeriod); // double remainingRuns = (remainingDuration / period); // double idealAvgRuns = (1D + remainingRuns); // // System.out.println("Theoretical first cycle time : " + minAvgPeriod); // System.out.println("Remaining duration : " + remainingDuration); // System.out.println("Remaining runs : " + remainingRuns); // System.out.println("Idealized avg. runs per task : " + idealAvgRuns); // System.out.println("(1 + (requested duration"); // System.out.println(" - theor. first cycle time"); // System.out.println(" ) / requested period)"); // } // // testJavaUtilTimer(taskCount, period, duration); // testHsqlTimer(taskCount, period, duration); // } // // // /** // * Runs the java.util.Timer test using the given arguments.

// * // * @param taskCount the number of WriteAndSync tasks to add // * @param periodMultiplier the period with with to schedule // * the tasks, as a multiple of the computed, system-specific // * average sync time. // * @param duration The number of milliseconds that the foreground // * Thread should sleep while the specified number of WriteAndSync // * tasks are running in the background thread // */ // public static void testJavaUtilTimer(final int taskCount, // final long period, // final long duration) { // // System.out.println(); // System.out.println("****************************************"); // System.out.println("* java.util.Timer *"); // System.out.println("****************************************"); // System.out.println(); // // WriteAndSyncTask.serial = 0; // // final java.util.Timer timer = new java.util.Timer(); // final WriteAndSyncTask[] tasks = new WriteAndSyncTask[taskCount]; // // for (int i = 0; i < taskCount; i++) { // tasks[i] = new WriteAndSyncTask(); // timer.scheduleAtFixedRate(tasks[i], 0, period); // } // // final long start = now(); // // try { // Thread.sleep(duration); // } catch (Exception e) { // e.printStackTrace(); // } // // for (int i = 0; i < tasks.length; i++) { // tasks[i].cancel(); // } // // timer.cancel(); // // final long elapsed = now() - start; // // System.out.println("Actual test duration: " + elapsed + " ms."); // System.out.println(); // // printTaskStats(tasks); // } // // /** // * Runs the HsqlTimer test using the given arguments.

// * // * @param taskCount the number of WriteAndSync tasks to add // * @param periodMultiplier the period with with to schedule // * the tasks, as a multiple of the computed, system-specific // * average sync time. // * @param duration The number of milliseconds that the foreground // * Thread should sleep while the specified number of WriteAndSync // * tasks are running in the background thread // */ // public static void testHsqlTimer(final int taskCount, // final long period, // final long duration) { // // System.out.println(); // System.out.println("****************************************"); // System.out.println("* org.hsqldb.lib.HsqlTimer *"); // System.out.println("****************************************"); // System.out.println(); // // WriteAndSyncTask.serial = 0; // // final HsqlTimer timer = new HsqlTimer(); // final WriteAndSyncTask[] tasks = new WriteAndSyncTask[taskCount]; // final Object[] ttasks = new Object[taskCount]; // // for (int i = 0; i < taskCount; i++) { // tasks[i] = new WriteAndSyncTask(); // ttasks[i] = timer.schedulePeriodicallyAfter(0, period, tasks[i], true); // } // // final long start = now(); // // try { // Thread.sleep(duration); // } catch (Exception e) { // e.printStackTrace(); // } // // final Thread timerThread = timer.getThread(); // // for (int i = 0; i < taskCount; i++) { // timer.cancel(ttasks[i]); // } // // try { // timerThread.join(); // } catch (Exception e) { // e.printStackTrace(); // } // // final long elapsed = now() - start; // // System.out.println("Actual test duration: " + elapsed + " ms."); // System.out.println(); // // printTaskStats(tasks); // // } // // static void printTaskStats(WriteAndSyncTask[] tasks) { // float avgTotal = 0; // int avgCount = 0; // int starved = 0; // int runs = 0; // Stats periodStats = new Stats(); // Stats runStats = new Stats(); // // for (int i = 0; i < tasks.length; i++) { // if (tasks[i].runs > 1) { // double avgPeriod = tasks[i].getAveragePeriod(); // periodStats.addDataPoint(avgPeriod); // avgTotal += avgPeriod; // avgCount++; // } // runs += tasks[i].runs; // if (tasks[i].runs == 0) { // starved++; // } // runStats.addDataPoint(tasks[i].runs); // tasks[i].release(); // } // // float periodAvg = (avgTotal / avgCount); // float periodMax = (float) periodStats.getMax(); // int periodMaxCnt = 0; // float periodMin = (float) periodStats.getMin(); // int periodMinCnt = 0; // float periodRange = (periodMax - periodMin); // float periodStddev = (float)periodStats.getStdDev(); // float periodGMean = (float)periodStats.getGeometricMean(); // float periodStddevR = (periodRange / periodStddev); // // float runsAvg = (runs / (float)tasks.length); // int runsMin = Math.round((float)runStats.getMin()); // int runsMinCnt = 0; // int runsMax = Math.round((float)runStats.getMax()); // int runsMaxCnt = 0; // int runsRange = (runsMax - runsMin); // float runsStddev = (float) runStats.getStdDev(); // float runsGMean = (float) runStats.getGeometricMean(); // float runsStddevR = (runsRange / runsStddev); // // for (int i = 0; i < tasks.length; i++) { // double avgPeriod = tasks[i].getAveragePeriod(); // // if (avgPeriod == periodMin) { // periodMinCnt++; // } // // if (avgPeriod == periodMax) { // periodMaxCnt++; // } // // if (tasks[i].runs == runsMin) { // runsMinCnt++; // } // // if (tasks[i].runs == runsMax) { // runsMaxCnt++; // } // } // // System.out.println("------------------------"); // System.out.println("Starved tasks (runs = 0): " + starved + " (" + ((100*starved)/tasks.length) + "%)"); // System.out.println("------------------------"); // System.out.println("Period :"); // System.out.println("------------------------"); // System.out.println("Average : " + periodAvg); // System.out.println("~Minimum (count/runs) : " + periodMin + " (" + periodMinCnt + "/" + tasks.length + ")"); // System.out.println("~Maximum (count/runs) : " + periodMax + " (" + periodMaxCnt + "/" + tasks.length + ")"); // System.out.println("~Range : " + periodRange); // System.out.println("Geometric mean : " + periodGMean); // System.out.println("Stddev : " + periodStddev); // System.out.println("~Range/Stddev : " + periodStddevR); // System.out.println("------------------------"); // System.out.println("Runs :"); // System.out.println("------------------------"); // System.out.println("Average : " + runsAvg); // System.out.println("Minimum (count/runs) : " + runsMin + " (" + runsMinCnt + "/" + tasks.length + ")"); // System.out.println("Maximum (count/runs) : " + runsMax + " (" + runsMaxCnt + "/" + tasks.length + ")"); // System.out.println("Range : " + runsRange); // System.out.println("Geometric mean : " + runsGMean); // System.out.println("Stddev : " + runsStddev); // System.out.println("Range/Stddev : " + runsStddevR); // } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy