feat: add dependency

This commit is contained in:
2023-01-20 22:36:19 +08:00
parent 68e8d103b4
commit cf8e579f27
644 changed files with 150099 additions and 14 deletions

View File

@@ -0,0 +1,115 @@
use crate::{finalizer_safe, trace::Trace, Finalize, Gc, GcBox};
use std::{cell::Cell, ptr::NonNull};
/// The inner allocation of an [`Ephemeron`][crate::Ephemeron] pointer.
pub(crate) struct EphemeronBox<K: Trace + ?Sized + 'static, V: Trace + ?Sized + 'static> {
key: Cell<Option<NonNull<GcBox<K>>>>,
value: V,
}
impl<K: Trace + ?Sized, V: Trace> EphemeronBox<K, V> {
pub(crate) fn new(key: &Gc<K>, value: V) -> Self {
Self {
key: Cell::new(Some(key.inner_ptr())),
value,
}
}
}
impl<K: Trace + ?Sized, V: Trace + ?Sized> EphemeronBox<K, V> {
/// Checks if the key pointer is marked by Trace
pub(crate) fn is_marked(&self) -> bool {
self.inner_key().map_or(false, GcBox::is_marked)
}
/// Returns some pointer to the `key`'s `GcBox` or None
/// # Panics
/// This method will panic if called while the garbage collector is dropping.
pub(crate) fn inner_key_ptr(&self) -> Option<*mut GcBox<K>> {
assert!(finalizer_safe());
self.key.get().map(NonNull::as_ptr)
}
/// Returns some reference to `key`'s `GcBox` or None
pub(crate) fn inner_key(&self) -> Option<&GcBox<K>> {
// SAFETY: This is safe as `EphemeronBox::inner_key_ptr()` will
// fetch either a live `GcBox` or None. The value of `key` is set
// to None in the case where `EphemeronBox` and `key`'s `GcBox`
// entered into `Collector::sweep()` as unmarked.
unsafe { self.inner_key_ptr().map(|inner_key| &*inner_key) }
}
/// Returns a reference to the value of `key`'s `GcBox`
pub(crate) fn key(&self) -> Option<&K> {
self.inner_key().map(GcBox::value)
}
/// Returns a reference to `value`
pub(crate) const fn value(&self) -> &V {
&self.value
}
/// Calls [`Trace::weak_trace()`][crate::Trace] on key
fn weak_trace_key(&self) {
if let Some(key) = self.inner_key() {
key.weak_trace_inner();
}
}
/// Calls [`Trace::weak_trace()`][crate::Trace] on value
fn weak_trace_value(&self) {
// SAFETY: Value is a sized element that must implement trace. The
// operation is safe as EphemeronBox owns value and `Trace::weak_trace`
// must be implemented on it
unsafe {
self.value().weak_trace();
}
}
}
// `EphemeronBox`'s Finalize is special in that if it is determined to be unreachable
// and therefore so has the `GcBox` that `key`stores the pointer to, then we set `key`
// to None to guarantee that we do not access freed memory.
impl<K: Trace + ?Sized, V: Trace + ?Sized> Finalize for EphemeronBox<K, V> {
fn finalize(&self) {
self.key.set(None);
}
}
// SAFETY: EphemeronBox implements primarly two methods of trace `Trace::is_marked_ephemeron`
// to determine whether the key field is stored and `Trace::weak_trace` which continues the `Trace::weak_trace()`
// into `key` and `value`.
unsafe impl<K: Trace + ?Sized, V: Trace + ?Sized> Trace for EphemeronBox<K, V> {
unsafe fn trace(&self) {
/* An ephemeron is never traced with Phase One Trace */
}
/// Checks if the `key`'s `GcBox` has been marked by `Trace::trace()` or `Trace::weak_trace`.
fn is_marked_ephemeron(&self) -> bool {
self.is_marked()
}
/// Checks if this `EphemeronBox` has already been determined reachable. If so, continue to trace
/// value in `key` and `value`.
unsafe fn weak_trace(&self) {
if self.is_marked() {
self.weak_trace_key();
self.weak_trace_value();
}
}
// EphemeronBox does not implement root.
unsafe fn root(&self) {}
// EphemeronBox does not implement unroot
unsafe fn unroot(&self) {}
// An `EphemeronBox`'s key is set to None once it has been finalized.
//
// NOTE: while it is possible for the `key`'s pointer value to be
// resurrected, we should still consider the finalize the ephemeron
// box and set the `key` to None.
fn run_finalizer(&self) {
Finalize::finalize(self);
}
}

View File

