5

State machines, for example UML state machines, statecharts or other finite state machines, allow actions to be executed when a state machine takes a transition between states or is within a state.

enter image description here

In programming languages with exceptions, exceptions can occur within such actions, even if you disallow checked exceptions for actions in programming languages that have checked exceptions or disallow exceptions all together. In programming languages like Java or Ada exceptions are part of the semantics of the language, for example OutOfMemoryError or Constraint_Error, and can occur at many points. So state machines have to handle them somehow.

Events are often enqueued on an even event queue of the state machine and handled asynchronously. Therefore, it is impossible to propagate an exception to a caller. So far I found two methods of handling exception in actions of state machines:

  1. Invoke some exception handler callback of the state machine, source or target states or transition that is being taken. The North State Framework for UML state machines uses this method.
  2. Generate an event for the exception and handle that event in the state machine. This has been described in Modeling and Analysis of Exception Handling by Using UML Statecharts.

However, it is unclear to me what an exception during a transition means for the semantics of state machines. Some questions that come to mind are:

  • Is the transition taken when the exception occurs?
  • Is it equivalent to a guard expression that evaluates to false and does the state machine have to consider other conflicting transitions with lower priority?
  • Is the event considered to be handled or should the state machine try to reprocess it after an exception handler has been invoked?
  • What about the side-effects of actions of transitions that have been executed before exception occurred?
  • What is the priority of the event that is generated for the exception if there is one?

The more I think about it, the more questions come up. Unfortunately, the UML specification and other descriptions of state machine notations do not consider exceptions. Even the Precise Semantics of UML State Machines do not consider them.

Do you have additional ideas how handle exceptions in state machines? What are the semantics of a transition if an exception occurs in its action?

3
  • 4
    Mixing exceptions into state machines is going to rot your brain cells. Ensure no exceptions are thrown from state transitions from any situation other than catastrophically crashing. Jun 1, 2017 at 20:00
  • @whatsisname Language runtimes like the JVM don't catastrophically crash. Even if you want to just crash, you have to coordinate this crash among multiple threads. But let's not diverge from my question here.
    – user274081
    Jun 3, 2017 at 17:36
  • You should read 14.2.3.9.1 The run-to-completion paradigm (p. 314 of the UML 2.5 specs). It's very detailed.
    – user188153
    Jul 22, 2017 at 21:29

4 Answers 4

9

State machines don't have exceptions, though they may have transitions to an error state.

There's no “one size fits all” answer for how you should handle errors. This depends a lot on the problem domain. Possibilities I consider useful:

  • If an exception occurs, this is interpreted as a transition to an error state. This error state may be terminal, i.e. aborts further event handling. This would be effectively equivalent to not handling the exception in your state machine but letting it bubble up to the user of the state machine. Such an approach seems sensible if the cause of the exception does not lie in the events, but in your implementation, i.e. the exception can't be handled by your model of the state machine.

  • The error state could also be non-terminal, and offer further transitions. This corresponds to a try-catch. Such an approach is particularly desirable when the state machine must not terminate. This is generally applicable if the cause of the error is in the sequence of events, and can therefore be modelled.

How this meshes with event handling depends. You can't really re-apply an event that led to an exception. It would usually be sensible to consume the event with the transition to an error state.

I'd also like to point out that state machines are often desirable because we can prove many properties about the state machine in advance, e.g that it must terminate. An out of memory error can often be avoided since the memory usage of a DFA is bounded. As such, you shouldn't expect to see any exceptions, and should model error cases that are to be expected as explicit state transitions: either the state machine has a bug, or the state machine will successfully handle the “language” of events that is submitted to the state machine.

