Skip to content

Commit 5c13dd7

Browse files
authored
Faster shared_get
* Faster `shared_get`
1 parent 221fcbe commit 5c13dd7

File tree

8 files changed

+430
-127
lines changed

8 files changed

+430
-127
lines changed

arcshift/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "arcshift"
3-
version = "0.3.0"
3+
version = "0.3.1"
44
documentation = "https://docs.rs/arcshift/"
55
homepage = "https://github.com/avl/arcshift/"
66
repository = "https://github.com/avl/arcshift/"

arcshift/examples/weak.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
use arcshift::{ArcShift, ArcShiftWeak};
2+
use std::cell::RefCell;
3+
4+
fn main() {
5+
#[allow(dead_code)]
6+
struct Node {
7+
parent: Option<ArcShiftWeak<Node>>,
8+
child: RefCell<Option<ArcShift<Node>>>,
9+
}
10+
11+
let mut root = ArcShift::new(Node {
12+
parent: None,
13+
child: RefCell::new(None),
14+
});
15+
16+
let child = ArcShift::new(Node {
17+
parent: Some(ArcShift::downgrade(&root)),
18+
child: RefCell::new(None),
19+
});
20+
21+
root.get().child.borrow_mut().replace(child.clone());
22+
23+
// Both root and child will be dropped here, there will be no memory-leak
24+
}

arcshift/run_loom_slow.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
LOOM_MAX_BRANCHES=4000 LOOM_MAX_PREEMPTIONS=3 RUSTFLAGS="--cfg loom" cargo nextest run --features=loom,validate --release $@

arcshift/src/cell.rs

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -67,15 +67,16 @@ impl<T: 'static + ?Sized> Deref for ArcShiftCellHandle<'_, T> {
6767
}
6868

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

8182
impl<T: 'static + ?Sized> Clone for ArcShiftCell<T> {
@@ -129,6 +130,7 @@ impl<T: 'static + ?Sized> ArcShiftCell<T> {
129130
/// Make sure not to leak this handle: See further documentation on
130131
/// [`ArcShiftCellHandle`]. Leaking the handle will leak resources, but
131132
/// not cause undefined behaviour.
133+
#[inline]
132134
pub fn borrow(&self) -> ArcShiftCellHandle<T> {
133135
self.recursion.set(self.recursion.get() + 1);
134136
ArcShiftCellHandle {
@@ -146,7 +148,8 @@ impl<T: 'static + ?Sized> ArcShiftCell<T> {
146148
///
147149
/// This method is reentrant - you are allowed to call it from within the closure 'f'.
148150
/// However, only the outermost invocation will cause a reload.
149-
pub fn get(&self, f: impl FnOnce(&T)) {
151+
#[inline]
152+
pub fn get<R>(&self, f: impl FnOnce(&T) -> R) -> R {
150153
self.recursion.set(self.recursion.get() + 1);
151154
let val = if self.recursion.get() == 1 {
152155
// SAFETY:
@@ -157,8 +160,9 @@ impl<T: 'static + ?Sized> ArcShiftCell<T> {
157160
// Getting the inner value is safe, no other thread can be accessing it now
158161
unsafe { &*self.inner.get() }.shared_non_reloading_get()
159162
};
160-
f(val);
163+
let t = f(val);
161164
self.recursion.set(self.recursion.get() - 1);
165+
t
162166
}
163167

164168
/// Assign the given ArcShift to this instance.
@@ -179,7 +183,7 @@ impl<T: 'static + ?Sized> ArcShiftCell<T> {
179183
}
180184
}
181185
/// Reload this ArcShiftCell-instance.
182-
/// This allows dropping heap blocks kept alive by this instance of
186+
/// This allows heap blocks kept alive by this instance of
183187
/// ArcShiftCell to be dropped.
184188
/// Note, this method only works when not called from within a closure
185189
/// supplied to the 'get' method. If such recursion occurs, this method
@@ -198,7 +202,7 @@ impl<T: 'static + ?Sized> ArcShiftCell<T> {
198202
/// Create an ArcShift-instance pointing to the same data
199203
pub fn make_arcshift(&self) -> ArcShift<T> {
200204
// SAFETY:
201-
// ArcShiftCell is not Sync, and 'reload' does not recursively call into user
205+
// ArcShiftCell is not Sync, and 'make_arcshift' does not recursively call into user
202206
// code, so we know no other operation can be ongoing.
203207
unsafe { &mut *self.inner.get() }.clone()
204208
}

arcshift/src/deferred_panics_helper.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
//! This module contains routines to help defer panics to outside of the critical sections
2-
//! handling the lock free data structure. Instead, unwinding is deferred to after
3-
//! the lock free structures have been updated. This avoids potential memory leaks, when
4-
//! multiple objects need to be dropped simultaneously, and the first drop impl
5-
//! panics. In this case we still wish to call other drop handlers and not resume unwind
6-
//! until all drops have occurred.
1+
//! This module contains routines to help ensure that panicking drop-implementations
2+
//! do not cause corruption in the heap data-structures. The strategy to achieve
3+
//! this differs depending on if we run in `no_std` case or not.
4+
//! While running in `no_std`, dropping is deferred until after all lock-free memory
5+
//! structures have been updated, at some extra cost.
6+
//! When not using `no_std`, `catch_unwind` is used to catch panics and resume them
7+
//! when it is safe.
78
use crate::{IMetadata, ItemHolder};
89

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

0 commit comments

Comments
 (0)