Greetings
I'm studying OS and inevitably encountered some memory ordering. After I had some ideas what they are meant for, I encountered a place where I need to implement something just like Rust std::sync::OnceLock
in C (simply busy loop while waiting i.e. without futex
: it's merely a small section during boot), so I decided to make sure I understand how Rust did it first.
But then I have some trouble figuring this out: why does the CAS success case need Acquire
?
AFAIK Acquire
and Release
come in pairs or they don't serve much purpose except when implementing interrupt handlers since the reordering (or rather, lack of) is transparent to single thread executions. So it's probably to synchronize with the CompletionGuard
, since that's the only place Release
ordering is used.
The impl<'a> Drop for CompletionGuard<'a>
sets Once::state_and_queued
to either COMPLETE
or POISONED
, and in this particular case in Once::call
, it's the latter case, for we're discussing the CAS success case.
Together with the fact that the modification order for Once::state_and_queued
is comprised only of RMW/CAS operations which make release sequences, I suspect this Acquire
here is to make sure for all the past functions that had been put here (i.e. those impl FnOnce
), all artifacts of all the past impl FnOnce
are "visible" to this current invocation of Once::call
with ignore_poisoning
parameter set to true
: when user wants to clear the mess as historical invocation(s) might have panicked, we'd better make sure this latest invocation of Once::call
sees all the things that had happened.
Does such a statement hold any water? If this statement is in itself fine, I'm wondering if it's indeed the full story: am I missing something?