1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
//! The values of the `shard_local!` statics on one shard: one per key, made
//! on its first use there and dropped with the shard. They live in the
//! shard's own part, so they move with it when it goes to another thread
//! (`rt::handoff`), and only the thread running the shard reaches them.
use core::any::Any;
use core::cell::RefCell;
/// A key's place on a shard.
enum Place {
Empty,
/// Its value is being made: the maker used the key itself.
Making,
/// Boxed: a value never moves while the shard lives, however many keys
/// come after it.
Made(Box<dyn Any + Send>),
}
/// A shard's shard-local values, by key.
#[derive(Default)]
pub(crate) struct Locals(RefCell<Vec<Place>>);
/// Why a key has no value to lend.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum Missing {
/// Its value is being made, by a maker that used the key.
Making,
}
impl Locals {
/// The value of `key`, made with `init` on its first use here. Its look at a value made
/// already is inlined at each `with`, the hand-back's too, the making out of line.
#[inline(always)]
pub(crate) fn get_or_init<T>(&self, key: usize, init: fn() -> T) -> Result<&T, Missing>
where
T: Send + 'static,
{
if let Some(Place::Made(value)) = self.0.borrow().get(key) {
// SAFETY: see `lend`.
return Ok(unsafe { &*Self::lend(value) });
}
self.make(key, init)
}
/// [`get_or_init`](Self::get_or_init) for a key with no value made yet.
#[inline(never)]
fn make<T>(&self, key: usize, init: fn() -> T) -> Result<&T, Missing>
where
T: Send + 'static,
{
{
let mut places = self.0.borrow_mut();
if places.len() <= key {
places.resize_with(key + 1, || Place::Empty);
}
match &places[key] {
// SAFETY: see `lend`.
Place::Made(value) => return Ok(unsafe { &*Self::lend(value) }),
Place::Making => return Err(Missing::Making),
Place::Empty => places[key] = Place::Making,
}
}
// Back to empty if `init` unwinds, so a later use tries again.
struct Unmade<'a>(&'a Locals, usize, bool);
impl Drop for Unmade<'_> {
fn drop(&mut self) {
if !self.2 {
self.0.0.borrow_mut()[self.1] = Place::Empty;
}
}
}
let mut unmade = Unmade(self, key, false);
// The borrow is let go of: `init` may use other keys.
let value: Box<dyn Any + Send> = Box::new(init());
let mut places = self.0.borrow_mut();
places[key] = Place::Made(value);
unmade.2 = true;
match &places[key] {
// SAFETY: see `lend`.
Place::Made(value) => Ok(unsafe { &*Self::lend(value) }),
Place::Empty | Place::Making => Err(Missing::Making),
}
}
/// The value in `value`'s box, which may be lent for as long as the
/// shard lives: the box is never dropped or moved meanwhile, as places
/// are only ever added, and `take` needs the shard by `&mut`.
fn lend<T: 'static>(value: &Box<dyn Any + Send>) -> *const T {
match value.downcast_ref::<T>() {
Some(value) => value,
// A key is one static's, of one type.
None => unreachable_type(),
}
}
/// Takes every value out, to be dropped as the shard goes.
pub(crate) fn take(&mut self) -> Vec<Box<dyn Any + Send>> {
core::mem::take(self.0.get_mut())
.into_iter()
.filter_map(|place| match place {
Place::Made(value) => Some(value),
Place::Empty | Place::Making => None,
})
.collect()
}
}
#[cold]
#[expect(
clippy::panic,
reason = "a key belongs to one static of one type: another type is a bug of this module"
)]
fn unreachable_type() -> ! {
panic!("a shard-local key held a value of another type")
}