diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Memory.pm.in b/scripts/ZoneMinder/lib/ZoneMinder/Memory.pm.in index 27a084e21..5eb24f389 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Memory.pm.in +++ b/scripts/ZoneMinder/lib/ZoneMinder/Memory.pm.in @@ -605,15 +605,18 @@ sub zmTriggerEventOn { my $text = shift; my $showtext = shift; + # Write score/cause/text before trigger_state so the analysis process always + # reads complete trigger data when it sees TRIGGER_ON. Perl hash iteration + # order is non-deterministic, so trigger_state must be written in a separate + # call that executes after all data fields are already in shared memory. my $values = { 'trigger_data:trigger_score' => $score, 'trigger_data:trigger_cause' => $cause, }; $values->{'trigger_data:trigger_text'} = $text if defined($text); $values->{'trigger_data:trigger_showtext'} = $showtext if defined($showtext); - $values->{'trigger_data:trigger_state'} = TRIGGER_ON; # Write state last so event not read incomplete - zmMemWrite($monitor, $values); + zmMemWrite($monitor, {'trigger_data:trigger_state' => TRIGGER_ON}); # Write state last so event not read incomplete } sub zmTriggerEventOff { diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 27591b0d1..7fe6af380 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -1452,10 +1452,12 @@ void Monitor::UpdateAdaptiveSkip() { } void Monitor::ForceAlarmOn( int force_score, const char *force_cause, const char *force_text ) { - trigger_data->trigger_state = TriggerState::TRIGGER_ON; + // Write score/cause/text before trigger_state so the analysis thread always + // reads complete trigger data when it observes TRIGGER_ON. trigger_data->trigger_score = force_score; strncpy(trigger_data->trigger_cause, force_cause, sizeof(trigger_data->trigger_cause)-1); strncpy(trigger_data->trigger_text, force_text, sizeof(trigger_data->trigger_text)-1); + trigger_data->trigger_state = TriggerState::TRIGGER_ON; } void Monitor::ForceAlarmOff() {