mirror of
https://github.com/openai/codex.git
synced 2026-11-13 14:46:14 +00:00
Serialize concurrent MCP stdin writes (#33180)
## What changed - Guard executor-backed MCP stdio sends with a single-permit semaphore so a second JSON-RPC message cannot start writing while the first write is pending. - Add a regression test that drives two concurrent sends and verifies the process receives each newline-delimited message in order without overlap. GitOrigin-RevId: 30c2e51827ecf57b5eb23d9ad1e876734d6e784e
This commit is contained in:
1 parent
68c6e5df2b
commit
44954d1b4b
2 files changed
+143
No files matched your search
@@ -40,6 +40,7 @@ use rmcp::transport::Transport;
|
||||
use serde_json::from_slice;
|
||||
use serde_json::to_vec;
|
||||
use tokio::runtime::Handle;
|
||||
use tokio::sync::Semaphore;
|
||||
use tokio::sync::broadcast;
|
||||
use tracing::debug;
|
||||
use tracing::info;
|
||||
@@ -162,6 +163,10 @@ pub(super) struct ExecutorProcessTransport {
|
||||
/// closes the transport.
|
||||
process: Arc<dyn ExecProcess>,
|
||||
|
||||
/// Prevents concurrent rmcp send futures from issuing overlapping stdin writes.
|
||||
/// The single-slot semaphore gives mutex semantics while its permit can safely cross `.await`.
|
||||
stdin_write_semaphore: Arc<Semaphore>,
|
||||
|
||||
/// Pushed output/lifecycle stream for the process.
|
||||
///
|
||||
/// The executor process API still supports retained-output reads, but MCP
|
||||
@@ -204,6 +209,7 @@ impl ExecutorProcessTransport {
|
||||
let events = process.subscribe_events();
|
||||
Self {
|
||||
process,
|
||||
stdin_write_semaphore: Arc::new(Semaphore::new(1)),
|
||||
events,
|
||||
program_name,
|
||||
stdout: LineBuffer::default(),
|
||||
@@ -231,7 +237,12 @@ impl Transport<RoleClient> for ExecutorProcessTransport {
|
||||
item: TxJsonRpcMessage<RoleClient>,
|
||||
) -> impl Future<Output = std::result::Result<(), Self::Error>> + Send + 'static {
|
||||
let process = Arc::clone(&self.process);
|
||||
let stdin_write_semaphore = Arc::clone(&self.stdin_write_semaphore);
|
||||
async move {
|
||||
let _stdin_write_permit = stdin_write_semaphore
|
||||
.acquire()
|
||||
.await
|
||||
.map_err(io::Error::other)?;
|
||||
// rmcp hands us a structured JSON-RPC message. Stdio transport on
|
||||
// the wire is JSON plus one newline delimiter.
|
||||
let mut bytes = to_vec(&item).map_err(io::Error::other)?;
|
||||
|
||||
Reference in new issue
Block a user