Statecharts · Problem 4
P4 · TCP, Flat and Then Not
The most widely implemented state diagram in the world, transcribed literally and then refactored into a hierarchy — with a nineteen-arrow acceptance table that is not allowed to move. 68 tests.
The system
The TCP connection state machine, exactly as
RFC 9293 §3.3.2
draws it (Figure 5; unchanged in substance from RFC 793 in 1981). Eleven states, one of them
— CLOSED — described in the RFC as “fictional”. No hierarchy, no
regions, no history. It is the most widely implemented state diagram in the world and it is completely
flat.
That is the point of the problem. Hierarchy is not a thing you apply to greenfield drawings; it is mostly a thing you apply to a flat machine that already works, without changing what it does. So: build it flat, get a table of nineteen transitions green, then refactor — and the table does not move.
Part A — transcribe it
Open Figure 5 and read it arrow by arrow. Two details repay attention:
SYN-RECEIVED → LISTEN on RST happens
only if SYN-RECEIVED was reached from a passive OPEN. Otherwise there is no
LISTEN to fall back to. Two transitions on one event, distinguished by a guard over a
flag set at OPEN time — your first piece of context.SYN-SENT → SYN-RECEIVED and FIN-WAIT-1 → CLOSING are the
arrows people leave out. They are what CLOSING exists for.Part B — group it
Now find the boxes. The eleven RFC names must survive unchanged — they are the protocol, and renaming them would make your implementation unreadable next to the spec. What changes is what contains them.
LISTEN, SYN-SENT, SYN-RECEIVED), and the states where it is being torn down (the six FIN/ACK/TIME-WAIT states). CLOSED and ESTABLISHED stay alone — they are the two stable points.abort — RST, or the user's ABORT call — reaches CLOSED from every state. Flat, that is ten arrows. Grouped, it is three. One of the tests asserts both numbers, and a twelfth state inside either group would not change the three.sm.disconnecting.is_active. Ancestors are in the configuration, so grouping gives you predicates for free — and predicates that stay correct when states are added.Build it
exercises/p4_tcp/machine.py —
the skeleton. Its docstring is the contract: exact state ids, event names, and context attributes.exercises/p4_tcp/test_machine.py —
68 acceptance tests. Do not edit them.exercises/p4_tcp/_solution.py —
a reference solution with commentary. Open it after yours is green, not before.cd statecharts/exercises/p4_tcp pytest -q
The suite is parametrised over both classes and the full
transition table, which is why there are 68 tests for 19 arrows. Part A is done when the
TCPFlat half is green; Part B when the TCPNested half is too, with the table
untouched.
SEND, but
send is the method you deliver events with. Event names become attributes on the chart,
so declaring send = listen.to(syn_sent) shadows it and every subsequent
sm.send(...) stops working, with no error. The contract calls it
user_send.
When it is green
Ask the honest question: is the grouped version better? It is shorter and it answers “is this connection being torn down?” directly. It is also one more level of indirection between a reader and Figure 5, which is the document they will actually have open. The RFC authors chose flat and they were not wrong to. Hierarchy buys you shared transitions and shared predicates; where there are few of either, it buys nothing and costs a level.
Sources: RFC 9293 §3.3.2 · RFC 793 · RESOURCES.md