use std::io;
use std::sync::{Arc, Condvar, Mutex};
use win_sync_sys::Event;
use windows_ioring_sys::{IoBuf, IoBufMut};
use crate::windows_backend::pool::{PoolEngine, Shared};
use durable_ioring::DiagnosticSink;
use durable_ioring::DomainOutcome;
use durable_ioring::EndLineageRefusal;
use durable_ioring::EpochId;
use durable_ioring::Lineage;
use durable_ioring::StampClock;
use durable_ioring::driver::inconsistent;
use durable_ioring::driver::lock;
use durable_ioring::driver::{Answer, DomainCall};
use durable_ioring::driver::{Home, InstanceId};
pub(crate) struct Hub<E, B, C, K, R>
where
E: EpochId + 'static,
B: IoBuf,
R: IoBufMut,
K: StampClock,
{
engine: Shared<E, B, C, K, R>,
readiness: Event,
instance: InstanceId,
diagnostics: Arc<dyn DiagnosticSink>,
unanswered: Mutex<usize>,
answered: Condvar,
}
impl<E, B, C, K, R> Hub<E, B, C, K, R>
where
E: EpochId + 'static,
B: IoBuf,
R: IoBufMut,
K: StampClock,
{
pub(crate) fn new(
engine: Shared<E, B, C, K, R>,
readiness: Event,
instance: InstanceId,
diagnostics: Arc<dyn DiagnosticSink>,
) -> Self {
Self {
engine,
readiness,
instance,
diagnostics,
unanswered: Mutex::new(0),
answered: Condvar::new(),
}
}
pub(crate) fn readiness(&self) -> io::Result<Event> {
self.readiness.try_clone()
}
pub(crate) fn signal(&self) {
let set = self.readiness.set();
debug_assert!(set.is_ok(), "setting the readiness event failed: {set:?}");
}
pub(crate) fn wait_for_answers(&self) {
let mut unanswered = lock(&self.unanswered);
while *unanswered > 0 {
unanswered = self
.answered
.wait(unanswered)
.unwrap_or_else(std::sync::PoisonError::into_inner);
}
}
#[cold]
#[track_caller]
pub(crate) fn inconsistent(&self, what: &'static str, detail: std::fmt::Arguments<'_>) {
inconsistent(self.instance, self.diagnostics.as_ref(), what, detail);
}
fn reach(&self, act: impl FnOnce(&mut PoolEngine<E, B, C, K, R>) -> bool) {
let deliver = lock(&self.engine).as_mut().is_some_and(act);
if deliver {
self.signal();
}
}
}
impl<E, B, C, K, R> Home for Hub<E, B, C, K, R>
where
E: EpochId + Send + Sync + 'static,
B: IoBuf + Send + 'static,
C: Send + 'static,
K: StampClock,
R: IoBufMut + Send + 'static,
{
fn release(&self, lineage: Lineage) {
let mut refused = None;
self.reach(|engine| match engine.core.end_lineage(lineage) {
Ok(actions) => engine.act(actions),
Err(refusal) => {
refused = Some(refusal);
false
}
});
match refused {
None | Some(EndLineageRefusal::Default) => {}
Some(refusal) => self.inconsistent(
"a lineage's last handle was released but the lineage was not live",
format_args!("lineage {lineage:?}, refused as {refusal:?}"),
),
}
}
}
impl<E, B, C, K, R> Answer for Hub<E, B, C, K, R>
where
E: EpochId + Send + Sync + 'static,
B: IoBuf + Send + 'static,
C: Send + 'static,
K: StampClock,
R: IoBufMut + Send + 'static,
{
fn issued(&self) {
*lock(&self.unanswered) += 1;
}
fn answer(&self, call: &DomainCall, outcome: DomainOutcome) {
{
let mut unanswered = lock(&self.unanswered);
let Some(left) = unanswered.checked_sub(1) else {
drop(unanswered);
self.inconsistent(
"a provider answered a completion never made",
format_args!(
"lineage {:?}, seal {}, domain {:?}",
call.lineage, call.seal, call.name
),
);
return;
};
*unanswered = left;
if left == 0 {
self.answered.notify_all();
}
}
self.reach(|engine| {
let actions = engine
.core
.provided(call.lineage, call.seal, &call.name, outcome);
engine.act(actions)
});
}
}