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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
//! Per-thread value caching, consolidated from the duplicated
//! nightly-`#[thread_local]` / stable-`thread_local!` patterns that themis
//! (`CACHED_NODE`) and mnemosyne (`CACHED_CPU_ID`) carried independently.
//!
//! Thread-local statics cannot be expressed as a generic type — the storage
//! must be declared per site with the toolchain-appropriate attribute — so the
//! single authoritative implementation is the
//! [`thread_cached!`](crate::thread_cached!) macro, which
//! expands to a module owning the cfg-paired storage plus a typed accessor
//! surface. Boilerplate generation is the sanctioned macro use here: the
//! variation (nightly fast path vs stable fallback) is a *declaration-site*
//! dimension that traits and generics cannot capture.
//!
//! The cache always stores `Option<T>` — "uninitialized" is a real state, not
//! a sentinel value carved out of `T`'s domain.
/// Declares a per-thread cached value with `get_or_init` / `set` / `get` /
/// `clear` accessors.
///
/// Expands to a module named `$name` containing the thread-local storage and
/// four functions:
///
/// - `get_or_init(init: impl FnOnce() -> T) -> T` — returns the cached value,
/// computing and caching it on first access from the calling thread.
/// - `set(value: T)` — overwrites the calling thread's cached value.
/// - `get() -> Option<T>` — reads the calling thread's cached value without
/// initializing it.
/// - `clear()` — returns the calling thread's cache to the uninitialized state.
///
/// `T` must be `Copy`.
///
/// # Consumer requirements
///
/// On nightly toolchains the expansion uses `#[thread_local] Cell<Option<T>>`
/// statics, so the consuming crate must carry:
///
/// - a build script emitting `cargo:rustc-check-cfg=cfg(nightly_tls_active)`
/// and `cargo:rustc-cfg=nightly_tls_active` when the compiler is nightly
/// (themis and mnemosyne-local already do), and
/// - `#![cfg_attr(nightly_tls_active, feature(thread_local))]` at crate root.
///
/// On stable the expansion falls back to `std::thread_local!` with the same
/// `Cell<Option<T>>` payload, so a stable consumer must link `std`.
///
/// # Example
///
/// ```
/// #![cfg_attr(nightly_tls_active, feature(thread_local))]
/// melinoe::thread_cached! {
/// /// Cached worker shard index for the calling thread.
/// pub mod cached_shard: u32;
/// }
///
/// assert_eq!(cached_shard::get_or_init(|| 7), 7);
/// cached_shard::set(11);
/// assert_eq!(cached_shard::get(), Some(11));
/// cached_shard::clear();
/// assert_eq!(cached_shard::get(), None);
/// assert_eq!(cached_shard::get_or_init(|| 13), 13);
/// ```