Files
windows_exporter/internal/mi/AGENTS.md
T
2026-10-10 23:15:42 +02:00

42 lines
2.3 KiB
Markdown

# MI package guidance
`Session.Query`, `QueryUnmarshal` and `QueryFunc` receive results through
`MI_OperationCallbacks` ([`callbacks.go`](callbacks.go)). Do not move them back
to the synchronous `MI_Operation_GetInstance` loop: when its result hand-over
waits, e.g. for parallel queries or slow providers such as `MSFT_StoragePool`,
the WMI client leaks Event handles (`miutils!RtlInterlockedCompareWait`).
`Test_MI_ParallelQuery_HandleGrowth` guards this. `QueryInstances` and
`Operation.GetInstance` remain as the synchronous low-level API for tests.
Rules for the callback path:
- Create native callbacks once (`sync.OnceValue`) and identify the query by
the callback context. `windows.NewCallback` never frees a callback and a
process can create only about 2000.
- Keep the callback context and the `MI_OperationCallbacks` pinned until
`MI_Operation_Close` has returned.
- Never call `Cancel` or `Close` from a callback. The goroutine that started
the query cancels on request and closes after the final callback
(`moreResults == MI_FALSE`).
- Recover panics in callbacks; they must not unwind into MI.
- Never block a callback on a goroutine. `QueryFunc` copies each instance
(`MI_Instance_Clone`) in the callback and runs `fn` for the copies on the
calling goroutine. Handing the original over and waiting for `fn` costs two
thread wake-ups per instance, about 25% more process CPU than the copy
(`Benchmark_MI_ProcessCPU`).
- MI reports parameter errors from within `MI_Session_QueryInstances`; the
runtime runs that callback on the calling goroutine. Never run caller code
such as the `QueryFunc` handler inside a callback: a panic cannot reach the
caller there and `runtime.Goexit` is fatal.
- While waiting for callbacks, keep a timer pending. Otherwise the runtime can
declare a false deadlock (golang/go#55015, still open in Go 1.27);
`Test_MI_Query_DeadlockDetector` reproduces it. Do not use a goroutine that
sleeps forever.
- Guard shared callback state with a mutex: results of one operation may
arrive on different MI threads. `-race` cannot reveal a missing lock there,
because the runtime treats every callback as synchronized with every native
call.
Run `CGO_ENABLED=1 go test -race` (gcc from w64devkit) and a
`-gcflags=all=-d=checkptr` run for changes in this package.