Here is the situation. I have a container moving between threads, holding a maybe uninitialized value. I keep track of the initialization status using a boolean. It looks like this:
struct Wrap {
initialized: bool,
value: MaybeUninit<ManuallyDrop<T>>
}
Don't ask me why I'm doing such nasty stuff, its for the greater good. Now I provide one method to create an uninitialized Wrap
and one method to initialize it. Of course the later can be called from any thread.
impl Wrap {
pub fn new() {
Wrap {
initialized: false,
value: MaybeUninit::uninit()
}
}
/// This can be called from any thread
pub fn init(&mut self, value: T) {
/// scary section
self.initialized = true;
self.value = MaybeUninit::new(ManuallyDrop::new(value))
/// end of the scary section
}
}
Now comes the very scary part. I will need to drop the value someday.
impl Drop for Wrap {
/// This can be called from any thread
fn drop(&mut self) {
/// very scary section
if self.initialized {
unsafe {
ManuallyDrop::drop(self.value.get_mut())
}
}
/// end of the very scary section
}
}
Since init
and drop
may be called from different threads, and since init
may never be called at all, I need to be really sure that the content of the init
method happens atomically (so that I cannot free uninitialized memory), and that the value of initialized
is propagated to the other threads before the drop
function is called (so that I can't have a memory leak).
One very important detail, I want to avoid the use of a Mutex
. I was imagining something using AtomicBool
. But if I know something about concurrency and the std::sync::atomic
module is that 1) things have a great tendency to go wrong 2) I don't understand the semantics of Ordering
well enough to do it correctly. I need help.
Two questions then: Is it possible to pull that off without locking? How?
Thanks in advance