package net.glowstone.scheduler; import com.google.common.collect.ImmutableList; import net.glowstone.GlowServer; import net.glowstone.GlowWorld; import java.util.List; import java.util.UUID; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.Phaser; import java.util.logging.Level; /** * Manager for world thread pool. * <p/> * This is a little magical and finnicky, so tread with caution when messing with the phasers */ public class WorldScheduler { private static class WorldEntry { private final GlowWorld world; private WorldThread task; private WorldEntry(GlowWorld world) { this.world = world; } } private final Object advanceCondition = new Object(); private final Phaser tickBegin = new Phaser(1); private final Phaser tickEnd = new Phaser(1); private final List<WorldEntry> worlds = new CopyOnWriteArrayList<>(); private volatile int currentTick = -1; private class WorldThread extends Thread { private final GlowWorld world; public WorldThread(GlowWorld world) { super("Glowstone-world-" + world.getName()); this.world = world; } @Override public void run() { try { while (!isInterrupted() && !tickEnd.isTerminated()) { tickBegin.arriveAndAwaitAdvance(); try { world.pulse(); } catch (Exception e) { GlowServer.logger.log(Level.SEVERE, "Error occurred while pulsing world " + world.getName(), e); } finally { tickEnd.arriveAndAwaitAdvance(); } } } finally { tickBegin.arriveAndDeregister(); tickEnd.arriveAndDeregister(); } } } public List<GlowWorld> getWorlds() { ImmutableList.Builder<GlowWorld> ret = ImmutableList.builder(); for (WorldEntry entry : worlds) { ret.add(entry.world); } return ret.build(); } public GlowWorld getWorld(String name) { for (WorldEntry went : worlds) { if (went.world.getName().equals(name)) { return went.world; } } return null; } public GlowWorld getWorld(UUID uid) { for (WorldEntry went : worlds) { if (went.world.getUID().equals(uid)) { return went.world; } } return null; } public GlowWorld addWorld(final GlowWorld world) { final WorldEntry went = new WorldEntry(world); worlds.add(went); try { went.task = new WorldThread(world); tickBegin.register(); tickEnd.register(); went.task.start(); return world; } catch (Throwable t) { tickBegin.arriveAndDeregister(); tickEnd.arriveAndDeregister(); worlds.remove(went); return null; } } public boolean removeWorld(final GlowWorld world) { for (WorldEntry entry : worlds) { if (entry.world.equals(world)) { if (entry.task != null) { entry.task.interrupt(); } worlds.remove(entry); return true; } } return false; } int beginTick() throws InterruptedException { tickEnd.awaitAdvanceInterruptibly(currentTick); // Make sure previous tick is complete return currentTick = tickBegin.arrive(); } boolean isTickComplete(int tick) { return tickEnd.getPhase() > tick || tickEnd.getPhase() < 0; } void stop() { tickBegin.forceTermination(); tickEnd.forceTermination(); for (WorldEntry ent : worlds) { if (ent.task != null) { ent.task.interrupt(); } } } void doTickEnd() { final int currentTick = this.currentTick; // Mark ourselves as arrived so world threads automatically trigger advance once done int endPhase = tickEnd.arriveAndAwaitAdvance(); if (endPhase != currentTick + 1) { GlowServer.logger.warning("Tick end barrier " + endPhase + " has advanced differently from tick begin barrier:" + currentTick + 1); } synchronized (advanceCondition) { advanceCondition.notifyAll(); } } public Object getAdvanceCondition() { return advanceCondition; } }