3
  • I thought of checked vs. unchecked exceptions (like in Java). A checked exception is very much like an expected transition (what you're calling a "language" of events). Unchecked exceptions might be the generic error state you mention. Jul 14, 2017 at 14:48
  • Can a State Machine be used as a sort of "transaction" with Microservices to determine when "eventual consistency" has become consistency?
    – johnny
    Jul 14, 2017 at 17:12
  • 1
    @johnny Not really, because eventual consistency/microservices are about distributed systems. Distributed systems are complicated not because managing all possible states is difficult, but because there is no universally correct response to events like network connection loss, race conditions, or failure of one or more nodes. In practice, consensus algorithms like Paxos are used to decide which version of the data should be used. However, State Machine Replication is used as a theoretical framework to analyse these algorithms.
    – amon
    Jul 16, 2017 at 20:38
3

So state machines have to handle them somehow.

No they don't.

How an exception should be handled is outside of what state machines model. You've stripped all meaning away and left us with a pattern. You can't ignore context. Wether an exception should even be recovered from is not something a state diagram tells you.

There are many other considerations. Is the system still useable? Has the user lost data? Was input invalid?

There's a lot going on that has nothing to do with your state machine. Don't obsess on solving everything with it

6
  • As I explained, exceptions can occur in actions of transitions or states. If you implement the state machine as software, they have to be handled somehow. So I don't think that your answer is applicable. If you read the paper or look at the state machine implementation that I referenced, the problem that I'm having becomes perhaps more clear.
    – user274081
    Jun 1, 2017 at 19:50
  • 4
    @user6231651: you don't have to "handle them somehow." Unless you are writing code for a pacemaker or an interstellar probe, if you get an e.g. OutOfMemory exception, you're totally screwed, it's not worth even attempting to handle it. Don't use an exception throwing paradigm in your state transitions, and catch any relevant exceptions from the platform in the state transition. Anything left, just let it crash and burn. Jun 1, 2017 at 20:03
  • @user6231651: Power outages can also occur. How do you propose to handle those in a state machine running on hardware that just lost its power? Jun 2, 2017 at 11:12
  • @BartvanIngenSchenau The software deals with power outages in a way that the state machine does not have to be persistent. This was also not the question that I asked.
    – user274081
    Jun 3, 2017 at 17:23
  • @whatsisname Correct me if I'm wrong, but your comment does seem to be not relevant to my question. High-integrity software requirements usually do not permit exceptions, so your pacemaker or interstellar probe example does not make sense. I don't think that the software should just crash. Even exceptions that are not handled should be caught by an exception handler that notifies the user and possibly offers to send a crash report. So in order to do that, some peace of software has to catch the exception. Hence my question.
    – user274081
    Jun 3, 2017 at 17:29
3

It's going to depend on your implementation of the state machine, the language and the type of exception. I added the diagram to your question for an example:

enter image description here

If it were Java, and action() had a try for a checked exception, you could possibly handle things well knowing that the current state was s1 and you were in transitionName (to s2). Access to all these things depends on how you implement the state machine. UML doesn't address that.

Architecturally, you could think of nested states, and unchecked exceptions take you to some error state:

enter image description here

If you can handle the error, you decide the next state accordingly. This might be an explicit transition in the state diagram, or not (a human operator could decide). If you're using UML as a sketch, I don't see any problem using exceptions as transitions.

If you're interested in patterns to handle error states (not part of UML), I like the book by Hanmer

Some errors require very drastic action. The SOMEONE IN CHARGE (8) might have overseen a series of ESCALATION (9) steps, but none of the steps resolved the error. This is a truly persistent fault.

Restarting the application can resolve the error if the fault is in the application. If the fault is in the underlying hardware or operating system/ middleware layers then the application restart will not help.

Many techniques such as ROLLBACK (32) and ROLL-FORWARD (33) recover the application by moving it to an error free state along the normal execution path.

Hanmer, Robert. Patterns for Fault Tolerant Software (Wiley Software Patterns Series) (p. 151). Wiley. Kindle Edition.

2
  • I think that this answer addresses the actual challenge presented in the initial question. And, in fact, I believe that it's even possible to model the case of the initial question considering that it may not have to do with exceptions (code exceptions). Consider, for example, that an exception, in the context of a code, is an event that happed at a higher-order code that is influencing the system — the system will need to react. If so, that is part of the real application behavior and it should be in a way or another included in the state machine. Jan 28, 2021 at 14:08
  • As for the exception and transition. Here is a consideration. Let's think in a different way, that you are the developer that programmed the exception in first place. If so, what you trying to do is to inform some other piece of code that an error occurred. Or it may not be an error. The exception can also inform a desired value. If so, and if you actually treat the exception, you are free to coordinate new transitions to your machine; then I would believe that the transition may occur after the exception, just like a transition can occur after an user's dialog action. Jan 28, 2021 at 14:24
2

To answer this question, we have to recognize that exceptions, themselves, are something akin to a state transition. They take you to a state which is defined by unwinding the stack. In practice, that's all they are. They're a very non-local goto.

So when it comes to exceptions there's always two ways to handle them. One is to treat them as fatal. In this case, you admit that you don't know the state of your process (much less the state machine you're running), so you can no longer continue. Whether you are in a state machine or any other kind of code, fatal is fatal.

The other approach is to identify the exception, and determine the current state of the process based on the exception. Some APIs will provide semantics for these exceptions. A common one is to guarantee that the underlying system is in a consistent state (as defined by the API's modeling assumptions). Some will even specify that the result will be "as though a function call did not occur." These kinds of exceptions provide guarantees that you can use within your state machine.

With these exceptions, you can catch them and use them as transitions to "error" states because you know enough of the state of the process to understand what parts of your data structures are consistent and which parts aren't. That error state can then further explore the current state of the process to try to identify which state to transition to next.

The key is that you need to be able to pull information about the current state of your data and objects from the exception. If you don't extract this information, you have nothing to determine which invariants are still valid. Without those, you can't trust anything.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.