Skip to content

Commit

Permalink
Don't crash on lazy_static init race
Browse files Browse the repository at this point in the history
The real lazy_static uses a lock to avoid races in initialization. We
don't implement that lock yet, so our initialization can race. If we do,
we should not crash like we did before.

Fixes tokio-rs#126.
  • Loading branch information
jonhoo committed Apr 12, 2020
1 parent 13b7dc4 commit 4849da4
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 7 deletions.
15 changes: 12 additions & 3 deletions src/lazy_static.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,22 @@ impl<T: 'static> Lazy<T> {
Some(v) => v,
None => {
// Init the value out of the `rt::execution`
let mut sv = crate::rt::lazy_static::StaticValue::new((self.init)());
let sv = crate::rt::lazy_static::StaticValue::new((self.init)());

// While calling init, we may have yielded to the scheduler, in which case some
// _other_ thread may have initialized the static. The real lazy_static does not
// have this issue, since it takes a lock before initializing the new value, and
// readers wait on that lock if they encounter it. We could implement that here
// too, but for simplicity's sake, we just do another try_get here for now.
if let Some(v) = unsafe { self.try_get() } {
return v;
}

rt::execution(|execution| {
let sv = execution.lazy_statics.init_static(self, sv);

// lazy_static uses std::sync::Once, which does a swap(AcqRel) to set
sv.sync.sync_store(&mut execution.threads, Ordering::AcqRel);

execution.lazy_statics.init_static(self, sv);
});

unsafe { self.try_get() }.expect("bug")
Expand Down
13 changes: 9 additions & 4 deletions src/rt/lazy_static.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,18 @@ impl Set {
&mut self,
key: &'static crate::lazy_static::Lazy<T>,
value: StaticValue,
) {
assert!(self
) -> &mut StaticValue {
let v = self
.statics
.as_mut()
.expect("attempted to access lazy_static during shutdown")
.insert(StaticKeyId::new(key), value)
.is_none())
.entry(StaticKeyId::new(key));

if let std::collections::hash_map::Entry::Occupied(_) = v {
unreachable!("told to init static, but it was already init'd");
}

v.or_insert(value)
}
}

Expand Down

0 comments on commit 4849da4

Please sign in to comment.