U-Boot Event Spies: A Static Handler Error Can Hide an Event from Dynamic Observers
A dynamic event handler can register successfully and still receive
no notification when an event is emitted. U-Boot v2025.01 dispatches
static handlers first and stops at the first nonzero result. A failing
static handler prevents the dynamic phase from starting.
The event mechanism is synchronous, so an observer is part of the
emitting call’s control flow. It is not an independent queued telemetry
consumer guaranteed to hear about every attempted event. This
distinction matters when a diagnostic hook is added to explain an
initialization failure.
Trace static
dispatch before dynamic dispatch
The event
guide describes static spies, optional event data and dynamically
registered handlers. It states that dynamic handlers run after static
spies and that handler errors return to the emitter.
The dispatcher
implementation makes the short-circuit behavior explicit.
event_notify() constructs the event and calls
notify_static(). If that returns a nonzero value,
notification returns immediately. Only a successful static pass reaches
the dynamic phase when dynamic events are enabled.
Within either phase, the first matching handler that returns nonzero
stops the loop. Later handlers in that same phase are not called. A
return value intended by a developer as “I handled this” is therefore
not a harmless acknowledgement in this version; nonzero is treated as an
error result.

Missing
observation is different from missing emission
Suppose an emitter reaches event_notify(), static spy A
runs, and A returns an error before dynamic observer B is reached. B’s
empty diagnostic record does not prove the emitter skipped the event. It
can instead be evidence that dispatch stopped earlier.
Instrument the boundary before notification and record its return
independently of the observer being investigated. Then trace which
handler ran last. This avoids using a downstream observer as the sole
witness for a path that can fail before reaching it.
The source contains comments describing possible future distinctions
between claiming an event and reporting an error. Those comments are not
implemented outcome classes. For this tagged version, the inspected
dispatch loops use the nonzero-error behavior directly.
Do
not infer a dependency order from registration folklore
Static handlers are traversed through the linked spy list. Dynamic
handlers are appended to their runtime list. Those are different
ordering mechanisms; source-file order or numeric event type is not a
general dependency scheduler.
If one subsystem requires another to finish first, make that
dependency explicit in the design or establish the exact supported
ordering contract. Merely adding another observer cannot guarantee that
it sees a state which an earlier failing handler never produced.
Also separate the two context models. A full static spy receives a
null context pointer, while a dynamic handler receives the context
supplied at registration. A simple static spy receives no event
arguments. Choose the interface appropriate to the information the
handler actually needs.
Check visibility and
lifetime boundaries
The command-side spy listing and executable-inspection tooling
described by the guide cover static spies. The implementation’s listing
function walks the static linker list. Absence there is not proof that
no dynamic handler was registered; dynamic registrations need runtime
evidence.
Notification uses a local event object and copies the supplied
payload bytes into it before calling handlers. Do not retain a pointer
to that event object after the callback returns. If a payload contains
pointers, copying the payload does not independently extend the lifetime
of the pointed-to objects.
A proposed fixture can register one static and one dynamic handler,
then vary whether the static handler returns zero or a controlled error.
Add a second handler in each phase to verify the stopping boundary.
Record emitter entry, individual handler entry and the notification
result as separate observations.
No handlers were registered or events emitted for this analysis. The
actionable conclusion is that event delivery is a synchronous, fail-fast
chain. Registration proves membership, while actual receipt depends on
every earlier relevant handler allowing dispatch to continue.