Skip to content

Commit 07c3c59

Browse files
committed
specify how we can add non-blocking set to libcore
1 parent fe46a50 commit 07c3c59

File tree

1 file changed

+43
-2
lines changed

1 file changed

+43
-2
lines changed

text/0000-standard-lazy-types.md

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -245,8 +245,7 @@ Non thread-safe flavor can be added to `core` as well.
245245

246246
The thread-safe variant is implemented similarly to `std::sync::Once`.
247247
Crucially, it has support for blocking: if many threads call `get_or_init` concurrently, only one will be able to execute the closure, while all other threads will block.
248-
For this reason, `std::sync::OnceCell` can not be provided in core.
249-
Even the minimal `OnceCell::<T>::set` API requires support for blocking, because one can't atomically set arbitrary `T`.
248+
For this reason, most of `std::sync::OnceCell` API can not be provided in `core`.
250249

251250
# Drawbacks
252251
[drawbacks]: #drawbacks
@@ -335,6 +334,48 @@ That is, we can store some initial state in the cell and consume it during initi
335334
In practice, such flexibility seems to be rarely required.
336335
Even if we add a type, similar to `OnceFlipCell`, having a dedicated `OnceCell` (which *could* be implemented on top of `OnceFlipCell`) type simplifies a common use-case.
337336

337+
## Variations of `set`
338+
339+
The RFC proposes "obvious" signature for the `set` method:
340+
341+
```rust
342+
fn set(&self, value: T) -> Result<(), T>;
343+
```
344+
345+
Note, however, that `set` establishes an invariant that the cell is initialized, so a more precise signature would be
346+
347+
```rust
348+
fn set(&self, value: T) -> (&T, Option<T>);
349+
```
350+
351+
To be able to return a reference, `set` might need to block a thread.
352+
For example, if two threads call `set` concurrently, one of them needs to block while the other moves the value into the cell.
353+
It is possible to provide a non-blocking alternative to `set`:
354+
355+
```rust
356+
fn try_set(&self, value: T) -> Result<&T, (Option<&T>, T)>
357+
```
358+
359+
That is, if value is set successfully, a reference is returned.
360+
Otherwise, ther the cell is either fully initialized, and a reference is returned as well, or the cell is being initialized, and no valid reference exist yet.
361+
362+
## Support for `no_std`
363+
364+
The RFC proposes to add `cell::OnceCell` and `cell::Lazy` to `core`, while keeping `sync::OnceCell` and `sync::Lazy` `std`-only.
365+
However, there's a subset of `sync::OnceCell` that can be provided in `core`:
366+
367+
```rust
368+
impl<T> OnceCell<T> {
369+
const fn new() -> OnceCell<T>;
370+
fn get(&self) -> Option<&T>;
371+
fn try_set(&self, value: T) -> Result<&T, (Option<&T>, T)>
372+
}
373+
```
374+
375+
It is possible because, while `OnceCell` needs block for full API, its internal state can be implemented as a single `AtomicUsize`, so the `core` part does not need to know about blocking.
376+
It is unclear if this API would be significantly useful.
377+
In particular, the guarantees of non-blocking `set` are pretty weak, and are not enough to implement the `Lazy` wrapper.
378+
338379
## Poisoning
339380

340381
As a cell can be empty or fully initialized, the proposed API does not use poisoning.

0 commit comments

Comments
 (0)