Skip to content

Commit

Permalink
[context] Allow building in #![no_std]
Browse files Browse the repository at this point in the history
See issue #25
  • Loading branch information
Techcable committed Apr 21, 2021
1 parent bbb8d0b commit ca56887
Show file tree
Hide file tree
Showing 8 changed files with 94 additions and 37 deletions.
6 changes: 5 additions & 1 deletion libs/context/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,18 @@ 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.
#
# Thread safe collectors can have increased overhead
# by requiring communication between threads.
sync = [
"parking_lot",
"crossbeam-utils"
"crossbeam-utils",
"std"
]

19 changes: 10 additions & 9 deletions libs/context/src/collector.rs
Original file line number Diff line number Diff line change
@@ -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};

Expand Down Expand Up @@ -137,7 +138,7 @@ pub unsafe trait CollectorPtr<C: RawCollectorImpl<Ptr=Self>>: Copy + Eq
/// This is implemented as a
/// raw pointer via [Arc::into_raw]
unsafe impl<C: RawCollectorImpl<Ptr=Self>> CollectorPtr<C> for NonNull<C> {
type Weak = std::sync::Weak<C>;
type Weak = alloc::sync::Weak<C>;

#[inline]
unsafe fn from_raw(ptr: *mut C) -> Self {
Expand All @@ -150,7 +151,7 @@ unsafe impl<C: RawCollectorImpl<Ptr=Self>> CollectorPtr<C> for NonNull<C> {
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 _)
}

Expand Down Expand Up @@ -189,7 +190,7 @@ unsafe impl<C: RawCollectorImpl<Ptr=Self>> CollectorPtr<C> for NonNull<C> {
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
}
}
Expand Down Expand Up @@ -288,8 +289,8 @@ unsafe impl<C: RawCollectorImpl> ::zerogc::CollectorId for CollectorId<C> {
unsafe fn assume_valid_system(&self) -> &Self::System {
// TODO: Make the API nicer? (avoid borrowing and indirection)
assert_eq!(
std::mem::size_of::<Self>(),
std::mem::size_of::<CollectorRef<C>>()
core::mem::size_of::<Self>(),
core::mem::size_of::<CollectorRef<C>>()
);
&*(self as *const CollectorId<C> as *const CollectorRef<C>)
}
Expand Down
30 changes: 18 additions & 12 deletions libs/context/src/handle.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down Expand Up @@ -39,7 +42,7 @@ pub struct GcHandleList<C: RawHandleImpl> {
}
impl<C: RawHandleImpl> GcHandleList<C> {
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()),
Expand All @@ -54,7 +57,7 @@ impl<C: RawHandleImpl> GcHandleList<C> {
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);
Expand Down Expand Up @@ -128,7 +131,7 @@ impl<C: RawHandleImpl> GcHandleList<C> {
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
Expand Down Expand Up @@ -297,7 +300,7 @@ impl<C: RawHandleImpl> GcHandleBucket<C> {
.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() {
Expand Down Expand Up @@ -547,15 +550,18 @@ impl<T: GcSafe, C: RawHandleImpl> Drop for GcHandle<T, C> {
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
Expand All @@ -564,7 +570,7 @@ impl<T: GcSafe, C: RawHandleImpl> Drop for GcHandle<T, C> {
}
// Mark the value as freed
inner.value.store(
std::ptr::null_mut(),
ptr::null_mut(),
Ordering::Release
);
unsafe {
Expand Down
25 changes: 21 additions & 4 deletions libs/context/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;

Expand Down Expand Up @@ -183,7 +200,7 @@ unsafe impl<C: RawCollectorImpl> GcContext for CollectorContext<C> {
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
})
Expand Down Expand Up @@ -233,7 +250,7 @@ impl<C: RawCollectorImpl> ShadowStack<C> {
}
#[inline]
pub unsafe fn reverse_iter(&self) -> impl Iterator<Item=C::GcDynPointer> + '_ {
std::iter::successors(
core::iter::successors(
self.last.as_ref(),
|link| link.prev.as_ref()
).map(|link| link.element)
Expand Down
7 changes: 5 additions & 2 deletions libs/context/src/state/mod.rs
Original file line number Diff line number Diff line change
@@ -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
///
Expand Down
16 changes: 10 additions & 6 deletions libs/context/src/state/nosync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand Down Expand Up @@ -125,7 +129,7 @@ unsafe impl<C> super::RawContext<C> for RawContext<C>
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)
}));
Expand Down Expand Up @@ -165,7 +169,7 @@ unsafe impl<C> super::RawContext<C> for RawContext<C>
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(),
Expand Down
5 changes: 4 additions & 1 deletion libs/context/src/state/sync.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
23 changes: 21 additions & 2 deletions libs/context/src/utils.rs
Original file line number Diff line number Diff line change
@@ -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<T> = ::crossbeam_utils::atomic::AtomicCell<T>;
Expand Down Expand Up @@ -39,21 +39,38 @@ impl<T: Copy> AtomicCell<T> {
pub enum ThreadId {
#[allow(unused)]
Nop,
#[cfg(feature = "std")]
Enabled {
id: std::thread::ThreadId,
name: Option<String>
}
}
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,
Expand Down Expand Up @@ -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)
Expand Down

0 comments on commit ca56887

Please # to comment.