ic_memory/runtime/
default.rs1use super::{
2 MemoryRuntime, RuntimeBootstrapError, RuntimeConstructionError, RuntimeDiagnosticError,
3 RuntimeMemory, RuntimeOpenError, RuntimeStateError, policy::GenericRangePolicy,
4};
5use crate::{
6 CommittedAllocations, DiagnosticExport, MemoryRuntimeDoctorReport, RuntimeBootstrapPolicy,
7 physical::CommitStoreDiagnostic, registry::sealed_declaration_snapshot,
8};
9use ic_stable_structures::DefaultMemoryImpl;
10use std::{cell::RefCell, convert::Infallible, fmt::Display};
11
12thread_local! {
13 static DEFAULT_RUNTIME:
14 RefCell<Option<Result<MemoryRuntime<DefaultMemoryImpl>, RuntimeConstructionError>>> =
15 const { RefCell::new(None) };
16}
17
18fn with_default_runtime<T, E>(
19 operation: impl FnOnce(&MemoryRuntime<DefaultMemoryImpl>) -> Result<T, E>,
20) -> Result<T, E>
21where
22 E: From<RuntimeStateError>,
23{
24 match DEFAULT_RUNTIME.try_with(|runtime| {
25 let mut runtime = runtime
26 .try_borrow_mut()
27 .map_err(|_| E::from(RuntimeStateError::ReentrantAccess))?;
28 let runtime = runtime
29 .get_or_insert_with(|| MemoryRuntime::new(DefaultMemoryImpl::default()))
30 .as_ref()
31 .map_err(|error| E::from(RuntimeStateError::Construction(*error)))?;
32 operation(runtime)
33 }) {
34 Ok(result) => result,
35 Err(_) => Err(E::from(RuntimeStateError::Unavailable)),
36 }
37}
38
39fn with_default_runtime_mut<T, E>(
40 operation: impl FnOnce(&mut MemoryRuntime<DefaultMemoryImpl>) -> Result<T, E>,
41) -> Result<T, E>
42where
43 E: From<RuntimeStateError>,
44{
45 match DEFAULT_RUNTIME.try_with(|runtime| {
46 let mut runtime = runtime
47 .try_borrow_mut()
48 .map_err(|_| E::from(RuntimeStateError::ReentrantAccess))?;
49 let runtime = runtime
50 .get_or_insert_with(|| MemoryRuntime::new(DefaultMemoryImpl::default()))
51 .as_mut()
52 .map_err(|error| E::from(RuntimeStateError::Construction(*error)))?;
53 operation(runtime)
54 }) {
55 Ok(result) => result,
56 Err(_) => Err(E::from(RuntimeStateError::Unavailable)),
57 }
58}
59
60fn with_existing_default_runtime<T, E>(
63 operation: impl FnOnce(Option<&MemoryRuntime<DefaultMemoryImpl>>) -> Result<T, E>,
64) -> Result<T, E>
65where
66 E: From<RuntimeStateError>,
67{
68 DEFAULT_RUNTIME
69 .try_with(|runtime| {
70 let runtime = runtime
71 .try_borrow()
72 .map_err(|_| E::from(RuntimeStateError::ReentrantAccess))?;
73 let existing = runtime
74 .as_ref()
75 .map(|runtime| {
76 runtime
77 .as_ref()
78 .map_err(|error| E::from(RuntimeStateError::Construction(*error)))
79 })
80 .transpose()?;
81 operation(existing)
82 })
83 .map_err(|_| E::from(RuntimeStateError::Unavailable))?
84}
85
86pub fn is_default_memory_manager_bootstrapped() -> Result<bool, RuntimeStateError> {
92 with_existing_default_runtime(|runtime| Ok(runtime.is_some_and(MemoryRuntime::is_bootstrapped)))
93}
94
95pub fn committed_allocations() -> Result<CommittedAllocations, RuntimeOpenError> {
101 with_existing_default_runtime(|runtime| {
102 runtime
103 .ok_or(RuntimeOpenError::NotBootstrapped)?
104 .committed_allocations()
105 .cloned()
106 })
107}
108
109pub fn bootstrap_default_memory_manager()
111-> Result<CommittedAllocations, RuntimeBootstrapError<Infallible>> {
112 bootstrap_default_memory_manager_with_policy(&GenericRangePolicy)
113}
114
115pub fn bootstrap_default_memory_manager_with_policy<P: RuntimeBootstrapPolicy>(
122 policy: &P,
123) -> Result<CommittedAllocations, RuntimeBootstrapError<P::Error>> {
124 let declarations = sealed_declaration_snapshot()?;
125 with_default_runtime_mut(|runtime| runtime.bootstrap(&declarations, policy).cloned())
126}
127
128pub fn open_default_memory_manager_memory(
130 stable_key: &str,
131 id: u8,
132) -> Result<RuntimeMemory<DefaultMemoryImpl>, RuntimeOpenError> {
133 with_default_runtime(|runtime| runtime.open_memory(stable_key, id))
134}
135
136pub fn default_memory_manager_diagnostic_export() -> Result<DiagnosticExport, RuntimeDiagnosticError>
138{
139 with_default_runtime(MemoryRuntime::diagnostic_export)
140}
141
142pub fn default_memory_manager_commit_recovery_diagnostic()
144-> Result<CommitStoreDiagnostic, RuntimeDiagnosticError> {
145 with_default_runtime(MemoryRuntime::commit_recovery_diagnostic)
146}
147
148pub fn default_memory_manager_doctor_report()
150-> Result<MemoryRuntimeDoctorReport, RuntimeDiagnosticError> {
151 default_memory_manager_doctor_report_with_policy(&GenericRangePolicy)
152}
153
154pub fn default_memory_manager_doctor_report_with_policy<P>(
156 policy: &P,
157) -> Result<MemoryRuntimeDoctorReport, RuntimeDiagnosticError>
158where
159 P: RuntimeBootstrapPolicy,
160 P::Error: Display,
161{
162 let declarations = sealed_declaration_snapshot()?;
163 with_default_runtime(|runtime| Ok(runtime.doctor_report(&declarations, policy)))
164}
165
166#[cfg(test)]
167pub(super) fn with_default_runtime_borrowed(
168 operation: impl FnOnce() -> Result<(), RuntimeStateError>,
169) -> Result<(), RuntimeStateError> {
170 DEFAULT_RUNTIME.with(|runtime| {
171 let _borrow = runtime.borrow_mut();
172 operation()
173 })
174}
175
176pub fn default_memory_manager_memory_allocations()
182-> Result<super::MemoryAllocations, RuntimeDiagnosticError> {
183 with_existing_default_runtime(|runtime| {
184 runtime
185 .ok_or(RuntimeDiagnosticError::NotBootstrapped)?
186 .memory_allocations()
187 })
188}
189
190pub fn bootstrap_default_memory_manager_with_config<P: RuntimeBootstrapPolicy>(
199 config: super::MemoryManagerConfig,
200 policy: &P,
201) -> Result<CommittedAllocations, RuntimeBootstrapError<P::Error>> {
202 DEFAULT_RUNTIME
203 .try_with(|runtime| {
204 let mut runtime = runtime
205 .try_borrow_mut()
206 .map_err(|_| RuntimeStateError::ReentrantAccess)?;
207 let runtime = runtime
208 .get_or_insert_with(|| {
209 MemoryRuntime::new_with_config(DefaultMemoryImpl::default(), config)
210 })
211 .as_mut()
212 .map_err(|error| RuntimeStateError::Construction(*error))?;
213 super::check_bucket_size(runtime.bucket_size_pages, config)
214 .map_err(RuntimeStateError::Construction)?;
215 let declarations = sealed_declaration_snapshot()?;
216 runtime.bootstrap(&declarations, policy).cloned()
217 })
218 .map_err(|_| RuntimeStateError::Unavailable)?
219}
220
221#[cfg(test)]
222mod tests {
223 use super::*;
224
225 #[test]
226 fn observations_leave_an_absent_runtime_absent() {
227 std::thread::spawn(|| {
228 for _ in 0..2 {
229 assert!(!is_default_memory_manager_bootstrapped().unwrap());
230 assert_eq!(
231 committed_allocations(),
232 Err(RuntimeOpenError::NotBootstrapped)
233 );
234 assert!(matches!(
235 default_memory_manager_memory_allocations(),
236 Err(RuntimeDiagnosticError::NotBootstrapped)
237 ));
238 DEFAULT_RUNTIME.with(|runtime| assert!(runtime.borrow().is_none()));
239 }
240 })
241 .join()
242 .unwrap();
243 }
244
245 #[test]
246 fn observations_preserve_unbootstrapped_configuration() {
247 std::thread::spawn(|| {
248 let config = super::super::MemoryManagerConfig::new(16).unwrap();
249 DEFAULT_RUNTIME.with(|runtime| {
250 *runtime.borrow_mut() = Some(MemoryRuntime::new_with_config(
251 DefaultMemoryImpl::default(),
252 config,
253 ));
254 });
255 let before = default_memory_manager_memory_allocations().unwrap();
256 assert!(!is_default_memory_manager_bootstrapped().unwrap());
257 assert_eq!(
258 committed_allocations(),
259 Err(RuntimeOpenError::NotBootstrapped)
260 );
261 assert_eq!(before.bucket_size_pages, 16);
262 assert_eq!(default_memory_manager_memory_allocations().unwrap(), before);
263 })
264 .join()
265 .unwrap();
266 }
267
268 #[test]
269 fn observations_preserve_cached_construction_failure() {
270 std::thread::spawn(|| {
271 let error = RuntimeConstructionError::ForeignMemory {
272 observed_magic: *b"BAD",
273 };
274 DEFAULT_RUNTIME.with(|runtime| *runtime.borrow_mut() = Some(Err(error)));
275 assert_eq!(
276 is_default_memory_manager_bootstrapped(),
277 Err(RuntimeStateError::Construction(error))
278 );
279 assert_eq!(
280 committed_allocations(),
281 Err(RuntimeOpenError::State(RuntimeStateError::Construction(
282 error
283 )))
284 );
285 assert!(matches!(
286 default_memory_manager_memory_allocations(),
287 Err(RuntimeDiagnosticError::State(RuntimeStateError::Construction(cause)))
288 if cause == error
289 ));
290 DEFAULT_RUNTIME.with(|runtime| {
291 assert!(matches!(runtime.borrow().as_ref(), Some(Err(cause)) if *cause == error));
292 });
293 })
294 .join()
295 .unwrap();
296 }
297}