private void fireStateChanged(T newState, FutureStateChange<T> futureStateChange, List<StateChangeListener<T>> stateChangeListeners) { checkState(!Thread.holdsLock(lock), "Can not fire state change event while holding the lock"); requireNonNull(newState, "newState is null"); // always fire listener callbacks from a different thread safeExecute(() -> { checkState(!Thread.holdsLock(lock), "Can not notify while holding the lock"); try { futureStateChange.complete(newState); } catch (Throwable e) { log.error(e, "Error setting future state for %s", name); } for (StateChangeListener<T> stateChangeListener : stateChangeListeners) { fireStateChangedListener(newState, stateChangeListener); } }); }
/** * Adds a listener to be notified when the state instance changes according to {@code .equals()}. * Listener is always notified asynchronously using a dedicated notification thread pool so, care should * be taken to avoid leaking {@code this} when adding a listener in a constructor. Additionally, it is * possible notifications are observed out of order due to the asynchronous execution. The listener is * immediately notified immediately of the current state. */ public void addStateChangeListener(StateChangeListener<T> stateChangeListener) { requireNonNull(stateChangeListener, "stateChangeListener is null"); boolean inTerminalState; T currentState; synchronized (lock) { currentState = state; inTerminalState = isTerminalState(currentState); if (!inTerminalState) { stateChangeListeners.add(stateChangeListener); } } // fire state change listener with the current state // always fire listener callbacks from a different thread safeExecute(() -> stateChangeListener.stateChanged(currentState)); }