diff --git a/libs/context/Cargo.toml b/libs/context/Cargo.toml index b72956a..e0085c0 100644 --- a/libs/context/Cargo.toml +++ b/libs/context/Cargo.toml @@ -20,7 +20,10 @@ slog = "2.7" [features] default = [ "sync", # Support thread-safety by default + "std" ] +# Use the standard library (required for `sync`) +std = [] # This will allow multiple threads to access the garbage collector # by creating a seperate context for each. # @@ -28,6 +31,7 @@ default = [ # by requiring communication between threads. sync = [ "parking_lot", - "crossbeam-utils" + "crossbeam-utils", + "std" ] diff --git a/libs/context/src/collector.rs b/libs/context/src/collector.rs index 45d65bc..b46a30e 100644 --- a/libs/context/src/collector.rs +++ b/libs/context/src/collector.rs @@ -1,9 +1,10 @@ //! The interface to a collector -use std::fmt::{self, Debug, Formatter}; -use std::sync::Arc; -use std::ptr::NonNull; -use std::marker::PhantomData; +use core::fmt::{self, Debug, Formatter}; +use core::ptr::NonNull; +use core::marker::PhantomData; + +use alloc::sync::Arc; use slog::{Logger, o}; @@ -137,7 +138,7 @@ pub unsafe trait CollectorPtr>: Copy + Eq /// This is implemented as a /// raw pointer via [Arc::into_raw] unsafe impl> CollectorPtr for NonNull { - type Weak = std::sync::Weak; + type Weak = alloc::sync::Weak; #[inline] unsafe fn from_raw(ptr: *mut C) -> Self { @@ -150,7 +151,7 @@ unsafe impl> CollectorPtr for NonNull { unsafe fn clone_owned(&self) -> Self { let original = Arc::from_raw(self.as_ptr()); let cloned = Arc::clone(&original); - std::mem::forget(original); + core::mem::forget(original); NonNull::new_unchecked(Arc::into_raw(cloned) as *mut _) } @@ -189,7 +190,7 @@ unsafe impl> CollectorPtr for NonNull { unsafe fn create_weak(&self) -> Self::Weak { let arc = Arc::from_raw(self.as_ptr()); let weak = Arc::downgrade(&arc); - std::mem::forget(arc); + core::mem::forget(arc); weak } } @@ -288,8 +289,8 @@ unsafe impl ::zerogc::CollectorId for CollectorId { unsafe fn assume_valid_system(&self) -> &Self::System { // TODO: Make the API nicer? (avoid borrowing and indirection) assert_eq!( - std::mem::size_of::(), - std::mem::size_of::>() + core::mem::size_of::(), + core::mem::size_of::>() ); &*(self as *const CollectorId as *const CollectorRef) } diff --git a/libs/context/src/handle.rs b/libs/context/src/handle.rs index 1184b9b..5176ef9 100644 --- a/libs/context/src/handle.rs +++ b/libs/context/src/handle.rs @@ -1,9 +1,12 @@ //! Implementation of [::zerogc::GcHandle] //! //! Inspired by [Mono's Lock free Gc Handles](https://www.mono-project.com/news/2016/08/16/lock-free-gc-handles/) -use std::ptr::NonNull; -use std::marker::PhantomData; -use std::sync::atomic::{self, AtomicPtr, AtomicUsize, Ordering}; +use core::ptr::{self, NonNull}; +use core::marker::PhantomData; +use core::sync::atomic::{self, AtomicPtr, AtomicUsize, Ordering}; + +use alloc::boxed::Box; +use alloc::vec::Vec; use zerogc::{Trace, GcSafe, GcErase, GcRebrand, GcVisitor, NullTrace, TraceImmutable, GcHandleSystem, GcBindHandle}; use crate::{Gc, WeakCollectorRef, CollectorId, CollectorContext, CollectorRef, CollectionManager}; @@ -39,7 +42,7 @@ pub struct GcHandleList { } impl GcHandleList { pub fn new() -> Self { - use std::ptr::null_mut; + use core::ptr::null_mut; GcHandleList { last_bucket: AtomicPtr::new(null_mut()), last_free_slot: AtomicPtr::new(null_mut()), @@ -54,7 +57,7 @@ impl GcHandleList { debug_assert_eq!( (*slot).valid.value .load(Ordering::SeqCst), - std::ptr::null_mut() + ptr::null_mut() ); let mut last_free = self.last_free_slot .load(Ordering::Acquire); @@ -128,7 +131,7 @@ impl GcHandleList { debug_assert_eq!( (*slot).valid.value .load(Ordering::SeqCst), - std::ptr::null_mut() + ptr::null_mut() ); /* * We own the slot, initialize it to point to @@ -297,7 +300,7 @@ impl GcHandleBucket { .skip(last_alloc) { // TODO: All these fences must be horrible on ARM if slot.valid.value.compare_exchange( - std::ptr::null_mut(), value, + ptr::null_mut(), value, Ordering::AcqRel, Ordering::Relaxed ).is_ok() { @@ -547,15 +550,18 @@ impl Drop for GcHandle { debug_assert!(!inner.value .load(Ordering::SeqCst) .is_null(), - "Pointer already invalid" + "Pointer already invalid" ); let prev = inner.refcnt .fetch_sub(1, Ordering::AcqRel); match prev { 0 => { - // This should be impossible. - eprintln!("GcHandle refcnt Underflow"); - std::process::abort(); + /* + * This should be impossible. + * + * I believe it's undefined behavior! + */ + panic!("UB: GcHandle refcnt overflow") }, 1 => { // Free underlying memory @@ -564,7 +570,7 @@ impl Drop for GcHandle { } // Mark the value as freed inner.value.store( - std::ptr::null_mut(), + ptr::null_mut(), Ordering::Release ); unsafe { diff --git a/libs/context/src/lib.rs b/libs/context/src/lib.rs index d42160d..5d51cbb 100644 --- a/libs/context/src/lib.rs +++ b/libs/context/src/lib.rs @@ -3,10 +3,27 @@ untagged_unions, // I want to avoid ManuallyDrop in unions const_fn, // Apparently this feature is unstable??? )] +#![cfg_attr(not(feature = "std"), no_std)] //! The implementation of [::zerogc::CollectorContext] that is //! shared among both thread-safe and thread-unsafe code. -use std::mem::ManuallyDrop; -use std::fmt::{self, Debug, Formatter}; + +/* + * NOTE: Allocation is still needed for internals + * + * Uses: + * 1. `Box` for each handle + * 2. `Vec` for listing buckets of handles + * 3. `Arc` and `Box` for boxing context state + * + * TODO: Should we drop these uses entirely? + */ +extern crate alloc; + +use core::mem::ManuallyDrop; +use core::fmt::{self, Debug, Formatter}; + +use alloc::boxed::Box; +use alloc::vec::Vec; use zerogc::prelude::*; @@ -183,7 +200,7 @@ unsafe impl GcContext for CollectorContext { let result = func(&mut *sub_context, value); debug_assert!(!sub_context.root); // No need to run drop code on context..... - std::mem::forget(sub_context); + core::mem::forget(sub_context); debug_assert_eq!((*self.raw).state(), ContextState::Active); result }) @@ -233,7 +250,7 @@ impl ShadowStack { } #[inline] pub unsafe fn reverse_iter(&self) -> impl Iterator + '_ { - std::iter::successors( + core::iter::successors( self.last.as_ref(), |link| link.prev.as_ref() ).map(|link| link.element) diff --git a/libs/context/src/state/mod.rs b/libs/context/src/state/mod.rs index 275d286..2746b3c 100644 --- a/libs/context/src/state/mod.rs +++ b/libs/context/src/state/mod.rs @@ -1,7 +1,10 @@ use crate::collector::RawCollectorImpl; use crate::{ContextState, ShadowStack, CollectorRef}; -use std::mem::ManuallyDrop; -use std::fmt::Debug; + +use core::mem::ManuallyDrop; +use core::fmt::Debug; + +use alloc::boxed::Box; /// The internal state of the collector /// diff --git a/libs/context/src/state/nosync.rs b/libs/context/src/state/nosync.rs index 7071ad0..7439120 100644 --- a/libs/context/src/state/nosync.rs +++ b/libs/context/src/state/nosync.rs @@ -2,11 +2,15 @@ //! that doesn't support multiple threads/contexts. //! //! In exchange, there is no locking :) +//! +//! Also, there is `#![no_std]` support + +use core::cell::{Cell, UnsafeCell, RefCell}; +use core::mem::ManuallyDrop; +use core::fmt::{self, Debug, Formatter}; +use core::marker::PhantomData; -use std::cell::{Cell, UnsafeCell, RefCell}; -use std::mem::ManuallyDrop; -use std::fmt::{self, Debug, Formatter}; -use std::marker::PhantomData; +use alloc::boxed::Box; use slog::{Logger, FnValue, trace, o}; @@ -125,7 +129,7 @@ unsafe impl super::RawContext for RawContext let context = ManuallyDrop::new(Box::new(RawContext { logger: logger.clone(), collector, shadow_stack: UnsafeCell::new(ShadowStack { - last: std::ptr::null_mut(), + last: core::ptr::null_mut(), }), state: Cell::new(ContextState::Active) })); @@ -165,7 +169,7 @@ unsafe impl super::RawContext for RawContext trace!( self.logger, "Beginning collection"; "ptr" => ?ptr, - "shadow_stack" => FnValue(|_| format!("{:?}", shadow_stack.as_vec())), + "shadow_stack" => FnValue(|_| alloc::format!("{:?}", shadow_stack.as_vec())), "state" => ?self.state, "collection_id" => collection_id, "original_size" => %self.collector.as_raw().allocated_size(), diff --git a/libs/context/src/state/sync.rs b/libs/context/src/state/sync.rs index b6707f9..e48b3c5 100644 --- a/libs/context/src/state/sync.rs +++ b/libs/context/src/state/sync.rs @@ -1,4 +1,7 @@ -/// Thread safe state +//! Thread safe state +//! +//! Note that this module has full permission to use +//! the standard library in all its glory. use std::cell::{Cell, UnsafeCell}; use std::sync::atomic::{Ordering, AtomicBool}; use std::mem::ManuallyDrop; diff --git a/libs/context/src/utils.rs b/libs/context/src/utils.rs index 6209dab..98b95ca 100644 --- a/libs/context/src/utils.rs +++ b/libs/context/src/utils.rs @@ -1,9 +1,9 @@ //! Utilities for the context library //! //! Also used by some collector implementations. -use std::fmt::{self, Debug, Formatter, Display}; +use core::fmt::{self, Debug, Formatter, Display}; #[cfg(not(feature = "sync"))] -use std::cell::Cell; +use core::cell::Cell; #[cfg(feature = "sync")] pub type AtomicCell = ::crossbeam_utils::atomic::AtomicCell; @@ -39,21 +39,38 @@ impl AtomicCell { pub enum ThreadId { #[allow(unused)] Nop, + #[cfg(feature = "std")] Enabled { id: std::thread::ThreadId, name: Option } } impl ThreadId { + #[cfg(feature = "std")] pub fn current() -> ThreadId { + // NOTE: It's okay: `sync` requires std let thread = std::thread::current(); ThreadId::Enabled { id: thread.id(), name: thread.name().map(String::from) } } + #[cfg(not(feature = "std"))] + #[inline] + pub fn current() -> ThreadId { + ThreadId::Nop + } } impl slog::Value for ThreadId { + #[cfg(not(feature = "std"))] + fn serialize( + &self, _record: &slog::Record, + _key: &'static str, + _serializer: &mut dyn slog::Serializer + ) -> slog::Result<()> { + Ok(()) // Nop + } + #[cfg(feature = "std")] fn serialize( &self, _record: &slog::Record, key: &'static str, @@ -81,9 +98,11 @@ impl Debug for ThreadId { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match *self { ThreadId::Nop => f.write_str("ThreadId(??)"), + #[cfg(feature = "std")] ThreadId::Enabled { id, name: None } => { write!(f, "{:?}", id) }, + #[cfg(feature = "std")] ThreadId::Enabled { id, name: Some(ref name) } => { f.debug_tuple("ThreadId") .field(&id)