Skip to content

Commit

Permalink
Deadlock prevention
Browse files Browse the repository at this point in the history
Also removed the collected function/macro.
  • Loading branch information
ecton committed Mar 24, 2024
1 parent c3e909d commit 43f7f5e
Show file tree
Hide file tree
Showing 15 changed files with 223 additions and 177 deletions.
55 changes: 26 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,35 +4,32 @@ An easy-to-use, incremental, multi-threaded garbage collector for Rust.

```rust
//! A basic usage example demonstrating the garbage collector.
use refuse::{collected, CollectionGuard, Ref, Root};

#[collected]
fn main() {
let guard = CollectionGuard::acquire();
// Allocate a vec![Ref(1), Ref(2), Ref(3)].
let values: Vec<Ref<u32>> = (1..=3).map(|value| Ref::new(value, &guard)).collect();
let values = Root::new(values, &guard);
drop(guard);

// Manually execute the garbage collector. Our data will not be freed,
// since `values` is a "root" reference.
refuse::collect();

// Root references allow direct access to their data, even when a
// `CollectionGuard` isn't held.
let (one, two, three) = (values[0], values[1], values[2]);

// Accessing the data contained in a `Ref` requires a guard, however.
let mut guard = CollectionGuard::acquire();
assert_eq!(one.load(&guard), Some(&1));
assert_eq!(two.load(&guard), Some(&2));
assert_eq!(three.load(&guard), Some(&3));

// Dropping our root will allow the collector to free our `Ref`s.
drop(values);
guard.collect();
assert_eq!(one.load(&guard), None);
}
use refuse::{CollectionGuard, Ref, Root};

let guard = CollectionGuard::acquire();
// Allocate a vec![Ref(1), Ref(2), Ref(3)].
let values: Vec<Ref<u32>> = (1..=3).map(|value| Ref::new(value, &guard)).collect();
let values = Root::new(values, &guard);
drop(guard);

// Manually execute the garbage collector. Our data will not be freed,
// since `values` is a "root" reference.
refuse::collect();

// Root references allow direct access to their data, even when a
// `CollectionGuard` isn't held.
let (one, two, three) = (values[0], values[1], values[2]);

// Accessing the data contained in a `Ref` requires a guard, however.
let mut guard = CollectionGuard::acquire();
assert_eq!(one.load(&guard), Some(&1));
assert_eq!(two.load(&guard), Some(&2));
assert_eq!(three.load(&guard), Some(&3));

// Dropping our root will allow the collector to free our `Ref`s.
drop(values);
guard.collect();
assert_eq!(one.load(&guard), None);
```

As the version number indicates, this crate is in early development. No semver
Expand Down
52 changes: 25 additions & 27 deletions benchmarks/benches/timings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::convert::Infallible;
use std::hint::black_box;
use std::sync::Arc;

use refuse::{collected, CollectionGuard, Ref, Root};
use refuse::{CollectionGuard, Ref, Root};
use timings::{Benchmark, BenchmarkImplementation, Label, LabeledTimings, Timings};

