Skip to content

Commit 851cc39

Browse files
committed
Optimize RefCell refcount tracking
1 parent 9cc3d44 commit 851cc39

File tree

5 files changed

+40
-34
lines changed

5 files changed

+40
-34
lines changed

src/libcore/cell.rs

+36-30
Original file line numberDiff line numberDiff line change
@@ -570,20 +570,31 @@ impl Display for BorrowMutError {
570570
}
571571
}
572572

573-
// Values [1, MIN_WRITING-1] represent the number of `Ref` active. Values in
574-
// [MIN_WRITING, MAX-1] represent the number of `RefMut` active. Multiple
575-
// `RefMut`s can only be active at a time if they refer to distinct,
576-
// nonoverlapping components of a `RefCell` (e.g., different ranges of a slice).
573+
// Positive values represent the number of `Ref` active. Negative values
574+
// represent the number of `RefMut` active. Multiple `RefMut`s can only be
575+
// active at a time if they refer to distinct, nonoverlapping components of a
576+
// `RefCell` (e.g., different ranges of a slice).
577577
//
578578
// `Ref` and `RefMut` are both two words in size, and so there will likely never
579579
// be enough `Ref`s or `RefMut`s in existence to overflow half of the `usize`
580-
// range. Thus, a `BorrowFlag` will probably never overflow. However, this is
581-
// not a guarantee, as a pathological program could repeatedly create and then
582-
// mem::forget `Ref`s or `RefMut`s. Thus, all code must explicitly check for
583-
// overflow in order to avoid unsafety.
584-
type BorrowFlag = usize;
580+
// range. Thus, a `BorrowFlag` will probably never overflow or underflow.
581+
// However, this is not a guarantee, as a pathological program could repeatedly
582+
// create and then mem::forget `Ref`s or `RefMut`s. Thus, all code must
583+
// explicitly check for overflow and underflow in order to avoid unsafety, or at
584+
// least behave correctly in the event that overflow or underflow happens (e.g.,
585+
// see BorrowRef::new).
586+
type BorrowFlag = isize;
585587
const UNUSED: BorrowFlag = 0;
586-
const MIN_WRITING: BorrowFlag = (!0)/2 + 1; // 0b1000...
588+
589+
#[inline(always)]
590+
fn is_writing(x: BorrowFlag) -> bool {
591+
x < UNUSED
592+
}
593+
594+
#[inline(always)]
595+
fn is_reading(x: BorrowFlag) -> bool {
596+
x > UNUSED
597+
}
587598

