Skip to content

Commit 6ad8383

Browse files
committed
Auto merge of #105590 - solid-rs:patch/kmc-solid/thread-lifecycle-ordering, r=m-ou-se
kmc-solid: Fix memory ordering in thread operations Fixes two memory ordering issues in the thread state machine (`ThreadInner::lifecycle`) of the [`*-kmc-solid_*`](https://doc.rust-lang.org/nightly/rustc/platform-support/kmc-solid.html) Tier 3 targets. 1. When detaching a thread that is still running (i.e., the owner updates `lifecycle` first, and the child updates it next), the first update did not synchronize-with the second update, resulting in a data race between the first update and the deallocation of `ThreadInner` by the child thread. 2. When joining on a thread, the joiner has to pass its own task ID to the joinee in order to be woken up later, but in doing so, it did not synchronize-with the read operation, creating possible sequences of execution where the joinee wakes up an incorrect or non-existent task. Both issue are theoretical and most likely have never manifested in practice because of the stronger guarantees provided by the Arm memory model (particularly due to its barrier-based definition). Compiler optimizations could have subverted this, but the inspection of compiled code did not reveal such optimizations taking place.
2 parents b15ca66 + 6fbef06 commit 6ad8383

File tree

1 file changed

+16
-9
lines changed

1 file changed

+16
-9
lines changed

library/std/src/sys/itron/thread.rs

+16-9
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ impl Thread {
119119

120120
let old_lifecycle = inner
121121
.lifecycle
122-
.swap(LIFECYCLE_EXITED_OR_FINISHED_OR_JOIN_FINALIZE, Ordering::Release);
122+
.swap(LIFECYCLE_EXITED_OR_FINISHED_OR_JOIN_FINALIZE, Ordering::AcqRel);
123123

124124
match old_lifecycle {
125125
LIFECYCLE_DETACHED => {
@@ -129,9 +129,9 @@ impl Thread {
129129

130130
// In this case, `*p_inner`'s ownership has been moved to
131131
// us, and we are responsible for dropping it. The acquire
132-
// ordering is not necessary because the parent thread made
133-
// no memory access needing synchronization since the call
134-
// to `acre_tsk`.
132+
// ordering ensures that the swap operation that wrote
133+
// `LIFECYCLE_DETACHED` happens-before `Box::from_raw(
134+
// p_inner)`.
135135
// Safety: See above.
136136
let _ = unsafe { Box::from_raw(p_inner) };
137137

@@ -151,6 +151,9 @@ impl Thread {
151151
// Since the parent might drop `*inner` and terminate us as
152152
// soon as it sees `JOIN_FINALIZE`, the release ordering
153153
// must be used in the above `swap` call.
154+
//
155+
// To make the task referred to by `parent_tid` visible, we
156+
// must use the acquire ordering in the above `swap` call.
154157

155158
// [JOINING → JOIN_FINALIZE]
156159
// Wake up the parent task.
@@ -218,11 +221,15 @@ impl Thread {
218221

219222
let current_task = current_task as usize;
220223

221-
match inner.lifecycle.swap(current_task, Ordering::Acquire) {
224+
match inner.lifecycle.swap(current_task, Ordering::AcqRel) {
222225
LIFECYCLE_INIT => {
223226
// [INIT → JOINING]
224227
// The child task will transition the state to `JOIN_FINALIZE`
225228
// and wake us up.
229+
//
230+
// To make the task referred to by `current_task` visible from
231+
// the child task's point of view, we must use the release
232+
// ordering in the above `swap` call.
226233
loop {
227234
expect_success_aborting(unsafe { abi::slp_tsk() }, &"slp_tsk");
228235
// To synchronize with the child task's memory accesses to
@@ -267,15 +274,15 @@ impl Drop for Thread {
267274
let inner = unsafe { self.p_inner.as_ref() };
268275

269276
// Detach the thread.
270-
match inner.lifecycle.swap(LIFECYCLE_DETACHED_OR_JOINED, Ordering::Acquire) {
277+
match inner.lifecycle.swap(LIFECYCLE_DETACHED_OR_JOINED, Ordering::AcqRel) {
271278
LIFECYCLE_INIT => {
272279
// [INIT → DETACHED]
273280
// When the time comes, the child will figure out that no
274281
// one will ever join it.
275282
// The ownership of `*p_inner` is moved to the child thread.
276-
// However, the release ordering is not necessary because we
277-
// made no memory access needing synchronization since the call
278-
// to `acre_tsk`.
283+
// The release ordering ensures that the above swap operation on
284+
// `lifecycle` happens-before the child thread's
285+
// `Box::from_raw(p_inner)`.
279286
}
280287
LIFECYCLE_FINISHED => {
281288
// [FINISHED → JOINED]

0 commit comments

Comments
 (0)