Skip to content
Draft
6 changes: 3 additions & 3 deletions .github/scripts/ci-style.sh
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,9 @@ else
fi

# mock tests
cargo clippy --features mock_test,mock_test_side_metadata
cargo clippy --features mock_test,mock_test_side_metadata --tests
cargo clippy --features mock_test,mock_test_side_metadata --benches
cargo clippy --features mock_test
cargo clippy --features mock_test --tests
cargo clippy --features mock_test --benches

# non-mock benchmarks
cargo clippy --features test_private --benches
Expand Down
8 changes: 1 addition & 7 deletions .github/scripts/ci-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,7 @@ find ./src ./tests -type f -name "mock_test_*" | while read -r file; do

# Run the test with each plan it needs.
for MMTK_PLAN in $PLANS; do
# Currently run all tests with side metadata, except LXR which requires in-header forwarding bits.
if [[ $MMTK_PLAN == 'LXR' ]]; then
METADATA=mock_test_header_metadata
else
METADATA=mock_test_side_metadata
fi
env MMTK_PLAN=$MMTK_PLAN cargo test --features mock_test,$METADATA,"$FEATURES" -- $t;
env MMTK_PLAN=$MMTK_PLAN cargo test --features mock_test,"$FEATURES" -- $t;
done
done

Expand Down
2 changes: 0 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,6 @@ perf_counter = ["dep:pfm"]
# This feature is only used for tests with MockVM.
# CI scripts run those tests with this feature.
mock_test = ["test_private", "dep:thread-id"]
mock_test_header_metadata = ["mock_test"]
mock_test_side_metadata = ["mock_test"]

# This feature will expose some private functions for testings or benchmarking.
test_private = []
Expand Down
54 changes: 33 additions & 21 deletions src/util/test_util/fixtures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@
#![allow(dead_code)]

use atomic_refcell::AtomicRefCell;
use std::marker::PhantomData;
use std::sync::Mutex;
use std::sync::Once;

use crate::memory_manager;
use crate::util::test_util::mock_vm::MockVM;
use crate::util::test_util::mock_vm::{MockVM, MutatorHandle};
use crate::util::{ObjectReference, VMMutatorThread, VMThread};
use crate::vm::VMBinding;
use crate::AllocationSemantics;
use crate::MMTKBuilder;
use crate::MMTK;
Expand Down Expand Up @@ -116,9 +118,15 @@ impl<T: FixtureContent> Default for SerialFixture<T> {
}
}

pub struct MMTKFixture;
/// A fixture that creates the MMTk singleton for the default [`MockVM`].
pub type MMTKFixture = GenericMMTKFixture<MockVM>;

