package org.bukkit.craftbukkit.scheduler; import org.apache.commons.lang.UnhandledException; import org.bukkit.plugin.Plugin; import org.bukkit.scheduler.BukkitWorker; import java.util.Iterator; import java.util.LinkedList; import java.util.Map; class CraftAsyncTask extends CraftTask { private final LinkedList<BukkitWorker> workers = new LinkedList<BukkitWorker>(); private final Map<Integer, CraftTask> runners; CraftAsyncTask(final Map<Integer, CraftTask> runners, final Plugin plugin, final Runnable task, final int id, final long delay) { super(plugin, task, id, delay); this.runners = runners; } @Override public boolean isSync() { return false; } @Override public void run() { final Thread thread = Thread.currentThread(); synchronized(workers) { if(getPeriod() == -2) { // Never continue running after cancelled. // Checking this with the lock is important! return; } workers.add( new BukkitWorker() { public Thread getThread() { return thread; } public int getTaskId() { return CraftAsyncTask.this.getTaskId(); } public Plugin getOwner() { return CraftAsyncTask.this.getOwner(); } }); } Throwable thrown = null; try { super.run(); } catch(final Throwable t) { thrown = t; throw new UnhandledException( String.format( "Plugin %s generated an exception while executing task %s", getOwner().getDescription().getFullName(), getTaskId()), thrown); } finally { // Cleanup is important for any async task, otherwise ghost tasks are everywhere synchronized(workers) { try { final Iterator<BukkitWorker> workers = this.workers.iterator(); boolean removed = false; while(workers.hasNext()) { if(workers.next().getThread() == thread) { workers.remove(); removed = true; // Don't throw exception break; } } if(!removed) { throw new IllegalStateException( String.format( "Unable to remove worker %s on task %s for %s", thread.getName(), getTaskId(), getOwner().getDescription().getFullName()), thrown); // We don't want to lose the original exception, if any } } finally { if(getPeriod() < 0 && workers.isEmpty()) { // At this spot, we know we are the final async task being executed! // Because we have the lock, nothing else is running or will run because delay < 0 runners.remove(getTaskId()); } } } } } LinkedList<BukkitWorker> getWorkers() { return workers; } boolean cancel0() { synchronized(workers) { // Synchronizing here prevents race condition for a completing task setPeriod(-2l); if(workers.isEmpty()) { runners.remove(getTaskId()); } } return true; } }