Skip to main content

bdwgc_alloc/
lib.rs

1#![doc = include_str!("../README.md")]
2#![no_std]
3
4mod error;
5
6use bdwgc_alloc_sys::{
7    GC_SUCCESS, GC_alloc_lock, GC_alloc_unlock, GC_allow_register_threads, GC_free, GC_gcollect,
8    GC_get_stack_base, GC_init, GC_malloc, GC_realloc, GC_register_finalizer,
9    GC_register_my_thread, GC_set_stackbottom, GC_stack_base, GC_unregister_my_thread,
10};
11use core::{
12    alloc::{GlobalAlloc, Layout},
13    ffi::c_void,
14    ptr::null_mut,
15};
16
17/// An allocator.
18pub struct Allocator;
19
20impl Allocator {
21    /// Locks a collector.
22    pub fn lock() {
23        unsafe { GC_alloc_lock() }
24    }
25
26    /// Unlocks a collector.
27    pub fn unlock() {
28        unsafe { GC_alloc_unlock() }
29    }
30
31    /// Initializes a collector.
32    ///
33    /// # Safety
34    ///
35    /// This function must be called in a main thread.
36    pub unsafe fn initialize() {
37        unsafe {
38            GC_init();
39            GC_allow_register_threads();
40        }
41    }
42
43    /// Registers a current thread to a collector.
44    ///
45    /// # Safety
46    ///
47    /// This function must not be called in a main thread.
48    pub unsafe fn register_current_thread() -> Result<(), error::Error> {
49        let mut base = GC_stack_base {
50            mem_base: null_mut(),
51        };
52
53        if unsafe { GC_get_stack_base(&mut base) } != GC_SUCCESS {
54            return Err(error::Error::new("failed to get stack base"));
55        } else if unsafe { GC_register_my_thread(&base) } != GC_SUCCESS {
56            return Err(error::Error::new("failed to register a thread for GC"));
57        }
58
59        Ok(())
60    }
61
62    /// Sets a bottom of a stack.
63    ///
64    /// You do not have to call this function in most cases.
65    /// A collector detects the bottom on initialization automatically.
66    ///
67    /// # Safety
68    ///
69    /// The bottom address must be valid.
70    pub unsafe fn set_stack_bottom(bottom: *const u8) {
71        unsafe {
72            GC_set_stackbottom(
73                null_mut(),
74                &GC_stack_base {
75                    mem_base: bottom.cast_mut().cast(),
76                },
77            )
78        }
79    }
80
81    /// Unregisters a current thread from a collector.
82    ///
83    /// # Safety
84    ///
85    /// The thread must be registered already.
86    pub unsafe fn unregister_current_thread() {
87        unsafe { GC_unregister_my_thread() };
88    }
89
90    /// Runs a garbage collection forcibly.
91    pub fn force_collect() {
92        unsafe { GC_gcollect() }
93    }
94
95    /// Registers a finalizer of an object.
96    ///
97    /// # Safety
98    ///
99    /// The given finalizer must not be null and handle pointers properly.
100    pub unsafe fn register_finalizer(
101        ptr: *const c_void,
102        finalizer: extern "C" fn(*mut c_void, *mut c_void),
103        client_data: *const c_void,
104    ) {
105        unsafe {
106            GC_register_finalizer(
107                ptr.cast_mut(),
108                Some(finalizer),
109                client_data.cast_mut(),
110                null_mut(),
111                null_mut(),
112            )
113        };
114    }
115}
116
117unsafe impl GlobalAlloc for Allocator {
118    unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
119        (unsafe { GC_malloc(layout.size()) }) as *mut u8
120    }
121
122    unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {
123        unsafe { GC_free(ptr as *mut c_void) }
124    }
125
126    unsafe fn realloc(&self, ptr: *mut u8, _layout: Layout, size: usize) -> *mut u8 {
127        (unsafe { GC_realloc(ptr as *mut c_void, size) }) as *mut u8
128    }
129}