Archiviert
13
0

Improved the Scheduler.

Adds nag message when async tasks are not properly shut down and adds a limiter for sync tasks. Once they use 35ms in a single tick, any remaining tasks are not executed until later ticks. Adds a method to report the pending tasks and one to report active worker threads
Dieser Commit ist enthalten in:
Raphfrk 2011-04-28 20:10:39 +01:00 committet von EvilSeph
Ursprung aa70240917
Commit fdb077e814
4 geänderte Dateien mit 182 neuen und 41 gelöschten Zeilen

Datei anzeigen

@ -21,6 +21,7 @@ import java.util.Map;
import java.util.Random; import java.util.Random;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import java.lang.InterruptedException;
import jline.ConsoleReader; import jline.ConsoleReader;
import net.minecraft.server.ChunkCoordinates; import net.minecraft.server.ChunkCoordinates;
import net.minecraft.server.ConvertProgressUpdater; import net.minecraft.server.ConvertProgressUpdater;
@ -45,6 +46,8 @@ import org.bukkit.craftbukkit.inventory.CraftFurnaceRecipe;
import org.bukkit.craftbukkit.inventory.CraftRecipe; import org.bukkit.craftbukkit.inventory.CraftRecipe;
import org.bukkit.craftbukkit.inventory.CraftShapedRecipe; import org.bukkit.craftbukkit.inventory.CraftShapedRecipe;
import org.bukkit.craftbukkit.inventory.CraftShapelessRecipe; import org.bukkit.craftbukkit.inventory.CraftShapelessRecipe;
import org.bukkit.scheduler.BukkitWorker;
import org.bukkit.scheduler.BukkitTask;
import org.bukkit.craftbukkit.scheduler.CraftScheduler; import org.bukkit.craftbukkit.scheduler.CraftScheduler;
import org.bukkit.util.config.Configuration; import org.bukkit.util.config.Configuration;
@ -292,6 +295,32 @@ public final class CraftServer implements Server {
pluginManager.clearPlugins(); pluginManager.clearPlugins();
commandMap.clearCommands(); commandMap.clearCommands();
int pollCount = 0;
// Wait for at most 2.5 seconds for plugins to close their threads
while(pollCount < 50 && getScheduler().getActiveWorkers().size() > 0) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
}
pollCount++;
}
List<BukkitWorker> overdueWorkers = getScheduler().getActiveWorkers();
for(BukkitWorker worker : overdueWorkers) {
Plugin plugin = worker.getOwner();
String author = "<NoAuthorGiven>";
if (plugin.getDescription().getAuthors().size() > 0) {
author = plugin.getDescription().getAuthors().get(0);
}
getLogger().log(Level.SEVERE, String.format(
"Nag author: '%s' of '%s' about the following: %s",
author,
plugin.getDescription().getName(),
"This plugin is not properly shutting down its async tasks when it is being reloaded. This may cause conflicts with the newly loaded version of the plugin"
));
}
loadPlugins(); loadPlugins();
} }

Datei anzeigen

