fix: subscribe-before-check in recv_blocking to avoid missed-wake race

This commit is contained in:
damocles 2026-05-17 02:42:06 +02:00
parent 4f56954422
commit f78c6085b9

View file

@ -142,15 +142,25 @@ impl Broker {
/// emit a `Sent { to: recipient }` event, then retries the pop. Lets
/// agents react to new mail without polling their socket on a fixed
/// interval.
///
/// **Subscribe-before-check order matters.** If we polled the sqlite
/// row first and only then called `subscribe()`, a concurrent `send`
/// landing in that window would commit + broadcast its event *before*
/// our receiver existed — and we'd then sit on the long-poll until
/// the timeout (or another, unrelated send) fired. That looked
/// externally like "the agent processed one wake then went deaf
/// until the operator poked it again". Subscribing first guarantees
/// any post-subscribe send notifies us; the redundant `recv()`
/// catches the message either way.
pub async fn recv_blocking(
&self,
recipient: &str,
timeout: std::time::Duration,
) -> Result<Option<Message>> {
let mut rx = self.subscribe();
if let Some(m) = self.recv(recipient)? {
return Ok(Some(m));
}
let mut rx = self.subscribe();
let deadline = tokio::time::Instant::now() + timeout;
loop {
let Some(remaining) = deadline.checked_duration_since(tokio::time::Instant::now())