Skip to content

Commit 8e71db2

Browse files
Merge #38
38: Update libgc to use single allocator r=ltratt a=jacob-hughes `libgc_internal` now uses a single allocator for both the `Allocator` and `GlobalAlloc` trait. This PR updates `libgc` to use that. Co-authored-by: Jake Hughes <[email protected]>
2 parents 73aafc1 + bc0a28a commit 8e71db2

File tree

8 files changed

+473
-31
lines changed

8 files changed

+473
-31
lines changed

.buildbot.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,4 @@ rustup toolchain link rustgc rustgc/build/x86_64-unknown-linux-gnu/stage1
2626

2727
cargo clean
2828

29-
cargo +rustgc test --features "rustgc"
29+
cargo +rustgc test --features "rustgc" -- --test-threads=1

Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ gc_stats = []
1515

1616
[dependencies]
1717
libc = "*"
18-
libgc_internal = { git = "https://github.com/softdevteam/libgc_internal" }
1918

2019
[build-dependencies]
2120
rerun_except = "0.1"

README.md

Lines changed: 5 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,28 +5,14 @@ libgc is a garbage collector for Rust. It works by providing a garbage-collected
55

66
# Structure
77

8-
There are three repositories which make up the gc infrastructure:
9-
- **libgc** the main library which provides the `Gc<T>` smart pointer and its
8+
There are two repositories which make up the gc infrastructure:
9+
10+
* **libgc** the main library which provides the `Gc<T>` smart pointer and its
1011
API.
11-
- **libgc_internal** contains the gc allocation and collector logic. This is
12-
collector specific, and can be conditionally compiled to support different
13-
implementations. At the moment, it only supports a single collector
14-
implementation: the Boehm-Demers-Weiser GC. Users should never interact
15-
directly with this crate. Instead, any relevant APIs are re-exported
16-
through libgc.
17-
- **rustgc** a fork of rustc with GC-aware optimisations. This can be used to
12+
* **rustgc** a fork of rustc with GC-aware optimisations. This can be used to
1813
compile user programs which use `libgc`, giving them better GC
1914
performance. Use of rustgc is not mandated, but it enables further
2015
optimisations for programs which use `libgc`.
2116

2217
This seperation between libgc and rustgc exists so that a stripped-down form of
23-
garbage collection can be used without compiler support. The further split
24-
between libgc and libgc_core exists to make linkage easier when the rustgc
25-
compiler is used.
26-
27-
rustgc needs access to the GC's `Allocator` implementation. This exists in the
28-
libgc_internal crate so that it can be linked to the target binary either as
29-
part of libgc, or as part of the rust standard library (if compiled with
30-
rustgc). libgc contains code which would not compile if it was packaged as part
31-
of rustgc. To prevent duplication, the libgc_interal crate will link correctly
32-
as either a standard cargo crate, or as part of the rust core library.
18+
garbage collection can be used without compiler support.

