Error and Exception Handling |
Provides some aspects about handling errors and exceptions in a state machine
Errors and exceptions in a state machine occur when a transition is executed and the corresponding actions fail to do, what they are expected to do.
Errors can also happen during checking of guard conditions, however their impact is usually low as these can often be handled graciously within the guard condition by returning a suitable fallback value.
Exceptions are program execution issues that can be handled at runtime of the program, whereas errors cannot be handled without restart of the program, most often the program has to be fixed.
Unfortunately this common definition overloads the C# exception language construct with the conceptual term exception. In this description an uppercase link to the runtime Exception class is used where C# exceptions are meant, otherwise the plain word exception is used.
StaMa throws Exceptions of type StateMachineException during setup of the StateMachineTemplate structure from the Region, EndRegion, State, EndState and Transition statements. These execptions are defined in the corresponding method documentation.
StaMa will not throw execptions after the StateMachine instance has been created and while executing the Startup, SendTriggerEvent and Finish methods, unless something very bad happened like multithreaded access or otherwise violent manipulations. All Exceptions thrown out of these methods come from the actions or guard conditions. Such Exceptions immediately terminate the execution of the Startup, SendTriggerEvent or Finish methods, potentially leaving the state machine inconsistent.
Inconsistent means that the exit actions of a state may have been executed but the active state has not been set to the new state, or that the new state has already been set, but the entry actions have not been executed completely.
In particular the following rules apply:
In case the Exception is thrown from a transition guard, the event is lost but the state machine remains unchanged and consistent.
In case the Exception is thrown from an exit action or from the transition action, the StateMachine.ActiveStateConfiguration remains in the old state, but no entry actions are executed and the run-to-completion processing is aborted.
In case the Exception is thrown from an entry method, the StateMachine.ActiveStateConfiguration is already changed to the target state of the transition, but the run-to-completion processing is aborted.
Exceptions like NullReferenceException, InvalidOperationException, ArgumentException, NotSupportedException, NotImplementedException, OutOfMemoryException, ObjectDisposedException, IndexOutOfRangeException and many others usually indicate an error that can not be handled without restart of the program and a change of the program.
Sometimes it may be possible to catch Exceptions like SocketException, IOException or similar and handle it through strategies described below.
Just as any other piece of code also execption handling in a state machine is specific for the individual state machine. There are few strategies to handle exceptions. Ideally exceptions shall be avoided by anticipating the condition that could cause an execption in the state machine execution flow. The remaining scenarios, where an execption cannot be avoided, might be cured through the following approaches:
State machines that purely use transition actions and avoid entry and exit actions are somewhat robust against exceptions within the transition actions. An exception in the single transition action can be implemented in a way that it leaves the state machine in a stable and consistent state by cleaning up locally within the transition action. The transition can then usually safely be retried by sending the event again.
Some exceptions in entry and exit actions can be handled by catching them and bringing the state machine to a fault state with the immediate next transition. In this case the exception handler of the failed action could set a property that enables transition with a single guard condition on a very outer level in the state machine. Such a transition on an outer composite states will take precedence in the immediate next step of the run-to-completion processing over all other nested transitions. A coordinated re-start or re-initialization of the state machine can then be tried from the fault state.