mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
5a56caf18c
## Why First-party async traits should expose their `Send` contracts explicitly without requiring `async_trait`. This completes the migration pattern established in #27303 and #27304. ## What changed - Replaced the remaining first-party `async_trait` traits with native return-position `impl Future + Send` where statically dispatched and explicit boxed `Send` futures where object safety is required. - Kept implementations behavior-preserving, outlining existing async bodies into inherent methods where that keeps the diff reviewable. - Removed all direct first-party `async-trait` dependencies and the workspace dependency declaration. - Added a cargo-deny policy that permits `async-trait` only through the remaining transitive wrapper crates. - Updated `rand` from 0.8.5 to 0.8.6 to resolve RUSTSEC-2026-0097 and keep the full cargo-deny check passing. ## Validation - `just test -p codex-exec-server`: 216 passed, 2 skipped. - `just test -p codex-model-provider`: 39 passed. - `just test -p codex-core` and `just test`: changed tests passed; remaining failures are environment-sensitive suites unrelated to this migration. - `cargo deny check` - `just fix` - `just fmt` - `cargo shear` - `just bazel-lock-check`
87 lines
2.0 KiB
Rust
87 lines
2.0 KiB
Rust
use std::future::Future;
|
|
use tokio_util::sync::CancellationToken;
|
|
|
|
#[derive(Debug, PartialEq, Eq)]
|
|
pub enum CancelErr {
|
|
Cancelled,
|
|
}
|
|
|
|
pub trait OrCancelExt: Sized {
|
|
type Output;
|
|
|
|
fn or_cancel(
|
|
self,
|
|
token: &CancellationToken,
|
|
) -> impl Future<Output = Result<Self::Output, CancelErr>> + Send;
|
|
}
|
|
|
|
impl<F> OrCancelExt for F
|
|
where
|
|
F: Future + Send,
|
|
F::Output: Send,
|
|
{
|
|
type Output = F::Output;
|
|
|
|
async fn or_cancel(self, token: &CancellationToken) -> Result<Self::Output, CancelErr> {
|
|
tokio::select! {
|
|
_ = token.cancelled() => Err(CancelErr::Cancelled),
|
|
res = self => Ok(res),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use pretty_assertions::assert_eq;
|
|
use std::time::Duration;
|
|
use tokio::task;
|
|
use tokio::time::sleep;
|
|
|
|
#[tokio::test]
|
|
async fn returns_ok_when_future_completes_first() {
|
|
let token = CancellationToken::new();
|
|
let value = async { 42 };
|
|
|
|
let result = value.or_cancel(&token).await;
|
|
|
|
assert_eq!(Ok(42), result);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn returns_err_when_token_cancelled_first() {
|
|
let token = CancellationToken::new();
|
|
let token_clone = token.clone();
|
|
|
|
let cancel_handle = task::spawn(async move {
|
|
sleep(Duration::from_millis(10)).await;
|
|
token_clone.cancel();
|
|
});
|
|
|
|
let result = async {
|
|
sleep(Duration::from_millis(100)).await;
|
|
7
|
|
}
|
|
.or_cancel(&token)
|
|
.await;
|
|
|
|
cancel_handle.await.expect("cancel task panicked");
|
|
assert_eq!(Err(CancelErr::Cancelled), result);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn returns_err_when_token_already_cancelled() {
|
|
let token = CancellationToken::new();
|
|
token.cancel();
|
|
|
|
let result = async {
|
|
sleep(Duration::from_millis(50)).await;
|
|
5
|
|
}
|
|
.or_cancel(&token)
|
|
.await;
|
|
|
|
assert_eq!(Err(CancelErr::Cancelled), result);
|
|
}
|
|
}
|