//----------------------------------------------------------------------------//
// //
// W o r k e r //
// //
//----------------------------------------------------------------------------//
// <editor-fold defaultstate="collapsed" desc="hdr"> //
// Copyright © Hervé Bitteur and others 2000-2013. All rights reserved. //
// This software is released under the GNU General Public License. //
// Goto http://kenai.com/projects/audiveris to report bugs or suggestions. //
//----------------------------------------------------------------------------//
// </editor-fold>
package omr.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Class {@code Worker} is a simple way to delegate processing to a
* worker thread, and synchronize at the end of the work.
*
* This is actually derived from the standard SwingWorker class, with the Swing
* part removed. The worker thread is always assigned the minimum priority.
*
* <p>Usage:<ol>
* <li>To get a concrete Worker, you only have to subclass this abstract class
* with a concrete definition of the construct() method.</li>
* <li>Then use start() to actually start the Worker, and use get() to [wait for
* completion if needed and] retrieve the result of the work if any.</li>
* <li>The interrupt() method allows to interrupt the worker.</li>
* </ol>
*
* <p>Nota: This class is now <b>deprecated</b> in favor of the use of
* {@link omr.util.OmrExecutors#getCachedLowExecutor()} which provides similar
* functionality plus has its lifecycle managed correctly when shutting down the
* application.</p>
*
* @param <T> the type used to convey the result of the work
*
* @author Hervé Bitteur
*/
@Deprecated
public abstract class Worker<T>
{
//~ Static fields/initializers ---------------------------------------------
/** Usual logger utility */
protected static final Logger logger = LoggerFactory.getLogger(
Worker.class);
//~ Instance fields --------------------------------------------------------
/** The work result, accessed only via getValue() and setValue() */
private T value;
/** Intermediate thread holder */
private ThreadVar threadVar;
/** Start time */
private long startTime = System.currentTimeMillis();
//~ Constructors -----------------------------------------------------------
//--------//
// Worker //
//--------//
/**
* Prepare a Worker with a specified stack size
*
* @param stackSize the worker stack size, specified in bytes
*/
public Worker (long stackSize)
{
Runnable doConstruct = new Runnable()
{
@Override
public void run ()
{
try {
setValue(construct());
} finally {
threadVar.clear();
logger.debug(
"{} finished after {} ms",
Worker.this.getClass().getName(),
System.currentTimeMillis() - startTime);
}
}
};
Thread t = new Thread(null, doConstruct, "Worker", stackSize);
t.setPriority(Thread.MIN_PRIORITY);
threadVar = new ThreadVar(t);
logger.debug("{} created", getClass().getName());
}
//--------//
// Worker //
//--------//
/**
* Prepare a thread that will call the {@code construct} method
* and then exit. You need to start() this worker.
* The worker will use the default stakc size.
*/
public Worker ()
{
// Use default stack size
this(0L);
}
//~ Methods ----------------------------------------------------------------
//-----------//
// construct //
//-----------//
/**
* Compute the value to be returned by the {@code get} method.
*
* @return the work result, if any
*/
public abstract T construct ();
//-----//
// get //
//-----//
/**
* Return the value created by the {@code construct} method.
* Returns null if either the constructing thread or the current
* thread was interrupted before a value was produced.
*
* @return the value created by the {@code construct} method
*/
public T get ()
{
while (true) {
Thread t = threadVar.get();
if (t == null) {
return getValue();
}
try {
t.join();
} catch (InterruptedException e) {
Thread.currentThread()
.interrupt(); // propagate
logger.debug("{} interrupted", getClass().getName());
return null;
}
}
}
//-----------//
// interrupt //
//-----------//
/**
* Interrupt the worker thread. Call this method to force the worker to stop
* what it's doing.
*/
public void interrupt ()
{
logger.debug("{} interrupt", getClass().getName());
Thread t = threadVar.get();
if (t != null) {
t.interrupt();
}
threadVar.clear();
}
//-------//
// start //
//-------//
/**
* Start the worker thread.
*/
public void start ()
{
Thread t = threadVar.get();
if (t != null) {
t.start();
logger.debug("{} started", getClass().getName());
}
}
//----------//
// getValue //
//----------//
/**
* Get the value produced by the worker thread, or null if it hasn't been
* constructed yet.
*
* @return the work result
*/
protected synchronized T getValue ()
{
return value;
}
//----------//
// setValue //
//----------//
/**
* Set the value produced by the worker thread
*/
private synchronized void setValue (T value)
{
this.value = value;
}
//~ Inner Classes ----------------------------------------------------------
//-----------//
// ThreadVar //
//-----------//
/**
* Class to maintain reference to current worker thread under separate
* synchronization control.
*/
private static class ThreadVar
{
//~ Instance fields ----------------------------------------------------
private Thread thread;
//~ Constructors -------------------------------------------------------
ThreadVar (Thread t)
{
thread = t;
}
//~ Methods ------------------------------------------------------------
synchronized void clear ()
{
thread = null;
}
synchronized Thread get ()
{
return thread;
}
}
}