Skip to content

fix-medium: re-parse LinkedMonitors on a monitor reload - #5276

Open
nabbi wants to merge 1 commit into
ZoneMinder:masterfrom
nabbi:fix/b84-reload-linked-monitors
Open

nabbi wants to merge 1 commit into
ZoneMinder:masterfrom
nabbi:fix/b84-reload-linked-monitors

Conversation

@nabbi

@nabbi nabbi commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

Impact: fix-medium (after zmu -L, a changed LinkedMonitors is ignored until the next reconnect)

Automated PR, tested in a zm-holodeck sandbox. An AI agent (Claude Opus 5.5) reproduced
the bug on a disposable ZoneMinder built from upstream master 233a15828 with
simulated cameras, wrote this fix and ran the checks below on the same lab. Minimal
human review before opening: please review it as you would any contributor's PR.

TL;DR

  • Fixes: zmc keeps using the old LinkedMonitors after zmu -L until it reconnects #5275: after zmu -m N -L, zmc keeps linking to the old LinkedMonitors until it reconnects.
  • Change: Monitor::Reload() calls ReloadLinkedMonitors() after re-reading the row, under event_mutex; 1 file, +9/-0
  • Risk: Reload() runs on zmc's main thread while the analysis thread evaluates linked_monitors; Analyse() holds event_mutex for that whole section (zm_monitor.cpp:2120-2694), so the swap takes the same lock. Rebuilding queries the database once per linked monitor while holding it.
  • Tested: reproduced before, passes after; the quick check set green
Details: reproduction, change, checks

Before

Upstream master 233a15828, clean build. Monitor 2 linked to 3, then LinkedMonitors set to 4
and zmu -m 2 -L; monitors 4 and 3 force-alarmed in turn:

"head":"233a1582829edc49554a0f1a9bc36d61dd951aaf","branch":"master","dirty":false,
10/12/26 21:21:11.437886 zmc_m2[582].INF-zm_monitor.cpp/1938 [Received reload indication at count 435]
  after forced alarm on monitor 4: monitor 2 Linked events=0; zmc_m2 scored:     348 Score from monitor hikvision-1 is 0;
  after forced alarm on monitor 3: monitor 2 Linked events=1; zmc_m2 scored:     138 Score from monitor hikvision-1 is 0;     22 Score from monitor hikvision-1 is 1;    188 Score from monitor hikvision-1 is 255;

(hikvision-1 is monitor 3.) Reload() replaces linked_monitors_string through Load() but
the parsed expression is only rebuilt in connect().

Change

  • src/zm_monitor.cpp (Monitor::Reload): call ReloadLinkedMonitors() after Load(), inside
    a std::lock_guard on event_mutex, which Analyse() holds while it reads linked_monitors.
    The SIGHUP path (zmc.cpp:449-455) also calls Reload(), after disconnect(); there the
    expression is built once more and replaced again by connect(), which costs one query per
    linked monitor and changes nothing else.
  • No unit test: Reload() needs a monitor row and shared memory; the reload is covered by the
    live probe above.

Behaviour changes

Change Who sees it Intended / collateral Alternatives looked into Chosen, why Tested by
A changed LinkedMonitors applies on zmu -L instead of on the next reconnect users of zmu -L with linked monitors intended build the new expression outside the lock and swap the pointer under it: shorter hold, larger change to ReloadLinkedMonitors rebuild under the lock; reload is rare and the hold is a few queries probe: alarms on 4 link, on 3 don't
The analysis thread waits on event_mutex while the expression is rebuilt zmc of a monitor with linked monitors, during a reload collateral as above as above check --quick events (linked, reload)

Side fixes in this PR: none.

Relation to #5272 (B81): independent and merges cleanly. On master, between Load() replacing
the string and this rebuild, the old tokens still view the replaced string; #5272 gives the
expression its own copy, which closes that window. With both, the old expression is never read
after its string changes.

After

Branch fix/b84-reload-linked-monitors at a5892c71e (base 233a15828):

"head":"a5892c71e1276ee2ae0bce0da187bf7e1e497129","branch":"fix/b84-reload-linked-monitors","dirty":false,
10/12/26 21:26:59.843842 zmc_m2[571].DB1-zm_monitor.cpp/2752 [Reloading linked monitors for monitor dahua-1, '4']
  after forced alarm on monitor 4: monitor 2 Linked events=1; zmc_m2 scored:     146 Score from monitor onvif-1 is 0;     24 Score from monitor onvif-1 is 1;    186 Score from monitor onvif-1 is 255;
  after forced alarm on monitor 3: monitor 2 Linked events=0; zmc_m2 scored:     356 Score from monitor onvif-1 is 0;
Check Result
build with -DENABLE_WERROR=ON (gcc, Debian trixie) builds
holodeck check --quick (one run on a throwaway branch, 31b577409, merging this with the two other fixes from the same lab session: bad LinkedMonitors tokens, and the zms zoom crop) unit-tests ok (All tests passed (14796 assertions in 260 test cases)), script-tests PASS=32, smoke PASS=11 XPASS=1 (stock zmonvif-probe.pl discovery), resilience PASS=5, events PASS=10 (incl. linked, reload), dblog PASS=1 XFAIL=2 (#5197, known), zmscmd PASS=4 XFAIL=1 XPASS=3 (known entries for master c3d2e54; speed XFAIL known), clients: pyzmNg 61 passed, ha-zoneminder 20 passed (known-flaky test passed on re-run), zmNinjaNg passed=39
pyzmNg / ha-zoneminder / zmNinjaNg not run: no API, web or stream change

Not run: macOS clang build; TSan/ASan runs of the reload. Backport to release-1.38: same bug and
same locking in Analyse(); the cherry-pick conflicts only on context (master's LoadActions()
call in Reload()), the same block goes after Load().

Tested as bce169449; a5892c71e is the same tree with fixes #5275 added to the message.

fixes #5275

🤖 Generated with Claude Code

Monitor::Reload() (the RELOAD action `zmu -m N -L` sends) re-reads the
monitor row, which replaces linked_monitors_string, but the
MonitorLinkExpression built from it is only rebuilt in connect(). The
old links stayed in use until zmc reconnected: after changing
LinkedMonitors from 3 to 4 and reloading, zmc kept scoring monitor 3
and ignored alarms on 4.

Rebuild the expression in Reload(), under event_mutex, which Analyse()
holds while it evaluates linked_monitors.

fixes ZoneMinder#5275

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@nabbi nabbi changed the title fix: re-parse LinkedMonitors on a monitor reload fix-medium: re-parse LinkedMonitors on a monitor reload Oct 12, 2026

This branch has not been deployed

No deployments
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.

zmc keeps using the old LinkedMonitors after zmu -L until it reconnects

1 participant