-
package org.feistymeow.process;
-//still in progress...
-// not compilable yet probably,
-// plus missing the timed features of ethread.
-
-
-import java.io.BufferedOutputStream;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* A simple java thread that hearkens back to the HOOPLE C++ ethread in features.
- *
+ *
* @author Chris Koeritz
+ * @copyright Copyright (c) 2010-$now By Feisty Meow Concerns Ltd.
+ * @license This file is free software; you can modify and redistribute it under the terms of the Apache License v2.0:
+ * http://www.apache.org/licenses/LICENSE-2.0
*/
-class ethread implements Runnable
+public abstract class ethread implements Runnable
{
-//fix below to use better way to get class object.
- private static Log c_logger = LogFactory.getLog(ethread.class);
+ private static Log c_logger = LogFactory.getLog(ethread.class);
+
+ // the actual java thread object.
+ private volatile Thread c_RealThread = null;
+ // provides synchronization for the thread.
+ private volatile Object c_lock = new Object();
+ // this is the timing period, for a timed thread. if zero, then this is a single shot thread.
+ private long c_period = 0;
+ // records whether the thread should shut down or not.
+ private boolean c_stopThread = false;
+ // snooze between checks on the stop timer.
+ final long SNOOZE_PERIOD = 20;
+
+ /**
+ * creates a new single-shot ethread without starting it. this type of thread will run just once.
+ */
+ public ethread()
+ {
+ }
+
+ /**
+ * creates a new periodic ethread without starting it. this type of thread runs every "period" milliseconds until stopped or until the
+ * performActivity method returns false.
+ */
+ public ethread(long period)
+ {
+ c_period = period;
+ }
+
+ /**
+ * this is the main function that derived classes must implement. it does the actual work that the thread is intended to perform. note
+ * that the derived version must not do anything to cause the thread to be ripped out while performActivity is still being invoked. the
+ * return value should be true if the thread can continue executing. this is meaningless for single shot threads executed via runOnce, but
+ * matters for the periodic threads started with runPeriodic.
+ */
+ abstract public boolean performActivity();
+
+ /**
+ * Begins execution of the thread.
+ */
+ public void start()
+ {
+ synchronized (c_lock) {
+ if (null == this.c_RealThread) {
+ this.c_RealThread = new Thread(this);
+ c_logger.debug("starting thread " + c_RealThread.getId());
+ this.c_RealThread.start();
+ }
+ }
+ }
+
+ /**
+ * Stops execution of the thread, or at least attempts to.
+ */
+ public void stop()
+ {
+ cancel();
+ while (true) {
+ if (threadAlive()) {
+ try {
+ Thread.sleep(40);
+ } catch (InterruptedException e) {
+ // ignoring this since we'll keep snoozing as needed.
+ }
+ } else {
+ break;
+ }
+ }
+ }
- private volatile Thread c_RealThread = null;
+ /**
+ * Signals the thread to stop executing, but does not wait for it.
+ */
+ void cancel()
+ {
+ synchronized (c_lock) {
+ c_stopThread = true;
+ Thread goAway = c_RealThread;
+ c_RealThread = null;
+ if (null != goAway) {
+ goAway.interrupt();
+ }
+ }
+ }
- // the only variable on which both synchronize is the "thread finished" variable.
- public ethread()
- {
- }
+ /**
+ * Returns true if the thread object is still alive. this does not necessarily mean it is currently active.
+ */
+ public boolean threadAlive()
+ {
+ synchronized (c_lock) {
+ return this.c_RealThread != null;
+ }
+ }
- /**
- * Begins execution of the thread.
- */
- public void start()
- {
- if( null == this.c_RealThread )
- {
- this.c_RealThread = new Thread(this);
- this.c_RealThread.start();
- }
- }
-
- /**
- * Stops execution of the thread, or at least attempts to.
- */
- public void stop()
- {
- Thread goAway = c_RealThread;
- c_RealThread = null;
- if (null != goAway) { goAway.interrupt(); }
- }
-
- /**
- * Returns true if the thread isn't null.
- */
- public boolean threadRunning()
- {
- return (null != this.c_RealThread);
- }
-
- public void run()
- {
- if (false == threadRunning())
- {
- return; // stopped before it ever started
- }
-
- }
+ /**
+ * returns true if the thread has been told to stop running.
+ */
+ public boolean shouldStop()
+ {
+ synchronized (c_lock) {
+ return c_stopThread;
+ }
+ }
+ /**
+ * this is the override from Runnable that allows us to call our own performActivity method. implementors should not override this; they
+ * should override performActivity instead.
+ */
+ @Override
+ public void run()
+ {
+ if (!threadAlive()) {
+ return; // stopped before it ever started. how can this be? we just got invoked.
+ }
+ try {
+ while (true) {
+ boolean keepGoing = performActivity();
+ if (!keepGoing) {
+ c_logger.debug("thread returned false, signifying it wants to exit. now dropping it.");
+ break;
+ }
+ if (c_period == 0) {
+ // not a periodic thread, so we're done now.
+ break;
+ }
+ long nextRun = System.currentTimeMillis() + c_period;
+ while (System.currentTimeMillis() < nextRun) {
+ if (shouldStop()) {
+ break;
+ }
+ try {
+ Thread.sleep(SNOOZE_PERIOD);
+ } catch (InterruptedException e) {
+ // well, we'll hit it again soon.
+ }
+ }
+ }
+ } catch (Throwable t) {
+ c_logger.info("exception thrown from performActivity: " + t.getLocalizedMessage(), t);
+ }
+ // reset the thread held since we're leaving right now.
+ c_stopThread = true;
+ c_RealThread = null;
+ }
}