1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
/// Creates a set of types for running "sequences" on `gc_arena` "arena" types.
///
/// Takes two parameters, the first is the name of a module that will be created, the second is the
/// name of a type that can be made into an "arena" type with `gc_arena::make_arena`.
///
/// The module will contain two accessible types, `module::Arena` `module::Sequencer`.  The `Arena`
/// type is the same as what would be produced by `gc_arena::make_arena`, but has a single extra
/// method `Arena::sequence`.  `Arena::sequence` can be used to produce a `module::Sequencer`, which
/// can then be stepped until a result is produced.
///
/// ```
/// # use gc_arena::{ArenaParameters, Collect, Gc};
/// # use gc_sequence::{make_sequencable_arena, SequenceExt, SequenceResultExt};
///
/// #[derive(Collect)]
/// #[collect(empty_drop)]
/// struct TestRoot<'gc> {
///     test: Gc<'gc, i32>,
/// }
///
/// make_sequencable_arena!(test_sequencer, TestRoot);
/// use test_sequencer::Arena as TestArena;
///
/// fn main() {
///     let arena = TestArena::new(ArenaParameters::default(), |mc| TestRoot {
///         test: Gc::allocate(mc, 42),
///     });
///
///     let mut sequence = arena.sequence(|root| {
///         gc_sequence::from_fn_with(root.test, |_, test| {
///             *test + 10
///         })
///         .then(|_, r| r - 20)
///         .then(|_, r| r - 30)
///         .then(|_, r| r + 2)
///         .boxed()
///     });
///
///     loop {
///         match sequence.step() {
///             Ok((_, output)) => {
///                 assert_eq!(output, 4);
///                 return;
///             }
///             Err(s) => sequence = s,
///         }
///     }
/// }
/// ```
#[macro_export]
macro_rules! make_sequencable_arena {
    ($module:ident, $root:ident) => {
        make_sequencable_arena!(@impl pub(self), pub(super), $module, $root);
    };

    ($vis:vis $module:ident, $root:ident) => {
        make_sequencable_arena!(@impl $vis, $vis, $module, $root);
    };

    (@impl $modvis:vis, $innervis:vis, $module:ident, $root:ident) => {
        $modvis mod $module {
            use std::any::Any;
            use std::marker::PhantomData;

            use gc_arena::{make_arena, ArenaParameters, Collect, GcCell, MutationContext};
            use gc_sequence::{Sequence, SequenceExt};

            use super::$root;

            #[derive(Collect)]
            #[collect(empty_drop)]
            struct InnerRoot<'gc> {
                root: $root<'gc>,
                current_sequence: GcCell<
                    'gc,
                    Option<Box<dyn Sequence<'gc, Output = Box<dyn Any + 'static>> + 'gc>>,
                >,
            }

            make_arena!(InnerArena, InnerRoot);

            $innervis struct Arena(InnerArena);

            impl Arena {
                /// Create a new arena with the given garbage collector tuning parameters.
                #[allow(unused)]
                $innervis fn new<F>(arena_parameters: ArenaParameters, f: F) -> Arena
                where
                    F: for<'gc> FnOnce(MutationContext<'gc, '_>) -> $root<'gc>,
                {
                    Arena(InnerArena::new(arena_parameters, move |mc| InnerRoot {
                        root: f(mc),
                        current_sequence: GcCell::allocate(mc, None),
                    }))
                }

                /// Allows for creating an arena with a constructor that can fail.
                #[allow(unused)]
                $innervis fn try_new<F, E>(arena_parameters: ArenaParameters, f: F) -> Result<Arena, E>
                where
                    F: for<'gc> FnOnce(MutationContext<'gc, '_>) -> Result<$root<'gc>, E>,
                {
                    Ok(Arena(InnerArena::try_new(arena_parameters, move |mc| {
                        Ok(InnerRoot {
                            root: f(mc)?,
                            current_sequence: GcCell::allocate(mc, None),
                        })
                    })?))
                }

                /// Provides access to a garbage collected arena, during which no garbage collection
                /// may take place.
                #[allow(unused)]
                $innervis fn mutate<F, R>(&mut self, f: F) -> R
                where
                    F: for<'gc> FnOnce(MutationContext<'gc, '_>, &$root<'gc>) -> R,
                {
                    self.0.mutate(move |mc, root| f(mc, &root.root))
                }

                /// Access a garbage collected arena with a `Sequence`, allowing garbage collection
                /// to take place in between sequence steps.
                ///
                /// Consumes this arena type, but the arena will be returned when the `Sequencer` is
                /// finished.
                #[allow(unused)]
                $innervis fn sequence<F, O>(mut self, f: F) -> Sequencer<O>
                where
                    O: 'static,
                    F: for<'gc> FnOnce(&$root<'gc>) -> Box<dyn Sequence<'gc, Output = O> + 'gc>,
                {
                    self.0.mutate(move |mc, root| {
                        *root.current_sequence.write(mc) =
                            Some(f(&root.root).map(|r| -> Box<Any> { Box::new(r) }).boxed());
                    });
                    Sequencer(self.0, PhantomData)
                }

                /// Returns total currently used memory
                #[allow(unused)]
                #[inline]
                $innervis fn total_allocated(&self) -> usize {
                    self.0.total_allocated()
                }

                /// Returns the current "allocation debt", measured in bytes.  Allocation debt rises
                /// as allocation takes place based on the `ArenaParameters` set for this arena.
                #[allow(unused)]
                #[inline]
                $innervis fn allocation_debt(&self) -> f64 {
                    self.0.allocation_debt()
                }

                /// Runs the incremental garbage collector until the allocation debt is <= 0.0.
                /// There is no minimum unit of work enforced here, so it may be faster to only call
                /// this method when the allocation debt is above some threshold.
                #[allow(unused)]
                #[inline]
                $innervis fn collect_debt(&mut self) {
                    self.0.collect_debt()
                }

                /// Run the current garbage collection cycle to completion, stopping once the
                /// garbage collector has entered the sleeping phase.
                #[allow(unused)]
                $innervis fn collect_all(&mut self) {
                    self.0.collect_all()
                }
            }

            $innervis struct Sequencer<O>(InnerArena, PhantomData<O>);

            impl<O> Sequencer<O>
            where
                O: 'static,
            {
                /// Steps the current sequence.  Returns `Ok((arena, result))` if the sequence is
                /// complete, and `Err(self)` otherwise.
                #[allow(unused)]
                $innervis fn step(mut self) -> Result<(Arena, O), Sequencer<O>> {
                    let r = self.0.mutate(move |mc, root| {
                        root.current_sequence.write(mc).as_mut().unwrap().step(mc)
                    });

                    if let Some(r) = r {
                        self.0.mutate(|mc, root| {
                            *root.current_sequence.write(mc) = None;
                        });
                        Ok((
                            Arena(self.0),
                            *Box::<dyn Any + 'static>::downcast(r).unwrap(),
                        ))
                    } else {
                        Err(self)
                    }
                }

                /// *Abort* this sequence, returning the inner arena type.
                $innervis fn abort(mut self) -> Arena {
                    self.0.mutate(|mc, root| {
                        *root.current_sequence.write(mc) = None;
                    });
                    Arena(self.0)
                }

                #[allow(unused)]
                #[inline]
                $innervis fn total_allocated(&self) -> usize {
                    self.0.total_allocated()
                }

                #[allow(unused)]
                #[inline]
                $innervis fn allocation_debt(&self) -> f64 {
                    self.0.allocation_debt()
                }

                #[allow(unused)]
                #[inline]
                $innervis fn collect_debt(&mut self) {
                    self.0.collect_debt()
                }

                #[allow(unused)]
                $innervis fn collect_all(&mut self) {
                    self.0.collect_all()
                }
            }
        }
    };
}