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

com.cosylab.epics.caj.util.SearchTimer Maven / Gradle / Ivy

Go to download

JCA is an EPICS Channel Access library for Java. For more information concerning EPICS or Channel Access please refer to the <a href="http://www.aps.anl.gov/epics">EPICS Web pages</a> or read the <a href="http://www.aps.anl.gov/epics/base/R3-14/8-docs/CAref.html">Channel Access manual (3.14)</a>. <p>This module also includes CAJ, A 100% pure Java implementation of the EPICS Channel Access library.</p>

There is a newer version: 2.4.10
Show newest version
/*
 * Copyright (c) 2004 by Cosylab
 *
 * The full license specifying the redistribution, modification, usage and other
 * rights and obligations is included with the distribution of this project in
 * the file "LICENSE-CAJ". If the license is not included visit Cosylab web site,
 * .
 *
 * THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY OF ANY KIND, NOT EVEN THE
 * IMPLIED WARRANTY OF MERCHANTABILITY. THE AUTHOR OF THIS SOFTWARE, ASSUMES
 * _NO_ RESPONSIBILITY FOR ANY CONSEQUENCE RESULTING FROM THE USE, MODIFICATION,
 * OR REDISTRIBUTION OF THIS SOFTWARE.
 */

package com.cosylab.epics.caj.util;

/**
 * Timer. Based on EDU.oswego.cs.dl.util.concurrent. Timer tasks
 * should complete quickly. If a timer task takes excessive time to complete, it
 * "hogs" the timer's task execution thread. This can, in turn, delay the
 * execution of subsequent tasks, which may "bunch up" and execute in rapid
 * succession when (and if) the offending task finally completes.
 * 
 * @author Matej Sekoranja
 * @version $id$
 */
public class SearchTimer extends Thread {

	/**
	 * Tasks are maintained in a standard priority queue.
	 **/
	protected final Heap heap = new Heap(64);

	protected final RunLoop runLoop = new RunLoop();

	/**
	 * Protected constructor (singleton pattern).
	 */
	public SearchTimer() {
	}


	public static abstract class TimerTask implements Comparable {
		private long timeToRun; // The time to run command
		private long delay; // The delay

		// Cancellation does not immediately remove node, it just
		// sets up lazy deletion bit, so is thrown away when next
		// encountered in run loop
		private boolean cancelled = false;

		// Access to cancellation status and and run time needs sync
		// since they can be written and read in different threads

		synchronized void setCancelled() {
			cancelled = true;
		}

		synchronized boolean getCancelled() {
			return cancelled;
		}

		// rt = now + d
		synchronized void setTimeToRun(long d, long rt) {
			delay = d;
			timeToRun = rt;
		}

		public synchronized long getTimeToRun() {
			return timeToRun;
		}

		public synchronized long getDelay() {
			return delay;
		}

		public int compareTo(Object other) {
			long a = getTimeToRun();
			long b = ((TimerTask) (other)).getTimeToRun();
			return (a < b) ? -1 : ((a == b) ? 0 : 1);
		}

        /**
         * Method invoked by timer at requested time.
         * 
         * @return delay (in ms) after which to reschedule, not rescheduled if <= 0.
         */
        public abstract long timeout();
    }

	/**
	 * Execute the given command after waiting for the given delay.
	 * 
	 * @param millisecondsToDelay
	 *            -- the number of milliseconds from now to run the command.
	 * @param task
	 *            -- the command to run after the delay.
	 **/
	public synchronized void executeAfterDelay(
			long millisecondsToDelay,
			TimerTask task) {
		long runtime = System.currentTimeMillis() + millisecondsToDelay;
		task.setTimeToRun(millisecondsToDelay, runtime);
		heap.insert(task);
		restart();
	}

	public synchronized void rescheduleAllAfterDelay(long millisecondsToDelay) {
		long timeToRun = System.currentTimeMillis() + millisecondsToDelay;

		synchronized (heap) {
			Object[] nodes = heap.getNodes();
			int count = heap.size();
			for (int i = 0; i < count; i++)
				((TimerTask) nodes[i]).setTimeToRun(millisecondsToDelay, timeToRun);
		}

		restart();
	}