const TOTAL_ITERS: usize = 100_000;
Expand Down Expand Up @@ -105,21 +105,20 @@ impl BenchmarkImplementation<Label, (), Infallible> for GcRef {

fn measure(&mut self, measurements: &LabeledTimings<Label>) -> Result<(), Infallible> {
let mut allocated = Vec::<Ref<[u8; 32]>>::with_capacity(ITERS_PER_RELEASE);
collected(|| {
let mut guard = CollectionGuard::acquire();
for _ in 0..OUTER_ITERS {
for i in 0..ITERS_PER_RELEASE {
let timing = measurements.begin(self.metric.clone());
let result = black_box(Ref::new([0; 32], &guard));
if i == ITERS_PER_RELEASE - 1 {
allocated.clear();
guard.yield_to_collector();
}
timing.finish();
allocated.push(result);

let mut guard = CollectionGuard::acquire();
for _ in 0..OUTER_ITERS {
for i in 0..ITERS_PER_RELEASE {
let timing = measurements.begin(self.metric.clone());
let result = black_box(Ref::new([0; 32], &guard));
if i == ITERS_PER_RELEASE - 1 {
allocated.clear();
guard.yield_to_collector();
}
timing.finish();
allocated.push(result);
}
});
}

Ok(())
}
Expand Down Expand Up @@ -157,21 +156,20 @@ impl BenchmarkImplementation<Label, (), Infallible> for GcRoot {

fn measure(&mut self, measurements: &LabeledTimings<Label>) -> Result<(), Infallible> {
let mut allocated = Vec::<Root<[u8; 32]>>::with_capacity(ITERS_PER_RELEASE);
collected(|| {
let mut guard = CollectionGuard::acquire();
for _ in 0..OUTER_ITERS {
for i in 0..ITERS_PER_RELEASE {
let timing = measurements.begin(self.metric.clone());
let result = black_box(Root::new([0; 32], &guard));
if i == ITERS_PER_RELEASE - 1 {
allocated.clear();
guard.yield_to_collector();
}
timing.finish();
allocated.push(result);

let mut guard = CollectionGuard::acquire();
for _ in 0..OUTER_ITERS {
for i in 0..ITERS_PER_RELEASE {
let timing = measurements.begin(self.metric.clone());
let result = black_box(Root::new([0; 32], &guard));
if i == ITERS_PER_RELEASE - 1 {
allocated.clear();
guard.yield_to_collector();
}
timing.finish();
allocated.push(result);
}
});
}

Ok(())
}
Expand Down
24 changes: 12 additions & 12 deletions benchmarks/benches/vs-arc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use std::sync::Arc;

use criterion::{black_box, criterion_group, criterion_main, Criterion};
use refuse::{collected, CollectionGuard, Ref, Root};
use refuse::{CollectionGuard, Ref, Root};

fn arc_string() -> Arc<[u8; 32]> {
Arc::new([0; 32])
Expand All @@ -19,18 +19,18 @@ fn ref_string(guard: &mut CollectionGuard<'_>) -> Ref<[u8; 32]> {
fn criterion_benchmark(c: &mut Criterion) {
let mut group = c.benchmark_group("alloc-drop");
group.bench_function("arc", |b| b.iter(|| black_box(arc_string())));
collected(|| {
group.bench_function("ref", |b| {
b.iter(move || {
let mut guard = CollectionGuard::acquire();
black_box(ref_string(&mut guard))
});

group.bench_function("ref", |b| {
b.iter(move || {
let mut guard = CollectionGuard::acquire();
black_box(ref_string(&mut guard))
});
group.bench_function("root", |b| {
b.iter(move || {
let mut guard = CollectionGuard::acquire();
black_box(root_string(&mut guard))
});
});

group.bench_function("root", |b| {
b.iter(move || {
let mut guard = CollectionGuard::acquire();
black_box(root_string(&mut guard))
});
});
}
Expand Down
14 changes: 6 additions & 8 deletions examples/allocate_a_lot.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
//! An example demonstrating multithreaded allocations.
//!
//! This exists an easy executable to tweak and run with profiling tools.
use refuse::{collected, CollectionGuard, Ref};
use refuse::{CollectionGuard, Ref};

fn main() {
std::thread::scope(|s| {
for _ in 0..16 {
s.spawn(|| {
collected(|| {
let mut guard = CollectionGuard::acquire();
let mut guard = CollectionGuard::acquire();
for _ in 0..100 {
for _ in 0..100 {
for _ in 0..100 {
Ref::new([0; 32], &guard);
}
guard.yield_to_collector();
Ref::new([0; 32], &guard);
}
});
guard.yield_to_collector();
}
});
}
});
Expand Down
3 changes: 1 addition & 2 deletions examples/basic.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
//! A basic usage example demonstrating the garbage collector.
use refuse::{collected, CollectionGuard, Ref, Root};
use refuse::{CollectionGuard, Ref, Root};

#[collected]
fn main() {
let guard = CollectionGuard::acquire();
// Allocate a vec![Ref(1), Ref(2), Ref(3)].
Expand Down
3 changes: 1 addition & 2 deletions examples/map_as.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
//! [rustnomicon]:
//! https://doc.rust-lang.org/nomicon/exotic-sizes.html#dynamically-sized-types-dsts
use refuse::{collected, AnyRef, CollectionGuard, MapAs, Ref, Trace};
use refuse::{AnyRef, CollectionGuard, MapAs, Ref, Trace};

trait SomeTrait {
fn do_something(&self);
Expand All @@ -29,7 +29,6 @@ impl SomeTrait for SomeType {
}
}

#[collected]
fn main() {
let guard = CollectionGuard::acquire();
let gced: Ref<SomeType> = Ref::new(SomeType, &guard);
Expand Down
3 changes: 1 addition & 2 deletions examples/trace.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
//! This example shows how the `Collectable` derive implements tracing
//! automatically.
use refuse::{collected, CollectionGuard, MapAs, Ref, Root, Trace};
use refuse::{CollectionGuard, MapAs, Ref, Root, Trace};

#[derive(Trace, MapAs)]
struct Error {
message: Ref<String>,
}

#[collected]
fn main() {
let mut guard = CollectionGuard::acquire();

Expand Down
19 changes: 0 additions & 19 deletions refuse-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,6 @@ use manyhow::manyhow;
use proc_macro2::{Span, TokenStream};
use quote::quote;

#[manyhow(item_as_dummy)]
#[proc_macro_attribute]
pub fn collected(
_input: TokenStream,
syn::ItemFn {
attrs,
vis,
sig,
block,
}: syn::ItemFn,
) -> TokenStream {
quote! {
#(#attrs)*
#vis #sig {
refuse::collected(|| #block);
}
}
}

#[manyhow]
#[proc_macro_derive(MapAs, attributes(map_as))]
pub fn derive_map_as(input: syn::Item) -> manyhow::Result {
Expand Down
30 changes: 14 additions & 16 deletions refuse-pool/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -306,20 +306,18 @@ impl PartialEq<RefString> for RootString {

#[test]
fn intern() {
refuse::collected(|| {
let mut guard = CollectionGuard::acquire();
let a = RootString::from("a");
let b = RootString::from("a");
assert!(Root::ptr_eq(&a.0, &b.0));

let as_ref = a.downgrade();
drop(a);
drop(b);
assert_eq!(as_ref.load(&guard), Some("a"));

guard.collect();

let _a = RootString::from("a");
assert!(as_ref.load(&guard).is_none());
});
let mut guard = CollectionGuard::acquire();
let a = RootString::from("a");
let b = RootString::from("a");
assert!(Root::ptr_eq(&a.0, &b.0));

let as_ref = a.downgrade();
drop(a);
drop(b);
assert_eq!(as_ref.load(&guard), Some("a"));

guard.collect();

let _a = RootString::from("a");
assert!(as_ref.load(&guard).is_none());
}
Loading

0 comments on commit 43f7f5e

Please sign in to comment.