-
Notifications
You must be signed in to change notification settings - Fork 12.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Introduce DynSend
and DynSync
auto trait for parallel compiler
#107586
Changes from all commits
b9746ce
261b727
f196e27
9f8ab2a
089a388
bffccdd
d7e3e5b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,257 @@ | ||
cfg_if!( | ||
if #[cfg(not(parallel_compiler))] { | ||
pub auto trait DynSend {} | ||
pub auto trait DynSync {} | ||
|
||
impl<T> DynSend for T {} | ||
impl<T> DynSync for T {} | ||
} else { | ||
#[rustc_on_unimplemented( | ||
message = "`{Self}` doesn't implement `DynSend`. \ | ||
Add it to `rustc_data_structures::marker` or use `IntoDynSyncSend` if it's already `Send`" | ||
)] | ||
// This is an auto trait for types which can be sent across threads if `sync::is_dyn_thread_safe()` | ||
// is true. These types can be wrapped in a `FromDyn` to get a `Send` type. Wrapping a | ||
// `Send` type in `IntoDynSyncSend` will create a `DynSend` type. | ||
pub unsafe auto trait DynSend {} | ||
|
||
#[rustc_on_unimplemented( | ||
message = "`{Self}` doesn't implement `DynSync`. \ | ||
Add it to `rustc_data_structures::marker` or use `IntoDynSyncSend` if it's already `Sync`" | ||
)] | ||
// This is an auto trait for types which can be shared across threads if `sync::is_dyn_thread_safe()` | ||
// is true. These types can be wrapped in a `FromDyn` to get a `Sync` type. Wrapping a | ||
// `Sync` type in `IntoDynSyncSend` will create a `DynSync` type. | ||
pub unsafe auto trait DynSync {} | ||
|
||
// Same with `Sync` and `Send`. | ||
unsafe impl<T: DynSync + ?Sized> DynSend for &T {} | ||
|
||
macro_rules! impls_dyn_send_neg { | ||
($([$t1: ty $(where $($generics1: tt)*)?])*) => { | ||
$(impl$(<$($generics1)*>)? !DynSend for $t1 {})* | ||
}; | ||
} | ||
|
||
// Consistent with `std` | ||
impls_dyn_send_neg!( | ||
[std::env::Args] | ||
[std::env::ArgsOs] | ||
[*const T where T: ?Sized] | ||
[*mut T where T: ?Sized] | ||
[std::ptr::NonNull<T> where T: ?Sized] | ||
[std::rc::Rc<T> where T: ?Sized] | ||
[std::rc::Weak<T> where T: ?Sized] | ||
[std::sync::MutexGuard<'_, T> where T: ?Sized] | ||
[std::sync::RwLockReadGuard<'_, T> where T: ?Sized] | ||
[std::sync::RwLockWriteGuard<'_, T> where T: ?Sized] | ||
[std::io::StdoutLock<'_>] | ||
[std::io::StderrLock<'_>] | ||
); | ||
cfg_if!( | ||
// Consistent with `std` | ||
// `os_imp::Env` is `!Send` in these platforms | ||
if #[cfg(any(unix, target_os = "hermit", target_os = "wasi", target_os = "solid_asp3"))] { | ||
impl !DynSend for std::env::VarsOs {} | ||
} | ||
); | ||
|
||
macro_rules! already_send { | ||
($([$ty: ty])*) => { | ||
$(unsafe impl DynSend for $ty where $ty: Send {})* | ||
}; | ||
} | ||
|
||
// These structures are already `Send`. | ||
already_send!( | ||
[std::backtrace::Backtrace] | ||
[std::io::Stdout] | ||
[std::io::Stderr] | ||
[std::io::Error] | ||
[std::fs::File] | ||
[rustc_arena::DroplessArena] | ||
[crate::memmap::Mmap] | ||
[crate::profiling::SelfProfiler] | ||
[crate::owned_slice::OwnedSlice] | ||
); | ||
|
||
macro_rules! impl_dyn_send { | ||
($($($attr: meta)* [$ty: ty where $($generics2: tt)*])*) => { | ||
$(unsafe impl<$($generics2)*> DynSend for $ty {})* | ||
}; | ||
} | ||
|
||
impl_dyn_send!( | ||
[std::sync::atomic::AtomicPtr<T> where T] | ||
[std::sync::Mutex<T> where T: ?Sized+ DynSend] | ||
[std::sync::mpsc::Sender<T> where T: DynSend] | ||
[std::sync::Arc<T> where T: ?Sized + DynSync + DynSend] | ||
[std::sync::LazyLock<T, F> where T: DynSend, F: DynSend] | ||
[std::collections::HashSet<K, S> where K: DynSend, S: DynSend] | ||
[std::collections::HashMap<K, V, S> where K: DynSend, V: DynSend, S: DynSend] | ||
[std::collections::BTreeMap<K, V, A> where K: DynSend, V: DynSend, A: std::alloc::Allocator + Clone + DynSend] | ||
[Vec<T, A> where T: DynSend, A: std::alloc::Allocator + DynSend] | ||
[Box<T, A> where T: ?Sized + DynSend, A: std::alloc::Allocator + DynSend] | ||
[crate::sync::Lock<T> where T: DynSend] | ||
[crate::sync::RwLock<T> where T: DynSend] | ||
[crate::tagged_ptr::CopyTaggedPtr<P, T, CP> where P: Send + crate::tagged_ptr::Pointer, T: Send + crate::tagged_ptr::Tag, const CP: bool] | ||
[rustc_arena::TypedArena<T> where T: DynSend] | ||
[indexmap::IndexSet<V, S> where V: DynSend, S: DynSend] | ||
[indexmap::IndexMap<K, V, S> where K: DynSend, V: DynSend, S: DynSend] | ||
[thin_vec::ThinVec<T> where T: DynSend] | ||
[smallvec::SmallVec<A> where A: smallvec::Array + DynSend] | ||
); | ||
|
||
macro_rules! impls_dyn_sync_neg { | ||
($([$t1: ty $(where $($generics1: tt)*)?])*) => { | ||
$(impl$(<$($generics1)*>)? !DynSync for $t1 {})* | ||
}; | ||
} | ||
|
||
// Consistent with `std` | ||
impls_dyn_sync_neg!( | ||
[std::env::Args] | ||
[std::env::ArgsOs] | ||
[*const T where T: ?Sized] | ||
[*mut T where T: ?Sized] | ||
[std::cell::Cell<T> where T: ?Sized] | ||
[std::cell::RefCell<T> where T: ?Sized] | ||
[std::cell::UnsafeCell<T> where T: ?Sized] | ||
[std::ptr::NonNull<T> where T: ?Sized] | ||
[std::rc::Rc<T> where T: ?Sized] | ||
[std::rc::Weak<T> where T: ?Sized] | ||
[std::cell::OnceCell<T> where T] | ||
[std::sync::mpsc::Receiver<T> where T] | ||
[std::sync::mpsc::Sender<T> where T] | ||
); | ||
cfg_if!( | ||
// Consistent with `std` | ||
// `os_imp::Env` is `!Sync` in these platforms | ||
if #[cfg(any(unix, target_os = "hermit", target_os = "wasi", target_os = "solid_asp3"))] { | ||
impl !DynSync for std::env::VarsOs {} | ||
} | ||
); | ||
|
||
macro_rules! already_sync { | ||
($([$ty: ty])*) => { | ||
$(unsafe impl DynSync for $ty where $ty: Sync {})* | ||
}; | ||
} | ||
|
||
// These structures are already `Sync`. | ||
already_sync!( | ||
[std::sync::atomic::AtomicBool] | ||
[std::sync::atomic::AtomicUsize] | ||
[std::sync::atomic::AtomicU8] | ||
[std::sync::atomic::AtomicU32] | ||
[std::sync::atomic::AtomicU64] | ||
[std::backtrace::Backtrace] | ||
[std::io::Error] | ||
[std::fs::File] | ||
[jobserver_crate::Client] | ||
[crate::memmap::Mmap] | ||
[crate::profiling::SelfProfiler] | ||
[crate::owned_slice::OwnedSlice] | ||
); | ||
|
||
macro_rules! impl_dyn_sync { | ||
($($($attr: meta)* [$ty: ty where $($generics2: tt)*])*) => { | ||
$(unsafe impl<$($generics2)*> DynSync for $ty {})* | ||
}; | ||
} | ||
|
||
impl_dyn_sync!( | ||
[std::sync::atomic::AtomicPtr<T> where T] | ||
[std::sync::OnceLock<T> where T: DynSend + DynSync] | ||
[std::sync::Mutex<T> where T: ?Sized + DynSend] | ||
[std::sync::Arc<T> where T: ?Sized + DynSync + DynSend] | ||
[std::sync::LazyLock<T, F> where T: DynSend + DynSync, F: DynSend] | ||
[std::collections::HashSet<K, S> where K: DynSync, S: DynSync] | ||
[std::collections::HashMap<K, V, S> where K: DynSync, V: DynSync, S: DynSync] | ||
[std::collections::BTreeMap<K, V, A> where K: DynSync, V: DynSync, A: std::alloc::Allocator + Clone + DynSync] | ||
[Vec<T, A> where T: DynSync, A: std::alloc::Allocator + DynSync] | ||
[Box<T, A> where T: ?Sized + DynSync, A: std::alloc::Allocator + DynSync] | ||
[crate::sync::Lock<T> where T: DynSend] | ||
[crate::sync::RwLock<T> where T: DynSend + DynSync] | ||
[crate::sync::OneThread<T> where T] | ||
[crate::sync::WorkerLocal<T> where T: DynSend] | ||
[crate::intern::Interned<'a, T> where 'a, T: DynSync] | ||
[crate::tagged_ptr::CopyTaggedPtr<P, T, CP> where P: Sync + crate::tagged_ptr::Pointer, T: Sync + crate::tagged_ptr::Tag, const CP: bool] | ||
[parking_lot::lock_api::Mutex<R, T> where R: DynSync, T: ?Sized + DynSend] | ||
[parking_lot::lock_api::RwLock<R, T> where R: DynSync, T: ?Sized + DynSend + DynSync] | ||
[indexmap::IndexSet<V, S> where V: DynSync, S: DynSync] | ||
[indexmap::IndexMap<K, V, S> where K: DynSync, V: DynSync, S: DynSync] | ||
[smallvec::SmallVec<A> where A: smallvec::Array + DynSync] | ||
[thin_vec::ThinVec<T> where T: DynSync] | ||
); | ||
} | ||
); | ||
|
||
pub fn assert_dyn_sync<T: ?Sized + DynSync>() {} | ||
pub fn assert_dyn_send<T: ?Sized + DynSend>() {} | ||
pub fn assert_dyn_send_val<T: ?Sized + DynSend>(_t: &T) {} | ||
pub fn assert_dyn_send_sync_val<T: ?Sized + DynSync + DynSend>(_t: &T) {} | ||
|
||
#[derive(Copy, Clone)] | ||
pub struct FromDyn<T>(T); | ||
|
||
impl<T> FromDyn<T> { | ||
#[inline(always)] | ||
pub fn from(val: T) -> Self { | ||
// Check that `sync::is_dyn_thread_safe()` is true on creation so we can | ||
// implement `Send` and `Sync` for this structure when `T` | ||
// implements `DynSend` and `DynSync` respectively. | ||
#[cfg(parallel_compiler)] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd move the comment down here and reword it:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done :) |
||
assert!(crate::sync::is_dyn_thread_safe()); | ||
FromDyn(val) | ||
} | ||
|
||
#[inline(always)] | ||
pub fn into_inner(self) -> T { | ||
self.0 | ||
} | ||
} | ||
|
||
// `FromDyn` is `Send` if `T` is `DynSend`, since it ensures that sync::is_dyn_thread_safe() is true. | ||
#[cfg(parallel_compiler)] | ||
unsafe impl<T: DynSend> Send for FromDyn<T> {} | ||
|
||
// `FromDyn` is `Sync` if `T` is `DynSync`, since it ensures that sync::is_dyn_thread_safe() is true. | ||
#[cfg(parallel_compiler)] | ||
unsafe impl<T: DynSync> Sync for FromDyn<T> {} | ||
|
||
impl<T> std::ops::Deref for FromDyn<T> { | ||
type Target = T; | ||
|
||
#[inline(always)] | ||
fn deref(&self) -> &Self::Target { | ||
&self.0 | ||
} | ||
} | ||
|
||
// A wrapper to convert a struct that is already a `Send` or `Sync` into | ||
// an instance of `DynSend` and `DynSync`, since the compiler cannot infer | ||
// it automatically in some cases. (e.g. Box<dyn Send / Sync>) | ||
#[derive(Copy, Clone)] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A comment for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, I first thought There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think |
||
pub struct IntoDynSyncSend<T: ?Sized>(pub T); | ||
|
||
#[cfg(parallel_compiler)] | ||
unsafe impl<T: ?Sized + Send> DynSend for IntoDynSyncSend<T> {} | ||
#[cfg(parallel_compiler)] | ||
unsafe impl<T: ?Sized + Sync> DynSync for IntoDynSyncSend<T> {} | ||
|
||
impl<T> std::ops::Deref for IntoDynSyncSend<T> { | ||
type Target = T; | ||
|
||
#[inline(always)] | ||
fn deref(&self) -> &T { | ||
&self.0 | ||
} | ||
} | ||
|
||
impl<T> std::ops::DerefMut for IntoDynSyncSend<T> { | ||
#[inline(always)] | ||
fn deref_mut(&mut self) -> &mut T { | ||
&mut self.0 | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see that
Spacing
is missing here. You could sneak that in.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done :)