Skip to content

Commit 24decf1

Browse files
committed
Avoid using futures' block_on in the request database guard
The request guard works by sending on a channel when it is dropped. Since we can't do async code in `Drop::drop`, I was using `futures::executor::block_on` under the (false!) impression that this *specific* usecase would not be a problem as we are always pulling from that channel. However, sending on a channel can result in `Pending` when the Tokio worker's cooperative budget is exhausted, even if the queue is completely empty! A simple example: ```rust // Drain the budget while task::consume_budget().now_or_never().is_some() {} // Try to use more budget than we have and block forever futures::executor::block_on(tx.send(42u8)).unwrap(); ``` This `Pending` will never resolve because we are no longer running the Tokio runtime so it will never see an `.await` and replenish the budget. With enough bad luck, all the worker threads get stuck like this and the entire system becomes unavailable. There are a number of possible workarounds, but the least invasive is to punt the final logging off to a Tokio task and let it happen out-of-band. Technically this will make the timing a little bit less accurate, but I was never worried about exact nanoseconds anyway.
1 parent 08a7449 commit 24decf1

File tree

1 file changed

+8
-4
lines changed

1 file changed

+8
-4
lines changed

ui/src/request_database.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ impl Handle {
215215
let g = self
216216
.attempt_start_request(category, payload)
217217
.await
218-
.map(|id| EndGuardInner(id, How::Abandoned, self));
218+
.map(|id| EndGuardInner(id, How::Abandoned, Some(self)));
219219
EndGuard(g)
220220
}
221221
}
@@ -238,12 +238,16 @@ impl EndGuard {
238238
}
239239
}
240240

241-
struct EndGuardInner(Id, How, Handle);
241+
struct EndGuardInner(Id, How, Option<Handle>);
242242

243243
impl Drop for EndGuardInner {
244244
fn drop(&mut self) {
245-
let Self(id, how, ref handle) = *self;
246-
futures::executor::block_on(handle.attempt_end_request(id, how))
245+
let Self(id, how, ref mut handle) = *self;
246+
if let Ok(h) = tokio::runtime::Handle::try_current() {
247+
if let Some(handle) = handle.take() {
248+
h.spawn(async move { handle.attempt_end_request(id, how).await });
249+
}
250+
}
247251
}
248252
}
249253

0 commit comments

Comments
 (0)