1use ocaml_boxroot_sys::boxroot_teardown;
5use std::{
6 cell::UnsafeCell,
7 marker::PhantomData,
8 ops::{Deref, DerefMut},
9};
10
11use crate::{memory::OCamlRef, value::OCaml};
12
13thread_local! {
14 static TLS_RUNTIME: UnsafeCell<OCamlRuntime> = const { UnsafeCell::new({
15 OCamlRuntime { _not_send_sync: PhantomData }
16 })};
17}
18
19pub struct OCamlRuntimeStartupGuard {
21 _not_send_sync: PhantomData<*const ()>,
22}
23
24impl Deref for OCamlRuntimeStartupGuard {
25 type Target = OCamlRuntime;
26
27 fn deref(&self) -> &OCamlRuntime {
28 unsafe { internal::recover_runtime_handle() }
29 }
30}
31
32impl DerefMut for OCamlRuntimeStartupGuard {
33 fn deref_mut(&mut self) -> &mut OCamlRuntime {
34 unsafe { internal::recover_runtime_handle_mut() }
35 }
36}
37
38pub struct OCamlRuntime {
49 _not_send_sync: PhantomData<*const ()>,
50}
51
52impl OCamlRuntime {
53 pub fn init() -> Result<OCamlRuntimeStartupGuard, String> {
60 #[cfg(not(feature = "no-caml-startup"))]
61 {
62 use std::sync::atomic::{AtomicBool, Ordering};
63
64 static INIT_CALLED: AtomicBool = AtomicBool::new(false);
65
66 if INIT_CALLED
67 .compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst)
68 .is_err()
69 {
70 return Err("OCaml runtime already initialized".to_string());
71 }
72 unsafe {
73 let arg0 = c"ocaml".as_ptr() as *const ocaml_sys::Char;
74 let args = [arg0, core::ptr::null()];
75 ocaml_sys::caml_startup(args.as_ptr());
76 ocaml_boxroot_sys::boxroot_setup();
77 ocaml_sys::caml_enter_blocking_section();
78 }
79
80 Ok(OCamlRuntimeStartupGuard {
81 _not_send_sync: PhantomData,
82 })
83 }
84 #[cfg(feature = "no-caml-startup")]
85 return Err(
86 "Rust code called from OCaml should not try to initialize the runtime".to_string(),
87 );
88 }
89
90 pub fn releasing_runtime<T, F>(&mut self, f: F) -> T
92 where
93 F: FnOnce() -> T,
94 {
95 OCamlBlockingSection::new().perform(f)
96 }
97
98 pub fn get<'tmp, T>(&'tmp self, reference: OCamlRef<T>) -> OCaml<'tmp, T> {
100 OCaml {
101 _marker: PhantomData,
102 raw: unsafe { reference.get_raw() },
103 }
104 }
105
106 pub fn with_domain_lock<F, T>(f: F) -> T
110 where
111 F: FnOnce(&mut Self) -> T,
112 {
113 let mut lock = OCamlDomainLock::new();
114 f(&mut lock)
115 }
116}
117
118impl Drop for OCamlRuntimeStartupGuard {
119 fn drop(&mut self) {
120 unsafe {
121 ocaml_sys::caml_leave_blocking_section();
122 boxroot_teardown();
123 ocaml_sys::caml_shutdown();
124 }
125 }
126}
127
128struct OCamlBlockingSection;
129
130impl OCamlBlockingSection {
131 fn new() -> Self {
132 Self
133 }
134
135 fn perform<T, F>(self, f: F) -> T
136 where
137 F: FnOnce() -> T,
138 {
139 unsafe { ocaml_sys::caml_enter_blocking_section() };
140 f()
141 }
142}
143
144impl Drop for OCamlBlockingSection {
145 fn drop(&mut self) {
146 unsafe { ocaml_sys::caml_leave_blocking_section() };
147 }
148}
149
150struct OCamlDomainLock {
174 _not_send_sync: PhantomData<*const ()>,
175}
176
177impl OCamlDomainLock {
178 #[inline(always)]
179 fn new() -> Self {
180 OCamlThreadRegistrationGuard::ensure();
181 unsafe {
182 ocaml_sys::caml_leave_blocking_section();
183 };
184 Self {
185 _not_send_sync: PhantomData,
186 }
187 }
188}
189
190impl Drop for OCamlDomainLock {
191 fn drop(&mut self) {
192 unsafe {
193 ocaml_sys::caml_enter_blocking_section();
194 };
195 }
196}
197
198impl Deref for OCamlDomainLock {
199 type Target = OCamlRuntime;
200
201 fn deref(&self) -> &OCamlRuntime {
202 unsafe { internal::recover_runtime_handle() }
203 }
204}
205
206impl DerefMut for OCamlDomainLock {
207 fn deref_mut(&mut self) -> &mut OCamlRuntime {
208 unsafe { internal::recover_runtime_handle_mut() }
209 }
210}
211
212extern "C" {
215 pub fn caml_c_thread_register() -> isize;
216 pub fn caml_c_thread_unregister() -> isize;
217}
218
219struct OCamlThreadRegistrationGuard {
229 registered: bool,
230}
231
232thread_local! {
233 static OCAML_THREAD_REGISTRATION_GUARD: OCamlThreadRegistrationGuard = {
234 let ok = unsafe { caml_c_thread_register() } == 1;
235 OCamlThreadRegistrationGuard { registered: ok }
236 };
237}
238
239impl OCamlThreadRegistrationGuard {
240 #[inline(always)]
244 pub fn ensure() {
245 OCAML_THREAD_REGISTRATION_GUARD.with(|_| {});
246 }
247}
248
249impl Drop for OCamlThreadRegistrationGuard {
250 fn drop(&mut self) {
251 if self.registered {
252 unsafe {
253 caml_c_thread_unregister();
254 }
255 }
256 }
257}
258
259#[no_mangle]
262extern "C" fn ocaml_interop_setup(_unit: crate::RawOCaml) -> crate::RawOCaml {
263 ocaml_sys::UNIT
264}
265
266#[no_mangle]
267extern "C" fn ocaml_interop_teardown(_unit: crate::RawOCaml) -> crate::RawOCaml {
268 unsafe { boxroot_teardown() };
269 ocaml_sys::UNIT
270}
271
272#[doc(hidden)]
273pub mod internal {
274 use super::{OCamlRuntime, TLS_RUNTIME};
275
276 #[inline(always)]
277 pub unsafe fn recover_runtime_handle_mut() -> &'static mut OCamlRuntime {
278 TLS_RUNTIME.with(|cell| &mut *cell.get())
279 }
280
281 #[inline(always)]
282 pub unsafe fn recover_runtime_handle() -> &'static OCamlRuntime {
283 TLS_RUNTIME.with(|cell| &*cell.get())
284 }
285}