| 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; |
| } |
| } |