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}