Skip to main content

pylon_shimload/
runtime_impl.rs

1use std::sync::{Mutex, OnceLock};
2
3use crate::{shim_loader, PylonError, PylonResult};
4
5#[derive(Debug)]
6struct RuntimeState {
7    leases: usize,
8    initialized: bool,
9}
10
11impl RuntimeState {
12    fn new() -> Self {
13        Self {
14            leases: 0,
15            initialized: false,
16        }
17    }
18}
19
20static RUNTIME_STATE: OnceLock<Mutex<RuntimeState>> = OnceLock::new();
21
22fn state() -> &'static Mutex<RuntimeState> {
23    RUNTIME_STATE.get_or_init(|| Mutex::new(RuntimeState::new()))
24}
25
26fn lock_state() -> std::sync::MutexGuard<'static, RuntimeState> {
27    match state().lock() {
28        Ok(guard) => guard,
29        Err(poisoned) => poisoned.into_inner(),
30    }
31}
32
33pub(crate) fn acquire_runtime() -> PylonResult<RuntimeLease> {
34    let mut st = lock_state();
35    if !st.initialized {
36        let shim = shim_loader::shim_or_err()?;
37        unsafe { (shim.pylon_initialize)() };
38        st.initialized = true;
39    }
40    st.leases += 1;
41    Ok(RuntimeLease { active: true })
42}
43
44fn release_runtime_internal() {
45    let mut st = lock_state();
46    if st.leases == 0 {
47        return;
48    }
49
50    st.leases -= 1;
51    if st.leases == 0 && st.initialized {
52        unsafe { (shim_loader::shim().pylon_terminate)(1) };
53        st.initialized = false;
54    }
55}
56
57pub(crate) fn runtime_version() -> PylonResult<crate::PylonVersion> {
58    let _lease = acquire_runtime()?;
59
60    let mut major = 0u32;
61    let mut minor = 0u32;
62    let mut subminor = 0u32;
63    let mut build = 0u32;
64
65    unsafe {
66        (shim_loader::shim().pylon_get_version)(&mut major, &mut minor, &mut subminor, &mut build)
67    };
68
69    Ok(crate::PylonVersion {
70        major,
71        minor,
72        subminor,
73        build,
74    })
75}
76
77pub fn shutdown() -> PylonResult<()> {
78    let mut st = lock_state();
79    if st.leases != 0 {
80        return Err(PylonError::Msg(
81            "Cannot shutdown runtime while handles are alive".to_string(),
82        ));
83    }
84    if st.initialized {
85        unsafe { (shim_loader::shim().pylon_terminate)(1) };
86        st.initialized = false;
87    }
88    Ok(())
89}
90
91/// Keeps the Pylon runtime alive for explicit lifecycle management.
92///
93/// Obtained via [`crate::runtime::init`]; see that function's documentation
94/// for when this type is needed.
95pub struct RuntimeGuard {
96    _lease: RuntimeLease,
97}
98
99impl RuntimeGuard {
100    pub fn new() -> PylonResult<Self> {
101        Ok(Self {
102            _lease: acquire_runtime()?,
103        })
104    }
105}
106
107pub(crate) struct RuntimeLease {
108    active: bool,
109}
110
111impl Clone for RuntimeLease {
112    fn clone(&self) -> Self {
113        if self.active {
114            let mut st = lock_state();
115            st.leases += 1;
116        }
117        Self {
118            active: self.active,
119        }
120    }
121}
122
123impl Drop for RuntimeLease {
124    fn drop(&mut self) {
125        if self.active {
126            self.active = false;
127            release_runtime_internal();
128        }
129    }
130}