Skip to content

Commit

Permalink
runtime: fix Stacked Borrows violation in LocalOwnedTasks (#5099)
Browse files Browse the repository at this point in the history
## Motivation

It turns out that `LocalSet` has...never actually passed Miri. This is
because all of the tests for `LocalSet` are defined as integration tests
in `tokio/tests/task_local_set.rs`, rather than lib tests in
`tokio/src`, and we never run Miri for integration tests.

PR #5095 added a new test reproducing an unrelated bug in `LocalSet`,
which had to be implemented as a lib test, as it must make internal
assertions about the state of the `LocalSet` that cannot be tested with
just the public API. This test failed under Miri, and it turns out that
the reason for this is unrelated to the change in #5095 --- `LocalSet`
uses the `LocalOwnedTasks` type, which turns out to contain a stacked
borrows violation due to converting an `&Header` into an
`NonNull<Header>` when removing a task from the `LocalOwnedTasks` list.

## Solution

Fortunately, this was actually quite easy to fix. The non-`Local`
version of `OwnedTasks` already uses `Task::header_ptr()` to get a
`NonNull<Header>` in its version of `remove`, which avoids this issue.
Therefore, the fix was as simple as updating `LocalOwnedTasks` to do the
same.

I've also added a very simple lib test in `src/task/local_set.rs` that
just creates a `LocalSet` and spawns a single task on it under a
current-thread runtime. This test fails under Miri without the fix in
this PR: https://github.com/tokio-rs/tokio/actions/runs/3237072694/jobs/5303682530

We should probably keep the test so that we're always testing at least
the most trivial `LocalSet` usage under Miri...
  • Loading branch information
hawkw committed Oct 12, 2022
1 parent ae0d49d commit ca9dd72
Show file tree
Hide file tree
Showing 2 changed files with 20 additions and 1 deletion.
2 changes: 1 addition & 1 deletion tokio/src/runtime/task/list.rs
Expand Up @@ -240,7 +240,7 @@ impl<S: 'static> LocalOwnedTasks<S> {
self.with_inner(|inner|
// safety: We just checked that the provided task is not in some
// other linked list.
unsafe { inner.list.remove(task.header().into()) })
unsafe { inner.list.remove(task.header_ptr()) })
}

/// Asserts that the given task is owned by this LocalOwnedTasks and convert
Expand Down
19 changes: 19 additions & 0 deletions tokio/src/task/local.rs
Expand Up @@ -921,3 +921,22 @@ impl task::Schedule for Arc<Shared> {
}
}
}

#[cfg(test)]
mod tests {
use super::*;
#[test]
fn local_current_thread_scheduler() {
let f = async {
LocalSet::new()
.run_until(async {
spawn_local(async {}).await.unwrap();
})
.await;
};
crate::runtime::Builder::new_current_thread()
.build()
.expect("rt")
.block_on(f)
}
}

0 comments on commit ca9dd72

Please sign in to comment.