Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rt: implement task::Id using StaticAtomicU64 #5282

Merged
merged 3 commits into from
Dec 10, 2022
Merged

Conversation

carllerche
Copy link
Member

This patch simplifies the implementation of task::Id by moving conditional compilation into the AtomicU64 definition. To handle platforms that do not include const fn Mutex::new(), StaticAtomicU64 is defined, which always has a const fn new(). StaticAtomicU64 is implemented with OnceCell when needed.

Future patches will update other code locations to use StaticAtomicU64 (e.g. ThreadId).

This patch simplifies the implementation of `task::Id` by moving
conditional compilation into the `AtomicU64` definition. To handle
platforms that do not include `const fn Mutex::new()`, `StaticAtomicU64`
is defined which always has a `const fn new()`. `StaticAtomicU64` is
implemented with `OnceCell` when needed.
@carllerche carllerche added C-maintenance Category: PRs that clean code up or issues documenting cleanup. A-tokio Area: The main tokio crate M-runtime Module: tokio/runtime labels Dec 9, 2022
@carllerche carllerche requested review from Darksonn and hawkw December 9, 2022 22:39
@github-actions github-actions bot added the R-loom Run loom tests on this PR label Dec 9, 2022
@@ -1,18 +1,24 @@
use crate::loom::sync::Mutex;
use std::sync::atomic::Ordering;

cfg_has_const_mutex_new! {
#[path = "atomic_u64_static_const_new.rs"]
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm experimenting with a way to have less code within the cfg macros. This should help rustfmt apply to more code.

use crate::util::once_cell::OnceCell;

pub(crate) struct StaticAtomicU64 {
init: u64,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is a bit annoying to store the initial value forever, but this is only for platforms that do not have AtomicU64 and no const fn Mutex::new() (i.e. old versions of Rust), so it isn't a big deal IMO.

@carllerche
Copy link
Member Author

Seems like I have to figure out how to make it work with loom...

Comment on lines +32 to +33
// TODO: implement a loom version
pub(crate) type StaticAtomicU64 = std::sync::atomic::AtomicU64;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a bit (ok, a lot) of a hack, but it is equivalent to before the patch. Unfortunately, right now, loom does not have a way to model static variables.

@@ -25,7 +25,7 @@ impl<T> OnceCell<T> {
/// If the `init` closure panics, then the `OnceCell` is poisoned and all
/// future calls to `get` will panic.
#[inline]
pub(crate) fn get(&self, init: fn() -> T) -> &T {
pub(crate) fn get(&self, init: impl FnOnce() -> T) -> &T {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo on line 22: `intiailizing' -> initializing

Copy link
Contributor

@notgull notgull left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This may be a bad idea, but wouldn't it be reasonable to use something like atomic-polyfill or portable-atomic to polyfill out atomics on systems that don't have them? I know this probably doesn't apply to this PR, but I think that a sequence-lock system may be a more ergonomic way of accessing this value.

@carllerche carllerche merged commit 3976622 into master Dec 10, 2022
@carllerche carllerche deleted the better-atomic-u64 branch December 10, 2022 21:49
@taiki-e
Copy link
Member

taiki-e commented Dec 11, 2022

@notgull
tokio does not support no-std targets and the requirement for std is to have atomic types. This is an issue regarding having 64-bit atomic, and atomic-polyfill does not improve anything here.

portable-atomic provides fallback on platforms where 64-bit atomic is not available, it can more accurately detect platforms where 64-bit atomic is available than tokio1, and the seqlock used by portable-atomic in its fallback is probably more efficient for such uses.

That said, given the differences between global locking and per-object locking and tokio's policy on dependencies, it is doubtful that it is worth adding a non-optional dependency to tokio for this purpose.

Footnotes

  1. The logic of tokio in this area is also based on my implementation, and AFAIK portable-atomic's implementation is the best form for this area at this time. (although it is complex).

@notgull
Copy link
Contributor

notgull commented Dec 11, 2022

@taiki-e Thanks for explaining this to me. Would you accept a PR where the current Mutex implementation is replaced with a hand-rolled seq-lock implementation?

@taiki-e
Copy link
Member

taiki-e commented Dec 11, 2022

There are not many targets where 64-bit atomic is essentially unavailable (although this fallback is used by many targets because the current detection logic is a bit rough)1, and I'm not sure if the complexity is worth it.2 Proper seqlock implementation on a 32-bit system is somewhat complex and sound seqlock implementation requires logic like per-byte atomic memcpy that loom is unclear whether it understands.

What I would like to do here is to improve the detection logic.
When using cfg(target_has_atomic = "64") on Rust 1.60+ like portable-atomic does, it should be able to accurately detect targets without 64-bit atomic, greatly reducing the number of platforms that require fallback implementation. Specifically, this could lead to visible performance improvements in armv6 and armv7.

Footnotes

  1. Also, recent linux has a helper for 64-bit CAS (__kuser_cmpxchg64), so maybe future increases in Rust's kernel requirements will further reduce the number of targets requiring fallback for 64-bit atomic.

  2. portable-atomic also supports 128-bit atomic, so the situation is somewhat different.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-tokio Area: The main tokio crate C-maintenance Category: PRs that clean code up or issues documenting cleanup. M-runtime Module: tokio/runtime R-loom Run loom tests on this PR
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants