Skip to content

Commit

Permalink
misc: applied suggestions from code review
Browse files Browse the repository at this point in the history
  • Loading branch information
baszalmstra committed Mar 25, 2020
1 parent 85e6f32 commit 2fcc6c9
Show file tree
Hide file tree
Showing 13 changed files with 229 additions and 225 deletions.
4 changes: 2 additions & 2 deletions crates/mun_gc/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "mun_gc"
version = "0.2.0"
version = "0.1.0"
authors = ["The Mun Team <team@mun-lang.org>"]
edition = "2018"
homepage = "https://mun-lang.org"
Expand All @@ -13,4 +13,4 @@ abi = { path = "../mun_abi", package = "mun_abi" }
parking_lot = "0.10"

[dev-dependencies]
paste = "0.1"
paste = "0.1"
91 changes: 47 additions & 44 deletions crates/mun_gc/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,89 +1,92 @@
mod handle;
mod mark_sweep;
mod root_handle;
mod ptr;
mod root_ptr;

pub use handle::{GCPtr, HasIndirectionPtr, RawGCPtr};
pub use mark_sweep::MarkSweep;
pub use root_handle::GCRootHandle;
pub use ptr::{GcPtr, HasIndirectionPtr, RawGcPtr};
pub use root_ptr::GcRootPtr;
use std::marker::PhantomData;

/// Contains stats about the current state of a GC implementation
#[derive(Debug, Clone, Default)]
pub struct Stats {
pub allocated_memory: usize,
}

/// A trait used by the GC to identify an object.
/// A trait used by the `GcRuntime` to identify an object type.
pub trait Type: Send + Sync {
type Trace: Iterator<Item = GCPtr>;
type Trace: Iterator<Item = GcPtr>;

/// Returns the size in bytes of an object of this type.
/// Returns the size of an object of this type (in bytes).
fn size(&self) -> usize;

/// Returns the alignment of a type
/// Returns the alignment of a type (in bytes).
fn alignment(&self) -> usize;

/// Returns an iterator to iterate over all GC objects that are referenced by the given object.
fn trace(&self, obj: GCPtr) -> Self::Trace;
fn trace(&self, obj: GcPtr) -> Self::Trace;
}

/// An object that can be used to allocate and collect memory.
pub trait GCRuntime<T: Type>: Send + Sync {
/// Allocates an object of the given type returning a GCPtr
fn alloc(&self, ty: T) -> GCPtr;
pub trait GcRuntime<T: Type>: Send + Sync {
/// Allocates an object of the given type returning a GcPtr
fn alloc(&self, ty: T) -> GcPtr;

/// Returns the type of the specified `obj`.
///
/// # Safety
///
/// This method is unsafe because the passed GCPtr could point to random memory.
unsafe fn ptr_type(&self, obj: GCPtr) -> T;

/// Tell the runtime that the specified object should be considered a root which keeps all other
/// objects it references alive. Objects marked as root, must also be unrooted before they can
/// be collected. Internally this increments a root refcount.
///
/// # Safety
///
/// This method is unsafe because the passed GCPtr could point to random memory.
unsafe fn root(&self, obj: GCPtr);

/// Tell the runtime that the specified object should unrooted which keeps all other
/// objects it references alive. Objects marked as root, must also be unrooted before they can
/// be collected. Internally this decrements a root refcount. When the refcount reaches 0, the
/// object is considered non-rooted.
///
/// # Safety
///
/// This method is unsafe because the passed GCPtr could point to random memory.
unsafe fn unroot(&self, obj: GCPtr);
fn ptr_type(&self, obj: GcPtr) -> T;

/// Roots the specified `obj`, which keeps its and objects it references alive. Objects marked
/// as root, must call `unroot` before they can be collected. An object can be rooted multiple
/// times, but you must make sure to call `unroot` an equal number of times before the object
/// can be collected.
fn root(&self, obj: GcPtr);

/// Unroots the specified `obj`, potentially allowing it and objects it references to be
/// collected. Internally this decrements a root refcount. An object can be rooted multiple
/// times, but you must make sure to call `unroot` an equal number of times before the object
/// can be collected.
fn unroot(&self, obj: GcPtr);

/// Returns stats about the current state of the runtime.
fn stats(&self) -> Stats;
}

/// An `Event` is an event that can be emitted by a `GcRuntime` through the use of an `Observer`.
/// This enables tracking of the runtimes behavior which is useful for testing.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Event {
/// The GC performed an allocation
Allocation(GCPtr),
Allocation(GcPtr),

/// A GC cycle started
Start,

/// A deallocation took place
Deallocation(GCPtr),
Deallocation(GcPtr),

/// A GC cycle ended
End,
}

