Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

som-gc: Custom Mark-and-Sweep Garbage Collector #33

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
Open
5 changes: 5 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ members = [
"som-parser-core",
"som-parser-symbols",
"som-parser-text",
"som-gc",
]

[profile.release]
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ Here is a rundown of these different crates (as of now, layout may change in the
| **`som-parser-core`** | The common foundational types for building parsers |
| **`som-parser-text`** | A SOM parser that works directly with text (without a lexer). |
| **`som-parser-symbols`** | A SOM parser that works with **`som-lexer`**'s output. |
| **`som-gc`** | The SOM mark-and-sweep garbage collector as a library. |

[**Cargo workspace**]: https://doc.rust-lang.org/cargo/reference/workspaces.html

Expand Down
8 changes: 8 additions & 0 deletions som-gc/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[package]
name = "som-gc"
version = "0.1.0"
description = "A garbage collector for the Simple Object Machine"
authors = ["Nicolas Polomack <[email protected]>"]
edition = "2021"
publish = false
license = "MIT OR Apache-2.0"
4 changes: 4 additions & 0 deletions som-gc/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
The SOM Garbage Collector
=========================

This is the mark-and-sweep garbage collector implementation for `som-rs`.
168 changes: 168 additions & 0 deletions som-gc/src/gc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
use std::borrow::Borrow;
use std::cell::Cell;
use std::fmt;
use std::hash::Hash;
use std::ops::Deref;
use std::ptr::NonNull;

use crate::gc_box::GcBox;
use crate::trace::Trace;

/// Represent a handle to a GC-allocated value.
pub struct Gc<T>
where
T: Trace + 'static,
{
/// The pointer to the referenced `GcBox<T>`.
pub(crate) ptr: Cell<NonNull<GcBox<T>>>,
}

impl<T> Gc<T>
where
T: Trace + 'static,
{
#[inline]
pub fn as_mut_ptr(&self) -> *const T {
unsafe { &mut self.ptr.get().as_mut().value as *mut T }
}

#[inline]
pub fn as_ptr(&self) -> *const T {
unsafe { &self.ptr.get().as_ref().value as *const T }
}

#[inline]
pub fn ptr_eq(&self, other: &Self) -> bool {
self.ptr == other.ptr
}
}

impl<T> Gc<T>
where
T: Deref + Trace + 'static,
{
#[inline]
pub fn as_deref(&self) -> &T::Target {
&**self
}
}

impl<T> fmt::Debug for Gc<T>
where
T: fmt::Debug + Trace + 'static,
{
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.as_ref().fmt(f)
}
}

impl<T> fmt::Display for Gc<T>
where
T: fmt::Display + Trace + 'static,
{
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.as_ref().fmt(f)
}
}

impl<T> PartialEq for Gc<T>
where
T: PartialEq + Trace + 'static,
{
#[inline]
fn eq(&self, other: &Self) -> bool {
self.as_ref().eq(other.as_ref())
}
}

impl<T> Eq for Gc<T> where T: Eq + Trace + 'static {}

impl<T> Hash for Gc<T>
where
T: Hash + Trace + 'static,
{
#[inline]
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.as_ref().hash(state)
}
}

impl<T> PartialOrd for Gc<T>
where
T: PartialOrd + Trace + 'static,
{
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.as_ref().partial_cmp(other.as_ref())
}
}

impl<T> Ord for Gc<T>
where
T: Ord + Trace + 'static,
{
#[inline]
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.as_ref().cmp(other.as_ref())
}
}

impl<T> Borrow<T> for Gc<T>
where
T: Trace + 'static,
{
#[inline]
fn borrow(&self) -> &T {
&*self
}
}

impl<T> AsRef<T> for Gc<T>
where
T: Trace + 'static,
{
#[inline]
fn as_ref(&self) -> &T {
&*self
}
}

impl<T> Clone for Gc<T>
where
T: Trace + 'static,
{
#[inline]
fn clone(&self) -> Self {
Self {
ptr: self.ptr.clone(),
}
}
}

impl<T> Trace for Gc<T>
where
T: Trace + 'static,
{
#[inline]
fn trace(&self) {
let ptr = unsafe { self.ptr.get().as_mut() };
if !ptr.is_marked() {
ptr.mark();
ptr.value.trace();
}
}
}

impl<T> Deref for Gc<T>
where
T: Trace + 'static,
{
type Target = T;

#[inline]
fn deref(&self) -> &Self::Target {
unsafe { self.ptr.get().as_ref() }
}
}
67 changes: 67 additions & 0 deletions som-gc/src/gc_box.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
use std::cell::Cell;
use std::ops::{Deref, DerefMut};

/// Represents a value, as it is stored within the GC.
#[repr(C)]
pub(crate) struct GcBox<T: ?Sized> {
/// Pointer to next value in the GC chain.
pub(crate) marked: Cell<bool>,
pub(crate) value: T,
}

impl<T> GcBox<T> {
/// Creates a singleton `GcBox` value (which isn't part of any chain yet).
#[inline]
pub fn new(value: T) -> Self {
Self {
marked: Cell::new(false),
value,
}
}
}

impl<T: ?Sized> GcBox<T> {
/// Clears the `mark` bit for this GC object.
#[inline]
pub fn clear_mark(&mut self) {
self.marked.set(false);
}

/// Sets the `mark` bit for this GC object.
#[inline]
pub fn mark(&mut self) {
self.marked.set(true);
}

/// Returns whether the `mark` bit is set for this GC object.
#[inline]
pub fn is_marked(&self) -> bool {
self.marked.get()
}
}

impl<T: Default> Default for GcBox<T> {
#[inline]
fn default() -> Self {
Self {
marked: Cell::new(false),
value: T::default(),
}
}
}

impl<T> Deref for GcBox<T> {
type Target = T;

#[inline]
fn deref(&self) -> &Self::Target {
&self.value
}
}

impl<T> DerefMut for GcBox<T> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.value
}
}
Loading
Loading