Skip to content

stateMachine: wait until commands need checking, instead of polling every 5 msec - #205

Draft
lhoward wants to merge 1 commit into
L-Acoustics:devfrom
PADL:lhoward/statemachine-polling
Draft

lhoward wants to merge 1 commit into
L-Acoustics:devfrom
PADL:lhoward/statemachine-polling

Conversation

@lhoward

@lhoward lhoward commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

The state machine thread wakes every 5 msec, 200 times a second, even when there's nothing to do. It doesn't stand between a command and the network: sendAecpCommand and sendAcmpCommand send a command at once when a slot is free, and a response releases the next queued command straight away. The thread only has to time commands out, retry them, and send a queued command once the send interval allows. Advertising, discovery and remote entity timeouts are all hundreds of milliseconds or more.

  • Waiting: the thread waits until CommandStateMachine::getNextCheckTime(), the earliest inflight command timeout or time a queued command may be sent, or now if errors are waiting to be reported. It waits no longer than 250 msec, which is soon enough for advertising, discovery and remote entity timeouts, and for the watchdog.
  • Waking: whenever a command goes inflight, fails to be sent, or has to wait for the send interval, the command state machine calls Manager::scheduleStateMachinesCheck() with the time it needs checking. The manager wakes the thread if that is sooner than it is waiting for.
  • Stopping: stopStateMachines wakes the thread, so it stops at once.

So the thread never delays a command, a queued command is sent as soon as the send interval has elapsed rather than on the next 5 msec tick, and an idle thread wakes 4 times a second instead of 200.

Testing: builds on dev, and together with the thread name and watchdog changes in #203 and #204, the unit tests give the same results as dev without them: 289 of 292 pass. INTEGRATION_EndStation.DestroyWhileMessageInflight_pcap and INTEGRATION_ProtocolInterfacePCap_F.VuDelegate fail either way, as they need pcap access to a real interface. The run took 31.7 s, against 32.2 s for dev.

🤖 Generated with Claude Code

https://claude.ai/code/session_012i5Dd3gCyYqp4X7vxhSmxh

@christophe-calmejane

Copy link
Copy Markdown
Collaborator

Hi Luke.
Thanks for looking into this. The library was initially designed to run on a computer which can easily handle a lot of context switches ; it's good to have it run on a low-resources device.
That being said, we'll have to find a clever way to optimize both behaviors as I fear 50msec delay might be a bit too long for sending a command. Some manufacturers are using avdecc CONTROLS to set the gain of the inputs/outputs and it may be critical to have as little delay as possible to send such command. One solution could be to add a compilation option, or ask claude (with that new condition) what brilliant idea he might have.

…very 5 msec

The state machine thread woke every 5 msec, 200 times a second, even with
nothing to do. A command is sent at once when it can be, by
sendAecpCommand/sendAcmpCommand or when a response frees a slot, so the
thread only has to time commands out, retry them, and send a queued
command once the send interval allows. Advertising, discovery and remote
entity timeouts are hundreds of milliseconds or more.

The thread now waits until CommandStateMachine::getNextCheckTime(), the
earliest inflight command timeout or time a queued command may be sent,
but no longer than 250 msec, which is soon enough for advertising,
discovery and remote entity timeouts, and for the watchdog. Whenever a
command goes inflight, fails to be sent, or has to wait for the send
interval, the command state machine tells the manager when it needs
checking, and the manager wakes the thread if that is sooner than it
waits for. So the thread never delays a command, a queued command is
sent as soon as the send interval has elapsed rather than on the next
5 msec tick, and an idle thread wakes 4 times a second.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012i5Dd3gCyYqp4X7vxhSmxh
@lhoward
lhoward force-pushed the lhoward/statemachine-polling branch from b6d53da to 40878d8 Compare September 14, 2026 20:46
@lhoward lhoward changed the title stateMachine: poll every 5 msec only while commands are pending stateMachine: wait until commands need checking, instead of polling every 5 msec Sep 14, 2026
@lhoward

lhoward commented Sep 14, 2026

Copy link
Copy Markdown
Contributor Author

Thanks Christophe, that's a fair concern, and it led to a better approach. Looking more closely, the thread doesn't sit between a command and the network: sendAecpCommand and sendAcmpCommand send immediately when a slot is free, and a response releases the next queued command straight away. The thread times commands out, retries them, and releases queued commands once the 1 msec send interval has passed, so today a queued burst goes out on the 5 msec ticks.

So there's no need for a compile option. The thread now waits until CommandStateMachine::getNextCheckTime(), the earliest in-flight timeout or the time a queued command may be sent, capped at 250 msec for advertising, discovery, remote entity timeouts and the watchdog. Whenever a command goes in flight, fails to send, or has to wait for the send interval, the command state machine tells the manager, which wakes the thread if that's sooner. Commands are never delayed by the thread, queued ones go out as soon as the interval allows (sooner than before), and an idle thread wakes 4 times a second instead of 200. The unit tests give the same results as dev, in the same time. Happy to adjust if you'd prefer a different trade-off.

@sonarqubecloud

Copy link
Copy Markdown

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