/// <summary> /// Atomically transitions <see cref="State" /> to <paramref name="targetState" />. /// The current state must not change during the attempted transition (i.e., synchronization may be required). /// If provided, <paramref name="onCommit"/> is called upon committing to transition to the next state but /// before the state could possibly transition further. /// </summary> /// <remarks> /// This method is effectively not thread safe, since one must somehow know that a transition to /// <paramref name="targetState" /> is valid /// and that the current state will not change during this call (e.g. via some collaborating lock). /// </remarks> /// <returns>The prior pip state</returns> public PipState Transition(PipState targetState, PipType pipType, Action <PipState, PipState, PipType> onCommit = null) { // This Requires is Static (not at runtime) so we avoid loading State twice (it can change behind our backs). // Below we do a final (authoritative) load of State and then runtime-verify a valid transition. Contract.RequiresDebug(State.CanTransitionTo(targetState)); Contract.Requires(pipType < PipType.Max || onCommit == null); PipState presentState = State; if (!State.CanTransitionTo(targetState)) { Contract.Assume(false, I($"Transition failure (not a valid transition): {presentState:G} -> {targetState:G}")); } Contract.Assume(((int)presentState & PreCommitStateBit) == 0); bool transitioned = TryTransitionInternal(presentState, targetState, pipType, onCommit); if (!transitioned) { Contract.Assume( false, I($"Failed to transition a pip from {presentState:G} to {targetState:G} due to an unexpected intervening state change (current state is now {State:G})")); } return(presentState); }
/// <summary> /// Atomically transitions <see cref="State" /> from <paramref name="assumedPresentState" /> to /// <paramref name="targetState" />. /// Returns a bool indicating if the transition succeeded (if false, the <paramref name="assumedPresentState" /> was /// mismatched with the current state). /// If provided, <paramref name="onCommit"/> is called upon committing to transition to the next state but /// before the state could possibly transition further. /// </summary> /// <remarks> /// This method is thread safe. /// </remarks> public bool TryTransition(PipState assumedPresentState, PipState targetState, PipType pipType = PipType.Max, Action <PipState, PipState, PipType> onCommit = null) { Contract.Requires(assumedPresentState.CanTransitionTo(targetState)); Contract.Requires(((int)assumedPresentState & PreCommitStateBit) == 0, "PipState values should have the high bit cleared"); Contract.Requires(pipType < PipType.Max || onCommit == null); return(TryTransitionInternal(assumedPresentState, targetState, pipType, onCommit)); }