-
Notifications
You must be signed in to change notification settings - Fork 33
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement
Once
and generalize thread-local storage map for reuse (#49)
`Once` is a synchronization primitive often used in a static variable to ensure some initialization logic runs at most once in the execution of a program. To ensure that we support this pattern, we implement a new global storage map that is empied across executions, and use it to ensure that a `Once` cell's state resets across test executions. This also means that multiple Shuttle tests running concurrently (e.g., under the cargo test runner with a default config) will see independent instances of a static Once cell and be properly isolated from each other. The storage map shares common logic with the TLS map we already have, so this PR factors that map out into a single abstraction. In a future PR we'll use Once to implement lazy_static, which will also store its values into the global storage map.
- Loading branch information
1 parent
31f169d
commit 7e10ce3
Showing
9 changed files
with
559 additions
and
77 deletions.
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
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 |
---|---|---|
@@ -1,5 +1,6 @@ | ||
pub(crate) mod execution; | ||
mod failure; | ||
pub(crate) mod runner; | ||
pub(crate) mod storage; | ||
pub(crate) mod task; | ||
pub(crate) mod thread; |
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,60 @@ | ||
use std::any::Any; | ||
use std::collections::{HashMap, VecDeque}; | ||
|
||
/// A unique identifier for a storage slot | ||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
pub(crate) struct StorageKey(pub usize, pub usize); // (identifier, type) | ||
|
||
/// A map of storage values. | ||
/// | ||
/// We remember the insertion order into the storage HashMap so that destruction is deterministic. | ||
/// Values are Option<_> because we need to be able to incrementally destruct them, as it's valid | ||
/// for TLS destructors to initialize new TLS slots. When a slot is destructed, its key is removed | ||
/// from `order` and its value is replaced with None. | ||
pub(crate) struct StorageMap { | ||
locals: HashMap<StorageKey, Option<Box<dyn Any>>>, | ||
order: VecDeque<StorageKey>, | ||
} | ||
|
||
impl StorageMap { | ||
pub fn new() -> Self { | ||
Self { | ||
locals: HashMap::new(), | ||
order: VecDeque::new(), | ||
} | ||
} | ||
|
||
pub fn get<T: 'static>(&self, key: StorageKey) -> Option<Result<&T, AlreadyDestructedError>> { | ||
self.locals.get(&key).map(|val| { | ||
val.as_ref() | ||
.map(|val| { | ||
Ok(val | ||
.downcast_ref::<T>() | ||
.expect("local value must downcast to expected type")) | ||
}) | ||
.unwrap_or(Err(AlreadyDestructedError)) | ||
}) | ||
} | ||
|
||
pub fn init<T: 'static>(&mut self, key: StorageKey, value: T) { | ||
let result = self.locals.insert(key, Some(Box::new(value))); | ||
assert!(result.is_none(), "cannot reinitialize a storage slot"); | ||
self.order.push_back(key); | ||
} | ||
|
||
/// Return ownership of the next still-initialized storage slot. | ||
pub fn pop(&mut self) -> Option<Box<dyn Any>> { | ||
let key = self.order.pop_front()?; | ||
let value = self | ||
.locals | ||
.get_mut(&key) | ||
.expect("keys in `order` must exist") | ||
.take() | ||
.expect("keys in `order` must not yet be destructed"); | ||
Some(value) | ||
} | ||
} | ||
|
||
#[derive(Debug)] | ||
#[non_exhaustive] | ||
pub(crate) struct AlreadyDestructedError; |
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
Oops, something went wrong.