Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This adds the `lazy_static!` macro from the `lazy_static` crate. We need to re-implement it both since it establishes causality (through its use of `Once`), and because the state needs to be cleared on every execution. This implementation does that. One unfortunate downside of this implementation is that it is inherently unsafe. Specifically, in the context of loom, statics are, well, not static. And yet the API of `lazy_static!` is such that it gives out `'static` references. We _cannot_ safely give out `'static` refs into statics that we clear on every execution. One option here is to leak every static we allocate. The code would then be safe (since the references would indeed be `'static`), but it would mean we leak _all_ statics for _every_ execution, no matter whether the user needs them to be `'static` or not. Fixes #124.
- Loading branch information
Showing
7 changed files
with
262 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
//! Mock implementation of `std::thread`. | ||
|
||
use crate::rt; | ||
pub use crate::rt::thread::AccessError; | ||
pub use crate::rt::yield_now; | ||
use crate::sync::atomic::Ordering; | ||
|
||
pub use std::thread::panicking; | ||
|
||
use std::fmt; | ||
use std::marker::PhantomData; | ||
|
||
/// Mock implementation of `lazy_static::Lazy`. | ||
pub struct Lazy<T> { | ||
// Sadly, these fields have to be public, since function pointers in const | ||
// fns are unstable. When fn pointer arguments to const fns stabilize, these | ||
// should be made private and replaced with a `const fn new`. | ||
// | ||
// User code should not rely on the existence of these fields. | ||
#[doc(hidden)] | ||
pub init: fn() -> T, | ||
#[doc(hidden)] | ||
pub _p: PhantomData<fn(T)>, | ||
} | ||
|
||
impl<T: 'static> Lazy<T> { | ||
/// Mock implementation of `lazy_static::Lazy::get`. | ||
pub fn get(&'static self) -> &'static T { | ||
// This is not great. Specifically, we're returning a 'static reference to a value that | ||
// only lives for the duration of the execution. Unfortunately, the semantics of lazy | ||
// static is that, well, you get a value that is in fact 'static. If we did not provide | ||
// that, then this replacement wouldn't actually work. | ||
// | ||
// The "upside" here is that _if_ the code compiled with `lazy_static::lazy_static!`, | ||
// _then_ this is safe. That's not super satisfying, but I'm not sure how we do better | ||
// without changing the API pretty drastically. We could perhaps here provide a | ||
// `with(closure)` like we do for `UnsafeCell`, and require people to wrap the "real" | ||
// `lazy_static` the same way, but that seems like its own kind of unfortunate as I'm sure | ||
// users sometimes _rely_ on the returned reference being 'static. If we provided something | ||
// that used a closure to give the user a non-`'static` reference, we wouldn't be all that | ||
// much further along. | ||
match unsafe { self.try_get() } { | ||
Some(v) => v, | ||
None => { | ||
// Init the value out of the `rt::execution` | ||
let mut sv = crate::rt::lazy_static::StaticValue::new((self.init)()); | ||
|
||
rt::execution(|execution| { | ||
// 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") | ||
} | ||
} | ||
} | ||
|
||
unsafe fn try_get(&'static self) -> Option<&'static T> { | ||
unsafe fn transmute_lt<'a, 'b, T>(t: &'a T) -> &'b T { | ||
std::mem::transmute::<&'a T, &'b T>(t) | ||
} | ||
|
||
let sv = rt::execution(|execution| { | ||
let sv = execution.lazy_statics.get_static(self)?; | ||
|
||
// lazy_static uses std::sync::Once, which does a load(Acquire) to get | ||
sv.sync.sync_load(&mut execution.threads, Ordering::Acquire); | ||
|
||
Some(transmute_lt(sv)) | ||
})?; | ||
|
||
Some(sv.get::<T>()) | ||
} | ||
} | ||
|
||
impl<T: 'static> fmt::Debug for Lazy<T> { | ||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
f.pad("Lazy { .. }") | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
use crate::rt::synchronize::Synchronize; | ||
use std::{any::Any, collections::HashMap}; | ||
|
||
pub(crate) struct Set { | ||
/// Registered statics. | ||
statics: Option<HashMap<StaticKeyId, StaticValue>>, | ||
} | ||
|
||
#[derive(Eq, PartialEq, Hash, Copy, Clone)] | ||
pub(crate) struct StaticKeyId(usize); | ||
|
||
pub(crate) struct StaticValue { | ||
pub(crate) sync: Synchronize, | ||
v: Box<dyn Any>, | ||
} | ||
|
||
impl Set { | ||
/// Create an empty statics set. | ||
pub(crate) fn new() -> Set { | ||
Set { | ||
statics: Some(HashMap::new()), | ||
} | ||
} | ||
|
||
pub(crate) fn reset(&mut self) { | ||
assert!( | ||
self.statics.is_none(), | ||
"lazy_static was not dropped during execution" | ||
); | ||
self.statics = Some(HashMap::new()); | ||
} | ||
|
||
pub(crate) fn drop(&mut self) -> HashMap<StaticKeyId, StaticValue> { | ||
self.statics | ||
.take() | ||
.expect("lazy_statics were dropped twice in one execution") | ||
} | ||
|
||
pub(crate) fn get_static<T: 'static>( | ||
&mut self, | ||
key: &'static crate::lazy_static::Lazy<T>, | ||
) -> Option<&mut StaticValue> { | ||
self.statics | ||
.as_mut() | ||
.expect("attempted to access lazy_static during shutdown") | ||
.get_mut(&StaticKeyId::new(key)) | ||
} | ||
|
||
pub(crate) fn init_static<T: 'static>( | ||
&mut self, | ||
key: &'static crate::lazy_static::Lazy<T>, | ||
value: StaticValue, | ||
) { | ||
assert!(self | ||
.statics | ||
.as_mut() | ||
.expect("attempted to access lazy_static during shutdown") | ||
.insert(StaticKeyId::new(key), value) | ||
.is_none()) | ||
} | ||
} | ||
|
||
impl StaticKeyId { | ||
fn new<T>(key: &'static crate::lazy_static::Lazy<T>) -> Self { | ||
Self(key as *const _ as usize) | ||
} | ||
} | ||
|
||
impl StaticValue { | ||
pub(crate) fn new<T: 'static>(value: T) -> Self { | ||
Self { | ||
sync: Synchronize::new(), | ||
v: Box::new(value), | ||
} | ||
} | ||
|
||
pub(crate) fn get<T: 'static>(&self) -> &T { | ||
self.v | ||
.downcast_ref::<T>() | ||
.expect("lazy value must downcast to expected type") | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters