Skip to content

Commit

Permalink
chore: don't use once_cell for 1.18.x LTS release (tokio-rs#5048)
Browse files Browse the repository at this point in the history
The latest release of once_cell has an MSRV that is incompatible with the MSRV of the 1.18.x LTS release.
  • Loading branch information
Darksonn authored and dbischof90 committed Oct 1, 2022
1 parent de9de94 commit b78e0ea
Show file tree
Hide file tree
Showing 13 changed files with 125 additions and 47 deletions.
4 changes: 2 additions & 2 deletions .circleci/config.yml
Expand Up @@ -6,7 +6,7 @@ jobs:
resource_class: arm.medium
environment:
# Change to pin rust version
RUST_STABLE: stable
RUST_STABLE: 1.60.0
steps:
- checkout
- run:
Expand All @@ -22,4 +22,4 @@ jobs:
workflows:
ci:
jobs:
- test-arm
- test-arm
2 changes: 1 addition & 1 deletion .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

Expand Down
19 changes: 6 additions & 13 deletions .github/workflows/ci.yml
Expand Up @@ -10,17 +10,9 @@ env:
RUSTFLAGS: -Dwarnings
RUST_BACKTRACE: 1
# Change to specific Rust release to pin
rust_stable: stable
rust_nightly: nightly-2022-07-26
rust_clippy: 1.52.0
# When updating this, also update:
# - README.md
# - tokio/README.md
# - CONTRIBUTING.md
# - tokio/Cargo.toml
# - tokio-util/Cargo.toml
# - tokio-test/Cargo.toml
# - tokio-stream/Cargo.toml
rust_stable: 1.60.0
rust_nightly: nightly-2022-03-21
rust_clippy: 1.56.0
rust_min: 1.49.0

defaults:
Expand Down Expand Up @@ -297,8 +289,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
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/loom.yml
Expand Up @@ -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:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/stress-test.yml
Expand Up @@ -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:
stress-test:
Expand Down
6 changes: 0 additions & 6 deletions examples/Cargo.toml
Expand Up @@ -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]
Expand Down Expand Up @@ -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"
Expand Down
5 changes: 1 addition & 4 deletions tokio/Cargo.toml
Expand Up @@ -63,7 +63,6 @@ net = [
]
process = [
"bytes",
"once_cell",
"libc",
"mio/os-poll",
"mio/os-ext",
Expand All @@ -77,13 +76,12 @@ process = [
"winapi/winnt",
]
# Includes basic task execution capabilities
rt = ["once_cell"]
rt = []
rt-multi-thread = [
"num_cpus",
"rt",
]
signal = [
"once_cell",
"libc",
"mio/os-poll",
"mio/net",
Expand Down Expand Up @@ -112,7 +110,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.4", optional = true }
num_cpus = { version = "1.8.0", optional = true }
Expand Down
2 changes: 1 addition & 1 deletion tokio/src/loom/std/parking_lot.rs
Expand Up @@ -52,7 +52,7 @@ impl<T> Mutex<T> {
}

#[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<T> {
Mutex(PhantomData, parking_lot::const_mutex(t))
Expand Down
14 changes: 9 additions & 5 deletions tokio/src/process/unix/mod.rs
Expand Up @@ -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;
Expand All @@ -64,25 +64,29 @@ impl Kill for StdChild {
}
}

static ORPHAN_QUEUE: Lazy<OrphanQueueImpl<StdChild>> = Lazy::new(OrphanQueueImpl::new);
fn get_orphan_queue() -> &'static OrphanQueueImpl<StdChild> {
static ORPHAN_QUEUE: OnceCell<OrphanQueueImpl<StdChild>> = 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<StdChild> for GlobalOrphanQueue {
fn push_orphan(&self, orphan: StdChild) {
ORPHAN_QUEUE.push_orphan(orphan)
get_orphan_queue().push_orphan(orphan)
}
}

Expand Down
12 changes: 9 additions & 3 deletions tokio/src/runtime/task/mod.rs
Expand Up @@ -508,11 +508,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<Mutex<u64>> = Lazy::new(|| Mutex::new(1));
let mut lock = NEXT_ID.lock();
fn init_next_id() -> Mutex<u64> {
Mutex::new(1)
}

static NEXT_ID: OnceCell<Mutex<u64>> = OnceCell::new();

let next_id = NEXT_ID.get(init_next_id);
let mut lock = next_id.lock();
let id = *lock;
*lock += 1;
Self(id)
Expand Down
25 changes: 15 additions & 10 deletions 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};
Expand Down Expand Up @@ -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<Pin<Box<Globals>>> = Lazy::new(|| {
Box::pin(Globals {
extra: OsExtraData::init(),
registry: Registry::new(OsStorage::init()),
})
});

GLOBALS.as_ref()
static GLOBALS: OnceCell<Globals> = OnceCell::new();

Pin::new(GLOBALS.get(globals_init))
}

#[cfg(all(test, not(loom)))]
Expand Down
9 changes: 9 additions & 0 deletions tokio/src/util/mod.rs
Expand Up @@ -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",
Expand Down
70 changes: 70 additions & 0 deletions 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<T> {
once: Once,
value: UnsafeCell<MaybeUninit<T>>,
}

unsafe impl<T: Send + Sync> Send for OnceCell<T> {}
unsafe impl<T: Send + Sync> Sync for OnceCell<T> {}

impl<T> OnceCell<T> {
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<T> Drop for OnceCell<T> {
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);
}
}
}
}

0 comments on commit b78e0ea

Please sign in to comment.