Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion arcshift/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "arcshift"
version = "0.3.0"
version = "0.3.1"
documentation = "https://docs.rs/arcshift/"
homepage = "https://github.com/avl/arcshift/"
repository = "https://github.com/avl/arcshift/"
Expand Down
24 changes: 24 additions & 0 deletions arcshift/examples/weak.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
use arcshift::{ArcShift, ArcShiftWeak};
use std::cell::RefCell;

fn main() {
#[allow(dead_code)]
struct Node {
parent: Option<ArcShiftWeak<Node>>,
child: RefCell<Option<ArcShift<Node>>>,
}

let mut root = ArcShift::new(Node {
parent: None,
child: RefCell::new(None),
});

let child = ArcShift::new(Node {
parent: Some(ArcShift::downgrade(&root)),
child: RefCell::new(None),
});

root.get().child.borrow_mut().replace(child.clone());

// Both root and child will be dropped here, there will be no memory-leak
}
1 change: 1 addition & 0 deletions arcshift/run_loom_slow.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
LOOM_MAX_BRANCHES=4000 LOOM_MAX_PREEMPTIONS=3 RUSTFLAGS="--cfg loom" cargo nextest run --features=loom,validate --release $@
24 changes: 14 additions & 10 deletions arcshift/src/cell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,15 +67,16 @@ impl<T: 'static + ?Sized> Deref for ArcShiftCellHandle<'_, T> {
}

/// ArcShiftCell cannot be Sync, but there's nothing stopping it from being Send.
/// SAFETY:
/// As long as the contents of the cell are not !Send, it is safe to
/// send the cell. The object must be uniquely owned to be sent, and
/// this is only possible if we're not in a recursive call to
/// 'get'. And in this case, the properties of ArcShiftCell are the same
/// as ArcShift, and ArcShift is Send (if T is Send + Sync).
///
/// Note that ArcShiftCell *cannot* be Sync, because then multiple threads
/// could call 'get' simultaneously, corrupting the (non-atomic) refcount.
// SAFETY:
// As long as the contents of the cell are not !Send, it is safe to
// send the cell. The object must be uniquely owned to be sent, and
// this is only possible if we're not in a recursive call to
// 'get'. And in this case, the properties of ArcShiftCell are the same
// as ArcShift, and ArcShift is Send (if T is Send + Sync).
//
unsafe impl<T: 'static> Send for ArcShiftCell<T> where T: Send + Sync {}

impl<T: 'static + ?Sized> Clone for ArcShiftCell<T> {
Expand Down Expand Up @@ -129,6 +130,7 @@ impl<T: 'static + ?Sized> ArcShiftCell<T> {
/// Make sure not to leak this handle: See further documentation on
/// [`ArcShiftCellHandle`]. Leaking the handle will leak resources, but
/// not cause undefined behaviour.
#[inline]
pub fn borrow(&self) -> ArcShiftCellHandle<T> {
self.recursion.set(self.recursion.get() + 1);
ArcShiftCellHandle {
Expand All @@ -146,7 +148,8 @@ impl<T: 'static + ?Sized> ArcShiftCell<T> {
///
/// This method is reentrant - you are allowed to call it from within the closure 'f'.
/// However, only the outermost invocation will cause a reload.
pub fn get(&self, f: impl FnOnce(&T)) {
#[inline]
pub fn get<R>(&self, f: impl FnOnce(&T) -> R) -> R {
self.recursion.set(self.recursion.get() + 1);
let val = if self.recursion.get() == 1 {
// SAFETY:
Expand All @@ -157,8 +160,9 @@ impl<T: 'static + ?Sized> ArcShiftCell<T> {
// Getting the inner value is safe, no other thread can be accessing it now
unsafe { &*self.inner.get() }.shared_non_reloading_get()
};
f(val);
let t = f(val);
self.recursion.set(self.recursion.get() - 1);
t
}

/// Assign the given ArcShift to this instance.
Expand All @@ -179,7 +183,7 @@ impl<T: 'static + ?Sized> ArcShiftCell<T> {
}
}
/// Reload this ArcShiftCell-instance.
/// This allows dropping heap blocks kept alive by this instance of
/// This allows heap blocks kept alive by this instance of
/// ArcShiftCell to be dropped.
/// Note, this method only works when not called from within a closure
/// supplied to the 'get' method. If such recursion occurs, this method
Expand All @@ -198,7 +202,7 @@ impl<T: 'static + ?Sized> ArcShiftCell<T> {
/// Create an ArcShift-instance pointing to the same data
pub fn make_arcshift(&self) -> ArcShift<T> {
// SAFETY:
// ArcShiftCell is not Sync, and 'reload' does not recursively call into user
// ArcShiftCell is not Sync, and 'make_arcshift' does not recursively call into user
// code, so we know no other operation can be ongoing.
unsafe { &mut *self.inner.get() }.clone()
}
Expand Down
13 changes: 7 additions & 6 deletions arcshift/src/deferred_panics_helper.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
//! This module contains routines to help defer panics to outside of the critical sections
//! handling the lock free data structure. Instead, unwinding is deferred to after
//! the lock free structures have been updated. This avoids potential memory leaks, when
//! multiple objects need to be dropped simultaneously, and the first drop impl
//! panics. In this case we still wish to call other drop handlers and not resume unwind
//! until all drops have occurred.
//! This module contains routines to help ensure that panicking drop-implementations
//! do not cause corruption in the heap data-structures. The strategy to achieve
//! this differs depending on if we run in `no_std` case or not.
//! While running in `no_std`, dropping is deferred until after all lock-free memory
//! structures have been updated, at some extra cost.
//! When not using `no_std`, `catch_unwind` is used to catch panics and resume them
//! when it is safe.
use crate::{IMetadata, ItemHolder};

pub(crate) trait IDropHandler<T: ?Sized, M: IMetadata> {
Expand Down
Loading