mmtk/scheduler/
gc_work.rs

1use super::work_bucket::WorkBucketStage;
2use super::*;
3use crate::global_state::GcStatus;
4use crate::vm::*;
5use crate::*;
6use std::marker::PhantomData;
7
8pub struct ScheduleCollection;
9
10impl<VM: VMBinding> GCWork<VM> for ScheduleCollection {
11    fn do_work(&mut self, worker: &mut GCWorker<VM>, mmtk: &'static MMTK<VM>) {
12        // Tell GC trigger that GC started.
13        mmtk.gc_trigger.policy.on_gc_start(mmtk);
14
15        // Determine collection kind
16        let is_emergency = mmtk.state.set_collection_kind(
17            mmtk.get_plan().last_collection_was_exhaustive(),
18            mmtk.gc_trigger.policy.can_heap_size_grow(),
19        );
20        if is_emergency {
21            mmtk.get_plan().notify_emergency_collection();
22        }
23        // Set to GcPrepare
24        mmtk.set_gc_status(GcStatus::GcPrepare);
25
26        // Let the plan to schedule collection work
27        mmtk.get_plan().schedule_collection(worker.scheduler());
28    }
29}
30
31/// The global GC Preparation Work
32/// This work packet invokes prepare() for the plan (which will invoke prepare() for each space), and
33/// pushes work packets for preparing mutators and collectors.
34/// We should only have one such work packet per GC, before any actual GC work starts.
35/// We assume this work packet is the only running work packet that accesses plan, and there should
36/// be no other concurrent work packet that accesses plan (read or write). Otherwise, there may
37/// be a race condition.
38pub struct Prepare<C: GCWorkContext> {
39    pub plan: *const C::PlanType,
40}
41
42unsafe impl<C: GCWorkContext> Send for Prepare<C> {}
43
44impl<C: GCWorkContext> Prepare<C> {
45    pub fn new(plan: *const C::PlanType) -> Self {
46        Self { plan }
47    }
48}
49
50impl<C: GCWorkContext> GCWork<C::VM> for Prepare<C> {
51    fn do_work(&mut self, worker: &mut GCWorker<C::VM>, mmtk: &'static MMTK<C::VM>) {
52        trace!("Prepare Global");
53        // We assume this is the only running work packet that accesses plan at the point of execution
54        let plan_mut: &mut C::PlanType = unsafe { &mut *(self.plan as *const _ as *mut _) };
55        plan_mut.prepare(worker.tls);
56
57        if plan_mut.constraints().needs_prepare_mutator {
58            let prepare_mutator_packets = <C::VM as VMBinding>::VMActivePlan::mutators()
59                .map(|mutator| Box::new(PrepareMutator::<C::VM>::new(mutator)) as _)
60                .collect::<Vec<_>>();
61            // Just in case the VM binding is inconsistent about the number of mutators and the actual mutator list.
62            debug_assert_eq!(
63                prepare_mutator_packets.len(),
64                <C::VM as VMBinding>::VMActivePlan::number_of_mutators()
65            );
66            mmtk.scheduler.work_buckets[WorkBucketStage::Prepare].bulk_add(prepare_mutator_packets);
67        }
68
69        for w in &mmtk.scheduler.worker_group.workers_shared {
70            let result = w.designated_work.push(Box::new(PrepareCollector));
71            debug_assert!(result.is_ok());
72        }
73    }
74}
75
76/// The mutator GC Preparation Work
77pub struct PrepareMutator<VM: VMBinding> {
78    // The mutator reference has static lifetime.
79    // It is safe because the actual lifetime of this work-packet will not exceed the lifetime of a GC.
80    pub mutator: &'static mut Mutator<VM>,
81}
82
83impl<VM: VMBinding> PrepareMutator<VM> {
84    pub fn new(mutator: &'static mut Mutator<VM>) -> Self {
85        Self { mutator }
86    }
87}
88
89impl<VM: VMBinding> GCWork<VM> for PrepareMutator<VM> {
90    fn do_work(&mut self, worker: &mut GCWorker<VM>, _mmtk: &'static MMTK<VM>) {
91        trace!("Prepare Mutator");
92        self.mutator.prepare(worker.tls);
93    }
94}
95
96/// The collector GC Preparation Work
97#[derive(Default)]
98pub struct PrepareCollector;
99
100impl<VM: VMBinding> GCWork<VM> for PrepareCollector {
101    fn do_work(&mut self, worker: &mut GCWorker<VM>, mmtk: &'static MMTK<VM>) {
102        trace!("Prepare Collector");
103        worker.get_copy_context_mut().prepare();
104        mmtk.get_plan().prepare_worker(worker);
105    }
106}
107
108/// The global GC release Work
109/// This work packet invokes release() for the plan (which will invoke release() for each space), and
110/// pushes work packets for releasing mutators and collectors.
111/// We should only have one such work packet per GC, after all actual GC work ends.
112/// We assume this work packet is the only running work packet that accesses plan, and there should
113/// be no other concurrent work packet that accesses plan (read or write). Otherwise, there may
114/// be a race condition.
115pub struct Release<C: GCWorkContext> {
116    pub plan: *const C::PlanType,
117}
118
119impl<C: GCWorkContext> Release<C> {
120    pub fn new(plan: *const C::PlanType) -> Self {
121        Self { plan }
122    }
123}
124
125unsafe impl<C: GCWorkContext> Send for Release<C> {}
126
127impl<C: GCWorkContext + 'static> GCWork<C::VM> for Release<C> {
128    fn do_work(&mut self, worker: &mut GCWorker<C::VM>, mmtk: &'static MMTK<C::VM>) {
129        trace!("Release Global");
130
131        mmtk.gc_trigger.policy.on_gc_release(mmtk);
132        // We assume this is the only running work packet that accesses plan at the point of execution
133
134        let plan_mut: &mut C::PlanType = unsafe { &mut *(self.plan as *const _ as *mut _) };
135        plan_mut.release(worker.tls);
136
137        let release_mutator_packets = <C::VM as VMBinding>::VMActivePlan::mutators()
138            .map(|mutator| Box::new(ReleaseMutator::<C::VM>::new(mutator)) as _)
139            .collect::<Vec<_>>();
140        // Just in case the VM binding is inconsistent about the number of mutators and the actual mutator list.
141        debug_assert_eq!(
142            release_mutator_packets.len(),
143            <C::VM as VMBinding>::VMActivePlan::number_of_mutators()
144        );
145        mmtk.scheduler.work_buckets[WorkBucketStage::Release].bulk_add(release_mutator_packets);
146
147        for w in &mmtk.scheduler.worker_group.workers_shared {
148            let result = w.designated_work.push(Box::new(ReleaseCollector));
149            debug_assert!(result.is_ok());
150        }
151    }
152}
153
154/// The mutator release Work
155pub struct ReleaseMutator<VM: VMBinding> {
156    // The mutator reference has static lifetime.
157    // It is safe because the actual lifetime of this work-packet will not exceed the lifetime of a GC.
158    pub mutator: &'static mut Mutator<VM>,
159}
160
161impl<VM: VMBinding> ReleaseMutator<VM> {
162    pub fn new(mutator: &'static mut Mutator<VM>) -> Self {
163        Self { mutator }
164    }
165}
166
167impl<VM: VMBinding> GCWork<VM> for ReleaseMutator<VM> {
168    fn do_work(&mut self, worker: &mut GCWorker<VM>, _mmtk: &'static MMTK<VM>) {
169        trace!("Release Mutator");
170        self.mutator.release(worker.tls);
171    }
172}
173
174/// The collector release Work
175#[derive(Default)]
176pub struct ReleaseCollector;
177
178impl<VM: VMBinding> GCWork<VM> for ReleaseCollector {
179    fn do_work(&mut self, worker: &mut GCWorker<VM>, _mmtk: &'static MMTK<VM>) {
180        trace!("Release Collector");
181        worker.get_copy_context_mut().release();
182    }
183}
184
185/// Stop all mutators
186///
187/// TODO: Smaller work granularity
188#[derive(Default)]
189pub struct StopMutators<C: GCWorkContext> {
190    /// If this is true, we skip creating root-scanning work packets.
191    /// By default, this is false.
192    skip_roots: bool,
193    /// Flush mutators once they are stopped. By default this is false. [`ScanMutatorRoots`] will flush mutators.
194    flush_mutator: bool,
195    phantom: PhantomData<C>,
196}
197
198impl<C: GCWorkContext> StopMutators<C> {
199    pub fn new() -> Self {
200        Self {
201            skip_roots: false,
202            flush_mutator: false,
203            phantom: PhantomData,
204        }
205    }
206
207    /// Create a `StopMutators` work packet that does not create any root-scanning work packets, and will simply flush mutators.
208    pub fn new_no_scan_roots() -> Self {
209        Self {
210            skip_roots: true,
211            flush_mutator: true,
212            phantom: PhantomData,
213        }
214    }
215}
216
217impl<C: GCWorkContext> GCWork<C::VM> for StopMutators<C> {
218    fn do_work(&mut self, worker: &mut GCWorker<C::VM>, mmtk: &'static MMTK<C::VM>) {
219        trace!("stop_all_mutators start");
220        mmtk.state.prepare_for_stack_scanning();
221        <C::VM as VMBinding>::VMCollection::stop_all_mutators(worker.tls, |mutator| {
222            // TODO: The stack scanning work won't start immediately, as the `Prepare` bucket is not opened yet (the bucket is opened in notify_mutators_paused).
223            // Should we push to Unconstrained instead?
224
225            if self.flush_mutator {
226                mutator.flush();
227            }
228            if !self.skip_roots {
229                mmtk.scheduler.work_buckets[WorkBucketStage::Prepare]
230                    .add(ScanMutatorRoots::<C>(mutator));
231            }
232        });
233        trace!("stop_all_mutators end");
234        mmtk.get_plan().notify_mutators_paused(&mmtk.scheduler);
235        mmtk.scheduler.notify_mutators_paused(mmtk);
236        if !self.skip_roots {
237            mmtk.scheduler.work_buckets[WorkBucketStage::Prepare]
238                .add(ScanVMSpecificRoots::<C>::new());
239        }
240    }
241}
242
243pub struct ScanMutatorRoots<C: GCWorkContext>(pub &'static mut Mutator<C::VM>);
244
245impl<C: GCWorkContext> GCWork<C::VM> for ScanMutatorRoots<C> {
246    fn do_work(&mut self, worker: &mut GCWorker<C::VM>, mmtk: &'static MMTK<C::VM>) {
247        trace!("ScanMutatorRoots for mutator {:?}", self.0.get_tls());
248        let mutators = <C::VM as VMBinding>::VMActivePlan::number_of_mutators();
249        let factory = C::make_roots_work_factory(mmtk);
250        <C::VM as VMBinding>::VMScanning::scan_roots_in_mutator_thread(
251            worker.tls,
252            unsafe { &mut *(self.0 as *mut _) },
253            factory,
254        );
255        self.0.flush();
256
257        if mmtk.state.inform_stack_scanned(mutators) {
258            <C::VM as VMBinding>::VMScanning::notify_initial_thread_scan_complete(
259                false, worker.tls,
260            );
261            mmtk.set_gc_status(GcStatus::GcProper);
262        }
263    }
264}
265
266#[derive(Default)]
267pub struct ScanVMSpecificRoots<C: GCWorkContext>(PhantomData<C>);
268
269impl<C: GCWorkContext> ScanVMSpecificRoots<C> {
270    pub fn new() -> Self {
271        Self(PhantomData)
272    }
273}
274
275impl<C: GCWorkContext> GCWork<C::VM> for ScanVMSpecificRoots<C> {
276    fn do_work(&mut self, worker: &mut GCWorker<C::VM>, mmtk: &'static MMTK<C::VM>) {
277        trace!("ScanStaticRoots");
278        let factory = C::make_roots_work_factory(mmtk);
279        <C::VM as VMBinding>::VMScanning::scan_vm_specific_roots(worker.tls, factory);
280    }
281}