internal/agent/reconcile/managerhelpers_test.go
Ref: Size: 1.8 KiB History
package reconcile
// Test-only introspection for the per-VM worker manager. Neither runs in
// production — the agent never counts its workers, and it never waits on them
// (not blocking on workers is the whole point of this layer). They live in the
// test build so the production package has no unreachable methods, while tests
// can observe a tick's work before asserting on it.
// count reports the number of live workers (one per VM this host is tracking).
func (m *manager) count() int {
m.mu.Lock()
defer m.mu.Unlock()
return len(m.workers)
}
// waitIdle blocks until every worker has consumed its pending assignment and
// finished the resulting pass, so a test can assert on a tick's work.
func (m *manager) waitIdle() {
for _, w := range m.snapshot() {
w.mu.Lock()
// !w.stopped is an escape, not a nicety: a worker stopped while an
// assignment was still pending returns from run without ever clearing
// pending, and this would otherwise wait on it forever.
for (w.pending != nil || w.busy) && !w.stopped {
w.cond.Wait()
}
w.mu.Unlock()
}
}
// Stop shuts down every per-VM worker. It signals and returns without waiting: a
// worker mid-pass exits when that pass ends (bounded by VMTimeout).
//
// TERMINAL: an Engine cannot be restarted, and after Stop, Step dispatches
// nothing and reports empty actual state — which the control plane reads as every
// VM on this host having vanished. Production therefore never calls it
// (cmd/eitri-agent lets the process exit and the OS reclaim the goroutines); it
// exists only so a test's cleanup can tear workers down between cases.
func (e *Engine) Stop() {
m := e.manager()
m.mu.Lock()
defer m.mu.Unlock()
m.stopped = true
for id, w := range m.workers {
delete(m.workers, id)
w.stop()
}
}