/// A `Observer` is trait that can receive `Event`s from a GC implementation. A `GCRuntime` can
/// be typed by a `GCObserver` which enables optional tracing of events.
/// The `Observer` trait allows receiving of `Event`s. A `GcRuntime` can be typed by a `Observer`
/// which enables optional tracing of events.
pub trait Observer: Send + Sync {
fn event(&self, _event: Event) {}
type Event;

fn event(&self, _event: Self::Event) {}
}

/// A default implementation of a `Observer` which ensures that the compiler does not generate
/// code for event handling.
#[derive(Clone, Default)]
pub struct NoopObserver;
impl Observer for NoopObserver {}
#[derive(Clone)]
pub struct NoopObserver<T: Send + Sync> {
data: PhantomData<T>,
}
impl<T: Send + Sync> Observer for NoopObserver<T> {
type Event = T;
}
impl<T: Send + Sync> Default for NoopObserver<T> {
fn default() -> Self {
NoopObserver { data: PhantomData }
}
}
70 changes: 35 additions & 35 deletions crates/mun_gc/src/mark_sweep.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{Event, GCPtr, GCRuntime, Observer, RawGCPtr, Stats, Type};
use crate::{Event, GcPtr, GcRuntime, Observer, RawGcPtr, Stats, Type};
use parking_lot::RwLock;
use std::alloc::Layout;
use std::collections::{HashMap, VecDeque};
Expand All @@ -7,13 +7,13 @@ use std::pin::Pin;

