From 05e661490b87a3d60a8342535bdc9d213048519c Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Mon, 26 Sep 2022 10:41:35 +0200 Subject: [PATCH] chore: don't use `once_cell` for 1.18.x LTS release (#5048) The latest release of once_cell has an MSRV that is incompatible with the MSRV of the 1.18.x LTS release. --- .circleci/config.yml | 6 +-- .cirrus.yml | 2 +- .github/workflows/ci.yml | 9 ++-- .github/workflows/loom.yml | 2 +- .github/workflows/stress-test.yml | 2 +- examples/Cargo.toml | 6 --- tokio/Cargo.toml | 5 +-- tokio/src/loom/std/parking_lot.rs | 2 +- tokio/src/process/unix/mod.rs | 14 ++++--- tokio/src/runtime/task/mod.rs | 12 ++++-- tokio/src/signal/registry.rs | 25 ++++++----- tokio/src/util/mod.rs | 9 ++++ tokio/src/util/once_cell.rs | 70 +++++++++++++++++++++++++++++++ 13 files changed, 125 insertions(+), 39 deletions(-) create mode 100644 tokio/src/util/once_cell.rs diff --git a/.circleci/config.yml b/.circleci/config.yml index 7e5b83fbdb9..4ec482174b6 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -5,8 +5,8 @@ jobs: image: ubuntu-2004:202101-01 resource_class: arm.medium environment: - # Change to pin rust versino - RUST_STABLE: stable + # Change to pin rust version + RUST_STABLE: 1.60.0 steps: - checkout - run: @@ -22,4 +22,4 @@ jobs: workflows: ci: jobs: - - test-arm \ No newline at end of file + - test-arm diff --git a/.cirrus.yml b/.cirrus.yml index 1adc492ad6c..6dcd8b19226 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -1,7 +1,7 @@ freebsd_instance: image: freebsd-12-3-release-amd64 env: - RUST_STABLE: stable + RUST_STABLE: 1.60.0 RUST_NIGHTLY: nightly-2022-03-21 RUSTFLAGS: -D warnings diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e344f1c1fa6..8b67a0dd17d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,9 +10,9 @@ env: RUSTFLAGS: -Dwarnings RUST_BACKTRACE: 1 # Change to specific Rust release to pin - rust_stable: stable + rust_stable: 1.60.0 rust_nightly: nightly-2022-03-21 - rust_clippy: 1.52.0 + rust_clippy: 1.56.0 rust_min: 1.49.0 defaults: @@ -284,8 +284,9 @@ jobs: toolchain: ${{ env.rust_min }} override: true - uses: Swatinem/rust-cache@v1 - - name: "test --workspace --all-features" - run: cargo check --workspace --all-features + - name: "test --all-features" + run: cargo check --all-features + working-directory: tokio minimal-versions: name: minimal-versions diff --git a/.github/workflows/loom.yml b/.github/workflows/loom.yml index 83a6743cb17..29ed8c17994 100644 --- a/.github/workflows/loom.yml +++ b/.github/workflows/loom.yml @@ -11,7 +11,7 @@ env: RUSTFLAGS: -Dwarnings RUST_BACKTRACE: 1 # Change to specific Rust release to pin - rust_stable: stable + rust_stable: 1.60.0 jobs: loom: diff --git a/.github/workflows/stress-test.yml b/.github/workflows/stress-test.yml index a6a24fdea14..f48c7a01996 100644 --- a/.github/workflows/stress-test.yml +++ b/.github/workflows/stress-test.yml @@ -9,7 +9,7 @@ env: RUSTFLAGS: -Dwarnings RUST_BACKTRACE: 1 # Change to specific Rust release to pin - rust_stable: stable + rust_stable: 1.60.0 jobs: stess-test: diff --git a/examples/Cargo.toml b/examples/Cargo.toml index d2aca69d84a..a6f6eda3267 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -21,7 +21,6 @@ serde_derive = "1.0" serde_json = "1.0" httparse = "1.0" httpdate = "1.0" -once_cell = "1.5.2" rand = "0.8.3" [target.'cfg(windows)'.dev-dependencies.winapi] @@ -71,11 +70,6 @@ path = "udp-codec.rs" name = "tinyhttp" path = "tinyhttp.rs" -[[example]] -name = "custom-executor" -path = "custom-executor.rs" - - [[example]] name = "custom-executor-tokio-context" path = "custom-executor-tokio-context.rs" diff --git a/tokio/Cargo.toml b/tokio/Cargo.toml index cc25ae1054e..305af9c2397 100644 --- a/tokio/Cargo.toml +++ b/tokio/Cargo.toml @@ -56,7 +56,6 @@ net = [ ] process = [ "bytes", - "once_cell", "libc", "mio/os-poll", "mio/os-ext", @@ -65,13 +64,12 @@ process = [ "winapi/threadpoollegacyapiset", ] # Includes basic task execution capabilities -rt = ["once_cell"] +rt = [] rt-multi-thread = [ "num_cpus", "rt", ] signal = [ - "once_cell", "libc", "mio/os-poll", "mio/net", @@ -95,7 +93,6 @@ pin-project-lite = "0.2.0" # Everything else is optional... bytes = { version = "1.0.0", optional = true } -once_cell = { version = "1.5.2", optional = true } memchr = { version = "2.2", optional = true } mio = { version = "0.8.1", optional = true } socket2 = { version = "0.4.4", optional = true, features = [ "all" ] } diff --git a/tokio/src/loom/std/parking_lot.rs b/tokio/src/loom/std/parking_lot.rs index 034a0ce69a5..e3af258d116 100644 --- a/tokio/src/loom/std/parking_lot.rs +++ b/tokio/src/loom/std/parking_lot.rs @@ -52,7 +52,7 @@ impl Mutex { } #[inline] - #[cfg(all(feature = "parking_lot", not(all(loom, test)),))] + #[cfg(all(feature = "parking_lot", not(all(loom, test))))] #[cfg_attr(docsrs, doc(cfg(all(feature = "parking_lot",))))] pub(crate) const fn const_new(t: T) -> Mutex { Mutex(PhantomData, parking_lot::const_mutex(t)) diff --git a/tokio/src/process/unix/mod.rs b/tokio/src/process/unix/mod.rs index 576fe6cb47f..0bc4e78503f 100644 --- a/tokio/src/process/unix/mod.rs +++ b/tokio/src/process/unix/mod.rs @@ -34,10 +34,10 @@ use crate::process::kill::Kill; use crate::process::SpawnedChild; use crate::signal::unix::driver::Handle as SignalHandle; use crate::signal::unix::{signal, Signal, SignalKind}; +use crate::util::once_cell::OnceCell; use mio::event::Source; use mio::unix::SourceFd; -use once_cell::sync::Lazy; use std::fmt; use std::fs::File; use std::future::Future; @@ -64,25 +64,29 @@ impl Kill for StdChild { } } -static ORPHAN_QUEUE: Lazy> = Lazy::new(OrphanQueueImpl::new); +fn get_orphan_queue() -> &'static OrphanQueueImpl { + static ORPHAN_QUEUE: OnceCell> = OnceCell::new(); + + ORPHAN_QUEUE.get(OrphanQueueImpl::new) +} pub(crate) struct GlobalOrphanQueue; impl fmt::Debug for GlobalOrphanQueue { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - ORPHAN_QUEUE.fmt(fmt) + get_orphan_queue().fmt(fmt) } } impl GlobalOrphanQueue { fn reap_orphans(handle: &SignalHandle) { - ORPHAN_QUEUE.reap_orphans(handle) + get_orphan_queue().reap_orphans(handle) } } impl OrphanQueue for GlobalOrphanQueue { fn push_orphan(&self, orphan: StdChild) { - ORPHAN_QUEUE.push_orphan(orphan) + get_orphan_queue().push_orphan(orphan) } } diff --git a/tokio/src/runtime/task/mod.rs b/tokio/src/runtime/task/mod.rs index 316b4a49675..37909b75c6d 100644 --- a/tokio/src/runtime/task/mod.rs +++ b/tokio/src/runtime/task/mod.rs @@ -500,11 +500,17 @@ impl Id { cfg_not_has_atomic_u64! { pub(crate) fn next() -> Self { - use once_cell::sync::Lazy; + use crate::util::once_cell::OnceCell; use crate::loom::sync::Mutex; - static NEXT_ID: Lazy> = Lazy::new(|| Mutex::new(1)); - let mut lock = NEXT_ID.lock(); + fn init_next_id() -> Mutex { + Mutex::new(1) + } + + static NEXT_ID: OnceCell> = OnceCell::new(); + + let next_id = NEXT_ID.get(init_next_id); + let mut lock = next_id.lock(); let id = *lock; *lock += 1; Self(id) diff --git a/tokio/src/signal/registry.rs b/tokio/src/signal/registry.rs index 7795ca8dfa8..e1b3d108767 100644 --- a/tokio/src/signal/registry.rs +++ b/tokio/src/signal/registry.rs @@ -1,10 +1,9 @@ #![allow(clippy::unit_arg)] use crate::signal::os::{OsExtraData, OsStorage}; - use crate::sync::watch; +use crate::util::once_cell::OnceCell; -use once_cell::sync::Lazy; use std::ops; use std::pin::Pin; use std::sync::atomic::{AtomicBool, Ordering}; @@ -152,19 +151,25 @@ impl Globals { } } +fn globals_init() -> Globals +where + OsExtraData: 'static + Send + Sync + Init, + OsStorage: 'static + Send + Sync + Init, +{ + Globals { + extra: OsExtraData::init(), + registry: Registry::new(OsStorage::init()), + } +} + pub(crate) fn globals() -> Pin<&'static Globals> where OsExtraData: 'static + Send + Sync + Init, OsStorage: 'static + Send + Sync + Init, { - static GLOBALS: Lazy>> = Lazy::new(|| { - Box::pin(Globals { - extra: OsExtraData::init(), - registry: Registry::new(OsStorage::init()), - }) - }); - - GLOBALS.as_ref() + static GLOBALS: OnceCell = OnceCell::new(); + + Pin::new(GLOBALS.get(globals_init)) } #[cfg(all(test, not(loom)))] diff --git a/tokio/src/util/mod.rs b/tokio/src/util/mod.rs index 618f5543802..ef0fdce6261 100644 --- a/tokio/src/util/mod.rs +++ b/tokio/src/util/mod.rs @@ -6,6 +6,15 @@ cfg_io_driver! { #[cfg(feature = "rt")] pub(crate) mod atomic_cell; +cfg_has_atomic_u64! { + #[cfg(any(feature = "signal", feature = "process"))] + pub(crate) mod once_cell; +} +cfg_not_has_atomic_u64! { + #[cfg(any(feature = "rt", feature = "signal", feature = "process"))] + pub(crate) mod once_cell; +} + #[cfg(any( // io driver uses `WakeList` directly feature = "net", diff --git a/tokio/src/util/once_cell.rs b/tokio/src/util/once_cell.rs new file mode 100644 index 00000000000..15639e6307f --- /dev/null +++ b/tokio/src/util/once_cell.rs @@ -0,0 +1,70 @@ +#![cfg_attr(loom, allow(dead_code))] +use std::cell::UnsafeCell; +use std::mem::MaybeUninit; +use std::sync::Once; + +pub(crate) struct OnceCell { + once: Once, + value: UnsafeCell>, +} + +unsafe impl Send for OnceCell {} +unsafe impl Sync for OnceCell {} + +impl OnceCell { + pub(crate) const fn new() -> Self { + Self { + once: Once::new(), + value: UnsafeCell::new(MaybeUninit::uninit()), + } + } + + /// Get the value inside this cell, intiailizing it using the provided + /// function if necessary. + /// + /// If the `init` closure panics, then the `OnceCell` is poisoned and all + /// future calls to `get` will panic. + #[inline] + pub(crate) fn get(&self, init: fn() -> T) -> &T { + if !self.once.is_completed() { + self.do_init(init); + } + + // Safety: The `std::sync::Once` guarantees that we can only reach this + // line if a `call_once` closure has been run exactly once and without + // panicking. Thus, the value is not uninitialized. + // + // There is also no race because the only `&self` method that modifies + // `value` is `do_init`, but if the `call_once` closure is still + // running, then no thread has gotten past the `call_once`. + unsafe { &*(self.value.get() as *const T) } + } + + #[cold] + fn do_init(&self, init: fn() -> T) { + let value_ptr = self.value.get() as *mut T; + + self.once.call_once(|| { + let set_to = init(); + + // Safety: The `std::sync::Once` guarantees that this initialization + // will run at most once, and that no thread can get past the + // `call_once` until it has run exactly once. Thus, we have + // exclusive access to `value`. + unsafe { + std::ptr::write(value_ptr, set_to); + } + }); + } +} + +impl Drop for OnceCell { + fn drop(&mut self) { + if self.once.is_completed() { + let value_ptr = self.value.get() as *mut T; + unsafe { + std::ptr::drop_in_place(value_ptr); + } + } + } +}