Skip to content

Commit ca56887

Browse files
committed
[context] Allow building in #![no_std]
See issue #25
1 parent bbb8d0b commit ca56887

File tree

8 files changed

+94
-37
lines changed

8 files changed

+94
-37
lines changed

libs/context/Cargo.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,18 @@ slog = "2.7"
2020
[features]
2121
default = [
2222
"sync", # Support thread-safety by default
23+
"std"
2324
]
25+
# Use the standard library (required for `sync`)
26+
std = []
2427
# This will allow multiple threads to access the garbage collector
2528
# by creating a seperate context for each.
2629
#
2730
# Thread safe collectors can have increased overhead
2831
# by requiring communication between threads.
2932
sync = [
3033
"parking_lot",
31-
"crossbeam-utils"
34+
"crossbeam-utils",
35+
"std"
3236
]
3337

libs/context/src/collector.rs

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
//! The interface to a collector
22
3-
use std::fmt::{self, Debug, Formatter};
4-
use std::sync::Arc;
5-
use std::ptr::NonNull;
6-
use std::marker::PhantomData;
3+
use core::fmt::{self, Debug, Formatter};
4+
use core::ptr::NonNull;
5+
use core::marker::PhantomData;
6+
7+
use alloc::sync::Arc;
78

89
use slog::{Logger, o};
910

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

