1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
use std::cell::Cell;
use std::sync::atomic::{AtomicU16, AtomicU32, AtomicU64, AtomicU8, AtomicUsize, Ordering};
/// Trait for refcount
///
/// Some method names explain required memory ordering in multi-threaded context.
pub trait RefCount {
/// Type of count
type Value;
/// Creates a new RefCount object.
///
/// The object is initialized as "one".
fn one() -> Self;
/// Checks whether a value equals to one.
fn is_one(val: &Self::Value) -> bool;
/// Gets a current value.
///
/// # Memory ordering
/// [Acquire](std::sync::atomic::Ordering::Acquire) or stronger ordering is required if
/// implementors are atomic types.
fn load_acquire(&self) -> Self::Value;
/// Memory fence.
///
/// This method is needed only for multi-threaded refcount.
/// In single-threaded implementors, this can be NO-OP.
///
/// # Memory ordering
/// [Acquire](std::sync::atomic::Ordering::Acquire) or stronger ordering is required if
/// implementors are atomic types.
fn fence_acquire(&self);
/// Increments its value and returns previous value.
///
/// # Memory ordering
/// [Relaxed](std::sync::atomic::Ordering::Relaxed) ordering is allowed if implementors are
/// atomic types.
fn fetch_inc_relaxed(&self) -> Self::Value;
/// Decrements its value and returns previous value.
///
/// # Memory ordering
/// [Release](std::sync::atomic::Ordering::Release) or stronger ordering is required if
/// implementors are atomic types.
fn fetch_dec_release(&self) -> Self::Value;
}
/// Implements RefCount for Cell<$type>.
/// Its implementors are used as single-threaded refcount.
macro_rules! impl_cell_refcount {
($type:ty) => {
impl RefCount for Cell<$type> {
type Value = $type;
#[inline]
fn one() -> Self {
Self::new(1)
}
#[inline]
fn is_one(val: &Self::Value) -> bool {
*val == 1
}
#[inline]
fn load_acquire(&self) -> Self::Value {
self.get()
}
#[inline(always)]
fn fence_acquire(&self) {
// noop
}
#[inline]
fn fetch_inc_relaxed(&self) -> Self::Value {
let count = self.load_acquire();
assume!(count != 0);
match count.checked_add(1) {
Some(c) => {
self.set(c);
count
}
None => std::process::abort(), // Overflow
}
}
#[inline]
fn fetch_dec_release(&self) -> Self::Value {
let count = self.load_acquire();
assume!(0 < count);
self.set(count - 1);
count
}
}
};
}
impl_cell_refcount!(u8);
impl_cell_refcount!(u16);
impl_cell_refcount!(u32);
impl_cell_refcount!(u64);
impl_cell_refcount!(usize);
/// Implements RefCount for atomic $types.
/// Its implementors are used as multi-threaded refcount.
macro_rules! impl_atomic_refcount {
($type:ty, $value_type:ty) => {
impl RefCount for $type {
type Value = $value_type;
#[inline]
fn one() -> Self {
Self::new(1)
}
#[inline]
fn is_one(val: &Self::Value) -> bool {
*val == 1
}
#[inline]
fn load_acquire(&self) -> Self::Value {
self.load(Ordering::Acquire)
}
#[inline]
fn fence_acquire(&self) {
// Load-Acquire is used instead of a fence for performance[1].
// [1] https://developer.arm.com/documentation/102336/0100/Load-Acquire-and-Store-Release-instructions
let _count = self.load_acquire();
}
#[inline]
fn fetch_inc_relaxed(&self) -> Self::Value {
let count = self.fetch_add(1, Ordering::Relaxed);
if count == <$value_type>::MAX {
// Overflow
std::process::abort();
}
count
}
#[inline]
fn fetch_dec_release(&self) -> Self::Value {
let count = self.fetch_sub(1, Ordering::Release);
assume!(0 < count);
count
}
}
};
}
impl_atomic_refcount!(AtomicU8, u8);
impl_atomic_refcount!(AtomicU16, u16);
impl_atomic_refcount!(AtomicU32, u32);
impl_atomic_refcount!(AtomicU64, u64);
impl_atomic_refcount!(AtomicUsize, usize);