Edit: solved, thanks Paul!

Any bored #RustLang atomics enthusiast around? I'm trying to write a kind of global unique id allocator using fetch_add and compare_exchange but now I'm not even sure if it's possible with the right orderings or if I should just do the simple thing and wrap it in a mutex ...

https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=bb48f6352ffff02df49c2e12d98655f1

#rust #atomics

Rust Playground

A browser interface to the Rust compiler to experiment with the language

I don't mind skipped IDs but duplicates are bad unless wraparound happens between them.
@algebraicterror a single “fetch_add(1, Relaxed)” should be the only atomic operation you need. That operation does wrap already, and you can detect wrapping based on the value you get.
@pauldoo Oh, right! I actually have couple of forbidden values around u16::MIN and u16::MAX but the fetch_add can just be repeated until I get a number from the allowed range. Thanks!
@algebraicterror ahh okay. :) If you have lots of forbidden values you can also do something like:
```rust
let mut result = counter.load(Relaxed);
loop {
let next = computeNextValue(result);
match counter.compare_and_exchange(result, next, Relaxed, Relaxed) {
Ok(_) => return result,
Err(actual) => result = actual
}
}
```
@pauldoo Oh, nice general solution! There's 16 bad values, I think that's acceptable performance hit in exchange for more obvious code.

@algebraicterror `fetch_add()` wraps itself, so you don't need any extra work.

However, if you need to catch the situation, the `compare_exchange()` is ANOTHER atomic operation, and the `fetch_add()` from another thread could happen already, so you're going to compare not what you expect.

Rather something like this would work:
https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=237b0c34607a226badc4f7f3d4cc2fac

Rust Playground

A browser interface to the Rust compiler to experiment with the language

@algebraicterror I've just realised, the second variable in this case is probably redundant, as every thread can load it's prev_value first, worst case you'll get two warnings.
@michalfita I don't actually need to know when the wrap happens, but I can imagine circumstances when it's needed. Thanks!