Skip to content

fix: clear sub-SM defer queue when composite state is re-entered (#253)#705

Merged
kris-jusiak merged 2 commits into
boost-ext:masterfrom
PavelGuzenfeld:fix/issue-253-defer-clear-on-exit
May 30, 2026
Merged

fix: clear sub-SM defer queue when composite state is re-entered (#253)#705
kris-jusiak merged 2 commits into
boost-ext:masterfrom
PavelGuzenfeld:fix/issue-253-defer-clear-on-exit

Conversation

@PavelGuzenfeld
Copy link
Copy Markdown
Contributor

@PavelGuzenfeld PavelGuzenfeld commented May 29, 2026

Commit 1 — fix: clear sub-SM defer queue on composite re-entry (#253)

Root cause: Each sm_impl holds its own defer_ queue (same policy as the root, propagated via TPolicies). process_defer_events replays events from this queue on the next internal transition inside the sub-SM. update_composite_states resets current_state_[] via initialize() when a composite is entered, but never cleared defer_. Events deferred in a prior session fired spuriously on the first internal transition of the next session.

Fix: Added clear_deferred_events() to sm_impl, tag-dispatched on is_same<defer_t, no_policy> for C++14 compatibility. Called in both update_composite_states overloads (history and non-history) so the defer queue is always emptied when a composite state is (re-)entered.

Tests: test/ft/issue_253_defer_clear_on_exit.cpp — 3 cases: stale event must not fire after exit + re-entry + round-trip; normal deferral still works within a session; no spurious fires without prior deferral.


Commit 2 — feat: add sml::clear_defer action (#643)

Exposes the new clear_deferred_events() infrastructure as a user-accessible action object (sml::clear_defer, alongside sml::defer and sml::process). Lets users explicitly discard pending deferred events on a specific outgoing transition:

state<Nested> + event<EXIT> / clear_defer = "Init"_s

Tests: test/ft/issue_643_clear_defer_action.cpp — user-controlled clear discards stale deferred events; no-op on empty queue.


Verified: GCC 14 + Clang 18, C++14/17/20 (39–40/40 tests pass). MSVC 19.51 C++20 clean.

boost-ext#253)

Deferred events enqueued inside a sub-SM's lifetime were silently
retained across exit.  On re-entry the sub-SM's defer_ queue was not
cleared by update_composite_states, so any event deferred in a prior
session would fire spuriously on the next internal transition that
triggers process_defer_events.

Root cause: each sm_impl holds its own defer_ (same policy as the root,
propagated through TPolicies).  update_composite_states resets
current_state_[] via initialize() but never touched defer_.

Fix: add clear_deferred_events() to sm_impl, tag-dispatched on
is_same<defer_t, no_policy> so it compiles for SMs without a
defer_queue policy. Call it in both overloads of update_composite_states
(history and non-history) so the defer queue is always emptied when a
composite state is (re-)entered.
…ransition (boost-ext#643)

Users working with sub-SMs sometimes need explicit control over the defer
queue — for example, to discard stale events when exiting a sub-SM via
a specific transition rather than relying on automatic cleanup.

Adds front::actions::clear_defer (exposed as the sml::clear_defer global
action object, alongside sml::defer and sml::process), which calls
sm_impl::clear_deferred_events() on the current SM when the action fires.

Usage:
    state<Nested> + event<EXIT> / clear_defer = "Init"_s

Verified GCC/Clang C++14/17/20 and MSVC 19.51 C++20.
@PavelGuzenfeld PavelGuzenfeld force-pushed the fix/issue-253-defer-clear-on-exit branch from 2716c08 to 8947e1f Compare May 30, 2026 11:01
@kris-jusiak kris-jusiak merged commit 0d430f8 into boost-ext:master May 30, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants