instantout/reservation: honor context when queueing request

RequestReservationFromServer dispatched the OnClientInitialized event to
the manager's Run loop via a bare 'm.reqChan <- ...' send. reqChan is an
unbuffered channel; if Run had already returned (e.g. because the block
epoch subscription errored, or the manager is shutting down) the send
would block forever, holding the gRPC handler goroutine and the caller's
connection open until something external killed it.

Wrap the send in a select that also watches the caller's context. A
cancelled caller context now returns ctx.Err() instead of hanging.

Note: this still does not detect "Run exited cleanly while reqChan was
empty" -- doing that requires exposing Run's runCtx (or a quit channel)
on the Manager struct. That refactor is left for a follow-up; the
caller-side cancel path above is enough to keep RPC handlers from
leaking when their grpc deadline fires.
This commit is contained in:
Slyghtning 2026-05-11 15:33:37 +02:00
parent eb58fcaa03
commit 800dae0750
No known key found for this signature in database
GPG key ID: F82D456EA023C9BF

View file

@ -263,11 +263,18 @@ func (m *Manager) RequestReservationFromServer(ctx context.Context,
}
reservationFSM := NewFSM(m.cfg, ProtocolVersionClientInitiated)
// Send the event to the main loop.
m.reqChan <- &FSMSendEventReq{
// Send the event to the main loop. reqChan is unbuffered so the
// raw send blocks until Run picks it up; if Run has already exited
// or the caller has cancelled, fall through with an error instead
// of hanging the RPC indefinitely.
select {
case m.reqChan <- &FSMSendEventReq{
fsm: reservationFSM,
event: OnClientInitialized,
eventCtx: req,
}:
case <-ctx.Done():
return nil, ctx.Err()
}
// We'll now wait for the reservation to be in the state where we are