From 878605450f4fd43f8081cf56252ccc6dce234c22 Mon Sep 17 00:00:00 2001 From: Schuyler Goodman Date: Tue, 13 Jan 2026 11:40:27 -0500 Subject: [PATCH 01/29] Add wing crate --- CHANGELOG.md | 1 + Cargo.lock | 8 +++ Cargo.toml | 1 + README.md | 8 +-- crates/wing/CHANGELOG.md | 1 + crates/wing/Cargo.toml | 36 +++++++++++++ crates/wing/README.md | 58 ++++++++++++++++++++ crates/wing/examples/custom_spawner.rs | 44 +++++++++++++++ crates/wing/examples/tokio_basic.rs | 21 ++++++++ crates/wing/favicon.ico | 3 ++ crates/wing/logo.png | 3 ++ crates/wing/src/lib.rs | 75 ++++++++++++++++++++++++++ crates/wing/src/spawner.rs | 12 +++++ crates/wing/src/testing.rs | 33 ++++++++++++ crates/wing/src/tokio.rs | 52 ++++++++++++++++++ 15 files changed, 353 insertions(+), 3 deletions(-) create mode 100644 crates/wing/CHANGELOG.md create mode 100644 crates/wing/Cargo.toml create mode 100644 crates/wing/README.md create mode 100644 crates/wing/examples/custom_spawner.rs create mode 100644 crates/wing/examples/tokio_basic.rs create mode 100644 crates/wing/favicon.ico create mode 100644 crates/wing/logo.png create mode 100644 crates/wing/src/lib.rs create mode 100644 crates/wing/src/spawner.rs create mode 100644 crates/wing/src/testing.rs create mode 100644 crates/wing/src/tokio.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 9862cfa6..2e9ee27d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,3 +18,4 @@ Please see each crate's change log below: - [`thread_aware_macros`](./crates/thread_aware_macros/CHANGELOG.md) - [`thread_aware_macros_impl`](./crates/thread_aware_macros_impl/CHANGELOG.md) - [`tick`](./crates/tick/CHANGELOG.md) +- [`wing`](./crates/wing/CHANGELOG.md) diff --git a/Cargo.lock b/Cargo.lock index 8ace845b..ae7647b7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2175,6 +2175,14 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" +[[package]] +name = "wing" +version = "0.1.0" +dependencies = [ + "futures", + "tokio", +] + [[package]] name = "winnow" version = "0.7.14" diff --git a/Cargo.toml b/Cargo.toml index dd28a3e0..b596b9ec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,6 +41,7 @@ thread_aware = { path = "crates/thread_aware", default-features = false, version thread_aware_macros = { path = "crates/thread_aware_macros", default-features = false, version = "0.6.0" } thread_aware_macros_impl = { path = "crates/thread_aware_macros_impl", default-features = false, version = "0.6.0" } tick = { path = "crates/tick", default-features = false, version = "0.1.2" } +wing = { path = "crates/wing", default-features = false, version = "0.1.0" } # external dependencies alloc_tracker = { version = "0.5.9", default-features = false } diff --git a/README.md b/README.md index 9650798c..e1b648de 100644 --- a/README.md +++ b/README.md @@ -11,14 +11,15 @@ This repository contains a set of crates that help you build robust highly scalable services in Rust. -- [Crates](#crates) -- [About this Repo](#about-this-repo) +- [The Oxidizer Project](#the-oxidizer-project) + - [Crates](#crates) + - [About this Repo](#about-this-repo) - [Adding New Crates](#adding-new-crates) - [Publishing Crates](#publishing-crates) - [Documenting Crates](#documenting-crates) - [CI Workflows](#ci-workflows) - [Pull Request Gates](#pull-request-gates) -- [Trademarks](#trademarks) + - [Trademarks](#trademarks) ## Crates @@ -40,6 +41,7 @@ These are the crates built out of this repo: - [`thread_aware_macros`](./crates/thread_aware_macros/README.md) - Macros for the `thread_aware` crate. - [`thread_aware_macros_impl`](./crates/thread_aware_macros_impl/README.md) - Macros for the `thread_aware` crate. - [`tick`](./crates/tick/README.md) - Provides primitives to interact with and manipulate machine time. +- [`wing`](./crates/wing/README.md) - A generic future spawner compatible with any async runtime ## About this Repo diff --git a/crates/wing/CHANGELOG.md b/crates/wing/CHANGELOG.md new file mode 100644 index 00000000..825c32f0 --- /dev/null +++ b/crates/wing/CHANGELOG.md @@ -0,0 +1 @@ +# Changelog diff --git a/crates/wing/Cargo.toml b/crates/wing/Cargo.toml new file mode 100644 index 00000000..e7028490 --- /dev/null +++ b/crates/wing/Cargo.toml @@ -0,0 +1,36 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +[package] +name = "wing" +description = "A generic future spawner compatible with any async runtime" +version = "0.1.0" +readme = "README.md" +keywords = ["oxidizer", "async", "runtime", "futures"] +categories = ["Asynchronous"] + +edition.workspace = true +rust-version.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true + +[package.metadata.docs.rs] +all-features = true + +[features] +default = ["test-util", "tokio"] +test-util = [] +tokio = ["dep:tokio"] + +[dependencies] +futures = { workspace = true, features = ["executor"] } +tokio = { workspace = true, features = ["rt", "rt-multi-thread"], optional = true } + +[dev-dependencies] +futures = { workspace = true, features = ["executor"] } +tokio = { workspace = true, features = ["macros", "rt-multi-thread", "sync", "time"] } + +[lints] +workspace = true diff --git a/crates/wing/README.md b/crates/wing/README.md new file mode 100644 index 00000000..248d6a8f --- /dev/null +++ b/crates/wing/README.md @@ -0,0 +1,58 @@ + + + + +Trait-based async runtime abstraction for spawning tasks. + +This crate provides a [`Spawner`] trait that abstracts task spawning across different async runtimes. +Users can implement `Spawner` for any runtime (tokio, oxidizer, custom runtimes). + +# Design Philosophy + +- **Trait-based**: Implement [`Spawner`] for your runtime +- **Simple**: Just one method to implement +- **Flexible**: Works with any async runtime + +# Quick Start + +## Using Tokio + +```rust +use wing::tokio::TokioSpawner; +use wing::Spawner; + +let spawner = TokioSpawner; + +// Spawn and await a task +let result = spawner.spawn(async { 42 }); +assert_eq!(result, 42); +``` + +## Custom Implementation + +```rust +use wing::Spawner; + +#[derive(Clone)] +struct MySpawner; + +impl Spawner for MySpawner { + fn spawn(&self, work: impl Future + Send + 'static) -> T + where + T: Send + 'static, + { + // Your implementation here + } +} +``` + +# Features + +- `tokio` (default): Enables [`tokio::TokioSpawner`] implementation +- `test-util`: Enables [`testing::MockSpawner`] for testing + + diff --git a/crates/wing/examples/custom_spawner.rs b/crates/wing/examples/custom_spawner.rs new file mode 100644 index 00000000..a9ce3b7d --- /dev/null +++ b/crates/wing/examples/custom_spawner.rs @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +//! Example of implementing a custom spawner using std::thread. + +use wing::Spawner; +use std::thread; + +/// A spawner that uses std::thread to execute futures in a blocking manner. +/// +/// This demonstrates how to integrate with non-async runtimes. +#[derive(Clone, Copy)] +struct ThreadSpawner; + +impl Spawner for ThreadSpawner { + fn spawn(&self, work: impl Future + Send + 'static) -> T + where + T: Send + 'static, + { + let (sender, receiver) = std::sync::mpsc::channel(); + + thread::spawn(move || { + // Block on the future using a simple executor + let result = futures::executor::block_on(work); + let _ = sender.send(result); + }); + + receiver.recv().expect("thread panicked or disconnected") + } +} + +#[tokio::main] +async fn main() { + let spawner = ThreadSpawner; + + println!("Spawning task on std::thread..."); + let result = spawner.spawn(async { + println!("Task running on thread!"); + std::thread::sleep(std::time::Duration::from_millis(100)); + 42 + }); + + println!("Result: {result}"); +} diff --git a/crates/wing/examples/tokio_basic.rs b/crates/wing/examples/tokio_basic.rs new file mode 100644 index 00000000..be03d622 --- /dev/null +++ b/crates/wing/examples/tokio_basic.rs @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +//! Basic tokio usage example. + +use wing::tokio::TokioSpawner; +use wing::Spawner; + +#[tokio::main] +async fn main() { + let spawner = TokioSpawner; + + // Spawn and wait for result + println!("Spawning task..."); + let result = spawner.spawn(async { + tokio::time::sleep(tokio::time::Duration::from_millis(50)).await; + 42 + }); + + println!("Task returned: {result}"); +} diff --git a/crates/wing/favicon.ico b/crates/wing/favicon.ico new file mode 100644 index 00000000..ccae8114 --- /dev/null +++ b/crates/wing/favicon.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e48225cb42c8ac02df5dd4a9bdba29c1e8d10436cc27055d6a021bcb951d4145 +size 480683 diff --git a/crates/wing/logo.png b/crates/wing/logo.png new file mode 100644 index 00000000..1562ae9f --- /dev/null +++ b/crates/wing/logo.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6bf624b54edbaeb8bf0d961e895a86e09b18502fe6c761d00748317883dd09b8 +size 62560 diff --git a/crates/wing/src/lib.rs b/crates/wing/src/lib.rs new file mode 100644 index 00000000..6fb2beeb --- /dev/null +++ b/crates/wing/src/lib.rs @@ -0,0 +1,75 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#![cfg_attr(coverage_nightly, feature(coverage_attribute))] +#![cfg_attr(docsrs, feature(doc_cfg))] +#![warn(missing_docs)] + +//! Trait-based async runtime abstraction for spawning tasks. +//! +//! This crate provides a [`Spawner`] trait that abstracts task spawning across different async runtimes. +//! Users can implement `Spawner` for any runtime (tokio, oxidizer, custom runtimes). +//! +//! # Design Philosophy +//! +//! - **Trait-based**: Implement [`Spawner`] for your runtime +//! - **Simple**: Just one method to implement +//! - **Flexible**: Works with any async runtime +//! +//! # Quick Start +//! +//! ## Using Tokio +//! +//! ```rust +//! use wing::tokio::TokioSpawner; +//! use wing::Spawner; +//! +//! let spawner = TokioSpawner; +//! +//! // Spawn and await a task +//! let result = spawner.spawn(async { 42 }); +//! assert_eq!(result, 42); +//! ``` +//! +//! ## Custom Implementation +//! +//! ```rust +//! use wing::Spawner; +//! +//! #[derive(Clone)] +//! struct MySpawner; +//! +//! impl Spawner for MySpawner { +//! fn spawn(&self, work: impl Future + Send + 'static) -> T +//! where +//! T: Send + 'static, +//! { +//! // Your implementation here +//! # futures::executor::block_on(work) +//! } +//! } +//! ``` +//! +//! # Features +//! +//! - `tokio` (default): Enables [`tokio::TokioSpawner`] implementation +//! - `test-util`: Enables [`testing::MockSpawner`] for testing + +#![doc( + html_logo_url = "https://media.githubusercontent.com/media/microsoft/oxidizer/refs/heads/main/crates/rts/logo.png" +)] +#![doc( + html_favicon_url = "https://media.githubusercontent.com/media/microsoft/oxidizer/refs/heads/main/crates/rts/favicon.ico" +)] + +mod spawner; + +#[cfg(feature = "test-util")] +#[cfg_attr(docsrs, doc(cfg(feature = "test-util")))] +pub mod testing; + +#[cfg(feature = "tokio")] +#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] +pub mod tokio; + +pub use spawner::Spawner; diff --git a/crates/wing/src/spawner.rs b/crates/wing/src/spawner.rs new file mode 100644 index 00000000..eccf4ff3 --- /dev/null +++ b/crates/wing/src/spawner.rs @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +//! Spawner trait for pluggable runtime implementations. + +/// Trait for spawning async tasks on a runtime. +pub trait Spawner { + /// Spawns an async task and returns a value. + fn spawn(&self, work: impl Future + Send + 'static) -> T + where + T: Send + 'static; +} diff --git a/crates/wing/src/testing.rs b/crates/wing/src/testing.rs new file mode 100644 index 00000000..c107f507 --- /dev/null +++ b/crates/wing/src/testing.rs @@ -0,0 +1,33 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +//! Test utilities for mocking spawners. + +use crate::Spawner; + +/// A mock spawner for testing that executes tasks inline. +/// +/// This spawner is useful for testing code that uses [`Spawner`](crate::Spawner) +/// without requiring an actual async runtime. +/// +/// # Examples +/// +/// ```rust +/// use wing::testing::MockSpawner; +/// use wing::Spawner; +/// +/// let spawner = MockSpawner; +/// let result = spawner.spawn(async { 42 }); +/// assert_eq!(result, 42); +/// ``` +#[derive(Debug, Clone, Copy, Default)] +pub struct MockSpawner; + +impl Spawner for MockSpawner { + fn spawn(&self, work: impl Future + Send + 'static) -> T + where + T: Send + 'static, + { + futures::executor::block_on(work) + } +} diff --git a/crates/wing/src/tokio.rs b/crates/wing/src/tokio.rs new file mode 100644 index 00000000..9ad236f2 --- /dev/null +++ b/crates/wing/src/tokio.rs @@ -0,0 +1,52 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +//! Tokio spawner implementation. + +use crate::Spawner; + +/// Spawner implementation for the tokio runtime. +/// +/// Uses `tokio::spawn` to spawn tasks on the tokio runtime. +/// +/// # Examples +/// +/// ```rust +/// use wing::tokio::TokioSpawner; +/// use wing::Spawner; +/// +/// let spawner = TokioSpawner; +/// let result = spawner.spawn(async { 42 }); +/// assert_eq!(result, 42); +/// ``` +#[derive(Debug, Clone, Copy, Default)] +pub struct TokioSpawner; + +impl Spawner for TokioSpawner { + fn spawn(&self, work: impl Future + Send + 'static) -> T + where + T: Send + 'static, + { + // Just block on the future directly using futures executor + futures::executor::block_on(work) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn tokio_spawn_with_result() { + let spawner = TokioSpawner; + let result = spawner.spawn(async { 42 }); + assert_eq!(result, 42); + } + + #[test] + fn tokio_spawn_with_string() { + let spawner = TokioSpawner; + let result = spawner.spawn(async { "hello".to_string() }); + assert_eq!(result, "hello"); + } +} From 42267482a364de5c7725e8dc5ed77b6dcd29eb0a Mon Sep 17 00:00:00 2001 From: Schuyler Goodman Date: Tue, 13 Jan 2026 14:12:53 -0500 Subject: [PATCH 02/29] Simplify --- crates/wing/CHANGELOG.md | 7 +++++ crates/wing/Cargo.toml | 3 +- crates/wing/README.md | 19 ++++++++---- crates/wing/examples/custom_spawner.rs | 3 +- crates/wing/src/lib.rs | 27 +++++++++-------- crates/wing/src/spawner.rs | 2 +- crates/wing/src/testing.rs | 33 -------------------- crates/wing/src/tokio.rs | 42 ++++++++++++++++++-------- 8 files changed, 68 insertions(+), 68 deletions(-) delete mode 100644 crates/wing/src/testing.rs diff --git a/crates/wing/CHANGELOG.md b/crates/wing/CHANGELOG.md index 825c32f0..d0cc418f 100644 --- a/crates/wing/CHANGELOG.md +++ b/crates/wing/CHANGELOG.md @@ -1 +1,8 @@ # Changelog + +## 0.1.0 + +Initial release. + +- `Spawner` trait for abstracting async task spawning across runtimes +- `TokioSpawner` implementation for the Tokio runtime (requires `tokio` feature) diff --git a/crates/wing/Cargo.toml b/crates/wing/Cargo.toml index e7028490..78337b36 100644 --- a/crates/wing/Cargo.toml +++ b/crates/wing/Cargo.toml @@ -20,8 +20,7 @@ repository.workspace = true all-features = true [features] -default = ["test-util", "tokio"] -test-util = [] +default = ["tokio"] tokio = ["dep:tokio"] [dependencies] diff --git a/crates/wing/README.md b/crates/wing/README.md index 248d6a8f..34bef56a 100644 --- a/crates/wing/README.md +++ b/crates/wing/README.md @@ -9,7 +9,7 @@ Trait-based async runtime abstraction for spawning tasks. This crate provides a [`Spawner`] trait that abstracts task spawning across different async runtimes. -Users can implement `Spawner` for any runtime (tokio, oxidizer, custom runtimes). +Users can implement `Spawner` for any runtime (Tokio, oxidizer, custom runtimes). # Design Philosophy @@ -25,11 +25,19 @@ Users can implement `Spawner` for any runtime (tokio, oxidizer, custom runtimes) use wing::tokio::TokioSpawner; use wing::Spawner; -let spawner = TokioSpawner; +// TokioSpawner requires a multi-threaded runtime +let rt = tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build() + .unwrap(); -// Spawn and await a task -let result = spawner.spawn(async { 42 }); -assert_eq!(result, 42); +rt.block_on(async { + let spawner = TokioSpawner; + + // Spawn and await a task + let result = spawner.spawn(async { 42 }); + assert_eq!(result, 42); +}); ``` ## Custom Implementation @@ -53,6 +61,5 @@ impl Spawner for MySpawner { # Features - `tokio` (default): Enables [`tokio::TokioSpawner`] implementation -- `test-util`: Enables [`testing::MockSpawner`] for testing diff --git a/crates/wing/examples/custom_spawner.rs b/crates/wing/examples/custom_spawner.rs index a9ce3b7d..a6305b67 100644 --- a/crates/wing/examples/custom_spawner.rs +++ b/crates/wing/examples/custom_spawner.rs @@ -29,8 +29,7 @@ impl Spawner for ThreadSpawner { } } -#[tokio::main] -async fn main() { +fn main() { let spawner = ThreadSpawner; println!("Spawning task on std::thread..."); diff --git a/crates/wing/src/lib.rs b/crates/wing/src/lib.rs index 6fb2beeb..370487ef 100644 --- a/crates/wing/src/lib.rs +++ b/crates/wing/src/lib.rs @@ -8,7 +8,7 @@ //! Trait-based async runtime abstraction for spawning tasks. //! //! This crate provides a [`Spawner`] trait that abstracts task spawning across different async runtimes. -//! Users can implement `Spawner` for any runtime (tokio, oxidizer, custom runtimes). +//! Users can implement `Spawner` for any runtime (Tokio, oxidizer, custom runtimes). //! //! # Design Philosophy //! @@ -24,11 +24,19 @@ //! use wing::tokio::TokioSpawner; //! use wing::Spawner; //! -//! let spawner = TokioSpawner; +//! // TokioSpawner requires a multi-threaded runtime +//! let rt = tokio::runtime::Builder::new_multi_thread() +//! .enable_all() +//! .build() +//! .unwrap(); //! -//! // Spawn and await a task -//! let result = spawner.spawn(async { 42 }); -//! assert_eq!(result, 42); +//! rt.block_on(async { +//! let spawner = TokioSpawner; +//! +//! // Spawn and await a task +//! let result = spawner.spawn(async { 42 }); +//! assert_eq!(result, 42); +//! }); //! ``` //! //! ## Custom Implementation @@ -53,21 +61,16 @@ //! # Features //! //! - `tokio` (default): Enables [`tokio::TokioSpawner`] implementation -//! - `test-util`: Enables [`testing::MockSpawner`] for testing #![doc( - html_logo_url = "https://media.githubusercontent.com/media/microsoft/oxidizer/refs/heads/main/crates/rts/logo.png" + html_logo_url = "https://media.githubusercontent.com/media/microsoft/oxidizer/refs/heads/main/crates/wing/logo.png" )] #![doc( - html_favicon_url = "https://media.githubusercontent.com/media/microsoft/oxidizer/refs/heads/main/crates/rts/favicon.ico" + html_favicon_url = "https://media.githubusercontent.com/media/microsoft/oxidizer/refs/heads/main/crates/wing/favicon.ico" )] mod spawner; -#[cfg(feature = "test-util")] -#[cfg_attr(docsrs, doc(cfg(feature = "test-util")))] -pub mod testing; - #[cfg(feature = "tokio")] #[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] pub mod tokio; diff --git a/crates/wing/src/spawner.rs b/crates/wing/src/spawner.rs index eccf4ff3..134e3c4d 100644 --- a/crates/wing/src/spawner.rs +++ b/crates/wing/src/spawner.rs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -//! Spawner trait for pluggable runtime implementations. +//! [`Spawner`] trait for plugging in runtime implementations. /// Trait for spawning async tasks on a runtime. pub trait Spawner { diff --git a/crates/wing/src/testing.rs b/crates/wing/src/testing.rs deleted file mode 100644 index c107f507..00000000 --- a/crates/wing/src/testing.rs +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -//! Test utilities for mocking spawners. - -use crate::Spawner; - -/// A mock spawner for testing that executes tasks inline. -/// -/// This spawner is useful for testing code that uses [`Spawner`](crate::Spawner) -/// without requiring an actual async runtime. -/// -/// # Examples -/// -/// ```rust -/// use wing::testing::MockSpawner; -/// use wing::Spawner; -/// -/// let spawner = MockSpawner; -/// let result = spawner.spawn(async { 42 }); -/// assert_eq!(result, 42); -/// ``` -#[derive(Debug, Clone, Copy, Default)] -pub struct MockSpawner; - -impl Spawner for MockSpawner { - fn spawn(&self, work: impl Future + Send + 'static) -> T - where - T: Send + 'static, - { - futures::executor::block_on(work) - } -} diff --git a/crates/wing/src/tokio.rs b/crates/wing/src/tokio.rs index 9ad236f2..8c1eadad 100644 --- a/crates/wing/src/tokio.rs +++ b/crates/wing/src/tokio.rs @@ -1,13 +1,14 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -//! Tokio spawner implementation. +//! Tokio [`Spawner`] implementation. use crate::Spawner; -/// Spawner implementation for the tokio runtime. +/// [`Spawner`] implementation for the Tokio runtime. /// -/// Uses `tokio::spawn` to spawn tasks on the tokio runtime. +/// Spawns tasks using `tokio::spawn` and blocks on the result. +/// This requires being called from within a Tokio **multi-threaded** runtime context. /// /// # Examples /// @@ -15,10 +16,23 @@ use crate::Spawner; /// use wing::tokio::TokioSpawner; /// use wing::Spawner; /// -/// let spawner = TokioSpawner; -/// let result = spawner.spawn(async { 42 }); -/// assert_eq!(result, 42); +/// let rt = tokio::runtime::Builder::new_multi_thread() +/// .enable_all() +/// .build() +/// .unwrap(); +/// +/// rt.block_on(async { +/// let spawner = TokioSpawner; +/// let result = spawner.spawn(async { 42 }); +/// assert_eq!(result, 42); +/// }); /// ``` +/// +/// # Panics +/// +/// - Panics if called outside of a Tokio runtime context +/// - Panics if called from within a single-threaded (`current_thread`) runtime +/// - Panics if the spawned task panics #[derive(Debug, Clone, Copy, Default)] pub struct TokioSpawner; @@ -27,8 +41,12 @@ impl Spawner for TokioSpawner { where T: Send + 'static, { - // Just block on the future directly using futures executor - futures::executor::block_on(work) + let handle = ::tokio::spawn(work); + ::tokio::task::block_in_place(|| { + ::tokio::runtime::Handle::current() + .block_on(handle) + .expect("spawned task panicked") + }) } } @@ -36,15 +54,15 @@ impl Spawner for TokioSpawner { mod tests { use super::*; - #[test] - fn tokio_spawn_with_result() { + #[tokio::test(flavor = "multi_thread")] + async fn tokio_spawn_with_result() { let spawner = TokioSpawner; let result = spawner.spawn(async { 42 }); assert_eq!(result, 42); } - #[test] - fn tokio_spawn_with_string() { + #[tokio::test(flavor = "multi_thread")] + async fn tokio_spawn_with_string() { let spawner = TokioSpawner; let result = spawner.spawn(async { "hello".to_string() }); assert_eq!(result, "hello"); From bcbe6fc8d079555bc63ac113f0581532eee89e92 Mon Sep 17 00:00:00 2001 From: Schuyler Goodman Date: Tue, 13 Jan 2026 21:34:27 -0500 Subject: [PATCH 03/29] Make it non-blocking --- crates/wing/examples/custom_spawner.rs | 22 +++--- crates/wing/examples/tokio_basic.rs | 8 ++- crates/wing/src/lib.rs | 25 +++---- crates/wing/src/spawner.rs | 6 +- crates/wing/src/tokio.rs | 99 ++++++++++++++++++-------- 5 files changed, 97 insertions(+), 63 deletions(-) diff --git a/crates/wing/examples/custom_spawner.rs b/crates/wing/examples/custom_spawner.rs index a6305b67..25e470a1 100644 --- a/crates/wing/examples/custom_spawner.rs +++ b/crates/wing/examples/custom_spawner.rs @@ -3,41 +3,37 @@ //! Example of implementing a custom spawner using std::thread. -use wing::Spawner; use std::thread; +use wing::Spawner; -/// A spawner that uses std::thread to execute futures in a blocking manner. +/// A spawner that uses std::thread to execute futures. /// /// This demonstrates how to integrate with non-async runtimes. #[derive(Clone, Copy)] struct ThreadSpawner; impl Spawner for ThreadSpawner { - fn spawn(&self, work: impl Future + Send + 'static) -> T + fn spawn(&self, work: T) where - T: Send + 'static, + T: Future + Send + 'static, { - let (sender, receiver) = std::sync::mpsc::channel(); - thread::spawn(move || { - // Block on the future using a simple executor - let result = futures::executor::block_on(work); - let _ = sender.send(result); + futures::executor::block_on(work); }); - - receiver.recv().expect("thread panicked or disconnected") } } fn main() { let spawner = ThreadSpawner; + let (sender, receiver) = std::sync::mpsc::channel(); println!("Spawning task on std::thread..."); - let result = spawner.spawn(async { + spawner.spawn(async move { println!("Task running on thread!"); std::thread::sleep(std::time::Duration::from_millis(100)); - 42 + sender.send(42).unwrap(); }); + let result = receiver.recv().expect("thread panicked or disconnected"); println!("Result: {result}"); } diff --git a/crates/wing/examples/tokio_basic.rs b/crates/wing/examples/tokio_basic.rs index be03d622..e38a3ba3 100644 --- a/crates/wing/examples/tokio_basic.rs +++ b/crates/wing/examples/tokio_basic.rs @@ -9,13 +9,15 @@ use wing::Spawner; #[tokio::main] async fn main() { let spawner = TokioSpawner; + let (tx, rx) = tokio::sync::oneshot::channel(); - // Spawn and wait for result + // Spawn a task that sends its result through a channel println!("Spawning task..."); - let result = spawner.spawn(async { + spawner.spawn(async move { tokio::time::sleep(tokio::time::Duration::from_millis(50)).await; - 42 + tx.send(42).unwrap(); }); + let result = rx.await.unwrap(); println!("Task returned: {result}"); } diff --git a/crates/wing/src/lib.rs b/crates/wing/src/lib.rs index 370487ef..385632b2 100644 --- a/crates/wing/src/lib.rs +++ b/crates/wing/src/lib.rs @@ -24,36 +24,33 @@ //! use wing::tokio::TokioSpawner; //! use wing::Spawner; //! -//! // TokioSpawner requires a multi-threaded runtime -//! let rt = tokio::runtime::Builder::new_multi_thread() -//! .enable_all() -//! .build() -//! .unwrap(); +//! # #[tokio::main] +//! # async fn main() { +//! let spawner = TokioSpawner; //! -//! rt.block_on(async { -//! let spawner = TokioSpawner; -//! -//! // Spawn and await a task -//! let result = spawner.spawn(async { 42 }); -//! assert_eq!(result, 42); +//! // Spawn a fire-and-forget task +//! spawner.spawn(async { +//! println!("Task running!"); //! }); +//! # } //! ``` //! //! ## Custom Implementation //! //! ```rust //! use wing::Spawner; +//! use std::future::Future; //! //! #[derive(Clone)] //! struct MySpawner; //! //! impl Spawner for MySpawner { -//! fn spawn(&self, work: impl Future + Send + 'static) -> T +//! fn spawn(&self, work: T) //! where -//! T: Send + 'static, +//! T: Future + Send + 'static, //! { //! // Your implementation here -//! # futures::executor::block_on(work) +//! std::thread::spawn(move || futures::executor::block_on(work)); //! } //! } //! ``` diff --git a/crates/wing/src/spawner.rs b/crates/wing/src/spawner.rs index 134e3c4d..a1539a52 100644 --- a/crates/wing/src/spawner.rs +++ b/crates/wing/src/spawner.rs @@ -5,8 +5,8 @@ /// Trait for spawning async tasks on a runtime. pub trait Spawner { - /// Spawns an async task and returns a value. - fn spawn(&self, work: impl Future + Send + 'static) -> T + /// Spawns an async task + fn spawn(&self, work: T) where - T: Send + 'static; + T: Future + Send + 'static; } diff --git a/crates/wing/src/tokio.rs b/crates/wing/src/tokio.rs index 8c1eadad..69b4cb12 100644 --- a/crates/wing/src/tokio.rs +++ b/crates/wing/src/tokio.rs @@ -7,46 +7,87 @@ use crate::Spawner; /// [`Spawner`] implementation for the Tokio runtime. /// -/// Spawns tasks using `tokio::spawn` and blocks on the result. -/// This requires being called from within a Tokio **multi-threaded** runtime context. +/// Spawns fire-and-forget tasks using `tokio::spawn`. /// /// # Examples /// +/// Basic fire-and-forget usage: +/// /// ```rust /// use wing::tokio::TokioSpawner; /// use wing::Spawner; /// -/// let rt = tokio::runtime::Builder::new_multi_thread() -/// .enable_all() -/// .build() -/// .unwrap(); +/// # #[tokio::main] +/// # async fn main() { +/// let spawner = TokioSpawner; +/// spawner.spawn(async { +/// println!("Task running!"); +/// }); +/// # } +/// ``` +/// +/// ## Getting Results /// -/// rt.block_on(async { -/// let spawner = TokioSpawner; -/// let result = spawner.spawn(async { 42 }); -/// assert_eq!(result, 42); +/// Use a oneshot channel to retrieve a value from the spawned task: +/// +/// ```rust +/// use wing::tokio::TokioSpawner; +/// use wing::Spawner; +/// use tokio::sync::oneshot; +/// +/// # #[tokio::main] +/// # async fn main() { +/// let spawner = TokioSpawner; +/// let (tx, rx) = oneshot::channel(); +/// +/// spawner.spawn(async move { +/// let result = 1 + 1; +/// let _ = tx.send(result); /// }); +/// +/// let value = rx.await.unwrap(); +/// assert_eq!(value, 2); +/// # } +/// ``` +/// +/// ## Handling Errors +/// +/// Send a `Result` through the channel to propagate errors: +/// +/// ```rust +/// use wing::tokio::TokioSpawner; +/// use wing::Spawner; +/// use tokio::sync::oneshot; +/// +/// # #[tokio::main] +/// # async fn main() { +/// let spawner = TokioSpawner; +/// let (tx, rx) = oneshot::channel::>(); +/// +/// spawner.spawn(async move { +/// let result = if true { Ok(42) } else { Err("something went wrong") }; +/// let _ = tx.send(result); +/// }); +/// +/// match rx.await.unwrap() { +/// Ok(value) => println!("Got {value}"), +/// Err(e) => eprintln!("Task failed: {e}"), +/// } +/// # } /// ``` /// /// # Panics /// /// - Panics if called outside of a Tokio runtime context -/// - Panics if called from within a single-threaded (`current_thread`) runtime -/// - Panics if the spawned task panics #[derive(Debug, Clone, Copy, Default)] pub struct TokioSpawner; impl Spawner for TokioSpawner { - fn spawn(&self, work: impl Future + Send + 'static) -> T + fn spawn(&self, work: T) where - T: Send + 'static, + T: Future + Send + 'static, { - let handle = ::tokio::spawn(work); - ::tokio::task::block_in_place(|| { - ::tokio::runtime::Handle::current() - .block_on(handle) - .expect("spawned task panicked") - }) + ::tokio::spawn(work); } } @@ -54,17 +95,15 @@ impl Spawner for TokioSpawner { mod tests { use super::*; - #[tokio::test(flavor = "multi_thread")] - async fn tokio_spawn_with_result() { + #[tokio::test] + async fn tokio_spawn_fire_and_forget() { let spawner = TokioSpawner; - let result = spawner.spawn(async { 42 }); - assert_eq!(result, 42); - } + let (tx, rx) = tokio::sync::oneshot::channel(); - #[tokio::test(flavor = "multi_thread")] - async fn tokio_spawn_with_string() { - let spawner = TokioSpawner; - let result = spawner.spawn(async { "hello".to_string() }); - assert_eq!(result, "hello"); + spawner.spawn(async move { + tx.send(42).unwrap(); + }); + + assert_eq!(rx.await.unwrap(), 42); } } From dbfc6c4aa7fdea472c40e3b2742929466effa89c Mon Sep 17 00:00:00 2001 From: Schuyler Goodman Date: Tue, 13 Jan 2026 21:35:15 -0500 Subject: [PATCH 04/29] Update readme --- crates/wing/README.md | 71 +++++++++++++++++++++++++------------------ 1 file changed, 41 insertions(+), 30 deletions(-) diff --git a/crates/wing/README.md b/crates/wing/README.md index 34bef56a..4c43fe6b 100644 --- a/crates/wing/README.md +++ b/crates/wing/README.md @@ -1,65 +1,76 @@ - +
+ Wing Logo - +# Wing + +[![crate.io](https://img.shields.io/crates/v/wing.svg)](https://crates.io/crates/wing) +[![docs.rs](https://docs.rs/wing/badge.svg)](https://docs.rs/wing) +[![MSRV](https://img.shields.io/crates/msrv/wing)](https://crates.io/crates/wing) +[![CI](https://github.com/microsoft/oxidizer/actions/workflows/main.yml/badge.svg?event=push)](https://github.com/microsoft/oxidizer/actions/workflows/main.yml) +[![Coverage](https://codecov.io/gh/microsoft/oxidizer/graph/badge.svg?token=FCUG0EL5TI)](https://codecov.io/gh/microsoft/oxidizer) +[![License](https://img.shields.io/badge/license-MIT-blue.svg)](../../LICENSE) +This crate was developed as part of the Oxidizer project + +
Trait-based async runtime abstraction for spawning tasks. -This crate provides a [`Spawner`] trait that abstracts task spawning across different async runtimes. +This crate provides a [`Spawner`][__link0] trait that abstracts task spawning across different async runtimes. Users can implement `Spawner` for any runtime (Tokio, oxidizer, custom runtimes). -# Design Philosophy +## Design Philosophy -- **Trait-based**: Implement [`Spawner`] for your runtime -- **Simple**: Just one method to implement -- **Flexible**: Works with any async runtime +* **Trait-based**: Implement [`Spawner`][__link1] for your runtime +* **Simple**: Just one method to implement +* **Flexible**: Works with any async runtime -# Quick Start +## Quick Start -## Using Tokio +### Using Tokio ```rust use wing::tokio::TokioSpawner; use wing::Spawner; -// TokioSpawner requires a multi-threaded runtime -let rt = tokio::runtime::Builder::new_multi_thread() - .enable_all() - .build() - .unwrap(); - -rt.block_on(async { - let spawner = TokioSpawner; +let spawner = TokioSpawner; - // Spawn and await a task - let result = spawner.spawn(async { 42 }); - assert_eq!(result, 42); +// Spawn a fire-and-forget task +spawner.spawn(async { + println!("Task running!"); }); ``` -## Custom Implementation +### Custom Implementation ```rust use wing::Spawner; +use std::future::Future; #[derive(Clone)] struct MySpawner; impl Spawner for MySpawner { - fn spawn(&self, work: impl Future + Send + 'static) -> T + fn spawn(&self, work: T) where - T: Send + 'static, + T: Future + Send + 'static, { // Your implementation here + std::thread::spawn(move || futures::executor::block_on(work)); } } ``` -# Features +## Features + +* `tokio` (default): Enables [`tokio::TokioSpawner`][__link2] implementation + -- `tokio` (default): Enables [`tokio::TokioSpawner`] implementation +
+ +This crate was developed as part of The Oxidizer Project. Browse this crate's source code. + - + [__cargo_doc2readme_dependencies_info]: ggGkYW0CYXSEGy4k8ldDFPOhG2VNeXtD5nnKG6EPY6OfW5wBG8g18NOFNdxpYXKEGynk_PiPXAuVG-KP_EnSed44G22eKq6fdB7jG6qsjSxCufmjYWSBgmR3aW5nZTAuMS4w + [__link0]: https://docs.rs/wing/0.1.0/wing/?search=Spawner + [__link1]: https://docs.rs/wing/0.1.0/wing/?search=Spawner + [__link2]: https://docs.rs/wing/0.1.0/wing/?search=tokio::TokioSpawner From 5b1415afddcb1f3edaad27eb8ab7168185236db4 Mon Sep 17 00:00:00 2001 From: Schuyler Goodman Date: Wed, 14 Jan 2026 10:58:16 -0500 Subject: [PATCH 05/29] Simplify with custom spawner as enum variant instead of trait --- crates/wing/examples/custom_spawner.rs | 25 +--- crates/wing/examples/tokio_basic.rs | 3 +- crates/wing/src/lib.rs | 43 ++---- crates/wing/src/spawner.rs | 191 ++++++++++++++++++++++++- crates/wing/src/tokio.rs | 109 -------------- 5 files changed, 206 insertions(+), 165 deletions(-) delete mode 100644 crates/wing/src/tokio.rs diff --git a/crates/wing/examples/custom_spawner.rs b/crates/wing/examples/custom_spawner.rs index 25e470a1..c1b1774d 100644 --- a/crates/wing/examples/custom_spawner.rs +++ b/crates/wing/examples/custom_spawner.rs @@ -1,30 +1,15 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -//! Example of implementing a custom spawner using std::thread. +//! Example of using a custom spawner with std::thread. -use std::thread; use wing::Spawner; -/// A spawner that uses std::thread to execute futures. -/// -/// This demonstrates how to integrate with non-async runtimes. -#[derive(Clone, Copy)] -struct ThreadSpawner; - -impl Spawner for ThreadSpawner { - fn spawn(&self, work: T) - where - T: Future + Send + 'static, - { - thread::spawn(move || { - futures::executor::block_on(work); - }); - } -} - fn main() { - let spawner = ThreadSpawner; + let spawner = Spawner::new_custom(|fut| { + std::thread::spawn(move || futures::executor::block_on(fut)); + }); + let (sender, receiver) = std::sync::mpsc::channel(); println!("Spawning task on std::thread..."); diff --git a/crates/wing/examples/tokio_basic.rs b/crates/wing/examples/tokio_basic.rs index e38a3ba3..e1afbc59 100644 --- a/crates/wing/examples/tokio_basic.rs +++ b/crates/wing/examples/tokio_basic.rs @@ -3,12 +3,11 @@ //! Basic tokio usage example. -use wing::tokio::TokioSpawner; use wing::Spawner; #[tokio::main] async fn main() { - let spawner = TokioSpawner; + let spawner = Spawner::Tokio; let (tx, rx) = tokio::sync::oneshot::channel(); // Spawn a task that sends its result through a channel diff --git a/crates/wing/src/lib.rs b/crates/wing/src/lib.rs index 385632b2..d2224e45 100644 --- a/crates/wing/src/lib.rs +++ b/crates/wing/src/lib.rs @@ -5,15 +5,15 @@ #![cfg_attr(docsrs, feature(doc_cfg))] #![warn(missing_docs)] -//! Trait-based async runtime abstraction for spawning tasks. +//! Runtime-agnostic async task spawning. //! -//! This crate provides a [`Spawner`] trait that abstracts task spawning across different async runtimes. -//! Users can implement `Spawner` for any runtime (Tokio, oxidizer, custom runtimes). +//! This crate provides a [`Spawner`] enum that abstracts task spawning across +//! different async runtimes without generic infection. //! //! # Design Philosophy //! -//! - **Trait-based**: Implement [`Spawner`] for your runtime -//! - **Simple**: Just one method to implement +//! - **Concrete type**: No generics needed in your code +//! - **Simple**: Use built-in variants or provide a closure //! - **Flexible**: Works with any async runtime //! //! # Quick Start @@ -21,43 +21,34 @@ //! ## Using Tokio //! //! ```rust -//! use wing::tokio::TokioSpawner; //! use wing::Spawner; //! //! # #[tokio::main] //! # async fn main() { -//! let spawner = TokioSpawner; -//! -//! // Spawn a fire-and-forget task +//! let spawner = Spawner::Tokio; //! spawner.spawn(async { //! println!("Task running!"); //! }); //! # } //! ``` //! -//! ## Custom Implementation +//! ## Custom Runtime //! //! ```rust //! use wing::Spawner; -//! use std::future::Future; //! -//! #[derive(Clone)] -//! struct MySpawner; +//! let spawner = Spawner::new_custom(|fut| { +//! std::thread::spawn(move || futures::executor::block_on(fut)); +//! }); //! -//! impl Spawner for MySpawner { -//! fn spawn(&self, work: T) -//! where -//! T: Future + Send + 'static, -//! { -//! // Your implementation here -//! std::thread::spawn(move || futures::executor::block_on(work)); -//! } -//! } +//! spawner.spawn(async { +//! println!("Running on custom runtime!"); +//! }); //! ``` //! //! # Features //! -//! - `tokio` (default): Enables [`tokio::TokioSpawner`] implementation +//! - `tokio` (default): Enables the [`Spawner::Tokio`] variant #![doc( html_logo_url = "https://media.githubusercontent.com/media/microsoft/oxidizer/refs/heads/main/crates/wing/logo.png" @@ -68,8 +59,4 @@ mod spawner; -#[cfg(feature = "tokio")] -#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] -pub mod tokio; - -pub use spawner::Spawner; +pub use spawner::{CustomSpawner, Spawner}; diff --git a/crates/wing/src/spawner.rs b/crates/wing/src/spawner.rs index a1539a52..736dabe3 100644 --- a/crates/wing/src/spawner.rs +++ b/crates/wing/src/spawner.rs @@ -1,12 +1,191 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -//! [`Spawner`] trait for plugging in runtime implementations. +//! [`Spawner`] enum for plugging in runtime implementations. -/// Trait for spawning async tasks on a runtime. -pub trait Spawner { - /// Spawns an async task - fn spawn(&self, work: T) +use std::pin::Pin; +use std::sync::Arc; + +type BoxedFuture = Pin + Send>>; +type SpawnFn = dyn Fn(BoxedFuture) + Send + Sync; + +/// Runtime-agnostic task spawner. +/// +/// `Spawner` abstracts task spawning across different async runtimes. Use the +/// built-in variants for common runtimes, or [`Spawner::new_custom`] for custom +/// implementations. +/// +/// # Examples +/// +/// Using Tokio: +/// +/// ```rust +/// use wing::Spawner; +/// +/// # #[tokio::main] +/// # async fn main() { +/// let spawner = Spawner::Tokio; +/// spawner.spawn(async { +/// println!("Task running!"); +/// }); +/// # } +/// ``` +/// +/// ## Custom Runtime +/// +/// ```rust +/// use wing::Spawner; +/// +/// # fn main() { +/// let spawner = Spawner::new_custom(|fut| { +/// std::thread::spawn(move || futures::executor::block_on(fut)); +/// }); +/// +/// spawner.spawn(async { +/// println!("Running on custom runtime!"); +/// }); +/// # } +/// ``` +/// +/// ## Getting Results +/// +/// Use a oneshot channel to retrieve a value from the spawned task: +/// +/// ```rust +/// use wing::Spawner; +/// use tokio::sync::oneshot; +/// +/// # #[tokio::main] +/// # async fn main() { +/// let spawner = Spawner::Tokio; +/// let (tx, rx) = oneshot::channel(); +/// +/// spawner.spawn(async move { +/// let result = 1 + 1; +/// let _ = tx.send(result); +/// }); +/// +/// let value = rx.await.unwrap(); +/// assert_eq!(value, 2); +/// # } +/// ``` +/// +/// ## Handling Errors +/// +/// Send a `Result` through the channel to propagate errors: +/// +/// ```rust +/// use wing::Spawner; +/// use tokio::sync::oneshot; +/// +/// # #[tokio::main] +/// # async fn main() { +/// let spawner = Spawner::Tokio; +/// let (tx, rx) = oneshot::channel::>(); +/// +/// spawner.spawn(async move { +/// let result = if true { Ok(42) } else { Err("something went wrong") }; +/// let _ = tx.send(result); +/// }); +/// +/// match rx.await.unwrap() { +/// Ok(value) => println!("Got {value}"), +/// Err(e) => eprintln!("Task failed: {e}"), +/// } +/// # } +/// ``` +#[derive(Debug)] +pub enum Spawner { + /// Spawns tasks using [`tokio::spawn`]. + /// + /// # Panics + /// + /// Panics if called outside of a Tokio runtime context. + #[cfg(feature = "tokio")] + #[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] + Tokio, + + /// Custom spawner implementation. + /// + /// Created via [`Spawner::new_custom`]. + Custom(CustomSpawner), +} + +impl Spawner { + /// Creates a custom spawner from a closure. + /// + /// The closure receives a boxed, pinned future and is responsible for + /// spawning it on the appropriate runtime. + /// + /// # Examples + /// + /// ```rust + /// use wing::Spawner; + /// + /// let spawner = Spawner::new_custom(|fut| { + /// std::thread::spawn(move || futures::executor::block_on(fut)); + /// }); + /// ``` + pub fn new_custom(f: F) -> Self where - T: Future + Send + 'static; + F: Fn(BoxedFuture) + Send + Sync + 'static, + { + Spawner::Custom(CustomSpawner(Arc::new(f))) + } + + /// Spawns an async task on the runtime. + /// + /// The task runs independently and its result is discarded. Use a channel + /// to retrieve results if needed. + pub fn spawn(&self, work: impl Future + Send + 'static) { + match self { + #[cfg(feature = "tokio")] + Spawner::Tokio => { + ::tokio::spawn(work); + } + Spawner::Custom(c) => (c.0)(Box::pin(work)), + } + } +} + +/// Internal wrapper for custom spawn functions. +pub struct CustomSpawner(Arc); + +impl std::fmt::Debug for CustomSpawner { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("CustomSpawner").finish_non_exhaustive() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[cfg(feature = "tokio")] + #[tokio::test] + async fn tokio_spawn_fire_and_forget() { + let spawner = Spawner::Tokio; + let (tx, rx) = tokio::sync::oneshot::channel(); + + spawner.spawn(async move { + tx.send(42).unwrap(); + }); + + assert_eq!(rx.await.unwrap(), 42); + } + + #[test] + fn custom_spawn() { + let spawner = Spawner::new_custom(|fut| { + std::thread::spawn(move || futures::executor::block_on(fut)); + }); + + let (tx, rx) = std::sync::mpsc::channel(); + + spawner.spawn(async move { + tx.send(42).unwrap(); + }); + + assert_eq!(rx.recv().unwrap(), 42); + } } diff --git a/crates/wing/src/tokio.rs b/crates/wing/src/tokio.rs deleted file mode 100644 index 69b4cb12..00000000 --- a/crates/wing/src/tokio.rs +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -//! Tokio [`Spawner`] implementation. - -use crate::Spawner; - -/// [`Spawner`] implementation for the Tokio runtime. -/// -/// Spawns fire-and-forget tasks using `tokio::spawn`. -/// -/// # Examples -/// -/// Basic fire-and-forget usage: -/// -/// ```rust -/// use wing::tokio::TokioSpawner; -/// use wing::Spawner; -/// -/// # #[tokio::main] -/// # async fn main() { -/// let spawner = TokioSpawner; -/// spawner.spawn(async { -/// println!("Task running!"); -/// }); -/// # } -/// ``` -/// -/// ## Getting Results -/// -/// Use a oneshot channel to retrieve a value from the spawned task: -/// -/// ```rust -/// use wing::tokio::TokioSpawner; -/// use wing::Spawner; -/// use tokio::sync::oneshot; -/// -/// # #[tokio::main] -/// # async fn main() { -/// let spawner = TokioSpawner; -/// let (tx, rx) = oneshot::channel(); -/// -/// spawner.spawn(async move { -/// let result = 1 + 1; -/// let _ = tx.send(result); -/// }); -/// -/// let value = rx.await.unwrap(); -/// assert_eq!(value, 2); -/// # } -/// ``` -/// -/// ## Handling Errors -/// -/// Send a `Result` through the channel to propagate errors: -/// -/// ```rust -/// use wing::tokio::TokioSpawner; -/// use wing::Spawner; -/// use tokio::sync::oneshot; -/// -/// # #[tokio::main] -/// # async fn main() { -/// let spawner = TokioSpawner; -/// let (tx, rx) = oneshot::channel::>(); -/// -/// spawner.spawn(async move { -/// let result = if true { Ok(42) } else { Err("something went wrong") }; -/// let _ = tx.send(result); -/// }); -/// -/// match rx.await.unwrap() { -/// Ok(value) => println!("Got {value}"), -/// Err(e) => eprintln!("Task failed: {e}"), -/// } -/// # } -/// ``` -/// -/// # Panics -/// -/// - Panics if called outside of a Tokio runtime context -#[derive(Debug, Clone, Copy, Default)] -pub struct TokioSpawner; - -impl Spawner for TokioSpawner { - fn spawn(&self, work: T) - where - T: Future + Send + 'static, - { - ::tokio::spawn(work); - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[tokio::test] - async fn tokio_spawn_fire_and_forget() { - let spawner = TokioSpawner; - let (tx, rx) = tokio::sync::oneshot::channel(); - - spawner.spawn(async move { - tx.send(42).unwrap(); - }); - - assert_eq!(rx.await.unwrap(), 42); - } -} From 17e5d8539eb9801c3b4d3acea74f08c38a5e7b63 Mon Sep 17 00:00:00 2001 From: Schuyler Goodman Date: Wed, 14 Jan 2026 12:08:43 -0500 Subject: [PATCH 06/29] Rename wing to arty --- Cargo.lock | 16 ++-- Cargo.toml | 2 +- crates/{wing => arty}/CHANGELOG.md | 0 crates/{wing => arty}/Cargo.toml | 2 +- crates/arty/README.md | 66 ++++++++++++++++ .../{wing => arty}/examples/custom_spawner.rs | 2 +- crates/{wing => arty}/examples/tokio_basic.rs | 2 +- crates/{wing => arty}/favicon.ico | 0 crates/{wing => arty}/logo.png | 0 crates/{wing => arty}/src/lib.rs | 8 +- crates/{wing => arty}/src/spawner.rs | 10 +-- crates/wing/README.md | 76 ------------------- 12 files changed, 87 insertions(+), 97 deletions(-) rename crates/{wing => arty}/CHANGELOG.md (100%) rename crates/{wing => arty}/Cargo.toml (98%) create mode 100644 crates/arty/README.md rename crates/{wing => arty}/examples/custom_spawner.rs (97%) rename crates/{wing => arty}/examples/tokio_basic.rs (96%) rename crates/{wing => arty}/favicon.ico (100%) rename crates/{wing => arty}/logo.png (100%) rename crates/{wing => arty}/src/lib.rs (89%) rename crates/{wing => arty}/src/spawner.rs (97%) delete mode 100644 crates/wing/README.md diff --git a/Cargo.lock b/Cargo.lock index ae7647b7..b71472c7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -44,6 +44,14 @@ version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" +[[package]] +name = "arty" +version = "0.1.0" +dependencies = [ + "futures", + "tokio", +] + [[package]] name = "autocfg" version = "1.5.0" @@ -2175,14 +2183,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" -[[package]] -name = "wing" -version = "0.1.0" -dependencies = [ - "futures", - "tokio", -] - [[package]] name = "winnow" version = "0.7.14" diff --git a/Cargo.toml b/Cargo.toml index b596b9ec..781f45b7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,7 +41,7 @@ thread_aware = { path = "crates/thread_aware", default-features = false, version thread_aware_macros = { path = "crates/thread_aware_macros", default-features = false, version = "0.6.0" } thread_aware_macros_impl = { path = "crates/thread_aware_macros_impl", default-features = false, version = "0.6.0" } tick = { path = "crates/tick", default-features = false, version = "0.1.2" } -wing = { path = "crates/wing", default-features = false, version = "0.1.0" } +arty = { path = "crates/arty", default-features = false, version = "0.1.0" } # external dependencies alloc_tracker = { version = "0.5.9", default-features = false } diff --git a/crates/wing/CHANGELOG.md b/crates/arty/CHANGELOG.md similarity index 100% rename from crates/wing/CHANGELOG.md rename to crates/arty/CHANGELOG.md diff --git a/crates/wing/Cargo.toml b/crates/arty/Cargo.toml similarity index 98% rename from crates/wing/Cargo.toml rename to crates/arty/Cargo.toml index 78337b36..cfac4afa 100644 --- a/crates/wing/Cargo.toml +++ b/crates/arty/Cargo.toml @@ -2,7 +2,7 @@ # Licensed under the MIT License. [package] -name = "wing" +name = "arty" description = "A generic future spawner compatible with any async runtime" version = "0.1.0" readme = "README.md" diff --git a/crates/arty/README.md b/crates/arty/README.md new file mode 100644 index 00000000..d4c71437 --- /dev/null +++ b/crates/arty/README.md @@ -0,0 +1,66 @@ +
+ Arty Logo + +# Arty + +[![crate.io](https://img.shields.io/crates/v/arty.svg)](https://crates.io/crates/arty) +[![docs.rs](https://docs.rs/arty/badge.svg)](https://docs.rs/arty) +[![MSRV](https://img.shields.io/crates/msrv/arty)](https://crates.io/crates/arty) +[![CI](https://github.com/microsoft/oxidizer/actions/workflows/main.yml/badge.svg?event=push)](https://github.com/microsoft/oxidizer/actions/workflows/main.yml) +[![Coverage](https://codecov.io/gh/microsoft/oxidizer/graph/badge.svg?token=FCUG0EL5TI)](https://codecov.io/gh/microsoft/oxidizer) +[![License](https://img.shields.io/badge/license-MIT-blue.svg)](../../LICENSE) +This crate was developed as part of the Oxidizer project + +
+ +Runtime-agnostic async task spawning. + +This crate provides a [`Spawner`][__link0] enum that abstracts task spawning across +different async runtimes without generic infection. + +## Design Philosophy + +* **Concrete type**: No generics needed in your code +* **Simple**: Use built-in variants or provide a closure +* **Flexible**: Works with any async runtime + +## Quick Start + +### Using Tokio + +```rust +use arty::Spawner; + +let spawner = Spawner::Tokio; +spawner.spawn(async { + println!("Task running!"); +}); +``` + +### Custom Runtime + +```rust +use arty::Spawner; + +let spawner = Spawner::new_custom(|fut| { + std::thread::spawn(move || futures::executor::block_on(fut)); +}); + +spawner.spawn(async { + println!("Running on custom runtime!"); +}); +``` + +## Features + +* `tokio` (default): Enables the [`Spawner::Tokio`][__link1] variant + + +
+ +This crate was developed as part of The Oxidizer Project. Browse this crate's source code. + + + [__cargo_doc2readme_dependencies_info]: ggGkYW0CYXSEGy4k8ldDFPOhG2VNeXtD5nnKG6EPY6OfW5wBG8g18NOFNdxpYXKEGy8QLYWQ6TvkGzOuS19rYB8RG4HN5vQgcR69G8qNc7VUYU_3YWSBgmRhcnR5ZTAuMS4w + [__link0]: https://docs.rs/arty/0.1.0/arty/?search=Spawner + [__link1]: https://docs.rs/arty/0.1.0/arty/?search=Spawner::Tokio diff --git a/crates/wing/examples/custom_spawner.rs b/crates/arty/examples/custom_spawner.rs similarity index 97% rename from crates/wing/examples/custom_spawner.rs rename to crates/arty/examples/custom_spawner.rs index c1b1774d..2bc8439c 100644 --- a/crates/wing/examples/custom_spawner.rs +++ b/crates/arty/examples/custom_spawner.rs @@ -3,7 +3,7 @@ //! Example of using a custom spawner with std::thread. -use wing::Spawner; +use arty::Spawner; fn main() { let spawner = Spawner::new_custom(|fut| { diff --git a/crates/wing/examples/tokio_basic.rs b/crates/arty/examples/tokio_basic.rs similarity index 96% rename from crates/wing/examples/tokio_basic.rs rename to crates/arty/examples/tokio_basic.rs index e1afbc59..5ffd4198 100644 --- a/crates/wing/examples/tokio_basic.rs +++ b/crates/arty/examples/tokio_basic.rs @@ -3,7 +3,7 @@ //! Basic tokio usage example. -use wing::Spawner; +use arty::Spawner; #[tokio::main] async fn main() { diff --git a/crates/wing/favicon.ico b/crates/arty/favicon.ico similarity index 100% rename from crates/wing/favicon.ico rename to crates/arty/favicon.ico diff --git a/crates/wing/logo.png b/crates/arty/logo.png similarity index 100% rename from crates/wing/logo.png rename to crates/arty/logo.png diff --git a/crates/wing/src/lib.rs b/crates/arty/src/lib.rs similarity index 89% rename from crates/wing/src/lib.rs rename to crates/arty/src/lib.rs index d2224e45..8217a573 100644 --- a/crates/wing/src/lib.rs +++ b/crates/arty/src/lib.rs @@ -21,7 +21,7 @@ //! ## Using Tokio //! //! ```rust -//! use wing::Spawner; +//! use arty::Spawner; //! //! # #[tokio::main] //! # async fn main() { @@ -35,7 +35,7 @@ //! ## Custom Runtime //! //! ```rust -//! use wing::Spawner; +//! use arty::Spawner; //! //! let spawner = Spawner::new_custom(|fut| { //! std::thread::spawn(move || futures::executor::block_on(fut)); @@ -51,10 +51,10 @@ //! - `tokio` (default): Enables the [`Spawner::Tokio`] variant #![doc( - html_logo_url = "https://media.githubusercontent.com/media/microsoft/oxidizer/refs/heads/main/crates/wing/logo.png" + html_logo_url = "https://media.githubusercontent.com/media/microsoft/oxidizer/refs/heads/main/crates/arty/logo.png" )] #![doc( - html_favicon_url = "https://media.githubusercontent.com/media/microsoft/oxidizer/refs/heads/main/crates/wing/favicon.ico" + html_favicon_url = "https://media.githubusercontent.com/media/microsoft/oxidizer/refs/heads/main/crates/arty/favicon.ico" )] mod spawner; diff --git a/crates/wing/src/spawner.rs b/crates/arty/src/spawner.rs similarity index 97% rename from crates/wing/src/spawner.rs rename to crates/arty/src/spawner.rs index 736dabe3..cd8e4eb3 100644 --- a/crates/wing/src/spawner.rs +++ b/crates/arty/src/spawner.rs @@ -20,7 +20,7 @@ type SpawnFn = dyn Fn(BoxedFuture) + Send + Sync; /// Using Tokio: /// /// ```rust -/// use wing::Spawner; +/// use arty::Spawner; /// /// # #[tokio::main] /// # async fn main() { @@ -34,7 +34,7 @@ type SpawnFn = dyn Fn(BoxedFuture) + Send + Sync; /// ## Custom Runtime /// /// ```rust -/// use wing::Spawner; +/// use arty::Spawner; /// /// # fn main() { /// let spawner = Spawner::new_custom(|fut| { @@ -52,7 +52,7 @@ type SpawnFn = dyn Fn(BoxedFuture) + Send + Sync; /// Use a oneshot channel to retrieve a value from the spawned task: /// /// ```rust -/// use wing::Spawner; +/// use arty::Spawner; /// use tokio::sync::oneshot; /// /// # #[tokio::main] @@ -75,7 +75,7 @@ type SpawnFn = dyn Fn(BoxedFuture) + Send + Sync; /// Send a `Result` through the channel to propagate errors: /// /// ```rust -/// use wing::Spawner; +/// use arty::Spawner; /// use tokio::sync::oneshot; /// /// # #[tokio::main] @@ -120,7 +120,7 @@ impl Spawner { /// # Examples /// /// ```rust - /// use wing::Spawner; + /// use arty::Spawner; /// /// let spawner = Spawner::new_custom(|fut| { /// std::thread::spawn(move || futures::executor::block_on(fut)); diff --git a/crates/wing/README.md b/crates/wing/README.md deleted file mode 100644 index 4c43fe6b..00000000 --- a/crates/wing/README.md +++ /dev/null @@ -1,76 +0,0 @@ -
- Wing Logo - -# Wing - -[![crate.io](https://img.shields.io/crates/v/wing.svg)](https://crates.io/crates/wing) -[![docs.rs](https://docs.rs/wing/badge.svg)](https://docs.rs/wing) -[![MSRV](https://img.shields.io/crates/msrv/wing)](https://crates.io/crates/wing) -[![CI](https://github.com/microsoft/oxidizer/actions/workflows/main.yml/badge.svg?event=push)](https://github.com/microsoft/oxidizer/actions/workflows/main.yml) -[![Coverage](https://codecov.io/gh/microsoft/oxidizer/graph/badge.svg?token=FCUG0EL5TI)](https://codecov.io/gh/microsoft/oxidizer) -[![License](https://img.shields.io/badge/license-MIT-blue.svg)](../../LICENSE) -This crate was developed as part of the Oxidizer project - -
- -Trait-based async runtime abstraction for spawning tasks. - -This crate provides a [`Spawner`][__link0] trait that abstracts task spawning across different async runtimes. -Users can implement `Spawner` for any runtime (Tokio, oxidizer, custom runtimes). - -## Design Philosophy - -* **Trait-based**: Implement [`Spawner`][__link1] for your runtime -* **Simple**: Just one method to implement -* **Flexible**: Works with any async runtime - -## Quick Start - -### Using Tokio - -```rust -use wing::tokio::TokioSpawner; -use wing::Spawner; - -let spawner = TokioSpawner; - -// Spawn a fire-and-forget task -spawner.spawn(async { - println!("Task running!"); -}); -``` - -### Custom Implementation - -```rust -use wing::Spawner; -use std::future::Future; - -#[derive(Clone)] -struct MySpawner; - -impl Spawner for MySpawner { - fn spawn(&self, work: T) - where - T: Future + Send + 'static, - { - // Your implementation here - std::thread::spawn(move || futures::executor::block_on(work)); - } -} -``` - -## Features - -* `tokio` (default): Enables [`tokio::TokioSpawner`][__link2] implementation - - -
- -This crate was developed as part of The Oxidizer Project. Browse this crate's source code. - - - [__cargo_doc2readme_dependencies_info]: ggGkYW0CYXSEGy4k8ldDFPOhG2VNeXtD5nnKG6EPY6OfW5wBG8g18NOFNdxpYXKEGynk_PiPXAuVG-KP_EnSed44G22eKq6fdB7jG6qsjSxCufmjYWSBgmR3aW5nZTAuMS4w - [__link0]: https://docs.rs/wing/0.1.0/wing/?search=Spawner - [__link1]: https://docs.rs/wing/0.1.0/wing/?search=Spawner - [__link2]: https://docs.rs/wing/0.1.0/wing/?search=tokio::TokioSpawner From dfa8fbabea37972694c783af8137a146842fb024 Mon Sep 17 00:00:00 2001 From: Schuyler Goodman Date: Wed, 14 Jan 2026 12:59:51 -0500 Subject: [PATCH 07/29] Fix clippy issues --- .spelling | 3 ++- Cargo.toml | 2 +- crates/arty/examples/custom_spawner.rs | 4 ++-- crates/arty/examples/tokio_basic.rs | 4 ++-- crates/arty/src/lib.rs | 8 ++------ crates/arty/src/spawner.rs | 6 +++--- 6 files changed, 12 insertions(+), 15 deletions(-) diff --git a/.spelling b/.spelling index 447f9019..5959ef85 100644 --- a/.spelling +++ b/.spelling @@ -233,6 +233,7 @@ shareable skimmable SLAs smallvec +spawner startup stderr stdlib @@ -298,4 +299,4 @@ workflows workspace w.r.t. Xamarin -xxH3 \ No newline at end of file +xxH3 diff --git a/Cargo.toml b/Cargo.toml index 781f45b7..4dd3c649 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ repository = "https://github.com/microsoft/oxidizer" [workspace.dependencies] # local dependencies +arty = { path = "crates/arty", default-features = false, version = "0.1.0" } bytesbuf = { path = "crates/bytesbuf", default-features = false, version = "0.2.1" } bytesbuf_io = { path = "crates/bytesbuf_io", default-features = false, version = "0.1.1" } data_privacy = { path = "crates/data_privacy", default-features = false, version = "0.10.0" } @@ -41,7 +42,6 @@ thread_aware = { path = "crates/thread_aware", default-features = false, version thread_aware_macros = { path = "crates/thread_aware_macros", default-features = false, version = "0.6.0" } thread_aware_macros_impl = { path = "crates/thread_aware_macros_impl", default-features = false, version = "0.6.0" } tick = { path = "crates/tick", default-features = false, version = "0.1.2" } -arty = { path = "crates/arty", default-features = false, version = "0.1.0" } # external dependencies alloc_tracker = { version = "0.5.9", default-features = false } diff --git a/crates/arty/examples/custom_spawner.rs b/crates/arty/examples/custom_spawner.rs index 2bc8439c..ec5549e1 100644 --- a/crates/arty/examples/custom_spawner.rs +++ b/crates/arty/examples/custom_spawner.rs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -//! Example of using a custom spawner with std::thread. +//! Example of using a custom spawner with `std::thread`. use arty::Spawner; @@ -16,7 +16,7 @@ fn main() { spawner.spawn(async move { println!("Task running on thread!"); std::thread::sleep(std::time::Duration::from_millis(100)); - sender.send(42).unwrap(); + sender.send(42).expect("Failed to send result"); }); let result = receiver.recv().expect("thread panicked or disconnected"); diff --git a/crates/arty/examples/tokio_basic.rs b/crates/arty/examples/tokio_basic.rs index 5ffd4198..cd531829 100644 --- a/crates/arty/examples/tokio_basic.rs +++ b/crates/arty/examples/tokio_basic.rs @@ -14,9 +14,9 @@ async fn main() { println!("Spawning task..."); spawner.spawn(async move { tokio::time::sleep(tokio::time::Duration::from_millis(50)).await; - tx.send(42).unwrap(); + tx.send(42).expect("Failed to send result"); }); - let result = rx.await.unwrap(); + let result = rx.await.expect("Task panicked or sender dropped"); println!("Task returned: {result}"); } diff --git a/crates/arty/src/lib.rs b/crates/arty/src/lib.rs index 8217a573..802ae036 100644 --- a/crates/arty/src/lib.rs +++ b/crates/arty/src/lib.rs @@ -50,12 +50,8 @@ //! //! - `tokio` (default): Enables the [`Spawner::Tokio`] variant -#![doc( - html_logo_url = "https://media.githubusercontent.com/media/microsoft/oxidizer/refs/heads/main/crates/arty/logo.png" -)] -#![doc( - html_favicon_url = "https://media.githubusercontent.com/media/microsoft/oxidizer/refs/heads/main/crates/arty/favicon.ico" -)] +#![doc(html_logo_url = "https://media.githubusercontent.com/media/microsoft/oxidizer/refs/heads/main/crates/arty/logo.png")] +#![doc(html_favicon_url = "https://media.githubusercontent.com/media/microsoft/oxidizer/refs/heads/main/crates/arty/favicon.ico")] mod spawner; diff --git a/crates/arty/src/spawner.rs b/crates/arty/src/spawner.rs index cd8e4eb3..66371447 100644 --- a/crates/arty/src/spawner.rs +++ b/crates/arty/src/spawner.rs @@ -130,7 +130,7 @@ impl Spawner { where F: Fn(BoxedFuture) + Send + Sync + 'static, { - Spawner::Custom(CustomSpawner(Arc::new(f))) + Self::Custom(CustomSpawner(Arc::new(f))) } /// Spawns an async task on the runtime. @@ -140,10 +140,10 @@ impl Spawner { pub fn spawn(&self, work: impl Future + Send + 'static) { match self { #[cfg(feature = "tokio")] - Spawner::Tokio => { + Self::Tokio => { ::tokio::spawn(work); } - Spawner::Custom(c) => (c.0)(Box::pin(work)), + Self::Custom(c) => (c.0)(Box::pin(work)), } } } From e5cef96f360b8e4fd9a4c7bed86d2aba604cffb6 Mon Sep 17 00:00:00 2001 From: Schuyler Goodman Date: Wed, 14 Jan 2026 15:00:20 -0500 Subject: [PATCH 08/29] Fix unused dependency --- Cargo.lock | 1 - crates/arty/Cargo.toml | 2 -- 2 files changed, 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b71472c7..de315128 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -48,7 +48,6 @@ checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" name = "arty" version = "0.1.0" dependencies = [ - "futures", "tokio", ] diff --git a/crates/arty/Cargo.toml b/crates/arty/Cargo.toml index cfac4afa..e4f5d166 100644 --- a/crates/arty/Cargo.toml +++ b/crates/arty/Cargo.toml @@ -24,11 +24,9 @@ default = ["tokio"] tokio = ["dep:tokio"] [dependencies] -futures = { workspace = true, features = ["executor"] } tokio = { workspace = true, features = ["rt", "rt-multi-thread"], optional = true } [dev-dependencies] -futures = { workspace = true, features = ["executor"] } tokio = { workspace = true, features = ["macros", "rt-multi-thread", "sync", "time"] } [lints] From ab50740ef64abe29bdd0c5546ccb382c1601434e Mon Sep 17 00:00:00 2001 From: Schuyler Goodman Date: Wed, 14 Jan 2026 15:01:32 -0500 Subject: [PATCH 09/29] Fix unused dependency --- Cargo.lock | 1 + crates/arty/Cargo.toml | 1 + 2 files changed, 2 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index de315128..b71472c7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -48,6 +48,7 @@ checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" name = "arty" version = "0.1.0" dependencies = [ + "futures", "tokio", ] diff --git a/crates/arty/Cargo.toml b/crates/arty/Cargo.toml index e4f5d166..7d7e8e91 100644 --- a/crates/arty/Cargo.toml +++ b/crates/arty/Cargo.toml @@ -27,6 +27,7 @@ tokio = ["dep:tokio"] tokio = { workspace = true, features = ["rt", "rt-multi-thread"], optional = true } [dev-dependencies] +futures = { workspace = true, features = ["executor"] } tokio = { workspace = true, features = ["macros", "rt-multi-thread", "sync", "time"] } [lints] From 615fb0c9cb4e0e21d744aaac7cd3885f57d5f817 Mon Sep 17 00:00:00 2001 From: Schuyler Goodman Date: Wed, 14 Jan 2026 16:41:12 -0500 Subject: [PATCH 10/29] Add run method. Clean up examples and tests. Clean up imports --- Cargo.lock | 2 + Cargo.toml | 1 + crates/arty/Cargo.toml | 6 +- crates/arty/README.md | 10 +- crates/arty/examples/custom.rs | 29 +++++ crates/arty/examples/custom_spawner.rs | 24 ---- crates/arty/examples/tokio.rs | 31 +++++ crates/arty/examples/tokio_basic.rs | 22 ---- crates/arty/src/lib.rs | 12 +- crates/arty/src/spawner.rs | 157 +++++++++++++++++-------- 10 files changed, 188 insertions(+), 106 deletions(-) create mode 100644 crates/arty/examples/custom.rs delete mode 100644 crates/arty/examples/custom_spawner.rs create mode 100644 crates/arty/examples/tokio.rs delete mode 100644 crates/arty/examples/tokio_basic.rs diff --git a/Cargo.lock b/Cargo.lock index b71472c7..8e2fb8ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -49,6 +49,8 @@ name = "arty" version = "0.1.0" dependencies = [ "futures", + "futures-channel", + "tick", "tokio", ] diff --git a/Cargo.toml b/Cargo.toml index 4dd3c649..a23c9a9e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -54,6 +54,7 @@ derive_more = { version = "2.0.1", default-features = false } duct = { version = "1.1.1", default-features = false } dynosaur = { version = "0.3.0", default-features = false } futures = { version = "0.3.31", default-features = false } +futures-channel = { version = "0.3.31", default-features = false } futures-core = { version = "0.3.31", default-features = false } futures-util = { version = "0.3.31", default-features = false } infinity_pool = { version = "0.8.1", default-features = false } diff --git a/crates/arty/Cargo.toml b/crates/arty/Cargo.toml index 7d7e8e91..ec8e1257 100644 --- a/crates/arty/Cargo.toml +++ b/crates/arty/Cargo.toml @@ -24,11 +24,13 @@ default = ["tokio"] tokio = ["dep:tokio"] [dependencies] -tokio = { workspace = true, features = ["rt", "rt-multi-thread"], optional = true } +futures-channel = { workspace = true, features = ["alloc"] } +tokio = { workspace = true, features = ["rt"], optional = true } [dev-dependencies] futures = { workspace = true, features = ["executor"] } -tokio = { workspace = true, features = ["macros", "rt-multi-thread", "sync", "time"] } +tick = { workspace = true, features = ["tokio"] } +tokio = { workspace = true, features = ["macros", "rt-multi-thread", "sync"] } [lints] workspace = true diff --git a/crates/arty/README.md b/crates/arty/README.md index d4c71437..74d8f0b8 100644 --- a/crates/arty/README.md +++ b/crates/arty/README.md @@ -15,13 +15,13 @@ Runtime-agnostic async task spawning. -This crate provides a [`Spawner`][__link0] enum that abstracts task spawning across +This crate provides a [`Spawner`][__link0] type that abstracts task spawning across different async runtimes without generic infection. ## Design Philosophy * **Concrete type**: No generics needed in your code -* **Simple**: Use built-in variants or provide a closure +* **Simple**: Use built-in constructors or provide a closure * **Flexible**: Works with any async runtime ## Quick Start @@ -31,7 +31,7 @@ different async runtimes without generic infection. ```rust use arty::Spawner; -let spawner = Spawner::Tokio; +let spawner = Spawner::tokio(); spawner.spawn(async { println!("Task running!"); }); @@ -42,7 +42,7 @@ spawner.spawn(async { ```rust use arty::Spawner; -let spawner = Spawner::new_custom(|fut| { +let spawner = Spawner::custom(|fut| { std::thread::spawn(move || futures::executor::block_on(fut)); }); @@ -53,7 +53,7 @@ spawner.spawn(async { ## Features -* `tokio` (default): Enables the [`Spawner::Tokio`][__link1] variant +* `tokio` (default): Enables the [`Spawner::tokio`][__link1] constructor
diff --git a/crates/arty/examples/custom.rs b/crates/arty/examples/custom.rs new file mode 100644 index 00000000..9edd0155 --- /dev/null +++ b/crates/arty/examples/custom.rs @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +//! Spawning tasks with a custom spawner. + +use std::time::Duration; + +use arty::Spawner; + +fn main() { + // Create a spawner that runs futures on background threads + let spawner = Spawner::custom(|fut| { + std::thread::spawn(move || futures::executor::block_on(fut)); + }); + + // Fire-and-forget: spawn a task without waiting for its result + spawner.spawn(async { + std::thread::sleep(Duration::from_millis(10)); + println!("Background task completed!"); + }); + + // Retrieve a result using run + let rx = spawner.run(async { 1 + 1 }); + let value = futures::executor::block_on(rx).unwrap(); + println!("Got result: {value}"); + + // Wait for background task + std::thread::sleep(Duration::from_millis(50)); +} diff --git a/crates/arty/examples/custom_spawner.rs b/crates/arty/examples/custom_spawner.rs deleted file mode 100644 index ec5549e1..00000000 --- a/crates/arty/examples/custom_spawner.rs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -//! Example of using a custom spawner with `std::thread`. - -use arty::Spawner; - -fn main() { - let spawner = Spawner::new_custom(|fut| { - std::thread::spawn(move || futures::executor::block_on(fut)); - }); - - let (sender, receiver) = std::sync::mpsc::channel(); - - println!("Spawning task on std::thread..."); - spawner.spawn(async move { - println!("Task running on thread!"); - std::thread::sleep(std::time::Duration::from_millis(100)); - sender.send(42).expect("Failed to send result"); - }); - - let result = receiver.recv().expect("thread panicked or disconnected"); - println!("Result: {result}"); -} diff --git a/crates/arty/examples/tokio.rs b/crates/arty/examples/tokio.rs new file mode 100644 index 00000000..a60f1e23 --- /dev/null +++ b/crates/arty/examples/tokio.rs @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +//! Spawning tasks with Tokio. + +use std::time::Duration; + +use arty::Spawner; +use tick::Clock; + +#[tokio::main] +async fn main() { + let clock = Clock::new_tokio(); + let spawner = Spawner::tokio(); + + // Fire-and-forget: spawn a task without waiting for its result + spawner.spawn({ + let clock = clock.clone(); + async move { + clock.delay(Duration::from_millis(10)).await; + println!("Background task completed!"); + } + }); + + // Retrieve a result using run + let value = spawner.run(async { 1 + 1 }).await.unwrap(); + println!("Got result: {value}"); + + // Wait for background task + clock.delay(Duration::from_millis(50)).await; +} diff --git a/crates/arty/examples/tokio_basic.rs b/crates/arty/examples/tokio_basic.rs deleted file mode 100644 index cd531829..00000000 --- a/crates/arty/examples/tokio_basic.rs +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -//! Basic tokio usage example. - -use arty::Spawner; - -#[tokio::main] -async fn main() { - let spawner = Spawner::Tokio; - let (tx, rx) = tokio::sync::oneshot::channel(); - - // Spawn a task that sends its result through a channel - println!("Spawning task..."); - spawner.spawn(async move { - tokio::time::sleep(tokio::time::Duration::from_millis(50)).await; - tx.send(42).expect("Failed to send result"); - }); - - let result = rx.await.expect("Task panicked or sender dropped"); - println!("Task returned: {result}"); -} diff --git a/crates/arty/src/lib.rs b/crates/arty/src/lib.rs index 802ae036..87fc0a57 100644 --- a/crates/arty/src/lib.rs +++ b/crates/arty/src/lib.rs @@ -7,13 +7,13 @@ //! Runtime-agnostic async task spawning. //! -//! This crate provides a [`Spawner`] enum that abstracts task spawning across +//! This crate provides a [`Spawner`] type that abstracts task spawning across //! different async runtimes without generic infection. //! //! # Design Philosophy //! //! - **Concrete type**: No generics needed in your code -//! - **Simple**: Use built-in variants or provide a closure +//! - **Simple**: Use built-in constructors or provide a closure //! - **Flexible**: Works with any async runtime //! //! # Quick Start @@ -25,7 +25,7 @@ //! //! # #[tokio::main] //! # async fn main() { -//! let spawner = Spawner::Tokio; +//! let spawner = Spawner::tokio(); //! spawner.spawn(async { //! println!("Task running!"); //! }); @@ -37,7 +37,7 @@ //! ```rust //! use arty::Spawner; //! -//! let spawner = Spawner::new_custom(|fut| { +//! let spawner = Spawner::custom(|fut| { //! std::thread::spawn(move || futures::executor::block_on(fut)); //! }); //! @@ -48,11 +48,11 @@ //! //! # Features //! -//! - `tokio` (default): Enables the [`Spawner::Tokio`] variant +//! - `tokio` (default): Enables the [`Spawner::tokio`] constructor #![doc(html_logo_url = "https://media.githubusercontent.com/media/microsoft/oxidizer/refs/heads/main/crates/arty/logo.png")] #![doc(html_favicon_url = "https://media.githubusercontent.com/media/microsoft/oxidizer/refs/heads/main/crates/arty/favicon.ico")] mod spawner; -pub use spawner::{CustomSpawner, Spawner}; +pub use spawner::{Canceled, Spawner}; diff --git a/crates/arty/src/spawner.rs b/crates/arty/src/spawner.rs index 66371447..8656f53d 100644 --- a/crates/arty/src/spawner.rs +++ b/crates/arty/src/spawner.rs @@ -1,18 +1,23 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -//! [`Spawner`] enum for plugging in runtime implementations. +//! [`Spawner`] for plugging in runtime implementations. +use std::fmt::Debug; use std::pin::Pin; use std::sync::Arc; +use futures_channel::oneshot; + +pub use oneshot::Canceled; + type BoxedFuture = Pin + Send>>; type SpawnFn = dyn Fn(BoxedFuture) + Send + Sync; /// Runtime-agnostic task spawner. /// /// `Spawner` abstracts task spawning across different async runtimes. Use the -/// built-in variants for common runtimes, or [`Spawner::new_custom`] for custom +/// built-in constructors for common runtimes, or [`Spawner::custom`] for custom /// implementations. /// /// # Examples @@ -24,7 +29,7 @@ type SpawnFn = dyn Fn(BoxedFuture) + Send + Sync; /// /// # #[tokio::main] /// # async fn main() { -/// let spawner = Spawner::Tokio; +/// let spawner = Spawner::tokio(); /// spawner.spawn(async { /// println!("Task running!"); /// }); @@ -37,7 +42,7 @@ type SpawnFn = dyn Fn(BoxedFuture) + Send + Sync; /// use arty::Spawner; /// /// # fn main() { -/// let spawner = Spawner::new_custom(|fut| { +/// let spawner = Spawner::custom(|fut| { /// std::thread::spawn(move || futures::executor::block_on(fut)); /// }); /// @@ -49,69 +54,79 @@ type SpawnFn = dyn Fn(BoxedFuture) + Send + Sync; /// /// ## Getting Results /// -/// Use a oneshot channel to retrieve a value from the spawned task: +/// Use [`run`](Spawner::run) to retrieve a value from the task: /// /// ```rust /// use arty::Spawner; -/// use tokio::sync::oneshot; /// /// # #[tokio::main] /// # async fn main() { -/// let spawner = Spawner::Tokio; -/// let (tx, rx) = oneshot::channel(); -/// -/// spawner.spawn(async move { -/// let result = 1 + 1; -/// let _ = tx.send(result); -/// }); -/// -/// let value = rx.await.unwrap(); +/// let spawner = Spawner::tokio(); +/// let value = spawner.run(async { 1 + 1 }).await.unwrap(); /// assert_eq!(value, 2); /// # } /// ``` /// /// ## Handling Errors /// -/// Send a `Result` through the channel to propagate errors: +/// Return a `Result` from the task to propagate errors: /// /// ```rust /// use arty::Spawner; -/// use tokio::sync::oneshot; /// /// # #[tokio::main] /// # async fn main() { -/// let spawner = Spawner::Tokio; -/// let (tx, rx) = oneshot::channel::>(); +/// let spawner = Spawner::tokio(); /// -/// spawner.spawn(async move { -/// let result = if true { Ok(42) } else { Err("something went wrong") }; -/// let _ = tx.send(result); -/// }); +/// let result = spawner +/// .run(async { +/// if true { Ok(42) } else { Err("something went wrong") } +/// }) +/// .await +/// .unwrap(); /// -/// match rx.await.unwrap() { +/// match result { /// Ok(value) => println!("Got {value}"), /// Err(e) => eprintln!("Task failed: {e}"), /// } /// # } /// ``` -#[derive(Debug)] -pub enum Spawner { - /// Spawns tasks using [`tokio::spawn`]. +#[derive(Debug, Clone)] +pub struct Spawner(SpawnerKind); + +#[derive(Debug, Clone)] +enum SpawnerKind { + #[cfg(feature = "tokio")] + Tokio, + Custom(CustomSpawner), +} + +impl Spawner { + /// Creates a spawner that uses the Tokio runtime. /// /// # Panics /// /// Panics if called outside of a Tokio runtime context. + /// + /// # Examples + /// + /// ```rust + /// use arty::Spawner; + /// + /// # #[tokio::main] + /// # async fn main() { + /// let spawner = Spawner::tokio(); + /// spawner.spawn(async { + /// println!("Running on Tokio!"); + /// }); + /// # } + /// ``` #[cfg(feature = "tokio")] #[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] - Tokio, - - /// Custom spawner implementation. - /// - /// Created via [`Spawner::new_custom`]. - Custom(CustomSpawner), -} + pub fn tokio() -> Self { + Self(SpawnerKind::Tokio) + } -impl Spawner { /// Creates a custom spawner from a closure. /// /// The closure receives a boxed, pinned future and is responsible for @@ -122,36 +137,66 @@ impl Spawner { /// ```rust /// use arty::Spawner; /// - /// let spawner = Spawner::new_custom(|fut| { + /// let spawner = Spawner::custom(|fut| { /// std::thread::spawn(move || futures::executor::block_on(fut)); /// }); /// ``` - pub fn new_custom(f: F) -> Self + pub fn custom(f: F) -> Self where F: Fn(BoxedFuture) + Send + Sync + 'static, { - Self::Custom(CustomSpawner(Arc::new(f))) + Self(SpawnerKind::Custom(CustomSpawner(Arc::new(f)))) } /// Spawns an async task on the runtime. /// - /// The task runs independently and its result is discarded. Use a channel - /// to retrieve results if needed. + /// The task runs independently and its result is discarded. Use + /// [`run`](Self::run) to retrieve results. pub fn spawn(&self, work: impl Future + Send + 'static) { - match self { + match &self.0 { #[cfg(feature = "tokio")] - Self::Tokio => { + SpawnerKind::Tokio => { ::tokio::spawn(work); } - Self::Custom(c) => (c.0)(Box::pin(work)), + SpawnerKind::Custom(c) => (c.0)(Box::pin(work)), } } + + /// Runs an async task and returns a receiver for its result. + /// + /// The task runs independently. Await the returned receiver to get the + /// result. If the task panics or is cancelled, the receiver returns + /// [`Canceled`]. + /// + /// # Examples + /// + /// ```rust + /// use arty::Spawner; + /// + /// # #[tokio::main] + /// # async fn main() { + /// let spawner = Spawner::tokio(); + /// let result = spawner.run(async { 1 + 1 }); + /// assert_eq!(result.await.unwrap(), 2); + /// # } + /// ``` + pub fn run( + &self, + work: impl Future + Send + 'static, + ) -> oneshot::Receiver { + let (tx, rx) = oneshot::channel(); + self.spawn(async move { + let _ = tx.send(work.await); + }); + rx + } } /// Internal wrapper for custom spawn functions. -pub struct CustomSpawner(Arc); +#[derive(Clone)] +pub(crate) struct CustomSpawner(Arc); -impl std::fmt::Debug for CustomSpawner { +impl Debug for CustomSpawner { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("CustomSpawner").finish_non_exhaustive() } @@ -164,7 +209,7 @@ mod tests { #[cfg(feature = "tokio")] #[tokio::test] async fn tokio_spawn_fire_and_forget() { - let spawner = Spawner::Tokio; + let spawner = Spawner::tokio(); let (tx, rx) = tokio::sync::oneshot::channel(); spawner.spawn(async move { @@ -176,7 +221,7 @@ mod tests { #[test] fn custom_spawn() { - let spawner = Spawner::new_custom(|fut| { + let spawner = Spawner::custom(|fut| { std::thread::spawn(move || futures::executor::block_on(fut)); }); @@ -188,4 +233,22 @@ mod tests { assert_eq!(rx.recv().unwrap(), 42); } + + #[cfg(feature = "tokio")] + #[tokio::test] + async fn tokio_run() { + let spawner = Spawner::tokio(); + let rx = spawner.run(async { 42 }); + assert_eq!(rx.await.unwrap(), 42); + } + + #[test] + fn custom_run() { + let spawner = Spawner::custom(|fut| { + std::thread::spawn(move || futures::executor::block_on(fut)); + }); + + let rx = spawner.run(async { 42 }); + assert_eq!(futures::executor::block_on(rx).unwrap(), 42); + } } From 13758c7d732824cfec745c41e11f1f600105332d Mon Sep 17 00:00:00 2001 From: Schuyler Goodman Date: Wed, 14 Jan 2026 16:48:37 -0500 Subject: [PATCH 11/29] Fix PR issues --- .spelling | 1 + crates/arty/Cargo.toml | 6 ++++++ crates/arty/src/spawner.rs | 6 ++---- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/.spelling b/.spelling index a2b4f7f1..07ce23f6 100644 --- a/.spelling +++ b/.spelling @@ -37,6 +37,7 @@ btree_map buildable bytesbuf callee +cancelled C-BITFLAG C-CONV C-CONV-SPECIFIC diff --git a/crates/arty/Cargo.toml b/crates/arty/Cargo.toml index ec8e1257..c73b2cf3 100644 --- a/crates/arty/Cargo.toml +++ b/crates/arty/Cargo.toml @@ -16,6 +16,12 @@ license.workspace = true homepage.workspace = true repository.workspace = true +[package.metadata.cargo_check_external_types] +allowed_external_types = [ + "futures_channel::oneshot::Canceled", + "futures_channel::oneshot::Receiver", +] + [package.metadata.docs.rs] all-features = true diff --git a/crates/arty/src/spawner.rs b/crates/arty/src/spawner.rs index 8656f53d..ae0d8460 100644 --- a/crates/arty/src/spawner.rs +++ b/crates/arty/src/spawner.rs @@ -121,6 +121,7 @@ impl Spawner { /// }); /// # } /// ``` + #[must_use] #[cfg(feature = "tokio")] #[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] pub fn tokio() -> Self { @@ -180,10 +181,7 @@ impl Spawner { /// assert_eq!(result.await.unwrap(), 2); /// # } /// ``` - pub fn run( - &self, - work: impl Future + Send + 'static, - ) -> oneshot::Receiver { + pub fn run(&self, work: impl Future + Send + 'static) -> oneshot::Receiver { let (tx, rx) = oneshot::channel(); self.spawn(async move { let _ = tx.send(work.await); From 5c453854b71d4eeaa15063b0a26374b88b6bc47c Mon Sep 17 00:00:00 2001 From: Schuyler Goodman Date: Thu, 15 Jan 2026 13:18:21 -0500 Subject: [PATCH 12/29] Return T from Spawner::run --- crates/arty/src/spawner.rs | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/crates/arty/src/spawner.rs b/crates/arty/src/spawner.rs index ae0d8460..1c947c10 100644 --- a/crates/arty/src/spawner.rs +++ b/crates/arty/src/spawner.rs @@ -9,8 +9,6 @@ use std::sync::Arc; use futures_channel::oneshot; -pub use oneshot::Canceled; - type BoxedFuture = Pin + Send>>; type SpawnFn = dyn Fn(BoxedFuture) + Send + Sync; @@ -163,11 +161,13 @@ impl Spawner { } } - /// Runs an async task and returns a receiver for its result. + /// Runs an async task and returns its result. + /// + /// The task runs independently. /// - /// The task runs independently. Await the returned receiver to get the - /// result. If the task panics or is cancelled, the receiver returns - /// [`Canceled`]. + /// # Panics + /// + /// Panics if the spawned task panics before producing a result. /// /// # Examples /// @@ -177,16 +177,19 @@ impl Spawner { /// # #[tokio::main] /// # async fn main() { /// let spawner = Spawner::tokio(); - /// let result = spawner.run(async { 1 + 1 }); - /// assert_eq!(result.await.unwrap(), 2); + /// let result = spawner.run(async { 1 + 1 }).await; + /// assert_eq!(result, 2); /// # } /// ``` - pub fn run(&self, work: impl Future + Send + 'static) -> oneshot::Receiver { + pub async fn run( + &self, + work: impl Future + Send + 'static, + ) -> T { let (tx, rx) = oneshot::channel(); self.spawn(async move { let _ = tx.send(work.await); }); - rx + rx.await.expect("spawned task panicked") } } @@ -236,8 +239,8 @@ mod tests { #[tokio::test] async fn tokio_run() { let spawner = Spawner::tokio(); - let rx = spawner.run(async { 42 }); - assert_eq!(rx.await.unwrap(), 42); + let result = spawner.run(async { 42 }).await; + assert_eq!(result, 42); } #[test] @@ -246,7 +249,7 @@ mod tests { std::thread::spawn(move || futures::executor::block_on(fut)); }); - let rx = spawner.run(async { 42 }); - assert_eq!(futures::executor::block_on(rx).unwrap(), 42); + let result = futures::executor::block_on(spawner.run(async { 42 })); + assert_eq!(result, 42); } } From fef891aa1258bda6bcdf0ec7a97e132e61643186 Mon Sep 17 00:00:00 2001 From: Schuyler Goodman Date: Thu, 15 Jan 2026 13:22:23 -0500 Subject: [PATCH 13/29] Fix clippy/fmt/error --- crates/arty/Cargo.toml | 6 ------ crates/arty/examples/custom.rs | 2 +- crates/arty/examples/tokio.rs | 2 +- crates/arty/src/lib.rs | 2 +- crates/arty/src/spawner.rs | 10 +++------- 5 files changed, 6 insertions(+), 16 deletions(-) diff --git a/crates/arty/Cargo.toml b/crates/arty/Cargo.toml index c73b2cf3..ec8e1257 100644 --- a/crates/arty/Cargo.toml +++ b/crates/arty/Cargo.toml @@ -16,12 +16,6 @@ license.workspace = true homepage.workspace = true repository.workspace = true -[package.metadata.cargo_check_external_types] -allowed_external_types = [ - "futures_channel::oneshot::Canceled", - "futures_channel::oneshot::Receiver", -] - [package.metadata.docs.rs] all-features = true diff --git a/crates/arty/examples/custom.rs b/crates/arty/examples/custom.rs index 9edd0155..9256aae5 100644 --- a/crates/arty/examples/custom.rs +++ b/crates/arty/examples/custom.rs @@ -21,7 +21,7 @@ fn main() { // Retrieve a result using run let rx = spawner.run(async { 1 + 1 }); - let value = futures::executor::block_on(rx).unwrap(); + let value = futures::executor::block_on(rx); println!("Got result: {value}"); // Wait for background task diff --git a/crates/arty/examples/tokio.rs b/crates/arty/examples/tokio.rs index a60f1e23..b3f0bc94 100644 --- a/crates/arty/examples/tokio.rs +++ b/crates/arty/examples/tokio.rs @@ -23,7 +23,7 @@ async fn main() { }); // Retrieve a result using run - let value = spawner.run(async { 1 + 1 }).await.unwrap(); + let value = spawner.run(async { 1 + 1 }).await; println!("Got result: {value}"); // Wait for background task diff --git a/crates/arty/src/lib.rs b/crates/arty/src/lib.rs index 87fc0a57..3f715841 100644 --- a/crates/arty/src/lib.rs +++ b/crates/arty/src/lib.rs @@ -55,4 +55,4 @@ mod spawner; -pub use spawner::{Canceled, Spawner}; +pub use spawner::Spawner; diff --git a/crates/arty/src/spawner.rs b/crates/arty/src/spawner.rs index 1c947c10..a170a783 100644 --- a/crates/arty/src/spawner.rs +++ b/crates/arty/src/spawner.rs @@ -60,7 +60,7 @@ type SpawnFn = dyn Fn(BoxedFuture) + Send + Sync; /// # #[tokio::main] /// # async fn main() { /// let spawner = Spawner::tokio(); -/// let value = spawner.run(async { 1 + 1 }).await.unwrap(); +/// let value = spawner.run(async { 1 + 1 }).await; /// assert_eq!(value, 2); /// # } /// ``` @@ -80,8 +80,7 @@ type SpawnFn = dyn Fn(BoxedFuture) + Send + Sync; /// .run(async { /// if true { Ok(42) } else { Err("something went wrong") } /// }) -/// .await -/// .unwrap(); +/// .await; /// /// match result { /// Ok(value) => println!("Got {value}"), @@ -181,10 +180,7 @@ impl Spawner { /// assert_eq!(result, 2); /// # } /// ``` - pub async fn run( - &self, - work: impl Future + Send + 'static, - ) -> T { + pub async fn run(&self, work: impl Future + Send + 'static) -> T { let (tx, rx) = oneshot::channel(); self.spawn(async move { let _ = tx.send(work.await); From 706447e31127c4c26c343c123a821f76472c9884 Mon Sep 17 00:00:00 2001 From: Schuyler Goodman Date: Thu, 15 Jan 2026 13:38:52 -0500 Subject: [PATCH 14/29] Update README --- crates/arty/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/arty/README.md b/crates/arty/README.md index 74d8f0b8..89ea293a 100644 --- a/crates/arty/README.md +++ b/crates/arty/README.md @@ -61,6 +61,6 @@ spawner.spawn(async { This crate was developed as part of The Oxidizer Project. Browse this crate's source code. - [__cargo_doc2readme_dependencies_info]: ggGkYW0CYXSEGy4k8ldDFPOhG2VNeXtD5nnKG6EPY6OfW5wBG8g18NOFNdxpYXKEGy8QLYWQ6TvkGzOuS19rYB8RG4HN5vQgcR69G8qNc7VUYU_3YWSBgmRhcnR5ZTAuMS4w + [__cargo_doc2readme_dependencies_info]: ggGkYW0CYXSEGy4k8ldDFPOhG2VNeXtD5nnKG6EPY6OfW5wBG8g18NOFNdxpYXKEGz2vPTqTl9UCG38obYO2K--jG0X7KlikClvvG6i1Pg-T7jGeYWSBgmRhcnR5ZTAuMS4w [__link0]: https://docs.rs/arty/0.1.0/arty/?search=Spawner - [__link1]: https://docs.rs/arty/0.1.0/arty/?search=Spawner::Tokio + [__link1]: https://docs.rs/arty/0.1.0/arty/?search=Spawner::tokio From fe599df2d0a3e7366b3f5b3471c7d330304bd733 Mon Sep 17 00:00:00 2001 From: Schuyler Goodman Date: Thu, 15 Jan 2026 14:56:57 -0500 Subject: [PATCH 15/29] Fix code coverage --- crates/arty/src/spawner.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/crates/arty/src/spawner.rs b/crates/arty/src/spawner.rs index a170a783..5ce81f32 100644 --- a/crates/arty/src/spawner.rs +++ b/crates/arty/src/spawner.rs @@ -248,4 +248,11 @@ mod tests { let result = futures::executor::block_on(spawner.run(async { 42 })); assert_eq!(result, 42); } + + #[test] + fn custom_spawner_debug() { + let spawner = Spawner::custom(|_| {}); + let debug_str = format!("{spawner:?}"); + assert!(debug_str.contains("CustomSpawner")); + } } From 25b462a01df5d0fd2b131d243e3c5719a26f773c Mon Sep 17 00:00:00 2001 From: Schuyler Goodman Date: Fri, 16 Jan 2026 10:21:47 -0500 Subject: [PATCH 16/29] Fix some missed renames wing -> arty --- CHANGELOG.md | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e9ee27d..6b14ee1d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ Please see each crate's change log below: +- [`arty`](./crates/arty/CHANGELOG.md) - [`bytesbuf`](./crates/bytesbuf/CHANGELOG.md) - [`bytesbuf_io`](./crates/bytesbuf_io/CHANGELOG.md) - [`data_privacy`](./crates/data_privacy/CHANGELOG.md) @@ -18,4 +19,3 @@ Please see each crate's change log below: - [`thread_aware_macros`](./crates/thread_aware_macros/CHANGELOG.md) - [`thread_aware_macros_impl`](./crates/thread_aware_macros_impl/CHANGELOG.md) - [`tick`](./crates/tick/CHANGELOG.md) -- [`wing`](./crates/wing/CHANGELOG.md) diff --git a/README.md b/README.md index b7ad26e4..60c5d9fc 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ This repository contains a set of crates that help you build robust highly scala These are the crates built out of this repo: +- [`arty`](./crates/arty/README.md) - A generic future spawner compatible with any async runtime - [`bytesbuf`](./crates/bytesbuf/README.md) - Types for creating and manipulating byte sequences. - [`bytesbuf_io`](./crates/bytesbuf_io/README.md) - Asynchronous I/O abstractions expressed via `bytesbuf` types. - [`data_privacy`](./crates/data_privacy/README.md) - Mechanisms to classify, manipulate, and redact sensitive data. @@ -41,7 +42,6 @@ These are the crates built out of this repo: - [`thread_aware_macros`](./crates/thread_aware_macros/README.md) - Macros for the `thread_aware` crate. - [`thread_aware_macros_impl`](./crates/thread_aware_macros_impl/README.md) - Macros for the `thread_aware` crate. - [`tick`](./crates/tick/README.md) - Provides primitives to interact with and manipulate machine time. -- [`wing`](./crates/wing/README.md) - A generic future spawner compatible with any async runtime ## About this Repo From cc04c3ec09b90e5dc53052cb6f41ab64e775cd9a Mon Sep 17 00:00:00 2001 From: Schuyler Goodman Date: Fri, 16 Jan 2026 11:42:43 -0500 Subject: [PATCH 17/29] Better logo, some readme updates --- README.md | 2 +- crates/arty/README.md | 4 ++-- crates/arty/favicon.ico | 4 ++-- crates/arty/logo.png | 4 ++-- crates/arty/src/lib.rs | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 60c5d9fc..d64b531d 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ This repository contains a set of crates that help you build robust highly scala These are the crates built out of this repo: -- [`arty`](./crates/arty/README.md) - A generic future spawner compatible with any async runtime +- [`arty`](./crates/arty/README.md) - Async runtime abstractions. - [`bytesbuf`](./crates/bytesbuf/README.md) - Types for creating and manipulating byte sequences. - [`bytesbuf_io`](./crates/bytesbuf_io/README.md) - Asynchronous I/O abstractions expressed via `bytesbuf` types. - [`data_privacy`](./crates/data_privacy/README.md) - Mechanisms to classify, manipulate, and redact sensitive data. diff --git a/crates/arty/README.md b/crates/arty/README.md index 89ea293a..8c6082ef 100644 --- a/crates/arty/README.md +++ b/crates/arty/README.md @@ -13,7 +13,7 @@ -Runtime-agnostic async task spawning. +Async runtime abstractions This crate provides a [`Spawner`][__link0] type that abstracts task spawning across different async runtimes without generic infection. @@ -61,6 +61,6 @@ spawner.spawn(async { This crate was developed as part of The Oxidizer Project. Browse this crate's source code. - [__cargo_doc2readme_dependencies_info]: ggGkYW0CYXSEGy4k8ldDFPOhG2VNeXtD5nnKG6EPY6OfW5wBG8g18NOFNdxpYXKEGz2vPTqTl9UCG38obYO2K--jG0X7KlikClvvG6i1Pg-T7jGeYWSBgmRhcnR5ZTAuMS4w + [__cargo_doc2readme_dependencies_info]: ggGkYW0CYXSEGy4k8ldDFPOhG2VNeXtD5nnKG6EPY6OfW5wBG8g18NOFNdxpYXKEG97CGOXohRcKG6oqJHGRKB_UG6aHakh4orawGyQoqFPoHfauYWSBgmRhcnR5ZTAuMS4w [__link0]: https://docs.rs/arty/0.1.0/arty/?search=Spawner [__link1]: https://docs.rs/arty/0.1.0/arty/?search=Spawner::tokio diff --git a/crates/arty/favicon.ico b/crates/arty/favicon.ico index ccae8114..42c826b9 100644 --- a/crates/arty/favicon.ico +++ b/crates/arty/favicon.ico @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e48225cb42c8ac02df5dd4a9bdba29c1e8d10436cc27055d6a021bcb951d4145 -size 480683 +oid sha256:f2a7cb259fa5c491b39fd9e2c2e32aa25497b818198957c7d1f90b02c896e524 +size 15406 diff --git a/crates/arty/logo.png b/crates/arty/logo.png index 1562ae9f..ae2ae5ce 100644 --- a/crates/arty/logo.png +++ b/crates/arty/logo.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6bf624b54edbaeb8bf0d961e895a86e09b18502fe6c761d00748317883dd09b8 -size 62560 +oid sha256:dfdc338b2229ac33c210aa8093128f2236c7a88d20eabce213b0f9b090fd24c8 +size 41426 diff --git a/crates/arty/src/lib.rs b/crates/arty/src/lib.rs index 3f715841..89b16560 100644 --- a/crates/arty/src/lib.rs +++ b/crates/arty/src/lib.rs @@ -5,7 +5,7 @@ #![cfg_attr(docsrs, feature(doc_cfg))] #![warn(missing_docs)] -//! Runtime-agnostic async task spawning. +//! Async runtime abstractions //! //! This crate provides a [`Spawner`] type that abstracts task spawning across //! different async runtimes without generic infection. From b897f60b9d6e1578c3598d566b476e13f8a82ea4 Mon Sep 17 00:00:00 2001 From: Schuyler Goodman Date: Fri, 16 Jan 2026 14:56:05 -0500 Subject: [PATCH 18/29] Add JoinHandle, remove run --- Cargo.lock | 343 +++++++++++++++++++++++++++++-- crates/arty/Cargo.toml | 21 +- crates/arty/benches/BENCHMARK.md | 28 +++ crates/arty/benches/spawner.rs | 45 ++++ crates/arty/examples/custom.rs | 8 +- crates/arty/examples/tokio.rs | 6 +- crates/arty/src/custom.rs | 29 +++ crates/arty/src/handle.rs | 49 +++++ crates/arty/src/lib.rs | 14 +- crates/arty/src/spawner.rs | 143 ++++--------- crates/arty/tests/spawner.rs | 72 +++++++ 11 files changed, 614 insertions(+), 144 deletions(-) create mode 100644 crates/arty/benches/BENCHMARK.md create mode 100644 crates/arty/benches/spawner.rs create mode 100644 crates/arty/src/custom.rs create mode 100644 crates/arty/src/handle.rs create mode 100644 crates/arty/tests/spawner.rs diff --git a/Cargo.lock b/Cargo.lock index 7fbbf605..f5e49bbe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -48,12 +48,144 @@ checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" name = "arty" version = "0.1.0" dependencies = [ + "async-std", + "criterion", "futures", "futures-channel", "tick", "tokio", ] +[[package]] +name = "async-attributes" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3203e79f4dd9bdda415ed03cf14dae5a2bf775c683a00f94e9cd1faf0f596e5" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "async-channel" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" +dependencies = [ + "concurrent-queue", + "event-listener 2.5.3", + "futures-core", +] + +[[package]] +name = "async-channel" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-executor" +version = "1.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497c00e0fd83a72a79a39fcbd8e3e2f055d6f6c7e025f3b3d91f4f8e76527fb8" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "pin-project-lite", + "slab", +] + +[[package]] +name = "async-global-executor" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" +dependencies = [ + "async-channel 2.5.0", + "async-executor", + "async-io", + "async-lock", + "blocking", + "futures-lite", + "once_cell", +] + +[[package]] +name = "async-io" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc" +dependencies = [ + "autocfg", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix", + "slab", + "windows-sys 0.61.2", +] + +[[package]] +name = "async-lock" +version = "3.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" +dependencies = [ + "event-listener 5.4.1", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-std" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c8e079a4ab67ae52b7403632e4618815d6db36d2a010cfe41b02c1b1578f93b" +dependencies = [ + "async-attributes", + "async-channel 1.9.0", + "async-global-executor", + "async-io", + "async-lock", + "crossbeam-utils", + "futures-channel", + "futures-core", + "futures-io", + "futures-lite", + "gloo-timers", + "kv-log-macro", + "log", + "memchr", + "once_cell", + "pin-project-lite", + "pin-utils", + "slab", + "wasm-bindgen-futures", +] + +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "autocfg" version = "1.5.0" @@ -76,6 +208,19 @@ version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" +[[package]] +name = "blocking" +version = "1.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" +dependencies = [ + "async-channel 2.5.0", + "async-task", + "futures-io", + "futures-lite", + "piper", +] + [[package]] name = "bumpalo" version = "3.19.1" @@ -222,6 +367,15 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "convert_case" version = "0.10.0" @@ -279,6 +433,12 @@ dependencies = [ "itertools 0.13.0", ] +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + [[package]] name = "crunchy" version = "0.2.4" @@ -318,7 +478,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -349,7 +509,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn", + "syn 2.0.111", "unicode-xid", ] @@ -394,7 +554,7 @@ checksum = "0b0713d5c1d52e774c5cd7bb8b043d7c0fc4f921abfb678556140bfbe6ab2364" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -419,6 +579,33 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "event-listener" +version = "5.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" +dependencies = [ + "event-listener 5.4.1", + "pin-project-lite", +] + [[package]] name = "fastrand" version = "2.3.0" @@ -474,7 +661,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -525,6 +712,19 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" +[[package]] +name = "futures-lite" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + [[package]] name = "futures-macro" version = "0.3.31" @@ -533,7 +733,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -584,6 +784,18 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" +[[package]] +name = "gloo-timers" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + [[package]] name = "half" version = "2.7.1" @@ -620,6 +832,12 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + [[package]] name = "iana-time-zone" version = "0.1.64" @@ -725,7 +943,7 @@ checksum = "b787bebb543f8969132630c51fd0afab173a86c6abae56ff3b9e5e3e3f9f6e58" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -753,6 +971,15 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "kv-log-macro" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" +dependencies = [ + "log", +] + [[package]] name = "layered" version = "0.1.0" @@ -802,6 +1029,9 @@ name = "log" version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +dependencies = [ + "value-bag", +] [[package]] name = "many_cpus" @@ -853,7 +1083,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -870,7 +1100,7 @@ checksum = "67670e6848468ee39b823ed93add8bd2adb66a132b0c25fce6e60af0b81d930a" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -952,7 +1182,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -977,6 +1207,12 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + [[package]] name = "parking_lot" version = "0.12.5" @@ -1083,6 +1319,31 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "piper" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + +[[package]] +name = "polling" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi", + "pin-project-lite", + "rustix", + "windows-sys 0.61.2", +] + [[package]] name = "portable-atomic" version = "1.13.0" @@ -1156,7 +1417,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn", + "syn 2.0.111", ] [[package]] @@ -1376,7 +1637,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -1468,6 +1729,17 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "syn" version = "2.0.111" @@ -1556,7 +1828,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -1567,7 +1839,7 @@ checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -1585,7 +1857,7 @@ name = "thread_aware_macros" version = "0.6.0" dependencies = [ "mutants", - "syn", + "syn 2.0.111", "thread_aware_macros_impl", ] @@ -1597,7 +1869,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -1712,7 +1984,7 @@ checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -1834,7 +2106,7 @@ checksum = "70977707304198400eb4835a78f6a9f928bf41bba420deb8fdb175cd965d77a7" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -1876,6 +2148,12 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +[[package]] +name = "value-bag" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ba6f5989077681266825251a52748b8c1d8a4ad098cc37e440103d0ea717fc0" + [[package]] name = "walkdir" version = "2.5.0" @@ -1908,6 +2186,19 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "836d9622d604feee9e5de25ac10e3ea5f2d65b41eac0d9ce72eb5deae707ce7c" +dependencies = [ + "cfg-if", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.106" @@ -1927,7 +2218,7 @@ dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn", + "syn 2.0.111", "wasm-bindgen-shared", ] @@ -1940,6 +2231,16 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "web-sys" +version = "0.3.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b32828d774c412041098d182a8b38b16ea816958e07cf40eec2bc080ae137ac" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "winapi-util" version = "0.1.11" @@ -2002,7 +2303,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -2013,7 +2314,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -2232,7 +2533,7 @@ checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] diff --git a/crates/arty/Cargo.toml b/crates/arty/Cargo.toml index ec8e1257..2238a096 100644 --- a/crates/arty/Cargo.toml +++ b/crates/arty/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "arty" -description = "A generic future spawner compatible with any async runtime" +description = "A generic task spawner compatible with any async runtime" version = "0.1.0" readme = "README.md" keywords = ["oxidizer", "async", "runtime", "futures"] @@ -20,17 +20,32 @@ repository.workspace = true all-features = true [features] -default = ["tokio"] +default = ["tokio", "custom"] tokio = ["dep:tokio"] +custom = ["dep:futures-channel"] [dependencies] -futures-channel = { workspace = true, features = ["alloc"] } +futures-channel = { workspace = true, features = ["alloc"], optional = true } tokio = { workspace = true, features = ["rt"], optional = true } [dev-dependencies] +async-std = { version = "1.13", features = ["attributes"] } +criterion = { workspace = true } futures = { workspace = true, features = ["executor"] } tick = { workspace = true, features = ["tokio"] } tokio = { workspace = true, features = ["macros", "rt-multi-thread", "sync"] } +[[bench]] +name = "spawner" +harness = false + +[[example]] +name = "custom" +requires-features = ["custom"] + +[[example]] +name = "tokio" +requires-features = ["tokio"] + [lints] workspace = true diff --git a/crates/arty/benches/BENCHMARK.md b/crates/arty/benches/BENCHMARK.md new file mode 100644 index 00000000..3ecb4263 --- /dev/null +++ b/crates/arty/benches/BENCHMARK.md @@ -0,0 +1,28 @@ +# Spawner Benchmarks + +Compares the overhead of using `Spawner::spawn()` vs calling runtime spawn functions directly. + +## Results + +| Benchmark | Time | +|-----------|------| +| `tokio_direct` | ~12 µs | +| `tokio_via_spawner` | ~12 µs | +| `async_std_direct` | ~35 µs | +| `async_std_via_spawner` | ~21 µs | + +## Analysis + +**Tokio**: The spawner abstraction has **zero overhead**. Both paths use tokio's `JoinHandle` directly, so the only cost is the match statement which is negligible. + +**async-std**: The custom spawner uses a oneshot channel to return results, which adds ~14 µs overhead compared to async-std's native `JoinHandle`. However, it's still faster than async-std's direct approach in absolute terms due to the channel implementation. + +## Key Takeaway + +For Tokio (the default), the `Spawner` abstraction is essentially free. For custom spawners, there's a small fixed overhead from the oneshot channel (~few µs), which is negligible for any real-world task. + +## Running + +```sh +cargo bench -p arty --bench spawner --features custom +``` diff --git a/crates/arty/benches/spawner.rs b/crates/arty/benches/spawner.rs new file mode 100644 index 00000000..fb81d522 --- /dev/null +++ b/crates/arty/benches/spawner.rs @@ -0,0 +1,45 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#![allow( + missing_docs, + clippy::unwrap_used, + reason = "Benchmarks don't require documentation and should fail fast on errors" +)] + +use arty::Spawner; +use criterion::{Criterion, criterion_group, criterion_main}; + +fn entry(c: &mut Criterion) { + let mut group = c.benchmark_group("spawner"); + + // Tokio benchmarks + let rt = tokio::runtime::Runtime::new().unwrap(); + let tokio_spawner = Spawner::tokio(); + + group.bench_function("tokio_direct", |b| { + b.iter(|| rt.block_on(async { tokio::spawn(async { 42 }).await.unwrap() })); + }); + + group.bench_function("tokio_via_spawner", |b| { + b.iter(|| rt.block_on(async { tokio_spawner.spawn(async { 42 }).await })); + }); + + // async-std benchmarks + let async_std_spawner = Spawner::custom(|fut| { + async_std::task::spawn(fut); + }); + + group.bench_function("async_std_direct", |b| { + b.iter(|| async_std::task::block_on(async { async_std::task::spawn(async { 42 }).await })); + }); + + group.bench_function("async_std_via_spawner", |b| { + b.iter(|| async_std::task::block_on(async { async_std_spawner.spawn(async { 42 }).await })); + }); + + group.finish(); +} + +criterion_group!(benches, entry); +criterion_main!(benches); diff --git a/crates/arty/examples/custom.rs b/crates/arty/examples/custom.rs index 9256aae5..5774ff09 100644 --- a/crates/arty/examples/custom.rs +++ b/crates/arty/examples/custom.rs @@ -14,14 +14,14 @@ fn main() { }); // Fire-and-forget: spawn a task without waiting for its result - spawner.spawn(async { + let _ = spawner.spawn(async { std::thread::sleep(Duration::from_millis(10)); println!("Background task completed!"); }); - // Retrieve a result using run - let rx = spawner.run(async { 1 + 1 }); - let value = futures::executor::block_on(rx); + // Retrieve a result by awaiting the JoinHandle + let handle = spawner.spawn(async { 1 + 1 }); + let value = futures::executor::block_on(handle); println!("Got result: {value}"); // Wait for background task diff --git a/crates/arty/examples/tokio.rs b/crates/arty/examples/tokio.rs index b3f0bc94..70ea0816 100644 --- a/crates/arty/examples/tokio.rs +++ b/crates/arty/examples/tokio.rs @@ -14,7 +14,7 @@ async fn main() { let spawner = Spawner::tokio(); // Fire-and-forget: spawn a task without waiting for its result - spawner.spawn({ + let _ = spawner.spawn({ let clock = clock.clone(); async move { clock.delay(Duration::from_millis(10)).await; @@ -22,8 +22,8 @@ async fn main() { } }); - // Retrieve a result using run - let value = spawner.run(async { 1 + 1 }).await; + // Retrieve a result by awaiting the JoinHandle + let value = spawner.spawn(async { 1 + 1 }).await; println!("Got result: {value}"); // Wait for background task diff --git a/crates/arty/src/custom.rs b/crates/arty/src/custom.rs new file mode 100644 index 00000000..398364b9 --- /dev/null +++ b/crates/arty/src/custom.rs @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use std::{fmt::Debug, pin::Pin, sync::Arc}; + +use futures_channel::oneshot; + +pub(crate) type BoxedFuture = Pin + Send>>; +type SpawnFn = dyn Fn(BoxedFuture) + Send + Sync; + +/// Internal wrapper for custom spawn functions. +#[derive(Clone)] +pub(crate) struct CustomSpawner(pub(crate) Arc); + +impl CustomSpawner { + pub(crate) fn call(&self, work: impl Future + Send + 'static) -> oneshot::Receiver { + let (tx, rx) = oneshot::channel(); + (self.0)(Box::pin(async move { + let _ = tx.send(work.await); + })); + rx + } +} + +impl Debug for CustomSpawner { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("CustomSpawner").finish_non_exhaustive() + } +} diff --git a/crates/arty/src/handle.rs b/crates/arty/src/handle.rs new file mode 100644 index 00000000..e64d4928 --- /dev/null +++ b/crates/arty/src/handle.rs @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +//! [`JoinHandle`] for awaiting spawned task results. + +use std::{ + fmt::Debug, + pin::Pin, + task::{Context, Poll}, +}; + +#[cfg(feature = "custom")] +use futures_channel::oneshot; + +/// A handle to a spawned task that can be awaited to retrieve its result. +/// +/// This is returned by [`Spawner::spawn`](crate::Spawner::spawn) and implements +/// [`Future`] to allow awaiting the task's completion. +/// +/// # Panics +/// +/// Awaiting a `JoinHandle` will panic if the spawned task panicked. +pub struct JoinHandle(pub(crate) JoinHandleInner); + +pub(crate) enum JoinHandleInner { + #[cfg(feature = "tokio")] + Tokio(::tokio::task::JoinHandle), + #[cfg(feature = "custom")] + Custom(oneshot::Receiver), +} + +impl Future for JoinHandle { + type Output = T; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + match &mut self.get_mut().0 { + #[cfg(feature = "tokio")] + JoinHandleInner::Tokio(jh) => Pin::new(jh).poll(cx).map(|res| res.expect("spawned task panicked")), + #[cfg(feature = "custom")] + JoinHandleInner::Custom(rx) => Pin::new(rx).poll(cx).map(|res| res.expect("spawned task panicked")), + } + } +} + +impl Debug for JoinHandle { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("JoinHandle").finish_non_exhaustive() + } +} diff --git a/crates/arty/src/lib.rs b/crates/arty/src/lib.rs index 89b16560..00fa3780 100644 --- a/crates/arty/src/lib.rs +++ b/crates/arty/src/lib.rs @@ -26,9 +26,8 @@ //! # #[tokio::main] //! # async fn main() { //! let spawner = Spawner::tokio(); -//! spawner.spawn(async { -//! println!("Task running!"); -//! }); +//! let result = spawner.spawn(async { 1 + 1 }).await; +//! assert_eq!(result, 2); //! # } //! ``` //! @@ -41,9 +40,8 @@ //! std::thread::spawn(move || futures::executor::block_on(fut)); //! }); //! -//! spawner.spawn(async { -//! println!("Running on custom runtime!"); -//! }); +//! // Returns a JoinHandle that can be awaited or dropped +//! let handle = spawner.spawn(async { 42 }); //! ``` //! //! # Features @@ -53,6 +51,10 @@ #![doc(html_logo_url = "https://media.githubusercontent.com/media/microsoft/oxidizer/refs/heads/main/crates/arty/logo.png")] #![doc(html_favicon_url = "https://media.githubusercontent.com/media/microsoft/oxidizer/refs/heads/main/crates/arty/favicon.ico")] +#[cfg(feature = "custom")] +mod custom; +mod handle; mod spawner; +pub use handle::JoinHandle; pub use spawner::Spawner; diff --git a/crates/arty/src/spawner.rs b/crates/arty/src/spawner.rs index 5ce81f32..5541f702 100644 --- a/crates/arty/src/spawner.rs +++ b/crates/arty/src/spawner.rs @@ -3,14 +3,14 @@ //! [`Spawner`] for plugging in runtime implementations. -use std::fmt::Debug; -use std::pin::Pin; -use std::sync::Arc; +use std::{fmt::Debug, sync::Arc}; -use futures_channel::oneshot; - -type BoxedFuture = Pin + Send>>; -type SpawnFn = dyn Fn(BoxedFuture) + Send + Sync; +#[cfg(feature = "tokio")] +use crate::handle::JoinHandleInner; +use crate::{ + custom::{BoxedFuture, CustomSpawner}, + handle::JoinHandle, +}; /// Runtime-agnostic task spawner. /// @@ -28,9 +28,10 @@ type SpawnFn = dyn Fn(BoxedFuture) + Send + Sync; /// # #[tokio::main] /// # async fn main() { /// let spawner = Spawner::tokio(); -/// spawner.spawn(async { +/// let handle = spawner.spawn(async { /// println!("Task running!"); /// }); +/// handle.await; // Wait for task to complete /// # } /// ``` /// @@ -44,15 +45,16 @@ type SpawnFn = dyn Fn(BoxedFuture) + Send + Sync; /// std::thread::spawn(move || futures::executor::block_on(fut)); /// }); /// -/// spawner.spawn(async { +/// let handle = spawner.spawn(async { /// println!("Running on custom runtime!"); /// }); +/// // handle can be awaited or dropped (fire-and-forget) /// # } /// ``` /// /// ## Getting Results /// -/// Use [`run`](Spawner::run) to retrieve a value from the task: +/// Await the [`JoinHandle`](crate::JoinHandle) to retrieve a value from the task: /// /// ```rust /// use arty::Spawner; @@ -60,7 +62,7 @@ type SpawnFn = dyn Fn(BoxedFuture) + Send + Sync; /// # #[tokio::main] /// # async fn main() { /// let spawner = Spawner::tokio(); -/// let value = spawner.run(async { 1 + 1 }).await; +/// let value = spawner.spawn(async { 1 + 1 }).await; /// assert_eq!(value, 2); /// # } /// ``` @@ -77,7 +79,7 @@ type SpawnFn = dyn Fn(BoxedFuture) + Send + Sync; /// let spawner = Spawner::tokio(); /// /// let result = spawner -/// .run(async { +/// .spawn(async { /// if true { Ok(42) } else { Err("something went wrong") } /// }) /// .await; @@ -95,6 +97,7 @@ pub struct Spawner(SpawnerKind); enum SpawnerKind { #[cfg(feature = "tokio")] Tokio, + #[cfg(feature = "custom")] Custom(CustomSpawner), } @@ -113,9 +116,8 @@ impl Spawner { /// # #[tokio::main] /// # async fn main() { /// let spawner = Spawner::tokio(); - /// spawner.spawn(async { - /// println!("Running on Tokio!"); - /// }); + /// let result = spawner.spawn(async { 42 }).await; + /// assert_eq!(result, 42); /// # } /// ``` #[must_use] @@ -139,6 +141,8 @@ impl Spawner { /// std::thread::spawn(move || futures::executor::block_on(fut)); /// }); /// ``` + #[cfg(feature = "custom")] + #[cfg_attr(docsrs, doc(cfg(feature = "custom")))] pub fn custom(f: F) -> Self where F: Fn(BoxedFuture) + Send + Sync + 'static, @@ -148,25 +152,12 @@ impl Spawner { /// Spawns an async task on the runtime. /// - /// The task runs independently and its result is discarded. Use - /// [`run`](Self::run) to retrieve results. - pub fn spawn(&self, work: impl Future + Send + 'static) { - match &self.0 { - #[cfg(feature = "tokio")] - SpawnerKind::Tokio => { - ::tokio::spawn(work); - } - SpawnerKind::Custom(c) => (c.0)(Box::pin(work)), - } - } - - /// Runs an async task and returns its result. - /// - /// The task runs independently. + /// Returns a [`JoinHandle`] that can be awaited to retrieve the task's result, + /// or dropped to run the task in fire-and-forget mode. /// /// # Panics /// - /// Panics if the spawned task panics before producing a result. + /// Awaiting the returned `JoinHandle` will panic if the spawned task panics. /// /// # Examples /// @@ -176,83 +167,21 @@ impl Spawner { /// # #[tokio::main] /// # async fn main() { /// let spawner = Spawner::tokio(); - /// let result = spawner.run(async { 1 + 1 }).await; - /// assert_eq!(result, 2); + /// + /// // Await to get the result + /// let value = spawner.spawn(async { 1 + 1 }).await; + /// assert_eq!(value, 2); + /// + /// // Or fire-and-forget by dropping the handle + /// let _ = spawner.spawn(async { println!("background task") }); /// # } /// ``` - pub async fn run(&self, work: impl Future + Send + 'static) -> T { - let (tx, rx) = oneshot::channel(); - self.spawn(async move { - let _ = tx.send(work.await); - }); - rx.await.expect("spawned task panicked") - } -} - -/// Internal wrapper for custom spawn functions. -#[derive(Clone)] -pub(crate) struct CustomSpawner(Arc); - -impl Debug for CustomSpawner { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("CustomSpawner").finish_non_exhaustive() - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[cfg(feature = "tokio")] - #[tokio::test] - async fn tokio_spawn_fire_and_forget() { - let spawner = Spawner::tokio(); - let (tx, rx) = tokio::sync::oneshot::channel(); - - spawner.spawn(async move { - tx.send(42).unwrap(); - }); - - assert_eq!(rx.await.unwrap(), 42); - } - - #[test] - fn custom_spawn() { - let spawner = Spawner::custom(|fut| { - std::thread::spawn(move || futures::executor::block_on(fut)); - }); - - let (tx, rx) = std::sync::mpsc::channel(); - - spawner.spawn(async move { - tx.send(42).unwrap(); - }); - - assert_eq!(rx.recv().unwrap(), 42); - } - - #[cfg(feature = "tokio")] - #[tokio::test] - async fn tokio_run() { - let spawner = Spawner::tokio(); - let result = spawner.run(async { 42 }).await; - assert_eq!(result, 42); - } - - #[test] - fn custom_run() { - let spawner = Spawner::custom(|fut| { - std::thread::spawn(move || futures::executor::block_on(fut)); - }); - - let result = futures::executor::block_on(spawner.run(async { 42 })); - assert_eq!(result, 42); - } - - #[test] - fn custom_spawner_debug() { - let spawner = Spawner::custom(|_| {}); - let debug_str = format!("{spawner:?}"); - assert!(debug_str.contains("CustomSpawner")); + pub fn spawn(&self, work: impl Future + Send + 'static) -> JoinHandle { + match &self.0 { + #[cfg(feature = "tokio")] + SpawnerKind::Tokio => JoinHandle(JoinHandleInner::Tokio(::tokio::spawn(work))), + #[cfg(feature = "custom")] + SpawnerKind::Custom(c) => JoinHandle(JoinHandleInner::Custom(c.call(work))), + } } } diff --git a/crates/arty/tests/spawner.rs b/crates/arty/tests/spawner.rs new file mode 100644 index 00000000..28390e90 --- /dev/null +++ b/crates/arty/tests/spawner.rs @@ -0,0 +1,72 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +//! Tests for `Spawner` implementations. + +use arty::Spawner; + +#[cfg(feature = "tokio")] +#[tokio::test] +async fn tokio_spawn_and_await() { + let spawner = Spawner::tokio(); + let result = spawner.spawn(async { 42 }).await; + assert_eq!(result, 42); +} + +#[cfg(feature = "tokio")] +#[tokio::test] +async fn tokio_spawn_fire_and_forget() { + let spawner = Spawner::tokio(); + let (tx, rx) = tokio::sync::oneshot::channel(); + + let _ = spawner.spawn(async move { + tx.send(42).unwrap(); + }); + + assert_eq!(rx.await.unwrap(), 42); +} + +#[cfg(feature = "custom")] +#[test] +fn custom_spawn_and_await() { + let spawner = Spawner::custom(|fut| { + std::thread::spawn(move || futures::executor::block_on(fut)); + }); + + let result = futures::executor::block_on(spawner.spawn(async { 42 })); + assert_eq!(result, 42); +} + +#[cfg(feature = "custom")] +#[test] +fn custom_spawn_fire_and_forget() { + let spawner = Spawner::custom(|fut| { + std::thread::spawn(move || futures::executor::block_on(fut)); + }); + + let (tx, rx) = std::sync::mpsc::channel(); + + let _ = spawner.spawn(async move { + tx.send(42).unwrap(); + }); + + assert_eq!(rx.recv().unwrap(), 42); +} + +#[cfg(feature = "custom")] +#[test] +fn custom_spawner_debug() { + let spawner = Spawner::custom(|_| {}); + let debug_str = format!("{spawner:?}"); + assert!(debug_str.contains("CustomSpawner")); +} + +#[cfg(feature = "tokio")] +#[tokio::test] +async fn join_handle_debug() { + let spawner = Spawner::tokio(); + let handle = spawner.spawn(async { 42 }); + let debug_str = format!("{handle:?}"); + assert!(debug_str.contains("JoinHandle")); + let _ = handle.await; +} From b2578ef3a48a0a1afa96b396134a71413fc2a507 Mon Sep 17 00:00:00 2001 From: Schuyler Goodman Date: Fri, 16 Jan 2026 15:14:46 -0500 Subject: [PATCH 19/29] Add custom feature --- crates/arty/Cargo.toml | 7 ++++--- crates/arty/README.md | 14 +++++++------- crates/arty/benches/BENCHMARK.md | 4 ++-- crates/arty/examples/custom.rs | 13 ++++++++----- crates/arty/examples/tokio.rs | 16 +++++++++------- crates/arty/src/lib.rs | 3 ++- crates/arty/src/spawner.rs | 18 +++++++++--------- crates/arty/tests/handle.rs | 16 ++++++++++++++++ crates/arty/tests/spawner.rs | 30 ++++++++++++------------------ 9 files changed, 69 insertions(+), 52 deletions(-) create mode 100644 crates/arty/tests/handle.rs diff --git a/crates/arty/Cargo.toml b/crates/arty/Cargo.toml index 2238a096..ca46b32e 100644 --- a/crates/arty/Cargo.toml +++ b/crates/arty/Cargo.toml @@ -20,7 +20,7 @@ repository.workspace = true all-features = true [features] -default = ["tokio", "custom"] +default = ["tokio"] tokio = ["dep:tokio"] custom = ["dep:futures-channel"] @@ -38,14 +38,15 @@ tokio = { workspace = true, features = ["macros", "rt-multi-thread", "sync"] } [[bench]] name = "spawner" harness = false +required-features = ["tokio", "custom"] [[example]] name = "custom" -requires-features = ["custom"] +required-features = ["custom", "tokio"] [[example]] name = "tokio" -requires-features = ["tokio"] +required-features = ["tokio"] [lints] workspace = true diff --git a/crates/arty/README.md b/crates/arty/README.md index 8c6082ef..c3264ab2 100644 --- a/crates/arty/README.md +++ b/crates/arty/README.md @@ -32,9 +32,8 @@ different async runtimes without generic infection. use arty::Spawner; let spawner = Spawner::tokio(); -spawner.spawn(async { - println!("Task running!"); -}); +let result = spawner.spawn(async { 1 + 1 }).await; +assert_eq!(result, 2); ``` ### Custom Runtime @@ -46,14 +45,14 @@ let spawner = Spawner::custom(|fut| { std::thread::spawn(move || futures::executor::block_on(fut)); }); -spawner.spawn(async { - println!("Running on custom runtime!"); -}); +// Returns a JoinHandle that can be awaited or dropped +let handle = spawner.spawn(async { 42 }); ``` ## Features * `tokio` (default): Enables the [`Spawner::tokio`][__link1] constructor +* `custom`: Enables the [`Spawner::custom`][__link2] constructor
@@ -61,6 +60,7 @@ spawner.spawn(async { This crate was developed as part of The Oxidizer Project. Browse this crate's source code. - [__cargo_doc2readme_dependencies_info]: ggGkYW0CYXSEGy4k8ldDFPOhG2VNeXtD5nnKG6EPY6OfW5wBG8g18NOFNdxpYXKEG97CGOXohRcKG6oqJHGRKB_UG6aHakh4orawGyQoqFPoHfauYWSBgmRhcnR5ZTAuMS4w + [__cargo_doc2readme_dependencies_info]: ggGkYW0CYXSEGy4k8ldDFPOhG2VNeXtD5nnKG6EPY6OfW5wBG8g18NOFNdxpYXKEGyjnWqOXl0hfG5JzJZzygN1qG0YJmwqDZUQGGzO95yYeMZDOYWSBgmRhcnR5ZTAuMS4w [__link0]: https://docs.rs/arty/0.1.0/arty/?search=Spawner [__link1]: https://docs.rs/arty/0.1.0/arty/?search=Spawner::tokio + [__link2]: https://docs.rs/arty/0.1.0/arty/?search=Spawner::custom diff --git a/crates/arty/benches/BENCHMARK.md b/crates/arty/benches/BENCHMARK.md index 3ecb4263..ee732f36 100644 --- a/crates/arty/benches/BENCHMARK.md +++ b/crates/arty/benches/BENCHMARK.md @@ -15,11 +15,11 @@ Compares the overhead of using `Spawner::spawn()` vs calling runtime spawn funct **Tokio**: The spawner abstraction has **zero overhead**. Both paths use tokio's `JoinHandle` directly, so the only cost is the match statement which is negligible. -**async-std**: The custom spawner uses a oneshot channel to return results, which adds ~14 µs overhead compared to async-std's native `JoinHandle`. However, it's still faster than async-std's direct approach in absolute terms due to the channel implementation. +**async-std**: The custom spawner uses a oneshot channel to return results, which is ~14 µs faster than async-std's native `JoinHandle` in this benchmark. ## Key Takeaway -For Tokio (the default), the `Spawner` abstraction is essentially free. For custom spawners, there's a small fixed overhead from the oneshot channel (~few µs), which is negligible for any real-world task. +The `Spawner` abstraction has negligible overhead. For Tokio, it's essentially free since it uses the native `JoinHandle` directly. For custom spawners, the oneshot channel approach performs well. ## Running diff --git a/crates/arty/examples/custom.rs b/crates/arty/examples/custom.rs index 5774ff09..bf94a575 100644 --- a/crates/arty/examples/custom.rs +++ b/crates/arty/examples/custom.rs @@ -7,17 +7,20 @@ use std::time::Duration; use arty::Spawner; -fn main() { +#[tokio::main] +async fn main() { // Create a spawner that runs futures on background threads let spawner = Spawner::custom(|fut| { std::thread::spawn(move || futures::executor::block_on(fut)); }); // Fire-and-forget: spawn a task without waiting for its result - let _ = spawner.spawn(async { - std::thread::sleep(Duration::from_millis(10)); - println!("Background task completed!"); - }); + let () = spawner + .spawn(async { + std::thread::sleep(Duration::from_millis(10)); + println!("Background task completed!"); + }) + .await; // Retrieve a result by awaiting the JoinHandle let handle = spawner.spawn(async { 1 + 1 }); diff --git a/crates/arty/examples/tokio.rs b/crates/arty/examples/tokio.rs index 70ea0816..eb38d9f6 100644 --- a/crates/arty/examples/tokio.rs +++ b/crates/arty/examples/tokio.rs @@ -14,13 +14,15 @@ async fn main() { let spawner = Spawner::tokio(); // Fire-and-forget: spawn a task without waiting for its result - let _ = spawner.spawn({ - let clock = clock.clone(); - async move { - clock.delay(Duration::from_millis(10)).await; - println!("Background task completed!"); - } - }); + let () = spawner + .spawn({ + let clock = clock.clone(); + async move { + clock.delay(Duration::from_millis(10)).await; + println!("Background task completed!"); + } + }) + .await; // Retrieve a result by awaiting the JoinHandle let value = spawner.spawn(async { 1 + 1 }).await; diff --git a/crates/arty/src/lib.rs b/crates/arty/src/lib.rs index 00fa3780..7c0302d3 100644 --- a/crates/arty/src/lib.rs +++ b/crates/arty/src/lib.rs @@ -33,7 +33,7 @@ //! //! ## Custom Runtime //! -//! ```rust +//! ```rust,ignore //! use arty::Spawner; //! //! let spawner = Spawner::custom(|fut| { @@ -47,6 +47,7 @@ //! # Features //! //! - `tokio` (default): Enables the [`Spawner::tokio`] constructor +//! - `custom`: Enables the [`Spawner::custom`] constructor #![doc(html_logo_url = "https://media.githubusercontent.com/media/microsoft/oxidizer/refs/heads/main/crates/arty/logo.png")] #![doc(html_favicon_url = "https://media.githubusercontent.com/media/microsoft/oxidizer/refs/heads/main/crates/arty/favicon.ico")] diff --git a/crates/arty/src/spawner.rs b/crates/arty/src/spawner.rs index 5541f702..5dd03a76 100644 --- a/crates/arty/src/spawner.rs +++ b/crates/arty/src/spawner.rs @@ -3,14 +3,16 @@ //! [`Spawner`] for plugging in runtime implementations. -use std::{fmt::Debug, sync::Arc}; +use std::fmt::Debug; +#[cfg(feature = "custom")] +use std::sync::Arc; +use crate::handle::JoinHandle; #[cfg(feature = "tokio")] use crate::handle::JoinHandleInner; -use crate::{ - custom::{BoxedFuture, CustomSpawner}, - handle::JoinHandle, -}; + +#[cfg(feature = "custom")] +use crate::custom::{BoxedFuture, CustomSpawner}; /// Runtime-agnostic task spawner. /// @@ -37,10 +39,9 @@ use crate::{ /// /// ## Custom Runtime /// -/// ```rust +/// ```rust,ignore /// use arty::Spawner; /// -/// # fn main() { /// let spawner = Spawner::custom(|fut| { /// std::thread::spawn(move || futures::executor::block_on(fut)); /// }); @@ -49,7 +50,6 @@ use crate::{ /// println!("Running on custom runtime!"); /// }); /// // handle can be awaited or dropped (fire-and-forget) -/// # } /// ``` /// /// ## Getting Results @@ -134,7 +134,7 @@ impl Spawner { /// /// # Examples /// - /// ```rust + /// ```rust,ignore /// use arty::Spawner; /// /// let spawner = Spawner::custom(|fut| { diff --git a/crates/arty/tests/handle.rs b/crates/arty/tests/handle.rs new file mode 100644 index 00000000..43b502f0 --- /dev/null +++ b/crates/arty/tests/handle.rs @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +//! Tests for `JoinHandle` implementations. + +use arty::Spawner; + +#[cfg(feature = "tokio")] +#[tokio::test] +async fn join_handle_debug() { + let spawner = Spawner::tokio(); + let handle = spawner.spawn(async { 42 }); + let debug_str = format!("{handle:?}"); + assert!(debug_str.contains("JoinHandle")); + let _ = handle.await; +} diff --git a/crates/arty/tests/spawner.rs b/crates/arty/tests/spawner.rs index 28390e90..877b47bc 100644 --- a/crates/arty/tests/spawner.rs +++ b/crates/arty/tests/spawner.rs @@ -19,9 +19,11 @@ async fn tokio_spawn_fire_and_forget() { let spawner = Spawner::tokio(); let (tx, rx) = tokio::sync::oneshot::channel(); - let _ = spawner.spawn(async move { - tx.send(42).unwrap(); - }); + let () = spawner + .spawn(async move { + tx.send(42).unwrap(); + }) + .await; assert_eq!(rx.await.unwrap(), 42); } @@ -38,17 +40,19 @@ fn custom_spawn_and_await() { } #[cfg(feature = "custom")] -#[test] -fn custom_spawn_fire_and_forget() { +#[tokio::test] +async fn custom_spawn_fire_and_forget() { let spawner = Spawner::custom(|fut| { std::thread::spawn(move || futures::executor::block_on(fut)); }); let (tx, rx) = std::sync::mpsc::channel(); - let _ = spawner.spawn(async move { - tx.send(42).unwrap(); - }); + let () = spawner + .spawn(async move { + tx.send(42).unwrap(); + }) + .await; assert_eq!(rx.recv().unwrap(), 42); } @@ -60,13 +64,3 @@ fn custom_spawner_debug() { let debug_str = format!("{spawner:?}"); assert!(debug_str.contains("CustomSpawner")); } - -#[cfg(feature = "tokio")] -#[tokio::test] -async fn join_handle_debug() { - let spawner = Spawner::tokio(); - let handle = spawner.spawn(async { 42 }); - let debug_str = format!("{handle:?}"); - assert!(debug_str.contains("JoinHandle")); - let _ = handle.await; -} From 5328f727f5f48da63e52403898582ce4379a2ff7 Mon Sep 17 00:00:00 2001 From: Schuyler Goodman Date: Fri, 16 Jan 2026 15:15:01 -0500 Subject: [PATCH 20/29] nit --- crates/arty/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/arty/Cargo.toml b/crates/arty/Cargo.toml index ca46b32e..cbde687c 100644 --- a/crates/arty/Cargo.toml +++ b/crates/arty/Cargo.toml @@ -42,7 +42,7 @@ required-features = ["tokio", "custom"] [[example]] name = "custom" -required-features = ["custom", "tokio"] +required-features = ["tokio", "custom"] [[example]] name = "tokio" From 1904e36c90825c39bda3b4ea74a4195475f16b92 Mon Sep 17 00:00:00 2001 From: Schuyler Goodman Date: Fri, 16 Jan 2026 15:32:12 -0500 Subject: [PATCH 21/29] Add static assertion for JoinHandle impl Send --- Cargo.lock | 1 + crates/arty/Cargo.toml | 1 + crates/arty/tests/handle.rs | 4 +++- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index f5e49bbe..db575e0d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -52,6 +52,7 @@ dependencies = [ "criterion", "futures", "futures-channel", + "static_assertions", "tick", "tokio", ] diff --git a/crates/arty/Cargo.toml b/crates/arty/Cargo.toml index cbde687c..a436d25f 100644 --- a/crates/arty/Cargo.toml +++ b/crates/arty/Cargo.toml @@ -32,6 +32,7 @@ tokio = { workspace = true, features = ["rt"], optional = true } async-std = { version = "1.13", features = ["attributes"] } criterion = { workspace = true } futures = { workspace = true, features = ["executor"] } +static_assertions.workspace = true tick = { workspace = true, features = ["tokio"] } tokio = { workspace = true, features = ["macros", "rt-multi-thread", "sync"] } diff --git a/crates/arty/tests/handle.rs b/crates/arty/tests/handle.rs index 43b502f0..87e582b8 100644 --- a/crates/arty/tests/handle.rs +++ b/crates/arty/tests/handle.rs @@ -3,7 +3,9 @@ //! Tests for `JoinHandle` implementations. -use arty::Spawner; +use arty::{JoinHandle, Spawner}; + +static_assertions::assert_impl_all!(JoinHandle<()>: Send, Sync); #[cfg(feature = "tokio")] #[tokio::test] From 764777e4e67fa5893918d513149b9bce2cabba7c Mon Sep 17 00:00:00 2001 From: Schuyler Goodman Date: Fri, 16 Jan 2026 15:33:45 -0500 Subject: [PATCH 22/29] fix wrong static assertion --- crates/arty/tests/handle.rs | 2 -- crates/arty/tests/spawner.rs | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/arty/tests/handle.rs b/crates/arty/tests/handle.rs index 87e582b8..6000c23b 100644 --- a/crates/arty/tests/handle.rs +++ b/crates/arty/tests/handle.rs @@ -5,8 +5,6 @@ use arty::{JoinHandle, Spawner}; -static_assertions::assert_impl_all!(JoinHandle<()>: Send, Sync); - #[cfg(feature = "tokio")] #[tokio::test] async fn join_handle_debug() { diff --git a/crates/arty/tests/spawner.rs b/crates/arty/tests/spawner.rs index 877b47bc..58b59143 100644 --- a/crates/arty/tests/spawner.rs +++ b/crates/arty/tests/spawner.rs @@ -5,6 +5,8 @@ use arty::Spawner; +static_assertions::assert_impl_all!(Spawner: Send, Sync); + #[cfg(feature = "tokio")] #[tokio::test] async fn tokio_spawn_and_await() { From b35c5879f06392ed49011dc1c0863cf89ef31398 Mon Sep 17 00:00:00 2001 From: Schuyler Goodman Date: Fri, 16 Jan 2026 15:34:05 -0500 Subject: [PATCH 23/29] Remove unused use statement --- crates/arty/tests/handle.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/arty/tests/handle.rs b/crates/arty/tests/handle.rs index 6000c23b..43b502f0 100644 --- a/crates/arty/tests/handle.rs +++ b/crates/arty/tests/handle.rs @@ -3,7 +3,7 @@ //! Tests for `JoinHandle` implementations. -use arty::{JoinHandle, Spawner}; +use arty::Spawner; #[cfg(feature = "tokio")] #[tokio::test] From d90e5396b1057900348555aef86f6de9c69db75d Mon Sep 17 00:00:00 2001 From: Schuyler Goodman Date: Fri, 16 Jan 2026 17:25:07 -0500 Subject: [PATCH 24/29] Replace async-std with smol. Don't compile modules without any features. Throw compile error without any features. --- Cargo.lock | 232 ++++++++++++------------------- crates/arty/Cargo.toml | 2 +- crates/arty/benches/BENCHMARK.md | 6 +- crates/arty/benches/spawner.rs | 14 +- crates/arty/src/lib.rs | 7 + 5 files changed, 110 insertions(+), 151 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index db575e0d..3f55178f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -48,36 +48,15 @@ checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" name = "arty" version = "0.1.0" dependencies = [ - "async-std", "criterion", "futures", "futures-channel", + "smol", "static_assertions", "tick", "tokio", ] -[[package]] -name = "async-attributes" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3203e79f4dd9bdda415ed03cf14dae5a2bf775c683a00f94e9cd1faf0f596e5" -dependencies = [ - "quote", - "syn 1.0.109", -] - -[[package]] -name = "async-channel" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" -dependencies = [ - "concurrent-queue", - "event-listener 2.5.3", - "futures-core", -] - [[package]] name = "async-channel" version = "2.5.0" @@ -105,18 +84,14 @@ dependencies = [ ] [[package]] -name = "async-global-executor" -version = "2.4.1" +name = "async-fs" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" +checksum = "8034a681df4aed8b8edbd7fbe472401ecf009251c8b40556b304567052e294c5" dependencies = [ - "async-channel 2.5.0", - "async-executor", - "async-io", "async-lock", "blocking", "futures-lite", - "once_cell", ] [[package]] @@ -143,36 +118,56 @@ version = "3.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" dependencies = [ - "event-listener 5.4.1", + "event-listener", "event-listener-strategy", "pin-project-lite", ] [[package]] -name = "async-std" -version = "1.13.2" +name = "async-net" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c8e079a4ab67ae52b7403632e4618815d6db36d2a010cfe41b02c1b1578f93b" +checksum = "b948000fad4873c1c9339d60f2623323a0cfd3816e5181033c6a5cb68b2accf7" dependencies = [ - "async-attributes", - "async-channel 1.9.0", - "async-global-executor", + "async-io", + "blocking", + "futures-lite", +] + +[[package]] +name = "async-process" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75" +dependencies = [ + "async-channel", "async-io", "async-lock", - "crossbeam-utils", - "futures-channel", + "async-signal", + "async-task", + "blocking", + "cfg-if", + "event-listener", + "futures-lite", + "rustix", +] + +[[package]] +name = "async-signal" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43c070bbf59cd3570b6b2dd54cd772527c7c3620fce8be898406dd3ed6adc64c" +dependencies = [ + "async-io", + "async-lock", + "atomic-waker", + "cfg-if", "futures-core", "futures-io", - "futures-lite", - "gloo-timers", - "kv-log-macro", - "log", - "memchr", - "once_cell", - "pin-project-lite", - "pin-utils", + "rustix", + "signal-hook-registry", "slab", - "wasm-bindgen-futures", + "windows-sys 0.61.2", ] [[package]] @@ -215,7 +210,7 @@ version = "1.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" dependencies = [ - "async-channel 2.5.0", + "async-channel", "async-task", "futures-io", "futures-lite", @@ -479,7 +474,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn 2.0.111", + "syn", ] [[package]] @@ -510,7 +505,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.111", + "syn", "unicode-xid", ] @@ -555,7 +550,7 @@ checksum = "0b0713d5c1d52e774c5cd7bb8b043d7c0fc4f921abfb678556140bfbe6ab2364" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn", ] [[package]] @@ -580,12 +575,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "event-listener" -version = "2.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" - [[package]] name = "event-listener" version = "5.4.1" @@ -603,7 +592,7 @@ version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" dependencies = [ - "event-listener 5.4.1", + "event-listener", "pin-project-lite", ] @@ -662,7 +651,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn 2.0.111", + "syn", ] [[package]] @@ -734,7 +723,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn", ] [[package]] @@ -785,18 +774,6 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" -[[package]] -name = "gloo-timers" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994" -dependencies = [ - "futures-channel", - "futures-core", - "js-sys", - "wasm-bindgen", -] - [[package]] name = "half" version = "2.7.1" @@ -944,7 +921,7 @@ checksum = "b787bebb543f8969132630c51fd0afab173a86c6abae56ff3b9e5e3e3f9f6e58" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn", ] [[package]] @@ -972,15 +949,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "kv-log-macro" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" -dependencies = [ - "log", -] - [[package]] name = "layered" version = "0.1.0" @@ -1030,9 +998,6 @@ name = "log" version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" -dependencies = [ - "value-bag", -] [[package]] name = "many_cpus" @@ -1084,7 +1049,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.111", + "syn", ] [[package]] @@ -1101,7 +1066,7 @@ checksum = "67670e6848468ee39b823ed93add8bd2adb66a132b0c25fce6e60af0b81d930a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn", ] [[package]] @@ -1183,7 +1148,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn 2.0.111", + "syn", ] [[package]] @@ -1418,7 +1383,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn 2.0.111", + "syn", ] [[package]] @@ -1638,7 +1603,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn", ] [[package]] @@ -1694,6 +1659,16 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + [[package]] name = "similar" version = "2.7.0" @@ -1718,6 +1693,23 @@ version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +[[package]] +name = "smol" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a33bd3e260892199c3ccfc487c88b2da2265080acb316cd920da72fdfd7c599f" +dependencies = [ + "async-channel", + "async-executor", + "async-fs", + "async-io", + "async-lock", + "async-net", + "async-process", + "blocking", + "futures-lite", +] + [[package]] name = "stable_deref_trait" version = "1.2.1" @@ -1730,17 +1722,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - [[package]] name = "syn" version = "2.0.111" @@ -1829,7 +1810,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn", ] [[package]] @@ -1840,7 +1821,7 @@ checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn", ] [[package]] @@ -1858,7 +1839,7 @@ name = "thread_aware_macros" version = "0.6.0" dependencies = [ "mutants", - "syn 2.0.111", + "syn", "thread_aware_macros_impl", ] @@ -1870,7 +1851,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn 2.0.111", + "syn", ] [[package]] @@ -1985,7 +1966,7 @@ checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn", ] [[package]] @@ -2107,7 +2088,7 @@ checksum = "70977707304198400eb4835a78f6a9f928bf41bba420deb8fdb175cd965d77a7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn", ] [[package]] @@ -2149,12 +2130,6 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" -[[package]] -name = "value-bag" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ba6f5989077681266825251a52748b8c1d8a4ad098cc37e440103d0ea717fc0" - [[package]] name = "walkdir" version = "2.5.0" @@ -2187,19 +2162,6 @@ dependencies = [ "wasm-bindgen-shared", ] -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.56" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836d9622d604feee9e5de25ac10e3ea5f2d65b41eac0d9ce72eb5deae707ce7c" -dependencies = [ - "cfg-if", - "js-sys", - "once_cell", - "wasm-bindgen", - "web-sys", -] - [[package]] name = "wasm-bindgen-macro" version = "0.2.106" @@ -2219,7 +2181,7 @@ dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.111", + "syn", "wasm-bindgen-shared", ] @@ -2232,16 +2194,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "web-sys" -version = "0.3.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b32828d774c412041098d182a8b38b16ea816958e07cf40eec2bc080ae137ac" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - [[package]] name = "winapi-util" version = "0.1.11" @@ -2304,7 +2256,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn", ] [[package]] @@ -2315,7 +2267,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn", ] [[package]] @@ -2534,7 +2486,7 @@ checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn", ] [[package]] diff --git a/crates/arty/Cargo.toml b/crates/arty/Cargo.toml index a436d25f..dc26f8e6 100644 --- a/crates/arty/Cargo.toml +++ b/crates/arty/Cargo.toml @@ -29,9 +29,9 @@ futures-channel = { workspace = true, features = ["alloc"], optional = true } tokio = { workspace = true, features = ["rt"], optional = true } [dev-dependencies] -async-std = { version = "1.13", features = ["attributes"] } criterion = { workspace = true } futures = { workspace = true, features = ["executor"] } +smol = "2" static_assertions.workspace = true tick = { workspace = true, features = ["tokio"] } tokio = { workspace = true, features = ["macros", "rt-multi-thread", "sync"] } diff --git a/crates/arty/benches/BENCHMARK.md b/crates/arty/benches/BENCHMARK.md index ee732f36..3d12a7d0 100644 --- a/crates/arty/benches/BENCHMARK.md +++ b/crates/arty/benches/BENCHMARK.md @@ -8,14 +8,14 @@ Compares the overhead of using `Spawner::spawn()` vs calling runtime spawn funct |-----------|------| | `tokio_direct` | ~12 µs | | `tokio_via_spawner` | ~12 µs | -| `async_std_direct` | ~35 µs | -| `async_std_via_spawner` | ~21 µs | +| `smol_direct` | ~2 µs | +| `smol_via_spawner` | ~2.6 µs | ## Analysis **Tokio**: The spawner abstraction has **zero overhead**. Both paths use tokio's `JoinHandle` directly, so the only cost is the match statement which is negligible. -**async-std**: The custom spawner uses a oneshot channel to return results, which is ~14 µs faster than async-std's native `JoinHandle` in this benchmark. +**smol**: The custom spawner adds ~0.6 µs overhead from the oneshot channel. smol is significantly faster than tokio in this benchmark. ## Key Takeaway diff --git a/crates/arty/benches/spawner.rs b/crates/arty/benches/spawner.rs index fb81d522..06b130b0 100644 --- a/crates/arty/benches/spawner.rs +++ b/crates/arty/benches/spawner.rs @@ -25,17 +25,17 @@ fn entry(c: &mut Criterion) { b.iter(|| rt.block_on(async { tokio_spawner.spawn(async { 42 }).await })); }); - // async-std benchmarks - let async_std_spawner = Spawner::custom(|fut| { - async_std::task::spawn(fut); + // smol benchmarks + let smol_spawner = Spawner::custom(|fut| { + smol::spawn(fut).detach(); }); - group.bench_function("async_std_direct", |b| { - b.iter(|| async_std::task::block_on(async { async_std::task::spawn(async { 42 }).await })); + group.bench_function("smol_direct", |b| { + b.iter(|| smol::block_on(async { smol::spawn(async { 42 }).await })); }); - group.bench_function("async_std_via_spawner", |b| { - b.iter(|| async_std::task::block_on(async { async_std_spawner.spawn(async { 42 }).await })); + group.bench_function("smol_via_spawner", |b| { + b.iter(|| smol::block_on(async { smol_spawner.spawn(async { 42 }).await })); }); group.finish(); diff --git a/crates/arty/src/lib.rs b/crates/arty/src/lib.rs index 7c0302d3..14017bd8 100644 --- a/crates/arty/src/lib.rs +++ b/crates/arty/src/lib.rs @@ -52,10 +52,17 @@ #![doc(html_logo_url = "https://media.githubusercontent.com/media/microsoft/oxidizer/refs/heads/main/crates/arty/logo.png")] #![doc(html_favicon_url = "https://media.githubusercontent.com/media/microsoft/oxidizer/refs/heads/main/crates/arty/favicon.ico")] +#[cfg(not(any(feature = "tokio", feature = "custom")))] +compile_error!("at least one of the `tokio` or `custom` features must be enabled"); + #[cfg(feature = "custom")] mod custom; +#[cfg(any(feature = "tokio", feature = "custom"))] mod handle; +#[cfg(any(feature = "tokio", feature = "custom"))] mod spawner; +#[cfg(any(feature = "tokio", feature = "custom"))] pub use handle::JoinHandle; +#[cfg(any(feature = "tokio", feature = "custom"))] pub use spawner::Spawner; From ebed5dc4d23cd8bb39f6fee34cb1f90cfbe4f287 Mon Sep 17 00:00:00 2001 From: Schuyler Goodman Date: Fri, 16 Jan 2026 17:35:04 -0500 Subject: [PATCH 25/29] Remove no features compile error --- crates/arty/src/lib.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/crates/arty/src/lib.rs b/crates/arty/src/lib.rs index 14017bd8..71825eda 100644 --- a/crates/arty/src/lib.rs +++ b/crates/arty/src/lib.rs @@ -52,9 +52,6 @@ #![doc(html_logo_url = "https://media.githubusercontent.com/media/microsoft/oxidizer/refs/heads/main/crates/arty/logo.png")] #![doc(html_favicon_url = "https://media.githubusercontent.com/media/microsoft/oxidizer/refs/heads/main/crates/arty/favicon.ico")] -#[cfg(not(any(feature = "tokio", feature = "custom")))] -compile_error!("at least one of the `tokio` or `custom` features must be enabled"); - #[cfg(feature = "custom")] mod custom; #[cfg(any(feature = "tokio", feature = "custom"))] From 2a659fcd8fb920e80fb78c4c4b255842408f01ad Mon Sep 17 00:00:00 2001 From: Schuyler Goodman Date: Fri, 16 Jan 2026 17:43:54 -0500 Subject: [PATCH 26/29] Fix some build issues --- crates/arty/src/spawner.rs | 2 +- crates/arty/tests/handle.rs | 2 ++ crates/arty/tests/spawner.rs | 2 ++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/crates/arty/src/spawner.rs b/crates/arty/src/spawner.rs index 5dd03a76..a22d376f 100644 --- a/crates/arty/src/spawner.rs +++ b/crates/arty/src/spawner.rs @@ -8,7 +8,7 @@ use std::fmt::Debug; use std::sync::Arc; use crate::handle::JoinHandle; -#[cfg(feature = "tokio")] +#[cfg(any(feature = "tokio", feature = "custom"))] use crate::handle::JoinHandleInner; #[cfg(feature = "custom")] diff --git a/crates/arty/tests/handle.rs b/crates/arty/tests/handle.rs index 43b502f0..d67c206c 100644 --- a/crates/arty/tests/handle.rs +++ b/crates/arty/tests/handle.rs @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +#![cfg(any(feature = "tokio", feature = "custom"))] + //! Tests for `JoinHandle` implementations. use arty::Spawner; diff --git a/crates/arty/tests/spawner.rs b/crates/arty/tests/spawner.rs index 58b59143..52cabbcc 100644 --- a/crates/arty/tests/spawner.rs +++ b/crates/arty/tests/spawner.rs @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +#![cfg(any(feature = "tokio", feature = "custom"))] + //! Tests for `Spawner` implementations. use arty::Spawner; From af0a8a627bf8650b4beea350be3bab5e078becd4 Mon Sep 17 00:00:00 2001 From: Schuyler Goodman Date: Tue, 20 Jan 2026 16:46:27 -0500 Subject: [PATCH 27/29] Ignore tests docs --- crates/arty/tests/handle.rs | 1 + crates/arty/tests/spawner.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/crates/arty/tests/handle.rs b/crates/arty/tests/handle.rs index d67c206c..7949188b 100644 --- a/crates/arty/tests/handle.rs +++ b/crates/arty/tests/handle.rs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +#![allow(missing_docs)] #![cfg(any(feature = "tokio", feature = "custom"))] //! Tests for `JoinHandle` implementations. diff --git a/crates/arty/tests/spawner.rs b/crates/arty/tests/spawner.rs index 52cabbcc..e18fd849 100644 --- a/crates/arty/tests/spawner.rs +++ b/crates/arty/tests/spawner.rs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +#![allow(missing_docs)] #![cfg(any(feature = "tokio", feature = "custom"))] //! Tests for `Spawner` implementations. From 4681000b6f8133cf623c0b3b0c37c56dedaac8b3 Mon Sep 17 00:00:00 2001 From: Schuyler Goodman Date: Thu, 22 Jan 2026 10:59:16 -0500 Subject: [PATCH 28/29] Rename arty to anyspawn --- CHANGELOG.md | 2 +- Cargo.lock | 186 +++++++++---------- Cargo.toml | 2 +- README.md | 2 +- crates/{arty => anyspawn}/CHANGELOG.md | 0 crates/{arty => anyspawn}/Cargo.toml | 2 +- crates/{arty => anyspawn}/README.md | 24 +-- crates/{arty => anyspawn}/benches/spawner.rs | 2 +- crates/{arty => anyspawn}/examples/custom.rs | 2 +- crates/{arty => anyspawn}/examples/tokio.rs | 2 +- crates/{arty => anyspawn}/favicon.ico | 0 crates/{arty => anyspawn}/logo.png | 0 crates/{arty => anyspawn}/src/custom.rs | 0 crates/{arty => anyspawn}/src/handle.rs | 0 crates/{arty => anyspawn}/src/lib.rs | 8 +- crates/{arty => anyspawn}/src/spawner.rs | 14 +- crates/{arty => anyspawn}/tests/handle.rs | 4 +- crates/{arty => anyspawn}/tests/spawner.rs | 4 +- crates/arty/benches/BENCHMARK.md | 28 --- 19 files changed, 127 insertions(+), 155 deletions(-) rename crates/{arty => anyspawn}/CHANGELOG.md (100%) rename crates/{arty => anyspawn}/Cargo.toml (98%) rename crates/{arty => anyspawn}/README.md (67%) rename crates/{arty => anyspawn}/benches/spawner.rs (98%) rename crates/{arty => anyspawn}/examples/custom.rs (97%) rename crates/{arty => anyspawn}/examples/tokio.rs (97%) rename crates/{arty => anyspawn}/favicon.ico (100%) rename crates/{arty => anyspawn}/logo.png (100%) rename crates/{arty => anyspawn}/src/custom.rs (100%) rename crates/{arty => anyspawn}/src/handle.rs (100%) rename crates/{arty => anyspawn}/src/lib.rs (90%) rename crates/{arty => anyspawn}/src/spawner.rs (95%) rename crates/{arty => anyspawn}/tests/handle.rs (86%) rename crates/{arty => anyspawn}/tests/spawner.rs (95%) delete mode 100644 crates/arty/benches/BENCHMARK.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b14ee1d..03cb048a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ Please see each crate's change log below: -- [`arty`](./crates/arty/CHANGELOG.md) +- [`anyspawn`](./crates/anyspawn/CHANGELOG.md) - [`bytesbuf`](./crates/bytesbuf/CHANGELOG.md) - [`bytesbuf_io`](./crates/bytesbuf_io/CHANGELOG.md) - [`data_privacy`](./crates/data_privacy/CHANGELOG.md) diff --git a/Cargo.lock b/Cargo.lock index 3f55178f..64154da0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13,9 +13,9 @@ dependencies = [ [[package]] name = "alloc_tracker" -version = "0.5.12" +version = "0.5.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "541f3dfa28da9fba29d46a58d1fc137d401b7cdd5fff76c2cdc03abe3ad5d87c" +checksum = "6d6f8b0cbd2617bdb93d237d7e11032f0ec3ea509ce1fbd351397567e36dd6d6" [[package]] name = "android_system_properties" @@ -45,7 +45,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" [[package]] -name = "arty" +name = "anyspawn" version = "0.1.0" dependencies = [ "criterion", @@ -276,9 +276,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.51" +version = "1.2.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a0aeaff4ff1a90589618835a598e545176939b97874f7abc7851caa0618f203" +checksum = "755d2fce177175ffca841e9a06afdb2c4ab0f593d53b4dee48147dfaade85932" dependencies = [ "find-msvc-tools", "shlex", @@ -292,9 +292,9 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "chrono" -version = "0.4.42" +version = "0.4.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" +checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118" dependencies = [ "iana-time-zone", "num-traits", @@ -340,18 +340,18 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.53" +version = "4.5.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8" +checksum = "c6e6ff9dcd79cff5cd969a17a545d79e84ab086e444102a591e288a8aa3ce394" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.5.53" +version = "4.5.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00" +checksum = "fa42cf4d2b7a41bc8f663a7cab4031ebafa1bf3875705bfaf8466dc60ab52c00" dependencies = [ "anstyle", "clap_lex", @@ -359,9 +359,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" +checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32" [[package]] name = "concurrent-queue" @@ -389,13 +389,13 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpulist" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11a45acbbf4646bcff1e38cfd1f7d88ae73353d4add7ca31609023216599bbfa" +checksum = "10921f832bb6e2075c6910ab0be7b33e413029ae6d74b01f049fc6cb237362e6" dependencies = [ "itertools 0.14.0", "new_zealand", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] @@ -604,9 +604,9 @@ checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "find-msvc-tools" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645cbb3a84e60b7531617d5ae4e57f7e27308f6445f5abf653209ea76dec8dff" +checksum = "8591b0bcc8a98a64310a2fae1bb3e9b8564dd10e381e6e28010fde8e8e8568db" [[package]] name = "foldhash" @@ -616,9 +616,9 @@ checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" [[package]] name = "folo_ffi" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44b0401f7b448a28be3e723e6a30cd29ebe707ebbf23ac01ab505da0de10005c" +checksum = "b2c4a9efbd06c6c8500f5d97a20159996679088bdb5f598a863960bedf3f2574" [[package]] name = "fragile" @@ -842,9 +842,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.12.1" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" dependencies = [ "equivalent", "hashbrown", @@ -852,9 +852,9 @@ dependencies = [ [[package]] name = "infinity_pool" -version = "0.8.4" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f81221d9c547a962679935ef10f3be03739f8a99c98ad7bfc1fcbd4850892bf" +checksum = "1b53f0294af2d25dc8eee1d483579ad476233efe0ce1e09c1721275a976d7055" dependencies = [ "new_zealand", "num-integer", @@ -864,9 +864,9 @@ dependencies = [ [[package]] name = "insta" -version = "1.45.1" +version = "1.46.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "983e3b24350c84ab8a65151f537d67afbbf7153bb9f1110e03e9fa9b07f67a5c" +checksum = "248b42847813a1550dafd15296fd9748c651d0c32194559dbc05d804d54b21e8" dependencies = [ "once_cell", "serde", @@ -900,9 +900,9 @@ checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" [[package]] name = "jiff" -version = "0.2.17" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a87d9b8105c23642f50cbbae03d1f75d8422c5cb98ce7ee9271f7ff7505be6b8" +checksum = "e67e8da4c49d6d9909fe03361f9b620f58898859f5c7aded68351e85e71ecf50" dependencies = [ "jiff-static", "jiff-tzdb-platform", @@ -915,9 +915,9 @@ dependencies = [ [[package]] name = "jiff-static" -version = "0.2.17" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b787bebb543f8969132630c51fd0afab173a86c6abae56ff3b9e5e3e3f9f6e58" +checksum = "e0c84ee7f197eca9a86c6fd6cb771e55eb991632f15f2bc3ca6ec838929e6e78" dependencies = [ "proc-macro2", "quote", @@ -941,9 +941,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.83" +version = "0.3.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" +checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3" dependencies = [ "once_cell", "wasm-bindgen", @@ -974,9 +974,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.178" +version = "0.2.180" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" +checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" [[package]] name = "linux-raw-sys" @@ -1001,9 +1001,9 @@ checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "many_cpus" -version = "1.1.3" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb82f238dc324f74bb9c2e25b4244dbdeab8d66809dc3e988472498e84a411ec" +checksum = "d4ab614fc8e14ffa2aab66ed7942362b33191752f3a5bf9533e9f88e4204545f" dependencies = [ "cpulist", "derive_more", @@ -1071,15 +1071,15 @@ dependencies = [ [[package]] name = "new_zealand" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62cc3bfd31ca5136b8449b5418a91a9c2a336b3ea61ae17eae350c10bd4ab26d" +checksum = "e098f45dda35490b475350685c55a9fb4c4f6cb885f6f0b09fea301cfef52161" [[package]] name = "nm" -version = "0.1.22" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19e593410e637cfb2542a21f30106fde38fcf7bb8359b39c1355d382ad09f888" +checksum = "93ee800dec1976af03cfa4c209e0fac85e140266782719f06bddfd321177acf9" dependencies = [ "foldhash", "new_zealand", @@ -1134,7 +1134,7 @@ dependencies = [ "mutants", "ohno_macros", "regex", - "thiserror 2.0.17", + "thiserror 2.0.18", "tokio", "typeid", ] @@ -1388,18 +1388,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.104" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9695f8df41bb4f3d222c95a67532365f569318332d03d5f3f67f37b20e6ebdf0" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.42" +version = "1.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +checksum = "dc74d9a594b72ae6656596548f56f667211f8a97b3d4c3d467150794690dc40a" dependencies = [ "proc-macro2", ] @@ -1426,7 +1426,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha", - "rand_core 0.9.3", + "rand_core 0.9.5", ] [[package]] @@ -1436,7 +1436,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core 0.9.3", + "rand_core 0.9.5", ] [[package]] @@ -1447,18 +1447,18 @@ checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" [[package]] name = "rand_core" -version = "0.9.3" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" dependencies = [ "getrandom", ] [[package]] name = "rapidhash" -version = "4.2.0" +version = "4.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2988730ee014541157f48ce4dcc603940e00915edc3c7f9a8d78092256bb2493" +checksum = "5d8b5b858a440a0bc02625b62dd95131b9201aa9f69f411195dd4a7cfb1de3d7" dependencies = [ "rustversion", ] @@ -1608,9 +1608,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.148" +version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3084b546a1dd6289475996f182a22aba973866ea8e8b02c51d9f46b1336a22da" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" dependencies = [ "itoa", "memchr", @@ -1724,9 +1724,9 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "syn" -version = "2.0.111" +version = "2.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" +checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" dependencies = [ "proc-macro2", "quote", @@ -1795,11 +1795,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.17" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" dependencies = [ - "thiserror-impl 2.0.17", + "thiserror-impl 2.0.18", ] [[package]] @@ -1815,9 +1815,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.17" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", @@ -1887,9 +1887,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.44" +version = "0.3.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" +checksum = "f9e442fc33d7fdb45aa9bfeb312c095964abdf596f7567261062b2a7107aaabd" dependencies = [ "deranged", "itoa", @@ -1898,22 +1898,22 @@ dependencies = [ "num-conv", "num_threads", "powerfmt", - "serde", + "serde_core", "time-core", "time-macros", ] [[package]] name = "time-core" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" +checksum = "8b36ee98fd31ec7426d599183e8fe26932a8dc1fb76ddb6214d05493377d34ca" [[package]] name = "time-macros" -version = "0.2.24" +version = "0.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" +checksum = "71e552d1249bf61ac2a52db88179fd0673def1e1ad8243a00d9ec9ed71fee3dd" dependencies = [ "num-conv", "time-core", @@ -1950,9 +1950,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.48.0" +version = "1.49.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" +checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" dependencies = [ "pin-project-lite", "tokio-macros", @@ -1971,9 +1971,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.17" +version = "0.7.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" dependencies = [ "bytes", "futures-core", @@ -1984,9 +1984,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.9.10+spec-1.1.0" +version = "0.9.11+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0825052159284a1a8b4d6c0c86cbc801f2da5afd2b225fa548c72f2e74002f48" +checksum = "f3afc9a848309fe1aaffaed6e1546a7a14de1f935dc9d89d32afd9a44bab7c46" dependencies = [ "indexmap", "serde_core", @@ -2023,9 +2023,9 @@ checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607" [[package]] name = "tower" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" dependencies = [ "futures-core", "futures-util", @@ -2142,18 +2142,18 @@ dependencies = [ [[package]] name = "wasip2" -version = "1.0.1+wasi-0.2.4" +version = "1.0.2+wasi-0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" dependencies = [ "wit-bindgen", ] [[package]] name = "wasm-bindgen" -version = "0.2.106" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" +checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566" dependencies = [ "cfg-if", "once_cell", @@ -2164,9 +2164,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.106" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" +checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2174,9 +2174,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.106" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" +checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55" dependencies = [ "bumpalo", "proc-macro2", @@ -2187,9 +2187,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.106" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" +checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12" dependencies = [ "unicode-ident", ] @@ -2447,9 +2447,9 @@ checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" [[package]] name = "wit-bindgen" -version = "0.46.0" +version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" [[package]] name = "xml-rs" @@ -2471,18 +2471,18 @@ checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" [[package]] name = "zerocopy" -version = "0.8.31" +version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3" +checksum = "668f5168d10b9ee831de31933dc111a459c97ec93225beb307aed970d1372dfd" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.31" +version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a" +checksum = "2c7962b26b0a8685668b671ee4b54d007a67d4eaf05fda79ac0ecf41e32270f1" dependencies = [ "proc-macro2", "quote", @@ -2491,6 +2491,6 @@ dependencies = [ [[package]] name = "zmij" -version = "1.0.3" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9747e91771f56fd7893e1164abd78febd14a670ceec257caad15e051de35f06" +checksum = "dfcd145825aace48cff44a8844de64bf75feec3080e0aa5cdbde72961ae51a65" diff --git a/Cargo.toml b/Cargo.toml index 3d15c2b0..0858fb2c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,7 @@ repository = "https://github.com/microsoft/oxidizer" [workspace.dependencies] # local dependencies -arty = { path = "crates/arty", default-features = false, version = "0.1.0" } +anyspawn = { path = "crates/anyspawn", default-features = false, version = "0.1.0" } bytesbuf = { path = "crates/bytesbuf", default-features = false, version = "0.2.2" } bytesbuf_io = { path = "crates/bytesbuf_io", default-features = false, version = "0.1.1" } data_privacy = { path = "crates/data_privacy", default-features = false, version = "0.10.1" } diff --git a/README.md b/README.md index d64b531d..b44f5a7f 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ This repository contains a set of crates that help you build robust highly scala These are the crates built out of this repo: -- [`arty`](./crates/arty/README.md) - Async runtime abstractions. +- [`anyspawn`](./crates/anyspawn/README.md) - Async runtime abstractions. - [`bytesbuf`](./crates/bytesbuf/README.md) - Types for creating and manipulating byte sequences. - [`bytesbuf_io`](./crates/bytesbuf_io/README.md) - Asynchronous I/O abstractions expressed via `bytesbuf` types. - [`data_privacy`](./crates/data_privacy/README.md) - Mechanisms to classify, manipulate, and redact sensitive data. diff --git a/crates/arty/CHANGELOG.md b/crates/anyspawn/CHANGELOG.md similarity index 100% rename from crates/arty/CHANGELOG.md rename to crates/anyspawn/CHANGELOG.md diff --git a/crates/arty/Cargo.toml b/crates/anyspawn/Cargo.toml similarity index 98% rename from crates/arty/Cargo.toml rename to crates/anyspawn/Cargo.toml index dc26f8e6..a3b63b4c 100644 --- a/crates/arty/Cargo.toml +++ b/crates/anyspawn/Cargo.toml @@ -2,7 +2,7 @@ # Licensed under the MIT License. [package] -name = "arty" +name = "anyspawn" description = "A generic task spawner compatible with any async runtime" version = "0.1.0" readme = "README.md" diff --git a/crates/arty/README.md b/crates/anyspawn/README.md similarity index 67% rename from crates/arty/README.md rename to crates/anyspawn/README.md index c3264ab2..e5cbffaa 100644 --- a/crates/arty/README.md +++ b/crates/anyspawn/README.md @@ -1,11 +1,11 @@
- Arty Logo + Anyspawn Logo -# Arty +# Anyspawn -[![crate.io](https://img.shields.io/crates/v/arty.svg)](https://crates.io/crates/arty) -[![docs.rs](https://docs.rs/arty/badge.svg)](https://docs.rs/arty) -[![MSRV](https://img.shields.io/crates/msrv/arty)](https://crates.io/crates/arty) +[![crate.io](https://img.shields.io/crates/v/anyspawn.svg)](https://crates.io/crates/anyspawn) +[![docs.rs](https://docs.rs/anyspawn/badge.svg)](https://docs.rs/anyspawn) +[![MSRV](https://img.shields.io/crates/msrv/anyspawn)](https://crates.io/crates/anyspawn) [![CI](https://github.com/microsoft/oxidizer/actions/workflows/main.yml/badge.svg?event=push)](https://github.com/microsoft/oxidizer/actions/workflows/main.yml) [![Coverage](https://codecov.io/gh/microsoft/oxidizer/graph/badge.svg?token=FCUG0EL5TI)](https://codecov.io/gh/microsoft/oxidizer) [![License](https://img.shields.io/badge/license-MIT-blue.svg)](../../LICENSE) @@ -29,7 +29,7 @@ different async runtimes without generic infection. ### Using Tokio ```rust -use arty::Spawner; +use anyspawn::Spawner; let spawner = Spawner::tokio(); let result = spawner.spawn(async { 1 + 1 }).await; @@ -39,7 +39,7 @@ assert_eq!(result, 2); ### Custom Runtime ```rust -use arty::Spawner; +use anyspawn::Spawner; let spawner = Spawner::custom(|fut| { std::thread::spawn(move || futures::executor::block_on(fut)); @@ -57,10 +57,10 @@ let handle = spawner.spawn(async { 42 });
-This crate was developed as part of The Oxidizer Project. Browse this crate's source code. +This crate was developed as part of The Oxidizer Project. Browse this crate's source code. - [__cargo_doc2readme_dependencies_info]: ggGkYW0CYXSEGy4k8ldDFPOhG2VNeXtD5nnKG6EPY6OfW5wBG8g18NOFNdxpYXKEGyjnWqOXl0hfG5JzJZzygN1qG0YJmwqDZUQGGzO95yYeMZDOYWSBgmRhcnR5ZTAuMS4w - [__link0]: https://docs.rs/arty/0.1.0/arty/?search=Spawner - [__link1]: https://docs.rs/arty/0.1.0/arty/?search=Spawner::tokio - [__link2]: https://docs.rs/arty/0.1.0/arty/?search=Spawner::custom + [__cargo_doc2readme_dependencies_info]: ggGkYW0CYXSEGy4k8ldDFPOhG2VNeXtD5nnKG6EPY6OfW5wBG8g18NOFNdxpYXKEG-uxdJT263GuGzrWqTf_02hGG_fiPeXEDO8BG8LhU0Uc9qTPYWSBgmhhbnlzcGF3bmUwLjEuMA + [__link0]: https://docs.rs/anyspawn/0.1.0/anyspawn/?search=Spawner + [__link1]: https://docs.rs/anyspawn/0.1.0/anyspawn/?search=Spawner::tokio + [__link2]: https://docs.rs/anyspawn/0.1.0/anyspawn/?search=Spawner::custom diff --git a/crates/arty/benches/spawner.rs b/crates/anyspawn/benches/spawner.rs similarity index 98% rename from crates/arty/benches/spawner.rs rename to crates/anyspawn/benches/spawner.rs index 06b130b0..87e7d464 100644 --- a/crates/arty/benches/spawner.rs +++ b/crates/anyspawn/benches/spawner.rs @@ -7,7 +7,7 @@ reason = "Benchmarks don't require documentation and should fail fast on errors" )] -use arty::Spawner; +use anyspawn::Spawner; use criterion::{Criterion, criterion_group, criterion_main}; fn entry(c: &mut Criterion) { diff --git a/crates/arty/examples/custom.rs b/crates/anyspawn/examples/custom.rs similarity index 97% rename from crates/arty/examples/custom.rs rename to crates/anyspawn/examples/custom.rs index bf94a575..e4647c3c 100644 --- a/crates/arty/examples/custom.rs +++ b/crates/anyspawn/examples/custom.rs @@ -5,7 +5,7 @@ use std::time::Duration; -use arty::Spawner; +use anyspawn::Spawner; #[tokio::main] async fn main() { diff --git a/crates/arty/examples/tokio.rs b/crates/anyspawn/examples/tokio.rs similarity index 97% rename from crates/arty/examples/tokio.rs rename to crates/anyspawn/examples/tokio.rs index eb38d9f6..a32af76b 100644 --- a/crates/arty/examples/tokio.rs +++ b/crates/anyspawn/examples/tokio.rs @@ -5,7 +5,7 @@ use std::time::Duration; -use arty::Spawner; +use anyspawn::Spawner; use tick::Clock; #[tokio::main] diff --git a/crates/arty/favicon.ico b/crates/anyspawn/favicon.ico similarity index 100% rename from crates/arty/favicon.ico rename to crates/anyspawn/favicon.ico diff --git a/crates/arty/logo.png b/crates/anyspawn/logo.png similarity index 100% rename from crates/arty/logo.png rename to crates/anyspawn/logo.png diff --git a/crates/arty/src/custom.rs b/crates/anyspawn/src/custom.rs similarity index 100% rename from crates/arty/src/custom.rs rename to crates/anyspawn/src/custom.rs diff --git a/crates/arty/src/handle.rs b/crates/anyspawn/src/handle.rs similarity index 100% rename from crates/arty/src/handle.rs rename to crates/anyspawn/src/handle.rs diff --git a/crates/arty/src/lib.rs b/crates/anyspawn/src/lib.rs similarity index 90% rename from crates/arty/src/lib.rs rename to crates/anyspawn/src/lib.rs index 71825eda..f81b70c3 100644 --- a/crates/arty/src/lib.rs +++ b/crates/anyspawn/src/lib.rs @@ -21,7 +21,7 @@ //! ## Using Tokio //! //! ```rust -//! use arty::Spawner; +//! use anyspawn::Spawner; //! //! # #[tokio::main] //! # async fn main() { @@ -34,7 +34,7 @@ //! ## Custom Runtime //! //! ```rust,ignore -//! use arty::Spawner; +//! use anyspawn::Spawner; //! //! let spawner = Spawner::custom(|fut| { //! std::thread::spawn(move || futures::executor::block_on(fut)); @@ -49,8 +49,8 @@ //! - `tokio` (default): Enables the [`Spawner::tokio`] constructor //! - `custom`: Enables the [`Spawner::custom`] constructor -#![doc(html_logo_url = "https://media.githubusercontent.com/media/microsoft/oxidizer/refs/heads/main/crates/arty/logo.png")] -#![doc(html_favicon_url = "https://media.githubusercontent.com/media/microsoft/oxidizer/refs/heads/main/crates/arty/favicon.ico")] +#![doc(html_logo_url = "https://media.githubusercontent.com/media/microsoft/oxidizer/refs/heads/main/crates/anyspawn/logo.png")] +#![doc(html_favicon_url = "https://media.githubusercontent.com/media/microsoft/oxidizer/refs/heads/main/crates/anyspawn/favicon.ico")] #[cfg(feature = "custom")] mod custom; diff --git a/crates/arty/src/spawner.rs b/crates/anyspawn/src/spawner.rs similarity index 95% rename from crates/arty/src/spawner.rs rename to crates/anyspawn/src/spawner.rs index a22d376f..1adbfc67 100644 --- a/crates/arty/src/spawner.rs +++ b/crates/anyspawn/src/spawner.rs @@ -25,7 +25,7 @@ use crate::custom::{BoxedFuture, CustomSpawner}; /// Using Tokio: /// /// ```rust -/// use arty::Spawner; +/// use anyspawn::Spawner; /// /// # #[tokio::main] /// # async fn main() { @@ -40,7 +40,7 @@ use crate::custom::{BoxedFuture, CustomSpawner}; /// ## Custom Runtime /// /// ```rust,ignore -/// use arty::Spawner; +/// use anyspawn::Spawner; /// /// let spawner = Spawner::custom(|fut| { /// std::thread::spawn(move || futures::executor::block_on(fut)); @@ -57,7 +57,7 @@ use crate::custom::{BoxedFuture, CustomSpawner}; /// Await the [`JoinHandle`](crate::JoinHandle) to retrieve a value from the task: /// /// ```rust -/// use arty::Spawner; +/// use anyspawn::Spawner; /// /// # #[tokio::main] /// # async fn main() { @@ -72,7 +72,7 @@ use crate::custom::{BoxedFuture, CustomSpawner}; /// Return a `Result` from the task to propagate errors: /// /// ```rust -/// use arty::Spawner; +/// use anyspawn::Spawner; /// /// # #[tokio::main] /// # async fn main() { @@ -111,7 +111,7 @@ impl Spawner { /// # Examples /// /// ```rust - /// use arty::Spawner; + /// use anyspawn::Spawner; /// /// # #[tokio::main] /// # async fn main() { @@ -135,7 +135,7 @@ impl Spawner { /// # Examples /// /// ```rust,ignore - /// use arty::Spawner; + /// use anyspawn::Spawner; /// /// let spawner = Spawner::custom(|fut| { /// std::thread::spawn(move || futures::executor::block_on(fut)); @@ -162,7 +162,7 @@ impl Spawner { /// # Examples /// /// ```rust - /// use arty::Spawner; + /// use anyspawn::Spawner; /// /// # #[tokio::main] /// # async fn main() { diff --git a/crates/arty/tests/handle.rs b/crates/anyspawn/tests/handle.rs similarity index 86% rename from crates/arty/tests/handle.rs rename to crates/anyspawn/tests/handle.rs index 7949188b..16d489ae 100644 --- a/crates/arty/tests/handle.rs +++ b/crates/anyspawn/tests/handle.rs @@ -1,12 +1,12 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -#![allow(missing_docs)] +#![allow(missing_docs, reason = "test code")] #![cfg(any(feature = "tokio", feature = "custom"))] //! Tests for `JoinHandle` implementations. -use arty::Spawner; +use anyspawn::Spawner; #[cfg(feature = "tokio")] #[tokio::test] diff --git a/crates/arty/tests/spawner.rs b/crates/anyspawn/tests/spawner.rs similarity index 95% rename from crates/arty/tests/spawner.rs rename to crates/anyspawn/tests/spawner.rs index e18fd849..521ae508 100644 --- a/crates/arty/tests/spawner.rs +++ b/crates/anyspawn/tests/spawner.rs @@ -1,12 +1,12 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -#![allow(missing_docs)] +#![allow(missing_docs, reason = "test code")] #![cfg(any(feature = "tokio", feature = "custom"))] //! Tests for `Spawner` implementations. -use arty::Spawner; +use anyspawn::Spawner; static_assertions::assert_impl_all!(Spawner: Send, Sync); diff --git a/crates/arty/benches/BENCHMARK.md b/crates/arty/benches/BENCHMARK.md deleted file mode 100644 index 3d12a7d0..00000000 --- a/crates/arty/benches/BENCHMARK.md +++ /dev/null @@ -1,28 +0,0 @@ -# Spawner Benchmarks - -Compares the overhead of using `Spawner::spawn()` vs calling runtime spawn functions directly. - -## Results - -| Benchmark | Time | -|-----------|------| -| `tokio_direct` | ~12 µs | -| `tokio_via_spawner` | ~12 µs | -| `smol_direct` | ~2 µs | -| `smol_via_spawner` | ~2.6 µs | - -## Analysis - -**Tokio**: The spawner abstraction has **zero overhead**. Both paths use tokio's `JoinHandle` directly, so the only cost is the match statement which is negligible. - -**smol**: The custom spawner adds ~0.6 µs overhead from the oneshot channel. smol is significantly faster than tokio in this benchmark. - -## Key Takeaway - -The `Spawner` abstraction has negligible overhead. For Tokio, it's essentially free since it uses the native `JoinHandle` directly. For custom spawners, the oneshot channel approach performs well. - -## Running - -```sh -cargo bench -p arty --bench spawner --features custom -``` From 7cf96b56fec273a789a7b3102ee7999ef765d22d Mon Sep 17 00:00:00 2001 From: Schuyler Goodman Date: Thu, 22 Jan 2026 11:19:21 -0500 Subject: [PATCH 29/29] Accidentally removed cargo.lock --- Cargo.lock | 2467 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2467 insertions(+) create mode 100644 Cargo.lock diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 00000000..4e6375fd --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,2467 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloc_tracker" +version = "0.5.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d6f8b0cbd2617bdb93d237d7e11032f0ec3ea509ce1fbd351397567e36dd6d6" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + +[[package]] +name = "anstyle" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" + +[[package]] +name = "anyspawn" +version = "0.1.0" +dependencies = [ + "criterion", + "futures", + "futures-channel", + "smol", + "static_assertions", + "tick", + "tokio", +] + +[[package]] +name = "async-channel" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-executor" +version = "1.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497c00e0fd83a72a79a39fcbd8e3e2f055d6f6c7e025f3b3d91f4f8e76527fb8" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "pin-project-lite", + "slab", +] + +[[package]] +name = "async-fs" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8034a681df4aed8b8edbd7fbe472401ecf009251c8b40556b304567052e294c5" +dependencies = [ + "async-lock", + "blocking", + "futures-lite", +] + +[[package]] +name = "async-io" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc" +dependencies = [ + "autocfg", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix", + "slab", + "windows-sys 0.61.2", +] + +[[package]] +name = "async-lock" +version = "3.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" +dependencies = [ + "event-listener", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-net" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b948000fad4873c1c9339d60f2623323a0cfd3816e5181033c6a5cb68b2accf7" +dependencies = [ + "async-io", + "blocking", + "futures-lite", +] + +[[package]] +name = "async-process" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75" +dependencies = [ + "async-channel", + "async-io", + "async-lock", + "async-signal", + "async-task", + "blocking", + "cfg-if", + "event-listener", + "futures-lite", + "rustix", +] + +[[package]] +name = "async-signal" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43c070bbf59cd3570b6b2dd54cd772527c7c3620fce8be898406dd3ed6adc64c" +dependencies = [ + "async-io", + "async-lock", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix", + "signal-hook-registry", + "slab", + "windows-sys 0.61.2", +] + +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "automation" +version = "0.1.0" +dependencies = [ + "duct", + "ohno", + "serde", + "serde_json", +] + +[[package]] +name = "bitflags" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" + +[[package]] +name = "blocking" +version = "1.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" +dependencies = [ + "async-channel", + "async-task", + "futures-io", + "futures-lite", + "piper", +] + +[[package]] +name = "bumpalo" +version = "3.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" + +[[package]] +name = "bytesbuf" +version = "0.2.2" +dependencies = [ + "alloc_tracker", + "bytes", + "criterion", + "infinity_pool", + "libc", + "mutants", + "new_zealand", + "nm", + "num-traits", + "smallvec", + "static_assertions", + "testing_aids", + "windows-sys 0.61.2", +] + +[[package]] +name = "bytesbuf_io" +version = "0.1.1" +dependencies = [ + "bytesbuf", + "futures", + "futures-core", + "mutants", + "new_zealand", + "ohno", + "testing_aids", + "trait-variant", +] + +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + +[[package]] +name = "cc" +version = "1.2.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "755d2fce177175ffca841e9a06afdb2c4ab0f593d53b4dee48147dfaade85932" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "chrono" +version = "0.4.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118" +dependencies = [ + "iana-time-zone", + "num-traits", + "windows-link", +] + +[[package]] +name = "chrono-tz" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6139a8597ed92cf816dfb33f5dd6cf0bb93a6adc938f11039f371bc5bcd26c3" +dependencies = [ + "chrono", + "phf 0.12.1", +] + +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "clap" +version = "4.5.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6e6ff9dcd79cff5cd969a17a545d79e84ab086e444102a591e288a8aa3ce394" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.5.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa42cf4d2b7a41bc8f663a7cab4031ebafa1bf3875705bfaf8466dc60ab52c00" +dependencies = [ + "anstyle", + "clap_lex", +] + +[[package]] +name = "clap_lex" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32" + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "convert_case" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpulist" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10921f832bb6e2075c6910ab0be7b33e413029ae6d74b01f049fc6cb237362e6" +dependencies = [ + "itertools 0.14.0", + "new_zealand", + "thiserror 2.0.18", +] + +[[package]] +name = "criterion" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1c047a62b0cc3e145fa84415a3191f628e980b194c2755aa12300a4e6cbd928" +dependencies = [ + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "itertools 0.13.0", + "num-traits", + "oorandom", + "regex", + "serde", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b1bcc0dc7dfae599d84ad0b1a55f80cde8af3725da8313b528da95ef783e338" +dependencies = [ + "cast", + "itertools 0.13.0", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "data_privacy" +version = "0.10.1" +dependencies = [ + "data_privacy_macros", + "derive_more", + "mutants", + "once_cell", + "rapidhash", + "rustc-hash", + "serde", + "serde_core", + "serde_json", + "xxhash-rust", +] + +[[package]] +name = "data_privacy_macros" +version = "0.9.0" +dependencies = [ + "data_privacy_macros_impl", + "mutants", +] + +[[package]] +name = "data_privacy_macros_impl" +version = "0.9.0" +dependencies = [ + "insta", + "mutants", + "prettyplease", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "deranged" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "derive_more" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn", + "unicode-xid", +] + +[[package]] +name = "downcast" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" + +[[package]] +name = "duct" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e66e9c0c03d094e1a0ba1be130b849034aa80c3a2ab8ee94316bc809f3fa684" +dependencies = [ + "libc", + "os_pipe", + "shared_child", + "shared_thread", +] + +[[package]] +name = "dynosaur" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a12303417f378f29ba12cb12fc78a9df0d8e16ccb1ad94abf04d48d96bdda532" +dependencies = [ + "dynosaur_derive", +] + +[[package]] +name = "dynosaur_derive" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b0713d5c1d52e774c5cd7bb8b043d7c0fc4f921abfb678556140bfbe6ab2364" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "event-listener" +version = "5.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" +dependencies = [ + "event-listener", + "pin-project-lite", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "find-msvc-tools" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8591b0bcc8a98a64310a2fae1bb3e9b8564dd10e381e6e28010fde8e8e8568db" + +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + +[[package]] +name = "folo_ffi" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2c4a9efbd06c6c8500f5d97a20159996679088bdb5f598a863960bedf3f2574" + +[[package]] +name = "fragile" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dd6caf6059519a65843af8fe2a3ae298b14b80179855aeb4adc2c1934ee619" + +[[package]] +name = "fundle" +version = "0.3.0" +dependencies = [ + "fundle_macros", + "trybuild", +] + +[[package]] +name = "fundle_macros" +version = "0.3.0" +dependencies = [ + "fundle_macros_impl", + "mutants", +] + +[[package]] +name = "fundle_macros_impl" +version = "0.3.0" +dependencies = [ + "insta", + "mutants", + "prettyplease", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-lite" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", +] + +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + +[[package]] +name = "half" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +dependencies = [ + "cfg-if", + "crunchy", + "zerocopy", +] + +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + +[[package]] +name = "heapless" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af2455f757db2b292a9b1768c4b70186d443bcb3b316252d6b540aec1cd89ed" +dependencies = [ + "hash32", + "stable_deref_trait", +] + +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + +[[package]] +name = "iana-time-zone" +version = "0.1.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "indexmap" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "infinity_pool" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b53f0294af2d25dc8eee1d483579ad476233efe0ce1e09c1721275a976d7055" +dependencies = [ + "new_zealand", + "num-integer", + "parking_lot", + "pastey", +] + +[[package]] +name = "insta" +version = "1.46.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "248b42847813a1550dafd15296fd9748c651d0c32194559dbc05d804d54b21e8" +dependencies = [ + "once_cell", + "serde", + "similar", + "tempfile", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" + +[[package]] +name = "jiff" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e67e8da4c49d6d9909fe03361f9b620f58898859f5c7aded68351e85e71ecf50" +dependencies = [ + "jiff-static", + "jiff-tzdb-platform", + "log", + "portable-atomic", + "portable-atomic-util", + "serde_core", + "windows-sys 0.61.2", +] + +[[package]] +name = "jiff-static" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0c84ee7f197eca9a86c6fd6cb771e55eb991632f15f2bc3ca6ec838929e6e78" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "jiff-tzdb" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68971ebff725b9e2ca27a601c5eb38a4c5d64422c4cbab0c535f248087eda5c2" + +[[package]] +name = "jiff-tzdb-platform" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "875a5a69ac2bab1a891711cf5eccbec1ce0341ea805560dcd90b7a2e925132e8" +dependencies = [ + "jiff-tzdb", +] + +[[package]] +name = "js-sys" +version = "0.3.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "layered" +version = "0.2.0" +dependencies = [ + "alloc_tracker", + "criterion", + "dynosaur", + "futures", + "mutants", + "pin-project-lite", + "static_assertions", + "tokio", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.180" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" + +[[package]] +name = "linux-raw-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "many_cpus" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4ab614fc8e14ffa2aab66ed7942362b33191752f3a5bf9533e9f88e4204545f" +dependencies = [ + "cpulist", + "derive_more", + "foldhash", + "folo_ffi", + "heapless", + "itertools 0.14.0", + "libc", + "negative-impl", + "new_zealand", + "nonempty", + "rand 0.9.2", + "smallvec", + "windows", +] + +[[package]] +name = "memchr" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" + +[[package]] +name = "mockall" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f58d964098a5f9c6b63d0798e5372fd04708193510a7af313c22e9f29b7b620b" +dependencies = [ + "cfg-if", + "downcast", + "fragile", + "mockall_derive", + "predicates", + "predicates-tree", +] + +[[package]] +name = "mockall_derive" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca41ce716dda6a9be188b385aa78ee5260fc25cd3802cb2a8afdc6afbe6b6dbf" +dependencies = [ + "cfg-if", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "mutants" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc0287524726960e07b119cebd01678f852f147742ae0d925e6a520dca956126" + +[[package]] +name = "negative-impl" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67670e6848468ee39b823ed93add8bd2adb66a132b0c25fce6e60af0b81d930a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "new_zealand" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e098f45dda35490b475350685c55a9fb4c4f6cb885f6f0b09fea301cfef52161" + +[[package]] +name = "nm" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93ee800dec1976af03cfa4c209e0fac85e140266782719f06bddfd321177acf9" +dependencies = [ + "foldhash", + "new_zealand", + "num-traits", +] + +[[package]] +name = "nonempty" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9737e026353e5cd0736f98eddae28665118eb6f6600902a7f50db585621fecb6" + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_threads" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" +dependencies = [ + "libc", +] + +[[package]] +name = "ohno" +version = "0.2.1" +dependencies = [ + "futures", + "mutants", + "ohno_macros", + "regex", + "thiserror 2.0.18", + "tokio", + "typeid", +] + +[[package]] +name = "ohno_macros" +version = "0.2.0" +dependencies = [ + "insta", + "mutants", + "prettyplease", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "oorandom" +version = "11.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" + +[[package]] +name = "os_pipe" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "parse-zoneinfo" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f2a05b18d44e2957b88f96ba460715e295bc1d7510468a2f3d3b44535d26c24" +dependencies = [ + "regex", +] + +[[package]] +name = "pastey" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec" + +[[package]] +name = "phf" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" +dependencies = [ + "phf_shared 0.11.3", +] + +[[package]] +name = "phf" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "913273894cec178f401a31ec4b656318d95473527be05c0752cc41cdc32be8b7" +dependencies = [ + "phf_shared 0.12.1", +] + +[[package]] +name = "phf_codegen" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" +dependencies = [ + "phf_generator", + "phf_shared 0.11.3", +] + +[[package]] +name = "phf_generator" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" +dependencies = [ + "phf_shared 0.11.3", + "rand 0.8.5", +] + +[[package]] +name = "phf_shared" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +dependencies = [ + "siphasher", +] + +[[package]] +name = "phf_shared" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06005508882fb681fd97892ecff4b7fd0fee13ef1aa569f8695dae7ab9099981" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "piper" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + +[[package]] +name = "polling" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi", + "pin-project-lite", + "rustix", + "windows-sys 0.61.2", +] + +[[package]] +name = "portable-atomic" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f89776e4d69bb58bc6993e99ffa1d11f228b839984854c7daeb5d37f87cbe950" + +[[package]] +name = "portable-atomic-util" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "predicates" +version = "3.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d19ee57562043d37e82899fade9a22ebab7be9cef5026b07fda9cdd4293573" +dependencies = [ + "anstyle", + "predicates-core", +] + +[[package]] +name = "predicates-core" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "727e462b119fe9c93fd0eb1429a5f7647394014cf3c04ab2c0350eeb09095ffa" + +[[package]] +name = "predicates-tree" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72dd2d6d381dfb73a193c7fca536518d7caee39fc8503f74e7dc0be0531b425c" +dependencies = [ + "predicates-core", + "termtree", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc74d9a594b72ae6656596548f56f667211f8a97b3d4c3d467150794690dc40a" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rapidhash" +version = "4.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d8b5b858a440a0bc02625b62dd95131b9201aa9f69f411195dd4a7cfb1de3d7" +dependencies = [ + "rustversion", +] + +[[package]] +name = "recoverable" +version = "0.1.0" +dependencies = [ + "ohno", + "static_assertions", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde-xml-rs" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65162e9059be2f6a3421ebbb4fef3e74b7d9e7c60c50a0e292c6239f19f1edfa" +dependencies = [ + "log", + "serde", + "thiserror 1.0.69", + "xml-rs", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_spanned" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776" +dependencies = [ + "serde_core", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shared_child" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e362d9935bc50f019969e2f9ecd66786612daae13e8f277be7bfb66e8bed3f7" +dependencies = [ + "libc", + "windows-sys 0.60.2", +] + +[[package]] +name = "shared_thread" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52b86057fcb5423f5018e331ac04623e32d6b5ce85e33300f92c79a1973928b0" + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + +[[package]] +name = "similar" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" + +[[package]] +name = "siphasher" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" + +[[package]] +name = "slab" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "smol" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a33bd3e260892199c3ccfc487c88b2da2265080acb316cd920da72fdfd7c599f" +dependencies = [ + "async-channel", + "async-executor", + "async-fs", + "async-io", + "async-lock", + "async-net", + "async-process", + "blocking", + "futures-lite", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "syn" +version = "2.0.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" + +[[package]] +name = "target-triple" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "591ef38edfb78ca4771ee32cf494cb8771944bee237a9b91fc9c1424ac4b777b" + +[[package]] +name = "tempfile" +version = "3.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c" +dependencies = [ + "fastrand", + "getrandom", + "once_cell", + "rustix", + "windows-sys 0.61.2", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "termtree" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683" + +[[package]] +name = "testing_aids" +version = "0.0.0" +dependencies = [ + "futures", + "mockall", + "mutants", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl 2.0.18", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_aware" +version = "0.6.1" +dependencies = [ + "many_cpus", + "mutants", + "static_assertions", + "thread_aware_macros", +] + +[[package]] +name = "thread_aware_macros" +version = "0.6.1" +dependencies = [ + "mutants", + "syn", + "thread_aware_macros_impl", +] + +[[package]] +name = "thread_aware_macros_impl" +version = "0.6.1" +dependencies = [ + "insta", + "prettyplease", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "tick" +version = "0.1.2" +dependencies = [ + "chrono", + "chrono-tz", + "criterion", + "futures", + "futures-core", + "jiff", + "mutants", + "ohno", + "pin-project-lite", + "serde", + "serde_core", + "serde_json", + "static_assertions", + "time", + "time-tz", + "tokio", +] + +[[package]] +name = "time" +version = "0.3.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9e442fc33d7fdb45aa9bfeb312c095964abdf596f7567261062b2a7107aaabd" +dependencies = [ + "deranged", + "itoa", + "js-sys", + "libc", + "num-conv", + "num_threads", + "powerfmt", + "serde_core", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b36ee98fd31ec7426d599183e8fe26932a8dc1fb76ddb6214d05493377d34ca" + +[[package]] +name = "time-macros" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e552d1249bf61ac2a52db88179fd0673def1e1ad8243a00d9ec9ed71fee3dd" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "time-tz" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "733bc522e97980eb421cbf381160ff225bd14262a48a739110f6653c6258d625" +dependencies = [ + "cfg-if", + "js-sys", + "parse-zoneinfo", + "phf 0.11.3", + "phf_codegen", + "serde", + "serde-xml-rs", + "thiserror 1.0.69", + "time", + "wasm-bindgen", + "windows-sys 0.32.0", +] + +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "tokio" +version = "1.49.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" +dependencies = [ + "pin-project-lite", + "tokio-macros", +] + +[[package]] +name = "tokio-macros" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-util" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "0.9.11+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3afc9a848309fe1aaffaed6e1546a7a14de1f935dc9d89d32afd9a44bab7c46" +dependencies = [ + "indexmap", + "serde_core", + "serde_spanned", + "toml_datetime", + "toml_parser", + "toml_writer", + "winnow", +] + +[[package]] +name = "toml_datetime" +version = "0.7.5+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_parser" +version = "1.0.6+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44" +dependencies = [ + "winnow", +] + +[[package]] +name = "toml_writer" +version = "1.0.6+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607" + +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" +dependencies = [ + "sharded-slab", + "thread_local", + "tracing-core", +] + +[[package]] +name = "trait-variant" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70977707304198400eb4835a78f6a9f928bf41bba420deb8fdb175cd965d77a7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "trybuild" +version = "1.0.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e17e807bff86d2a06b52bca4276746584a78375055b6e45843925ce2802b335" +dependencies = [ + "glob", + "serde", + "serde_derive", + "serde_json", + "target-triple", + "termcolor", + "toml", +] + +[[package]] +name = "typeid" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" + +[[package]] +name = "unicode-ident" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" + +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasip2" +version = "1.0.2+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "windows" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" +dependencies = [ + "windows-collections", + "windows-core", + "windows-future", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" +dependencies = [ + "windows-core", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-future" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" +dependencies = [ + "windows-core", + "windows-link", + "windows-threading", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-numerics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" +dependencies = [ + "windows-core", + "windows-link", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3df6e476185f92a12c072be4a189a0210dcdcf512a1891d6dff9edb874deadc6" +dependencies = [ + "windows_aarch64_msvc 0.32.0", + "windows_i686_gnu 0.32.0", + "windows_i686_msvc 0.32.0", + "windows_x86_64_gnu 0.32.0", + "windows_x86_64_msvc 0.32.0", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc 0.53.1", +] + +[[package]] +name = "windows-threading" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8e92753b1c443191654ec532f14c199742964a061be25d77d7a96f09db20bf5" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + +[[package]] +name = "windows_i686_gnu" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a711c68811799e017b6038e0922cb27a5e2f43a2ddb609fe0b6f3eeda9de615" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + +[[package]] +name = "windows_i686_msvc" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "146c11bb1a02615db74680b32a68e2d61f553cc24c4eb5b4ca10311740e44172" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c912b12f7454c6620635bbff3450962753834be2a594819bd5e945af18ec64bc" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "504a2476202769977a040c6364301a3f65d0cc9e3fb08600b2bda150a0488316" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + +[[package]] +name = "winnow" +version = "0.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" + +[[package]] +name = "xml-rs" +version = "0.8.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f" + +[[package]] +name = "xxhash-rust" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdd20c5420375476fbd4394763288da7eb0cc0b8c11deed431a91562af7335d3" + +[[package]] +name = "zerocopy" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "668f5168d10b9ee831de31933dc111a459c97ec93225beb307aed970d1372dfd" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c7962b26b0a8685668b671ee4b54d007a67d4eaf05fda79ac0ecf41e32270f1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zmij" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfcd145825aace48cff44a8844de64bf75feec3080e0aa5cdbde72961ae51a65"