@@ -0,0 +1,185 @@
use crate::Trace;
use std::{
cell::Cell,
fmt,
ptr::{self, NonNull},
};
// Age and Weak Flags
const MARK_MASK: usize = 1 << (usize::BITS - 2);
const WEAK_MASK: usize = 1 << (usize::BITS - 1);
const ROOTS_MASK: usize = !(MARK_MASK | WEAK_MASK);
const ROOTS_MAX: usize = ROOTS_MASK;
/// The `GcBoxheader` contains the `GcBox`'s current state for the `Collector`'s
/// Mark/Sweep as well as a pointer to the next node in the heap.
///
/// These flags include:
/// - Root Count
/// - Mark Flag Bit
/// - Weak Flag Bit
///
/// The next node is set by the `Allocator` during initialization and by the
/// `Collector` during the sweep phase.
pub(crate) struct GcBoxHeader {
roots: Cell<usize>,
pub(crate) next: Cell<Option<NonNull<GcBox<dyn Trace>>>>,
}
impl GcBoxHeader {
/// Creates a new `GcBoxHeader` with a root of 1 and next set to None.
pub(crate) fn new() -> Self {
Self {
roots: Cell::new(1),
next: Cell::new(None),
}
}
/// Creates a new `GcBoxHeader` with the Weak bit at 1 and roots of 1.
pub(crate) fn new_weak() -> Self {
// Set weak_flag
Self {
roots: Cell::new(WEAK_MASK | 1),
next: Cell::new(None),
}
}
/// Returns the `GcBoxHeader`'s current root count
pub(crate) fn roots(&self) -> usize {
self.roots.get() & ROOTS_MASK
}
/// Increments `GcBoxHeader`'s root count.
pub(crate) fn inc_roots(&self) {
let roots = self.roots.get();
if (roots & ROOTS_MASK) < ROOTS_MAX {
self.roots.set(roots + 1);
} else {
// TODO: implement a better way to handle root overload.
panic!("roots counter overflow");
}
}
/// Decreases `GcBoxHeader`'s current root count.
pub(crate) fn dec_roots(&self) {
// Underflow check as a stop gap for current issue when dropping.
if self.roots.get() > 0 {
self.roots.set(self.roots.get() - 1);
}
}
/// Returns a bool for whether `GcBoxHeader`'s mark bit is 1.
pub(crate) fn is_marked(&self) -> bool {
self.roots.get() & MARK_MASK != 0
}
/// Sets `GcBoxHeader`'s mark bit to 1.
pub(crate) fn mark(&self) {
self.roots.set(self.roots.get() | MARK_MASK);
}
/// Sets `GcBoxHeader`'s mark bit to 0.
pub(crate) fn unmark(&self) {
self.roots.set(self.roots.get() & !MARK_MASK);
}
/// Returns a bool for whether the `GcBoxHeader`'s weak bit is 1.
pub(crate) fn is_ephemeron(&self) -> bool {
self.roots.get() & WEAK_MASK != 0
}
}
impl fmt::Debug for GcBoxHeader {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("GcBoxHeader")
.field("Roots", &self.roots())
.field("Weak", &self.is_ephemeron())
.field("Marked", &self.is_marked())
.finish()
}
}
/// A garbage collected allocation.
#[derive(Debug)]
pub struct GcBox<T: Trace + ?Sized + 'static> {
pub(crate) header: GcBoxHeader,
pub(crate) value: T,
}
impl<T: Trace> GcBox<T> {
/// Returns a new `GcBox` with a rooted `GcBoxHeader`.
pub(crate) fn new(value: T) -> Self {
Self {
header: GcBoxHeader::new(),
value,
}
}
/// Returns a new `GcBox` with a rooted and weak `GcBoxHeader`.
pub(crate) fn new_weak(value: T) -> Self {
Self {
header: GcBoxHeader::new_weak(),
value,
}
}
}
impl<T: Trace + ?Sized> GcBox<T> {
/// Returns `true` if the two references refer to the same `GcBox`.
pub(crate) fn ptr_eq(this: &Self, other: &Self) -> bool {
// Use .header to ignore fat pointer vtables, to work around
// https://github.com/rust-lang/rust/issues/46139
ptr::eq(&this.header, &other.header)
}
/// Marks this `GcBox` and marks through its data.
pub(crate) unsafe fn trace_inner(&self) {
if !self.header.is_marked() && !self.header.is_ephemeron() {
self.header.mark();
// SAFETY: if `GcBox::trace_inner()` has been called, then,
// this box must have been deemed as reachable via tracing
// from a root, which by extension means that value has not
// been dropped either.
unsafe {
self.value.trace();
}
}
}
/// Trace inner data and search for ephemerons to add to the ephemeron queue.
pub(crate) fn weak_trace_inner(&self) {
if !self.header.is_marked() && !self.header.is_ephemeron() {
self.header.mark();
// SAFETY: if a `GcBox` has `weak_trace_inner` called, then the inner.
// value must have been deemed as reachable.
unsafe {
self.value.weak_trace();
}
}
}
/// Increases the root count on this `GcBox`.
///
/// Roots prevent the `GcBox` from being destroyed by the garbage collector.
pub(crate) fn root_inner(&self) {
self.header.inc_roots();
}
/// Decreases the root count on this `GcBox`.
///
/// Roots prevent the `GcBox` from being destroyed by the garbage collector.
pub(crate) fn unroot_inner(&self) {
self.header.dec_roots();
}
/// Returns a reference to the `GcBox`'s value.
pub(crate) const fn value(&self) -> &T {
&self.value
}
/// Returns a bool for whether the header is marked.
pub(crate) fn is_marked(&self) -> bool {
self.header.is_marked()
}
}

View File

@@ -0,0 +1,5 @@
mod ephemeron_box;
mod gc_box;
pub(crate) use self::ephemeron_box::EphemeronBox;
pub use self::gc_box::GcBox;