private void TransitionToSynchronized(QState targetState, ref TransitionChain transitionChain) { //The entire method had been embedded within [MethodImpl(MethodImplOptions.Synchronized)], //which is not available in .NET Core, and so a lock is used instead... lock (this) { if (transitionChain != null) { // We encountered a race condition. The first (non-synchronized) check indicated that the transition chain // is null. However, a second threat beat us in getting into this synchronized method and populated // the transition chain in the meantime. We can execute the regular method again now. TransitionTo(targetState, ref transitionChain); } else { // The transition chain is not initialized yet, we need to dynamically retrieve // the required transition steps and record them so that we can subsequently simply // play them back. TransitionChainRecorder recorder = new TransitionChainRecorder(); TransitionFromSourceToTarget(targetState.GetMethodInfo(), recorder); // We pass the recorded transition steps back to the caller: transitionChain = recorder.GetRecordedTransitionChain(); } } }
/// <summary> /// Reallocates the internal array <see cref="m_Items"/> to an array twice the previous capacity. /// </summary> private void IncreaseCapacity() { int newCapacity; if (m_Items.Length == 0) { newCapacity = c_DefaultCapacity; } else { newCapacity = m_Items.Length * 2; } TransitionChain[] newItems = new TransitionChain[newCapacity]; Array.Copy(m_Items, 0, newItems, 0, m_Items.Length); m_Items = newItems; }
/// <summary> /// Performs the transition from the current state to the specified target state. /// </summary> /// <param name="targetState">The <see cref="QState"/> to transition to.</param> /// <param name="transitionChain">A <see cref="TransitionChain"/> used to hold the transition chain that /// needs to be executed to perform the transition to the target state.</param> /// <remarks> /// The very first time that a given static transition is executed, the <see paramref="transitionChain"/> /// reference will point to <see langword="null"/>. In this case a new <see cref="TransitionChain"/> /// instance is created. As the complete transition is performed the individual transition steps are /// recorded in the new <see cref="TransitionChain"/> instance. At the end of the call the new /// (and now filled) <see cref="TransitionChain"/> is handed back to the caller. /// If the same transition needs to be performed later again, the caller needs to pass /// in the filled <see cref="TransitionChain"/> instance. The recorded transition path will then be /// played back very efficiently. /// </remarks> protected void TransitionTo(QState targetState, ref TransitionChain transitionChain) { Debug.Assert(targetState != s_TopState); // can't target 'top' state ExitUpToSourceState(); if (transitionChain == null) // for efficiency the first check is not thread-safe { // We implement the double-checked locking pattern TransitionToSynchronized(targetState, ref transitionChain); } else { // We just need to 'replay' the transition chain that is stored in the transitions chain. ExecuteTransitionChain(transitionChain); } }
private void ExecuteTransitionChain(TransitionChain transitionChain) { // There must always be at least one transition step in the provided transition chain Debug.Assert(transitionChain.Length > 0); TransitionStep transitionStep = transitionChain[0]; // to shut up the compiler; // without it we would get the following error on the line // m_MyState = transitionStep.State; // at the end of this method: Use of possibly unassigned field 'State' for (int i = 0; i < transitionChain.Length; i++) { transitionStep = transitionChain[i]; Trigger(transitionStep.State, transitionStep.QSignal); } m_MyState = transitionStep.State; }
private void TransitionToSynchronized(QState targetState, ref TransitionChain transitionChain) { if (transitionChain != null) { // We encountered a race condition. The first (non-synchronized) check indicated that the transition chain // is null. However, a second threat beat us in getting into this synchronized method and populated // the transition chain in the meantime. We can execute the regular method again now. TransitionTo(targetState, ref transitionChain); } else { // The transition chain is not initialized yet, we need to dynamically retrieve // the required transition steps and record them so that we can subsequently simply // play them back. TransitionChainRecorder recorder = new TransitionChainRecorder(); TransitionFromSourceToTarget(targetState, recorder); // We pass the recorded transition steps back to the caller: transitionChain = recorder.GetRecordedTransitionChain(); } }
private void EnsureLastTransistionStepIsEntryIntoTargetState( QState targetState, TransitionChainRecorder recorder) { if (recorder.GetRecordedTransitionChain().Length == 0) { // Nothing recorded so far RecordEntryIntoTargetState(targetState, recorder); return; } else { // We need to test whether the last recorded transition step is the entry into the target state TransitionChain transitionChain = recorder.GetRecordedTransitionChain(); TransitionStep lastTransitionStep = transitionChain[transitionChain.Length - 1]; if (lastTransitionStep.State != targetState || lastTransitionStep.QSignal != QSignals.Entry) { RecordEntryIntoTargetState(targetState, recorder); return; } } }
/// <summary> /// Creates a TransitionChain and runs it. /// </summary> public static void runChain(params Transition[] transitions) { TransitionChain chain = new TransitionChain(transitions); }
/// <summary> /// Should be called once all required slots have been established in order to minimize the memory /// footprint of the store. /// </summary> public void ShrinkToActualSize() { TransitionChain[] newItems = new TransitionChain[m_Size]; Array.Copy(m_Items, 0, newItems, 0, m_Size); m_Items = newItems; }
/// <summary> /// Reallocates the internal array <see cref="m_Items"/> to an array twice the previous capacity. /// </summary> private void IncreaseCapacity() { int newCapacity; if (m_Items.Length == 0) { newCapacity = c_DefaultCapacity; } else { newCapacity = m_Items.Length * 2; } TransitionChain[] newItems = new TransitionChain[newCapacity]; Array.Copy(m_Items, 0, newItems, 0, m_Items.Length); m_Items = newItems; }
/// <summary> /// Should be called once all required slots have been established in order to minimize the memory /// footprint of the store. /// </summary> public void ShrinkToActualSize() { TransitionChain[] newItems = new TransitionChain[m_Size]; Array.Copy(m_Items, 0, newItems, 0, m_Size); m_Items = newItems; }
private void TransitionToSynchronized(QState targetState, ref TransitionChain transitionChain) { if (transitionChain != null) { // We encountered a race condition. The first (non-synchronized) check indicated that the transition chain // is null. However, a second threat beat us in getting into this synchronized method and populated // the transition chain in the meantime. We can execute the regular method again now. TransitionTo(targetState, ref transitionChain); } else { // The transition chain is not initialized yet, we need to dynamically retrieve // the required transition steps and record them so that we can subsequently simply // play them back. TransitionChainRecorder recorder = new TransitionChainRecorder(); TransitionFromSourceToTarget(targetState.Method, recorder); // We pass the recorded transition steps back to the caller: transitionChain = recorder.GetRecordedTransitionChain(); } }
private void ExecuteTransitionChain(TransitionChain transitionChain) { // There must always be at least one transition step in the provided transition chain Debug.Assert(transitionChain.Length > 0); TransitionStep transitionStep = transitionChain[0]; // to shut up the compiler; // without it we would get the following error on the line // m_MyStateMethod = transitionStep.StateMethod; // at the end of this method: Use of possibly unassigned field 'State' for(int i = 0; i < transitionChain.Length; i++) { transitionStep = transitionChain[i]; Trigger(transitionStep.StateMethod, transitionStep.QSignal); } m_MyStateMethod = transitionStep.StateMethod; }
/// <summary> /// Performs the transition from the current state to the specified target state. /// </summary> /// <param name="targetState">The <see cref="QState"/> to transition to.</param> /// <param name="transitionChain">A <see cref="TransitionChain"/> used to hold the transition chain that /// needs to be executed to perform the transition to the target state.</param> /// <remarks> /// The very first time that a given static transition is executed, the <see paramref="transitionChain"/> /// reference will point to <see langword="null"/>. In this case a new <see cref="TransitionChain"/> /// instance is created. As the complete transition is performed the individual transition steps are /// recorded in the new <see cref="TransitionChain"/> instance. At the end of the call the new /// (and now filled) <see cref="TransitionChain"/> is handed back to the caller. /// If the same transition needs to be performed later again, the caller needs to pass /// in the filled <see cref="TransitionChain"/> instance. The recorded transition path will then be /// played back very efficiently. /// </remarks> protected void TransitionTo(QState targetState, ref TransitionChain transitionChain) { Debug.Assert(targetState != s_TopState); // can't target 'top' state ExitUpToSourceState(); if (transitionChain == null) // for efficiency the first check is not thread-safe { // We implement the double-checked locking pattern TransitionToSynchronized(targetState, ref transitionChain); } else { // We just need to 'replay' the transition chain that is stored in the transitions chain. ExecuteTransitionChain(transitionChain); } }
public static void runChain(params Transition[] transitions) { TransitionChain transitionChain = new TransitionChain(transitions); }