142143
#[inline]
143144
unsafe fn from_raw(ptr: *mut C) -> Self {
@@ -150,7 +151,7 @@ unsafe impl<C: RawCollectorImpl<Ptr=Self>> CollectorPtr<C> for NonNull<C> {
150151
unsafe fn clone_owned(&self) -> Self {
151152
let original = Arc::from_raw(self.as_ptr());
152153
let cloned = Arc::clone(&original);
153-
std::mem::forget(original);
154+
core::mem::forget(original);
154155
NonNull::new_unchecked(Arc::into_raw(cloned) as *mut _)
155156
}
156157

@@ -189,7 +190,7 @@ unsafe impl<C: RawCollectorImpl<Ptr=Self>> CollectorPtr<C> for NonNull<C> {
189190
unsafe fn create_weak(&self) -> Self::Weak {
190191
let arc = Arc::from_raw(self.as_ptr());
191192
let weak = Arc::downgrade(&arc);
192-
std::mem::forget(arc);
193+
core::mem::forget(arc);
193194
weak
194195
}
195196
}
@@ -288,8 +289,8 @@ unsafe impl<C: RawCollectorImpl> ::zerogc::CollectorId for CollectorId<C> {
288289
unsafe fn assume_valid_system(&self) -> &Self::System {
289290
// TODO: Make the API nicer? (avoid borrowing and indirection)
290291
assert_eq!(
291-
std::mem::size_of::<Self>(),
292-
std::mem::size_of::<CollectorRef<C>>()
292+
core::mem::size_of::<Self>(),
293+
core::mem::size_of::<CollectorRef<C>>()
293294
);
294295
&*(self as *const CollectorId<C> as *const CollectorRef<C>)
295296
}

libs/context/src/handle.rs

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
//! Implementation of [::zerogc::GcHandle]
22
//!
33
//! Inspired by [Mono's Lock free Gc Handles](https://www.mono-project.com/news/2016/08/16/lock-free-gc-handles/)
4-
use std::ptr::NonNull;
5-
use std::marker::PhantomData;
6-
use std::sync::atomic::{self, AtomicPtr, AtomicUsize, Ordering};
4+
use core::ptr::{self, NonNull};
5+
use core::marker::PhantomData;
6+
use core::sync::atomic::{self, AtomicPtr, AtomicUsize, Ordering};
7+
8+
use alloc::boxed::Box;
9+
use alloc::vec::Vec;
710

811
use zerogc::{Trace, GcSafe, GcErase, GcRebrand, GcVisitor, NullTrace, TraceImmutable, GcHandleSystem, GcBindHandle};
912
use crate::{Gc, WeakCollectorRef, CollectorId, CollectorContext, CollectorRef, CollectionManager};
@@ -39,7 +42,7 @@ pub struct GcHandleList<C: RawHandleImpl> {
3942
}
4043
impl<C: RawHandleImpl> GcHandleList<C> {
4144
pub fn new() -> Self {
42-
use std::ptr::null_mut;
45+
use core::ptr::null_mut;
4346
GcHandleList {
4447
last_bucket: AtomicPtr::new(null_mut()),
4548
last_free_slot: AtomicPtr::new(null_mut()),
@@ -54,7 +57,7 @@ impl<C: RawHandleImpl> GcHandleList<C> {
5457
debug_assert_eq!(
5558
(*slot).valid.value
5659
.load(Ordering::SeqCst),
57-
std::ptr::null_mut()
60+
ptr::null_mut()
5861
);
5962
let mut last_free = self.last_free_slot
6063
.load(Ordering::Acquire);
@@ -128,7 +131,7 @@ impl<C: RawHandleImpl> GcHandleList<C> {
128131
debug_assert_eq!(
129132
(*slot).valid.value
130133
.load(Ordering::SeqCst),
131-
std::ptr::null_mut()
134+
ptr::null_mut()
132135
);
133136
/*
134137
* We own the slot, initialize it to point to
@@ -297,7 +300,7 @@ impl<C: RawHandleImpl> GcHandleBucket<C> {
297300
.skip(last_alloc) {
298301
// TODO: All these fences must be horrible on ARM
299302
if slot.valid.value.compare_exchange(
300-
std::ptr::null_mut(), value,
303+
ptr::null_mut(), value,
301304
Ordering::AcqRel,
302305
Ordering::Relaxed
303306
).is_ok() {
@@ -547,15 +550,18 @@ impl<T: GcSafe, C: RawHandleImpl> Drop for GcHandle<T, C> {
547550
debug_assert!(!inner.value
548551
.load(Ordering::SeqCst)
549552
.is_null(),
550-
"Pointer already invalid"
553+
"Pointer already invalid"
551554
);
552555
let prev = inner.refcnt
553556
.fetch_sub(1, Ordering::AcqRel);
554557
match prev {
555558
0 => {
556-
// This should be impossible.
557-
eprintln!("GcHandle refcnt Underflow");
558-
std::process::abort();
559+
/*
560+
* This should be impossible.
561+
*
562+
* I believe it's undefined behavior!
563+
*/
564+
panic!("UB: GcHandle refcnt overflow")
559565
},
560566
1 => {
561567
// Free underlying memory
@@ -564,7 +570,7 @@ impl<T: GcSafe, C: RawHandleImpl> Drop for GcHandle<T, C> {
564570
}
565571
// Mark the value as freed
566572
inner.value.store(
567-
std::ptr::null_mut(),
573+
ptr::null_mut(),
568574
Ordering::Release
569575
);
570576
unsafe {

libs/context/src/lib.rs

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,27 @@
33
untagged_unions, // I want to avoid ManuallyDrop in unions
44
const_fn, // Apparently this feature is unstable???
55
)]
6+
#![cfg_attr(not(feature = "std"), no_std)]
67
//! The implementation of [::zerogc::CollectorContext] that is
78
//! shared among both thread-safe and thread-unsafe code.
8-
use std::mem::ManuallyDrop;
9-
use std::fmt::{self, Debug, Formatter};
9+
10+
/*
11+
* NOTE: Allocation is still needed for internals
12+
*
13+
* Uses:
14+
* 1. `Box` for each handle
15+
* 2. `Vec` for listing buckets of handles
16+
* 3. `Arc` and `Box` for boxing context state
17+
*
18+
* TODO: Should we drop these uses entirely?
19+
*/
20+
extern crate alloc;
21+
22+
use core::mem::ManuallyDrop;
23+
use core::fmt::{self, Debug, Formatter};
24+
25+
use alloc::boxed::Box;
26+
use alloc::vec::Vec;
1027

1128
use zerogc::prelude::*;
1229

@@ -183,7 +200,7 @@ unsafe impl<C: RawCollectorImpl> GcContext for CollectorContext<C> {
183200
let result = func(&mut *sub_context, value);
184201
debug_assert!(!sub_context.root);
185202
// No need to run drop code on context.....
186-
std::mem::forget(sub_context);
203+
core::mem::forget(sub_context);
187204
debug_assert_eq!((*self.raw).state(), ContextState::Active);
188205
result
189206
})
@@ -233,7 +250,7 @@ impl<C: RawCollectorImpl> ShadowStack<C> {
233250
}
234251
#[inline]
235252
pub unsafe fn reverse_iter(&self) -> impl Iterator<Item=C::GcDynPointer> + '_ {
236-
std::iter::successors(
253+
core::iter::successors(
237254
self.last.as_ref(),
238255
|link| link.prev.as_ref()
239256
).map(|link| link.element)

libs/context/src/state/mod.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
use crate::collector::RawCollectorImpl;
22
use crate::{ContextState, ShadowStack, CollectorRef};
3-
use std::mem::ManuallyDrop;
4-
use std::fmt::Debug;
3+
4+
use core::mem::ManuallyDrop;
5+
use core::fmt::Debug;
6+
7+
use alloc::boxed::Box;
58

69
/// The internal state of the collector
710
///

libs/context/src/state/nosync.rs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,15 @@
22
//! that doesn't support multiple threads/contexts.
33
//!
44
//! In exchange, there is no locking :)
5+
//!
6+
//! Also, there is `#![no_std]` support
7+
8+
use core::cell::{Cell, UnsafeCell, RefCell};
9+
use core::mem::ManuallyDrop;
10+
use core::fmt::{self, Debug, Formatter};
11+
use core::marker::PhantomData;
512

6-
use std::cell::{Cell, UnsafeCell, RefCell};
7-
use std::mem::ManuallyDrop;
8-
use std::fmt::{self, Debug, Formatter};
9-
use std::marker::PhantomData;
13+
use alloc::boxed::Box;
1014

1115
use slog::{Logger, FnValue, trace, o};
1216

@@ -125,7 +129,7 @@ unsafe impl<C> super::RawContext<C> for RawContext<C>
125129
let context = ManuallyDrop::new(Box::new(RawContext {
126130
logger: logger.clone(), collector,
127131
shadow_stack: UnsafeCell::new(ShadowStack {
128-
last: std::ptr::null_mut(),
132+
last: core::ptr::null_mut(),
129133
}),
130134
state: Cell::new(ContextState::Active)
131135
}));
@@ -165,7 +169,7 @@ unsafe impl<C> super::RawContext<C> for RawContext<C>
165169
trace!(
166170
self.logger, "Beginning collection";
167171
"ptr" => ?ptr,
168-
"shadow_stack" => FnValue(|_| format!("{:?}", shadow_stack.as_vec())),
172+
"shadow_stack" => FnValue(|_| alloc::format!("{:?}", shadow_stack.as_vec())),
169173
"state" => ?self.state,
170174
"collection_id" => collection_id,
171175
"original_size" => %self.collector.as_raw().allocated_size(),

libs/context/src/state/sync.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
/// Thread safe state
1+
//! Thread safe state
2+
//!
3+
//! Note that this module has full permission to use
4+
//! the standard library in all its glory.
25
use std::cell::{Cell, UnsafeCell};
36
use std::sync::atomic::{Ordering, AtomicBool};
47
use std::mem::ManuallyDrop;

libs/context/src/utils.rs

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
//! Utilities for the context library
22
//!
33
//! Also used by some collector implementations.
4-
use std::fmt::{self, Debug, Formatter, Display};
4+
use core::fmt::{self, Debug, Formatter, Display};
55
#[cfg(not(feature = "sync"))]
6-
use std::cell::Cell;
6+
use core::cell::Cell;
77

88
#[cfg(feature = "sync")]
99
pub type AtomicCell<T> = ::crossbeam_utils::atomic::AtomicCell<T>;
@@ -39,21 +39,38 @@ impl<T: Copy> AtomicCell<T> {
3939
pub enum ThreadId {
4040
#[allow(unused)]
4141
Nop,
42+
#[cfg(feature = "std")]
4243
Enabled {
4344
id: std::thread::ThreadId,
4445
name: Option<String>
4546
}
4647
}
4748
impl ThreadId {
49+
#[cfg(feature = "std")]
4850
pub fn current() -> ThreadId {
51+
// NOTE: It's okay: `sync` requires std
4952
let thread = std::thread::current();
5053
ThreadId::Enabled {
5154
id: thread.id(),
5255
name: thread.name().map(String::from)
5356
}
5457
}
58+
#[cfg(not(feature = "std"))]
59+
#[inline]
60+
pub fn current() -> ThreadId {
61+
ThreadId::Nop
62+
}
5563
}
5664
impl slog::Value for ThreadId {
65+
#[cfg(not(feature = "std"))]
66+
fn serialize(
67+
&self, _record: &slog::Record,
68+
_key: &'static str,
69+
_serializer: &mut dyn slog::Serializer
70+
) -> slog::Result<()> {
71+
Ok(()) // Nop
72+
}
73+
#[cfg(feature = "std")]
5774
fn serialize(
5875
&self, _record: &slog::Record,
5976
key: &'static str,
@@ -81,9 +98,11 @@ impl Debug for ThreadId {
8198
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
8299
match *self {
83100
ThreadId::Nop => f.write_str("ThreadId(??)"),
101+
#[cfg(feature = "std")]
84102
ThreadId::Enabled { id, name: None } => {
85103
write!(f, "{:?}", id)
86104
},
105+
#[cfg(feature = "std")]
87106
ThreadId::Enabled { id, name: Some(ref name) } => {
88107
f.debug_tuple("ThreadId")
89108
.field(&id)

0 commit comments

Comments
 (0)