use super::{Clock, LabelUniverse, Lane, Rendezvous, SessionId, Transport};
use crate::rendezvous::core::EndpointLeaseId;
#[must_use = "revoked public endpoint cleanup must be finished"]
pub(crate) struct RevokedPublicEndpoint<'cfg> {
header: core::ptr::NonNull<()>,
ops: crate::endpoint::carrier::EndpointOps<'cfg>,
sid: SessionId,
finish_entered: bool,
}
impl<'cfg> RevokedPublicEndpoint<'cfg> {
#[inline]
pub(crate) fn finish(mut self) {
self.finish_entered = true;
unsafe {
(self.ops.finish_revoke_for_session)(self.header, self.sid);
}
}
}
#[must_use = "prepared public endpoint revocation must be committed"]
pub(crate) struct PreparedEndpointRevocation<'cfg, Phase> {
inner: Option<EndpointRevocationPlan<'cfg>>,
_phase: core::marker::PhantomData<Phase>,
}
pub(crate) enum NeedsDescriptorRollback {}
pub(crate) enum ReadyToRelease {}
struct EndpointRevocationPlan<'cfg> {
header: core::ptr::NonNull<()>,
ops: crate::endpoint::carrier::EndpointOps<'cfg>,
sid: SessionId,
terminal: crate::endpoint::kernel::EndpointRevocationTerminal<'cfg>,
released_lanes: [Lane; u8::MAX as usize + 1],
released_len: usize,
lease_slot: EndpointLeaseId,
lease_generation: u32,
}
impl<'cfg, Phase> PreparedEndpointRevocation<'cfg, Phase> {
#[inline]
fn from_plan(inner: EndpointRevocationPlan<'cfg>) -> Self {
Self {
inner: Some(inner),
_phase: core::marker::PhantomData,
}
}
#[inline]
fn take_inner(&mut self) -> EndpointRevocationPlan<'cfg> {
self.inner
.take()
.expect("prepared endpoint revocation phase already consumed")
}
#[inline]
fn inner_mut(&mut self) -> &mut EndpointRevocationPlan<'cfg> {
self.inner
.as_mut()
.expect("prepared endpoint revocation phase already consumed")
}
#[inline]
fn inner_ref(&self) -> &EndpointRevocationPlan<'cfg> {
self.inner
.as_ref()
.expect("prepared endpoint revocation phase already consumed")
}
}
impl<'cfg> PreparedEndpointRevocation<'cfg, NeedsDescriptorRollback> {
#[inline]
pub(crate) fn rollback_descriptor_with<R>(
mut self,
rollback: &mut R,
) -> PreparedEndpointRevocation<'cfg, ReadyToRelease>
where
R: crate::endpoint::kernel::EndpointRevocationDescriptorRollback + ?Sized,
{
self.inner_mut().terminal.rollback_send_with(rollback);
let inner = self.take_inner();
PreparedEndpointRevocation::<'cfg, ReadyToRelease>::from_plan(inner)
}
}
impl Drop for RevokedPublicEndpoint<'_> {
fn drop(&mut self) {
assert!(
self.finish_entered,
"revoked public endpoint cleanup must be entered exactly once"
);
}
}
impl<Phase> Drop for PreparedEndpointRevocation<'_, Phase> {
fn drop(&mut self) {
assert!(
self.inner.is_none(),
"prepared public endpoint revocation must be consumed through its typestate phases"
);
}
}
impl<'rv, 'cfg, T, U, C, E> Rendezvous<'rv, 'cfg, T, U, C, E>
where
'cfg: 'rv,
T: Transport,
U: LabelUniverse,
C: Clock,
E: crate::control::cap::mint::EpochTable,
{
pub(crate) fn prepare_one_public_endpoint_revocation(
&mut self,
sid: SessionId,
) -> Option<PreparedEndpointRevocation<'cfg, NeedsDescriptorRollback>> {
let mut released_lanes = [Lane::new(0); u8::MAX as usize + 1];
let lease_capacity = usize::from(self.endpoint_lease_capacity());
let mut idx = 0usize;
while idx < lease_capacity {
let Some((slot, generation)) = self.public_endpoint_lease_by_index(idx) else {
idx += 1;
continue;
};
let Some((offset, len)) = self.endpoint_lease_storage(slot, generation) else {
idx += 1;
continue;
};
let (slab_ptr, slab_len) = self.slab_ptr_and_len();
idx += 1;
if len == 0 || offset + len > slab_len {
continue;
}
let Some(header) = core::ptr::NonNull::new(
unsafe {
slab_ptr
.add(offset)
.cast::<crate::endpoint::carrier::KernelEndpointHeader<'cfg>>()
},
) else {
continue;
};
let ops = unsafe { header.as_ref().ops() };
let mut terminal = crate::endpoint::kernel::EndpointRevocationTerminal::none();
let released = unsafe {
(ops.prepare_revoke_for_session)(
header.cast(),
sid,
released_lanes.as_mut_ptr(),
released_lanes.len(),
core::ptr::from_mut(&mut terminal).cast(),
)
};
if released == 0 {
continue;
}
return Some(PreparedEndpointRevocation::from_plan(
EndpointRevocationPlan {
header: header.cast(),
ops: *ops,
sid,
terminal,
released_lanes,
released_len: released,
lease_slot: slot,
lease_generation: generation,
},
));
}
None
}
pub(crate) fn commit_prepared_public_endpoint_revocation(
&mut self,
mut revocation: PreparedEndpointRevocation<'cfg, ReadyToRelease>,
) -> RevokedPublicEndpoint<'cfg> {
let plan = revocation.inner_ref();
if let Some(lane) = plan.terminal.waiter_lane() {
self.clear_session_waiter(plan.sid, lane);
}
self.release_endpoint_lease(plan.lease_slot, plan.lease_generation);
let mut released_idx = 0usize;
while released_idx < plan.released_len {
let owned_lane = plan.released_lanes[released_idx];
if let Some(released_sid) = self.release_lane(owned_lane) {
self.emit_lane_release(released_sid, owned_lane);
}
released_idx += 1;
}
let EndpointRevocationPlan {
header,
ops,
sid,
terminal: _,
released_lanes: _,
released_len: _,
lease_slot: _,
lease_generation: _,
} = revocation.take_inner();
RevokedPublicEndpoint {
header,
ops,
sid,
finish_entered: false,
}
}
}