Orthogonal States in Statecharts – Modeling Parallel Behavior and Concurrency
In real-world systems, things often happen at the same time — or at least, independently. For example, a washing machine might be heating water while spinning the drum. A robot might be tracking its position while checking for obstacles.
To model this kind of behavior, statecharts support orthogonal states — a way to express concurrent behavior within a single statechart.
What Are Orthogonal States?
An orthogonal state is essentially a composite state that contains multiple regions. Each region can have its own set of substates, transitions, and logic. These regions are executed virtually in parallel.
Important: Orthogonal states are not true parallel execution.
They are evaluated sequentially, one after another, during each execution cycle.
This design is intentional. Orthogonality is about modeling logical concurrency, not multithreading. You don’t have to deal with synchronization, race conditions, or thread safety — you just model separate, independent behaviors that run side-by-side from a conceptual point of view.
Why Use Orthogonal States?
Orthogonal states are ideal when:
- You want to model independent modes or components within a system
- You have multiple subsystems that operate in parallel
- You want to avoid tangled logic by splitting responsibilities across regions
Examples:
- In a smart home system, one region controls the fan, another handles the temperature.
- In a vehicle, one region manages cruise control, another handles lane keeping.
Each part has its own logic, but they run together as long as the parent orthogonal state is active.
Example: Smart Home Temperature and Fan Control
This statechart represents a system with two independent control mechanisms: one for temperature and one for fan operation. Each control mechanism can toggle between two states based on specific events (toggleTemp and toggleFan). The use of orthogonal regions allows the temperature and fan controls to operate independently, reflecting a design where both systems can be managed concurrently without interference.
How Execution Works
When an orthogonal state is entered:
- All of its regions are entered simultaneously (logically speaking)
- Each region activates its own initial substate
During each execution cycle:
- All regions are evaluated in a defined order (typically top-to-bottom, left-to-right)
- Transitions in one region do not block or delay transitions in other regions
This allows multiple independent state machines to share a synchronized clock, yet evolve independently.
When an orthogonal state is exited:
- All active substates in all regions are exited
Join and Fork: Coordinating Orthogonal Regions
For more sophisticated coordination between orthogonal regions, statecharts provide join and fork nodes. These special synchronization constructs allow you to:
- Split execution into multiple parallel paths (fork)
- Synchronize and merge multiple parallel paths back together (join)
Fork Nodes
A fork node (also called a fork bar) splits a single transition into multiple parallel transitions, activating several regions simultaneously. It’s represented by a thick black bar with one incoming transition and multiple outgoing transitions.
Fork nodes are useful when:
- You need to start multiple parallel activities at once
- An event should trigger several independent processes
- You want to coordinate the start of orthogonal regions
Join Nodes
A join node (also called a join bar) waits for multiple parallel regions to reach specific states before proceeding. It’s represented by a thick black bar with multiple incoming transitions and one outgoing transition.
Join nodes are useful when:
- You need to wait for multiple parallel activities to complete
- Several conditions must be met before proceeding
- You want to synchronize the end of orthogonal regions
Example: Embedded System Initialization
Consider an embedded IoT device that must initialize two subsystems before it becomes operational:
The fork ensures all initialization processes start simultaneously when the device powers on. The join ensures the system only transitions to “Operational” state when all subsystems have successfully initialized.
This pattern is common in embedded systems where:
- Network connectivity, sensor calibration, and security setup can happen in parallel
- The system must not accept user commands until all subsystems are operational
- Failure in any subsystem should prevent the device from becoming ready
Execution Semantics
- Fork execution: When a fork is triggered, all outgoing transitions are taken simultaneously, activating their target states in parallel
- Join execution: A join transition is only enabled when all incoming transitions are enabled (i.e., all prerequisite states are active)
This provides deterministic synchronization without the complexity of traditional threading models.
Summary
Orthogonal states are a powerful tool for modeling concurrent, but independent behaviors within a single system.
They help you:
- Keep related concerns separated
- Model multiple control flows cleanly
- Avoid complex nesting by distributing logic across parallel regions
- Coordinate parallel activities using join and fork synchronization
Advanced synchronization with join and fork nodes enables you to:
- Split single transitions into multiple parallel paths
- Synchronize multiple parallel activities before proceeding
- Create deterministic coordination patterns without threading complexity
Just remember: orthogonality is not true parallelism. It’s a way to think about things happening side by side — while still executing in a defined, sequential order.
In the next chapters, we’ll look at more advanced features like history states, choice nodes, and final states, which add even more power to your modeling toolbox.
Frequently Asked Questions
What are orthogonal states?
Composite states with multiple regions that execute logically in parallel. Each region has its own substates and transitions.
Is this true parallel execution?
No. Execution is sequential per cycle. Orthogonality models logical concurrency without threads or synchronization primitives.
When should I use orthogonal states?
When modeling independent subsystems that operate side-by-side, such as fan and temperature control in a smart home.
How do regions coordinate?
Use events, shared variables, or synchronization mechanisms like join and fork nodes to communicate between regions as needed.
What are join and fork nodes?
Fork nodes split execution into multiple parallel paths, while join nodes synchronize multiple parallel paths back together before proceeding.
When should I use join/fork?
Use fork to start multiple parallel activities simultaneously, and join to wait for multiple parallel activities to complete before proceeding.