Skip to main content

rustc_data_structures/
sync.rs

1//! This module defines various operations and types that are implemented in
2//! one way for the serial compiler, and another way the parallel compiler.
3//!
4//! Operations
5//! ----------
6//! The parallel versions of operations use Rayon to execute code in parallel,
7//! while the serial versions degenerate straightforwardly to serial execution.
8//! The operations include `join`, `parallel`, `par_iter`, and `par_for_each`.
9//!
10//! Types
11//! -----
12//! The parallel versions of types provide various kinds of synchronization,
13//! while the serial compiler versions do not.
14//!
15//! The following table shows how the types are implemented internally. Except
16//! where noted otherwise, the type in column one is defined as a
17//! newtype around the type from column two or three.
18//!
19//! | Type                    | Serial version      | Parallel version                |
20//! | ----------------------- | ------------------- | ------------------------------- |
21//! | `Lock<T>`               | `RefCell<T>`        | `RefCell<T>` or                 |
22//! |                         |                     | `parking_lot::Mutex<T>`         |
23//! | `RwLock<T>`             | `RefCell<T>`        | `parking_lot::RwLock<T>`        |
24
25use std::collections::HashMap;
26use std::hash::{BuildHasher, Hash};
27
28pub use parking_lot::{
29    MappedRwLockReadGuard as MappedReadGuard, MappedRwLockWriteGuard as MappedWriteGuard,
30    RwLockReadGuard as ReadGuard, RwLockWriteGuard as WriteGuard,
31};
32
33pub use self::atomic::AtomicU64;
34pub use self::freeze::{FreezeLock, FreezeReadGuard, FreezeWriteGuard};
35#[doc(no_inline)]
36pub use self::lock::{Lock, LockGuard, Mode};
37pub use self::mode::{
38    FromDyn, check_dyn_thread_safe, is_dyn_thread_safe, set_dyn_thread_safe_mode,
39};
40pub use self::parallel::{
41    broadcast, par_fns, par_for_each_in, par_join, par_map, parallel_guard, spawn,
42    try_par_for_each_in,
43};
44pub use self::vec::{AppendOnlyIndexVec, AppendOnlyVec};
45pub use self::worker_local::{Registry, WorkerLocal};
46pub use crate::marker::*;
47
48mod freeze;
49mod lock;
50mod parallel;
51mod vec;
52mod worker_local;
53
54/// Keep the conditional imports together in a submodule, so that import-sorting
55/// doesn't split them up.
56mod atomic {
57    // Most hosts can just use a regular AtomicU64.
58    #[cfg(target_has_atomic = "64")]
59    pub use std::sync::atomic::AtomicU64;
60
61    // Some 32-bit hosts don't have AtomicU64, so use a fallback.
62    #[cfg(not(target_has_atomic = "64"))]
63    pub use portable_atomic::AtomicU64;
64}
65
66mod mode {
67    use std::sync::atomic::{AtomicU8, Ordering};
68
69    use crate::sync::{DynSend, DynSync};
70
71    const UNINITIALIZED: u8 = 0;
72    const DYN_NOT_THREAD_SAFE: u8 = 1;
73    const DYN_THREAD_SAFE: u8 = 2;
74
75    static DYN_THREAD_SAFE_MODE: AtomicU8 = AtomicU8::new(UNINITIALIZED);
76
77    // Whether thread safety is enabled (due to running under multiple threads).
78    #[inline]
79    pub fn check_dyn_thread_safe() -> Option<FromDyn<()>> {
80        is_dyn_thread_safe().then_some(FromDyn(()))
81    }
82
83    // Whether thread safety is enabled (due to running under multiple threads).
84    #[inline]
85    pub fn is_dyn_thread_safe() -> bool {
86        match DYN_THREAD_SAFE_MODE.load(Ordering::Relaxed) {
87            DYN_NOT_THREAD_SAFE => false,
88            DYN_THREAD_SAFE => true,
89            _ => {
    ::core::panicking::panic_fmt(format_args!("uninitialized dyn_thread_safe mode!"));
}panic!("uninitialized dyn_thread_safe mode!"),
90        }
91    }
92
93    // Whether thread safety might be enabled.
94    #[inline]
95    pub(super) fn might_be_dyn_thread_safe() -> bool {
96        DYN_THREAD_SAFE_MODE.load(Ordering::Relaxed) != DYN_NOT_THREAD_SAFE
97    }
98
99    // Only set by the `-Z threads` compile option
100    pub fn set_dyn_thread_safe_mode(mode: bool) {
101        let set: u8 = if mode { DYN_THREAD_SAFE } else { DYN_NOT_THREAD_SAFE };
102        let previous = DYN_THREAD_SAFE_MODE.compare_exchange(
103            UNINITIALIZED,
104            set,
105            Ordering::Relaxed,
106            Ordering::Relaxed,
107        );
108
109        // Check that the mode was either uninitialized or was already set to the requested mode.
110        if !(previous.is_ok() || previous == Err(set)) {
    ::core::panicking::panic("assertion failed: previous.is_ok() || previous == Err(set)")
};assert!(previous.is_ok() || previous == Err(set));
111    }
112
113    #[derive(#[automatically_derived]
impl<T: ::core::marker::Copy> ::core::marker::Copy for FromDyn<T> { }Copy, #[automatically_derived]
impl<T: ::core::clone::Clone> ::core::clone::Clone for FromDyn<T> {
    #[inline]
    fn clone(&self) -> FromDyn<T> {
        FromDyn(::core::clone::Clone::clone(&self.0))
    }
}Clone)]
114    pub struct FromDyn<T>(T);
115
116    impl<T> FromDyn<T> {
117        #[inline(always)]
118        pub fn derive<O>(&self, val: O) -> FromDyn<O> {
119            // We already did the check for `sync::is_dyn_thread_safe()` when creating `Self`
120            FromDyn(val)
121        }
122
123        #[inline(always)]
124        pub fn into_inner(self) -> T {
125            self.0
126        }
127    }
128
129    // `FromDyn` is `Send` if `T` is `DynSend`, since it ensures that sync::is_dyn_thread_safe() is true.
130    unsafe impl<T: DynSend> Send for FromDyn<T> {}
131
132    // `FromDyn` is `Sync` if `T` is `DynSync`, since it ensures that sync::is_dyn_thread_safe() is true.
133    unsafe impl<T: DynSync> Sync for FromDyn<T> {}
134
135    impl<T> std::ops::Deref for FromDyn<T> {
136        type Target = T;
137
138        #[inline(always)]
139        fn deref(&self) -> &Self::Target {
140            &self.0
141        }
142    }
143
144    impl<T> std::ops::DerefMut for FromDyn<T> {
145        #[inline(always)]
146        fn deref_mut(&mut self) -> &mut Self::Target {
147            &mut self.0
148        }
149    }
150}
151
152/// This makes locks panic if they are already held.
153/// It is only useful when you are running in a single thread
154const ERROR_CHECKING: bool = false;
155
156#[derive(#[automatically_derived]
impl<T: ::core::default::Default> ::core::default::Default for CacheAligned<T>
    {
    #[inline]
    fn default() -> CacheAligned<T> {
        CacheAligned(::core::default::Default::default())
    }
}Default)]
157#[repr(align(64))]
158pub struct CacheAligned<T>(pub T);
159
160pub trait HashMapExt<K, V> {
161    /// Same as HashMap::insert, but it may panic if there's already an
162    /// entry for `key` with a value not equal to `value`
163    fn insert_same(&mut self, key: K, value: V);
164}
165
166impl<K: Eq + Hash, V: Eq, S: BuildHasher> HashMapExt<K, V> for HashMap<K, V, S> {
167    fn insert_same(&mut self, key: K, value: V) {
168        self.entry(key).and_modify(|old| if !(*old == value) {
    ::core::panicking::panic("assertion failed: *old == value")
}assert!(*old == value)).or_insert(value);
169    }
170}
171
172#[derive(#[automatically_derived]
impl<T: ::core::fmt::Debug> ::core::fmt::Debug for RwLock<T> {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_tuple_field1_finish(f, "RwLock",
            &&self.0)
    }
}Debug, #[automatically_derived]
impl<T: ::core::default::Default> ::core::default::Default for RwLock<T> {
    #[inline]
    fn default() -> RwLock<T> { RwLock(::core::default::Default::default()) }
}Default)]
173pub struct RwLock<T>(parking_lot::RwLock<T>);
174
175impl<T> RwLock<T> {
176    #[inline(always)]
177    pub fn new(inner: T) -> Self {
178        RwLock(parking_lot::RwLock::new(inner))
179    }
180
181    #[inline(always)]
182    pub fn into_inner(self) -> T {
183        self.0.into_inner()
184    }
185
186    #[inline(always)]
187    pub fn get_mut(&mut self) -> &mut T {
188        self.0.get_mut()
189    }
190
191    #[inline(always)]
192    pub fn read(&self) -> ReadGuard<'_, T> {
193        if ERROR_CHECKING {
194            self.0.try_read().expect("lock was already held")
195        } else {
196            self.0.read()
197        }
198    }
199
200    #[inline(always)]
201    pub fn try_write(&self) -> Result<WriteGuard<'_, T>, ()> {
202        self.0.try_write().ok_or(())
203    }
204
205    #[inline(always)]
206    pub fn write(&self) -> WriteGuard<'_, T> {
207        if ERROR_CHECKING {
208            self.0.try_write().expect("lock was already held")
209        } else {
210            self.0.write()
211        }
212    }
213
214    #[inline(always)]
215    #[track_caller]
216    pub fn borrow(&self) -> ReadGuard<'_, T> {
217        self.read()
218    }
219
220    #[inline(always)]
221    #[track_caller]
222    pub fn borrow_mut(&self) -> WriteGuard<'_, T> {
223        self.write()
224    }
225}