hibana 0.8.0

Const-projected Affine Multiparty Session Types for choreography-first Rust protocols
Documentation
use crate::binding::EndpointSlot;
use crate::control::cap::mint::{EpochTable, MintConfigMarker};
use crate::endpoint::{RecvError, RecvResult};
use crate::global::const_dsl::ScopeKind;
use crate::global::typestate::ARM_SHARED;
use crate::runtime::{config::Clock, consts::LabelUniverse};
use crate::transport::Transport;

use super::super::authority::RouteDecisionSource;
use super::super::core::{BranchPreviewView, CursorEndpoint};
use super::{BranchCommitPlan, BranchKind};
impl<'r, const ROLE: u8, T, U, C, E, const MAX_RV: usize, Mint, B>
    CursorEndpoint<'r, ROLE, T, U, C, E, MAX_RV, Mint, B>
where
    T: Transport + 'r,
    U: LabelUniverse,
    C: Clock,
    E: EpochTable,
    Mint: MintConfigMarker,
    B: EndpointSlot + 'r,
{
    pub(in crate::endpoint::kernel) fn preflight_branch_preview_commit_plan(
        &self,
        preview: BranchPreviewView,
    ) -> RecvResult<BranchCommitPlan> {
        let scope_id = preview.branch_meta.scope_id;
        let selected_arm = preview.branch_meta.selected_arm;
        let lane_wire = preview.branch_meta.lane_wire;
        let lane_idx = lane_wire as usize;
        if lane_idx >= self.cursor.logical_lane_count() {
            return Err(RecvError::PhaseInvariant);
        }
        let clear_other_lanes = self.selected_arm_for_scope(scope_id) != Some(selected_arm);
        let route_arm_proof = if clear_other_lanes {
            self.preflight_route_arm_commit_after_clearing_other_lanes(
                lane_wire,
                scope_id,
                selected_arm,
            )
        } else {
            self.preflight_route_arm_commit(lane_wire, scope_id, selected_arm)
        };
        if scope_id.kind() == ScopeKind::Route && route_arm_proof.is_none() {
            return Err(RecvError::PhaseInvariant);
        }
        if preview.branch_meta.route_source == RouteDecisionSource::Poll
            && preview.branch_meta.kind == BranchKind::WireRecv
        {
            if preview
                .branch_meta
                .profile
                .poll_wire_commit_requires_event()
            {
                if !preview
                    .branch_meta
                    .route_decision_commit_evidence
                    .emits_route_decision_event()
                {
                    return Err(RecvError::PhaseInvariant);
                }
            } else if preview
                .branch_meta
                .profile
                .poll_wire_commit_requires_static_observation()
            {
                let Some((arm, _)) = self.cursor.observed_recv_target_for_lane_frame_label(
                    scope_id,
                    lane_wire,
                    preview.branch_meta.frame_label,
                ) else {
                    return Err(RecvError::PhaseInvariant);
                };
                let arm = if arm == ARM_SHARED { 0 } else { arm };
                if arm != selected_arm {
                    return Err(RecvError::PhaseInvariant);
                }
            }
        }

        let meta = if preview.branch_meta.kind == BranchKind::WireRecv {
            let mut meta = if let Some(meta) = self.cursor.try_recv_meta() {
                meta
            } else {
                return Err(RecvError::PhaseInvariant);
            };
            if meta.route_arm.is_none() {
                meta.route_arm = Some(selected_arm);
            }
            if meta.label != preview.label {
                meta.label = preview.label;
            }
            Some(meta)
        } else {
            None
        };

        Ok(BranchCommitPlan {
            preview,
            meta,
            route_arm_proof,
            clear_other_lanes,
        })
    }

    pub(in crate::endpoint::kernel) fn publish_branch_preview_commit_plan(
        &mut self,
        plan: BranchCommitPlan,
    ) {
        let preview = plan.preview;
        let scope_id = preview.branch_meta.scope_id;
        let selected_arm = preview.branch_meta.selected_arm;
        let lane_wire = preview.branch_meta.lane_wire;

        if plan.clear_other_lanes {
            self.clear_scope_route_state_for_other_lanes(scope_id, lane_wire);
        }
        if let Some(proof) = plan.route_arm_proof {
            self.commit_route_arm_after_preflight(proof);
        }
        self.skip_unselected_arm_lanes(scope_id, selected_arm, lane_wire);

        if preview
            .branch_meta
            .profile
            .publishes_recvless_parent_route_decision()
        {
            if let Some(plan) = self.build_recvless_parent_route_decision_plan(scope_id) {
                self.publish_recvless_parent_route_decision(plan);
            }
        }

        match preview.branch_meta.route_source {
            RouteDecisionSource::Ack
                if preview
                    .branch_meta
                    .profile
                    .publishes_controller_ack_decision() =>
            {
                if matches!(preview.branch_meta.kind, BranchKind::ArmSendHint) {
                    let lane = lane_wire;
                    self.record_route_decision_for_lane(lane as usize, scope_id, selected_arm);
                    self.emit_route_decision(
                        scope_id,
                        selected_arm,
                        RouteDecisionSource::Ack,
                        lane,
                    );
                } else {
                    let offer_lanes = self.offer_lane_set_for_scope(scope_id);
                    if offer_lanes.is_empty() {
                        let lane = lane_wire;
                        self.record_route_decision_for_lane(lane as usize, scope_id, selected_arm);
                        self.emit_route_decision(
                            scope_id,
                            selected_arm,
                            RouteDecisionSource::Ack,
                            lane,
                        );
                    } else {
                        let lane_limit = self.cursor.logical_lane_count();
                        let mut next = offer_lanes.first_set(lane_limit);
                        while let Some(lane_idx) = next {
                            let lane = lane_idx as u8;
                            self.record_route_decision_for_lane(
                                lane as usize,
                                scope_id,
                                selected_arm,
                            );
                            self.emit_route_decision(
                                scope_id,
                                selected_arm,
                                RouteDecisionSource::Ack,
                                lane,
                            );
                            next =
                                offer_lanes.next_set_from(lane_idx.saturating_add(1), lane_limit);
                        }
                    }
                }
            }
            RouteDecisionSource::Poll => {
                self.emit_route_decision(
                    scope_id,
                    selected_arm,
                    RouteDecisionSource::Poll,
                    self.offer_lane_for_scope(scope_id),
                );
            }
            _ => {}
        }

        if self.arm_has_recv(scope_id, selected_arm) {
            self.consume_scope_ready_arm(scope_id, selected_arm);
        }
        self.clear_scope_evidence(scope_id);
        self.port_for_lane(lane_wire as usize).clear_route_hints();
    }
}