diff --git a/.cargo/config.toml b/.cargo/config.toml index 16d75ced..03a995c7 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,5 +1,22 @@ [alias] -chk = "check --workspace --all-features --tests --examples --bins" lint = "clippy --workspace --all-features --tests --examples --bins -- -Dclippy::todo" -ci-test = "test --workspace --all-features --lib --tests --no-fail-fast -- --nocapture" + ci-doctest = "test --workspace --all-features --doc --no-fail-fast -- --nocapture" + +# just check the library (without dev deps) +ci-check-min = "hack --workspace check --no-default-features" +ci-check-lib = "hack --workspace --feature-powerset --exclude-features io-uring check" +ci-check-lib-linux = "hack --workspace --feature-powerset check" + +# check everything +ci-check = "hack --workspace --feature-powerset --exclude-features io-uring check --tests --examples" +ci-check-linux = "hack --workspace --feature-powerset check --tests --examples" + +# tests avoiding io-uring feature +ci-test = "hack test --workspace --exclude=actix-rt --exclude=actix-server --all-features --lib --tests --no-fail-fast -- --nocapture" +ci-test-rt = " hack --feature-powerset --exclude-features io-uring test --package=actix-rt --lib --tests --no-fail-fast -- --nocapture" +ci-test-server = "hack --feature-powerset --exclude-features io-uring test --package=actix-server --lib --tests --no-fail-fast -- --nocapture" + +# test with io-uring feature +ci-test-rt-linux = " hack --feature-powerset test --package=actix-rt --lib --tests --no-fail-fast -- --nocapture" +ci-test-server-linux = "hack --feature-powerset test --package=actix-server --lib --tests --no-fail-fast -- --nocapture" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a0f62910..45841fb8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -75,36 +75,47 @@ jobs: command: install args: cargo-hack - - name: check minimal + - name: check lib + if: > + matrix.target.os != 'ubuntu-latest' + && matrix.target.triple != 'x86_64-pc-windows-gnu' uses: actions-rs/cargo@v1 - with: - command: hack - args: check --workspace --no-default-features - - - name: check minimal + tests + with: { command: ci-check-lib } + - name: check lib + if: matrix.target.os == 'ubuntu-latest' uses: actions-rs/cargo@v1 - with: - command: hack - args: check --workspace --no-default-features --tests --examples - - - name: check default + with: { command: ci-check-lib-linux } + - name: check lib + if: matrix.target.triple == 'x86_64-pc-windows-gnu' uses: actions-rs/cargo@v1 - with: - command: check - args: --workspace --tests --examples - + with: { command: ci-check-min } + - name: check full # TODO: compile OpenSSL and run tests on MinGW - if: matrix.target.triple != 'x86_64-pc-windows-gnu' + if: > + matrix.target.os != 'ubuntu-latest' + && matrix.target.triple != 'x86_64-pc-windows-gnu' uses: actions-rs/cargo@v1 - with: - command: check - args: --workspace --all-features --tests --examples + with: { command: ci-check } + - name: check all + if: matrix.target.os == 'ubuntu-latest' + uses: actions-rs/cargo@v1 + with: { command: ci-check-linux } - name: tests - if: matrix.target.triple != 'x86_64-pc-windows-gnu' - uses: actions-rs/cargo@v1 - with: { command: ci-test } + if: > + matrix.target.os != 'ubuntu-latest' + && matrix.target.triple != 'x86_64-pc-windows-gnu' + run: | + cargo ci-test + cargo ci-test-rt + cargo ci-test-server + - name: tests + if: matrix.target.os == 'ubuntu-latest' + run: | + cargo ci-test + cargo ci-test-rt-linux + cargo ci-test-server-linux - name: Generate coverage file if: > @@ -120,8 +131,7 @@ jobs: && matrix.version == 'stable' && github.ref == 'refs/heads/master' uses: codecov/codecov-action@v1 - with: - file: cobertura.xml + with: { file: cobertura.xml } - name: Clear the cargo caches run: | diff --git a/actix-codec/Cargo.toml b/actix-codec/Cargo.toml index 815f1039..7bf1c941 100644 --- a/actix-codec/Cargo.toml +++ b/actix-codec/Cargo.toml @@ -20,5 +20,5 @@ futures-core = { version = "0.3.7", default-features = false } futures-sink = { version = "0.3.7", default-features = false } log = "0.4" pin-project-lite = "0.2" -tokio = "1" +tokio = "1.5.1" tokio-util = { version = "0.6", features = ["codec", "io"] } diff --git a/actix-macros/CHANGES.md b/actix-macros/CHANGES.md index 8d7037cf..0509eb35 100644 --- a/actix-macros/CHANGES.md +++ b/actix-macros/CHANGES.md @@ -3,6 +3,13 @@ ## Unreleased - 2021-xx-xx +## 0.2.2 - 2021-10-14 +* Improve error recovery potential when macro input is invalid. [#391] +* Allow custom `System`s on test macro. [#391] + +[#391]: https://github.com/actix/actix-net/pull/391 + + ## 0.2.1 - 2021-02-02 * Add optional argument `system` to `main` macro which can be used to specify the path to `actix_rt::System` (useful for re-exports). [#363] diff --git a/actix-macros/Cargo.toml b/actix-macros/Cargo.toml index fd5737f8..ea1b51d0 100644 --- a/actix-macros/Cargo.toml +++ b/actix-macros/Cargo.toml @@ -1,12 +1,12 @@ [package] name = "actix-macros" -version = "0.2.1" +version = "0.2.2" authors = [ "Nikolay Kim ", "Ibraheem Ahmed ", ] description = "Macros for Actix system and runtime" -repository = "https://github.com/actix/actix-net" +repository = "https://github.com/actix/actix-net.git" categories = ["network-programming", "asynchronous"] license = "MIT OR Apache-2.0" edition = "2018" diff --git a/actix-macros/src/lib.rs b/actix-macros/src/lib.rs index d8aa5f77..4be79178 100644 --- a/actix-macros/src/lib.rs +++ b/actix-macros/src/lib.rs @@ -28,7 +28,12 @@ use quote::quote; #[proc_macro_attribute] #[cfg(not(test))] // Work around for rust-lang/rust#62127 pub fn main(args: TokenStream, item: TokenStream) -> TokenStream { - let mut input = syn::parse_macro_input!(item as syn::ItemFn); + let mut input = match syn::parse::(item.clone()) { + Ok(input) => input, + // on parse err, make IDEs happy; see fn docs + Err(err) => return input_and_compile_error(item, err), + }; + let args = syn::parse_macro_input!(args as syn::AttributeArgs); let attrs = &input.attrs; @@ -101,8 +106,15 @@ pub fn main(args: TokenStream, item: TokenStream) -> TokenStream { /// } /// ``` #[proc_macro_attribute] -pub fn test(_: TokenStream, item: TokenStream) -> TokenStream { - let mut input = syn::parse_macro_input!(item as syn::ItemFn); +pub fn test(args: TokenStream, item: TokenStream) -> TokenStream { + let mut input = match syn::parse::(item.clone()) { + Ok(input) => input, + // on parse err, make IDEs happy; see fn docs + Err(err) => return input_and_compile_error(item, err), + }; + + let args = syn::parse_macro_input!(args as syn::AttributeArgs); + let attrs = &input.attrs; let vis = &input.vis; let sig = &mut input.sig; @@ -132,13 +144,59 @@ pub fn test(_: TokenStream, item: TokenStream) -> TokenStream { quote!(#[test]) }; + let mut system = syn::parse_str::("::actix_rt::System").unwrap(); + + for arg in &args { + match arg { + syn::NestedMeta::Meta(syn::Meta::NameValue(syn::MetaNameValue { + lit: syn::Lit::Str(lit), + path, + .. + })) => match path + .get_ident() + .map(|i| i.to_string().to_lowercase()) + .as_deref() + { + Some("system") => match lit.parse() { + Ok(path) => system = path, + Err(_) => { + return syn::Error::new_spanned(lit, "Expected path") + .to_compile_error() + .into(); + } + }, + _ => { + return syn::Error::new_spanned(arg, "Unknown attribute specified") + .to_compile_error() + .into(); + } + }, + _ => { + return syn::Error::new_spanned(arg, "Unknown attribute specified") + .to_compile_error() + .into(); + } + } + } + (quote! { #missing_test_attr #(#attrs)* #vis #sig { - actix_rt::System::new() - .block_on(async { #body }) + <#system>::new().block_on(async { #body }) } }) .into() } + +/// Converts the error to a token stream and appends it to the original input. +/// +/// Returning the original input in addition to the error is good for IDEs which can gracefully +/// recover and show more precise errors within the macro body. +/// +/// See for more info. +fn input_and_compile_error(mut item: TokenStream, err: syn::Error) -> TokenStream { + let compile_err = TokenStream::from(err.to_compile_error()); + item.extend(compile_err); + return item; +} diff --git a/actix-macros/tests/trybuild.rs b/actix-macros/tests/trybuild.rs index 410d9499..c7f4a5ca 100644 --- a/actix-macros/tests/trybuild.rs +++ b/actix-macros/tests/trybuild.rs @@ -11,4 +11,7 @@ fn compile_macros() { t.pass("tests/trybuild/test-01-basic.rs"); t.pass("tests/trybuild/test-02-keep-attrs.rs"); t.compile_fail("tests/trybuild/test-03-only-async.rs"); + t.pass("tests/trybuild/test-04-system-path.rs"); + t.compile_fail("tests/trybuild/test-05-system-expect-path.rs"); + t.compile_fail("tests/trybuild/test-06-unknown-attr.rs"); } diff --git a/actix-macros/tests/trybuild/test-04-system-path.rs b/actix-macros/tests/trybuild/test-04-system-path.rs new file mode 100644 index 00000000..6d0f9951 --- /dev/null +++ b/actix-macros/tests/trybuild/test-04-system-path.rs @@ -0,0 +1,10 @@ +mod system { + pub use actix_rt::System as MySystem; +} + +#[actix_rt::test(system = "system::MySystem")] +async fn my_test() { + futures_util::future::ready(()).await +} + +fn main() {} diff --git a/actix-macros/tests/trybuild/test-05-system-expect-path.rs b/actix-macros/tests/trybuild/test-05-system-expect-path.rs new file mode 100644 index 00000000..fda3502b --- /dev/null +++ b/actix-macros/tests/trybuild/test-05-system-expect-path.rs @@ -0,0 +1,4 @@ +#[actix_rt::test(system = "!@#*&")] +async fn my_test() {} + +fn main() {} diff --git a/actix-macros/tests/trybuild/test-05-system-expect-path.stderr b/actix-macros/tests/trybuild/test-05-system-expect-path.stderr new file mode 100644 index 00000000..5eab3cf5 --- /dev/null +++ b/actix-macros/tests/trybuild/test-05-system-expect-path.stderr @@ -0,0 +1,5 @@ +error: Expected path + --> $DIR/test-05-system-expect-path.rs:1:27 + | +1 | #[actix_rt::test(system = "!@#*&")] + | ^^^^^^^ diff --git a/actix-macros/tests/trybuild/test-06-unknown-attr.rs b/actix-macros/tests/trybuild/test-06-unknown-attr.rs new file mode 100644 index 00000000..5d0c20c1 --- /dev/null +++ b/actix-macros/tests/trybuild/test-06-unknown-attr.rs @@ -0,0 +1,7 @@ +#[actix_rt::test(foo = "bar")] +async fn my_test_1() {} + +#[actix_rt::test(bar::baz)] +async fn my_test_2() {} + +fn main() {} diff --git a/actix-macros/tests/trybuild/test-06-unknown-attr.stderr b/actix-macros/tests/trybuild/test-06-unknown-attr.stderr new file mode 100644 index 00000000..f6896ddc --- /dev/null +++ b/actix-macros/tests/trybuild/test-06-unknown-attr.stderr @@ -0,0 +1,11 @@ +error: Unknown attribute specified + --> $DIR/test-06-unknown-attr.rs:1:18 + | +1 | #[actix_rt::test(foo = "bar")] + | ^^^^^^^^^^^ + +error: Unknown attribute specified + --> $DIR/test-06-unknown-attr.rs:4:18 + | +4 | #[actix_rt::test(bar::baz)] + | ^^^^^^^^ diff --git a/actix-rt/CHANGES.md b/actix-rt/CHANGES.md index 42879e12..642cf27a 100644 --- a/actix-rt/CHANGES.md +++ b/actix-rt/CHANGES.md @@ -1,9 +1,14 @@ # Changes ## Unreleased - 2021-xx-xx + + +## 2.3.0 - 2021-10-11 * The `spawn` method can now resolve with non-unit outputs. [#369] +* Add experimental (semver-exempt) `io-uring` feature for enabling async file I/O on linux. [#374] [#369]: https://github.com/actix/actix-net/pull/369 +[#374]: https://github.com/actix/actix-net/pull/374 ## 2.2.0 - 2021-03-29 diff --git a/actix-rt/Cargo.toml b/actix-rt/Cargo.toml index f4a90d2c..942d54aa 100644 --- a/actix-rt/Cargo.toml +++ b/actix-rt/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-rt" -version = "2.2.0" +version = "2.3.0" authors = [ "Nikolay Kim ", "Rob Ede ", @@ -8,8 +8,7 @@ authors = [ description = "Tokio-based single-threaded async runtime for the Actix ecosystem" keywords = ["async", "futures", "io", "runtime"] homepage = "https://actix.rs" -repository = "https://github.com/actix/actix-net" -documentation = "https://docs.rs/actix-rt" +repository = "https://github.com/actix/actix-net.git" categories = ["network-programming", "asynchronous"] license = "MIT OR Apache-2.0" edition = "2018" @@ -21,13 +20,17 @@ path = "src/lib.rs" [features] default = ["macros"] macros = ["actix-macros"] +io-uring = ["tokio-uring"] [dependencies] actix-macros = { version = "0.2.0", optional = true } futures-core = { version = "0.3", default-features = false } -tokio = { version = "1.3", features = ["rt", "net", "parking_lot", "signal", "sync", "time"] } +tokio = { version = "1.5.1", features = ["rt", "net", "parking_lot", "signal", "sync", "time"] } + +[target.'cfg(target_os = "linux")'.dependencies] +tokio-uring = { version = "0.1", optional = true } [dev-dependencies] -tokio = { version = "1.2", features = ["full"] } +tokio = { version = "1.5.1", features = ["full"] } hyper = { version = "0.14", default-features = false, features = ["server", "tcp", "http1"] } diff --git a/actix-rt/README.md b/actix-rt/README.md index 4ad75f09..eb1d1b6f 100644 --- a/actix-rt/README.md +++ b/actix-rt/README.md @@ -3,11 +3,11 @@ > Tokio-based single-threaded async runtime for the Actix ecosystem. [![crates.io](https://img.shields.io/crates/v/actix-rt?label=latest)](https://crates.io/crates/actix-rt) -[![Documentation](https://docs.rs/actix-rt/badge.svg?version=2.2.0)](https://docs.rs/actix-rt/2.2.0) +[![Documentation](https://docs.rs/actix-rt/badge.svg?version=2.3.0)](https://docs.rs/actix-rt/2.3.0) [![Version](https://img.shields.io/badge/rustc-1.46+-ab6000.svg)](https://blog.rust-lang.org/2020/03/12/Rust-1.46.html) ![MIT or Apache 2.0 licensed](https://img.shields.io/crates/l/actix-rt.svg)
-[![dependency status](https://deps.rs/crate/actix-rt/2.2.0/status.svg)](https://deps.rs/crate/actix-rt/2.2.0) +[![dependency status](https://deps.rs/crate/actix-rt/2.3.0/status.svg)](https://deps.rs/crate/actix-rt/2.3.0) ![Download](https://img.shields.io/crates/d/actix-rt.svg) [![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/WghFtEH6Hb) diff --git a/actix-rt/src/arbiter.rs b/actix-rt/src/arbiter.rs index 9ff1419d..97084f05 100644 --- a/actix-rt/src/arbiter.rs +++ b/actix-rt/src/arbiter.rs @@ -9,12 +9,9 @@ use std::{ }; use futures_core::ready; -use tokio::{sync::mpsc, task::LocalSet}; +use tokio::sync::mpsc; -use crate::{ - runtime::{default_tokio_runtime, Runtime}, - system::{System, SystemCommand}, -}; +use crate::system::{System, SystemCommand}; pub(crate) static COUNT: AtomicUsize = AtomicUsize::new(0); @@ -98,16 +95,19 @@ impl Arbiter { /// /// # Panics /// Panics if a [System] is not registered on the current thread. + #[cfg(not(all(target_os = "linux", feature = "io-uring")))] #[allow(clippy::new_without_default)] pub fn new() -> Arbiter { Self::with_tokio_rt(|| { - default_tokio_runtime().expect("Cannot create new Arbiter's Runtime.") + crate::runtime::default_tokio_runtime() + .expect("Cannot create new Arbiter's Runtime.") }) } /// Spawn a new Arbiter using the [Tokio Runtime](tokio-runtime) returned from a closure. /// /// [tokio-runtime]: tokio::runtime::Runtime + #[cfg(not(all(target_os = "linux", feature = "io-uring")))] #[doc(hidden)] pub fn with_tokio_rt(runtime_factory: F) -> Arbiter where @@ -127,7 +127,7 @@ impl Arbiter { .spawn({ let tx = tx.clone(); move || { - let rt = Runtime::from(runtime_factory()); + let rt = crate::runtime::Runtime::from(runtime_factory()); let hnd = ArbiterHandle::new(tx); System::set_current(sys); @@ -159,15 +159,67 @@ impl Arbiter { Arbiter { tx, thread_handle } } - /// Sets up an Arbiter runner in a new System using the provided runtime local task set. - pub(crate) fn in_new_system(local: &LocalSet) -> ArbiterHandle { + /// Spawn a new Arbiter thread and start its event loop with `tokio-uring` runtime. + /// + /// # Panics + /// Panics if a [System] is not registered on the current thread. + #[cfg(all(target_os = "linux", feature = "io-uring"))] + #[allow(clippy::new_without_default)] + pub fn new() -> Arbiter { + let sys = System::current(); + let system_id = sys.id(); + let arb_id = COUNT.fetch_add(1, Ordering::Relaxed); + + let name = format!("actix-rt|system:{}|arbiter:{}", system_id, arb_id); + let (tx, rx) = mpsc::unbounded_channel(); + + let (ready_tx, ready_rx) = std::sync::mpsc::channel::<()>(); + + let thread_handle = thread::Builder::new() + .name(name.clone()) + .spawn({ + let tx = tx.clone(); + move || { + let hnd = ArbiterHandle::new(tx); + + System::set_current(sys); + + HANDLE.with(|cell| *cell.borrow_mut() = Some(hnd.clone())); + + // register arbiter + let _ = System::current() + .tx() + .send(SystemCommand::RegisterArbiter(arb_id, hnd)); + + ready_tx.send(()).unwrap(); + + // run arbiter event processing loop + tokio_uring::start(ArbiterRunner { rx }); + + // deregister arbiter + let _ = System::current() + .tx() + .send(SystemCommand::DeregisterArbiter(arb_id)); + } + }) + .unwrap_or_else(|err| { + panic!("Cannot spawn Arbiter's thread: {:?}. {:?}", &name, err) + }); + + ready_rx.recv().unwrap(); + + Arbiter { tx, thread_handle } + } + + /// Sets up an Arbiter runner in a new System using the environment's local set. + pub(crate) fn in_new_system() -> ArbiterHandle { let (tx, rx) = mpsc::unbounded_channel(); let hnd = ArbiterHandle::new(tx); HANDLE.with(|cell| *cell.borrow_mut() = Some(hnd.clone())); - local.spawn_local(ArbiterRunner { rx }); + crate::spawn(ArbiterRunner { rx }); hnd } diff --git a/actix-rt/src/lib.rs b/actix-rt/src/lib.rs index 95afcac9..e078dd06 100644 --- a/actix-rt/src/lib.rs +++ b/actix-rt/src/lib.rs @@ -32,6 +32,10 @@ //! arbiter.stop(); //! arbiter.join().unwrap(); //! ``` +//! +//! # `io-uring` Support +//! There is experimental support for using io-uring with this crate by enabling the +//! `io-uring` feature. For now, it is semver exempt. #![deny(rust_2018_idioms, nonstandard_style)] #![allow(clippy::type_complexity)] @@ -39,6 +43,9 @@ #![doc(html_logo_url = "https://actix.rs/img/logo.png")] #![doc(html_favicon_url = "https://actix.rs/favicon.ico")] +#[cfg(all(not(target_os = "linux"), feature = "io-uring"))] +compile_error!("io_uring is a linux only feature."); + use std::future::Future; use tokio::task::JoinHandle; diff --git a/actix-rt/src/runtime.rs b/actix-rt/src/runtime.rs index 1adbf6c0..25937003 100644 --- a/actix-rt/src/runtime.rs +++ b/actix-rt/src/runtime.rs @@ -31,11 +31,6 @@ impl Runtime { }) } - /// Reference to local task set. - pub(crate) fn local_set(&self) -> &LocalSet { - &self.local - } - /// Offload a future onto the single-threaded runtime. /// /// The returned join handle can be used to await the future's result. diff --git a/actix-rt/src/system.rs b/actix-rt/src/system.rs index 3bc8a6e3..4f262ede 100644 --- a/actix-rt/src/system.rs +++ b/actix-rt/src/system.rs @@ -54,7 +54,7 @@ impl System { let (sys_tx, sys_rx) = mpsc::unbounded_channel(); let rt = Runtime::from(runtime_factory()); - let sys_arbiter = Arbiter::in_new_system(rt.local_set()); + let sys_arbiter = rt.block_on(async { Arbiter::in_new_system() }); let system = System::construct(sys_tx, sys_arbiter.clone()); system diff --git a/actix-rt/tests/tests.rs b/actix-rt/tests/tests.rs index e66696bf..5fe1e894 100644 --- a/actix-rt/tests/tests.rs +++ b/actix-rt/tests/tests.rs @@ -1,10 +1,6 @@ use std::{ future::Future, - sync::{ - atomic::{AtomicBool, Ordering}, - mpsc::channel, - Arc, - }, + sync::mpsc::channel, thread, time::{Duration, Instant}, }; @@ -221,8 +217,8 @@ fn system_stop_stops_arbiters() { System::current().stop(); sys.run().unwrap(); - // account for slightly slow thread de-spawns (only observed on windows) - thread::sleep(Duration::from_millis(100)); + // account for slightly slow thread de-spawns + thread::sleep(Duration::from_millis(500)); // arbiter should be dead and return false assert!(!Arbiter::current().spawn_fn(|| {})); @@ -231,6 +227,7 @@ fn system_stop_stops_arbiters() { arb.join().unwrap(); } +#[cfg(not(feature = "io-uring"))] #[test] fn new_system_with_tokio() { let (tx, rx) = channel(); @@ -263,8 +260,14 @@ fn new_system_with_tokio() { assert_eq!(rx.recv().unwrap(), 42); } +#[cfg(not(feature = "io-uring"))] #[test] fn new_arbiter_with_tokio() { + use std::sync::{ + atomic::{AtomicBool, Ordering}, + Arc, + }; + let _ = System::new(); let arb = Arbiter::with_tokio_rt(|| { @@ -323,3 +326,32 @@ fn spawn_local() { h(actix_rt::spawn(async { 1 })); }) } + +#[cfg(all(target_os = "linux", feature = "io-uring"))] +#[test] +fn tokio_uring_arbiter() { + let system = System::new(); + let (tx, rx) = std::sync::mpsc::channel(); + + Arbiter::new().spawn(async move { + let handle = actix_rt::spawn(async move { + let f = tokio_uring::fs::File::create("test.txt").await.unwrap(); + let buf = b"Hello World!"; + + let (res, _) = f.write_at(&buf[..], 0).await; + assert!(res.is_ok()); + + f.sync_all().await.unwrap(); + f.close().await.unwrap(); + + std::fs::remove_file("test.txt").unwrap(); + }); + + handle.await.unwrap(); + tx.send(true).unwrap(); + }); + + assert!(rx.recv().unwrap()); + + drop(system); +} diff --git a/actix-server/CHANGES.md b/actix-server/CHANGES.md index 86cde4f0..54096eca 100644 --- a/actix-server/CHANGES.md +++ b/actix-server/CHANGES.md @@ -1,21 +1,25 @@ # Changes ## Unreleased - 2021-xx-xx -* Remove `config` module. `ServiceConfig`, `ServiceRuntime` public types are removed due to this change. [#349] -* Remove `ServerBuilder::configure` [#349] -* Server no long listens to SIGHUP signal. - It actually did not take any action when receiving SIGHUP, the only thing SIGHUP did was to stop - the Server from receiving any future signal, because the `Signals` future stops on the first - signal received [#389] + +## 2.0.0-beta.6 - 2021-10-11 +* Add experimental (semver-exempt) `io-uring` feature for enabling async file I/O on linux. [#374] +* Server no long listens to `SIGHUP` signal. Previously, the received was not used but did block + subsequent exit signals from working. [#389] +* Remove `config` module. `ServiceConfig`, `ServiceRuntime` public types are removed due to + this change. [#349] +* Remove `ServerBuilder::configure` [#349] + +[#374]: https://github.com/actix/actix-net/pull/374 [#349]: https://github.com/actix/actix-net/pull/349 [#389]: https://github.com/actix/actix-net/pull/389 ## 2.0.0-beta.5 - 2021-04-20 -* Server shutdown would notify all workers to exit regardless if shutdown is graceful. - This would make all worker shutdown immediately in force shutdown case. [#333] - +* Server shutdown notifies all workers to exit regardless if shutdown is graceful. This causes all + workers to shutdown immediately in force shutdown case. [#333] + [#333]: https://github.com/actix/actix-net/pull/333 diff --git a/actix-server/Cargo.toml b/actix-server/Cargo.toml index 58471cf9..8fd3112b 100755 --- a/actix-server/Cargo.toml +++ b/actix-server/Cargo.toml @@ -1,13 +1,13 @@ [package] name = "actix-server" -version = "2.0.0-beta.5" +version = "2.0.0-beta.6" authors = [ "Nikolay Kim ", "fakeshadow <24548779@qq.com>", ] description = "General purpose TCP server built for the Actix ecosystem" keywords = ["network", "framework", "async", "futures"] -repository = "https://github.com/actix/actix-net" +repository = "https://github.com/actix/actix-net.git" categories = ["network-programming", "asynchronous"] license = "MIT OR Apache-2.0" edition = "2018" @@ -18,6 +18,7 @@ path = "src/lib.rs" [features] default = [] +io-uring = ["actix-rt/io-uring"] [dependencies] actix-rt = { version = "2.0.0", default-features = false } @@ -28,13 +29,13 @@ futures-core = { version = "0.3.7", default-features = false, features = ["alloc log = "0.4" mio = { version = "0.7.6", features = ["os-poll", "net"] } num_cpus = "1.13" -tokio = { version = "1.2", features = ["sync"] } +tokio = { version = "1.5.1", features = ["sync"] } [dev-dependencies] actix-codec = "0.4.0-beta.1" actix-rt = "2.0.0" bytes = "1" -env_logger = "0.8" +env_logger = "0.9" futures-util = { version = "0.3.7", default-features = false, features = ["sink"] } -tokio = { version = "1", features = ["io-util"] } +tokio = { version = "1.5.1", features = ["io-util"] } diff --git a/actix-server/src/builder.rs b/actix-server/src/builder.rs index 46a55ef4..871abb5b 100644 --- a/actix-server/src/builder.rs +++ b/actix-server/src/builder.rs @@ -312,23 +312,25 @@ impl ServerBuilder { // Handle `SIGINT`, `SIGTERM`, `SIGQUIT` signals and stop actix system match sig { Signal::Int => { - info!("SIGINT received, exiting"); + info!("SIGINT received, starting forced shutdown"); self.exit = true; self.handle_cmd(ServerCommand::Stop { graceful: false, completion: None, }) } + Signal::Term => { - info!("SIGTERM received, stopping"); + info!("SIGTERM received, starting graceful shutdown"); self.exit = true; self.handle_cmd(ServerCommand::Stop { graceful: true, completion: None, }) } + Signal::Quit => { - info!("SIGQUIT received, exiting"); + info!("SIGQUIT received, starting forced shutdown"); self.exit = true; self.handle_cmd(ServerCommand::Stop { graceful: false, @@ -359,12 +361,14 @@ impl ServerBuilder { rt::spawn(async move { if graceful { + // wait for all workers to shut down let _ = join_all(stop).await; } if let Some(tx) = completion { let _ = tx.send(()); } + for tx in notify { let _ = tx.send(()); } diff --git a/actix-server/src/server.rs b/actix-server/src/server.rs index 6b0d0aea..f0dfca0b 100644 --- a/actix-server/src/server.rs +++ b/actix-server/src/server.rs @@ -15,8 +15,8 @@ pub(crate) enum ServerCommand { Pause(oneshot::Sender<()>), Resume(oneshot::Sender<()>), Signal(Signal), - /// Whether to try and shut down gracefully Stop { + /// True if shut down should be graceful. graceful: bool, completion: Option>, }, @@ -24,6 +24,13 @@ pub(crate) enum ServerCommand { Notify(oneshot::Sender<()>), } +/// Server handle. +/// +/// # Shutdown Signals +/// On UNIX systems, `SIGQUIT` will start a graceful shutdown and `SIGTERM` or `SIGINT` will start a +/// forced shutdown. On Windows, a CTRL-C signal will start a forced shutdown. +/// +/// A graceful shutdown will wait for all workers to stop first. #[derive(Debug)] pub struct Server( UnboundedSender, diff --git a/actix-server/src/signals.rs b/actix-server/src/signals.rs index cdd96b9c..c9cdb45e 100644 --- a/actix-server/src/signals.rs +++ b/actix-server/src/signals.rs @@ -4,27 +4,33 @@ use std::task::{Context, Poll}; use crate::server::Server; -/// Different types of process signals +/// Types of process signals. #[allow(dead_code)] #[derive(PartialEq, Clone, Copy, Debug)] pub(crate) enum Signal { - /// SIGINT + /// `SIGINT` Int, - /// SIGTERM + + /// `SIGTERM` Term, - /// SIGQUIT + + /// `SIGQUIT` Quit, } +/// Process signal listener. pub(crate) struct Signals { srv: Server, + #[cfg(not(unix))] signals: futures_core::future::LocalBoxFuture<'static, std::io::Result<()>>, + #[cfg(unix)] signals: Vec<(Signal, actix_rt::signal::unix::Signal)>, } impl Signals { + /// Spawns a signal listening future that is able to send commands to the `Server`. pub(crate) fn start(srv: Server) { #[cfg(not(unix))] { @@ -33,6 +39,7 @@ impl Signals { signals: Box::pin(actix_rt::signal::ctrl_c()), }); } + #[cfg(unix)] { use actix_rt::signal::unix; @@ -76,6 +83,7 @@ impl Future for Signals { } Poll::Pending => Poll::Pending, } + #[cfg(unix)] { for (sig, fut) in self.signals.iter_mut() { diff --git a/actix-server/src/test_server.rs b/actix-server/src/test_server.rs index 0611cf4b..ad6ee8ee 100644 --- a/actix-server/src/test_server.rs +++ b/actix-server/src/test_server.rs @@ -5,13 +5,12 @@ use actix_rt::{net::TcpStream, System}; use crate::{Server, ServerBuilder, ServiceFactory}; -/// The `TestServer` type. +/// A testing server. /// -/// `TestServer` is very simple test server that simplify process of writing -/// integration tests for actix-net applications. +/// `TestServer` is very simple test server that simplify process of writing integration tests for +/// network applications. /// /// # Examples -/// /// ``` /// use actix_service::fn_service; /// use actix_server::TestServer; @@ -39,7 +38,7 @@ pub struct TestServerRuntime { } impl TestServer { - /// Start new server with server builder + /// Start new server with server builder. pub fn start(mut factory: F) -> TestServerRuntime where F: FnMut(ServerBuilder) -> ServerBuilder + Send + 'static, @@ -64,7 +63,7 @@ impl TestServer { } } - /// Start new test server with application factory + /// Start new test server with application factory. pub fn with>(factory: F) -> TestServerRuntime { let (tx, rx) = mpsc::channel(); @@ -99,7 +98,7 @@ impl TestServer { } } - /// Get first available unused local address + /// Get first available unused local address. pub fn unused_addr() -> net::SocketAddr { let addr: net::SocketAddr = "127.0.0.1:0".parse().unwrap(); let socket = mio::net::TcpSocket::new_v4().unwrap(); @@ -111,27 +110,27 @@ impl TestServer { } impl TestServerRuntime { - /// Test server host + /// Test server host. pub fn host(&self) -> &str { &self.host } - /// Test server port + /// Test server port. pub fn port(&self) -> u16 { self.port } - /// Get test server address + /// Get test server address. pub fn addr(&self) -> net::SocketAddr { self.addr } - /// Stop http server + /// Stop server. fn stop(&mut self) { self.system.stop(); } - /// Connect to server, return tokio TcpStream + /// Connect to server, returning a Tokio `TcpStream`. pub fn connect(&self) -> std::io::Result { TcpStream::from_std(net::TcpStream::connect(self.addr)?) } diff --git a/actix-server/src/worker.rs b/actix-server/src/worker.rs index a974522a..b99b2da2 100644 --- a/actix-server/src/worker.rs +++ b/actix-server/src/worker.rs @@ -280,14 +280,24 @@ impl ServerWorker { let counter_clone = counter.clone(); // every worker runs in it's own arbiter. // use a custom tokio runtime builder to change the settings of runtime. - Arbiter::with_tokio_rt(move || { + #[cfg(all(target_os = "linux", feature = "io-uring"))] + let arbiter = { + // TODO: pass max blocking thread config when tokio-uring enable configuration + // on building runtime. + let _ = config.max_blocking_threads; + Arbiter::new() + }; + + #[cfg(not(all(target_os = "linux", feature = "io-uring")))] + let arbiter = Arbiter::with_tokio_rt(move || { tokio::runtime::Builder::new_current_thread() .enable_all() .max_blocking_threads(config.max_blocking_threads) .build() .unwrap() - }) - .spawn(async move { + }); + + arbiter.spawn(async move { let fut = factories .iter() .enumerate() @@ -419,13 +429,15 @@ struct Restart { fut: LocalBoxFuture<'static, Result<(usize, BoxedServerService), ()>>, } -// Shutdown keep states necessary for server shutdown: -// Sleep for interval check the shutdown progress. -// Instant for the start time of shutdown. -// Sender for send back the shutdown outcome(force/grace) to StopCommand caller. +/// State necessary for server shutdown. struct Shutdown { + // Interval for checking the shutdown progress. timer: Pin>, + + /// Start time of shutdown. start_from: Instant, + + /// Notify of the shutdown outcome (force/grace) to stop caller. tx: oneshot::Sender, } @@ -511,23 +523,25 @@ impl Future for ServerWorker { self.poll(cx) } WorkerState::Shutdown(ref mut shutdown) => { - // Wait for 1 second. + // wait for 1 second ready!(shutdown.timer.as_mut().poll(cx)); if this.counter.total() == 0 { - // Graceful shutdown. + // graceful shutdown if let WorkerState::Shutdown(shutdown) = mem::take(&mut this.state) { let _ = shutdown.tx.send(true); } + Poll::Ready(()) } else if shutdown.start_from.elapsed() >= this.shutdown_timeout { - // Timeout forceful shutdown. + // timeout forceful shutdown if let WorkerState::Shutdown(shutdown) = mem::take(&mut this.state) { let _ = shutdown.tx.send(false); } + Poll::Ready(()) } else { - // Reset timer and wait for 1 second. + // reset timer and wait for 1 second let time = Instant::now() + Duration::from_secs(1); shutdown.timer.as_mut().reset(time); shutdown.timer.as_mut().poll(cx) diff --git a/actix-service/CHANGES.md b/actix-service/CHANGES.md index a0130dbc..c01fd8dd 100644 --- a/actix-service/CHANGES.md +++ b/actix-service/CHANGES.md @@ -3,6 +3,10 @@ ## Unreleased - 2021-xx-xx +## 2.0.1 - 2021-10-11 +* Documentation fix. + + ## 2.0.0 - 2021-04-16 * Removed pipeline and related structs/functions. [#335] diff --git a/actix-service/Cargo.toml b/actix-service/Cargo.toml index 7865cd86..f30768fb 100644 --- a/actix-service/Cargo.toml +++ b/actix-service/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-service" -version = "2.0.0" +version = "2.0.1" authors = [ "Nikolay Kim ", "Rob Ede ", @@ -8,7 +8,7 @@ authors = [ ] description = "Service trait and combinators for representing asynchronous request/response operations." keywords = ["network", "framework", "async", "futures", "service"] -categories = ["network-programming", "asynchronous"] +categories = ["network-programming", "asynchronous", "no-std"] repository = "https://github.com/actix/actix-net" license = "MIT OR Apache-2.0" edition = "2018" diff --git a/actix-service/README.md b/actix-service/README.md index 913ac199..d9afdb9c 100644 --- a/actix-service/README.md +++ b/actix-service/README.md @@ -3,10 +3,10 @@ > Service trait and combinators for representing asynchronous request/response operations. [![crates.io](https://img.shields.io/crates/v/actix-service?label=latest)](https://crates.io/crates/actix-service) -[![Documentation](https://docs.rs/actix-service/badge.svg?version=2.0.0)](https://docs.rs/actix-service/2.0.0) +[![Documentation](https://docs.rs/actix-service/badge.svg?version=2.0.1)](https://docs.rs/actix-service/2.0.1) [![Version](https://img.shields.io/badge/rustc-1.46+-ab6000.svg)](https://blog.rust-lang.org/2020/03/12/Rust-1.46.html) ![License](https://img.shields.io/crates/l/actix-service.svg) -[![Dependency Status](https://deps.rs/crate/actix-service/2.0.0/status.svg)](https://deps.rs/crate/actix-service/2.0.0) +[![Dependency Status](https://deps.rs/crate/actix-service/2.0.1/status.svg)](https://deps.rs/crate/actix-service/2.0.1) ![Download](https://img.shields.io/crates/d/actix-service.svg) [![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x) diff --git a/actix-tls/Cargo.toml b/actix-tls/Cargo.toml index 995c0e1b..8a864317 100755 --- a/actix-tls/Cargo.toml +++ b/actix-tls/Cargo.toml @@ -64,7 +64,7 @@ tokio-native-tls = { version = "0.3", optional = true } [dev-dependencies] actix-rt = "2.2.0" -actix-server = "2.0.0-beta.5" +actix-server = "2.0.0-beta.6" bytes = "1" env_logger = "0.8" futures-util = { version = "0.3.7", default-features = false, features = ["sink"] } diff --git a/actix-tls/src/lib.rs b/actix-tls/src/lib.rs index 83e18d58..dbda8834 100644 --- a/actix-tls/src/lib.rs +++ b/actix-tls/src/lib.rs @@ -5,6 +5,7 @@ #![doc(html_favicon_url = "https://actix.rs/favicon.ico")] #[cfg(feature = "openssl")] +#[allow(unused_extern_crates)] extern crate tls_openssl as openssl; #[cfg(feature = "accept")] diff --git a/bytestring/Cargo.toml b/bytestring/Cargo.toml index 3dbf07b7..34237ce9 100644 --- a/bytestring/Cargo.toml +++ b/bytestring/Cargo.toml @@ -24,4 +24,5 @@ serde = { version = "1.0", optional = true } [dev-dependencies] serde_json = "1.0" -ahash = { version = "0.7", default-features = false } +# TODO: remove when ahash MSRV is restored +ahash = { version = "=0.7.4", default-features = false } diff --git a/local-channel/Cargo.toml b/local-channel/Cargo.toml index 0ffd3597..1c20b941 100644 --- a/local-channel/Cargo.toml +++ b/local-channel/Cargo.toml @@ -18,4 +18,4 @@ futures-util = { version = "0.3.7", default-features = false } local-waker = "0.1" [dev-dependencies] -tokio = { version = "1", features = ["rt", "macros"] } +tokio = { version = "1.5.1", features = ["rt", "macros"] }