src/allocator.rs

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
//! This library acts as a shim to prevent static linking the Boehm GC directly
2+
//! inside library/alloc which causes surprising and hard to debug errors.
3+
4+
#![allow(dead_code)]
5+
6+
use core::{
7+
alloc::{AllocError, Allocator, GlobalAlloc, Layout},
8+
ptr::NonNull,
9+
};
10+
11+
pub struct GcAllocator;
12+
13+
use crate::boehm;
14+
#[cfg(feature = "rustgc")]
15+
use crate::specializer;
16+
17+
#[cfg(feature = "rustgc")]
18+
pub(crate) static ALLOCATOR: GcAllocator = GcAllocator;
19+
20+
unsafe impl GlobalAlloc for GcAllocator {
21+
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
22+
#[cfg(feature = "rustgc")]
23+
return boehm::GC_malloc(layout.size()) as *mut u8;
24+
#[cfg(not(feature = "rustgc"))]
25+
return boehm::GC_malloc_uncollectable(layout.size()) as *mut u8;
26+
}
27+
28+
unsafe fn dealloc(&self, ptr: *mut u8, _: Layout) {
29+
boehm::GC_free(ptr);
30+
}
31+
32+
unsafe fn realloc(&self, ptr: *mut u8, _: Layout, new_size: usize) -> *mut u8 {
33+
boehm::GC_realloc(ptr, new_size) as *mut u8
34+
}
35+
36+
#[cfg(feature = "rustgc_internal")]
37+
unsafe fn alloc_precise(&self, layout: Layout, bitmap: usize, bitmap_size: usize) -> *mut u8 {
38+
let gc_descr = boehm::GC_make_descriptor(&bitmap, bitmap_size);
39+
boehm::GC_malloc_explicitly_typed(layout.size(), gc_descr) as *mut u8
40+
}
41+
42+
#[cfg(feature = "rustgc_internal")]
43+
fn alloc_conservative(&self, layout: Layout) -> *mut u8 {
44+
unsafe { boehm::GC_malloc(layout.size()) as *mut u8 }
45+
}
46+
47+
#[cfg(feature = "rustgc_internal")]
48+
unsafe fn alloc_atomic(&self, layout: Layout) -> *mut u8 {
49+
boehm::GC_malloc_atomic(layout.size()) as *mut u8
50+
}
51+
}
52+
53+
unsafe impl Allocator for GcAllocator {
54+
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
55+
let ptr = unsafe { boehm::GC_malloc(layout.size()) } as *mut u8;
56+
assert!(!ptr.is_null());
57+
let ptr = unsafe { NonNull::new_unchecked(ptr) };
58+
Ok(NonNull::slice_from_raw_parts(ptr, layout.size()))
59+
}
60+
61+
unsafe fn deallocate(&self, _: NonNull<u8>, _: Layout) {}
62+
}
63+
64+
impl GcAllocator {
65+
#[cfg(feature = "rustgc_internal")]
66+
pub fn maybe_optimised_alloc<T>(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
67+
let sp = specializer::AllocationSpecializer::new();
68+
sp.maybe_optimised_alloc::<T>(layout)
69+
}
70+
71+
pub fn force_gc() {
72+
unsafe { boehm::GC_gcollect() }
73+
}
74+
75+
pub unsafe fn register_finalizer(
76+
&self,
77+
obj: *mut u8,
78+
finalizer: Option<unsafe extern "C" fn(*mut u8, *mut u8)>,
79+
client_data: *mut u8,
80+
old_finalizer: *mut extern "C" fn(*mut u8, *mut u8),
81+
old_client_data: *mut *mut u8,
82+
) {
83+
boehm::GC_register_finalizer_no_order(
84+
obj,
85+
finalizer,
86+
client_data,
87+
old_finalizer,
88+
old_client_data,
89+
)
90+
}
91+
92+
pub fn unregister_finalizer(&self, gcbox: *mut u8) {
93+
unsafe {
94+
boehm::GC_register_finalizer(
95+
gcbox,
96+
None,
97+
::core::ptr::null_mut(),
98+
::core::ptr::null_mut(),
99+
::core::ptr::null_mut(),
100+
);
101+
}
102+
}
103+
104+
pub fn get_stats() -> GcStats {
105+
let mut ps = boehm::ProfileStats::default();
106+
unsafe {
107+
boehm::GC_get_prof_stats(
108+
&mut ps as *mut boehm::ProfileStats,
109+
core::mem::size_of::<boehm::ProfileStats>(),
110+
);
111+
}
112+
let total_gc_time = unsafe { boehm::GC_get_full_gc_total_time() };
113+
114+
GcStats {
115+
total_gc_time,
116+
num_collections: ps.gc_no,
117+
total_freed: ps.bytes_reclaimed_since_gc,
118+
total_alloced: ps.bytes_allocd_since_gc,
119+
}
120+
}
121+
122+
pub fn init() {
123+
unsafe { boehm::GC_start_performance_measurement() };
124+
}
125+
}
126+
127+
#[derive(Debug)]
128+
pub struct GcStats {
129+
total_gc_time: usize, // In milliseconds.
130+
num_collections: usize,
131+
total_freed: usize, // In bytes
132+
total_alloced: usize, // In bytes
133+
}

