@@ -7,7 +7,12 @@ use crate::{
77 runtime:: OCamlRuntime ,
88 value:: OCaml ,
99} ;
10- use core:: { cell:: UnsafeCell , marker:: PhantomData , ptr} ;
10+ use core:: {
11+ cell:: { Cell , UnsafeCell } ,
12+ marker:: PhantomData ,
13+ pin:: Pin ,
14+ ptr,
15+ } ;
1116pub use ocaml_sys:: {
1217 caml_alloc, local_roots as ocaml_sys_local_roots, set_local_roots as ocaml_sys_set_local_roots,
1318 store_field,
@@ -122,6 +127,105 @@ impl<'a> OCamlRawRoot<'a> {
122127 }
123128}
124129
130+ /// A global root for keeping OCaml values alive and tracked
131+ ///
132+ /// This allows keeping a value around when exiting the stack frame.
133+ ///
134+ /// See [`OCaml::register_global_root`].
135+ pub struct OCamlGlobalRoot < T > {
136+ pub ( crate ) cell : Pin < Box < Cell < RawOCaml > > > ,
137+ _marker : PhantomData < Cell < T > > ,
138+ }
139+
140+ impl < T > std:: fmt:: Debug for OCamlGlobalRoot < T > {
141+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
142+ write ! ( f, "OCamlGlobalRoot({:#x})" , self . cell. get( ) )
143+ }
144+ }
145+
146+ impl < T > OCamlGlobalRoot < T > {
147+ // NOTE: we require initialisation here, unlike OCamlRoot which delays it
148+ // This is because we register with the GC in the constructor,
149+ // for easy pairing with Drop, and registering without initializing
150+ // would break OCaml runtime invariants.
151+ // Always registering with UNIT (like for GCFrame initialisation)
152+ // would also work, but for OCamlGenerationalRoot that would
153+ // make things slower (updating requires notifying the GC),
154+ // and it's better if the API is the same for both kinds of global roots.
155+ pub ( crate ) fn new ( val : OCaml < T > ) -> Self {
156+ let r = Self {
157+ cell : Box :: pin ( Cell :: new ( val. raw ) ) ,
158+ _marker : PhantomData ,
159+ } ;
160+ unsafe { ocaml_sys:: caml_register_global_root ( r. cell . as_ptr ( ) ) } ;
161+ r
162+ }
163+
164+ /// Access the rooted value
165+ pub fn get_ref ( & self ) -> OCamlRef < T > {
166+ unsafe { OCamlCell :: create_ref ( self . cell . as_ptr ( ) ) }
167+ }
168+
169+ /// Replace the rooted value
170+ pub fn set ( & self , val : OCaml < T > ) {
171+ self . cell . replace ( val. raw ) ;
172+ }
173+ }
174+
175+ impl < T > Drop for OCamlGlobalRoot < T > {
176+ fn drop ( & mut self ) {
177+ unsafe { ocaml_sys:: caml_remove_global_root ( self . cell . as_ptr ( ) ) } ;
178+ }
179+ }
180+
181+ /// A global, GC-friendly root for keeping OCaml values alive and tracked
182+ ///
183+ /// This allows keeping a value around when exiting the stack frame.
184+ ///
185+ /// Unlike with [`OCamlGlobalRoot`], the GC doesn't have to walk
186+ /// referenced values on every minor collection. This makes collection
187+ /// faster, except if the value is short-lived and frequently updated.
188+ ///
189+ /// See [`OCaml::register_generational_root`].
190+ pub struct OCamlGenerationalRoot < T > {
191+ pub ( crate ) cell : Pin < Box < Cell < RawOCaml > > > ,
192+ _marker : PhantomData < Cell < T > > ,
193+ }
194+
195+ impl < T > std:: fmt:: Debug for OCamlGenerationalRoot < T > {
196+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
197+ write ! ( f, "OCamlGenerationalRoot({:#x})" , self . cell. get( ) )
198+ }
199+ }
200+
201+ impl < T > OCamlGenerationalRoot < T > {
202+ pub ( crate ) fn new ( val : OCaml < T > ) -> Self {
203+ let r = Self {
204+ cell : Box :: pin ( Cell :: new ( val. raw ) ) ,
205+ _marker : PhantomData ,
206+ } ;
207+ unsafe { ocaml_sys:: caml_register_generational_global_root ( r. cell . as_ptr ( ) ) } ;
208+ r
209+ }
210+
211+ /// Access the rooted value
212+ pub fn get_ref ( & self ) -> OCamlRef < T > {
213+ unsafe { OCamlCell :: create_ref ( self . cell . as_ptr ( ) ) }
214+ }
215+
216+ /// Replace the rooted value
217+ pub fn set ( & self , val : OCaml < T > ) {
218+ unsafe { ocaml_sys:: caml_modify_generational_global_root ( self . cell . as_ptr ( ) , val. raw ) } ;
219+ debug_assert_eq ! ( self . cell. get( ) , val. raw) ;
220+ }
221+ }
222+
223+ impl < T > Drop for OCamlGenerationalRoot < T > {
224+ fn drop ( & mut self ) {
225+ unsafe { ocaml_sys:: caml_remove_generational_global_root ( self . cell . as_ptr ( ) ) } ;
226+ }
227+ }
228+
125229pub struct OCamlCell < T > {
126230 cell : UnsafeCell < RawOCaml > ,
127231 _marker : PhantomData < T > ,
0 commit comments