Skip to content

refactor: narrow async lock guard lifetimes#18211

Merged
bolinfest merged 1 commit intomainfrom
pr18211
Apr 17, 2026
Merged

refactor: narrow async lock guard lifetimes#18211
bolinfest merged 1 commit intomainfrom
pr18211

Conversation

@bolinfest
Copy link
Copy Markdown
Collaborator

@bolinfest bolinfest commented Apr 16, 2026

Follow-up to #18178, where we called out enabling the await-holding lint as a follow-up.

The long-term goal is to enable Clippy coverage for async guards held across awaits. This PR is intentionally only the first, low-risk cleanup pass: it narrows obvious lock guard lifetimes and leaves codex-rs/Cargo.toml unchanged so the lint is not enabled until the remaining cases are fixed or explicitly justified. It intentionally leaves the active-turn/turn-state locking pattern alone because those checks and mutations need to stay atomic.

Common fixes used here

These are the main patterns reviewers should expect in this PR, and they are also the patterns to reach for when fixing future await_holding_* findings:

  • Scope the guard to the synchronous work. If the code only needs data from a locked value, move the lock into a small block, clone or compute the needed values, and do the later .await after the block.
  • Use direct one-line mutations when there is no later await. Cases like map.lock().await.remove(&id) are acceptable when the guard is only needed for that single mutation and the statement ends before any async work.
  • Drain or clone work out of the lock before notifying or awaiting. For example, the JS REPL drains pending exec senders into a local vector and the websocket writer clones buffered envelopes before it serializes or sends them.
  • Use a Semaphore only when serialization is intentional across async work. The test serialization guards intentionally span awaited setup or execution, so using a semaphore communicates "one at a time" without holding a mutex guard.
  • Remove the mutex when there is only one owner. The PTY stdin writer task owns stdin directly; the old Arc<Mutex<_>> did not protect shared access because nothing else had access to the writer.
  • Do not split locks that protect an atomic invariant. This PR deliberately leaves active-turn/turn-state paths alone because those checks and mutations need to stay atomic. Those cases should be fixed separately with a design change or documented with #[expect].

What changed

  • Narrow scoped async mutex guards in app-server, JS REPL, network approval, remote-control websocket, and the RMCP test server.
  • Replace test-only async mutex serialization guards with semaphores where the guard intentionally lives across async work.
  • Let the PTY pipe writer task own stdin directly instead of wrapping it in an async mutex.

Verification

  • just fix -p codex-core -p codex-app-server -p codex-rmcp-client -p codex-shell-escalation -p codex-utils-pty -p codex-utils-readiness
  • just clippy -p codex-core
  • cargo test -p codex-core -p codex-app-server -p codex-rmcp-client -p codex-shell-escalation -p codex-utils-pty -p codex-utils-readiness was run; the app-server suite passed, and codex-core failed in the local sandbox on six otel approval tests plus suite::user_shell_cmd::user_shell_command_does_not_set_network_sandbox_env_var, which appear to depend on local command approval/default rules and CODEX_SANDBOX_NETWORK_DISABLED=1 in this environment.

@bolinfest bolinfest changed the title Follow-up to https://github.com/openai/codex/pull/18178. This handles the straightforward await-holding cleanup first, w refactor: narrow async lock guard lifetimes Apr 16, 2026
Copy link
Copy Markdown
Contributor

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9b65a0cd60

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread codex-rs/core/src/codex.rs Outdated
@bolinfest bolinfest force-pushed the pr18211 branch 4 times, most recently from 576e61d to 5de0d38 Compare April 17, 2026 19:10
Follow-up to #18178. This handles the straightforward await-holding cleanup first, without enabling the Clippy lint yet; the long-term goal is to enable the lint in a follow-up once the remaining cases are resolved or explicitly justified.
Copy link
Copy Markdown
Contributor

@starr-openai starr-openai left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

most changes were pretty trivial. for the ws outbound buffer it seems safe but perhaps the main thing to double check

@bolinfest
Copy link
Copy Markdown
Collaborator Author

most changes were pretty trivial. for the ws outbound buffer it seems safe but perhaps the main thing to double check

Pretty sure it should be equivalent in that, in the previous version, I don't believe anyone could call BoundedOutboundBuffer.insert() while state.lock().await.outbound_buffer.server_envelopes() is held because insert() takes &mut self, in which case the iterator couldn't get any "live updates" or anything like that.

@bolinfest bolinfest merged commit 1265df0 into main Apr 17, 2026
48 of 50 checks passed
@bolinfest bolinfest deleted the pr18211 branch April 17, 2026 21:06
@github-actions github-actions bot locked and limited conversation to collaborators Apr 17, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants