rune/runtime/
env.rs

1//! Thread-local access to the current context.
2//!
3//! This provides access to functions to call specific protocol functions, like:
4//! * [super::Value::into_iter]
5//! * [super::Value::debug_fmt]
6//! * [super::Value::into_type_name]
7//!
8//! See the corresponding function for documentation.
9
10use core::mem::ManuallyDrop;
11use core::ptr::NonNull;
12
13#[cfg_attr(feature = "std", path = "env/std.rs")]
14mod no_std;
15
16use ::rust_alloc::sync::Arc;
17
18use crate::runtime::vm_diagnostics::VmDiagnosticsObj;
19use crate::runtime::{RuntimeContext, Unit, VmErrorKind, VmResult};
20
21/// Access shared parts of the environment.
22///
23/// This does not take ownership of the environment, so the environment can be
24/// recursively accessed.
25pub(crate) fn shared<F, T>(c: F) -> VmResult<T>
26where
27    F: FnOnce(&Arc<RuntimeContext>, &Arc<Unit>) -> VmResult<T>,
28{
29    let env = self::no_std::rune_env_get();
30
31    let Env {
32        context: Some(context),
33        unit: Some(unit),
34        ..
35    } = env
36    else {
37        return VmResult::err(VmErrorKind::MissingInterfaceEnvironment);
38    };
39
40    // Safety: context and unit can only be registered publicly through
41    // [`Guard`], which makes sure that they are live for the duration of the
42    // registration.
43    let context = unsafe { ManuallyDrop::new(Arc::from_raw(context.as_ptr().cast_const())) };
44    let unit = unsafe { ManuallyDrop::new(Arc::from_raw(unit.as_ptr().cast_const())) };
45    c(&context, &unit)
46}
47
48/// Call the given closure with access to the checked environment accessing it
49/// exclusively.
50///
51/// This takes ownership of the environment, so recursive calls are not
52/// supported.
53pub(crate) fn exclusive<F, T>(c: F) -> VmResult<T>
54where
55    F: FnOnce(&Arc<RuntimeContext>, &Arc<Unit>, Option<&mut VmDiagnosticsObj>) -> VmResult<T>,
56{
57    let guard = Guard {
58        env: self::no_std::rune_env_replace(Env::null()),
59    };
60
61    let Env {
62        context: Some(context),
63        unit: Some(unit),
64        ..
65    } = guard.env
66    else {
67        return VmResult::err(VmErrorKind::MissingInterfaceEnvironment);
68    };
69
70    // Safety: context and unit can only be registered publicly through
71    // [`Guard`], which makes sure that they are live for the duration of the
72    // registration.
73    let context = unsafe { ManuallyDrop::new(Arc::from_raw(context.as_ptr().cast_const())) };
74    let unit = unsafe { ManuallyDrop::new(Arc::from_raw(unit.as_ptr().cast_const())) };
75    let diagnostics = match guard.env.diagnostics {
76        Some(mut d) => Some(unsafe { d.as_mut() }),
77        None => None,
78    };
79
80    c(&context, &unit, diagnostics)
81}
82
83pub(crate) struct Guard {
84    env: Env,
85}
86
87impl Guard {
88    /// Construct a new environment guard with the given context and unit.
89    ///
90    /// # Safety
91    ///
92    /// The returned guard must be dropped before the pointed to elements are.
93    pub(crate) fn new(
94        context: Arc<RuntimeContext>,
95        unit: Arc<Unit>,
96        diagnostics: Option<NonNull<VmDiagnosticsObj>>,
97    ) -> Guard {
98        let env = unsafe {
99            self::no_std::rune_env_replace(Env {
100                context: Some(NonNull::new_unchecked(Arc::into_raw(context).cast_mut())),
101                unit: Some(NonNull::new_unchecked(Arc::into_raw(unit).cast_mut())),
102                diagnostics,
103            })
104        };
105
106        Guard { env }
107    }
108}
109
110impl Drop for Guard {
111    fn drop(&mut self) {
112        let old_env = self::no_std::rune_env_replace(self.env);
113
114        unsafe {
115            if let Some(context) = old_env.context {
116                drop(Arc::from_raw(context.as_ptr().cast_const()));
117            }
118
119            if let Some(unit) = old_env.unit {
120                drop(Arc::from_raw(unit.as_ptr().cast_const()));
121            }
122        }
123    }
124}
125
126#[derive(Debug, Clone, Copy)]
127struct Env {
128    context: Option<NonNull<RuntimeContext>>,
129    unit: Option<NonNull<Unit>>,
130    diagnostics: Option<NonNull<VmDiagnosticsObj>>,
131}
132
133impl Env {
134    const fn null() -> Self {
135        Self {
136            context: None,
137            unit: None,
138            diagnostics: None,
139        }
140    }
141}