src/boehm.rs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
#[repr(C)]
2+
#[derive(Default)]
3+
pub struct ProfileStats {
4+
/// Heap size in bytes (including area unmapped to OS).
5+
pub(crate) heapsize_full: usize,
6+
/// Total bytes contained in free and unmapped blocks.
7+
pub(crate) free_bytes_full: usize,
8+
/// Amount of memory unmapped to OS.
9+
pub(crate) unmapped_bytes: usize,
10+
/// Number of bytes allocated since the recent collection.
11+
pub(crate) bytes_allocd_since_gc: usize,
12+
/// Number of bytes allocated before the recent collection.
13+
/// The value may wrap.
14+
pub(crate) allocd_bytes_before_gc: usize,
15+
/// Number of bytes not considered candidates for garbage collection.
16+
pub(crate) non_gc_bytes: usize,
17+
/// Garbage collection cycle number.
18+
/// The value may wrap.
19+
pub(crate) gc_no: usize,
20+
/// Number of marker threads (excluding the initiating one).
21+
pub(crate) markers_m1: usize,
22+
/// Approximate number of reclaimed bytes after recent collection.
23+
pub(crate) bytes_reclaimed_since_gc: usize,
24+
/// Approximate number of bytes reclaimed before the recent collection.
25+
/// The value may wrap.
26+
pub(crate) reclaimed_bytes_before_gc: usize,
27+
/// Number of bytes freed explicitly since the recent GC.
28+
pub(crate) expl_freed_bytes_since_gc: usize,
29+
}
30+
31+
#[link(name = "gc")]
32+
extern "C" {
33+
pub(crate) fn GC_malloc(nbytes: usize) -> *mut u8;
34+
35+
#[cfg(not(feature = "rustgc"))]
36+
pub(crate) fn GC_malloc_uncollectable(nbytes: usize) -> *mut u8;
37+
38+
pub(crate) fn GC_realloc(old: *mut u8, new_size: usize) -> *mut u8;
39+
40+
pub(crate) fn GC_free(dead: *mut u8);
41+
42+
pub(crate) fn GC_register_finalizer(
43+
ptr: *mut u8,
44+
finalizer: Option<unsafe extern "C" fn(*mut u8, *mut u8)>,
45+
client_data: *mut u8,
46+
old_finalizer: *mut extern "C" fn(*mut u8, *mut u8),
47+
old_client_data: *mut *mut u8,
48+
);
49+
50+
pub(crate) fn GC_register_finalizer_no_order(
51+
ptr: *mut u8,
52+
finalizer: Option<unsafe extern "C" fn(*mut u8, *mut u8)>,
53+
client_data: *mut u8,
54+
old_finalizer: *mut extern "C" fn(*mut u8, *mut u8),
55+
old_client_data: *mut *mut u8,
56+
);
57+
58+
pub(crate) fn GC_gcollect();
59+
60+
pub(crate) fn GC_start_performance_measurement();
61+
62+
pub(crate) fn GC_get_full_gc_total_time() -> usize;
63+
64+
pub(crate) fn GC_get_prof_stats(prof_stats: *mut ProfileStats, stats_size: usize) -> usize;
65+
66+
#[cfg(feature = "rustgc")]
67+
pub(crate) fn GC_malloc_explicitly_typed(size: usize, descriptor: usize) -> *mut u8;
68+
69+
#[cfg(feature = "rustgc")]
70+
pub(crate) fn GC_make_descriptor(bitmap: *const usize, len: usize) -> usize;
71+
72+
#[cfg(feature = "rustgc")]
73+
pub(crate) fn GC_malloc_atomic(nbytes: usize) -> *mut u8;
74+
}

