Skip to content

Commit 4dc732e

Browse files
committed
Note Consume as an alternative to acquire
1 parent b78c416 commit 4dc732e

File tree

1 file changed

+43
-0
lines changed

1 file changed

+43
-0
lines changed

text/0000-standard-lazy-types.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,48 @@ It is possible to add only `sync` version of the types, as they are the most use
432432
However, this would be against zero cost abstractions spirit.
433433
Additionally, non thread-safe version is required to replace `thread_local!` macro without imposing synchronization.
434434

435+
## Synchronization Guarantees
436+
437+
In theory, it is possible to specify two different synchronization guarantees for `get` operation, release/acquire or release/consume.
438+
They differ in how they treat side effects.
439+
If thread **A** executes `get_or_init(f)`, and thread **B** executes `get` and observes the value, release/acquire guarantees that **B** also observes side-effects of `f`.
440+
441+
Here's a program which allows to observe the difference:
442+
443+
```rust
444+
static FLAG: AtomicBool = AtomicBool::new(false);
445+
static CELL: OnceCell<()> = OnceCell::new();
446+
447+
// thread1
448+
CELL.get_or_init(|| FLAG.store(true, Relaxed));
449+
450+
// thread2
451+
if CELL.get().is_some() {
452+
assert!(FLAG.load(Relaxed))
453+
}
454+
```
455+
456+
Under release/acquire, the assert never fires.
457+
Under release/consume, it might fire.
458+
459+
Release/consume can potentially be implemented more efficiently on weak memory model architectures.
460+
However, the situation with `consume` ordering is cloudy right now:
461+
462+
* [nobody knows what it actually means](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0371r0.html),
463+
* [but people rely on it in practice for performance](https://docs.rs/crossbeam-utils/0.7.0/crossbeam_utils/atomic/trait.AtomicConsume.html#tymethod.load_consume).
464+
465+
We can do one of the following:
466+
467+
1. Specify and implement `acquire` ordering,
468+
2. Specify `consume` but implement `acquire` (or hack `consume` in an implementation-defined manner) with the hope to make implementation more efficient later.
469+
3. Specify and implement `acquire`, but provide additional API which can take `Ordering` as an argument.
470+
471+
Option two seems the most promising:
472+
473+
* it is forward compatible with specifying `acquire` later,
474+
* for typical `OnceCell` use-cases, `consume` should be enough.
475+
For guaranteeing side effects, `std::sync::Once` may be used instead.
476+
435477
# Prior art
436478
[prior-art]: #prior-art
437479

@@ -453,6 +495,7 @@ This design doesn't always work in Rust, as closing over `self` runs afoul of th
453495
- What is the best naming/place for these types?
454496
- What is the best naming scheme for methods? Is it `get_or_try_init` or `try_inert_with`?
455497
- Is the `F = fn() -> T` hack worth it?
498+
- Which synchronization guarantee should we pick?
456499

457500
# Future possibilities
458501
[future-possibilities]: #future-possibilities

0 commit comments

Comments
 (0)