/* * Copyright 2013-2016 Cel Skeggs * * This file is part of the CCRE, the Common Chicken Runtime Engine. * * The CCRE is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * The CCRE is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with the CCRE. If not, see <http://www.gnu.org/licenses/>. */ package ccre.concurrency; import java.io.InterruptedIOException; import ccre.log.Logger; import ccre.util.UniqueIds; import ccre.verifier.FlowPhase; import ccre.verifier.SuppressPhaseWarnings; /** * A nice wrapper for the builtin Thread. Provides a system to prevent execution * of the run outside of the target thread, provides an easy way to name the * thread, provides a builtin handler for throwables, and provides an abstract * method for the body of the thread. * * @author skeggsc */ public abstract class ReporterThread extends Thread { private static final UniqueIds idGen = new UniqueIds(); /** * Has the run method already been called? */ private boolean started = false; /** * A lock for the started boolean. */ private final Object startedLock = new Object(); /** * Create a new ReporterThread. The passed name will have a unique ID * appended to it. * * @param name the name of this type of thread. */ public ReporterThread(String name) { super(name + "-" + idGen.nextId()); } @Override public final void run() throws IllegalStateException { if (this != Thread.currentThread()) { throw new IllegalStateException("Run function of Thread " + getName() + " called directly!"); } synchronized (startedLock) { if (started) { throw new IllegalStateException("Run function of Thread " + getName() + " recalled!"); } started = true; } try { threadBody(); } catch (OutOfMemoryError oom) { System.err.println("OutOfMemory"); Logger.severe("OutOfMemory"); Logger.severe("OutOfMemory", oom); // in case we can } catch (InterruptedIOException ex) { Logger.warning("Interruption (during IO) of Thread " + this.getName(), ex); } catch (InterruptedException ex) { Logger.warning("Interruption of Thread " + this.getName(), ex); } catch (Throwable thr) { Logger.severe("Abrupt termination of Thread " + this.getName(), thr); } } /** * Starts this thread if it is not already running. * * @return true if the thread was started, or false if it was already * running. * @throws IllegalThreadStateException if the thread had died. */ @FlowPhase @SuppressPhaseWarnings // because it occurs at most once per thread, it's // okay to run start() in flow mode public boolean startIfNotAlive() throws IllegalThreadStateException { if (isAlive()) { return false; } start(); return true; } /** * The body of the thread. This will be called when the thread starts. This * is guaranteed to be called precisely once by the ReporterThread. * * @throws Throwable if something goes wrong. this will be caught by the * ReporterThread automatically. */ protected abstract void threadBody() throws Throwable; }