Skip to content

watchDog: check less often, and not at all when nothing would act - #204

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

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

Conversation

@lhoward

@lhoward lhoward commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

The watchdog thread woke every 10 msec, and on every pass called utils::isDebuggerPresent() for every registered watch, which on Linux opens and reads /proc/self/status. With a pcap protocol interface and its state machines running, that was about 100 reads of that file a second, and as many wakeups, to enforce timeouts of 500 msec or more.

  • Less often: watches are checked every 100 msec, and the debugger check runs at most once a second.
  • Shutdown: the thread waits on a condition variable, so destroying the watchdog doesn't wait out the interval, and _shouldTerminate is only read and written under the lock.
  • Nothing to act: when neither DEBUG nor COMPILE_AVDECC_ASSERT is defined, a missed watch is only reported to observers, so with none registered the thread doesn't check at all. It waits until an observer registers or the watchdog is destroyed. registerObserver wakes it without taking the lock, so that an observer can still register from within a notification, and the idle thread looks again every 10 seconds in case it missed that wakeup. With an observer registered, checking resumes, debugger check included.
  • SonarCloud: checking the watches moved into checkWatches and checkWatch to reduce the nesting of the thread's loop, and the destructor uses std::scoped_lock.

On an embedded Linux (aarch64) device, in a release build, the watchdog thread went from about 90 context switches a second, and 100 reads of /proc/self/status, to fewer than 0.2 switches and no reads.

This overlaps #203 in src/watchDog.cpp and CHANGELOG-avdecc.md, so whichever is merged second needs a small rebase.

Testing: builds on dev, and together with the thread name and state machine changes in #203 and #205, 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.

🤖 Generated with Claude Code

https://claude.ai/code/session_012i5Dd3gCyYqp4X7vxhSmxh

The watchdog woke every 10 msec, and on every pass asked for each watch
whether a debugger is attached, which on Linux opens and reads
/proc/self/status. With the pcap interface's state machines running,
that was about 100 opens and reads of that file a second, and as many
wakeups, to enforce timeouts of 500 msec and more.

Check the watches every 100 msec, look for a debugger at most once a
second, and wait on a condition variable so that shutting down doesn't
have to wait out the interval. The terminate flag is now only read and
written under the lock.

Without DEBUG or COMPILE_AVDECC_ASSERT defined, a missed watch doesn't
assert and is only reported to observers, so with none registered there
is nothing to check: the thread then waits until an observer registers
or the watchdog terminates. registerObserver wakes the thread without
taking the lock, so that an observer can still register from within a
notification, and the idle thread looks again every 10 seconds in case
it missed that wakeup. Once an observer is registered, checking resumes,
debugger check included, so a process stopped in a debugger still
doesn't read as a deadlock.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012i5Dd3gCyYqp4X7vxhSmxh
@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.

1 participant