Download codex-rs/app-server/src/connection_rpc_gate.rs from SaylorTwift/codex: direct link, hf CLI and curl.
- Browser
- Download file 7.73 kB
-
https://huggingface.co/SaylorTwift/codex/resolve/main/codex-rs/app-server/src/connection_rpc_gate.rs
- Command line
-
hf download hf://SaylorTwift/codex/codex-rs/app-server/src/connection_rpc_gate.rs
-
curl -L -o connection_rpc_gate.rs https://huggingface.co/SaylorTwift/codex/resolve/main/codex-rs/app-server/src/connection_rpc_gate.rs
7.73 kB
| //! Controls RPC admission and draining for each connection. | |
| //! Auth ownership changes reject queued work while started handlers finish cleanup. | |
| use std::future::Future; | |
| use codex_app_server_transport::ConnectionAuth; | |
| use tokio::sync::Mutex; | |
| use tokio_util::task::TaskTracker; | |
| /// Per-connection gate for incoming messages and initialized RPC handler execution. | |
| /// | |
| /// Closing the gate prevents queued handlers from starting while allowing | |
| /// handlers that already acquired a token to finish. | |
| pub(crate) struct ConnectionRpcGate { | |
| pub(crate) auth: Option<ConnectionAuth>, | |
| accepting: Mutex<bool>, | |
| tasks: TaskTracker, | |
| } | |
| impl ConnectionRpcGate { | |
| pub(crate) fn new() -> Self { | |
| let accepting = true; | |
| Self { | |
| auth: None, | |
| accepting: Mutex::new(accepting), | |
| tasks: TaskTracker::new(), | |
| } | |
| } | |
| pub(crate) async fn run<F>(&self, future: F) | |
| where | |
| F: Future<Output = ()>, | |
| { | |
| let token = { | |
| let accepting = self.accepting.lock().await; | |
| if !*accepting || self.is_closed() { | |
| return; | |
| } | |
| self.tasks.token() | |
| }; | |
| future.await; | |
| drop(token); | |
| } | |
| pub(crate) fn is_closed(&self) -> bool { | |
| self.tasks.is_closed() || self.auth.as_ref().is_some_and(|auth| !auth.is_current()) | |
| } | |
| pub(crate) async fn close(&self) { | |
| let mut accepting = self.accepting.lock().await; | |
| *accepting = false; | |
| self.tasks.close(); | |
| } | |
| pub(crate) async fn shutdown(&self) { | |
| self.close().await; | |
| self.tasks.wait().await; | |
| } | |
| async fn is_accepting(&self) -> bool { | |
| *self.accepting.lock().await | |
| } | |
| fn inflight_count(&self) -> usize { | |
| self.tasks.len() | |
| } | |
| } | |
| impl Default for ConnectionRpcGate { | |
| fn default() -> Self { | |
| Self::new() | |
| } | |
| } | |
| mod tests { | |
| use super::*; | |
| use pretty_assertions::assert_eq; | |
| use std::sync::Arc; | |
| use std::sync::atomic::AtomicBool; | |
| use std::sync::atomic::Ordering; | |
| use tokio::sync::oneshot; | |
| use tokio::time::Duration; | |
| use tokio::time::timeout; | |
| async fn run_executes_while_open() { | |
| let gate = ConnectionRpcGate::new(); | |
| let ran = Arc::new(AtomicBool::new(/*v*/ false)); | |
| let ran_clone = Arc::clone(&ran); | |
| gate.run(async move { | |
| ran_clone.store(/*val*/ true, Ordering::Release); | |
| }) | |
| .await; | |
| assert!(ran.load(Ordering::Acquire)); | |
| } | |
| async fn run_drops_future_without_polling_after_close() { | |
| let gate = ConnectionRpcGate::new(); | |
| gate.close().await; | |
| let polled = Arc::new(AtomicBool::new(/*v*/ false)); | |
| let polled_clone = Arc::clone(&polled); | |
| gate.run(async move { | |
| polled_clone.store(/*val*/ true, Ordering::Release); | |
| }) | |
| .await; | |
| assert!(!polled.load(Ordering::Acquire)); | |
| assert!(!gate.is_accepting().await); | |
| } | |
| async fn close_returns_while_started_run_remains_active() { | |
| let gate = Arc::new(ConnectionRpcGate::new()); | |
| let (started_tx, started_rx) = oneshot::channel(); | |
| let (finish_tx, finish_rx) = oneshot::channel(); | |
| let gate_for_run = Arc::clone(&gate); | |
| let run_task = tokio::spawn(async move { | |
| gate_for_run | |
| .run(async move { | |
| started_tx.send(()).expect("receiver should be open"); | |
| let _ = finish_rx.await; | |
| }) | |
| .await; | |
| }); | |
| started_rx.await.expect("run should start"); | |
| gate.close().await; | |
| assert!(!gate.is_accepting().await); | |
| assert_eq!(gate.inflight_count(), 1); | |
| finish_tx | |
| .send(()) | |
| .expect("running future should be waiting"); | |
| run_task.await.expect("run task should complete"); | |
| gate.shutdown().await; | |
| } | |
| async fn shutdown_waits_for_started_run_to_finish() { | |
| let gate = Arc::new(ConnectionRpcGate::new()); | |
| let (started_tx, started_rx) = oneshot::channel(); | |
| let (finish_tx, finish_rx) = oneshot::channel(); | |
| let gate_for_run = Arc::clone(&gate); | |
| let run_task = tokio::spawn(async move { | |
| gate_for_run | |
| .run(async move { | |
| started_tx.send(()).expect("receiver should be open"); | |
| let _ = finish_rx.await; | |
| }) | |
| .await; | |
| }); | |
| started_rx.await.expect("run should start"); | |
| assert_eq!(gate.inflight_count(), 1); | |
| let gate_for_shutdown = Arc::clone(&gate); | |
| let shutdown_task = tokio::spawn(async move { | |
| gate_for_shutdown.shutdown().await; | |
| }); | |
| timeout(Duration::from_millis(/*millis*/ 50), shutdown_task) | |
| .await | |
| .expect_err("shutdown should wait for the running future"); | |
| finish_tx | |
| .send(()) | |
| .expect("running future should be waiting"); | |
| run_task.await.expect("run task should complete"); | |
| gate.shutdown().await; | |
| assert_eq!(gate.inflight_count(), 0); | |
| } | |
| async fn shutdown_drops_late_runs_while_waiting_for_inflight_work() { | |
| let gate = Arc::new(ConnectionRpcGate::new()); | |
| let (started_tx, started_rx) = oneshot::channel(); | |
| let (finish_tx, finish_rx) = oneshot::channel(); | |
| let gate_for_run = Arc::clone(&gate); | |
| let run_task = tokio::spawn(async move { | |
| gate_for_run | |
| .run(async move { | |
| started_tx.send(()).expect("receiver should be open"); | |
| let _ = finish_rx.await; | |
| }) | |
| .await; | |
| }); | |
| started_rx.await.expect("run should start"); | |
| let gate_for_shutdown = Arc::clone(&gate); | |
| let shutdown_task = tokio::spawn(async move { | |
| gate_for_shutdown.shutdown().await; | |
| }); | |
| timeout(Duration::from_millis(/*millis*/ 50), shutdown_task) | |
| .await | |
| .expect_err("shutdown should wait for the running future"); | |
| let late_polled = Arc::new(AtomicBool::new(/*v*/ false)); | |
| let late_polled_clone = Arc::clone(&late_polled); | |
| gate.run(async move { | |
| late_polled_clone.store(/*val*/ true, Ordering::Release); | |
| }) | |
| .await; | |
| assert!(!late_polled.load(Ordering::Acquire)); | |
| finish_tx | |
| .send(()) | |
| .expect("running future should still be waiting"); | |
| run_task.await.expect("run task should complete"); | |
| gate.shutdown().await; | |
| assert_eq!(gate.inflight_count(), 0); | |
| } | |
| async fn run_is_counted_before_handler_body_continues() { | |
| let gate = Arc::new(ConnectionRpcGate::new()); | |
| let (entered_tx, entered_rx) = oneshot::channel(); | |
| let (continue_tx, continue_rx) = oneshot::channel(); | |
| let gate_for_run = Arc::clone(&gate); | |
| let run_task = tokio::spawn(async move { | |
| gate_for_run | |
| .run(async move { | |
| entered_tx.send(()).expect("receiver should be open"); | |
| let _ = continue_rx.await; | |
| }) | |
| .await; | |
| }); | |
| entered_rx.await.expect("handler body should be entered"); | |
| assert_eq!(gate.inflight_count(), 1); | |
| continue_tx | |
| .send(()) | |
| .expect("handler body should still be waiting"); | |
| run_task.await.expect("run task should complete"); | |
| assert_eq!(gate.inflight_count(), 0); | |
| } | |
| } | |