summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRaphfrk <raphfrk@gmail.com>2011-04-28 20:10:39 +0100
committerEvilSeph <evilseph@unaligned.org>2011-05-02 02:40:07 -0400
commitfdb077e814932d55111b9d60746a7a95306ce57a (patch)
tree848793a75ea9413328f84bc0e10a4e17d3945a22
parentaa70240917c2b6698c20c60a101431ba73da29af (diff)
downloadcraftbukkit-fdb077e814932d55111b9d60746a7a95306ce57a.tar
craftbukkit-fdb077e814932d55111b9d60746a7a95306ce57a.tar.gz
craftbukkit-fdb077e814932d55111b9d60746a7a95306ce57a.tar.lz
craftbukkit-fdb077e814932d55111b9d60746a7a95306ce57a.tar.xz
craftbukkit-fdb077e814932d55111b9d60746a7a95306ce57a.zip
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
-rw-r--r--src/main/java/org/bukkit/craftbukkit/CraftServer.java29
-rw-r--r--src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java171
-rw-r--r--src/main/java/org/bukkit/craftbukkit/scheduler/CraftTask.java13
-rw-r--r--src/main/java/org/bukkit/craftbukkit/scheduler/CraftWorker.java8
4 files changed, 181 insertions, 40 deletions
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
index fbf48c60..f28b7325 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -21,6 +21,7 @@ import java.util.Map;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;
+import java.lang.InterruptedException;
import jline.ConsoleReader;
import net.minecraft.server.ChunkCoordinates;
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.CraftShapedRecipe;
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.util.config.Configuration;
@@ -292,6 +295,32 @@ public final class CraftServer implements Server {
pluginManager.clearPlugins();
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();
}
diff --git a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java
index 9caef416..222a3158 100644
--- a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java
+++ b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java
@@ -3,6 +3,8 @@ package org.bukkit.craftbukkit.scheduler;
import java.util.LinkedList;
import java.util.TreeMap;
import java.util.Iterator;
+import java.util.List;
+import java.util.ArrayList;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@@ -12,6 +14,9 @@ import java.util.logging.Level;
import java.util.logging.Logger;
import org.bukkit.scheduler.BukkitScheduler;
+import org.bukkit.scheduler.BukkitWorker;
+import org.bukkit.scheduler.BukkitTask;
+
import org.bukkit.plugin.Plugin;
import org.bukkit.craftbukkit.CraftServer;
@@ -35,6 +40,7 @@ public class CraftScheduler implements BukkitScheduler, Runnable {
// This lock locks the mainThreadQueue and the currentTick value
private final Lock mainThreadLock = new ReentrantLock();
+ private final Lock syncedTasksLock = new ReentrantLock();
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
public void mainThreadHeartbeat(long currentTick) {
- if (mainThreadLock.tryLock()) {
+ if (syncedTasksLock.tryLock()) {
try {
- this.currentTick = currentTick;
- while (!mainThreadQueue.isEmpty()) {
- syncedTasks.addLast(mainThreadQueue.removeFirst());
+ if (mainThreadLock.tryLock()) {
+ try {
+ this.currentTick = currentTick;
+ while (!mainThreadQueue.isEmpty()) {
+ syncedTasks.addLast(mainThreadQueue.removeFirst());
+ }
+ } finally {
+ mainThreadLock.unlock();
+ }
}
- } finally {
- mainThreadLock.unlock();
- }
- while(!syncedTasks.isEmpty()) {
- 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) {
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) {
- if (plugin == null) throw new IllegalArgumentException("Plugin can not null");
- if (task == null) throw new IllegalArgumentException("Task can not null");
- if (delay < 0) throw new IllegalArgumentException("Delay cannot be less than 0");
-
+ if (plugin == null) {
+ throw new IllegalArgumentException("Plugin cannot be null");
+ }
+ 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);
synchronized (schedulerQueue) {
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) {
- if (plugin == null) throw new IllegalArgumentException("Plugin can not null");
- if (task == null) throw new IllegalArgumentException("Task can not null");
- if (delay < 0) throw new IllegalArgumentException("Delay cannot be less than 0");
-
+ if (plugin == null) {
+ throw new IllegalArgumentException("Plugin cannot be null");
+ }
+ 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);
synchronized (schedulerQueue) {
schedulerQueue.put(newTask, false);
@@ -203,7 +244,7 @@ public class CraftScheduler implements BukkitScheduler, Runnable {
public <T> Future<T> callSyncMethod(Plugin plugin, Callable<T> task) {
CraftFuture<T> craftFuture = new CraftFuture<T>(this, task);
- synchronized(craftFuture) {
+ synchronized (craftFuture) {
int taskId = scheduleSyncDelayedTask(plugin, craftFuture);
craftFuture.setTaskId(taskId);
}
@@ -224,15 +265,40 @@ public class CraftScheduler implements BukkitScheduler, Runnable {
}
public void cancelTasks(Plugin plugin) {
- synchronized (schedulerQueue) {
- Iterator<CraftTask> itr = schedulerQueue.keySet().iterator();
- while (itr.hasNext()) {
- CraftTask current = itr.next();
- if (current.getOwner().equals(plugin)) {
- itr.remove();
+ syncedTasksLock.lock();
+ try {
+ synchronized(schedulerQueue) {
+ mainThreadLock.lock();
+ try {
+ 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);
}
@@ -240,6 +306,9 @@ public class CraftScheduler implements BukkitScheduler, Runnable {
synchronized (schedulerQueue) {
schedulerQueue.clear();
}
+ wipeMainThreadQueue();
+ wipeSyncedTasks();
+
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;
+ }
+
}
diff --git a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftTask.java b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftTask.java
index 7d5b610e..d5cc4f4e 100644
--- a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftTask.java
+++ b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftTask.java
@@ -3,8 +3,9 @@ package org.bukkit.craftbukkit.scheduler;
import java.lang.Comparable;
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 boolean syncTask;
@@ -44,7 +45,7 @@ public class CraftTask implements Comparable<Object> {
return task;
}
- boolean isSync() {
+ public boolean isSync() {
return syncTask;
}
@@ -56,7 +57,7 @@ public class CraftTask implements Comparable<Object> {
return period;
}
- Plugin getOwner() {
+ public Plugin getOwner() {
return owner;
}
@@ -64,6 +65,10 @@ public class CraftTask implements Comparable<Object> {
executionTick += period;
}
+ public int getTaskId() {
+ return getIdNumber();
+ }
+
int getIdNumber() {
return idNumber;
}
@@ -104,6 +109,4 @@ public class CraftTask implements Comparable<Object> {
public int hashCode() {
return getIdNumber();
}
-
-
}
diff --git a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftWorker.java b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftWorker.java
index 3bf91e4f..42f9bb9b 100644
--- a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftWorker.java
+++ b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftWorker.java
@@ -1,8 +1,9 @@
package org.bukkit.craftbukkit.scheduler;
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 Object hashIdCounterSync = new Object();
@@ -49,6 +50,10 @@ public class CraftWorker implements Runnable {
return owner;
}
+ public Thread getThread() {
+ return t;
+ }
+
public void interrupt() {
t.interrupt();
}
@@ -70,7 +75,6 @@ public class CraftWorker implements Runnable {
@Override
public boolean equals( Object other ) {
-
if (other == null) {
return false;
}