588599
impl<T> RefCell<T> {
589600
/// Creates a new `RefCell` containing `value`.
@@ -1022,12 +1033,11 @@ impl<'b> BorrowRef<'b> {
10221033
#[inline]
10231034
fn new(borrow: &'b Cell<BorrowFlag>) -> Option<BorrowRef<'b>> {
10241035
let b = borrow.get();
1025-
if b >= MIN_WRITING {
1036+
if is_writing(b) || b == isize::max_value() {
1037+
// If there's currently a writing borrow, or if incrementing the
1038+
// refcount would overflow into a writing borrow.
10261039
None
10271040
} else {
1028-
// Prevent the borrow counter from overflowing into
1029-
// a writing borrow.
1030-
assert!(b < MIN_WRITING - 1);
10311041
borrow.set(b + 1);
10321042
Some(BorrowRef { borrow })
10331043
}
@@ -1038,7 +1048,7 @@ impl<'b> Drop for BorrowRef<'b> {
10381048
#[inline]
10391049
fn drop(&mut self) {
10401050
let borrow = self.borrow.get();
1041-
debug_assert!(borrow < MIN_WRITING && borrow != UNUSED);
1051+
debug_assert!(is_reading(borrow));
10421052
self.borrow.set(borrow - 1);
10431053
}
10441054
}
@@ -1047,12 +1057,12 @@ impl<'b> Clone for BorrowRef<'b> {
10471057
#[inline]
10481058
fn clone(&self) -> BorrowRef<'b> {
10491059
// Since this Ref exists, we know the borrow flag
1050-
// is not set to WRITING.
1060+
// is a reading borrow.
10511061
let borrow = self.borrow.get();
1052-
debug_assert!(borrow != UNUSED);
1062+
debug_assert!(is_reading(borrow));
10531063
// Prevent the borrow counter from overflowing into
10541064
// a writing borrow.
1055-
assert!(borrow < MIN_WRITING - 1);
1065+
assert!(borrow != isize::max_value());
10561066
self.borrow.set(borrow + 1);
10571067
BorrowRef { borrow: self.borrow }
10581068
}
@@ -1251,12 +1261,8 @@ impl<'b> Drop for BorrowRefMut<'b> {
12511261
#[inline]
12521262
fn drop(&mut self) {
12531263
let borrow = self.borrow.get();
1254-
debug_assert!(borrow >= MIN_WRITING);
1255-
self.borrow.set(if borrow == MIN_WRITING {
1256-
UNUSED
1257-
} else {
1258-
borrow - 1
1259-
});
1264+
debug_assert!(is_writing(borrow));
1265+
self.borrow.set(borrow + 1);
12601266
}
12611267
}
12621268

@@ -1266,10 +1272,10 @@ impl<'b> BorrowRefMut<'b> {
12661272
// NOTE: Unlike BorrowRefMut::clone, new is called to create the initial
12671273
// mutable reference, and so there must currently be no existing
12681274
// references. Thus, while clone increments the mutable refcount, here
1269-
// we simply go directly from UNUSED to MIN_WRITING.
1275+
// we explicitly only allow going from UNUSED to UNUSED - 1.
12701276
match borrow.get() {
12711277
UNUSED => {
1272-
borrow.set(MIN_WRITING);
1278+
borrow.set(UNUSED - 1);
12731279
Some(BorrowRefMut { borrow: borrow })
12741280
},
12751281
_ => None,
@@ -1284,10 +1290,10 @@ impl<'b> BorrowRefMut<'b> {
12841290
#[inline]
12851291
fn clone(&self) -> BorrowRefMut<'b> {
12861292
let borrow = self.borrow.get();
1287-
debug_assert!(borrow >= MIN_WRITING);
1288-
// Prevent the borrow counter from overflowing.
1289-
assert!(borrow != !0);
1290-
self.borrow.set(borrow + 1);
1293+
debug_assert!(is_writing(borrow));
1294+
// Prevent the borrow counter from underflowing.
1295+
assert!(borrow != isize::min_value());
1296+
self.borrow.set(borrow - 1);
12911297
BorrowRefMut { borrow: self.borrow }
12921298
}
12931299
}

src/test/compile-fail/not-panic-safe-2.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,5 @@ fn assert<T: UnwindSafe + ?Sized>() {}
1919
fn main() {
2020
assert::<Rc<RefCell<i32>>>();
2121
//~^ ERROR the type `std::cell::UnsafeCell<i32>` may contain interior mutability and a
22-
//~| ERROR the type `std::cell::UnsafeCell<usize>` may contain interior mutability and a
22+
//~| ERROR the type `std::cell::UnsafeCell<isize>` may contain interior mutability and a
2323
}

src/test/compile-fail/not-panic-safe-3.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,5 @@ fn assert<T: UnwindSafe + ?Sized>() {}
1919
fn main() {
2020
assert::<Arc<RefCell<i32>>>();
2121
//~^ ERROR the type `std::cell::UnsafeCell<i32>` may contain interior mutability and a
22-
//~| ERROR the type `std::cell::UnsafeCell<usize>` may contain interior mutability and a
22+
//~| ERROR the type `std::cell::UnsafeCell<isize>` may contain interior mutability and a
2323
}

src/test/compile-fail/not-panic-safe-4.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,5 @@ fn assert<T: UnwindSafe + ?Sized>() {}
1818
fn main() {
1919
assert::<&RefCell<i32>>();
2020
//~^ ERROR the type `std::cell::UnsafeCell<i32>` may contain interior mutability and a
21-
//~| ERROR the type `std::cell::UnsafeCell<usize>` may contain interior mutability and a
21+
//~| ERROR the type `std::cell::UnsafeCell<isize>` may contain interior mutability and a
2222
}

src/test/compile-fail/not-panic-safe-6.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,5 @@ fn assert<T: UnwindSafe + ?Sized>() {}
1818
fn main() {
1919
assert::<*mut RefCell<i32>>();
2020
//~^ ERROR the type `std::cell::UnsafeCell<i32>` may contain interior mutability and a
21-
//~| ERROR the type `std::cell::UnsafeCell<usize>` may contain interior mutability and a
21+
//~| ERROR the type `std::cell::UnsafeCell<isize>` may contain interior mutability and a
2222
}

0 commit comments

Comments
 (0)