/// Implements a simple mark-sweep type memory collector. Uses a HashMap of
#[derive(Debug)]
pub struct MarkSweep<T: Type + Clone, O: Observer> {
objects: RwLock<HashMap<GCPtr, Pin<Box<ObjectInfo<T>>>>>,
pub struct MarkSweep<T: Type + Clone, O: Observer<Event = Event>> {
objects: RwLock<HashMap<GcPtr, Pin<Box<ObjectInfo<T>>>>>,
observer: O,
stats: RwLock<Stats>,
}

impl<T: Type + Clone, O: Observer + Default> Default for MarkSweep<T, O> {
impl<T: Type + Clone, O: Observer<Event = Event> + Default> Default for MarkSweep<T, O> {
fn default() -> Self {
MarkSweep {
objects: RwLock::new(HashMap::new()),
Expand All @@ -23,26 +23,19 @@ impl<T: Type + Clone, O: Observer + Default> Default for MarkSweep<T, O> {
}
}

impl<T: Type + Clone, O: Observer + Default> MarkSweep<T, O> {
pub fn new() -> Self {
Default::default()
}
}

impl<T: Type + Clone, O: Observer> MarkSweep<T, O> {
impl<T: Type + Clone, O: Observer<Event = Event>> MarkSweep<T, O> {
/// Creates a `MarkSweep` memory collector with the specified `Observer`.
pub fn with_observer(observer: O) -> Self {
Self {
objects: RwLock::new(HashMap::new()),
observer,
stats: RwLock::new(Stats::default()),
}
}
}

impl<T: Type + Clone, O: Observer> MarkSweep<T, O> {
/// Allocates a block of memory
fn alloc_memory(&self, size: usize, alignment: usize) -> *mut u8 {
unsafe { std::alloc::alloc(Layout::from_size_align_unchecked(size, alignment)) }
fn alloc_memory(&self, layout: Layout) -> *mut u8 {
unsafe { std::alloc::alloc(layout) }
}

/// Returns the observer
Expand All @@ -51,10 +44,11 @@ impl<T: Type + Clone, O: Observer> MarkSweep<T, O> {
}
}

impl<T: Type + Clone, O: Observer> GCRuntime<T> for MarkSweep<T, O> {
fn alloc(&self, ty: T) -> GCPtr {
impl<T: Type + Clone, O: Observer<Event = Event>> GcRuntime<T> for MarkSweep<T, O> {
fn alloc(&self, ty: T) -> GcPtr {
let size = ty.size();
let ptr = self.alloc_memory(ty.size(), ty.alignment());
let ptr = self
.alloc_memory(unsafe { Layout::from_size_align_unchecked(ty.size(), ty.alignment()) });
let object = Box::pin(ObjectInfo {
ptr,
ty,
Expand All @@ -63,7 +57,7 @@ impl<T: Type + Clone, O: Observer> GCRuntime<T> for MarkSweep<T, O> {
});

// We want to return a pointer to the `ObjectInfo`, to be used as handle.
let handle = (object.as_ref().deref() as *const _ as RawGCPtr).into();
let handle = (object.as_ref().deref() as *const _ as RawGcPtr).into();

{
let mut objects = self.objects.write();
Expand All @@ -79,42 +73,42 @@ impl<T: Type + Clone, O: Observer> GCRuntime<T> for MarkSweep<T, O> {
handle
}

unsafe fn ptr_type(&self, handle: GCPtr) -> T {
fn ptr_type(&self, handle: GcPtr) -> T {
let _ = self.objects.read();

// Convert the handle to our internal representation
let object_info: *const ObjectInfo<T> = handle.into();

// Return the type of the object
(*object_info).ty.clone()
unsafe { (*object_info).ty.clone() }
}

unsafe fn root(&self, handle: GCPtr) {
fn root(&self, handle: GcPtr) {
let _ = self.objects.write();

// Convert the handle to our internal representation
let object_info: *mut ObjectInfo<T> = handle.into();

// Return the type of the object
(*object_info).roots += 1;
unsafe { (*object_info).roots += 1 };
}

unsafe fn unroot(&self, handle: GCPtr) {
fn unroot(&self, handle: GcPtr) {
let _ = self.objects.write();

// Convert the handle to our internal representation
let object_info: *mut ObjectInfo<T> = handle.into();

// Return the type of the object
(*object_info).roots -= 1;
unsafe { (*object_info).roots -= 1 };
}

fn stats(&self) -> Stats {
self.stats.read().clone()
}
}

impl<T: Type + Clone, O: Observer + Default> MarkSweep<T, O> {
impl<T: Type + Clone, O: Observer<Event = Event> + Default> MarkSweep<T, O> {
/// Collects all memory that is no longer referenced by rooted objects. Returns `true` if memory
/// was reclaimed, `false` otherwise.
pub fn collect(&self) -> bool {
Expand All @@ -136,7 +130,7 @@ impl<T: Type + Clone, O: Observer + Default> MarkSweep<T, O> {

// Iterate over all roots
while let Some(next) = roots.pop_front() {
let handle = (next as *const _ as RawGCPtr).into();
let handle = (next as *const _ as RawGcPtr).into();

// Trace all other objects
for reference in unsafe { (*next).ty.trace(handle) } {
Expand Down Expand Up @@ -179,10 +173,16 @@ impl<T: Type + Clone, O: Observer + Default> MarkSweep<T, O> {
}
}

/// Coloring used in the Mark Sweep phase.
#[derive(Debug, PartialEq, Eq)]
enum Color {
/// A white object has not been seen yet by the mark phase
White,

/// A gray object has been seen by the mark phase but has not yet been visited
Gray,

/// A black object has been visited by the mark phase
Black,
}

Expand All @@ -200,26 +200,26 @@ struct ObjectInfo<T: Type + Clone> {
unsafe impl<T: Type + Clone> Send for ObjectInfo<T> {}
unsafe impl<T: Type + Clone> Sync for ObjectInfo<T> {}

impl<T: Type + Clone> Into<*const ObjectInfo<T>> for GCPtr {
impl<T: Type + Clone> Into<*const ObjectInfo<T>> for GcPtr {
fn into(self) -> *const ObjectInfo<T> {
self.as_ptr() as *const ObjectInfo<T>
}
}

impl<T: Type + Clone> Into<*mut ObjectInfo<T>> for GCPtr {
impl<T: Type + Clone> Into<*mut ObjectInfo<T>> for GcPtr {
fn into(self) -> *mut ObjectInfo<T> {
self.as_ptr() as *mut ObjectInfo<T>
}
}

impl<T: Type + Clone> Into<GCPtr> for *const ObjectInfo<T> {
fn into(self) -> GCPtr {
(self as RawGCPtr).into()
impl<T: Type + Clone> Into<GcPtr> for *const ObjectInfo<T> {
fn into(self) -> GcPtr {
(self as RawGcPtr).into()
}
}

impl<T: Type + Clone> Into<GCPtr> for *mut ObjectInfo<T> {
fn into(self) -> GCPtr {
(self as RawGCPtr).into()
impl<T: Type + Clone> Into<GcPtr> for *mut ObjectInfo<T> {
fn into(self) -> GcPtr {
(self as RawGcPtr).into()
}
}
Loading

0 comments on commit 2fcc6c9

Please sign in to comment.