avmnif_rs/
context.rs

1//! Context management for AtomVM ports and NIFs
2//! 
3//! Provides safe wrappers around AtomVM's context structures
4
5use alloc::boxed::Box;
6use crate::term::Term;
7use core::ffi::c_void;
8
9/// Opaque context structure that matches AtomVM's internal representation
10#[repr(C)]
11pub struct Context {
12    _private: [u8; 0],
13}
14
15/// Global AtomVM context
16pub type GlobalContext = c_void;
17
18// AtomVM Context API FFI declarations
19extern "C" {
20    /// Create a new port context
21    pub fn create_port_context(global: *const GlobalContext) -> *mut Context;
22    
23    /// Destroy a port context and clean up resources
24    pub fn destroy_port_context(ctx: *mut Context);
25    
26    /// Check if a port is still alive
27    pub fn port_is_alive(ctx: *const Context) -> i32;
28    
29    /// Get platform data from context
30    pub fn context_get_platform_data(ctx: *const Context) -> *mut c_void;
31    
32    /// Set platform data in context
33    pub fn context_set_platform_data(ctx: *mut Context, data: *mut c_void);
34    
35    /// Get user data from context (for storing Erlang terms)
36    pub fn context_get_user_data(ctx: *const Context) -> u64;
37    
38    /// Set user data in context
39    pub fn context_set_user_data(ctx: *mut Context, data: u64);
40    
41    /// Get the global context pointer (for ISR use)
42    pub fn global_context_ptr() -> *mut GlobalContext;
43}
44
45/// Context extension trait for safe platform data management
46pub trait ContextExt {
47    /// Set platform-specific data in the context
48    unsafe fn set_platform_data(&mut self, data: *mut c_void);
49    
50    /// Get platform-specific data from the context
51    unsafe fn get_platform_data(&self) -> *mut c_void;
52    
53    /// Set user data (for storing Erlang terms as raw u64)
54    unsafe fn set_user_data(&mut self, data: u64);
55    
56    /// Get user data
57    unsafe fn get_user_data(&self) -> u64;
58    
59    /// Safely cast platform data to a specific type
60    unsafe fn get_platform_data_as<T>(&self) -> *mut T {
61        self.get_platform_data() as *mut T
62    }
63    
64    /// Safely set platform data from a boxed value
65    unsafe fn set_platform_data_box<T>(&mut self, data: Box<T>) {
66        self.set_platform_data(Box::into_raw(data) as *mut c_void);
67    }
68    
69    /// Safely take ownership of platform data back as a box
70    unsafe fn take_platform_data_box<T>(&mut self) -> Option<Box<T>> {
71        let ptr = self.get_platform_data() as *mut T;
72        if ptr.is_null() {
73            None
74        } else {
75            self.set_platform_data(core::ptr::null_mut());
76            Some(Box::from_raw(ptr))
77        }
78    }
79    
80    /// Set user data from a Term
81    unsafe fn set_user_term(&mut self, term: Term) {
82        self.set_user_data(term.raw().try_into().unwrap());
83    }
84    
85    /// Get user data as a Term
86    unsafe fn get_user_term(&self) -> Term {
87        Term::from_raw(self.get_user_data().try_into().unwrap())
88    }
89    
90    /// Check if platform data is set
91    fn has_platform_data(&self) -> bool {
92        unsafe { !self.get_platform_data().is_null() }
93    }
94    
95    /// Check if user data is set
96    fn has_user_data(&self) -> bool {
97        unsafe { self.get_user_data() != 0 }
98    }
99}
100
101impl ContextExt for Context {
102    unsafe fn set_platform_data(&mut self, data: *mut c_void) {
103        context_set_platform_data(self, data);
104    }
105    
106    unsafe fn get_platform_data(&self) -> *mut c_void {
107        context_get_platform_data(self)
108    }
109    
110    unsafe fn set_user_data(&mut self, data: u64) {
111        context_set_user_data(self, data);
112    }
113    
114    unsafe fn get_user_data(&self) -> u64 {
115        context_get_user_data(self)
116    }
117}
118
119/// Safe wrapper for creating port contexts
120pub fn create_port_context_safe(global: &GlobalContext) -> *mut Context {
121    unsafe { create_port_context(global as *const GlobalContext) }
122}
123
124/// Safe wrapper for destroying port contexts
125pub fn destroy_port_context_safe(ctx: *mut Context) {
126    if !ctx.is_null() {
127        unsafe { destroy_port_context(ctx) }
128    }
129}
130
131/// Check if a port is still alive
132pub fn is_port_alive(ctx: &Context) -> bool {
133    unsafe { port_is_alive(ctx as *const Context) != 0 }
134}
135
136/// Get the global context for ISR use
137pub fn get_global_context() -> *mut GlobalContext {
138    unsafe { global_context_ptr() }
139}
140
141/// Port builder for ergonomic port creation
142pub struct PortBuilder<T> {
143    data: T,
144}
145
146impl<T> PortBuilder<T> {
147    /// Create a new port builder with the given data
148    pub fn new(data: T) -> Self {
149        Self { data }
150    }
151    
152    /// Build the port context with the data
153    pub fn build(self, global: &GlobalContext) -> *mut Context {
154        let ctx = create_port_context_safe(global);
155        if !ctx.is_null() {
156            unsafe {
157                let boxed_data = Box::new(self.data);
158                (*ctx).set_platform_data_box(boxed_data);
159            }
160        }
161        ctx
162    }
163    
164    /// Build the port context and also set user data
165    pub fn build_with_user_data(self, global: &GlobalContext, user_data: u64) -> *mut Context {
166        let ctx = self.build(global);
167        if !ctx.is_null() {
168            unsafe {
169                (*ctx).set_user_data(user_data);
170            }
171        }
172        ctx
173    }
174    
175    /// Build the port context and also set user term
176    pub fn build_with_user_term(self, global: &GlobalContext, user_term: Term) -> *mut Context {
177        let ctx = self.build(global);
178        if !ctx.is_null() {
179            unsafe {
180                (*ctx).set_user_term(user_term);
181            }
182        }
183        ctx
184    }
185}
186
187/// RAII wrapper for automatic context cleanup
188pub struct ContextGuard {
189    ctx: *mut Context,
190}
191
192impl ContextGuard {
193    /// Create a new context guard
194    /// 
195    /// # Safety
196    /// The caller must ensure the context pointer is valid
197    pub unsafe fn new(ctx: *mut Context) -> Self {
198        Self { ctx }
199    }
200    
201    /// Get a reference to the context
202    pub fn context(&self) -> &Context {
203        unsafe { &*self.ctx }
204    }
205    
206    /// Get a mutable reference to the context
207    pub fn context_mut(&mut self) -> &mut Context {
208        unsafe { &mut *self.ctx }
209    }
210    
211    /// Release the context without destroying it
212    pub fn release(mut self) -> *mut Context {
213        let ctx = self.ctx;
214        self.ctx = core::ptr::null_mut();
215        ctx
216    }
217    
218    /// Check if the guard holds a valid context
219    pub fn is_valid(&self) -> bool {
220        !self.ctx.is_null()
221    }
222}
223
224impl Drop for ContextGuard {
225    fn drop(&mut self) {
226        destroy_port_context_safe(self.ctx);
227    }
228}
229
230/// Context manager for handling multiple contexts
231pub struct ContextManager {
232    contexts: alloc::vec::Vec<*mut Context>,
233}
234
235impl ContextManager {
236    /// Create a new context manager
237    pub fn new() -> Self {
238        Self {
239            contexts: alloc::vec::Vec::new(),
240        }
241    }
242    
243    /// Add a context to be managed
244    pub fn add_context(&mut self, ctx: *mut Context) {
245        if !ctx.is_null() {
246            self.contexts.push(ctx);
247        }
248    }
249    
250    /// Remove a context from management (doesn't destroy it)
251    pub fn remove_context(&mut self, ctx: *mut Context) -> bool {
252        if let Some(pos) = self.contexts.iter().position(|&x| x == ctx) {
253            self.contexts.remove(pos);
254            true
255        } else {
256            false
257        }
258    }
259    
260    /// Get the number of managed contexts
261    pub fn count(&self) -> usize {
262        self.contexts.len()
263    }
264    
265    /// Check if a context is being managed
266    pub fn contains(&self, ctx: *mut Context) -> bool {
267        self.contexts.contains(&ctx)
268    }
269    
270    /// Destroy all managed contexts
271    pub fn destroy_all(&mut self) {
272        for &ctx in &self.contexts {
273            destroy_port_context_safe(ctx);
274        }
275        self.contexts.clear();
276    }
277}
278
279impl Drop for ContextManager {
280    fn drop(&mut self) {
281        self.destroy_all();
282    }
283}
284
285impl Default for ContextManager {
286    fn default() -> Self {
287        Self::new()
288    }
289}
290
291/// Trait for types that can be stored as platform data
292pub trait PlatformData: Sized {
293    /// Called when the platform data is being cleaned up
294    fn cleanup(&mut self) {}
295    
296    /// Store this data in a context
297    unsafe fn store_in_context(self, ctx: &mut Context) {
298        ctx.set_platform_data_box(Box::new(self));
299    }
300    
301    /// Retrieve this data from a context
302    unsafe fn from_context(ctx: &Context) -> Option<&Self> {
303        let ptr = ctx.get_platform_data_as::<Self>();
304        if ptr.is_null() {
305            None
306        } else {
307            Some(&*ptr)
308        }
309    }
310    
311    /// Retrieve this data mutably from a context
312    unsafe fn from_context_mut(ctx: &mut Context) -> Option<&mut Self> {
313        let ptr = ctx.get_platform_data_as::<Self>();
314        if ptr.is_null() {
315            None
316        } else {
317            Some(&mut *ptr)
318        }
319    }
320    
321    /// Take ownership of this data from a context
322    unsafe fn take_from_context(ctx: &mut Context) -> Option<Self> {
323        ctx.take_platform_data_box::<Self>().map(|boxed| *boxed)
324    }
325}
326
327/// Macro for implementing PlatformData with custom cleanup
328#[macro_export]
329macro_rules! impl_platform_data {
330    ($type:ty) => {
331        impl $crate::context::PlatformData for $type {}
332    };
333    ($type:ty, cleanup = $cleanup:expr) => {
334        impl $crate::context::PlatformData for $type {
335            fn cleanup(&mut self) {
336                $cleanup(self)
337            }
338        }
339    };
340}
341
342/// Helper functions for common context operations
343
344/// Safely execute a function with platform data
345pub fn with_platform_data<T, R, F>(ctx: &Context, f: F) -> Option<R>
346where
347    T: PlatformData,
348    F: FnOnce(&T) -> R,
349{
350    unsafe {
351        T::from_context(ctx).map(f)
352    }
353}
354
355/// Safely execute a function with mutable platform data
356pub fn with_platform_data_mut<T, R, F>(ctx: &mut Context, f: F) -> Option<R>
357where
358    T: PlatformData,
359    F: FnOnce(&mut T) -> R,
360{
361    unsafe {
362        T::from_context_mut(ctx).map(f)
363    }
364}
365
366/// Initialize platform data in a context
367pub fn init_platform_data<T: PlatformData>(ctx: &mut Context, data: T) {
368    unsafe {
369        data.store_in_context(ctx);
370    }
371}
372
373/// Clean up platform data from a context
374pub fn cleanup_platform_data<T: PlatformData>(ctx: &mut Context) -> Option<T> {
375    unsafe {
376        T::take_from_context(ctx)
377    }
378}