	/**
	 * Cancel a scheduled task that has not yet been run. The task will be
	 * cancelled upon the next opportunity to run it. This has no
	 * effect if this is a one-shot task that has already executed. Also, if an
	 * execution is in progress, it will complete normally. (It may however be
	 * interrupted via getThread().interrupt()). But if it is a periodic task,
	 * future iterations are cancelled.
	 * 
	 * @param taskID
	 *            -- a task reference returned by one of the execute commands
	 * @exception ClassCastException
	 *                if the taskID argument is not of the type returned by an
	 *                execute command.
	 **/
	public static void cancel(Object taskID) {
		((TimerTask) taskID).setCancelled();
	}

	/** The thread used to process commands **/
	protected Thread thread;

	/*
	 * Return the thread being used to process commands, or null if there is no
	 * such thread. You can use this to invoke any special methods on the
	 * thread, for example, to interrupt it.
	 */
	public synchronized Thread getThread() {
		return thread;
	}

	/** set thread to null to indicate termination **/
	protected synchronized void clearThread() {
		thread = null;
	}

	/**
	 * Start (or restart) a thread to process commands, or wake up an existing
	 * thread if one is already running. This method can be invoked if the
	 * background thread crashed due to an unrecoverable exception in an
	 * executed command.
	 **/

	public synchronized void restart() {
		if (thread == null) {
			thread = new Thread(runLoop, this.getClass().getName());
			thread.start();
		} else
			notify();
	}

	/**
	 * Cancel all tasks and interrupt the background thread executing the
	 * current task, if any. A new background thread will be started if new
	 * execution requests are encountered. If the currently executing task does
	 * not respond to interrupts, the current thread may persist, even if a new
	 * thread is started via restart().
	 **/
	public synchronized void shutDown() {
		heap.clear();
		if (thread != null)
			thread.interrupt();
		thread = null;
	}

	/**
	 * Return the next task to execute, or null if thread is interrupted.
	 **/
	protected synchronized TimerTask nextTask(boolean blockAndExtract, long dt) {

		// Note: This code assumes that there is only one run loop thread

		try {
			while (!Thread.interrupted()) {

				// Using peek simplifies dealing with spurious wakeups

				TimerTask task = (TimerTask) (heap.peek());

				if (task == null) {
					if (!blockAndExtract)
						return null;
					wait();
				} else {
					long now = System.currentTimeMillis();
					long when = task.getTimeToRun();

					if ((when - dt) > now) { // false alarm wakeup
						if (!blockAndExtract)
							return null;
						wait(when - now);
					} else {
						if (!blockAndExtract)
							return task;
						
						task = (TimerTask) (heap.extract());

						if (!task.getCancelled()) { // Skip if cancelled by
							return task;
						}
					}
				}
			}
		} catch (InterruptedException ex) {
		} // fall through

		return null; // on interrupt
	}

	/**
	 * Check whether there is a task scheduled in next "dT" ms.
	 * @param dT
	 * @return
	 */
	public boolean hasNext(long dT)
	{
		return (nextTask(false, dT) != null);
	}
	
	/**
	 * The run loop is isolated in its own Runnable class just so that the main
	 * class need not implement Runnable, which would allow others to directly
	 * invoke run, which is not supported.
	 **/

	protected class RunLoop implements Runnable {
		public void run() {
			try {
				for (;;) {
					TimerTask task = nextTask(true, 0);
					if (task != null) {
						long millisecondsToDelay = task.timeout();
						if (millisecondsToDelay > 0) {
							long runtime = System.currentTimeMillis()
									+ millisecondsToDelay;
							task.setTimeToRun(millisecondsToDelay, runtime);
							heap.insert(task);
						}
					} else
						break;
				}
			} finally {
				clearThread();
			}
		}
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy