X-Git-Url: https://feistymeow.org/gitweb/?a=blobdiff_plain;f=kona%2Fsrc%2Forg%2Ffeistymeow%2Fprocess%2Fethread.java;h=e23d7d89d33e0c1dedf940c63d8a1fd58b1f3e0e;hb=ef338226ccf699c8d8a149d9b74e9888175c7099;hp=280a08305dd39d9495022ec86835c3b8155a1e06;hpb=21f089c2c404a27bed5bdaf88a80214cf63f6ee4;p=feisty_meow.git diff --git a/kona/src/org/feistymeow/process/ethread.java b/kona/src/org/feistymeow/process/ethread.java index 280a0830..e23d7d89 100644 --- a/kona/src/org/feistymeow/process/ethread.java +++ b/kona/src/org/feistymeow/process/ethread.java @@ -1,68 +1,161 @@ - package org.feistymeow.process; -//still in progress... -// not compilable yet probably, -// plus missing the timed features of ethread. - 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() - { - c_logger.warn("this class needs to be implemented and used in an example."); - } + /** + * 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; + } }