Fork of Poseidon providing Bukkit #1060 to older Beta versions (b1.0-b1.7.3)
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}