Skip to content

Commit 1152536

Browse files
bors[bot]taiki-e
andauthored
Merge #913
913: Remove dependency on once_cell and work around windows-gnu LTO issue r=taiki-e a=taiki-e Replace `once_cell` with our own `OnceLock` that based on unstable `std::sync::OnceLock` due to MSRV issue[^1]. This also fixes #856 due to windows-gnu LTO issue. [^1]: [The current readme says crossbeam support at least 6 months old compilers](https://github.com/crossbeam-rs/crossbeam/blob/e54f01e7a7e3913407e817ce3e578a76b6392ef1/README.md#compatibility), but that is a minimum guarantee and is [actually more conservative](#877 (comment)). Co-authored-by: Taiki Endo <[email protected]>
2 parents e54f01e + ace7e0a commit 1152536

File tree

10 files changed

+147
-38
lines changed

10 files changed

+147
-38
lines changed

.github/workflows/ci.yml

-10
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,6 @@ jobs:
6464
- name: Install cross
6565
uses: taiki-e/install-action@cross
6666
if: matrix.target != ''
67-
# TODO: remove dependency on once_cell or bump MSRV
68-
- name: Downgrade dependencies on MSRV
69-
run: |
70-
cargo update -p once_cell --precise 1.14.0
71-
if: matrix.rust == '1.38'
7267
- name: Test
7368
run: ./ci/test.sh
7469

@@ -90,11 +85,6 @@ jobs:
9085
toolchain: ${{ matrix.rust }}
9186
- name: Install cargo-hack
9287
uses: taiki-e/install-action@cargo-hack
93-
# TODO: remove dependency on once_cell or bump MSRV
94-
- name: Downgrade dependencies on MSRV
95-
run: |
96-
cargo update -p once_cell --precise 1.14.0
97-
if: matrix.rust == '1.38'
9888
- name: Check features
9989
run: ./ci/check-features.sh
10090

crossbeam-epoch/Cargo.toml

+1-2
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ default = ["std"]
1919

2020
# Enable to use APIs that require `std`.
2121
# This is enabled by default.
22-
std = ["alloc", "crossbeam-utils/std", "once_cell"]
22+
std = ["alloc", "crossbeam-utils/std"]
2323

2424
# Enable to use APIs that require `alloc`.
2525
# This is enabled by default and also enabled if the `std` feature is enabled.
@@ -39,7 +39,6 @@ autocfg = "1"
3939
[dependencies]
4040
cfg-if = "1"
4141
memoffset = "0.6"
42-
once_cell = { version = "1", optional = true }
4342
scopeguard = { version = "1.1", default-features = false }
4443

4544
# Enable the use of loom for concurrency testing.

crossbeam-epoch/src/default.rs

+21-13
Original file line numberDiff line numberDiff line change
@@ -8,22 +8,30 @@ use crate::collector::{Collector, LocalHandle};
88
use crate::guard::Guard;
99
use crate::primitive::thread_local;
1010
#[cfg(not(crossbeam_loom))]
11-
use once_cell::sync::Lazy;
11+
use crate::sync::once_lock::OnceLock;
1212

13-
/// The global data for the default garbage collector.
14-
#[cfg(not(crossbeam_loom))]
15-
static COLLECTOR: Lazy<Collector> = Lazy::new(Collector::new);
16-
// FIXME: loom does not currently provide the equivalent of Lazy:
17-
// https://github.com/tokio-rs/loom/issues/263
18-
#[cfg(crossbeam_loom)]
19-
loom::lazy_static! {
20-
/// The global data for the default garbage collector.
21-
static ref COLLECTOR: Collector = Collector::new();
13+
fn collector() -> &'static Collector {
14+
#[cfg(not(crossbeam_loom))]
15+
{
16+
/// The global data for the default garbage collector.
17+
static COLLECTOR: OnceLock<Collector> = OnceLock::new();
18+
COLLECTOR.get_or_init(Collector::new)
19+
}
20+
// FIXME: loom does not currently provide the equivalent of Lazy:
21+
// https://github.com/tokio-rs/loom/issues/263
22+
#[cfg(crossbeam_loom)]
23+
{
24+
loom::lazy_static! {
25+
/// The global data for the default garbage collector.
26+
static ref COLLECTOR: Collector = Collector::new();
27+
}
28+
&COLLECTOR
29+
}
2230
}
2331

