perf: remove recv_timeout and use explicit Stop#168
Conversation
Greptile SummaryThis PR replaces the 100 ms
Confidence Score: 4/5Safe to merge — the behavioral changes are confined to the threads actor stop path and the core loop invariants are preserved. The logic is sound: cancellation token + Shutdown message correctly unblocks the blocking recv() call in every relevant scenario. No data races or message-ordering regressions introduced. concurrency/src/threads/actor.rs deserves a second read around the interaction between the cancellation-token check after each handler and the queued Shutdown sentinel, but it holds up on close inspection.
|
| Filename | Overview |
|---|---|
| concurrency/src/threads/actor.rs | Core change: recv_timeout polling loop replaced with blocking recv() + MailboxItem::Shutdown. Logic is sound; one minor unnecessary clone in Context::stop and the stop-from-within-a-handler path leaves an unconsumed Shutdown in the channel (harmless but worth documenting). |
| concurrency/src/tasks/actor.rs | Only the From<ActorRef> for ChildHandle impl changed: cancel token call is now wrapped in a closure. Tasks main loop is untouched; no correctness concerns. |
| concurrency/src/child_handle.rs | Signature change: from_tasks and from_threads now accept a CancelFn instead of a bare CancellationToken. Test helpers correctly build the Arc<dyn Fn()> closure. Clean refactor. |
Comments Outside Diff (1)
-
concurrency/src/threads/actor.rs, line 583-609 (link)Shutdownleft unconsumed when stop is called from within a handlerWhen
ctx.stop()is called inside a message handler,Shutdownis enqueued but never consumed: the loop exits via theif cancellation_token.is_cancelled()check that fires after the handler returns, beforerx.recv()can see the sentinel. TheShutdownis silently dropped whenrxgoes out of scope, so there is no correctness problem. However, ifstop()is called N times (e.g., idempotent shutdown logic or supervisor retries), NShutdownitems accumulate in the channel for the duration of the last handler. A brief doc-comment onstop()noting this is worth adding for future maintainers.Prompt To Fix With AI
This is a comment left during a code review. Path: concurrency/src/threads/actor.rs Line: 583-609 Comment: **`Shutdown` left unconsumed when stop is called from within a handler** When `ctx.stop()` is called inside a message handler, `Shutdown` is enqueued but never consumed: the loop exits via the `if cancellation_token.is_cancelled()` check that fires after the handler returns, before `rx.recv()` can see the sentinel. The `Shutdown` is silently dropped when `rx` goes out of scope, so there is no correctness problem. However, if `stop()` is called N times (e.g., idempotent shutdown logic or supervisor retries), N `Shutdown` items accumulate in the channel for the duration of the last handler. A brief doc-comment on `stop()` noting this is worth adding for future maintainers. How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix All With AI
Fix the following 2 code review issues. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 2
concurrency/src/threads/actor.rs:138-141
`Context::stop` clones the cancellation token via `cancellation_token()` only to immediately call `cancel()` on the clone and drop it. The clone is unnecessary — `cancel()` propagates through any clone because they share the same underlying atomic state, but the direct field is already in scope.
```suggestion
pub fn stop(&self) {
self.cancellation_token.cancel();
let _ = self.sender.send(MailboxItem::Shutdown);
}
```
### Issue 2 of 2
concurrency/src/threads/actor.rs:583-609
**`Shutdown` left unconsumed when stop is called from within a handler**
When `ctx.stop()` is called inside a message handler, `Shutdown` is enqueued but never consumed: the loop exits via the `if cancellation_token.is_cancelled()` check that fires after the handler returns, before `rx.recv()` can see the sentinel. The `Shutdown` is silently dropped when `rx` goes out of scope, so there is no correctness problem. However, if `stop()` is called N times (e.g., idempotent shutdown logic or supervisor retries), N `Shutdown` items accumulate in the channel for the duration of the last handler. A brief doc-comment on `stop()` noting this is worth adding for future maintainers.
Reviews (1): Last reviewed commit: "update child handles" | Re-trigger Greptile
| pub fn stop(&self) { | ||
| self.cancellation_token.cancel(); | ||
| self.cancellation_token().cancel(); | ||
| let _ = self.sender.send(MailboxItem::Shutdown); | ||
| } |
There was a problem hiding this comment.
Context::stop clones the cancellation token via cancellation_token() only to immediately call cancel() on the clone and drop it. The clone is unnecessary — cancel() propagates through any clone because they share the same underlying atomic state, but the direct field is already in scope.
| pub fn stop(&self) { | |
| self.cancellation_token.cancel(); | |
| self.cancellation_token().cancel(); | |
| let _ = self.sender.send(MailboxItem::Shutdown); | |
| } | |
| pub fn stop(&self) { | |
| self.cancellation_token.cancel(); | |
| let _ = self.sender.send(MailboxItem::Shutdown); | |
| } |
Prompt To Fix With AI
This is a comment left during a code review.
Path: concurrency/src/threads/actor.rs
Line: 138-141
Comment:
`Context::stop` clones the cancellation token via `cancellation_token()` only to immediately call `cancel()` on the clone and drop it. The clone is unnecessary — `cancel()` propagates through any clone because they share the same underlying atomic state, but the direct field is already in scope.
```suggestion
pub fn stop(&self) {
self.cancellation_token.cancel();
let _ = self.sender.send(MailboxItem::Shutdown);
}
```
How can I resolve this? If you propose a fix, please make it concise.|
Nice fix — eliminates the 100ms polling overhead and shutdown latency cleanly. The poison-pill pattern is the right call given Suggestion: use
|
Close: #157