plugin: watchdog must assume a reset write may have just happened after restart using defer (#31635)

This commit is contained in:
Ingo 2026-07-10 17:01:22 +02:00 • committed by GitHub
parent c52b4a79ea
commit 623427e728
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 16 additions and 7 deletions

View file

@ -79,7 +79,9 @@ type deferredState[T comparable] struct {
// it is currently not possible to write this as a method
func setter[T comparable](o *watchdogPlugin, set func(T) error, reset []T) func(T) error {
var state *deferredState[T]
var lastUpdated time.Time
// seed with now, not zero: otherwise the first write's delay computes to 0 and skips
// deferral, which is wrong for an unknown last write
lastUpdated := o.clock.Now()
var last *T
// stop running wdt
@ -144,7 +146,7 @@ func setter[T comparable](o *watchdogPlugin, set func(T) error, reset []T) func(
delay := max(0, o.timeout+5*time.Second-o.clock.Since(lastUpdated))
// defer update to non-reset value
if o.deferred && delay > 0 && !lastUpdated.IsZero() && !slices.Contains(reset, val) {
if o.deferred && delay > 0 && !slices.Contains(reset, val) {
stopWdt()
// store deferred value

View file

@ -113,26 +113,33 @@ func TestWatchdogCancelPendingDeferredUpdate(t *testing.T) {
return nil
}, []int{1}) // 1 is reset value
// Value 3 (non-reset)
// Value 1 (reset) → establishes a known write time so the subsequent non-reset
// writes below are deferred based on that, not the fresh-setter startup assumption
require.NoError(t, set(1))
require.Equal(t, []int{1}, calls)
// Value 3 (non-reset) → deferred (no time has passed since the reset write)
require.NoError(t, set(3))
require.Equal(t, []int{3}, calls)
require.Equal(t, []int{1}, calls, "Value 3 should not be set yet")
c.Add(timeout + 5*time.Second)
require.Equal(t, []int{1, 3}, calls)
// Value 2 (deferred update)
require.NoError(t, set(2))
require.Equal(t, []int{3}, calls, "Value 2 should not be set yet")
require.Equal(t, []int{1, 3}, calls, "Value 2 should not be set yet")
// Wait a bit but not the full delay
c.Add(30 * time.Second)
// Value 1 (reset) → should cancel pending deferred update and set immediately
require.NoError(t, set(1))
require.Equal(t, []int{3, 1}, calls, "Value 1 should be set, Value 2 should be cancelled")
require.Equal(t, []int{1, 3, 1}, calls, "Value 1 should be set, Value 2 should be cancelled")
// Wait for what would have been the original delay
c.Add(timeout + 5*time.Second)
// Value 2 should still not have been set
require.Equal(t, []int{3, 1}, calls, "Value 2 should remain cancelled")
require.Equal(t, []int{1, 3, 1}, calls, "Value 2 should remain cancelled")
}
func TestWatchdogDelayBackwardCompatibility(t *testing.T) {