use anyhow::anyhow;
use parking_lot::Mutex as ParkingMutex;
use std::collections::BTreeMap;
use std::fmt::{self, Display, Formatter};
use std::sync::Arc;
use std::task::{Context, Poll, Waker};
use super::completion::{CompletionKind, PoisonArc, WaitRegistration};
use crate::events::handle::EventHandle;
use crate::events::status::{EventStatus, Generation};
const MAX_GENERATION: Generation = Generation::MAX;
const GENERATION_BITS: u32 = 32;
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub(crate) struct EventKey {
index: u32,
}
impl EventKey {
pub(crate) fn new(index: u32) -> Self {
Self { index }
}
pub(crate) fn from_handle(handle: EventHandle) -> Self {
Self {
index: handle.local_index(),
}
}
pub(crate) fn handle(&self, system_id: u64, generation: Generation) -> EventHandle {
EventHandle::new(system_id, self.index, generation)
}
}
impl Display for EventKey {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "EventKey(index={})", self.index)
}
}
#[derive(Debug)]
pub(crate) enum EventEntryError {
ActiveGeneration {
key: EventKey,
active: Generation,
},
GenerationOverflow {
key: EventKey,
},
InvalidGeneration {
key: EventKey,
requested: Generation,
active: Option<Generation>,
},
AlreadyCompleted {
key: EventKey,
generation: Generation,
},
}
impl Display for EventEntryError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Self::ActiveGeneration { key, active } => {
write!(f, "Event {} already has active generation {}", key, active)
}
Self::GenerationOverflow { key } => {
write!(
f,
"Event {} exhausted generation space ({} bits)",
key, GENERATION_BITS
)
}
Self::InvalidGeneration {
key,
requested,
active,
} => match active {
Some(current) => write!(
f,
"Invalid generation {} for event {}; active generation {}",
requested, key, current
),
None => write!(
f,
"Invalid generation {} for event {}; no active generation",
requested, key
),
},
Self::AlreadyCompleted { key, generation } => {
write!(
f,
"Event {} generation {} already completed successfully",
key, generation
)
}
}
}
}
impl std::error::Error for EventEntryError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
None
}
}
pub(crate) type EventEntryResult<T> = std::result::Result<T, EventEntryError>;
#[derive(Debug)]
pub(crate) enum PoisonOutcome {
Poisoned,
AlreadyPoisoned,
}
pub(crate) struct EventEntry {
key: EventKey,
state: ParkingMutex<EventState>,
}
impl EventEntry {
pub(crate) fn new(key: EventKey) -> Self {
Self {
key,
state: ParkingMutex::new(EventState::default()),
}
}
pub(crate) fn key(&self) -> EventKey {
self.key
}
pub(crate) fn begin_generation(&self) -> EventEntryResult<Generation> {
let stale_wakers;
let next;
{
let mut state = self.state.lock();
if let Some(active) = state.active_generation {
return Err(EventEntryError::ActiveGeneration {
key: self.key,
active,
});
}
if state.last_triggered == MAX_GENERATION || state.retired {
return Err(EventEntryError::GenerationOverflow { key: self.key });
}
next = state
.last_triggered
.checked_add(1)
.expect("checked for overflow above");
stale_wakers = std::mem::take(&mut state.wakers);
state.active_generation = Some(next);
}
for waker in stale_wakers {
waker.wake();
}
Ok(next)
}
pub(crate) fn status_for(&self, generation: Generation) -> EventStatus {
let state = self.state.lock();
if generation <= state.last_triggered {
if state.poisoned.contains_key(&generation) {
EventStatus::Poisoned
} else {
EventStatus::Ready
}
} else {
EventStatus::Pending
}
}
pub(crate) fn register_local_waiter(
&self,
generation: Generation,
) -> EventEntryResult<WaitRegistration> {
let state = self.state.lock();
if generation <= state.last_triggered {
if let Some(poison) = state.poisoned.get(&generation) {
return Ok(WaitRegistration::Poisoned(poison.clone()));
}
return Ok(WaitRegistration::Ready);
}
match state.active_generation {
Some(active) if active == generation => Ok(WaitRegistration::Pending),
Some(active) => Err(EventEntryError::InvalidGeneration {
key: self.key,
requested: generation,
active: Some(active),
}),
None => Err(EventEntryError::InvalidGeneration {
key: self.key,
requested: generation,
active: None,
}),
}
}
pub(crate) fn finalize_completion(
&self,
generation: Generation,
completion: CompletionKind,
) -> EventEntryResult<()> {
let wakers;
{
let mut state = self.state.lock();
if state.active_generation != Some(generation) {
return Err(EventEntryError::InvalidGeneration {
key: self.key,
requested: generation,
active: state.active_generation,
});
}
state.last_triggered = generation;
state.active_generation = None;
match &completion {
CompletionKind::Poisoned(poison) => {
state.poisoned.insert(generation, poison.clone());
}
CompletionKind::Triggered => {
state.poisoned.remove(&generation);
}
}
wakers = std::mem::take(&mut state.wakers);
}
for waker in wakers {
waker.wake();
}
Ok(())
}
pub(crate) fn try_to_poison(
&self,
generation: Generation,
poison: PoisonArc,
) -> EventEntryResult<PoisonOutcome> {
let wakers;
{
let mut state = self.state.lock();
if generation <= state.last_triggered {
return if state.poisoned.contains_key(&generation) {
Ok(PoisonOutcome::AlreadyPoisoned)
} else {
Err(EventEntryError::AlreadyCompleted {
key: self.key,
generation,
})
};
}
if state.active_generation != Some(generation) {
return Err(EventEntryError::InvalidGeneration {
key: self.key,
requested: generation,
active: state.active_generation,
});
}
state.last_triggered = generation;
state.active_generation = None;
state.poisoned.insert(generation, poison);
wakers = std::mem::take(&mut state.wakers);
}
for waker in wakers {
waker.wake();
}
Ok(PoisonOutcome::Poisoned)
}
pub(crate) fn poll_waiter(
&self,
observed_generation: Generation,
cx: &mut Context<'_>,
) -> Poll<anyhow::Result<()>> {
let mut state = self.state.lock();
if observed_generation <= state.last_triggered {
if let Some(poison) = state.poisoned.get(&observed_generation) {
return Poll::Ready(Err(anyhow::Error::new((**poison).clone())));
}
return Poll::Ready(Ok(()));
}
if state.active_generation.is_none() {
return Poll::Ready(Err(anyhow!("generation expired without completion")));
}
let waker = cx.waker();
if let Some(existing) = state.wakers.iter_mut().find(|w| w.will_wake(waker)) {
existing.clone_from(waker);
} else {
state.wakers.push(waker.clone());
}
Poll::Pending
}
pub(crate) fn retire(&self) {
let wakers;
{
let mut state = self.state.lock();
debug_assert!(
state.wakers.is_empty(),
"retire() called with {} registered wakers on {:?}",
state.wakers.len(),
self.key,
);
state.retired = true;
state.active_generation = None;
wakers = std::mem::take(&mut state.wakers);
}
for waker in wakers {
waker.wake();
}
}
pub(crate) fn is_retired(&self) -> bool {
let state = self.state.lock();
state.retired
}
pub(crate) fn active_handle(&self, system_id: u64) -> Option<EventHandle> {
let generation = {
let state = self.state.lock();
if state.retired {
return None;
}
state.active_generation
}?;
Some(self.key.handle(system_id, generation))
}
#[allow(dead_code)]
pub(crate) fn poison_reason(&self, generation: Generation) -> Option<Arc<str>> {
let state = self.state.lock();
state
.poisoned
.get(&generation)
.map(|p| Arc::<str>::from(p.reason().to_string()))
}
}
struct EventState {
last_triggered: Generation,
active_generation: Option<Generation>,
wakers: Vec<Waker>,
poisoned: BTreeMap<Generation, PoisonArc>,
retired: bool,
}
impl Default for EventState {
fn default() -> Self {
Self {
last_triggered: 0,
active_generation: None,
wakers: Vec::with_capacity(2),
poisoned: BTreeMap::new(),
retired: false,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::task::{Wake, Waker};
fn make_entry(index: u32) -> EventEntry {
EventEntry::new(EventKey::new(index))
}
#[test]
fn entry_error_active_generation() {
let entry = make_entry(0);
entry.begin_generation().unwrap(); let err = entry.begin_generation().unwrap_err();
let msg = format!("{}", err);
assert!(msg.contains("already has active generation"));
}
#[test]
fn entry_error_generation_overflow() {
let entry = make_entry(1);
entry.retire();
let err = entry.begin_generation().unwrap_err();
let msg = format!("{}", err);
assert!(msg.contains("exhausted generation space"));
}
#[test]
fn entry_error_invalid_generation_waiter() {
let entry = make_entry(2);
let generation = entry.begin_generation().unwrap();
match entry.register_local_waiter(generation + 99) {
Err(err) => {
let msg = format!("{}", err);
assert!(msg.contains("Invalid generation"));
assert!(msg.contains("active generation"));
}
Ok(_) => panic!("expected InvalidGeneration error"),
}
}
#[test]
fn entry_error_invalid_generation_no_active() {
let entry = make_entry(3);
match entry.register_local_waiter(1) {
Err(err) => {
let msg = format!("{}", err);
assert!(msg.contains("Invalid generation"));
assert!(msg.contains("no active generation"));
}
Ok(_) => panic!("expected InvalidGeneration error"),
}
}
#[test]
fn entry_key_display() {
let key = EventKey::new(42);
let display = format!("{}", key);
assert!(display.contains("EventKey"));
assert!(display.contains("42"));
}
#[test]
fn entry_active_handle_when_retired() {
let entry = make_entry(4);
entry.begin_generation().unwrap();
entry.retire();
assert!(entry.active_handle(0).is_none());
assert!(entry.is_retired());
}
#[test]
fn entry_active_handle_when_active() {
let entry = make_entry(5);
let generation = entry.begin_generation().unwrap();
let handle = entry.active_handle(0);
assert!(handle.is_some());
assert_eq!(handle.unwrap().generation(), generation);
}
#[test]
fn entry_error_source() {
let entry = make_entry(6);
entry.begin_generation().unwrap();
let err = entry.begin_generation().unwrap_err();
assert!(std::error::Error::source(&err).is_none());
}
#[test]
fn entry_status_for_pending_and_ready() {
let entry = make_entry(7);
let generation = entry.begin_generation().unwrap();
assert_eq!(entry.status_for(generation), EventStatus::Pending);
entry
.finalize_completion(generation, CompletionKind::Triggered)
.unwrap();
assert_eq!(entry.status_for(generation), EventStatus::Ready);
assert_eq!(entry.status_for(0), EventStatus::Ready);
}
#[test]
fn entry_status_for_poisoned() {
let entry = make_entry(8);
let generation = entry.begin_generation().unwrap();
let handle = entry.key().handle(0, generation);
let poison = Arc::new(crate::events::status::EventPoison::new(handle, "test"));
entry
.finalize_completion(generation, CompletionKind::Poisoned(poison))
.unwrap();
assert_eq!(entry.status_for(generation), EventStatus::Poisoned);
}
#[test]
fn entry_poison_reason() {
let entry = make_entry(9);
let generation = entry.begin_generation().unwrap();
let handle = entry.key().handle(0, generation);
let poison = Arc::new(crate::events::status::EventPoison::new(handle, "oops"));
entry
.finalize_completion(generation, CompletionKind::Poisoned(poison))
.unwrap();
let reason = entry.poison_reason(generation);
assert_eq!(&*reason.unwrap(), "oops");
}
#[derive(Default)]
struct CountingWake {
count: AtomicUsize,
}
impl Wake for CountingWake {
fn wake(self: Arc<Self>) {
self.count.fetch_add(1, Ordering::SeqCst);
}
}
fn counting_waker() -> (Arc<CountingWake>, Waker) {
let state = Arc::new(CountingWake::default());
let waker = Waker::from(Arc::clone(&state));
(state, waker)
}
#[test]
fn poll_waiter_deduplicates_waker_registrations() {
let entry = make_entry(10);
let generation = entry.begin_generation().unwrap();
let (wake_state, waker) = counting_waker();
let mut cx = Context::from_waker(&waker);
assert!(entry.poll_waiter(generation, &mut cx).is_pending());
assert!(entry.poll_waiter(generation, &mut cx).is_pending());
{
let state = entry.state.lock();
assert_eq!(state.wakers.len(), 1, "waker should be deduplicated");
}
entry
.finalize_completion(generation, CompletionKind::Triggered)
.unwrap();
assert_eq!(wake_state.count.load(Ordering::SeqCst), 1);
}
#[test]
fn finalize_completion_wakes_all_distinct_waiters() {
let entry = make_entry(11);
let generation = entry.begin_generation().unwrap();
let (first_state, first_waker) = counting_waker();
let (second_state, second_waker) = counting_waker();
let mut first_cx = Context::from_waker(&first_waker);
let mut second_cx = Context::from_waker(&second_waker);
assert!(entry.poll_waiter(generation, &mut first_cx).is_pending());
assert!(entry.poll_waiter(generation, &mut second_cx).is_pending());
entry
.finalize_completion(generation, CompletionKind::Triggered)
.unwrap();
assert_eq!(first_state.count.load(Ordering::SeqCst), 1);
assert_eq!(second_state.count.load(Ordering::SeqCst), 1);
}
#[test]
fn begin_generation_flushes_stale_wakers() {
let entry = make_entry(12);
let generation = entry.begin_generation().unwrap();
entry
.finalize_completion(generation, CompletionKind::Triggered)
.unwrap();
let (wake_state, stale_waker) = counting_waker();
{
let mut state = entry.state.lock();
state.wakers.push(stale_waker);
}
let next_generation = entry.begin_generation().unwrap();
assert_eq!(next_generation, generation + 1);
assert_eq!(wake_state.count.load(Ordering::SeqCst), 1);
}
#[test]
fn try_to_poison_pending_succeeds() {
let entry = make_entry(14);
let generation = entry.begin_generation().unwrap();
let handle = entry.key().handle(0, generation);
let poison = Arc::new(crate::events::status::EventPoison::new(handle, "boom"));
match entry.try_to_poison(generation, poison).unwrap() {
PoisonOutcome::Poisoned => {}
PoisonOutcome::AlreadyPoisoned => panic!("expected Poisoned"),
}
assert_eq!(entry.status_for(generation), EventStatus::Poisoned);
}
#[test]
fn try_to_poison_already_poisoned_is_idempotent() {
let entry = make_entry(15);
let generation = entry.begin_generation().unwrap();
let handle = entry.key().handle(0, generation);
let poison = Arc::new(crate::events::status::EventPoison::new(handle, "first"));
match entry.try_to_poison(generation, poison).unwrap() {
PoisonOutcome::Poisoned => {}
PoisonOutcome::AlreadyPoisoned => panic!("expected Poisoned on first call"),
}
let poison2 = Arc::new(crate::events::status::EventPoison::new(handle, "second"));
match entry.try_to_poison(generation, poison2).unwrap() {
PoisonOutcome::AlreadyPoisoned => {}
PoisonOutcome::Poisoned => panic!("expected AlreadyPoisoned on second call"),
}
}
#[test]
fn try_to_poison_already_triggered_returns_error() {
let entry = make_entry(16);
let generation = entry.begin_generation().unwrap();
entry
.finalize_completion(generation, CompletionKind::Triggered)
.unwrap();
let handle = entry.key().handle(0, generation);
let poison = Arc::new(crate::events::status::EventPoison::new(handle, "too late"));
let err = entry.try_to_poison(generation, poison).unwrap_err();
let msg = format!("{}", err);
assert!(msg.contains("already completed successfully"), "got: {msg}");
}
#[test]
fn try_to_poison_invalid_generation() {
let entry = make_entry(17);
let _generation = entry.begin_generation().unwrap();
let handle = entry.key().handle(0, 999);
let poison = Arc::new(crate::events::status::EventPoison::new(handle, "wrong gen"));
let err = entry.try_to_poison(999, poison).unwrap_err();
let msg = format!("{}", err);
assert!(msg.contains("Invalid generation"), "got: {msg}");
}
#[test]
fn try_to_poison_wakes_waiters() {
let entry = make_entry(18);
let generation = entry.begin_generation().unwrap();
let (wake_state, waker) = counting_waker();
let mut cx = Context::from_waker(&waker);
assert!(entry.poll_waiter(generation, &mut cx).is_pending());
let handle = entry.key().handle(0, generation);
let poison = Arc::new(crate::events::status::EventPoison::new(handle, "wake test"));
entry.try_to_poison(generation, poison).unwrap();
assert_eq!(wake_state.count.load(Ordering::SeqCst), 1);
}
#[test]
fn retire_wakes_registered_wakers() {
let entry = make_entry(13);
let generation = entry.begin_generation().unwrap();
let (wake_state, waker) = counting_waker();
let mut cx = Context::from_waker(&waker);
assert!(entry.poll_waiter(generation, &mut cx).is_pending());
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
entry.retire();
}));
if cfg!(debug_assertions) {
assert!(
result.is_err(),
"debug_assert should fire when wakers are registered"
);
} else {
result.expect("retire() should not panic in release");
assert_eq!(wake_state.count.load(Ordering::SeqCst), 1);
assert!(entry.is_retired());
}
}
}