Commits

AgentLV authored and md_5 committed 75a8885d1fb
Run sync tasks scheduled for the same tick FIFO
No tags

src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java

Modified
25 25 /**
26 26 * The fundamental concepts for this implementation:
27 27 * <li>Main thread owns {@link #head} and {@link #currentTick}, but it may be read from any thread</li>
28 28 * <li>Main thread exclusively controls {@link #temp} and {@link #pending}.
29 29 * They are never to be accessed outside of the main thread; alternatives exist to prevent locking.</li>
30 30 * <li>{@link #head} to {@link #tail} act as a linked list/queue, with 1 consumer and infinite producers.
31 31 * Adding to the tail is atomic and very efficient; utility method is {@link #handle(CraftTask, long)} or {@link #addTask(CraftTask)}. </li>
32 32 * <li>Changing the period on a task is delicate.
33 33 * Any future task needs to notify waiting threads.
34 34 * Async tasks must be synchronized to make sure that any thread that's finishing will remove itself from {@link #runners}.
35 - * Another utility method is provided for this, {@link #cancelTask(CraftTask)}</li>
35 + * Another utility method is provided for this, {@link #cancelTask(int)}</li>
36 36 * <li>{@link #runners} provides a moderately up-to-date view of active tasks.
37 37 * If the linked head to tail set is read, all remaining tasks that were active at the time execution started will be located in runners.</li>
38 38 * <li>Async tasks are responsible for removing themselves from runners</li>
39 39 * <li>Sync tasks are only to be removed from runners on the main thread when coupled with a removal from pending and temp.</li>
40 40 * <li>Most of the design in this scheduler relies on queuing special tasks to perform any data changes on the main thread.
41 41 * When executed from inside a synchronous method, the scheduler will be updated before next execution by virtue of the frequent {@link #parsePending()} calls.</li>
42 42 */
43 43 public class CraftScheduler implements BukkitScheduler {
44 44
45 45 /**
53 53 /**
54 54 * Tail of a linked-list. AtomicReference only matters when adding to queue
55 55 */
56 56 private final AtomicReference<CraftTask> tail = new AtomicReference<CraftTask>(head);
57 57 /**
58 58 * Main thread logic only
59 59 */
60 60 private final PriorityQueue<CraftTask> pending = new PriorityQueue<CraftTask>(10,
61 61 new Comparator<CraftTask>() {
62 62 public int compare(final CraftTask o1, final CraftTask o2) {
63 - return (int) (o1.getNextRun() - o2.getNextRun());
63 + int value = (int) (o1.getNextRun() - o2.getNextRun());
64 +
65 + // If the tasks should run on the same tick they should be run FIFO
66 + return value != 0 ? value : o1.getTaskId() - o2.getTaskId();
64 67 }
65 68 });
66 69 /**
67 70 * Main thread logic only
68 71 */
69 72 private final List<CraftTask> temp = new ArrayList<CraftTask>();
70 73 /**
71 74 * These are tasks that are currently active. It's provided for 'viewing' the current state.
72 75 */
73 76 private final ConcurrentHashMap<Integer, CraftTask> runners = new ConcurrentHashMap<Integer, CraftTask>();

Everything looks good. We'll let you know here if there's anything you should know about.

Add shortcut