Skip to content

Commit 560d0fa

Browse files
author
Tomasz Miąsko
authored
rt: read join waker conditionally to avoid data race (#2096)
The previous implementation would perform a load that might be part of a data race. The value read would be used only when race did not occur. This would be well defined in a memory model where a load that is a part of race merely returns an undefined value, the Rust memory model on the other hand defines it to be undefined behaviour. Perform read conditionally to avoid data race. Covered by existing loom tests after changing casualty check to be immediate rather than deferred. Fixes: #2087
1 parent 6232c74 commit 560d0fa

File tree

1 file changed

+19
-16
lines changed

1 file changed

+19
-16
lines changed

tokio/src/task/harness.rs

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
use crate::loom::alloc::Track;
2-
use crate::loom::cell::CausalCheck;
32
use crate::task::core::{Cell, Core, Header, Trailer};
43
use crate::task::state::Snapshot;
54
use crate::task::{JoinError, Schedule, Task};
@@ -151,8 +150,12 @@ where
151150
pub(super) unsafe fn drop_task(mut self) {
152151
let might_drop_join_waker_on_release = self.might_drop_join_waker_on_release();
153152

154-
// Read the join waker cell just to have it
155-
let (join_waker, check) = self.read_join_waker();
153+
let join_waker = if might_drop_join_waker_on_release {
154+
// Read the join waker cell just to have it
155+
self.read_join_waker()
156+
} else {
157+
MaybeUninit::uninit()
158+
};
156159

157160
// transition the task to released
158161
let res = self.header().state.release_task();
@@ -163,7 +166,6 @@ where
163166
debug_assert!(res.has_join_waker());
164167

165168
// Its our responsibility to drop the waker
166-
check.check();
167169
let _ = join_waker.assume_init();
168170
}
169171

@@ -201,14 +203,13 @@ where
201203
// possible that, after the transition, we are responsible for dropping
202204
// the waker but before the waker can be read from the struct, the
203205
// struct is deallocated.
204-
let (waker, check) = self.read_join_waker();
206+
let waker = self.read_join_waker();
205207

206208
// The operation counts as dropping the join handle
207209
let res = self.header().state.complete_join_handle();
208210

209211
if res.is_released() {
210212
// We are responsible for freeing the waker handle
211-
check.check();
212213
drop(waker.assume_init());
213214
}
214215

@@ -264,7 +265,7 @@ where
264265
// possible that, after the transition, we are responsible for dropping
265266
// the waker but before the waker can be read from the struct, the
266267
// struct is deallocated.
267-
let (waker, check) = self.read_join_waker();
268+
let waker = self.read_join_waker();
268269

269270
// The operation counts as dropping the join handle
270271
let res = match self.header().state.drop_join_handle_slow() {
@@ -283,7 +284,6 @@ where
283284

284285
if !(res.is_complete() | res.is_canceled()) || res.is_released() {
285286
// We are responsible for freeing the waker handle
286-
check.check();
287287
drop(waker.assume_init());
288288
}
289289

@@ -466,19 +466,22 @@ where
466466
if join_interest {
467467
let res1 = self.transition_to_complete(join_interest);
468468

469-
// At this point, the join waker may not be changed. Once we perform
470-
// `release_task` we may no longer read from the struct but we
471-
// **may** be responsible for dropping the waker. We do an
472-
// optimistic read here.
473-
let (join_waker, check) = unsafe { self.read_join_waker() };
469+
let join_waker = if res1.has_join_waker() {
470+
// At this point, the join waker may not be changed. Once we perform
471+
// `release_task` we may no longer read from the struct but we
472+
// **may** be responsible for dropping the waker. We do an
473+
// optimistic read here.
474+
unsafe { self.read_join_waker() }
475+
} else {
476+
MaybeUninit::uninit()
477+
};
474478

475479
let res2 = self.header().state.release_task();
476480

477481
if res1.has_join_waker() && !res2.is_join_interested() {
478482
debug_assert!(res2.has_join_waker());
479483

480484
// Its our responsibility to drop the waker
481-
check.check();
482485
unsafe {
483486
drop(join_waker.assume_init());
484487
}
@@ -544,8 +547,8 @@ where
544547
});
545548
}
546549

547-
unsafe fn read_join_waker(&mut self) -> (MaybeUninit<Option<Waker>>, CausalCheck) {
548-
self.trailer().waker.with_deferred(|ptr| ptr.read())
550+
unsafe fn read_join_waker(&mut self) -> MaybeUninit<Option<Waker>> {
551+
self.trailer().waker.with(|ptr| ptr.read())
549552
}
550553

551554
unsafe fn to_task(&self) -> Task<S> {

0 commit comments

Comments
 (0)