Closed matklad closed 2 years ago
Thinking of it, looks like even set
can't be lock, in general? You need to atomically set both the is_completed state and the value, and you can only do that if you can pack both into a single word.
You can do that if you heap-allocate the value, but that again needs std.
Thinking of it, looks like even set can't be lock, in general? You need to atomically set both the is_completed state and the value, and you can only do that if you can pack both into a single word.
Would one atomic with three states not be enough?
pub struct OnceCell<T> {
status: AtomicUsize,
value: UnsafeCell<Option<T>>,
}
pub fn set(&self, value: T) -> Result<(), T> {
match self.state.load(Ordering::Relaxed) {
UNINITIALIZED => {
let old = self.state.compare_and_swap(UNINITIALIZED, RUNNING, Ordering::Relaxed);
if old != UNINITIALIZED {
// Another thread must have initialized the cell just now, or is in the process of initializing it.
Err(value)
}
let slot = unsafe { &mut *self.inner.get() };
*slot = Some(value);
self.state.store(INITIALIZED, Ordering::Release);
Ok(())
},
RUNNING | INITIALIZED => Err(value),
}
}
I suppose that if the initializing thread gets suspended while the state is RUNNING
, all threads are out of luck until it can continue, but don't really see other problems yet.
Hm, that’s interesting!
I think we perhaps need two methods with set-like semantics: one that might block, but guarantees that the cell is initialized (maybe even returns an &T), and one that does not block, bit also does not guarantee that the cell is initialized.
Changing the current set
to not block would be a difficult change, as code currently could call get_unchecked
after a set
and expect it to be safe.
A non blocking set
would mean the first get
has to be used in a loop or something until it succeeds. I see not blocking as less then optimal, but suppose such a new method can unlock some uses and support no_std.
👍 to have set
return &T
This is now more or less availble in the race
module
OnceCell::get and OnceCell:: set could work without blocking, so we should support these APIs in no std. Note that we intentionally do not want to implement no_std blocking via spinning, see https://www.reddit.com/r/rust/comments/cy910u/comment/eyrfmna for rationale.