Fork of Poseidon providing Bukkit #1060 to older Beta versions (b1.0-b1.7.3)
at develop 389 lines 13 kB view raw
1package org.bukkit.craftbukkit.scheduler; 2 3import org.bukkit.craftbukkit.CraftServer; 4import org.bukkit.plugin.Plugin; 5import org.bukkit.scheduler.BukkitScheduler; 6import org.bukkit.scheduler.BukkitTask; 7import org.bukkit.scheduler.BukkitWorker; 8 9import java.util.*; 10import java.util.concurrent.Callable; 11import java.util.concurrent.Future; 12import java.util.concurrent.locks.Lock; 13import java.util.concurrent.locks.ReentrantLock; 14import java.util.logging.Level; 15import java.util.logging.Logger; 16 17public class CraftScheduler implements BukkitScheduler, Runnable { 18 19 private static final Logger logger = Logger.getLogger("Minecraft"); 20 21 private final CraftServer server; 22 23 private final CraftThreadManager craftThreadManager = new CraftThreadManager(); 24 25 private final LinkedList<CraftTask> mainThreadQueue = new LinkedList<CraftTask>(); 26 private final LinkedList<CraftTask> syncedTasks = new LinkedList<CraftTask>(); 27 28 private final TreeMap<CraftTask, Boolean> schedulerQueue = new TreeMap<CraftTask, Boolean>(); 29 30 private final Object currentTickSync = new Object(); 31 private Long currentTick = 0L; 32 33 // This lock locks the mainThreadQueue and the currentTick value 34 private final Lock mainThreadLock = new ReentrantLock(); 35 private final Lock syncedTasksLock = new ReentrantLock(); 36 37 public void run() { 38 39 while (true) { 40 boolean stop = false; 41 long firstTick = -1; 42 long currentTick = -1; 43 CraftTask first = null; 44 do { 45 synchronized (schedulerQueue) { 46 first = null; 47 if (!schedulerQueue.isEmpty()) { 48 first = schedulerQueue.firstKey(); 49 if (first != null) { 50 currentTick = getCurrentTick(); 51 52 firstTick = first.getExecutionTick(); 53 54 if (currentTick >= firstTick) { 55 schedulerQueue.remove(first); 56 processTask(first); 57 if (first.getPeriod() >= 0) { 58 first.updateExecution(); 59 schedulerQueue.put(first, first.isSync()); 60 } 61 } else { 62 stop = true; 63 } 64 } else { 65 stop = true; 66 } 67 } else { 68 stop = true; 69 } 70 } 71 } while (!stop); 72 73 long sleepTime = 0; 74 if (first == null) { 75 sleepTime = 60000L; 76 } else { 77 currentTick = getCurrentTick(); 78 sleepTime = (firstTick - currentTick) * 50 + 25; 79 } 80 81 if (sleepTime < 50L) { 82 sleepTime = 50L; 83 } else if (sleepTime > 60000L) { 84 sleepTime = 60000L; 85 } 86 87 synchronized (schedulerQueue) { 88 try { 89 schedulerQueue.wait(sleepTime); 90 } catch (InterruptedException ie) { 91 } 92 } 93 } 94 } 95 96 void processTask(CraftTask task) { 97 if (task.isSync()) { 98 addToMainThreadQueue(task); 99 } else { 100 craftThreadManager.executeTask(task.getTask(), task.getOwner(), task.getIdNumber()); 101 } 102 } 103 104 public CraftScheduler(CraftServer server) { 105 this.server = server; 106 107 Thread t = new Thread(this); 108 t.start(); 109 110 } 111 112 // If the main thread cannot obtain the lock, it doesn't wait 113 public void mainThreadHeartbeat(long currentTick) { 114 if (syncedTasksLock.tryLock()) { 115 try { 116 if (mainThreadLock.tryLock()) { 117 try { 118 this.currentTick = currentTick; 119 while (!mainThreadQueue.isEmpty()) { 120 syncedTasks.addLast(mainThreadQueue.removeFirst()); 121 } 122 } finally { 123 mainThreadLock.unlock(); 124 } 125 } 126 long breakTime = System.currentTimeMillis() + 35; // max time spent in loop = 35ms 127 while (!syncedTasks.isEmpty() && System.currentTimeMillis() <= breakTime) { 128 CraftTask task = syncedTasks.removeFirst(); 129 try { 130 task.getTask().run(); 131 } catch (Throwable t) { 132 // Bad plugin! 133 logger.log(Level.WARNING, "Task of '" + task.getOwner().getDescription().getName() + "' generated an exception", t); 134 synchronized (schedulerQueue) { 135 schedulerQueue.remove(task); 136 } 137 } 138 } 139 } finally { 140 syncedTasksLock.unlock(); 141 } 142 } 143 } 144 145 long getCurrentTick() { 146 mainThreadLock.lock(); 147 long tempTick = 0; 148 try { 149 tempTick = currentTick; 150 } finally { 151 mainThreadLock.unlock(); 152 } 153 return tempTick; 154 } 155 156 void addToMainThreadQueue(CraftTask task) { 157 mainThreadLock.lock(); 158 try { 159 mainThreadQueue.addLast(task); 160 } finally { 161 mainThreadLock.unlock(); 162 } 163 } 164 165 void wipeSyncedTasks() { 166 syncedTasksLock.lock(); 167 try { 168 syncedTasks.clear(); 169 } finally { 170 syncedTasksLock.unlock(); 171 } 172 } 173 174 void wipeMainThreadQueue() { 175 mainThreadLock.lock(); 176 try { 177 mainThreadQueue.clear(); 178 } finally { 179 mainThreadLock.unlock(); 180 } 181 } 182 183 public int scheduleSyncDelayedTask(Plugin plugin, Runnable task, long delay) { 184 return scheduleSyncRepeatingTask(plugin, task, delay, -1); 185 } 186 187 public int scheduleSyncDelayedTask(Plugin plugin, Runnable task) { 188 return scheduleSyncDelayedTask(plugin, task, 0L); 189 } 190 191 public int scheduleSyncRepeatingTask(Plugin plugin, Runnable task, long delay, long period) { 192 if (plugin == null) { 193 throw new IllegalArgumentException("Plugin cannot be null"); 194 } 195 if (task == null) { 196 throw new IllegalArgumentException("Task cannot be null"); 197 } 198 if (delay < 0) { 199 throw new IllegalArgumentException("Delay cannot be less than 0"); 200 } 201 202 CraftTask newTask = new CraftTask(plugin, task, true, getCurrentTick() + delay, period); 203 204 synchronized (schedulerQueue) { 205 schedulerQueue.put(newTask, true); 206 schedulerQueue.notify(); 207 } 208 return newTask.getIdNumber(); 209 } 210 211 public int scheduleAsyncDelayedTask(Plugin plugin, Runnable task, long delay) { 212 return scheduleAsyncRepeatingTask(plugin, task, delay, -1); 213 } 214 215 public int scheduleAsyncDelayedTask(Plugin plugin, Runnable task) { 216 return scheduleAsyncDelayedTask(plugin, task, 0L); 217 } 218 219 public int scheduleAsyncRepeatingTask(Plugin plugin, Runnable task, long delay, long period) { 220 if (plugin == null) { 221 throw new IllegalArgumentException("Plugin cannot be null"); 222 } 223 if (task == null) { 224 throw new IllegalArgumentException("Task cannot be null"); 225 } 226 if (delay < 0) { 227 throw new IllegalArgumentException("Delay cannot be less than 0"); 228 } 229 230 CraftTask newTask = new CraftTask(plugin, task, false, getCurrentTick() + delay, period); 231 232 synchronized (schedulerQueue) { 233 schedulerQueue.put(newTask, false); 234 schedulerQueue.notify(); 235 } 236 return newTask.getIdNumber(); 237 } 238 239 public <T> Future<T> callSyncMethod(Plugin plugin, Callable<T> task) { 240 CraftFuture<T> craftFuture = new CraftFuture<T>(this, task); 241 synchronized (craftFuture) { 242 int taskId = scheduleSyncDelayedTask(plugin, craftFuture); 243 craftFuture.setTaskId(taskId); 244 } 245 return craftFuture; 246 } 247 248 public void cancelTask(int taskId) { 249 syncedTasksLock.lock(); 250 try { 251 synchronized (schedulerQueue) { 252 mainThreadLock.lock(); 253 try { 254 Iterator<CraftTask> itr = schedulerQueue.keySet().iterator(); 255 while (itr.hasNext()) { 256 CraftTask current = itr.next(); 257 if (current.getIdNumber() == taskId) { 258 itr.remove(); 259 } 260 } 261 itr = mainThreadQueue.iterator(); 262 while (itr.hasNext()) { 263 CraftTask current = itr.next(); 264 if (current.getIdNumber() == taskId) { 265 itr.remove(); 266 } 267 } 268 itr = syncedTasks.iterator(); 269 while (itr.hasNext()) { 270 CraftTask current = itr.next(); 271 if (current.getIdNumber() == taskId) { 272 itr.remove(); 273 } 274 } 275 } finally { 276 mainThreadLock.unlock(); 277 } 278 } 279 } finally { 280 syncedTasksLock.unlock(); 281 } 282 283 craftThreadManager.interruptTask(taskId); 284 } 285 286 public void cancelTasks(Plugin plugin) { 287 syncedTasksLock.lock(); 288 try { 289 synchronized (schedulerQueue) { 290 mainThreadLock.lock(); 291 try { 292 Iterator<CraftTask> itr = schedulerQueue.keySet().iterator(); 293 while (itr.hasNext()) { 294 CraftTask current = itr.next(); 295 if (current.getOwner().equals(plugin)) { 296 itr.remove(); 297 } 298 } 299 itr = mainThreadQueue.iterator(); 300 while (itr.hasNext()) { 301 CraftTask current = itr.next(); 302 if (current.getOwner().equals(plugin)) { 303 itr.remove(); 304 } 305 } 306 itr = syncedTasks.iterator(); 307 while (itr.hasNext()) { 308 CraftTask current = itr.next(); 309 if (current.getOwner().equals(plugin)) { 310 itr.remove(); 311 } 312 } 313 } finally { 314 mainThreadLock.unlock(); 315 } 316 } 317 } finally { 318 syncedTasksLock.unlock(); 319 } 320 321 craftThreadManager.interruptTasks(plugin); 322 } 323 324 public void cancelAllTasks() { 325 synchronized (schedulerQueue) { 326 schedulerQueue.clear(); 327 } 328 wipeMainThreadQueue(); 329 wipeSyncedTasks(); 330 331 craftThreadManager.interruptAllTasks(); 332 } 333 334 public boolean isCurrentlyRunning(int taskId) { 335 return craftThreadManager.isAlive(taskId); 336 } 337 338 public boolean isQueued(int taskId) { 339 synchronized (schedulerQueue) { 340 Iterator<CraftTask> itr = schedulerQueue.keySet().iterator(); 341 while (itr.hasNext()) { 342 CraftTask current = itr.next(); 343 if (current.getIdNumber() == taskId) { 344 return true; 345 } 346 } 347 return false; 348 } 349 } 350 351 public List<BukkitWorker> getActiveWorkers() { 352 synchronized (craftThreadManager.workers) { 353 List<BukkitWorker> workerList = new ArrayList<BukkitWorker>(craftThreadManager.workers.size()); 354 Iterator<CraftWorker> itr = craftThreadManager.workers.iterator(); 355 356 while (itr.hasNext()) { 357 workerList.add((BukkitWorker) itr.next()); 358 } 359 return workerList; 360 } 361 } 362 363 public List<BukkitTask> getPendingTasks() { 364 List<CraftTask> taskList = null; 365 syncedTasksLock.lock(); 366 try { 367 synchronized (schedulerQueue) { 368 mainThreadLock.lock(); 369 try { 370 taskList = new ArrayList<CraftTask>(mainThreadQueue.size() + syncedTasks.size() + schedulerQueue.size()); 371 taskList.addAll(mainThreadQueue); 372 taskList.addAll(syncedTasks); 373 taskList.addAll(schedulerQueue.keySet()); 374 } finally { 375 mainThreadLock.unlock(); 376 } 377 } 378 } finally { 379 syncedTasksLock.unlock(); 380 } 381 List<BukkitTask> newTaskList = new ArrayList<BukkitTask>(taskList.size()); 382 383 for (CraftTask craftTask : taskList) { 384 newTaskList.add((BukkitTask) craftTask); 385 } 386 return newTaskList; 387 } 388 389}