use std::fmt;
use std::io;
use std::ops::Deref;
use std::sync::{Arc, Mutex, MutexGuard, PoisonError};
use win_sync_sys::Event;
use windows_ioring_sys::{IoBuf, IoBufMut, RegisteredSpan};
use windows_threadpool_sys::callback_env::CallbackEnviron;
use windows_threadpool_sys::wait::{ThreadpoolWait, WaitActivation};
use durable_ioring::{
AddTargetError, DurabilityRequest, EndLineageError, Epoch, EpochState, FailureInfo,
ImportScope, Imported, IoTargetKey, Leftovers, LineageInfo, ReadOptions, RemoveTargetError,
Resolution, ResolveError, RetireLineageError, SealError, Tag, UnknownLineage, WriteOptions,
};
use durable_ioring::{
DurableRing, EntryOf, EpochIdOf, FailureIdOf, HandleOf, Lin, PushResult, RegisteredBufferRing,
TokenOf,
};
#[cfg(test)]
mod tests;
#[derive(Debug)]
pub enum DeliveryEvent<E> {
Entry(E),
Failure(io::Error),
}
type Handler<D> = dyn FnMut(DeliveryEvent<EntryOf<D>>, &DeliveryHandle<D>) + Send;
struct Inner<D, H: ?Sized> {
handle: DeliveryHandle<D>,
handler: Mutex<H>,
}
pub struct DeliveryHandle<D> {
ring: Mutex<Option<D>>,
}
impl<D> DeliveryHandle<D> {
fn with<R>(&self, f: impl FnOnce(&mut D) -> R) -> R {
let mut ring = self.ring.lock().unwrap_or_else(PoisonError::into_inner);
f(ring
.as_mut()
.expect("the instance is taken only once nothing can reach this handle"))
}
fn take(&self) -> D {
self.ring
.lock()
.unwrap_or_else(PoisonError::into_inner)
.take()
.expect("the instance is taken once")
}
}
impl<D: DurableRing> DeliveryHandle<D> {
pub fn add_target(
&self,
key: IoTargetKey,
target: D::Target,
) -> Result<(), AddTargetError<D::Target>> {
self.with(|ring| ring.add_target(key, target))
}
pub fn remove_target(&self, key: IoTargetKey) -> Result<D::Target, RemoveTargetError<D::Ids>> {
self.with(|ring| ring.remove_target(key))
}
pub fn default_lineage(&self) -> HandleOf<D> {
self.with(|ring| ring.default_lineage())
}
pub fn mint_lineage(&self, description: Option<String>) -> HandleOf<D> {
self.with(|ring| ring.mint_lineage(description))
}
pub fn lineages(&self) -> Vec<LineageInfo<D::Ids>> {
self.with(|ring| ring.lineages())
}
pub fn end_lineage(&self, handle: HandleOf<D>) -> Result<(), EndLineageError<D::Ids>> {
self.with(|ring| ring.end_lineage(handle))
}
pub fn retire_lineage(&self, handle: HandleOf<D>) -> Result<(), RetireLineageError<D::Ids>> {
self.with(|ring| ring.retire_lineage(handle))
}
pub fn write(
&self,
file: IoTargetKey,
offset: u64,
buffer: D::Buffer,
tag: Tag<'_, D::Ids>,
context: D::Context,
) -> PushResult<D>
where
D::Buffer: IoBuf,
{
self.with(|ring| ring.write(file, offset, buffer, tag, context))
}
pub fn write_with(
&self,
file: IoTargetKey,
offset: u64,
buffer: D::Buffer,
tag: Tag<'_, D::Ids>,
context: D::Context,
options: WriteOptions<D::Ids>,
) -> PushResult<D>
where
D::Buffer: IoBuf,
{
self.with(|ring| ring.write_with(file, offset, buffer, tag, context, options))
}
pub fn read(
&self,
file: IoTargetKey,
offset: u64,
buffer: D::Buffer,
context: D::Context,
) -> PushResult<D>
where
D::Buffer: IoBufMut,
{
self.with(|ring| ring.read(file, offset, buffer, context))
}
pub fn read_with(
&self,
file: IoTargetKey,
offset: u64,
buffer: D::Buffer,
context: D::Context,
options: ReadOptions<D::Ids>,
) -> PushResult<D>
where
D::Buffer: IoBufMut,
{
self.with(|ring| ring.read_with(file, offset, buffer, context, options))
}
pub fn make_durable_through(
&self,
through: Tag<'_, D::Ids>,
) -> Result<DurabilityRequest<D::Ids>, SealError<D::Ids>> {
self.with(|ring| ring.make_durable_through(through))
}
pub fn durable_through(
&self,
lineage: Lin<D>,
) -> Result<Option<EpochIdOf<D>>, UnknownLineage<D::Ids>> {
self.with(|ring| ring.durable_through(lineage))
}
pub fn sealed_through(
&self,
lineage: Lin<D>,
) -> Result<Option<EpochIdOf<D>>, UnknownLineage<D::Ids>> {
self.with(|ring| ring.sealed_through(lineage))
}
pub fn epoch_state(
&self,
epoch: Epoch<D::Ids>,
) -> Result<EpochState<D::Ids>, UnknownLineage<D::Ids>> {
self.with(|ring| ring.epoch_state(epoch))
}
pub fn resolve(
&self,
items: Vec<(TokenOf<D>, Resolution)>,
) -> Result<(), ResolveError<D::Ids>> {
self.with(|ring| ring.resolve(items))
}
pub fn import_failure(
&self,
scope: ImportScope<D::Ids>,
) -> Result<Imported<D::Ids>, UnknownLineage<D::Ids>> {
self.with(|ring| ring.import_failure(scope))
}
pub fn failures(&self) -> Vec<FailureInfo<D::Ids>> {
self.with(|ring| ring.failures())
}
pub fn take_token(&self, failure: FailureIdOf<D>) -> Option<TokenOf<D>> {
self.with(|ring| ring.take_token(failure))
}
}
impl<D: RegisteredBufferRing> DeliveryHandle<D> {
pub fn with_registered_buffer<R>(&self, i: u32, f: impl FnOnce(&[u8]) -> R) -> io::Result<R> {
self.with(|ring| ring.with_registered_buffer(i, f))
}
pub fn with_registered_buffer_mut<R>(
&self,
i: u32,
f: impl FnOnce(&mut [u8]) -> R,
) -> io::Result<R> {
self.with(|ring| ring.with_registered_buffer_mut(i, f))
}
pub fn write_registered(
&self,
file: IoTargetKey,
offset: u64,
span: RegisteredSpan,
tag: Tag<'_, D::Ids>,
context: D::Context,
) -> PushResult<D> {
self.with(|ring| ring.write_registered(file, offset, span, tag, context))
}
pub fn write_registered_with(
&self,
file: IoTargetKey,
offset: u64,
span: RegisteredSpan,
tag: Tag<'_, D::Ids>,
context: D::Context,
options: WriteOptions<D::Ids>,
) -> PushResult<D> {
self.with(|ring| ring.write_registered_with(file, offset, span, tag, context, options))
}
pub fn read_registered(
&self,
file: IoTargetKey,
offset: u64,
span: RegisteredSpan,
context: D::Context,
) -> PushResult<D> {
self.with(|ring| ring.read_registered(file, offset, span, context))
}
pub fn read_registered_with(
&self,
file: IoTargetKey,
offset: u64,
span: RegisteredSpan,
context: D::Context,
options: ReadOptions<D::Ids>,
) -> PushResult<D> {
self.with(|ring| ring.read_registered_with(file, offset, span, context, options))
}
}
pub struct DeliverySetupError<D, F> {
pub error: io::Error,
pub ring: D,
pub on_entry: F,
}
impl<D, F> fmt::Debug for DeliverySetupError<D, F> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("DeliverySetupError")
.field("error", &self.error)
.finish_non_exhaustive()
}
}
pub struct EntryDelivery<D: DurableRing<Readiness = Event> + Send + 'static> {
wait: Option<ThreadpoolWait>,
inner: Arc<Inner<D, Handler<D>>>,
}
impl<D: DurableRing<Readiness = Event> + Send + 'static> EntryDelivery<D> {
pub fn new<F>(
mut ring: D,
on_entry: F,
env: Option<&mut CallbackEnviron<'_>>,
) -> Result<Self, DeliverySetupError<D, F>>
where
F: FnMut(DeliveryEvent<EntryOf<D>>, &DeliveryHandle<D>) + Send + 'static,
{
let duplicates = ring
.readiness()
.and_then(|signal| Ok((signal.try_clone()?, signal)));
let (signal, kick) = match duplicates {
Ok(duplicates) => duplicates,
Err(error) => {
return Err(DeliverySetupError {
error,
ring,
on_entry,
});
}
};
let inner: Arc<Inner<D, F>> = Arc::new(Inner {
handle: DeliveryHandle {
ring: Mutex::new(Some(ring)),
},
handler: Mutex::new(on_entry),
});
let deliverer: Arc<Inner<D, Handler<D>>> = inner.clone();
let wait = match ThreadpoolWait::new(
signal.into(),
move |activation| deliver(&deliverer, activation),
env,
) {
Ok(wait) => wait,
Err(error) => return Err(recover(inner, error)),
};
wait.arm(None);
if let Err(error) = kick.set() {
wait.stop_and_drain();
drop(wait);
return Err(recover(inner, error));
}
Ok(Self {
wait: Some(wait),
inner,
})
}
#[must_use]
pub fn into_inner(mut self) -> D {
self.quiesce();
self.inner.handle.take()
}
pub fn close(self) -> Leftovers<D::Ids, D::Buffer, D::Context> {
self.into_inner().close()
}
fn quiesce(&mut self) {
if let Some(wait) = self.wait.take() {
wait.stop_and_drain();
}
}
}
impl<D: DurableRing<Readiness = Event> + Send + 'static> Deref for EntryDelivery<D> {
type Target = DeliveryHandle<D>;
fn deref(&self) -> &DeliveryHandle<D> {
&self.inner.handle
}
}
impl<D: DurableRing<Readiness = Event> + Send + 'static> Drop for EntryDelivery<D> {
fn drop(&mut self) {
self.quiesce();
}
}
fn deliver<D: DurableRing>(inner: &Inner<D, Handler<D>>, activation: &WaitActivation<'_>) {
loop {
let event = match inner.handle.with(|ring| ring.pop()) {
Ok(Some(entry)) => DeliveryEvent::Entry(entry),
Ok(None) => break,
Err(error) => {
let mut handler = lock(&inner.handler);
(*handler)(DeliveryEvent::Failure(error), &inner.handle);
break;
}
};
let mut handler = lock(&inner.handler);
(*handler)(event, &inner.handle);
}
activation.rearm(None);
}
fn lock<T: ?Sized>(mutex: &Mutex<T>) -> MutexGuard<'_, T> {
mutex.lock().unwrap_or_else(PoisonError::into_inner)
}
fn recover<D, F>(inner: Arc<Inner<D, F>>, error: io::Error) -> DeliverySetupError<D, F> {
let Inner { handle, handler } = Arc::into_inner(inner)
.expect("no wait holds the deliverer's reference once construction has failed");
DeliverySetupError {
error,
ring: handle
.ring
.into_inner()
.unwrap_or_else(PoisonError::into_inner)
.expect("the instance is still in place"),
on_entry: handler.into_inner().unwrap_or_else(PoisonError::into_inner),
}
}