package electrosphere.engine.threads; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import electrosphere.engine.Globals; import electrosphere.engine.loadingthreads.LoadingThread; import electrosphere.engine.threads.LabeledThread.ThreadLabel; import electrosphere.util.CodeUtils; /** * Manages all running threads */ public class ThreadManager { //Threadsafes the manager Semaphore threadLock = new Semaphore(1); //All threads that are actively running private List activeThreads = new LinkedList(); //All loading threads that are actively running private List loadingThreads = new LinkedList(); //Used by main thread to alert other threads whether they should keep running or not private boolean shouldKeepRunning = true; /** * Updates what threads are being tracked */ public void update(){ threadLock.acquireUninterruptibly(); // //remove loading threads List threadsToRemove = new LinkedList(); for(LoadingThread thread : loadingThreads){ if(thread.isDone()){ threadsToRemove.add(thread); } } for(Thread thread : threadsToRemove){ loadingThreads.remove(thread); } threadLock.release(); } /** * Starts a new thread with tracking * @param thread The thread to start */ public void start(ThreadLabel label, Thread thread){ threadLock.acquireUninterruptibly(); activeThreads.add(new LabeledThread(label, thread)); thread.start(); threadLock.release(); } /** * Starts a new loading thread with tracking * @param thread The loading thread to start */ public void start(LoadingThread thread){ threadLock.acquireUninterruptibly(); activeThreads.add(new LabeledThread(ThreadLabel.LOADING, thread)); loadingThreads.add(thread); thread.start(); threadLock.release(); } /** * Checks if any loading threads are active * @return true if there is an active loading thread, false otherwise */ public boolean isLoading(){ return loadingThreads.size() > 0; } /** * Gets the list of loading threads * @return The list of loading threads */ public List getLoadingThreads(){ return Collections.unmodifiableList(loadingThreads); } /** * Tries to close all threads */ public void close(){ this.shouldKeepRunning = false; threadLock.acquireUninterruptibly(); //for some reason, server must be explicitly closed if(Globals.server != null){ Globals.server.close(); } // //interrupt all threads for(int i = 0; i < 3; i++){ for(LabeledThread thread : activeThreads){ thread.getThread().interrupt(); try { thread.getThread().join(10); if(thread.getThread().isAlive()){ String errorMessage = "Failed to interrupt thread! " + thread.getLabel(); System.err.println(errorMessage); throw new IllegalStateException(); } } catch (InterruptedException e) { CodeUtils.todo(e, "Think about how to handle this"); } } try { TimeUnit.MILLISECONDS.sleep(3); } catch (InterruptedException e) { CodeUtils.todo(e, "Handle failing to sleep while interrupting all other threads"); } } threadLock.release(); } /** * Checks if the thread should keep running or not * @return true if should keep running, false otherwise */ public boolean shouldKeepRunning(){ return this.shouldKeepRunning; } /** * Interrupts all thread under a label * @param label The label */ public void interruptLabel(ThreadLabel label){ threadLock.acquireUninterruptibly(); // //interrupt threads for(LabeledThread thread : activeThreads){ if(thread.getLabel() == label){ thread.getThread().interrupt(); } } threadLock.release(); } }