use super::{
guard::{GuardInfo, GuardRegistry},
sync::{AtomicU64, Mutex},
Inner,
};
use std::{
cell::Cell,
fmt::{self, Debug, Formatter},
mem,
ops::Deref,
panic::Location,
sync::{atomic::Ordering, Arc, Weak},
task::{Context, Poll, Waker},
};
pub(crate) const LOCAL_ONE: u64 = 1;
const LOCAL_MASK: u64 = 0xFFFF_FFFF;
pub(crate) const KIDS_ONE: u64 = 1 << 32;
pub(crate) const STOPPED: u64 = 1 << 62;
pub(crate) const ARRIVED: u64 = 1 << 63;
#[cfg(not(swansong_loom))]
const MAX_SHARDS: usize = 256;
#[cfg(not(swansong_loom))]
pub(crate) fn root_shard_count() -> usize {
static COUNT: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
*COUNT.get_or_init(|| {
std::thread::available_parallelism()
.map_or(1, std::num::NonZeroUsize::get)
.next_power_of_two()
.min(MAX_SHARDS)
})
}
#[cfg(swansong_loom)]
pub(crate) fn root_shard_count() -> usize {
2
}
pub(crate) fn thread_index() -> usize {
#[cfg(not(swansong_loom))]
static NEXT: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
#[cfg(not(swansong_loom))]
thread_local! {
static INDEX: Cell<usize> = const { Cell::new(usize::MAX) };
}
#[cfg(swansong_loom)]
loom::lazy_static! {
static ref NEXT: loom::sync::atomic::AtomicUsize = loom::sync::atomic::AtomicUsize::new(0);
}
#[cfg(swansong_loom)]
loom::thread_local! {
static INDEX: Cell<usize> = Cell::new(usize::MAX);
}
INDEX.with(|index| {
if index.get() == usize::MAX {
index.set(NEXT.fetch_add(1, Ordering::Relaxed));
}
index.get()
})
}
#[derive(Clone, Debug)]
pub(crate) enum ShardRef {
Padded(Arc<CachePadded>),
Plain(Arc<Shard>),
}
#[derive(Debug)]
#[repr(align(128))]
pub(crate) struct CachePadded(Shard);
impl ShardRef {
pub(crate) fn new(node: Weak<Inner>, root: bool, guard_report: bool) -> Self {
let shard = Shard::new(node, root, guard_report);
if root {
Self::Padded(Arc::new(CachePadded(shard)))
} else {
Self::Plain(Arc::new(shard))
}
}
}
impl Deref for ShardRef {
type Target = Shard;
fn deref(&self) -> &Shard {
match self {
Self::Padded(shard) => &shard.0,
Self::Plain(shard) => shard,
}
}
}
pub(crate) struct Shard {
state: AtomicU64,
registered: Mutex<Registered>,
node: Weak<Inner>,
root: bool,
guard_report: bool,
}
#[derive(Debug, Default)]
struct Registered {
waiters: Slab<Waker>,
guards: GuardRegistry,
children: Slab<Weak<Inner>>,
}
impl Debug for Shard {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("Shard")
.field("stopped", &self.is_stopped())
.field("guards", &self.guard_count())
.finish_non_exhaustive()
}
}
impl Shard {
fn new(node: Weak<Inner>, root: bool, guard_report: bool) -> Self {
Self {
state: AtomicU64::new(0),
registered: Mutex::new(Registered::default()),
node,
root,
guard_report,
}
}
pub(crate) fn is_stopped(&self) -> bool {
self.state.load(Ordering::Relaxed) & STOPPED != 0
}
pub(crate) fn stop(&self, node: &Inner) {
let previous = self.state.fetch_or(STOPPED, Ordering::SeqCst);
let waiters = mem::take(&mut self.registered.lock().unwrap().waiters);
for waker in waiters.into_values() {
waker.wake();
}
if previous & !STOPPED == ARRIVED {
self.flush(node);
}
}
pub(crate) fn arrive(&self, unit: u64, node: &Inner) {
let mut state = self.state.load(Ordering::SeqCst);
loop {
if state & ARRIVED == 0 {
debug_assert_eq!(state & !STOPPED, 0, "an unarrived shard is empty");
node.arrive();
match self.state.compare_exchange(
state,
state | ARRIVED | unit,
Ordering::SeqCst,
Ordering::SeqCst,
) {
Ok(_) => return,
Err(actual) => {
node.depart();
state = actual;
}
}
} else {
match self.state.compare_exchange_weak(
state,
state + unit,
Ordering::SeqCst,
Ordering::SeqCst,
) {
Ok(_) => return,
Err(actual) => state = actual,
}
}
}
}
pub(crate) fn arrive_if_arrived(&self, unit: u64) -> bool {
let mut state = self.state.load(Ordering::SeqCst);
while state & ARRIVED != 0 {
match self.state.compare_exchange_weak(
state,
state + unit,
Ordering::SeqCst,
Ordering::SeqCst,
) {
Ok(_) => return true,
Err(actual) => state = actual,
}
}
false
}
pub(crate) fn arrive_nonempty(&self, unit: u64) {
let previous = self.state.fetch_add(unit, Ordering::SeqCst);
debug_assert!(previous & ARRIVED != 0 && previous & !(ARRIVED | STOPPED) != 0);
}
#[must_use]
pub(crate) fn depart(&self, unit: u64) -> bool {
self.state.fetch_sub(unit, Ordering::SeqCst) - unit == ARRIVED | STOPPED
}
pub(crate) fn flush(&self, node: &Inner) {
if self
.state
.compare_exchange(
ARRIVED | STOPPED,
STOPPED,
Ordering::SeqCst,
Ordering::SeqCst,
)
.is_ok()
{
node.depart();
}
}
pub(crate) fn provenance(
&self,
location: &'static Location<'static>,
) -> Option<Arc<GuardInfo>> {
if !self.guard_report {
return None;
}
let info = GuardInfo::new(location);
self.registered
.lock()
.unwrap()
.guards
.register(Arc::downgrade(&info));
Some(info)
}
pub(crate) fn keeps_guard_report(&self) -> bool {
self.guard_report
}
pub(crate) fn guard_count(&self) -> usize {
usize::try_from(self.state.load(Ordering::Relaxed) & LOCAL_MASK)
.expect("local guard count fits in usize")
}
pub(crate) fn register_child(&self, child: Weak<Inner>) -> usize {
self.registered.lock().unwrap().children.insert(child)
}
pub(crate) fn deregister_child(&self, key: usize) {
let removed = self.registered.lock().unwrap().children.remove(key);
debug_assert!(removed.is_some(), "deregistered an unregistered child");
drop(removed);
}
pub(crate) fn children(&self) -> Vec<Arc<Inner>> {
let registered = self.registered.lock().unwrap();
registered
.children
.values()
.filter_map(Weak::upgrade)
.collect()
}
pub(crate) fn guard_infos(&self, out: &mut Vec<Arc<GuardInfo>>) {
let guards = &mut self.registered.lock().unwrap().guards;
guards.sweep();
out.extend(guards.live());
}
pub(crate) fn is_root(&self) -> bool {
self.root
}
pub(crate) fn node(&self) -> Option<Arc<Inner>> {
self.node.upgrade()
}
pub(crate) fn same_node(&self, other: &Self) -> bool {
Weak::ptr_eq(&self.node, &other.node)
}
#[cfg(all(test, swansong_loom))]
pub(crate) fn state(&self) -> u64 {
self.state.load(Ordering::SeqCst)
}
#[cfg(all(test, swansong_loom))]
pub(crate) fn waiter_count(&self) -> usize {
self.registered.lock().unwrap().waiters.len()
}
#[cfg(all(test, not(swansong_loom)))]
pub(crate) fn child_count(&self) -> usize {
self.registered.lock().unwrap().children.len()
}
}
#[derive(Debug)]
pub(crate) struct StopListener {
shard: ShardRef,
registration: Option<Registration>,
}
#[derive(Debug)]
struct Registration {
key: usize,
waker: Waker,
}
impl StopListener {
pub(crate) fn new(shard: ShardRef) -> Self {
Self {
shard,
registration: None,
}
}
pub(crate) fn is_stopped(&self) -> bool {
self.shard.is_stopped()
}
pub(crate) fn poll_stopped(&mut self, cx: &mut Context<'_>) -> Poll<()> {
let waker = cx.waker();
if let Some(registration) = &self.registration {
if registration.waker.will_wake(waker) {
return if self.shard.is_stopped() {
Poll::Ready(())
} else {
Poll::Pending
};
}
}
let (ours, theirs) = (waker.clone(), waker.clone());
let mut registered = self.shard.registered.lock().unwrap();
if self.shard.is_stopped() {
log::trace!("stopped before registering");
return Poll::Ready(());
}
let replaced = self
.registration
.as_ref()
.and_then(|registration| registered.waiters.remove(registration.key));
let key = registered.waiters.insert(theirs);
drop(registered);
drop(replaced);
self.registration = Some(Registration { key, waker: ours });
Poll::Pending
}
pub(crate) fn shard(&self) -> &ShardRef {
&self.shard
}
pub(crate) fn same_node(&self, other: &Self) -> bool {
self.shard.same_node(&other.shard)
}
}
impl Drop for StopListener {
fn drop(&mut self) {
if let Some(Registration { key, waker }) = self.registration.take() {
let removed = self.shard.registered.lock().unwrap().waiters.remove(key);
drop(removed);
drop(waker);
}
}
}
#[derive(Debug)]
struct Slab<T> {
slots: Vec<Slot<T>>,
next_vacant: usize,
}
impl<T> Default for Slab<T> {
fn default() -> Self {
Self {
slots: Vec::new(),
next_vacant: 0,
}
}
}
#[derive(Debug)]
enum Slot<T> {
Occupied(T),
Vacant(usize),
}
impl<T> Slab<T> {
fn insert(&mut self, value: T) -> usize {
let key = self.next_vacant;
if let Some(slot) = self.slots.get_mut(key) {
let Slot::Vacant(next) = mem::replace(slot, Slot::Occupied(value)) else {
unreachable!("the free list only links vacant slots");
};
self.next_vacant = next;
} else {
self.slots.push(Slot::Occupied(value));
self.next_vacant = self.slots.len();
}
key
}
fn remove(&mut self, key: usize) -> Option<T> {
let slot = self.slots.get_mut(key)?;
match mem::replace(slot, Slot::Vacant(self.next_vacant)) {
Slot::Occupied(value) => {
self.next_vacant = key;
Some(value)
}
vacant @ Slot::Vacant(_) => {
*slot = vacant;
None
}
}
}
fn values(&self) -> impl Iterator<Item = &T> {
self.slots.iter().filter_map(|slot| match slot {
Slot::Occupied(value) => Some(value),
Slot::Vacant(_) => None,
})
}
fn into_values(self) -> impl Iterator<Item = T> {
self.slots.into_iter().filter_map(|slot| match slot {
Slot::Occupied(value) => Some(value),
Slot::Vacant(_) => None,
})
}
#[cfg(test)]
fn len(&self) -> usize {
self.slots
.iter()
.filter(|slot| matches!(slot, Slot::Occupied(_)))
.count()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn slab_reuses_vacated_slots() {
let mut waiters = Slab::default();
let keys: Vec<_> = (0..4)
.map(|_| waiters.insert(Waker::noop().clone()))
.collect();
assert_eq!(keys, [0, 1, 2, 3]);
assert!(waiters.remove(1).is_some());
assert!(waiters.remove(3).is_some());
assert!(waiters.remove(3).is_none(), "a vacant key removes nothing");
assert!(
waiters.remove(7).is_none(),
"an out-of-range key removes nothing"
);
assert_eq!(waiters.len(), 2);
assert_eq!(waiters.insert(Waker::noop().clone()), 3);
assert_eq!(waiters.insert(Waker::noop().clone()), 1);
assert_eq!(waiters.insert(Waker::noop().clone()), 4);
assert_eq!(waiters.len(), 5);
assert_eq!(waiters.into_values().count(), 5);
}
#[test]
fn slab_survives_emptying() {
let mut waiters = Slab::default();
for round in 0..3 {
let keys: Vec<_> = (0..=round)
.map(|_| waiters.insert(Waker::noop().clone()))
.collect();
for key in keys {
assert!(waiters.remove(key).is_some());
}
assert_eq!(waiters.len(), 0);
}
assert_eq!(waiters.slots.len(), 3);
}
}