sys crate (#583)

Fix #508.
This commit is contained in:
Yota Toyama
2026-09-26 03:43:48 +00:00
committed by GitHub
parent 82135e07a7
commit 2f26b40e30
21 changed files with 436 additions and 80 deletions
+25
View File
@@ -0,0 +1,25 @@
[package]
name = "bdwgc-alloc-sys"
version.workspace = true
edition.workspace = true
description = "Rust bindings to the bdwgc C API"
documentation = "https://bdwgc.github.io/bdwgc-rust/bdwgc_alloc_sys/"
repository.workspace = true
license.workspace = true
categories = ["external-ffi-bindings", "memory-management", "no-std", "no-std::no-alloc"]
links = "gc"
[build-dependencies]
autotools = { version = "0.2.7", optional = true }
bindgen = "0.73.2"
cmake = { version = "0.1.58", optional = true }
[features]
default = ["autotools"]
[lints]
workspace = true
[[test]]
name = "collector"
harness = false
+28
View File
@@ -0,0 +1,28 @@
# bdwgc-alloc-sys
[![GitHub Action](https://img.shields.io/github/actions/workflow/status/bdwgc/bdwgc-rust/test.yaml?branch=main&style=flat-square)](https://github.com/bdwgc/bdwgc-rust/actions)
[![License](https://img.shields.io/github/license/bdwgc/bdwgc-rust.svg?style=flat-square)](https://github.com/bdwgc/bdwgc-rust/blob/main/LICENSE)
Rust bindings to the C API of [`bdwgc`][bdwgc], the conservative garbage collector.
This crate builds [`bdwgc`][bdwgc] from its vendored source and links it statically. The bindings are generated from its headers at build time by [`bindgen`](https://github.com/rust-lang/rust-bindgen), which requires `libclang`.
See the [`bdwgc-alloc`](https://crates.io/crates/bdwgc-alloc) crate for a [`GlobalAlloc`](https://doc.rust-lang.org/std/alloc/trait.GlobalAlloc.html) implementation on top of these bindings.
## Install
```sh
cargo add bdwgc-alloc-sys
```
By default [`bdwgc`][bdwgc] is built with autotools. To build with cmake, enable the `cmake` feature:
```sh
cargo add bdwgc-alloc-sys --no-default-features --features cmake
```
## License
[MIT](https://github.com/bdwgc/bdwgc-rust/blob/main/LICENSE)
[bdwgc]: https://github.com/bdwgc/bdwgc
+96
View File
@@ -0,0 +1,96 @@
//! A build script.
use core::error::Error;
use std::{env, path::PathBuf};
const LIB_ATOMIC_OPS_DIR: &str = "vendor/libatomic_ops";
const LIB_GC_DIR: &str = "vendor/bdwgc";
fn main() -> Result<(), Box<dyn Error>> {
build_library()?;
bindgen::builder()
.header(format!("{LIB_GC_DIR}/include/gc.h"))
.clang_arg(format!("-I{LIB_GC_DIR}/include"))
.clang_arg("-DGC_THREADS")
.use_core()
.allowlist_item("GC_.*")
.default_macro_constant_type(bindgen::MacroTypeVariation::Signed)
.generate()?
.write_to_file(PathBuf::from(env::var("OUT_DIR")?).join("bindings.rs"))?;
Ok(())
}
cfg_select! {
feature = "cmake" => {
fn build_library() -> Result<(), Box<dyn Error>> {
use cmake::Config;
use std::path::Path;
let libatomic_include_path = Path::new(LIB_ATOMIC_OPS_DIR)
.join("src")
.canonicalize()?
.display()
.to_string()
.replace(r"\\?\", "");
let dst = Config::new(LIB_GC_DIR)
.profile("Release")
.define("GC_BUILD_SHARED_LIBS", "FALSE")
.cflag(format!("-I{}", libatomic_include_path))
.build();
println!(
"cargo:rustc-link-search=native={}",
dst.join("lib").display()
);
println!("cargo:rustc-link-lib=static=gc");
Ok(())
}
}
feature = "autotools" => {
fn build_library() -> Result<(), Box<dyn Error>> {
for dir in &[LIB_ATOMIC_OPS_DIR, LIB_GC_DIR] {
std::process::Command::new("sh")
.arg("-c")
.arg(format!("cd {dir} && ./autogen.sh"))
.output()?;
}
let dst = autotools::Config::new(LIB_ATOMIC_OPS_DIR)
.cflag("-fPIC")
.build();
println!(
"cargo:rustc-link-search=native={}",
dst.join("lib").display()
);
println!("cargo:rustc-link-lib=static=atomic_ops");
let dst = autotools::Config::new(LIB_GC_DIR)
.cflag(format!(
// spell-checker: disable-next-line
"-I{} -L/lib/x86_64-linux-gnu -lpthread -fPIC",
dst.join("include").display()
))
.build();
println!(
"cargo:rustc-link-search=native={}",
dst.join("lib").display()
);
println!("cargo:rustc-link-lib=static=gc");
for dir in &[LIB_ATOMIC_OPS_DIR, LIB_GC_DIR] {
std::process::Command::new("sh")
.arg("-c")
.arg(format!("cd {dir} && git clean -dfx"))
.output()?;
}
Ok(())
}
}
}
+8
View File
@@ -0,0 +1,8 @@
#![doc = include_str!("../README.md")]
#![no_std]
#![allow(missing_docs)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(non_upper_case_globals)]
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
+76
View File
@@ -0,0 +1,76 @@
//! A smoke test of the bindings.
use bdwgc_alloc_sys::{
GC_SUCCESS, GC_allow_register_threads, GC_free, GC_gcollect, GC_get_stack_base, GC_init,
GC_malloc, GC_realloc, GC_register_finalizer, GC_register_my_thread, GC_stack_base,
GC_unregister_my_thread,
};
use core::{
ffi::c_void,
ptr::null_mut,
sync::atomic::{AtomicUsize, Ordering},
};
use std::thread::spawn;
const TOTAL_COUNT: usize = 1 << 10;
static FINALIZED_COUNT: AtomicUsize = AtomicUsize::new(0);
fn main() {
unsafe {
GC_init();
GC_allow_register_threads();
}
allocate();
finalize();
register_thread();
}
fn allocate() {
let ptr = unsafe { GC_malloc(42) };
assert!(!ptr.is_null());
let ptr = unsafe { GC_realloc(ptr, 84) };
assert!(!ptr.is_null());
unsafe { GC_free(ptr) };
}
fn finalize() {
extern "C" fn count(_object: *mut c_void, _client_data: *mut c_void) {
FINALIZED_COUNT.fetch_add(1, Ordering::Relaxed);
}
for _ in 0..TOTAL_COUNT {
unsafe {
GC_register_finalizer(
GC_malloc(42),
Some(count),
null_mut(),
null_mut(),
null_mut(),
);
}
}
unsafe { GC_gcollect() };
// TODO Is there any way to collect all?
assert!(FINALIZED_COUNT.load(Ordering::Relaxed) > TOTAL_COUNT / 2);
}
fn register_thread() {
spawn(|| {
let mut base = GC_stack_base {
mem_base: null_mut(),
};
assert_eq!(unsafe { GC_get_stack_base(&mut base) }, GC_SUCCESS);
assert_eq!(unsafe { GC_register_my_thread(&base) }, GC_SUCCESS);
assert!(!unsafe { GC_malloc(42) }.is_null());
assert_eq!(unsafe { GC_unregister_my_thread() }, GC_SUCCESS);
})
.join()
.unwrap();
}