package com.framsticks.util.dispatching; import java.util.Collections; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; import javax.annotation.Nonnull; import org.apache.commons.collections.CollectionUtils; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; import com.framsticks.util.FramsticksException; public abstract class AbstractJoinable implements Joinable { private static final Logger log = LogManager.getLogger(AbstractJoinable.class); protected final Set owners = new HashSet(); protected final Set joinableListeners = new HashSet(); protected static final Set joinablesRegistry = Collections.synchronizedSet(new HashSet()); protected JoinableState state = JoinableState.INITILIAZED; protected JoinableParent parent; protected Monitor monitor; /** * */ public AbstractJoinable() { joinablesRegistry.add(this); } public static void report() { StringBuilder b = new StringBuilder(); synchronized (joinablesRegistry) { for (AbstractJoinable j : joinablesRegistry) { b.append("\n").append(j.getState()).append(" : ").append(j); } } log.debug("state: {}", b); } private synchronized boolean changeState(JoinableState state) { if (this.state.ordinal() >= state.ordinal()) { return false; } if (this.state.ordinal() + 1 != state.ordinal()) { throw new FramsticksException().msg("failed to change state").arg("from", this.state).arg("to", state).arg("joinable", this); } this.state = state; log.debug("{} is notifying {} parents", this, joinableListeners.size()); List parents = new LinkedList<>(); CollectionUtils.addAll(parents, joinableListeners.iterator()); for (JoinableParent p : parents) { if (p != null) { Dispatching.childChangedState(p, this, state); } } this.notifyAll(); report(); return true; } public final boolean start() { if (changeState(JoinableState.RUNNING)) { joinableStart(); return true; } return false; } public final boolean interrupt() { if (changeState(JoinableState.FINISHING)) { joinableInterrupt(); return true; } return false; } protected final boolean finish() { if (changeState(JoinableState.JOINABLE)) { joinableFinish(); return true; } return false; } @Override public final void join() throws InterruptedException { synchronized (this) { if (this.state.equals(JoinableState.JOINED)) { return; } if (!this.state.equals(JoinableState.JOINABLE)) { throw new InterruptedException(); } } joinableJoin(); synchronized (this) { this.state = JoinableState.JOINED; } } protected final boolean proceedToState(JoinableState state) { switch (state) { case RUNNING: return start(); case FINISHING: return interrupt(); case JOINABLE: return finish(); default: return false; } } protected abstract void joinableStart(); protected abstract void joinableInterrupt(); protected abstract void joinableFinish(); protected abstract void joinableJoin() throws InterruptedException; public final boolean use(@Nonnull JoinableParent owner) { boolean start = false; synchronized (this) { if (owners.contains(owner)) { throw new FramsticksException().msg("owner is already using that joinable").arg("joinable", this).arg("owner", owner); } start = owners.isEmpty(); log.debug("{} is using {}", owner, this); owners.add(owner); joinableListeners.add(owner); } if (start) { assert monitor == null; monitor = owner.getMonitor(); return this.start(); } return false; } public final boolean drop(@Nonnull JoinableParent owner) { boolean stop = false; synchronized (this) { if (!owners.contains(owner)) { return false; // throw new FramsticksException().msg("object is not owning that joinable").arg("joinable", this).arg("object", owner); } // assert owners.containsKey(owner); log.debug("{} is droping {}", owner, this); owners.remove(owner); stop = owners.isEmpty(); } if (stop) { Dispatching.dispatcherGuardedInvoke(this, new RunAt(ThrowExceptionHandler.getInstance()) { @Override protected void runAt() { interrupt(); } }); return true; } return stop; } /** * @return the state */ public JoinableState getState() { return state; } protected boolean isInState(JoinableState state) { return this.state.equals(state); } protected boolean isRunning() { return state.equals(JoinableState.RUNNING); } @Override public String toString() { return getName(); } // @Override public Monitor getMonitor() { return monitor; } }