gc_sequence/
sequencable_arena.rs

1/// Creates a set of types for running "sequences" on `gc_arena` "arena" types.
2///
3/// Takes two parameters, the first is the name of a module that will be created, the second is the
4/// name of a type that can be made into an "arena" type with `gc_arena::make_arena`.
5///
6/// The module will contain two accessible types, `module::Arena` `module::Sequencer`.  The `Arena`
7/// type is the same as what would be produced by `gc_arena::make_arena`, but has a single extra
8/// method `Arena::sequence`.  `Arena::sequence` can be used to produce a `module::Sequencer`, which
9/// can then be stepped until a result is produced.
10///
11/// ```
12/// # use gc_arena::{ArenaParameters, Collect, Gc};
13/// # use gc_sequence::{make_sequencable_arena, SequenceExt, SequenceResultExt};
14///
15/// #[derive(Collect)]
16/// #[collect(no_drop)]
17/// struct TestRoot<'gc> {
18///     test: Gc<'gc, i32>,
19/// }
20///
21/// make_sequencable_arena!(test_sequencer, TestRoot);
22/// use test_sequencer::Arena as TestArena;
23///
24/// fn main() {
25///     let arena = TestArena::new(ArenaParameters::default(), |mc| TestRoot {
26///         test: Gc::allocate(mc, 42),
27///     });
28///
29///     let mut sequence = arena.sequence(|root| {
30///         gc_sequence::from_fn_with(root.test, |_, test| {
31///             *test + 10
32///         })
33///         .then(|_, r| r - 20)
34///         .then(|_, r| r - 30)
35///         .then(|_, r| r + 2)
36///         .boxed()
37///     });
38///
39///     loop {
40///         match sequence.step() {
41///             Ok((_, output)) => {
42///                 assert_eq!(output, 4);
43///                 return;
44///             }
45///             Err(s) => sequence = s,
46///         }
47///     }
48/// }
49/// ```
50#[macro_export]
51macro_rules! make_sequencable_arena {
52    ($module:ident, $root:ident) => {
53        make_sequencable_arena!(@impl pub(self), pub(super), $module, $root);
54    };
55
56    ($vis:vis $module:ident, $root:ident) => {
57        make_sequencable_arena!(@impl $vis, $vis, $module, $root);
58    };
59
60    (@impl $modvis:vis, $innervis:vis, $module:ident, $root:ident) => {
61        $modvis mod $module {
62            use core::any::Any;
63            use core::marker::PhantomData;
64
65            use gc_arena::{make_arena, ArenaParameters, Collect, GcCell, MutationContext};
66            use gc_sequence::{Sequence, SequenceExt};
67
68            use super::$root;
69
70            #[derive(Collect)]
71            #[collect(no_drop)]
72            struct InnerRoot<'gc> {
73                root: $root<'gc>,
74                current_sequence: GcCell<
75                    'gc,
76                    Option<Box<dyn Sequence<'gc, Output = Box<dyn Any + 'static>> + 'gc>>,
77                >,
78            }
79
80            make_arena!(InnerArena, InnerRoot);
81
82            $innervis struct Arena(InnerArena);
83
84            impl Arena {
85                /// Create a new arena with the given garbage collector tuning parameters.
86                #[allow(unused)]
87                $innervis fn new<F>(arena_parameters: ArenaParameters, f: F) -> Arena
88                where
89                    F: for<'gc> FnOnce(MutationContext<'gc, '_>) -> $root<'gc>,
90                {
91                    Arena(InnerArena::new(arena_parameters, move |mc| InnerRoot {
92                        root: f(mc),
93                        current_sequence: GcCell::allocate(mc, None),
94                    }))
95                }
96
97                /// Allows for creating an arena with a constructor that can fail.
98                #[allow(unused)]
99                $innervis fn try_new<F, E>(arena_parameters: ArenaParameters, f: F) -> Result<Arena, E>
100                where
101                    F: for<'gc> FnOnce(MutationContext<'gc, '_>) -> Result<$root<'gc>, E>,
102                {
103                    Ok(Arena(InnerArena::try_new(arena_parameters, move |mc| {
104                        Ok(InnerRoot {
105                            root: f(mc)?,
106                            current_sequence: GcCell::allocate(mc, None),
107                        })
108                    })?))
109                }
110
111                /// Provides access to a garbage collected arena, during which no garbage collection
112                /// may take place.
113                #[allow(unused)]
114                $innervis fn mutate<F, R>(&mut self, f: F) -> R
115                where
116                    F: for<'gc> FnOnce(MutationContext<'gc, '_>, &$root<'gc>) -> R,
117                {
118                    self.0.mutate(move |mc, root| f(mc, &root.root))
119                }
120
121                /// Access a garbage collected arena with a `Sequence`, allowing garbage collection
122                /// to take place in between sequence steps.
123                ///
124                /// Consumes this arena type, but the arena will be returned when the `Sequencer` is
125                /// finished.
126                #[allow(unused)]
127                $innervis fn sequence<F, O>(mut self, f: F) -> Sequencer<O>
128                where
129                    O: 'static,
130                    F: for<'gc> FnOnce(&$root<'gc>) -> Box<dyn Sequence<'gc, Output = O> + 'gc>,
131                {
132                    self.0.mutate(move |mc, root| {
133                        *root.current_sequence.write(mc) =
134                            Some(f(&root.root).map(|r| -> Box<Any> { Box::new(r) }).boxed());
135                    });
136                    Sequencer(self.0, PhantomData)
137                }
138
139                /// Returns total currently used memory
140                #[allow(unused)]
141                #[inline]
142                $innervis fn total_allocated(&self) -> usize {
143                    self.0.total_allocated()
144                }
145
146                /// Returns the current "allocation debt", measured in bytes.  Allocation debt rises
147                /// as allocation takes place based on the `ArenaParameters` set for this arena.
148                #[allow(unused)]
149                #[inline]
150                $innervis fn allocation_debt(&self) -> f64 {
151                    self.0.allocation_debt()
152                }
153
154                /// Runs the incremental garbage collector until the allocation debt is <= 0.0.
155                /// There is no minimum unit of work enforced here, so it may be faster to only call
156                /// this method when the allocation debt is above some threshold.
157                #[allow(unused)]
158                #[inline]
159                $innervis fn collect_debt(&mut self) {
160                    self.0.collect_debt()
161                }
162
163                /// Run the current garbage collection cycle to completion, stopping once the
164                /// garbage collector has entered the sleeping phase.
165                #[allow(unused)]
166                $innervis fn collect_all(&mut self) {
167                    self.0.collect_all()
168                }
169            }
170
171            $innervis struct Sequencer<O>(InnerArena, PhantomData<O>);
172
173            impl<O> Sequencer<O>
174            where
175                O: 'static,
176            {
177                /// Steps the current sequence.  Returns `Ok((arena, result))` if the sequence is
178                /// complete, and `Err(self)` otherwise.
179                #[allow(unused)]
180                $innervis fn step(mut self) -> Result<(Arena, O), Sequencer<O>> {
181                    let r = self.0.mutate(move |mc, root| {
182                        root.current_sequence.write(mc).as_mut().unwrap().step(mc)
183                    });
184
185                    if let Some(r) = r {
186                        self.0.mutate(|mc, root| {
187                            *root.current_sequence.write(mc) = None;
188                        });
189                        Ok((
190                            Arena(self.0),
191                            *Box::<dyn Any + 'static>::downcast(r).unwrap(),
192                        ))
193                    } else {
194                        Err(self)
195                    }
196                }
197
198                /// *Abort* this sequence, returning the inner arena type.
199                $innervis fn abort(mut self) -> Arena {
200                    self.0.mutate(|mc, root| {
201                        *root.current_sequence.write(mc) = None;
202                    });
203                    Arena(self.0)
204                }
205
206                #[allow(unused)]
207                #[inline]
208                $innervis fn total_allocated(&self) -> usize {
209                    self.0.total_allocated()
210                }
211
212                #[allow(unused)]
213                #[inline]
214                $innervis fn allocation_debt(&self) -> f64 {
215                    self.0.allocation_debt()
216                }
217
218                #[allow(unused)]
219                #[inline]
220                $innervis fn collect_debt(&mut self) {
221                    self.0.collect_debt()
222                }
223
224                #[allow(unused)]
225                $innervis fn collect_all(&mut self) {
226                    self.0.collect_all()
227                }
228            }
229        }
230    };
231}