package com.sleepycat.je.utilint; import java.util.Collection; import java.util.HashSet; import java.util.Set; import com.sleepycat.je.DatabaseException; import com.sleepycat.je.DeadlockException; import com.sleepycat.je.dbi.EnvironmentImpl; import de.ovgu.cide.jakutil.*; /** * A daemon thread. */ public abstract class DaemonThread implements DaemonRunner, Runnable { private static final int JOIN_MILLIS=10; private long waitTime; private Object synchronizer=new Object(); private Thread thread; private EnvironmentImpl env; protected String name; protected Set workQueue; protected int nWakeupRequests; private volatile boolean shutdownRequest=false; private volatile boolean paused=false; private boolean running=false; protected DaemonThread(){ } public DaemonThread( long waitTime, String name, EnvironmentImpl env){ init(waitTime,name,env); } protected void init( long waitTime, String name, EnvironmentImpl env){ this.waitTime=waitTime; this.name=name; this.env=env; workQueue=new HashSet(); this.hook856(name,env); } /** * For testing. */ public Thread getThread(){ return thread; } /** * If run is true, starts the thread if not started or unpauses it if * already started; if run is false, pauses the thread if started or does * nothing if not started. */ public void runOrPause( boolean run){ if (run) { paused=false; if (thread != null) { wakeup(); } else { thread=new Thread(this,name); thread.setDaemon(true); thread.start(); } } else { paused=true; } } public void requestShutdown(){ shutdownRequest=true; } /** * Requests shutdown and calls join() to wait for the thread to stop. */ public void shutdown(){ if (thread != null) { shutdownRequest=true; while (thread.isAlive()) { synchronized (synchronizer) { synchronizer.notifyAll(); } try { thread.join(JOIN_MILLIS); } catch ( InterruptedException e) { } } thread=null; } } public String toString(){ StringBuffer sb=new StringBuffer(); sb.append("<DaemonThread name=\"").append(name).append("\"/>"); return sb.toString(); } public void addToQueue( Object o) throws DatabaseException { workQueue.add(o); wakeup(); } public int getQueueSize() throws DatabaseException { int count=workQueue.size(); return count; } public void addToQueueAlreadyLatched( Collection c) throws DatabaseException { workQueue.addAll(c); } public void wakeup(){ if (!paused) { synchronized (synchronizer) { synchronizer.notifyAll(); } } } public void run(){ while (true) { if (shutdownRequest) { break; } try { this.hook858(); boolean nothingToDo=workQueue.size() == 0; this.hook857(); if (nothingToDo) { synchronized (synchronizer) { if (waitTime == 0) { synchronizer.wait(); } else { synchronizer.wait(waitTime); } } } if (shutdownRequest) { break; } if (paused) { synchronized (synchronizer) { synchronizer.wait(); } continue; } int numTries=0; int maxRetries=nDeadlockRetries(); do { try { nWakeupRequests++; running=true; onWakeup(); break; } catch ( DeadlockException e) { } finally { running=false; } numTries++; if (shutdownRequest) { break; } } while (numTries <= maxRetries); if (shutdownRequest) { break; } } catch ( InterruptedException IE) { System.err.println("Shutting down " + this + " due to exception: "+ IE); shutdownRequest=true; } catch ( Exception E) { System.err.println(this + " caught exception: " + E); E.printStackTrace(System.err); if (env.mayNotWrite()) { System.err.println("Exiting"); shutdownRequest=true; } else { System.err.println("Continuing"); } } } } /** * Returns the number of retries to perform when Deadlock Exceptions occur. */ protected int nDeadlockRetries() throws DatabaseException { return 0; } /** * onWakeup is synchronized to ensure that multiple invocations of the * DaemonThread aren't made. isRunnable must be called from within onWakeup * to avoid having the following sequence: Thread A: isRunnable() => true, * Thread B: isRunnable() => true, Thread A: onWakeup() starts Thread B: * waits for monitor on thread to call onWakeup() Thread A: onWakeup() * completes rendering isRunnable() predicate false Thread B: onWakeup() * starts, but isRunnable predicate is now false */ abstract protected void onWakeup() throws DatabaseException ; /** * Returns whether shutdown has been requested. This method should be used * to to terminate daemon loops. */ protected boolean isShutdownRequested(){ return shutdownRequest; } /** * Returns whether the onWakeup method is currently executing. This is only * an approximation and is used to avoid unnecessary wakeups. */ public boolean isRunning(){ return running; } /** * For unit testing. */ public int getNWakeupRequests(){ return nWakeupRequests; } protected void hook856( String name, EnvironmentImpl env){ } protected void hook857() throws InterruptedException, Exception { } protected void hook858() throws InterruptedException, Exception { } }