Skip to content

Commit ebecc13

Browse files
authored
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 #98498 r? `````@m-ou-se`````
2 parents 0e71d1f + af0c1fe commit ebecc13

File tree

2 files changed

+17
-10
lines changed

2 files changed

+17
-10
lines changed

library/std/src/thread/mod.rs

+11-6
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ use crate::cell::UnsafeCell;
159159
use crate::ffi::{CStr, CString};
160160
use crate::fmt;
161161
use crate::io;
162+
use crate::marker::PhantomData;
162163
use crate::mem;
163164
use crate::num::NonZeroU64;
164165
use crate::num::NonZeroUsize;
@@ -462,7 +463,7 @@ impl Builder {
462463
unsafe fn spawn_unchecked_<'a, 'scope, F, T>(
463464
self,
464465
f: F,
465-
scope_data: Option<&'scope scoped::ScopeData>,
466+
scope_data: Option<Arc<scoped::ScopeData>>,
466467
) -> io::Result<JoinInner<'scope, T>>
467468
where
468469
F: FnOnce() -> T,
@@ -479,8 +480,11 @@ impl Builder {
479480
}));
480481
let their_thread = my_thread.clone();
481482

482-
let my_packet: Arc<Packet<'scope, T>> =
483-
Arc::new(Packet { scope: scope_data, result: UnsafeCell::new(None) });
483+
let my_packet: Arc<Packet<'scope, T>> = Arc::new(Packet {
484+
scope: scope_data,
485+
result: UnsafeCell::new(None),
486+
_marker: PhantomData,
487+
});
484488
let their_packet = my_packet.clone();
485489

486490
let output_capture = crate::io::set_output_capture(None);
@@ -507,7 +511,7 @@ impl Builder {
507511
unsafe { *their_packet.result.get() = Some(try_result) };
508512
};
509513

510-
if let Some(scope_data) = scope_data {
514+
if let Some(scope_data) = &my_packet.scope {
511515
scope_data.increment_num_running_threads();
512516
}
513517

@@ -1298,8 +1302,9 @@ pub type Result<T> = crate::result::Result<T, Box<dyn Any + Send + 'static>>;
12981302
// An Arc to the packet is stored into a `JoinInner` which in turns is placed
12991303
// in `JoinHandle`.
13001304
struct Packet<'scope, T> {
1301-
scope: Option<&'scope scoped::ScopeData>,
1305+
scope: Option<Arc<scoped::ScopeData>>,
13021306
result: UnsafeCell<Option<Result<T>>>,
1307+
_marker: PhantomData<Option<&'scope scoped::ScopeData>>,
13031308
}
13041309

13051310
// Due to the usage of `UnsafeCell` we need to manually implement Sync.
@@ -1330,7 +1335,7 @@ impl<'scope, T> Drop for Packet<'scope, T> {
13301335
rtabort!("thread result panicked on drop");
13311336
}
13321337
// Book-keeping so the scope knows when it's done.
1333-
if let Some(scope) = self.scope {
1338+
if let Some(scope) = &self.scope {
13341339
// Now that there will be no more user code running on this thread
13351340
// that can use 'scope, mark the thread as 'finished'.
13361341
// It's important we only do this after the `result` has been dropped,

library/std/src/thread/scoped.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use crate::sync::Arc;
1111
/// See [`scope`] for details.
1212
#[stable(feature = "scoped_threads", since = "1.63.0")]
1313
pub struct Scope<'scope, 'env: 'scope> {
14-
data: ScopeData,
14+
data: Arc<ScopeData>,
1515
/// Invariance over 'scope, to make sure 'scope cannot shrink,
1616
/// which is necessary for soundness.
1717
///
@@ -130,12 +130,14 @@ pub fn scope<'env, F, T>(f: F) -> T
130130
where
131131
F: for<'scope> FnOnce(&'scope Scope<'scope, 'env>) -> T,
132132
{
133+
// We put the `ScopeData` into an `Arc` so that other threads can finish their
134+
// `decrement_num_running_threads` even after this function returns.
133135
let scope = Scope {
134-
data: ScopeData {
136+
data: Arc::new(ScopeData {
135137
num_running_threads: AtomicUsize::new(0),
136138
main_thread: current(),
137139
a_thread_panicked: AtomicBool::new(false),
138-
},
140+
}),
139141
env: PhantomData,
140142
scope: PhantomData,
141143
};
@@ -250,7 +252,7 @@ impl Builder {
250252
F: FnOnce() -> T + Send + 'scope,
251253
T: Send + 'scope,
252254
{
253-
Ok(ScopedJoinHandle(unsafe { self.spawn_unchecked_(f, Some(&scope.data)) }?))
255+
Ok(ScopedJoinHandle(unsafe { self.spawn_unchecked_(f, Some(scope.data.clone())) }?))
254256
}
255257
}
256258

0 commit comments

Comments
 (0)