2432
thread_local! {
2533
/// The per-thread participant for the default garbage collector.
26-
static HANDLE: LocalHandle = COLLECTOR.register();
34+
static HANDLE: LocalHandle = collector().register();
2735
}
2836

2937
/// Pins the current thread.
@@ -40,7 +48,7 @@ pub fn is_pinned() -> bool {
4048

4149
/// Returns the default global collector.
4250
pub fn default_collector() -> &'static Collector {
43-
&COLLECTOR
51+
collector()
4452
}
4553

4654
#[inline]
@@ -50,7 +58,7 @@ where
5058
{
5159
HANDLE
5260
.try_with(|h| f(h))
53-
.unwrap_or_else(|_| f(&COLLECTOR.register()))
61+
.unwrap_or_else(|_| f(&collector().register()))
5462
}
5563

5664
#[cfg(all(test, not(crossbeam_loom)))]

crossbeam-epoch/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ mod primitive {
108108
// https://github.com/tokio-rs/loom#handling-loom-api-differences
109109
impl<T> UnsafeCell<T> {
110110
#[inline]
111-
pub(crate) fn new(data: T) -> UnsafeCell<T> {
111+
pub(crate) const fn new(data: T) -> UnsafeCell<T> {
112112
UnsafeCell(::core::cell::UnsafeCell::new(data))
113113
}
114114

crossbeam-epoch/src/sync/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
//! Synchronization primitives.
22
33
pub(crate) mod list;
4+
#[cfg(feature = "std")]
5+
#[cfg(not(crossbeam_loom))]
6+
pub(crate) mod once_lock;
47
pub(crate) mod queue;

crossbeam-epoch/src/sync/once_lock.rs

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../../crossbeam-utils/src/sync/once_lock.rs

crossbeam-utils/Cargo.toml

+1-2
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,10 @@ default = ["std"]
1919

2020
# Enable to use APIs that require `std`.
2121
# This is enabled by default.
22-
std = ["once_cell"]
22+
std = []
2323

2424
[dependencies]
2525
cfg-if = "1"
26-
once_cell = { version = "1", optional = true }
2726

2827
# Enable the use of loom for concurrency testing.
2928
#

crossbeam-utils/src/sync/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
//! * [`ShardedLock`], a sharded reader-writer lock with fast concurrent reads.
55
//! * [`WaitGroup`], for synchronizing the beginning or end of some computation.
66
7+
#[cfg(not(crossbeam_loom))]
8+
mod once_lock;
79
mod parker;
810
#[cfg(not(crossbeam_loom))]
911
mod sharded_lock;

crossbeam-utils/src/sync/once_lock.rs

+103
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
// Based on unstable std::sync::OnceLock.
2+
//
3+
// Source: https://github.com/rust-lang/rust/blob/8e9c93df464b7ada3fc7a1c8ccddd9dcb24ee0a0/library/std/src/sync/once_lock.rs
4+
5+
use core::cell::UnsafeCell;
6+
use core::mem::MaybeUninit;
7+
use core::sync::atomic::{AtomicBool, Ordering};
8+
use std::sync::Once;
9+
10+
pub(crate) struct OnceLock<T> {
11+
once: Once,
12+
// Once::is_completed requires Rust 1.43, so use this to track of whether they have been initialized.
13+
is_initialized: AtomicBool,
14+
value: UnsafeCell<MaybeUninit<T>>,
15+
// Unlike std::sync::OnceLock, we don't need PhantomData here because
16+
// we don't use #[may_dangle].
17+
}
18+
19+
unsafe impl<T: Sync + Send> Sync for OnceLock<T> {}
20+
unsafe impl<T: Send> Send for OnceLock<T> {}
21+
22+
impl<T> OnceLock<T> {
23+
/// Creates a new empty cell.
24+
#[must_use]
25+
pub(crate) const fn new() -> Self {
26+
Self {
27+
once: Once::new(),
28+
is_initialized: AtomicBool::new(false),
29+
value: UnsafeCell::new(MaybeUninit::uninit()),
30+
}
31+
}
32+
33+
/// Gets the contents of the cell, initializing it with `f` if the cell
34+
/// was empty.
35+
///
36+
/// Many threads may call `get_or_init` concurrently with different
37+
/// initializing functions, but it is guaranteed that only one function
38+
/// will be executed.
39+
///
40+
/// # Panics
41+
///
42+
/// If `f` panics, the panic is propagated to the caller, and the cell
43+
/// remains uninitialized.
44+
///
45+
/// It is an error to reentrantly initialize the cell from `f`. The
46+
/// exact outcome is unspecified. Current implementation deadlocks, but
47+
/// this may be changed to a panic in the future.
48+
pub(crate) fn get_or_init<F>(&self, f: F) -> &T
49+
where
50+
F: FnOnce() -> T,
51+
{
52+
// Fast path check
53+
if self.is_initialized() {
54+
// SAFETY: The inner value has been initialized
55+
return unsafe { self.get_unchecked() };
56+
}
57+
self.initialize(f);
58+
59+
debug_assert!(self.is_initialized());
60+
61+
// SAFETY: The inner value has been initialized
62+
unsafe { self.get_unchecked() }
63+
}
64+
65+
#[inline]
66+
fn is_initialized(&self) -> bool {
67+
self.is_initialized.load(Ordering::Acquire)
68+
}
69+
70+
#[cold]
71+
fn initialize<F>(&self, f: F)
72+
where
73+
F: FnOnce() -> T,
74+
{
75+
let slot = self.value.get().cast::<T>();
76+
let is_initialized = &self.is_initialized;
77+
78+
self.once.call_once(|| {
79+
let value = f();
80+
unsafe {
81+
slot.write(value);
82+
}
83+
is_initialized.store(true, Ordering::Release);
84+
});
85+
}
86+
87+
/// # Safety
88+
///
89+
/// The value must be initialized
90+
unsafe fn get_unchecked(&self) -> &T {
91+
debug_assert!(self.is_initialized());
92+
&*self.value.get().cast::<T>()
93+
}
94+
}
95+
96+
impl<T> Drop for OnceLock<T> {
97+
fn drop(&mut self) {
98+
if self.is_initialized() {
99+
// SAFETY: The inner value has been initialized
100+
unsafe { self.value.get().cast::<T>().drop_in_place() };
101+
}
102+
}
103+
}

crossbeam-utils/src/sync/sharded_lock.rs

+14-10
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ use std::sync::{LockResult, PoisonError, TryLockError, TryLockResult};
99
use std::sync::{Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard};
1010
use std::thread::{self, ThreadId};
1111

12+
use crate::sync::once_lock::OnceLock;
1213
use crate::CachePadded;
13-
use once_cell::sync::Lazy;
1414

1515
/// The number of shards per sharded lock. Must be a power of two.
1616
const NUM_SHARDS: usize = 8;
@@ -583,13 +583,17 @@ struct ThreadIndices {
583583
next_index: usize,
584584
}
585585

586-
static THREAD_INDICES: Lazy<Mutex<ThreadIndices>> = Lazy::new(|| {
587-
Mutex::new(ThreadIndices {
588-
mapping: HashMap::new(),
589-
free_list: Vec::new(),
590-
next_index: 0,
591-
})
592-
});
586+
fn thread_indices() -> &'static Mutex<ThreadIndices> {
587+
static THREAD_INDICES: OnceLock<Mutex<ThreadIndices>> = OnceLock::new();
588+
fn init() -> Mutex<ThreadIndices> {
589+
Mutex::new(ThreadIndices {
590+
mapping: HashMap::new(),
591+
free_list: Vec::new(),
592+
next_index: 0,
593+
})
594+
}
595+
THREAD_INDICES.get_or_init(init)
596+
}
593597

594598
/// A registration of a thread with an index.
595599
///
@@ -601,7 +605,7 @@ struct Registration {
601605

602606
impl Drop for Registration {
603607
fn drop(&mut self) {
604-
let mut indices = THREAD_INDICES.lock().unwrap();
608+
let mut indices = thread_indices().lock().unwrap();
605609
indices.mapping.remove(&self.thread_id);
606610
indices.free_list.push(self.index);
607611
}
@@ -610,7 +614,7 @@ impl Drop for Registration {
610614
thread_local! {
611615
static REGISTRATION: Registration = {
612616
let thread_id = thread::current().id();
613-
let mut indices = THREAD_INDICES.lock().unwrap();
617+
let mut indices = thread_indices().lock().unwrap();
614618

615619
let index = match indices.free_list.pop() {
616620
Some(i) => i,

0 commit comments

Comments
 (0)