mirror of
https://github.com/rust-lang/rust.git
synced 2025-01-28 15:43:21 +00:00
Rollup merge of #98503 - RalfJung:scope-race, r=m-ou-se
fix data race in thread::scope Puts the `ScopeData` into an `Arc` so it sticks around as long as we need it. This means one extra `Arc::clone` per spawned scoped thread, which I hope is fine. Fixes https://github.com/rust-lang/rust/issues/98498 r? `````@m-ou-se`````
This commit is contained in:
commit
ebecc13106
@ -159,6 +159,7 @@ use crate::cell::UnsafeCell;
|
||||
use crate::ffi::{CStr, CString};
|
||||
use crate::fmt;
|
||||
use crate::io;
|
||||
use crate::marker::PhantomData;
|
||||
use crate::mem;
|
||||
use crate::num::NonZeroU64;
|
||||
use crate::num::NonZeroUsize;
|
||||
@ -462,7 +463,7 @@ impl Builder {
|
||||
unsafe fn spawn_unchecked_<'a, 'scope, F, T>(
|
||||
self,
|
||||
f: F,
|
||||
scope_data: Option<&'scope scoped::ScopeData>,
|
||||
scope_data: Option<Arc<scoped::ScopeData>>,
|
||||
) -> io::Result<JoinInner<'scope, T>>
|
||||
where
|
||||
F: FnOnce() -> T,
|
||||
@ -479,8 +480,11 @@ impl Builder {
|
||||
}));
|
||||
let their_thread = my_thread.clone();
|
||||
|
||||
let my_packet: Arc<Packet<'scope, T>> =
|
||||
Arc::new(Packet { scope: scope_data, result: UnsafeCell::new(None) });
|
||||
let my_packet: Arc<Packet<'scope, T>> = Arc::new(Packet {
|
||||
scope: scope_data,
|
||||
result: UnsafeCell::new(None),
|
||||
_marker: PhantomData,
|
||||
});
|
||||
let their_packet = my_packet.clone();
|
||||
|
||||
let output_capture = crate::io::set_output_capture(None);
|
||||
@ -507,7 +511,7 @@ impl Builder {
|
||||
unsafe { *their_packet.result.get() = Some(try_result) };
|
||||
};
|
||||
|
||||
if let Some(scope_data) = scope_data {
|
||||
if let Some(scope_data) = &my_packet.scope {
|
||||
scope_data.increment_num_running_threads();
|
||||
}
|
||||
|
||||
@ -1298,8 +1302,9 @@ pub type Result<T> = crate::result::Result<T, Box<dyn Any + Send + 'static>>;
|
||||
// An Arc to the packet is stored into a `JoinInner` which in turns is placed
|
||||
// in `JoinHandle`.
|
||||
struct Packet<'scope, T> {
|
||||
scope: Option<&'scope scoped::ScopeData>,
|
||||
scope: Option<Arc<scoped::ScopeData>>,
|
||||
result: UnsafeCell<Option<Result<T>>>,
|
||||
_marker: PhantomData<Option<&'scope scoped::ScopeData>>,
|
||||
}
|
||||
|
||||
// Due to the usage of `UnsafeCell` we need to manually implement Sync.
|
||||
@ -1330,7 +1335,7 @@ impl<'scope, T> Drop for Packet<'scope, T> {
|
||||
rtabort!("thread result panicked on drop");
|
||||
}
|
||||
// Book-keeping so the scope knows when it's done.
|
||||
if let Some(scope) = self.scope {
|
||||
if let Some(scope) = &self.scope {
|
||||
// Now that there will be no more user code running on this thread
|
||||
// that can use 'scope, mark the thread as 'finished'.
|
||||
// It's important we only do this after the `result` has been dropped,
|
||||
|
@ -11,7 +11,7 @@ use crate::sync::Arc;
|
||||
/// See [`scope`] for details.
|
||||
#[stable(feature = "scoped_threads", since = "1.63.0")]
|
||||
pub struct Scope<'scope, 'env: 'scope> {
|
||||
data: ScopeData,
|
||||
data: Arc<ScopeData>,
|
||||
/// Invariance over 'scope, to make sure 'scope cannot shrink,
|
||||
/// which is necessary for soundness.
|
||||
///
|
||||
@ -130,12 +130,14 @@ pub fn scope<'env, F, T>(f: F) -> T
|
||||
where
|
||||
F: for<'scope> FnOnce(&'scope Scope<'scope, 'env>) -> T,
|
||||
{
|
||||
// We put the `ScopeData` into an `Arc` so that other threads can finish their
|
||||
// `decrement_num_running_threads` even after this function returns.
|
||||
let scope = Scope {
|
||||
data: ScopeData {
|
||||
data: Arc::new(ScopeData {
|
||||
num_running_threads: AtomicUsize::new(0),
|
||||
main_thread: current(),
|
||||
a_thread_panicked: AtomicBool::new(false),
|
||||
},
|
||||
}),
|
||||
env: PhantomData,
|
||||
scope: PhantomData,
|
||||
};
|
||||
@ -250,7 +252,7 @@ impl Builder {
|
||||
F: FnOnce() -> T + Send + 'scope,
|
||||
T: Send + 'scope,
|
||||
{
|
||||
Ok(ScopedJoinHandle(unsafe { self.spawn_unchecked_(f, Some(&scope.data)) }?))
|
||||
Ok(ScopedJoinHandle(unsafe { self.spawn_unchecked_(f, Some(scope.data.clone())) }?))
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user