src/gc.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use std::{
99
ptr::NonNull,
1010
};
1111

12-
use crate::GC_ALLOCATOR;
12+
use crate::ALLOCATOR;
1313

1414
/// This is usually a no-op, but if `gc_stats` is enabled it will setup the GC
1515
/// for profiliing.
@@ -182,7 +182,7 @@ struct GcBox<T: ?Sized>(ManuallyDrop<T>);
182182
impl<T> GcBox<T> {
183183
fn new(value: T) -> *mut GcBox<T> {
184184
let layout = Layout::new::<T>();
185-
let ptr = unsafe { GC_ALLOCATOR.allocate(layout).unwrap().as_ptr() } as *mut GcBox<T>;
185+
let ptr = ALLOCATOR.allocate(layout).unwrap().as_ptr() as *mut GcBox<T>;
186186
let gcbox = GcBox(ManuallyDrop::new(value));
187187

188188
unsafe {
@@ -196,7 +196,7 @@ impl<T> GcBox<T> {
196196

197197
fn new_from_layout(layout: Layout) -> NonNull<GcBox<MaybeUninit<T>>> {
198198
unsafe {
199-
let base_ptr = GC_ALLOCATOR.allocate(layout).unwrap().as_ptr() as *mut usize;
199+
let base_ptr = ALLOCATOR.allocate(layout).unwrap().as_ptr() as *mut usize;
200200
NonNull::new_unchecked(base_ptr as *mut GcBox<MaybeUninit<T>>)
201201
}
202202
}
@@ -214,7 +214,7 @@ impl<T> GcBox<T> {
214214
}
215215

216216
unsafe {
217-
GC_ALLOCATOR.register_finalizer(
217+
ALLOCATOR.register_finalizer(
218218
self as *mut _ as *mut u8,
219219
Some(fshim::<T>),
220220
::std::ptr::null_mut(),
@@ -225,7 +225,7 @@ impl<T> GcBox<T> {
225225
}
226226

227227
fn unregister_finalizer(&mut self) {
228-
unsafe { GC_ALLOCATOR.unregister_finalizer(self as *mut _ as *mut u8) };
228+
ALLOCATOR.unregister_finalizer(self as *mut _ as *mut u8);
229229
}
230230
}
231231

src/lib.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,28 @@
44
#![feature(alloc_layout_extra)]
55
#![feature(arbitrary_self_types)]
66
#![feature(dispatch_from_dyn)]
7+
#![feature(specialization)]
78
#![feature(nonnull_slice_from_raw_parts)]
89
#![feature(raw_vec_internals)]
910
#![feature(const_fn)]
1011
#![feature(coerce_unsized)]
1112
#![feature(unsize)]
1213
#![feature(maybe_uninit_ref)]
14+
#![feature(negative_impls)]
15+
#![allow(incomplete_features)]
1316
#[cfg(not(all(target_pointer_width = "64", target_arch = "x86_64")))]
1417
compile_error!("Requires x86_64 with 64 bit pointer width.");
1518

1619
pub mod gc;
1720
#[cfg(feature = "gc_stats")]
1821
pub mod stats;
1922

20-
pub use gc::Gc;
23+
mod allocator;
24+
mod boehm;
25+
#[cfg(feature = "rustgc")]
26+
mod specializer;
2127

22-
use libgc_internal::GcAllocator;
23-
pub use libgc_internal::GlobalAllocator;
28+
pub use allocator::GcAllocator;
29+
pub use gc::Gc;
2430

25-
static GC_ALLOCATOR: GcAllocator = GcAllocator;
26-
pub static GLOBAL_ALLOCATOR: GlobalAllocator = GlobalAllocator;
31+
pub static ALLOCATOR: GcAllocator = GcAllocator;

0 commit comments

Comments
 (0)