use std::future::Future;
use std::pin::Pin;
use std::sync::{Arc, Mutex, MutexGuard, PoisonError};
use std::task::{Context, Poll, Waker};
use dma_libfabric_protocol::DmaError;
use crate::configuration::Configuration;
use crate::memory_region::MemoryRegion;
use crate::operands::Operands;
use crate::pool::Pool;
use crate::server::{
Completion, Outcome, TransferRequest, {self},
};
struct Cell<TContext> {
outcome: Option<(Outcome, TContext)>,
waker: Option<Waker>,
}
impl<TContext> std::fmt::Debug for Cell<TContext> {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter
.debug_struct("Cell")
.field("complete", &self.outcome.is_some())
.field("waiting", &self.waker.is_some())
.finish()
}
}
fn lock<TContext>(cell: &Mutex<Cell<TContext>>) -> MutexGuard<'_, Cell<TContext>> {
cell.lock().unwrap_or_else(PoisonError::into_inner)
}
struct Awaited<TContext> {
user: TContext,
cell: Arc<Mutex<Cell<TContext>>>,
}
impl<TContext> std::fmt::Debug for Awaited<TContext> {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter.debug_struct("Awaited").finish_non_exhaustive()
}
}
impl<TContext: Operands> Operands for Awaited<TContext> {
fn source(&self) -> Option<&[u8]> {
self.user.source()
}
fn allocate(&mut self, length: usize) -> Option<&mut [u8]> {
self.user.allocate(length)
}
fn cacheable_span(&self) -> Option<crate::CacheableSpan> {
self.user.cacheable_span()
}
}
pub struct Transfer<TContext> {
cell: Arc<Mutex<Cell<TContext>>>,
}
impl<TContext> std::fmt::Debug for Transfer<TContext> {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter
.debug_struct("Transfer")
.field("complete", &lock(&self.cell).outcome.is_some())
.finish()
}
}
impl<TContext> Future for Transfer<TContext> {
type Output = (Outcome, TContext);
fn poll(self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll<Self::Output> {
let mut cell = lock(&self.cell);
if let Some(finished) = cell.outcome.take() {
return Poll::Ready(finished);
}
if !matches!(&cell.waker, Some(waker) if waker.will_wake(context.waker())) {
cell.waker = Some(context.waker().clone());
}
Poll::Pending
}
}
impl<TContext> Drop for Transfer<TContext> {
fn drop(&mut self) {
lock(&self.cell).waker = None;
}
}
fn deliver<TContext>(completion: Completion<Awaited<TContext>>) {
let Completion {
caller_context: Awaited { user, cell },
outcome,
} = completion;
let waker = {
let mut guard = lock(&cell);
guard.outcome = Some((outcome, user));
guard.waker.take()
};
drop(cell);
if let Some(waker) = waker {
waker.wake();
}
}
fn complete_batch<TContext>(completions: std::vec::Drain<'_, Completion<Awaited<TContext>>>) {
for completion in completions {
let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| deliver(completion)));
}
}
fn map_context<TOld, TNew>(
request: TransferRequest<TOld>,
map: impl FnOnce(TOld) -> TNew,
) -> TransferRequest<TNew> {
TransferRequest {
client_id: request.client_id,
peer_address: request.peer_address,
remote_key: request.remote_key,
remote_address: request.remote_address,
direction: request.direction,
want_checksum: request.want_checksum,
caller_context: map(request.caller_context),
parent_id: request.parent_id,
}
}
#[derive(Debug)]
pub struct FabricService<TContext: Send + 'static> {
inner: server::FabricService<Awaited<TContext>>,
}
impl<TContext: Operands + Send + 'static> FabricService<TContext> {
pub fn start(configuration: &Configuration, pool: Arc<Pool>) -> Result<Self, DmaError> {
let inner = server::FabricService::start(configuration, Box::new(complete_batch), pool)?;
Ok(Self { inner })
}
pub fn transfer(
&self,
request: TransferRequest<TContext>,
) -> Result<Transfer<TContext>, TransferRequest<TContext>> {
let cell = Arc::new(Mutex::new(Cell {
outcome: None,
waker: None,
}));
let request = map_context(request, |user| Awaited {
user,
cell: Arc::clone(&cell),
});
match self.inner.submit(request) {
Ok(()) => Ok(Transfer { cell }),
Err(request) => Err(map_context(request, |awaited| awaited.user)),
}
}
pub fn register<S>(&self, storage: S) -> Result<MemoryRegion<S>, DmaError>
where
S: AsRef<[u8]> + Send + Sync + 'static,
{
self.inner.register(storage)
}
pub fn local_address(&self) -> &[u8] {
self.inner.local_address()
}
pub fn outstanding(&self) -> usize {
self.inner.outstanding()
}
pub fn add_peer(&self, client_id: u64, address: &[u8]) -> Result<(), DmaError> {
self.inner.add_peer(client_id, address)
}
pub fn remove_peer(&self, client_id: u64) {
self.inner.remove_peer(client_id);
}
}