@ -3,6 +3,8 @@ package org.bukkit.craftbukkit.scheduler;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.TreeMap; import java.util.TreeMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.List;
import java.util.ArrayList;
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
@ -12,6 +14,9 @@ import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import org.bukkit.scheduler.BukkitScheduler; import org.bukkit.scheduler.BukkitScheduler;
import org.bukkit.scheduler.BukkitWorker;
import org.bukkit.scheduler.BukkitTask;
import org.bukkit.plugin.Plugin; import org.bukkit.plugin.Plugin;
import org.bukkit.craftbukkit.CraftServer; import org.bukkit.craftbukkit.CraftServer;
@ -35,6 +40,7 @@ public class CraftScheduler implements BukkitScheduler, Runnable {
// This lock locks the mainThreadQueue and the currentTick value // This lock locks the mainThreadQueue and the currentTick value
private final Lock mainThreadLock = new ReentrantLock(); private final Lock mainThreadLock = new ReentrantLock();
private final Lock syncedTasksLock = new ReentrantLock();
public void run() { public void run() {
@ -113,28 +119,33 @@ public class CraftScheduler implements BukkitScheduler, Runnable {
// If the main thread cannot obtain the lock, it doesn't wait // If the main thread cannot obtain the lock, it doesn't wait
public void mainThreadHeartbeat(long currentTick) { public void mainThreadHeartbeat(long currentTick) {
if (mainThreadLock.tryLock()) { if (syncedTasksLock.tryLock()) {
try { try {
this.currentTick = currentTick; if (mainThreadLock.tryLock()) {
while (!mainThreadQueue.isEmpty()) { try {
syncedTasks.addLast(mainThreadQueue.removeFirst()); this.currentTick = currentTick;
} while (!mainThreadQueue.isEmpty()) {
} finally { syncedTasks.addLast(mainThreadQueue.removeFirst());
mainThreadLock.unlock(); }
} } finally {
while(!syncedTasks.isEmpty()) { mainThreadLock.unlock();
CraftTask task = syncedTasks.removeFirst();
try {
task.getTask().run();
} catch (Throwable t) {
// Bad plugin!
logger.log(Level.WARNING,
"Task of '" + task.getOwner().getDescription().getName()
+ "' generated an exception", t);
synchronized (schedulerQueue) {
schedulerQueue.remove(task);
} }
} }
long breakTime = System.currentTimeMillis() + 35; // max time spent in loop = 35ms
while (!syncedTasks.isEmpty() && System.currentTimeMillis() <= breakTime) {
CraftTask task = syncedTasks.removeFirst();
try {
task.getTask().run();
} catch (Throwable t) {
// Bad plugin!
logger.log(Level.WARNING, "Task of '" + task.getOwner().getDescription().getName() + "' generated an exception", t);
synchronized (schedulerQueue) {
schedulerQueue.remove(task);
}
}
}
} finally {
syncedTasksLock.unlock();
} }
} }
} }
@ -159,6 +170,24 @@ public class CraftScheduler implements BukkitScheduler, Runnable {
} }
} }
void wipeSyncedTasks() {
syncedTasksLock.lock();
try {
syncedTasks.clear();
} finally {
syncedTasksLock.unlock();
}
}
void wipeMainThreadQueue() {
mainThreadLock.lock();
try {
mainThreadQueue.clear();
} finally {
mainThreadLock.unlock();
}
}
public int scheduleSyncDelayedTask(Plugin plugin, Runnable task, long delay) { public int scheduleSyncDelayedTask(Plugin plugin, Runnable task, long delay) {
return scheduleSyncRepeatingTask(plugin, task, delay, -1); return scheduleSyncRepeatingTask(plugin, task, delay, -1);
} }
@ -168,10 +197,16 @@ public class CraftScheduler implements BukkitScheduler, Runnable {
} }
public int scheduleSyncRepeatingTask(Plugin plugin, Runnable task, long delay, long period) { public int scheduleSyncRepeatingTask(Plugin plugin, Runnable task, long delay, long period) {
if (plugin == null) throw new IllegalArgumentException("Plugin can not null"); if (plugin == null) {
if (task == null) throw new IllegalArgumentException("Task can not null"); throw new IllegalArgumentException("Plugin cannot be null");
if (delay < 0) throw new IllegalArgumentException("Delay cannot be less than 0"); }
if (task == null) {
throw new IllegalArgumentException("Task cannot be null");
}
if (delay < 0) {
throw new IllegalArgumentException("Delay cannot be less than 0");
}
CraftTask newTask = new CraftTask(plugin, task, true, getCurrentTick()+delay, period); CraftTask newTask = new CraftTask(plugin, task, true, getCurrentTick()+delay, period);
synchronized (schedulerQueue) { synchronized (schedulerQueue) {
schedulerQueue.put(newTask, true); schedulerQueue.put(newTask, true);
@ -189,10 +224,16 @@ public class CraftScheduler implements BukkitScheduler, Runnable {
} }
public int scheduleAsyncRepeatingTask(Plugin plugin, Runnable task, long delay, long period) { public int scheduleAsyncRepeatingTask(Plugin plugin, Runnable task, long delay, long period) {
if (plugin == null) throw new IllegalArgumentException("Plugin can not null"); if (plugin == null) {
if (task == null) throw new IllegalArgumentException("Task can not null"); throw new IllegalArgumentException("Plugin cannot be null");
if (delay < 0) throw new IllegalArgumentException("Delay cannot be less than 0"); }
if (task == null) {
throw new IllegalArgumentException("Task cannot be null");
}
if (delay < 0) {
throw new IllegalArgumentException("Delay cannot be less than 0");
}
CraftTask newTask = new CraftTask(plugin, task, false, getCurrentTick()+delay, period); CraftTask newTask = new CraftTask(plugin, task, false, getCurrentTick()+delay, period);
synchronized (schedulerQueue) { synchronized (schedulerQueue) {
schedulerQueue.put(newTask, false); schedulerQueue.put(newTask, false);
@ -203,7 +244,7 @@ public class CraftScheduler implements BukkitScheduler, Runnable {
public <T> Future<T> callSyncMethod(Plugin plugin, Callable<T> task) { public <T> Future<T> callSyncMethod(Plugin plugin, Callable<T> task) {
CraftFuture<T> craftFuture = new CraftFuture<T>(this, task); CraftFuture<T> craftFuture = new CraftFuture<T>(this, task);
synchronized(craftFuture) { synchronized (craftFuture) {
int taskId = scheduleSyncDelayedTask(plugin, craftFuture); int taskId = scheduleSyncDelayedTask(plugin, craftFuture);
craftFuture.setTaskId(taskId); craftFuture.setTaskId(taskId);
} }
@ -224,15 +265,40 @@ public class CraftScheduler implements BukkitScheduler, Runnable {
} }
public void cancelTasks(Plugin plugin) { public void cancelTasks(Plugin plugin) {
synchronized (schedulerQueue) { syncedTasksLock.lock();
Iterator<CraftTask> itr = schedulerQueue.keySet().iterator(); try {
while (itr.hasNext()) { synchronized(schedulerQueue) {
CraftTask current = itr.next(); mainThreadLock.lock();
if (current.getOwner().equals(plugin)) { try {
itr.remove(); Iterator<CraftTask> itr = schedulerQueue.keySet().iterator();
while (itr.hasNext()) {
CraftTask current = itr.next();
if (current.getOwner().equals(plugin)) {
itr.remove();
}
}
itr = mainThreadQueue.iterator();
while (itr.hasNext()) {
CraftTask current = itr.next();
if (current.getOwner().equals(plugin)) {
itr.remove();
}
}
itr = syncedTasks.iterator();
while (itr.hasNext()) {
CraftTask current = itr.next();
if (current.getOwner().equals(plugin)) {
itr.remove();
}
}
} finally {
mainThreadLock.unlock();
} }
} }
} finally {
syncedTasksLock.unlock();
} }
craftThreadManager.interruptTasks(plugin); craftThreadManager.interruptTasks(plugin);
} }
@ -240,6 +306,9 @@ public class CraftScheduler implements BukkitScheduler, Runnable {
synchronized (schedulerQueue) { synchronized (schedulerQueue) {
schedulerQueue.clear(); schedulerQueue.clear();
} }
wipeMainThreadQueue();
wipeSyncedTasks();
craftThreadManager.interruptAllTasks(); craftThreadManager.interruptAllTasks();
} }
@ -260,4 +329,40 @@ public class CraftScheduler implements BukkitScheduler, Runnable {
} }
} }
public List<BukkitWorker> getActiveWorkers() {
synchronized (craftThreadManager.workers) {
List<BukkitWorker> workerList = new ArrayList<BukkitWorker>(craftThreadManager.workers.size());
Iterator<CraftWorker> itr = craftThreadManager.workers.iterator();
while (itr.hasNext()) {
workerList.add((BukkitWorker)itr.next());
}
return workerList;
}
}
public List<BukkitTask> getPendingTasks() {
List<CraftTask> taskList = null;
syncedTasksLock.lock();
try {
synchronized (schedulerQueue) {
mainThreadLock.lock();
try {
taskList = new ArrayList<CraftTask>(mainThreadQueue.size() + syncedTasks.size() + schedulerQueue.size());
taskList.addAll(mainThreadQueue);
taskList.addAll(syncedTasks);
taskList.addAll(schedulerQueue.keySet());
} finally {
mainThreadLock.unlock();
}
}
} finally {
syncedTasksLock.unlock();
}
List<BukkitTask> newTaskList = new ArrayList<BukkitTask>(taskList.size());
for (CraftTask craftTask : taskList) {
newTaskList.add((BukkitTask)craftTask);
}
return newTaskList;
}
} }

Datei anzeigen

@ -3,8 +3,9 @@ package org.bukkit.craftbukkit.scheduler;
import java.lang.Comparable; import java.lang.Comparable;
import org.bukkit.plugin.Plugin; import org.bukkit.plugin.Plugin;
import org.bukkit.scheduler.BukkitTask;
public class CraftTask implements Comparable<Object> { public class CraftTask implements Comparable<Object>, BukkitTask {
private final Runnable task; private final Runnable task;
private final boolean syncTask; private final boolean syncTask;
@ -44,7 +45,7 @@ public class CraftTask implements Comparable<Object> {
return task; return task;
} }
boolean isSync() { public boolean isSync() {
return syncTask; return syncTask;
} }
@ -56,7 +57,7 @@ public class CraftTask implements Comparable<Object> {
return period; return period;
} }
Plugin getOwner() { public Plugin getOwner() {
return owner; return owner;
} }
@ -64,6 +65,10 @@ public class CraftTask implements Comparable<Object> {
executionTick += period; executionTick += period;
} }
public int getTaskId() {
return getIdNumber();
}
int getIdNumber() { int getIdNumber() {
return idNumber; return idNumber;
} }
@ -104,6 +109,4 @@ public class CraftTask implements Comparable<Object> {
public int hashCode() { public int hashCode() {
return getIdNumber(); return getIdNumber();
} }
} }

Datei anzeigen

@ -1,8 +1,9 @@
package org.bukkit.craftbukkit.scheduler; package org.bukkit.craftbukkit.scheduler;
import org.bukkit.plugin.Plugin; import org.bukkit.plugin.Plugin;
import org.bukkit.scheduler.BukkitWorker;
public class CraftWorker implements Runnable { public class CraftWorker implements Runnable, BukkitWorker {
private static int hashIdCounter = 1; private static int hashIdCounter = 1;
private static Object hashIdCounterSync = new Object(); private static Object hashIdCounterSync = new Object();
@ -49,6 +50,10 @@ public class CraftWorker implements Runnable {
return owner; return owner;
} }
public Thread getThread() {
return t;
}
public void interrupt() { public void interrupt() {
t.interrupt(); t.interrupt();
} }
@ -70,7 +75,6 @@ public class CraftWorker implements Runnable {
@Override @Override
public boolean equals( Object other ) { public boolean equals( Object other ) {
if (other == null) { if (other == null) {
return false; return false;
} }