Skip to content

Commit f785236

Browse files
authored
Extra assertions for mutators and epilogues (#1182)
This commit adds assertions about the number of mutators and the list of mutators returned from the VM binding. Also asserts that epilogues are not silently dropped before they are executed. It could happen if a buggy VM binding returns a number of mutators that does not match the actual list of mutators. This should detect bugs like mmtk/mmtk-ruby#84
1 parent f9b85bc commit f785236

File tree

6 files changed

+59
-9
lines changed

6 files changed

+59
-9
lines changed

src/plan/marksweep/global.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ impl<VM: VMBinding> Plan for MarkSweep<VM> {
6565
self.common.release(tls, true);
6666
}
6767

68+
fn end_of_gc(&mut self, _tls: VMWorkerThread) {
69+
self.ms.end_of_gc();
70+
}
71+
6872
fn collection_required(&self, space_full: bool, _space: Option<SpaceStats<Self::VM>>) -> bool {
6973
self.base().collection_required(self, space_full)
7074
}

src/policy/immix/immixspace.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ use crate::policy::sft_map::SFTMap;
99
use crate::policy::space::{CommonSpace, Space};
1010
use crate::util::alloc::allocator::AllocatorContext;
1111
use crate::util::constants::LOG_BYTES_IN_PAGE;
12-
use crate::util::copy::*;
1312
use crate::util::heap::chunk_map::*;
1413
use crate::util::heap::BlockPageResource;
1514
use crate::util::heap::PageResource;
@@ -19,6 +18,7 @@ use crate::util::metadata::side_metadata::SideMetadataSpec;
1918
use crate::util::metadata::vo_bit;
2019
use crate::util::metadata::{self, MetadataSpec};
2120
use crate::util::object_forwarding;
21+
use crate::util::{copy::*, epilogue};
2222
use crate::util::{Address, ObjectReference};
2323
use crate::vm::*;
2424
use crate::{
@@ -979,6 +979,12 @@ impl<VM: VMBinding> FlushPageResource<VM> {
979979
}
980980
}
981981

982+
impl<VM: VMBinding> Drop for FlushPageResource<VM> {
983+
fn drop(&mut self) {
984+
epilogue::debug_assert_counter_zero(&self.counter, "FlushPageResource::counter");
985+
}
986+
}
987+
982988
use crate::policy::copy_context::PolicyCopyContext;
983989
use crate::util::alloc::Allocator;
984990
use crate::util::alloc::ImmixAllocator;

src/policy/marksweepspace/native_ms/global.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use crate::{
88
scheduler::{GCWorkScheduler, GCWorker, WorkBucketStage},
99
util::{
1010
copy::CopySemantics,
11+
epilogue,
1112
heap::{BlockPageResource, PageResource},
1213
metadata::{self, side_metadata::SideMetadataSpec, MetadataSpec},
1314
ObjectReference,
@@ -373,6 +374,13 @@ impl<VM: VMBinding> MarkSweepSpace<VM> {
373374
self.scheduler.work_buckets[crate::scheduler::WorkBucketStage::Release].add(work_packet);
374375
}
375376

377+
pub fn end_of_gc(&mut self) {
378+
epilogue::debug_assert_counter_zero(
379+
&self.pending_release_packets,
380+
"pending_release_packets",
381+
);
382+
}
383+
376384
/// Release a block.
377385
pub fn release_block(&self, block: Block) {
378386
self.block_clear_metadata(block);
@@ -581,3 +589,9 @@ impl<VM: VMBinding> RecycleBlocks<VM> {
581589
}
582590
}
583591
}
592+
593+
impl<VM: VMBinding> Drop for RecycleBlocks<VM> {
594+
fn drop(&mut self) {
595+
epilogue::debug_assert_counter_zero(&self.counter, "RecycleBlocks::counter");
596+
}
597+
}

src/scheduler/gc_work.rs

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -60,11 +60,17 @@ impl<C: GCWorkContext> GCWork<C::VM> for Prepare<C> {
6060
plan_mut.prepare(worker.tls);
6161

6262
if plan_mut.constraints().needs_prepare_mutator {
63-
for mutator in <C::VM as VMBinding>::VMActivePlan::mutators() {
64-
mmtk.scheduler.work_buckets[WorkBucketStage::Prepare]
65-
.add(PrepareMutator::<C::VM>::new(mutator));
66-
}
63+
let prepare_mutator_packets = <C::VM as VMBinding>::VMActivePlan::mutators()
64+
.map(|mutator| Box::new(PrepareMutator::<C::VM>::new(mutator)) as _)
65+
.collect::<Vec<_>>();
66+
// Just in case the VM binding is inconsistent about the number of mutators and the actual mutator list.
67+
debug_assert_eq!(
68+
prepare_mutator_packets.len(),
69+
<C::VM as VMBinding>::VMActivePlan::number_of_mutators()
70+
);
71+
mmtk.scheduler.work_buckets[WorkBucketStage::Prepare].bulk_add(prepare_mutator_packets);
6772
}
73+
6874
for w in &mmtk.scheduler.worker_group.workers_shared {
6975
let result = w.designated_work.push(Box::new(PrepareCollector));
7076
debug_assert!(result.is_ok());
@@ -133,10 +139,16 @@ impl<C: GCWorkContext + 'static> GCWork<C::VM> for Release<C> {
133139
let plan_mut: &mut C::PlanType = unsafe { &mut *(self.plan as *const _ as *mut _) };
134140
plan_mut.release(worker.tls);
135141

136-
for mutator in <C::VM as VMBinding>::VMActivePlan::mutators() {
137-
mmtk.scheduler.work_buckets[WorkBucketStage::Release]
138-
.add(ReleaseMutator::<C::VM>::new(mutator));
139-
}
142+
let release_mutator_packets = <C::VM as VMBinding>::VMActivePlan::mutators()
143+
.map(|mutator| Box::new(ReleaseMutator::<C::VM>::new(mutator)) as _)
144+
.collect::<Vec<_>>();
145+
// Just in case the VM binding is inconsistent about the number of mutators and the actual mutator list.
146+
debug_assert_eq!(
147+
release_mutator_packets.len(),
148+
<C::VM as VMBinding>::VMActivePlan::number_of_mutators()
149+
);
150+
mmtk.scheduler.work_buckets[WorkBucketStage::Release].bulk_add(release_mutator_packets);
151+
140152
for w in &mmtk.scheduler.worker_group.workers_shared {
141153
let result = w.designated_work.push(Box::new(ReleaseCollector));
142154
debug_assert!(result.is_ok());

src/util/epilogue.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
//! Utilities for implementing epilogues.
2+
//!
3+
//! Epilogues are operations that are done when several other operations are done.
4+
5+
use std::sync::atomic::{AtomicUsize, Ordering};
6+
7+
/// A debugging method for detecting the case when the epilogue is never executed.
8+
pub fn debug_assert_counter_zero(counter: &AtomicUsize, what: &'static str) {
9+
let value = counter.load(Ordering::SeqCst);
10+
if value != 0 {
11+
panic!("{what} is still {value}.");
12+
}
13+
}

src/util/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ pub mod test_util;
4444
/// An analysis framework for collecting data and profiling in GC.
4545
#[cfg(feature = "analysis")]
4646
pub(crate) mod analysis;
47+
pub(crate) mod epilogue;
4748
/// Non-generic refs to generic types of `<VM>`.
4849
pub(crate) mod erase_vm;
4950
/// Finalization implementation.

0 commit comments

Comments
 (0)