impl FixtureContent for MMTKFixture {
/// A fixture that creates the MMTk singleton for a mock VM type `VM`.
pub struct GenericMMTKFixture<VM: VMBinding> {
_vm: PhantomData<VM>,
}

impl<VM: VMBinding> FixtureContent for GenericMMTKFixture<VM> {
fn create() -> Self {
Self::create_with_builder(
|builder| {
Expand All @@ -133,52 +141,56 @@ impl FixtureContent for MMTKFixture {
}
}

impl MMTKFixture {
impl<VM: VMBinding> GenericMMTKFixture<VM> {
pub fn create_with_builder<F>(with_builder: F, initialize_collection: bool) -> Self
where
F: FnOnce(&mut MMTKBuilder),
{
let mut builder = MMTKBuilder::new();
with_builder(&mut builder);

let mmtk = memory_manager::mmtk_init(&builder);
let mmtk = memory_manager::mmtk_init::<VM>(&builder);
let mmtk_ptr = Box::into_raw(mmtk);
mock_api::set_singleton(mmtk_ptr);

if initialize_collection {
let mmtk_static: &'static MMTK<MockVM> = unsafe { &*mmtk_ptr };
let mmtk_static: &'static MMTK<VM> = unsafe { &*mmtk_ptr };
memory_manager::initialize_collection(mmtk_static, VMThread::UNINITIALIZED);
}

MMTKFixture
Self { _vm: PhantomData }
}

pub fn get_mmtk(&self) -> &'static MMTK<MockVM> {
pub fn get_mmtk(&self) -> &'static MMTK<VM> {
mock_api::singleton()
}

pub fn get_mmtk_mut(&mut self) -> &'static mut MMTK<MockVM> {
pub fn get_mmtk_mut(&mut self) -> &'static mut MMTK<VM> {
mock_api::singleton_mut()
}
}

use crate::plan::Mutator;

pub struct MutatorFixture {
mmtk: MMTKFixture,
/// A fixture that creates the MMTk singleton and binds a mutator for the default [`MockVM`].
pub type MutatorFixture = GenericMutatorFixture<MockVM>;

/// A fixture that creates the MMTk singleton and binds a mutator for a mock VM type `VM`.
pub struct GenericMutatorFixture<VM: VMBinding> {
mmtk: GenericMMTKFixture<VM>,
mutator: VMMutatorThread,
}

impl FixtureContent for MutatorFixture {
impl<VM: VMBinding> FixtureContent for GenericMutatorFixture<VM> {
fn create() -> Self {
const MB: usize = 1024 * 1024;
Self::create_with_heapsize(MB)
}
}

impl MutatorFixture {
impl<VM: VMBinding> GenericMutatorFixture<VM> {
pub fn create_with_heapsize(size: usize) -> Self {
let mmtk = MMTKFixture::create_with_builder(
let mmtk = GenericMMTKFixture::create_with_builder(
|builder| {
builder
.options
Expand All @@ -187,33 +199,33 @@ impl MutatorFixture {
},
true,
);
let mutator = mock_api::bind_mutator();
let mutator = MutatorHandle::bind::<VM>();
Self { mmtk, mutator }
}

pub fn create_with_builder<F>(with_builder: F) -> Self
where
F: FnOnce(&mut MMTKBuilder),
{
let mmtk = MMTKFixture::create_with_builder(with_builder, true);
let mutator = mock_api::bind_mutator();
let mmtk = GenericMMTKFixture::create_with_builder(with_builder, true);
let mutator = MutatorHandle::bind::<VM>();
Self { mmtk, mutator }
}

pub fn mmtk(&self) -> &'static MMTK<MockVM> {
pub fn mmtk(&self) -> &'static MMTK<VM> {
self.mmtk.get_mmtk()
}

pub fn mutator(&self) -> &'static mut Mutator<MockVM> {
self.mutator.as_mock_mutator()
pub fn mutator(&self) -> &'static mut Mutator<VM> {
self.mutator.as_generic_mock_mutator()
}

pub fn mutator_tls(&self) -> VMMutatorThread {
self.mutator
}
}

unsafe impl Send for MutatorFixture {}
unsafe impl<VM: VMBinding> Send for GenericMutatorFixture<VM> {}

pub struct SingleObject {
pub objref: ObjectReference,
Expand Down
52 changes: 32 additions & 20 deletions src/util/test_util/mock_vm/mock_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,40 +3,52 @@
//! the APIs from [`crate:memory_manager`]. For example, [`bind_mutator`] is provided here as a wrapped API
//! which not only calls [`crate::memory_manager::bind_mutator`], but also registers the returned mutator
//! to MockVM.
//!
//! The singleton can be an MMTK instance of any mock VM type (see [`crate::define_mock_vm`]). It is
//! type-erased, and is checked against the expected type when it is accessed.

use super::vm;
use super::MockVM;
use crate::util::*;
use crate::vm::VMBinding;
use crate::MMTK;

/// A singleton MMTK instance for MockVM.
pub static mut MMTK_SINGLETON: *mut MMTK<MockVM> = std::ptr::null_mut();
use std::any::Any;
use std::ptr::NonNull;

/// Get the singleton MMTK instance for MockVM.
pub fn singleton() -> &'static MMTK<MockVM> {
unsafe {
assert!(!MMTK_SINGLETON.is_null(), "MMTK singleton is not set");
&*MMTK_SINGLETON
}
/// A singleton MMTK instance for the mock VM.
static mut MMTK_SINGLETON: Option<NonNull<dyn Any>> = None;

/// Get the singleton MMTK instance for the mock VM type `VM`.
pub fn singleton<VM: VMBinding>() -> &'static MMTK<VM> {
singleton_mut()
}

/// Get a mutable reference to the singleton MMTK instance for MockVM.
pub fn singleton_mut() -> &'static mut MMTK<MockVM> {
unsafe {
assert!(!MMTK_SINGLETON.is_null(), "MMTK singleton is not set");
&mut *MMTK_SINGLETON
}
/// Get a mutable reference to the singleton MMTK instance for the mock VM type `VM`.
pub fn singleton_mut<VM: VMBinding>() -> &'static mut MMTK<VM> {
let ptr = unsafe { MMTK_SINGLETON }.expect("MMTK singleton is not set");
unsafe { &mut *ptr.as_ptr() }
.downcast_mut::<MMTK<VM>>()
.unwrap_or_else(|| {
panic!(
"MMTK singleton is not a {}",
std::any::type_name::<MMTK<VM>>()
)
})
}

/// Set the singleton MMTK instance for MockVM. This method should only be called once.
pub fn set_singleton(mmtk_ptr: *mut MMTK<MockVM>) {
/// Set the singleton MMTK instance for the mock VM. This method should only be called once.
pub fn set_singleton<VM: VMBinding>(mmtk_ptr: *mut MMTK<VM>) {
unsafe {
assert!(MMTK_SINGLETON.is_null(), "MMTK singleton is already set");
MMTK_SINGLETON = mmtk_ptr;
assert!(
(*std::ptr::addr_of!(MMTK_SINGLETON)).is_none(),
"MMTK singleton is already set"
);
MMTK_SINGLETON = Some(NonNull::new(mmtk_ptr as *mut dyn Any).unwrap());
}
}

/// Bind a mutator thread to the MMTK singleton instance for MockVM.
/// For a custom mock VM type, use [`super::GenericMockVM::bind_mutator`].
pub fn bind_mutator() -> VMMutatorThread {
vm::MutatorHandle::bind()
MockVM::bind_mutator()
}
2 changes: 2 additions & 0 deletions src/util/test_util/mock_vm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ pub use mock_method::*;
mod thread_park;

pub mod mock_api;

pub use crate::define_mock_vm;
Loading
Loading