-
Notifications
You must be signed in to change notification settings - Fork 12.9k
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
Atomic[IU]size: Supply a generic CAS loop #48655
Comments
This method would definitely be useful. However, I wonder if it's a better fit for something like Note that Java's |
This adds a new method to all numeric `Atomic*` types with a safe compare-and-set loop, so users will no longer need to write their own, except in *very* strange circumstances. This solves rust-lang#48384 with `x.fetch_max(_)`/`x.fetch_min(_)`. It also relates to rust-lang#48655 (which I misuse as tracking issue for now). *note* This *might* need a crater run because the functions could clash with third party extension traits.
Add a generic CAS loop to std::sync::Atomic* This adds two new methods to both `AtomicIsize` and `AtomicUsize` with optimized safe compare-and-set loops, so users will no longer need to write their own, except in *very* strange circumstances. `update_and_fetch` will apply the function and return its result, whereas `fetch_and_update` will apply the function and return the previous value. This solves rust-lang#48384 with `x.update_and_fetch(|x| x.max(y))`. It also relates to rust-lang#48655 (which I misuse as tracking issue for now).. *note* This *might* need a crater run because the functions could clash with third party extension traits.
I think the current doc could be read as "on success returns |
Agreed, the wording can certainly be improved. I had a lot of back-and-forth and frankly at one point almost closed the PR, I'm just happy it's landed. If you like, you may submit a doc improvement PR. Otherwise I can do it, citing you as the source. |
Documentation for the functions should specify which orderings are valid or not (e.g. |
The ordering argument names are also not entirely correct -- EDIT: Oh, the argument order is the wrong way around compared to EDIT2: Also see #53106. |
Is it worth mentioning that for the case where |
Is My concern is that Maybe a custom enum with |
Stumbled across this method today, and figured I'd prod this issue & drop in my bikeshed 2¢. I pulled the // We use a CAS loop to increment the strong count instead of a
// fetch_add because once the count hits 0 it must never be above 0.
let inner = self.inner()?;
// Relaxed load because any write of 0 that we can observe leaves the
// field in a permanently zero state (so a "stale" read of 0 is fine),
// and any other value is confirmed via the CAS.
inner
.strong
.fetch_update(
|n| {
if n == 0 {
return None;
}
// See comments in `Arc::clone` for why we do this (for `mem::forget`).
if n > MAX_REFCOUNT {
unsafe {
abort();
}
}
Some(n + 1)
},
Relaxed,
Relaxed,
)
.ok()
.map(|_| Arc {
// null checked above
ptr: self.ptr,
phantom: PhantomData,
}) A few things I've noticed:
inner
.strong
.fetch_update(Relaxed, Relaxed, |n| {
if n == 0 {
return None;
}
// See comments in `Arc::clone` for why we do this (for `mem::forget`).
if n > MAX_REFCOUNT {
unsafe {
abort();
}
}
Some(n + 1)
})
In summary, I think I'd move the lambda parameter after the two ordering parameters, and make them match the parameter order used by the Other than that, this seems like a handy API which makes writing CAS loops much cleaner, and I'd be happy to see it stabilized. |
Tweak and stabilize AtomicN::fetch_update The fetch_update method implements a compare-and-swap loop to update the value in an atomic to an arbitrary value computed by a closure. I've applied a few tweaks suggested by @mystor in this comment on the tracking issue: rust-lang#48655 (comment). Specifically, the load and store ordering arguments have been swapped to match with the orderings of `compare_exchange`, and the closure has been moved from the first to last argument. Moving the closure to the last argument is a change away from other methods on the atomic types which place the ordering(s) last, but matches with the broad convention that closure arguments come last in functions. In particular, rustfmt style lays calls with multi-line closures out more cleanly when the closure comes last.
Tweak and stabilize AtomicN::fetch_update The fetch_update method implements a compare-and-swap loop to update the value in an atomic to an arbitrary value computed by a closure. I've applied a few tweaks suggested by @mystor in this comment on the tracking issue: rust-lang#48655 (comment). Specifically, the load and store ordering arguments have been swapped to match with the orderings of `compare_exchange`, and the closure has been moved from the first to last argument. Moving the closure to the last argument is a change away from other methods on the atomic types which place the ordering(s) last, but matches with the broad convention that closure arguments come last in functions. In particular, rustfmt style lays calls with multi-line closures out more cleanly when the closure comes last.
Rationale: Writing an optimal CAS loop is time-consuming and risks errors (e.g.
mis-setting Orderings). On the other hand, there are two general operations:
fetch_and_update
which is useful for things like random number generators,and
update_and_fetch
which is the usual operation when you want to e.g. keepa maximum stat over a series of concurrently produced values.
Adding those two operations as methods has little cost. Conversely, requiring
an extra crate means that the methods would have to either be implemented as
functions instead of methods, or that an extension trait would have to be
imported to use them. Both of those options are suboptimal.
Prior art: Java has an
getAndUpdate
/updateAndGet
method on itsAtomicInteger
class since version 1.8. Due to the fact that Java lambdas aremore restricted w.r.t. mutability of their arguments than Rust's, there's also
accumulateAndGet
/getAndAccumulate
.The text was updated successfully, but these errors were encountered: