AWS SDK

AWS SDK

rev. 01140188efe3be26ec6653bc30094c80ed1a1b99

Files changed:

tmp-codegen-diff/aws-sdk/sdk/aws-smithy-http-client/src/client/pool/cell/h1.rs

@@ -1,1 +89,89 @@
    1      1   
/*
    2      2   
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
    3      3   
 * SPDX-License-Identifier: Apache-2.0
    4      4   
 */
    5      5   
    6      6   
//! HTTP/1 connection records and exclusive request-sender ownership.
    7      7   
//!
    8         -
//! Every installed connection remains in its connection-owning [`H1Records`]
           8  +
//! Every installed connection remains in its connection-owning [`H1CellState`]
    9      9   
//! until logical close. While the record is live, its exclusive sender exists
   10     10   
//! in exactly one place: inside an `Idle` record or in one external owner while
   11         -
//! the record is `Selected` or `Reserved`. Transitions move the sender; they
          11  +
//! the record is `Selected` or `ReservedForPeer`. Transitions move the sender; they
   12     12   
//! never copy it. The residence records that ownership:
   13     13   
//!
   14     14   
//! ```text
   15         -
//! completed handshake -- install_selected --------------------> Selected
   16         -
//! test fixture ------- install_idle ---------------------------> Idle(sender)
          15  +
//! completed handshake -- insert_selected ---------------------> Selected
          16  +
//! test fixture ------- insert_idle ----------------------------> Idle(sender)
   17     17   
//!
   18     18   
//! Idle(sender) -- select_idle --------------------------------> Selected
   19         -
//! Idle(sender) -- take_idle_for_reuse ------------------------> Reserved
   20         -
//! Selected ---- reserve_for_reuse ----------------------------> Reserved
   21         -
//! Reserved ---- commit_return_to_waiter ----------------------> Selected
          19  +
//! Idle(sender) -- take_idle_candidate ----------------> ReservedForPeer
          20  +
//! Selected ---- reserve_for_peer ---------------------> ReservedForPeer
          21  +
//! ReservedForPeer -- commit_return_to_waiter -----------------> Selected
   22     22   
//! Selected ---- return_idle ----------------------------------> Idle(sender)
   23         -
//! Reserved ---- return_idle ----------------------------------> Idle(sender)
          23  +
//! ReservedForPeer -- return_idle -----------------------------> Idle(sender)
   24     24   
//!
   25     25   
//! Idle(sender) ------------ begin_close ----------------------> Closing
   26         -
//! Selected / Reserved ---- begin_close / close_owned --------> Closing
   27         -
//! Closing -------- finish_close ------------------------------> removed
          26  +
//! Selected / ReservedForPeer -- begin_close / close_owned ---> Closing
          27  +
//! Closing -------- remove_closed -----------------------------> removed
   28     28   
//! ```
   29     29   
//!
   30     30   
//! Outside the lock, ownership moves through values whose drop behavior is
   31     31   
//! specific to the protocol phase:
   32     32   
//!
   33     33   
//! ```text
   34     34   
//! H1Selection
   35     35   
//!   |-- dropped ------------------------------> return to connection-owning cell
   36     36   
//!   |-- retire_connection --------------------------------> logical close
   37     37   
//!   `-- request accepted --> H1Exchange
   38     38   
//!                              |-- incomplete or dropped --> logical close
   39     39   
//!                              `-- offer_for_reuse --------> return to connection-owning cell
   40     40   
//!
   41     41   
//! cross-cell reuse --> ProvisionalH1
   42     42   
//!                        |-- borrow ----------------------> H1Selection
   43     43   
//!                        |-- reclaim ---------------------> logical close
   44     44   
//!                        `-- rejected or dropped ---------> return to connection-owning cell
   45     45   
//! ```
   46     46   
//!
   47     47   
//! [`H1Selection`], [`H1Exchange`], and [`ProvisionalH1`] are the
   48     48   
//! sender-owning values outside the cell lock. Their drop behavior returns or
   49     49   
//! retires the sender so a cancellation cannot leave a record with no terminal
   50     50   
//! owner. They retain the owning cell weakly because a selected sender may
   51     51   
//! temporarily be the ready result in that same cell; cell teardown makes the
   52     52   
//! fallback close the connection directly.
   53     53   
   54         -
use super::super::admission::reuse::{
   55         -
    H1AvailabilitySnapshot, PreparedReuseInstall, ReuseCandidate, ReuseId, ReuseInstallResult,
          54  +
use super::super::admission::{
          55  +
    AdmissionAction, H1Candidate, H1MatchId, H1SupplyStatus, OriginAdmission,
          56  +
    PreparedH1Reservation, SupplyRevision,
   56     57   
};
   57         -
use super::super::admission::{AdmissionAction, OriginAdmission};
   58     58   
use super::super::connection::{CloseReason, ConnectionState};
   59         -
use super::{AcquisitionResult, OriginCell, ReuseInstall};
          59  +
use super::{AcquisitionOutcome, OriginCell};
   60     60   
use crate::sync::{Arc, Weak};
   61     61   
use aws_smithy_runtime_api::client::connection::ConnectionId;
   62     62   
use std::collections::{HashMap, VecDeque};
   63     63   
use std::fmt;
   64     64   
use std::task::{Context, Poll};
   65     65   
use std::time::SystemTime;
   66     66   
   67     67   
use aws_smithy_types::body::SdkBody;
   68     68   
   69     69   
/// Exclusive request handle for one Hyper HTTP/1 client connection.
@@ -112,112 +1605,1675 @@
  132    132   
impl fmt::Debug for H1Sender {
  133    133   
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
  134    134   
        match self {
  135    135   
            Self::Hyper(_) => f.write_str("H1Sender::Hyper"),
  136    136   
            #[cfg(test)]
  137    137   
            Self::Test(id) => f.debug_tuple("H1Sender::Test").field(id).finish(),
  138    138   
        }
  139    139   
    }
  140    140   
}
  141    141   
  142         -
/// Local reservation for one cross-cell reuse operation and its fairness debt.
         142  +
/// Local reservation for one peer match and its fairness debt.
  143    143   
///
  144    144   
/// Reservation residence and fairness debt are separate because a completed
  145    145   
/// transfer releases the reservation immediately, while the debt remains until
  146    146   
/// later local service or the disappearance of compatible local demand.
  147    147   
#[derive(Debug, Default)]
  148         -
pub(super) struct H1ReuseReservation {
  149         -
    /// Residence of the current reuse reservation.
  150         -
    state: H1ReuseReservationState,
         148  +
struct H1Reservation {
         149  +
    /// State of the current peer reservation.
         150  +
    state: H1ReservationState,
  151    151   
    /// Whether this cell's next usable HTTP/1 turn must remain local after the
  152    152   
    /// operation that earned it has completed.
  153    153   
    local_turn_owed: bool,
  154    154   
}
  155    155   
  156         -
/// Authoritative residence of one cell-local reuse reservation.
         156  +
/// Authoritative state of one cell-local peer reservation.
  157    157   
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
  158         -
enum H1ReuseReservationState {
  159         -
    /// No reuse operation may intercept a sender return.
         158  +
enum H1ReservationState {
         159  +
    /// No peer match may intercept a sender return.
  160    160   
    #[default]
  161    161   
    Available,
  162         -
    /// The next reusable sender return is reserved for this reuse operation.
  163         -
    Installed(ReuseId),
  164         -
    /// A provisional sender is outside the connection-owning cell lock for this reuse operation.
  165         -
    Resolving(ReuseId),
         162  +
    /// The next reusable sender return is reserved for this match.
         163  +
    Installed(H1MatchId),
         164  +
    /// A provisional sender is outside the cell lock for this match.
         165  +
    Resolving(H1MatchId),
  166    166   
}
  167    167   
  168         -
impl H1ReuseReservation {
  169         -
    /// Installs a reuse operation that will intercept a future reusable return.
  170         -
    pub(super) fn install(&mut self, reuse_id: ReuseId) -> bool {
  171         -
        if !matches!(self.state, H1ReuseReservationState::Available) {
         168  +
impl H1Reservation {
         169  +
    /// Reserves a future reusable return for one retained match.
         170  +
    fn reserve_waiting(&mut self, match_id: H1MatchId) -> bool {
         171  +
        if !matches!(self.state, H1ReservationState::Available) {
  172    172   
            return false;
  173    173   
        }
  174         -
        self.state = H1ReuseReservationState::Installed(reuse_id);
         174  +
        self.state = H1ReservationState::Installed(match_id);
  175    175   
        true
  176    176   
    }
  177    177   
  178         -
    /// Installs a reuse operation that has already extracted an idle sender.
  179         -
    pub(super) fn install_resolving(&mut self, reuse_id: ReuseId) -> bool {
  180         -
        if !matches!(self.state, H1ReuseReservationState::Available) {
         178  +
    /// Reserves a match that already owns an extracted idle sender.
         179  +
    fn reserve_resolving(&mut self, match_id: H1MatchId) -> bool {
         180  +
        if !matches!(self.state, H1ReservationState::Available) {
  181    181   
            return false;
  182    182   
        }
  183         -
        self.state = H1ReuseReservationState::Resolving(reuse_id);
         183  +
        self.state = H1ReservationState::Resolving(match_id);
  184    184   
        true
  185    185   
    }
  186    186   
  187         -
    /// Reserves the next reusable return for an installed reuse operation.
  188         -
    pub(super) fn intercept_return(&mut self) -> Option<ReuseId> {
  189         -
        let H1ReuseReservationState::Installed(reuse_id) = self.state else {
         187  +
    /// Transfers a waiting match into resolution on sender return.
         188  +
    fn take_return_match(&mut self) -> Option<H1MatchId> {
         189  +
        let H1ReservationState::Installed(match_id) = self.state else {
  190    190   
            return None;
  191    191   
        };
  192         -
        self.state = H1ReuseReservationState::Resolving(reuse_id);
  193         -
        Some(reuse_id)
         192  +
        self.state = H1ReservationState::Resolving(match_id);
         193  +
        Some(match_id)
  194    194   
    }
  195    195   
  196         -
    /// Clears a matching reuse operation without earning a local fairness turn.
  197         -
    pub(super) fn reject(&mut self, reuse_id: ReuseId) -> bool {
  198         -
        if !self.names(reuse_id) {
         196  +
    /// Releases a matching reservation without earning a local fairness turn.
         197  +
    fn release(&mut self, match_id: H1MatchId) -> bool {
         198  +
        if !self.names(match_id) {
  199    199   
            return false;
  200    200   
        }
  201         -
        self.state = H1ReuseReservationState::Available;
         201  +
        self.state = H1ReservationState::Available;
  202    202   
        true
  203    203   
    }
  204    204   
  205    205   
    /// Completes an irreversible transfer and records any usable local turn.
  206         -
    pub(super) fn complete_transfer(&mut self, reuse_id: ReuseId, local_h1_demand: bool) -> bool {
  207         -
        if !matches!(self.state, H1ReuseReservationState::Resolving(current) if current == reuse_id)
  208         -
        {
         206  +
    fn complete_transfer(&mut self, match_id: H1MatchId, local_h1_demand: bool) -> bool {
         207  +
        if !matches!(self.state, H1ReservationState::Resolving(current) if current == match_id) {
  209    208   
            return false;
  210    209   
        }
  211         -
        self.state = H1ReuseReservationState::Available;
         210  +
        self.state = H1ReservationState::Available;
  212    211   
        self.local_turn_owed |= local_h1_demand;
  213    212   
        true
  214    213   
    }
  215    214   
  216         -
    /// Returns whether a usable local turn currently excludes a peer reuse operation.
  217         -
    pub(super) fn blocks_peer_reuse(&self, local_h1_demand: bool) -> bool {
         215  +
    /// Returns whether a usable local turn currently excludes a peer H1 match.
         216  +
    fn blocks_peer_selection(&self, local_h1_demand: bool) -> bool {
  218    217   
        self.local_turn_owed && local_h1_demand
  219    218   
    }
  220    219   
  221    220   
    /// Consumes an owed turn when local HTTP/1 service wins.
  222         -
    pub(super) fn consume_local_turn(&mut self) -> bool {
         221  +
    fn consume_local_turn(&mut self) -> bool {
  223    222   
        if !self.local_turn_owed {
  224    223   
            return false;
  225    224   
        }
  226    225   
        self.local_turn_owed = false;
  227    226   
        true
  228    227   
    }
  229    228   
  230    229   
    /// Clears debt that can no longer be consumed by local demand.
  231         -
    pub(super) fn clear_unused_turn(&mut self, local_h1_demand: bool) -> bool {
         230  +
    fn clear_unused_turn(&mut self, local_h1_demand: bool) -> bool {
  232    231   
        if !self.local_turn_owed || local_h1_demand {
  233    232   
            return false;
  234    233   
        }
  235    234   
        self.local_turn_owed = false;
  236    235   
        true
  237    236   
    }
  238    237   
  239         -
    /// Returns whether another reuse reservation may be installed.
  240         -
    pub(super) fn is_available(&self) -> bool {
  241         -
        matches!(self.state, H1ReuseReservationState::Available)
         238  +
    /// Returns whether another peer reservation may be installed.
         239  +
    fn is_available(&self) -> bool {
         240  +
        matches!(self.state, H1ReservationState::Available)
  242    241   
    }
  243    242   
  244    243   
    /// Returns whether this reservation still names the given operation.
  245         -
    pub(super) fn names(&self, reuse_id: ReuseId) -> bool {
         244  +
    fn names(&self, match_id: H1MatchId) -> bool {
  246    245   
        matches!(
  247    246   
            self.state,
  248         -
            H1ReuseReservationState::Installed(current) | H1ReuseReservationState::Resolving(current)
  249         -
                if current == reuse_id
         247  +
            H1ReservationState::Installed(current) | H1ReservationState::Resolving(current)
         248  +
                if current == match_id
  250    249   
        )
  251    250   
    }
  252    251   
  253    252   
    /// Checks relationships that are not already encoded by the state enum.
  254         -
    pub(super) fn assert_consistent(&self, _supports_installed_reuse: bool) {
  255         -
        #[cfg(debug_assertions)]
  256         -
        {
  257         -
            if std::thread::panicking() {
  258         -
                return;
  259         -
            }
  260         -
            if matches!(self.state, H1ReuseReservationState::Installed(_)) {
  261         -
                assert!(
  262         -
                    _supports_installed_reuse,
  263         -
                    "installed HTTP/1 reuse operation had no externally owned connection-owning cell record to settle it"
  264         -
                );
  265         -
            }
         253  +
    #[cfg(any(debug_assertions, test))]
         254  +
    fn assert_consistent(&self, supports_peer_reservation: bool) {
         255  +
        if std::thread::panicking() {
         256  +
            return;
         257  +
        }
         258  +
        if matches!(self.state, H1ReservationState::Installed(_)) {
         259  +
            assert!(
         260  +
                supports_peer_reservation,
         261  +
                "installed HTTP/1 peer reservation had no externally owned sender record to settle it"
         262  +
            );
  266    263   
        }
  267    264   
    }
  268    265   
  269    266   
    #[cfg(test)]
  270         -
    pub(super) fn local_turn_owed(&self) -> bool {
         267  +
    fn local_turn_owed(&self) -> bool {
  271    268   
        self.local_turn_owed
  272    269   
    }
  273    270   
}
  274    271   
  275         -
/// Records owned by one cell and the order of reusable senders.
         272  +
/// Complete HTTP/1 sender and peer-reservation state under one cell lock.
  276    273   
#[derive(Debug, Default)]
  277         -
pub(super) struct H1Records {
         274  +
pub(super) struct H1CellState {
  278    275   
    /// Every installed record that has not completed logical close.
  279    276   
    records: HashMap<ConnectionId, H1Record>,
  280    277   
    /// Reusable records in return order; selection takes the newest sender.
  281         -
    idle: VecDeque<ConnectionId>,
         278  +
    idle_order: VecDeque<ConnectionId>,
         279  +
    /// Peer match that may intercept the next reusable sender return.
         280  +
    peer_reservation: H1Reservation,
  282    281   
}
  283    282   
  284    283   
/// One cell-owned HTTP/1 connection record.
  285    284   
#[derive(Debug)]
  286    285   
struct H1Record {
  287    286   
    /// Shared logical and physical connection lifetime.
  288    287   
    connection: Arc<ConnectionState>,
  289    288   
    /// Location of the record's exclusive sender.
  290         -
    residence: H1Residence,
         289  +
    sender_state: H1SenderResidence,
  291    290   
}
  292    291   
  293    292   
/// Authoritative location of one exclusive HTTP/1 sender.
  294    293   
#[derive(Debug)]
  295         -
enum H1Residence {
         294  +
enum H1SenderResidence {
  296    295   
    /// The sender is stored in this record and available for local selection.
  297    296   
    Idle {
  298    297   
        /// Exclusive sender available for the next local request.
  299    298   
        sender: H1Sender,
  300    299   
        /// Maintenance deadline, absent when idle expiry is disabled.
  301    300   
        deadline: Option<SystemTime>,
  302    301   
    },
  303    302   
    /// A request-side [`H1Selection`] or [`H1Exchange`] owns the sender.
  304    303   
    Selected,
  305    304   
    /// A [`ProvisionalH1`] owns the sender for cross-cell reuse.
  306         -
    Reserved,
         305  +
    ReservedForPeer,
  307    306   
    /// Logical close has started and no new selection or return may commit.
  308    307   
    Closing,
  309    308   
}
  310    309   
  311         -
impl H1Records {
         310  +
impl H1CellState {
         311  +
    /// Returns whether admission may install another peer reservation.
         312  +
    pub(super) fn peer_reservation_available(&self) -> bool {
         313  +
        self.peer_reservation.is_available()
         314  +
    }
         315  +
         316  +
    /// Returns whether fairness currently excludes peer selection.
         317  +
    pub(super) fn blocks_peer_selection(&self, local_h1_demand: bool) -> bool {
         318  +
        self.peer_reservation.blocks_peer_selection(local_h1_demand)
         319  +
    }
         320  +
         321  +
    /// Reserves a future sender return for one retained peer match.
         322  +
    pub(super) fn reserve_waiting(&mut self, match_id: H1MatchId) -> bool {
         323  +
        self.peer_reservation.reserve_waiting(match_id)
         324  +
    }
         325  +
         326  +
    /// Reserves a peer match that already owns an extracted sender.
         327  +
    pub(super) fn reserve_resolving(&mut self, match_id: H1MatchId) -> bool {
         328  +
        self.peer_reservation.reserve_resolving(match_id)
         329  +
    }
         330  +
         331  +
    /// Takes a waiting peer match when a reusable sender returns.
         332  +
    pub(super) fn take_return_match(&mut self) -> Option<H1MatchId> {
         333  +
        self.peer_reservation.take_return_match()
         334  +
    }
         335  +
         336  +
    /// Releases a peer reservation without recording a local turn.
         337  +
    pub(super) fn release_peer_reservation(&mut self, match_id: H1MatchId) -> bool {
         338  +
        self.peer_reservation.release(match_id)
         339  +
    }
         340  +
         341  +
    /// Returns whether the peer reservation still names `match_id`.
         342  +
    pub(super) fn names_peer_reservation(&self, match_id: H1MatchId) -> bool {
         343  +
        self.peer_reservation.names(match_id)
         344  +
    }
         345  +
         346  +
    /// Completes a transferred peer sender and records any local fairness turn.
         347  +
    pub(super) fn complete_peer_transfer(
         348  +
        &mut self,
         349  +
        match_id: H1MatchId,
         350  +
        local_h1_demand: bool,
         351  +
    ) -> bool {
         352  +
        self.peer_reservation
         353  +
            .complete_transfer(match_id, local_h1_demand)
         354  +
    }
         355  +
         356  +
    /// Consumes an owed local turn after local HTTP/1 service wins.
         357  +
    pub(super) fn consume_local_turn(&mut self) -> bool {
         358  +
        self.peer_reservation.consume_local_turn()
         359  +
    }
         360  +
         361  +
    /// Clears fairness debt that no live local demand can consume.
         362  +
    pub(super) fn clear_unused_turn(&mut self, local_h1_demand: bool) -> bool {
         363  +
        self.peer_reservation.clear_unused_turn(local_h1_demand)
         364  +
    }
         365  +
         366  +
    /// Returns whether this cell owes its next usable sender to local demand.
         367  +
    #[cfg(test)]
         368  +
    pub(super) fn local_turn_owed(&self) -> bool {
         369  +
        self.peer_reservation.local_turn_owed()
         370  +
    }
         371  +
  312    372   
    /// Installs a fresh connection as selected by its launching acquisition.
  313         -
    pub(super) fn install_selected(
         373  +
    pub(super) fn insert_selected(
  314    374   
        &mut self,
  315    375   
        connection: Arc<ConnectionState>,
  316    376   
        sender: H1Sender,
  317         -
    ) -> Result<OwnedH1, OwnedH1> {
  318         -
        let owner = OwnedH1::new(connection, sender, false);
         377  +
    ) -> Result<OwnedH1Sender, OwnedH1Sender> {
         378  +
        let owner = OwnedH1Sender::new(connection, sender, false);
  319    379   
        if self.records.contains_key(&owner.id()) {
  320    380   
            return Err(owner);
  321    381   
        }
  322    382   
        self.records.insert(
  323    383   
            owner.id(),
  324    384   
            H1Record {
  325    385   
                connection: owner.connection.clone(),
  326         -
                residence: H1Residence::Selected,
         386  +
                sender_state: H1SenderResidence::Selected,
  327    387   
            },
  328    388   
        );
  329    389   
        self.assert_consistent();
  330    390   
        Ok(owner)
  331    391   
    }
  332    392   
  333    393   
    /// Installs a connection that completed without a live launching waiter.
  334    394   
    #[cfg(test)]
  335         -
    pub(super) fn install_idle(
         395  +
    pub(super) fn insert_idle(
  336    396   
        &mut self,
  337    397   
        connection: Arc<ConnectionState>,
  338    398   
        sender: H1Sender,
  339    399   
        deadline: Option<SystemTime>,
  340         -
    ) -> Result<(), OwnedH1> {
  341         -
        let owner = OwnedH1::new(connection, sender, true);
         400  +
    ) -> Result<(), OwnedH1Sender> {
         401  +
        let owner = OwnedH1Sender::new(connection, sender, true);
  342    402   
        if self.records.contains_key(&owner.id()) {
  343    403   
            return Err(owner);
  344    404   
        }
  345    405   
        let id = owner.id();
  346         -
        let OwnedH1 {
         406  +
        let OwnedH1Sender {
  347    407   
            connection, sender, ..
  348    408   
        } = owner;
  349    409   
        self.records.insert(
  350    410   
            id,
  351    411   
            H1Record {
  352    412   
                connection,
  353         -
                residence: H1Residence::Idle { sender, deadline },
         413  +
                sender_state: H1SenderResidence::Idle { sender, deadline },
  354    414   
            },
  355    415   
        );
  356         -
        self.idle.push_back(id);
         416  +
        self.idle_order.push_back(id);
  357    417   
        self.assert_consistent();
  358    418   
        Ok(())
  359    419   
    }
  360    420   
  361    421   
    /// Takes the most recently returned idle sender for one request.
  362         -
    pub(super) fn select_idle(&mut self) -> Option<OwnedH1> {
  363         -
        let id = self.idle.pop_back()?;
         422  +
    pub(super) fn select_idle(&mut self) -> Option<OwnedH1Sender> {
         423  +
        let id = self.idle_order.pop_back()?;
  364    424   
        let record = self
  365    425   
            .records
  366    426   
            .get_mut(&id)
  367    427   
            .expect("idle HTTP/1 record disappeared");
  368         -
        if !matches!(record.residence, H1Residence::Idle { .. }) {
         428  +
        if !matches!(record.sender_state, H1SenderResidence::Idle { .. }) {
  369    429   
            panic!("idle HTTP/1 order named a non-idle record");
  370    430   
        }
  371         -
        let H1Residence::Idle { sender, .. } =
  372         -
            std::mem::replace(&mut record.residence, H1Residence::Selected)
         431  +
        let H1SenderResidence::Idle { sender, .. } =
         432  +
            std::mem::replace(&mut record.sender_state, H1SenderResidence::Selected)
  373    433   
        else {
  374    434   
            unreachable!("HTTP/1 residence changed under the cell lock");
  375    435   
        };
  376         -
        let owner = OwnedH1::new(record.connection.clone(), sender, true);
         436  +
        let owner = OwnedH1Sender::new(record.connection.clone(), sender, true);
  377    437   
        self.assert_consistent();
  378    438   
        Some(owner)
  379    439   
    }
  380    440   
  381    441   
    /// Extracts the newest idle sender into provisional return residence.
  382    442   
    ///
  383         -
    /// A return reuse uses this path so rejection can follow the same ordinary
         443  +
    /// A returning sender uses this path so rejection can follow the same ordinary
  384    444   
    /// return fallback as a sender intercepted after an active exchange.
  385         -
    pub(super) fn take_idle_for_reuse(&mut self) -> Option<OwnedH1> {
  386         -
        let id = self.idle.pop_back()?;
         445  +
    pub(super) fn take_idle_candidate(&mut self) -> Option<OwnedH1Sender> {
         446  +
        let id = self.idle_order.pop_back()?;
  387    447   
        let record = self
  388    448   
            .records
  389    449   
            .get_mut(&id)
  390    450   
            .expect("idle HTTP/1 record disappeared");
  391         -
        let H1Residence::Idle { sender, .. } =
  392         -
            std::mem::replace(&mut record.residence, H1Residence::Reserved)
         451  +
        let H1SenderResidence::Idle { sender, .. } =
         452  +
            std::mem::replace(&mut record.sender_state, H1SenderResidence::ReservedForPeer)
  393    453   
        else {
  394    454   
            panic!("idle HTTP/1 order named a non-idle record");
  395    455   
        };
  396         -
        let owner = OwnedH1::new(record.connection.clone(), sender, true);
         456  +
        let owner = OwnedH1Sender::new(record.connection.clone(), sender, true);
  397    457   
        self.assert_consistent();
  398    458   
        Some(owner)
  399    459   
    }
  400    460   
  401         -
    /// Returns whether a sender could satisfy a connection-owning cell reuse now or on return.
         461  +
    /// Returns whether a sender could satisfy peer selection now or on return.
  402    462   
    pub(super) fn has_returnable(&self) -> bool {
  403    463   
        self.records.values().any(|record| {
  404    464   
            matches!(
  405         -
                record.residence,
  406         -
                H1Residence::Idle { .. } | H1Residence::Selected | H1Residence::Reserved
         465  +
                record.sender_state,
         466  +
                H1SenderResidence::Idle { .. }
         467  +
                    | H1SenderResidence::Selected
         468  +
                    | H1SenderResidence::ReservedForPeer
  407    469   
            )
  408    470   
        })
  409    471   
    }
  410    472   
  411    473   
    /// Returns whether `owner` may still re-enter reusable return policy.
  412         -
    pub(super) fn accepts_return(&self, owner: &OwnedH1) -> bool {
         474  +
    pub(super) fn accepts_return(&self, owner: &OwnedH1Sender) -> bool {
  413    475   
        self.records.get(&owner.id()).is_some_and(|record| {
  414    476   
            matches!(
  415         -
                record.residence,
  416         -
                H1Residence::Selected | H1Residence::Reserved
         477  +
                record.sender_state,
         478  +
                H1SenderResidence::Selected | H1SenderResidence::ReservedForPeer
  417    479   
            )
  418    480   
        })
  419    481   
    }
  420    482   
  421         -
    /// Returns whether an installed reuse still has a cell-local reservation.
         483  +
    /// Returns whether an installed peer reservation still has sender state.
  422    484   
    ///
  423    485   
    /// Logical close may move an externally owned sender to `Closing` before
  424         -
    /// its return resolves the installed reuse, so reuse consistency is
  425         -
    /// broader than current reuse eligibility.
  426         -
    pub(super) fn supports_installed_reuse(&self) -> bool {
         486  +
    /// its return resolves the installed match, so reservation consistency is
         487  +
    /// broader than current peer selectability.
         488  +
    #[cfg(any(debug_assertions, test))]
         489  +
    fn supports_peer_reservation(&self) -> bool {
  427    490   
        self.records.values().any(|record| {
  428    491   
            matches!(
  429         -
                record.residence,
  430         -
                H1Residence::Selected | H1Residence::Reserved | H1Residence::Closing
         492  +
                record.sender_state,
         493  +
                H1SenderResidence::Selected
         494  +
                    | H1SenderResidence::ReservedForPeer
         495  +
                    | H1SenderResidence::Closing
  431    496   
            )
  432    497   
        })
  433    498   
    }
  434    499   
  435    500   
    /// Reserves an external sender for cross-cell reuse.
  436         -
    pub(super) fn reserve_for_reuse(&mut self, owner: &OwnedH1) -> bool {
         501  +
    pub(super) fn reserve_for_peer(&mut self, owner: &OwnedH1Sender) -> bool {
  437    502   
        let Some(record) = self.records.get_mut(&owner.id()) else {
  438    503   
            return false;
  439    504   
        };
  440         -
        match record.residence {
  441         -
            H1Residence::Selected => {
  442         -
                record.residence = H1Residence::Reserved;
         505  +
        match record.sender_state {
         506  +
            H1SenderResidence::Selected => {
         507  +
                record.sender_state = H1SenderResidence::ReservedForPeer;
  443    508   
                self.assert_consistent();
  444    509   
                true
  445    510   
            }
  446         -
            H1Residence::Reserved => true,
  447         -
            H1Residence::Idle { .. } | H1Residence::Closing => false,
         511  +
            H1SenderResidence::ReservedForPeer => true,
         512  +
            H1SenderResidence::Idle { .. } | H1SenderResidence::Closing => false,
  448    513   
        }
  449    514   
    }
  450    515   
  451    516   
    /// Restores a returned sender to idle storage.
  452    517   
    pub(super) fn return_idle(
  453    518   
        &mut self,
  454         -
        owner: OwnedH1,
         519  +
        owner: OwnedH1Sender,
  455    520   
        deadline: Option<SystemTime>,
  456         -
    ) -> Result<(), OwnedH1> {
         521  +
    ) -> Result<(), OwnedH1Sender> {
  457    522   
        let Some(record) = self.records.get_mut(&owner.id()) else {
  458    523   
            return Err(owner);
  459    524   
        };
  460    525   
        if !matches!(
  461         -
            record.residence,
  462         -
            H1Residence::Selected | H1Residence::Reserved
         526  +
            record.sender_state,
         527  +
            H1SenderResidence::Selected | H1SenderResidence::ReservedForPeer
  463    528   
        ) {
  464    529   
            return Err(owner);
  465    530   
        }
  466    531   
        let id = owner.id();
  467         -
        let OwnedH1 { sender, .. } = owner;
  468         -
        record.residence = H1Residence::Idle { sender, deadline };
  469         -
        self.idle.push_back(id);
         532  +
        let OwnedH1Sender { sender, .. } = owner;
         533  +
        record.sender_state = H1SenderResidence::Idle { sender, deadline };
         534  +
        self.idle_order.push_back(id);
  470    535   
        self.assert_consistent();
  471    536   
        Ok(())
  472    537   
    }
  473    538   
  474    539   
    /// Commits a selected or returning sender to a waiting request.
  475         -
    pub(super) fn commit_return_to_waiter(&mut self, owner: &OwnedH1) -> bool {
         540  +
    pub(super) fn commit_return_to_waiter(&mut self, owner: &OwnedH1Sender) -> bool {
  476    541   
        let Some(record) = self.records.get_mut(&owner.id()) else {
  477    542   
            return false;
  478    543   
        };
  479         -
        match record.residence {
  480         -
            H1Residence::Selected => true,
  481         -
            H1Residence::Reserved => {
  482         -
                record.residence = H1Residence::Selected;
         544  +
        match record.sender_state {
         545  +
            H1SenderResidence::Selected => true,
         546  +
            H1SenderResidence::ReservedForPeer => {
         547  +
                record.sender_state = H1SenderResidence::Selected;
  483    548   
                self.assert_consistent();
  484    549   
                true
  485    550   
            }
  486         -
            H1Residence::Idle { .. } | H1Residence::Closing => false,
         551  +
            H1SenderResidence::Idle { .. } | H1SenderResidence::Closing => false,
  487    552   
        }
  488    553   
    }
  489    554   
  490    555   
    /// Marks a selected or returning sender as closing.
  491    556   
    ///
  492    557   
    /// The caller still owns the sender and must complete logical close after
  493    558   
    /// releasing the cell lock.
  494         -
    pub(super) fn close_owned(&mut self, owner: &OwnedH1) -> bool {
         559  +
    pub(super) fn close_owned(&mut self, owner: &OwnedH1Sender) -> bool {
  495    560   
        let should_close = match self.records.get_mut(&owner.id()) {
  496         -
            Some(record) => match record.residence {
  497         -
                H1Residence::Selected | H1Residence::Reserved => {
  498         -
                    record.residence = H1Residence::Closing;
         561  +
            Some(record) => match record.sender_state {
         562  +
                H1SenderResidence::Selected | H1SenderResidence::ReservedForPeer => {
         563  +
                    record.sender_state = H1SenderResidence::Closing;
  499    564   
                    true
  500    565   
                }
  501         -
                H1Residence::Closing => false,
  502         -
                H1Residence::Idle { .. } => {
         566  +
                H1SenderResidence::Closing => false,
         567  +
                H1SenderResidence::Idle { .. } => {
  503    568   
                    panic!("externally owned HTTP/1 sender was recorded as idle")
  504    569   
                }
  505    570   
            },
  506    571   
            None => true,
  507    572   
        };
  508    573   
        self.assert_consistent();
  509    574   
        should_close
  510    575   
    }
  511    576   
  512    577   
    /// Starts close for a record named without its external sender.
  513    578   
    ///
  514    579   
    /// Idle close extracts the sender. Selected and returning records stay
  515    580   
    /// represented as `Closing` until their external owner comes back.
  516         -
    pub(super) fn begin_close(&mut self, id: ConnectionId) -> Option<CloseRecord> {
         581  +
    pub(super) fn begin_close(&mut self, id: ConnectionId) -> Option<H1CloseTransition> {
  517    582   
        let record = self.records.get_mut(&id)?;
  518    583   
        let connection = record.connection.clone();
  519         -
        let sender = match &record.residence {
  520         -
            H1Residence::Idle { .. } => {
         584  +
        let sender = match &record.sender_state {
         585  +
            H1SenderResidence::Idle { .. } => {
  521    586   
                let position = self
  522         -
                    .idle
         587  +
                    .idle_order
  523    588   
                    .iter()
  524    589   
                    .position(|candidate| *candidate == id)
  525    590   
                    .expect("idle HTTP/1 record was absent from idle order");
  526         -
                self.idle.remove(position);
  527         -
                let H1Residence::Idle { sender, .. } =
  528         -
                    std::mem::replace(&mut record.residence, H1Residence::Closing)
         591  +
                self.idle_order.remove(position);
         592  +
                let H1SenderResidence::Idle { sender, .. } =
         593  +
                    std::mem::replace(&mut record.sender_state, H1SenderResidence::Closing)
  529    594   
                else {
  530    595   
                    unreachable!("HTTP/1 residence changed under the cell lock");
  531    596   
                };
  532    597   
                Some(sender)
  533    598   
            }
  534         -
            H1Residence::Selected | H1Residence::Reserved => {
  535         -
                record.residence = H1Residence::Closing;
         599  +
            H1SenderResidence::Selected | H1SenderResidence::ReservedForPeer => {
         600  +
                record.sender_state = H1SenderResidence::Closing;
  536    601   
                None
  537    602   
            }
  538         -
            H1Residence::Closing => return None,
         603  +
            H1SenderResidence::Closing => return None,
  539    604   
        };
  540    605   
        self.assert_consistent();
  541         -
        Some(CloseRecord { connection, sender })
         606  +
        Some(H1CloseTransition { connection, sender })
  542    607   
    }
  543    608   
  544    609   
    /// Removes a record after logical close and sender destruction complete.
  545         -
    pub(super) fn finish_close(&mut self, id: ConnectionId) {
         610  +
    pub(super) fn remove_closed(&mut self, id: ConnectionId) {
  546    611   
        let Some(record) = self.records.get(&id) else {
  547    612   
            return;
  548    613   
        };
  549         -
        if !matches!(record.residence, H1Residence::Closing) {
         614  +
        if !matches!(record.sender_state, H1SenderResidence::Closing) {
  550    615   
            return;
  551    616   
        }
  552    617   
        self.records.remove(&id);
  553    618   
        self.assert_consistent();
  554    619   
    }
  555    620   
  556    621   
    /// Returns the installed record and idle counts.
  557    622   
    #[cfg(test)]
  558    623   
    pub(super) fn counts(&self) -> (usize, usize) {
  559         -
        (self.records.len(), self.idle.len())
         624  +
        (self.records.len(), self.idle_order.len())
  560    625   
    }
  561    626   
  562    627   
    /// Returns the sole installed connection for focused dispatch tests.
  563    628   
    #[cfg(all(test, feature = "rt-tokio"))]
  564    629   
    pub(super) fn only_connection_for_test(&self) -> Arc<ConnectionState> {
  565    630   
        assert_eq!(
  566    631   
            1,
  567    632   
            self.records.len(),
  568    633   
            "expected exactly one installed HTTP/1 record"
  569    634   
        );
  570    635   
        self.records
  571    636   
            .values()
  572    637   
            .next()
  573    638   
            .expect("HTTP/1 record count changed under the cell lock")
  574    639   
            .connection
  575    640   
            .clone()
  576    641   
    }
  577    642   
  578    643   
    /// Returns idle records whose configured deadline has elapsed.
  579    644   
    pub(super) fn expired_idle(&self, now: SystemTime) -> Vec<ConnectionId> {
  580         -
        self.idle
         645  +
        self.idle_order
  581    646   
            .iter()
  582    647   
            .copied()
  583    648   
            .filter(|id| {
  584    649   
                matches!(
  585         -
                    self.records.get(id).map(|record| &record.residence),
  586         -
                    Some(H1Residence::Idle {
         650  +
                    self.records.get(id).map(|record| &record.sender_state),
         651  +
                    Some(H1SenderResidence::Idle {
  587    652   
                        deadline: Some(deadline),
  588    653   
                        ..
  589    654   
                    }) if *deadline <= now
  590    655   
                )
  591    656   
            })
  592    657   
            .collect()
  593    658   
    }
  594    659   
  595    660   
    /// Returns the nearest configured deadline among reusable senders.
  596    661   
    pub(super) fn nearest_idle_deadline(&self) -> Option<SystemTime> {
  597         -
        self.idle
         662  +
        self.idle_order
  598    663   
            .iter()
  599    664   
            .filter_map(|id| {
  600    665   
                match &self
  601    666   
                    .records
  602    667   
                    .get(id)
  603    668   
                    .expect("idle HTTP/1 record disappeared")
  604         -
                    .residence
         669  +
                    .sender_state
  605    670   
                {
  606         -
                    H1Residence::Idle { deadline, .. } => *deadline,
         671  +
                    H1SenderResidence::Idle { deadline, .. } => *deadline,
  607    672   
                    _ => unreachable!("idle HTTP/1 order named a non-idle record"),
  608    673   
                }
  609    674   
            })
  610    675   
            .min()
  611    676   
    }
  612    677   
  613    678   
    /// Returns every installed record identity for pool-wide shutdown.
  614    679   
    pub(super) fn connection_ids(&self) -> Vec<ConnectionId> {
  615    680   
        self.records.keys().copied().collect()
  616    681   
    }
  617    682   
  618    683   
    /// Checks that idle records and the idle order describe the same set.
  619    684   
    pub(super) fn assert_consistent(&self) {
  620         -
        #[cfg(debug_assertions)]
         685  +
        #[cfg(any(debug_assertions, test))]
  621    686   
        {
  622    687   
            if std::thread::panicking() {
  623    688   
                return;
  624    689   
            }
         690  +
            self.peer_reservation
         691  +
                .assert_consistent(self.supports_peer_reservation());
  625    692   
            let idle_records = self
  626    693   
                .records
  627    694   
                .values()
  628         -
                .filter(|record| matches!(record.residence, H1Residence::Idle { .. }))
         695  +
                .filter(|record| matches!(record.sender_state, H1SenderResidence::Idle { .. }))
  629    696   
                .count();
  630    697   
            assert_eq!(
  631    698   
                idle_records,
  632         -
                self.idle.len(),
         699  +
                self.idle_order.len(),
  633    700   
                "HTTP/1 idle count did not match idle order"
  634    701   
            );
  635    702   
            let mut seen = HashMap::new();
  636         -
            for id in &self.idle {
         703  +
            for id in &self.idle_order {
  637    704   
                assert!(
  638    705   
                    matches!(
  639         -
                        self.records.get(id).map(|record| &record.residence),
  640         -
                        Some(H1Residence::Idle { .. })
         706  +
                        self.records.get(id).map(|record| &record.sender_state),
         707  +
                        Some(H1SenderResidence::Idle { .. })
  641    708   
                    ),
  642    709   
                    "HTTP/1 idle order named a missing or non-idle record"
  643    710   
                );
  644    711   
                assert!(
  645    712   
                    seen.insert(*id, ()).is_none(),
  646    713   
                    "HTTP/1 idle order contained a duplicate record"
  647    714   
                );
  648    715   
            }
  649    716   
        }
  650    717   
    }
  651    718   
}
  652    719   
  653    720   
/// Exclusive sender and connection state detached from a connection-owning cell record.
  654    721   
#[derive(Debug)]
  655         -
pub(super) struct OwnedH1 {
         722  +
pub(super) struct OwnedH1Sender {
  656    723   
    /// Shared lifetime state for the physical connection.
  657    724   
    connection: Arc<ConnectionState>,
  658    725   
    /// The one Hyper HTTP/1 sender for the connection.
  659    726   
    sender: H1Sender,
  660    727   
    /// Whether this sender came from an already installed reusable record.
  661    728   
    reused: bool,
  662    729   
}
  663    730   
  664         -
impl OwnedH1 {
         731  +
impl OwnedH1Sender {
  665    732   
    /// Creates detached sender ownership.
  666    733   
    fn new(connection: Arc<ConnectionState>, sender: H1Sender, reused: bool) -> Self {
  667    734   
        Self {
  668    735   
            connection,
  669    736   
            sender,
  670    737   
            reused,
  671    738   
        }
  672    739   
    }
  673    740   
  674    741   
    /// Returns the installed connection identity.
  675    742   
    pub(super) fn id(&self) -> ConnectionId {
  676    743   
        self.connection.id()
  677    744   
    }
  678    745   
  679    746   
    /// Returns whether this selection came from an existing reusable record.
  680    747   
    pub(super) fn is_reused(&self) -> bool {
  681    748   
        self.reused
  682    749   
    }
  683    750   
  684    751   
    /// Marks the next request as reusing an already established connection.
  685    752   
    pub(super) fn mark_reused(&mut self) {
  686    753   
        self.reused = true;
  687    754   
    }
  688    755   
  689    756   
    /// Returns the shared connection state.
  690    757   
    pub(super) fn connection(&self) -> &Arc<ConnectionState> {
  691    758   
        &self.connection
  692    759   
    }
  693    760   
  694    761   
    /// Returns the exclusive sender.
  695    762   
    pub(super) fn sender_mut(&mut self) -> &mut H1Sender {
  696    763   
        &mut self.sender
  697    764   
    }
  698    765   
  699    766   
    /// Returns the synthetic sender identity.
  700    767   
    #[cfg(test)]
  701    768   
    pub(super) fn test_sender_id(&self) -> u64 {
  702    769   
        self.sender.test_id()
  703    770   
    }
  704    771   
}
  705    772   
  706    773   
/// Returns an external sender to its connection-owning cell.
  707    774   
///
  708    775   
/// A sender whose cell was torn down closes its connection directly.
  709         -
fn return_to_connection_cell(connection_cell: &Weak<OriginCell>, owner: OwnedH1) {
         776  +
fn return_to_connection_cell(connection_cell: &Weak<OriginCell>, owner: OwnedH1Sender) {
  710    777   
    if let Some(connection_cell) = connection_cell.upgrade() {
  711    778   
        OriginCell::return_h1_owner(&connection_cell, owner);
  712    779   
    } else {
  713    780   
        owner.connection().logical_close(CloseReason::PoolDropped);
  714    781   
        drop(owner);
  715    782   
    }
  716    783   
}
  717    784   
  718    785   
/// Retires an external sender through its connection-owning cell.
  719    786   
///
  720    787   
/// A sender whose cell was torn down closes its connection directly.
  721    788   
fn retire_at_connection_cell(
  722    789   
    connection_cell: &Weak<OriginCell>,
  723         -
    owner: OwnedH1,
         790  +
    owner: OwnedH1Sender,
  724    791   
    reason: CloseReason,
  725    792   
) {
  726    793   
    if let Some(connection_cell) = connection_cell.upgrade() {
  727    794   
        OriginCell::retire_h1_owner(&connection_cell, owner, reason);
  728    795   
    } else {
  729    796   
        owner.connection().logical_close(reason);
  730    797   
        drop(owner);
  731    798   
    }
  732    799   
}
  733    800   
  734    801   
/// State detached when close begins for a record named by identity.
  735         -
pub(super) struct CloseRecord {
         802  +
pub(super) struct H1CloseTransition {
  736    803   
    /// Connection whose logical lifetime close must end.
  737    804   
    pub(super) connection: Arc<ConnectionState>,
  738    805   
    /// Idle sender extracted for destruction, if close found one in the cell.
  739    806   
    pub(super) sender: Option<H1Sender>,
  740    807   
}
  741    808   
  742    809   
/// Exclusive sender checked out for readiness and one request dispatch.
  743    810   
///
  744    811   
/// Local selection, successful establishment, or peer reuse creates this value
  745    812   
/// while the installed record is `Selected`. The value alone may use the
  746    813   
/// sender. Hyper accepting the request transfers ownership to [`H1Exchange`];
  747    814   
/// explicit retirement closes the installed record. Dropping an undispatched
  748    815   
/// selection returns the sender through ordinary connection-owning-cell policy.
  749    816   
pub(in crate::client::pool) struct H1Selection {
  750    817   
    /// Non-retaining reference to the cell that owns the installed record.
  751    818   
    connection_cell: Weak<OriginCell>,
  752    819   
    /// Sender ownership until return, retirement, or response transfer.
  753         -
    owner: Option<OwnedH1>,
         820  +
    owner: Option<OwnedH1Sender>,
  754    821   
}
  755    822   
  756    823   
impl H1Selection {
  757    824   
    /// Creates a selected sender owned outside the connection-owning cell lock.
  758         -
    pub(super) fn new(connection_cell: &Arc<OriginCell>, owner: OwnedH1) -> Self {
         825  +
    pub(super) fn new(connection_cell: &Arc<OriginCell>, owner: OwnedH1Sender) -> Self {
  759    826   
        Self {
  760    827   
            connection_cell: Weak::from_arc(connection_cell),
  761    828   
            owner: Some(owner),
  762    829   
        }
  763    830   
    }
  764    831   
  765    832   
    /// Returns this selection's physical connection identity.
  766    833   
    pub(in crate::client::pool) fn connection_id(&self) -> ConnectionId {
  767    834   
        self.owner
  768    835   
            .as_ref()
  769    836   
            .expect("HTTP/1 selection consumed more than once")
  770    837   
            .id()
  771    838   
    }
  772    839   
  773    840   
    /// Returns whether this sender came from a reusable installed record.
  774    841   
    pub(in crate::client::pool) fn is_reused(&self) -> bool {
  775    842   
        self.owner
  776    843   
            .as_ref()
  777    844   
            .expect("HTTP/1 selection consumed more than once")
  778    845   
            .is_reused()
  779    846   
    }
  780    847   
  781    848   
    /// Returns the selected connection state.
  782    849   
    pub(in crate::client::pool) fn connection(&self) -> &Arc<ConnectionState> {
  783    850   
        self.owner
  784    851   
            .as_ref()
  785    852   
            .expect("HTTP/1 selection consumed more than once")
  786    853   
            .connection()
  787    854   
    }
  788    855   
  789    856   
    /// Returns the exclusive sender for readiness and dispatch.
  790    857   
    pub(in crate::client::pool) fn sender_mut(&mut self) -> &mut H1Sender {
  791    858   
        self.owner
  792    859   
            .as_mut()
  793    860   
            .expect("HTTP/1 selection consumed more than once")
  794    861   
            .sender_mut()
  795    862   
    }
  796    863   
  797    864   
    /// Transfers the sender after Hyper accepts the request.
  798    865   
    ///
  799    866   
    /// The installed record remains `Selected`; [`H1Exchange`] now owns the
  800    867   
    /// sender until the response proves reuse or requires retirement.
  801    868   
    pub(in crate::client::pool) fn into_exchange(mut self) -> H1Exchange {
  802    869   
        H1Exchange {
  803    870   
            connection_cell: self.connection_cell.clone(),
  804    871   
            owner: self.owner.take(),
  805    872   
        }
  806    873   
    }
  807    874   
  808         -
    /// Returns non-retaining close authority for this selected generation.
         875  +
    /// Returns non-retaining close authority for this selected connection.
  809    876   
    pub(in crate::client::pool) fn close_handle(&self) -> H1CloseHandle {
  810    877   
        let owner = self
  811    878   
            .owner
  812    879   
            .as_ref()
  813    880   
            .expect("HTTP/1 selection consumed more than once");
  814    881   
        H1CloseHandle {
  815    882   
            connection_cell: self.connection_cell.clone(),
  816    883   
            connection: Weak::from_arc(owner.connection()),
  817         -
            id: owner.id(),
         884  +
            connection_id: owner.id(),
  818    885   
        }
  819    886   
    }
  820    887   
  821    888   
    /// Retires this selection instead of returning it for reuse.
  822    889   
    pub(in crate::client::pool) fn retire_connection(mut self, reason: CloseReason) {
  823    890   
        if let Some(owner) = self.owner.take() {
  824    891   
            retire_at_connection_cell(&self.connection_cell, owner, reason);
  825    892   
        }
  826    893   
    }
  827    894   
  828    895   
    /// Returns the synthetic sender identity.
  829    896   
    #[cfg(test)]
  830    897   
    pub(super) fn test_sender_id(&self) -> u64 {
  831    898   
        self.owner
  832    899   
            .as_ref()
  833    900   
            .expect("HTTP/1 selection consumed more than once")
  834    901   
            .test_sender_id()
  835    902   
    }
  836    903   
}
  837    904   
  838    905   
impl fmt::Debug for H1Selection {
  839    906   
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
  840    907   
        f.debug_struct("H1Selection")
  841    908   
            .field(
  842    909   
                "connection-owning cell",
  843    910   
                &self
  844    911   
                    .connection_cell
  845    912   
                    .upgrade()
  846    913   
                    .map(|connection_cell| connection_cell.id().clone()),
  847    914   
            )
  848         -
            .field("connection_id", &self.owner.as_ref().map(OwnedH1::id))
         915  +
            .field("connection_id", &self.owner.as_ref().map(OwnedH1Sender::id))
  849    916   
            .finish()
  850    917   
    }
  851    918   
}
  852    919   
  853    920   
impl Drop for H1Selection {
  854    921   
    fn drop(&mut self) {
  855    922   
        if let Some(owner) = self.owner.take() {
  856    923   
            return_to_connection_cell(&self.connection_cell, owner);
  857    924   
        }
  858    925   
    }
  859    926   
}
  860    927   
  861    928   
/// Exclusive sender for a request Hyper accepted on this connection.
  862    929   
///
  863    930   
/// The installed record remains `Selected` while response-body processing owns
  864    931   
/// this value. After a complete response and successful Hyper readiness,
  865    932   
/// [`H1Exchange::offer_for_reuse`] runs owning-cell return arbitration.
  866    933   
/// Explicit retirement handles protocol failure or upgrade. Dropping the
  867    934   
/// exchange means a reusable message boundary was not proven and closes the
  868    935   
/// connection as an incomplete HTTP/1 exchange.
  869    936   
pub(in crate::client::pool) struct H1Exchange {
  870    937   
    /// Non-retaining reference to the cell that owns the selected record.
  871    938   
    connection_cell: Weak<OriginCell>,
  872    939   
    /// Sender held until Hyper proves it may return.
  873         -
    owner: Option<OwnedH1>,
         940  +
    owner: Option<OwnedH1Sender>,
  874    941   
}
  875    942   
  876    943   
impl H1Exchange {
  877    944   
    /// Returns whether Hyper already permits another request.
  878    945   
    pub(in crate::client::pool) fn is_ready(&self) -> bool {
  879    946   
        self.owner
  880    947   
            .as_ref()
  881    948   
            .expect("HTTP/1 exchange consumed more than once")
  882    949   
            .sender
  883    950   
            .is_ready()
  884    951   
    }
  885    952   
  886    953   
    /// Polls Hyper for proof that the sender can accept another request.
  887    954   
    pub(in crate::client::pool) fn poll_ready(
  888    955   
        &mut self,
  889    956   
        cx: &mut Context<'_>,
  890    957   
    ) -> Poll<Result<(), hyper::Error>> {
  891    958   
        self.owner
  892    959   
            .as_mut()
  893    960   
            .expect("HTTP/1 exchange consumed more than once")
  894    961   
            .sender_mut()
  895    962   
            .hyper_mut()
  896    963   
            .poll_ready(cx)
  897    964   
    }
  898    965   
  899    966   
    /// Offers a proven-ready sender to owning-cell reuse policy.
  900    967   
    pub(in crate::client::pool) fn offer_for_reuse(mut self) {
  901    968   
        let mut owner = self
  902    969   
            .owner
  903    970   
            .take()
  904    971   
            .expect("HTTP/1 exchange consumed more than once");
  905    972   
        owner.mark_reused();
  906    973   
        return_to_connection_cell(&self.connection_cell, owner);
  907    974   
    }
  908    975   
  909    976   
    /// Retires the sender instead of returning it to the connection-owning cell.
  910    977   
    pub(in crate::client::pool) fn retire_connection(mut self, reason: CloseReason) {
  911    978   
        if let Some(owner) = self.owner.take() {
  912    979   
            let upgrade = (reason == CloseReason::Upgraded).then(|| owner.connection().clone());
  913    980   
            retire_at_connection_cell(&self.connection_cell, owner, reason);
  914    981   
            if let Some(connection) = upgrade {
  915    982   
                connection.refine_protocol_close_as_upgrade();
  916         -
                #[cfg(debug_assertions)]
         983  +
                #[cfg(any(debug_assertions, test))]
  917    984   
                connection.debug_assert_close_reason(CloseReason::Upgraded);
  918    985   
            }
  919    986   
        }
  920    987   
    }
  921    988   
}
  922    989   
  923    990   
impl fmt::Debug for H1Exchange {
  924    991   
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
  925    992   
        f.debug_struct("H1Exchange")
  926    993   
            .field(
  927    994   
                "connection-owning cell",
  928    995   
                &self
  929    996   
                    .connection_cell
  930    997   
                    .upgrade()
  931    998   
                    .map(|connection_cell| connection_cell.id().clone()),
  932    999   
            )
  933         -
            .field("connection_id", &self.owner.as_ref().map(OwnedH1::id))
        1000  +
            .field("connection_id", &self.owner.as_ref().map(OwnedH1Sender::id))
  934   1001   
            .finish()
  935   1002   
    }
  936   1003   
}
  937   1004   
  938   1005   
impl Drop for H1Exchange {
  939   1006   
    fn drop(&mut self) {
  940   1007   
        if let Some(owner) = self.owner.take() {
  941   1008   
            retire_at_connection_cell(
  942   1009   
                &self.connection_cell,
  943   1010   
                owner,
  944   1011   
                CloseReason::IncompleteH1Exchange,
  945   1012   
            );
  946   1013   
        }
  947   1014   
    }
  948   1015   
}
  949   1016   
  950         -
/// Sender temporarily detached from a `Reserved` record for peer reuse.
        1017  +
/// Sender temporarily detached from a `ReservedForPeer` record.
  951   1018   
///
  952         -
/// Reuse arbitration creates this value before crossing from the
        1019  +
/// Admission matching creates this value before crossing from the
  953   1020   
/// connection-owning cell to a requesting cell. A successful borrow converts
  954   1021   
/// it into [`H1Selection`]; reclaim closes the connection and releases its
  955   1022   
/// capacity. Rejection, cancellation, or `Drop` returns the sender through
  956   1023   
/// ordinary connection-owning-cell policy.
  957   1024   
pub(in crate::client::pool) struct ProvisionalH1 {
  958         -
    /// Non-retaining connection-owning cell whose record remains in `Reserved`.
        1025  +
    /// Non-retaining owning cell whose record remains `ReservedForPeer`.
  959   1026   
    connection_cell: Weak<OriginCell>,
  960   1027   
    /// Sender reserved by the provisional action.
  961         -
    owner: Option<OwnedH1>,
        1028  +
    owner: Option<OwnedH1Sender>,
  962   1029   
}
  963   1030   
  964   1031   
impl ProvisionalH1 {
  965         -
    /// Creates a provisional owner for a sender extracted by reuse arbitration.
  966         -
    pub(super) fn new(connection_cell: &Arc<OriginCell>, owner: OwnedH1) -> Self {
        1032  +
    /// Creates a provisional owner for a sender extracted by admission matching.
        1033  +
    pub(super) fn new(connection_cell: &Arc<OriginCell>, owner: OwnedH1Sender) -> Self {
  967   1034   
        Self {
  968   1035   
            connection_cell: Weak::from_arc(connection_cell),
  969   1036   
            owner: Some(owner),
  970   1037   
        }
  971   1038   
    }
  972   1039   
  973   1040   
    /// Returns the selected connection identity without consuming the sender.
  974   1041   
    pub(in crate::client::pool) fn connection_id(&self) -> ConnectionId {
  975   1042   
        self.owner
  976   1043   
            .as_ref()
  977   1044   
            .expect("provisional HTTP/1 sender consumed more than once")
  978   1045   
            .id()
  979   1046   
    }
  980   1047   
  981         -
    /// Transfers the cell reference and sender into the next reuse transition.
  982         -
    pub(super) fn into_parts(mut self) -> (Weak<OriginCell>, OwnedH1) {
        1048  +
    /// Transfers the cell reference and sender into the next match transition.
        1049  +
    pub(super) fn into_parts(mut self) -> (Weak<OriginCell>, OwnedH1Sender) {
  983   1050   
        let owner = self
  984   1051   
            .owner
  985   1052   
            .take()
  986   1053   
            .expect("provisional HTTP/1 sender consumed more than once");
  987   1054   
        (self.connection_cell.clone(), owner)
  988   1055   
    }
  989   1056   
  990         -
    /// Restores provisional ownership when a reuse transition cannot commit.
  991         -
    pub(super) fn from_parts(connection_cell: Weak<OriginCell>, owner: OwnedH1) -> Self {
        1057  +
    /// Restores provisional ownership when a match transition cannot commit.
        1058  +
    pub(super) fn from_parts(connection_cell: Weak<OriginCell>, owner: OwnedH1Sender) -> Self {
  992   1059   
        Self {
  993   1060   
            connection_cell,
  994   1061   
            owner: Some(owner),
  995   1062   
        }
  996   1063   
    }
  997   1064   
}
  998   1065   
  999   1066   
impl fmt::Debug for ProvisionalH1 {
 1000   1067   
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 1001   1068   
        f.debug_struct("ProvisionalH1")
 1002   1069   
            .field(
 1003   1070   
                "connection-owning cell",
 1004   1071   
                &self
 1005   1072   
                    .connection_cell
 1006   1073   
                    .upgrade()
 1007   1074   
                    .map(|connection_cell| connection_cell.id().clone()),
 1008   1075   
            )
 1009         -
            .field("connection_id", &self.owner.as_ref().map(OwnedH1::id))
        1076  +
            .field("connection_id", &self.owner.as_ref().map(OwnedH1Sender::id))
 1010   1077   
            .finish()
 1011   1078   
    }
 1012   1079   
}
 1013   1080   
 1014   1081   
impl Drop for ProvisionalH1 {
 1015   1082   
    fn drop(&mut self) {
 1016   1083   
        if let Some(owner) = self.owner.take() {
 1017   1084   
            return_to_connection_cell(&self.connection_cell, owner);
 1018   1085   
        }
 1019   1086   
    }
 1020   1087   
}
 1021   1088   
 1022   1089   
/// Non-retaining authority to retire one installed H1 record.
 1023   1090   
#[derive(Clone, Debug)]
 1024   1091   
pub(in crate::client::pool) struct H1CloseHandle {
 1025   1092   
    /// Connection-owning cell used to remove dispatch eligibility before
 1026   1093   
    /// logical close.
 1027   1094   
    connection_cell: Weak<OriginCell>,
 1028   1095   
    /// Core fallback when the connection-owning cell no longer exists.
 1029   1096   
    connection: Weak<ConnectionState>,
 1030   1097   
    /// Generation identity rejected by stale close actions.
 1031         -
    id: ConnectionId,
        1098  +
    connection_id: ConnectionId,
 1032   1099   
}
 1033   1100   
 1034   1101   
impl H1CloseHandle {
 1035   1102   
    /// Creates a close handle for a newly installed H1 record.
 1036   1103   
    pub(in crate::client::pool) fn new(
 1037   1104   
        connection_cell: &Arc<OriginCell>,
 1038   1105   
        connection: &Arc<ConnectionState>,
 1039   1106   
    ) -> Self {
 1040   1107   
        Self {
 1041   1108   
            connection_cell: Weak::from_arc(connection_cell),
 1042   1109   
            connection: Weak::from_arc(connection),
 1043         -
            id: connection.id(),
        1110  +
            connection_id: connection.id(),
 1044   1111   
        }
 1045   1112   
    }
 1046   1113   
 1047   1114   
    /// Begins logical close and returns whether this signal won.
 1048   1115   
    pub(in crate::client::pool) fn close(&self, reason: CloseReason) -> bool {
 1049   1116   
        if let Some(connection_cell) = self.connection_cell.upgrade() {
 1050         -
            return OriginCell::close_h1(&connection_cell, self.id, reason);
        1117  +
            return OriginCell::close_h1(&connection_cell, self.connection_id, reason);
 1051   1118   
        }
 1052   1119   
        self.connection
 1053   1120   
            .upgrade()
 1054   1121   
            .is_some_and(|connection| connection.logical_close(reason))
 1055   1122   
    }
 1056   1123   
}
 1057   1124   
 1058   1125   
/// Driver-owned fallback that closes its H1 record on termination.
 1059   1126   
#[derive(Debug)]
 1060   1127   
pub(in crate::client::pool) struct H1DriverGuard {
 1061         -
    /// Non-retaining generation close authority.
        1128  +
    /// Non-retaining connection close authority.
 1062   1129   
    close: H1CloseHandle,
 1063   1130   
    /// Whether drop still represents owner-runtime shutdown.
 1064   1131   
    active: bool,
 1065   1132   
}
 1066   1133   
 1067   1134   
impl H1DriverGuard {
 1068   1135   
    /// Arms driver-lifecycle cleanup for an installed H1 record.
 1069   1136   
    pub(in crate::client::pool) fn new(close: H1CloseHandle) -> Self {
 1070   1137   
        Self {
 1071   1138   
            close,
 1072   1139   
            active: true,
 1073   1140   
        }
 1074   1141   
    }
 1075   1142   
 1076   1143   
    /// Records ordinary protocol-driver completion.
 1077   1144   
    pub(in crate::client::pool) fn protocol_closed(mut self) {
 1078   1145   
        self.active = false;
 1079   1146   
        self.close.close(CloseReason::ProtocolClosed);
 1080   1147   
    }
 1081   1148   
}
 1082   1149   
 1083   1150   
impl Drop for H1DriverGuard {
 1084   1151   
    fn drop(&mut self) {
 1085   1152   
        if self.active {
 1086   1153   
            self.close.close(CloseReason::OwnerRuntimeShutdown);
 1087   1154   
        }
 1088   1155   
    }
 1089   1156   
}
 1090   1157   
 1091   1158   
impl OriginCell {
 1092   1159   
    /// Installs a fresh H1 record selected by its launching request.
 1093   1160   
    ///
 1094   1161   
    /// # Panics
 1095   1162   
    ///
 1096   1163   
    /// Panics if this cell already contains the connection identity.
 1097         -
    pub(in crate::client::pool) fn install_selected_h1(
        1164  +
    pub(in crate::client::pool) fn insert_selected_h1(
 1098   1165   
        cell: &Arc<Self>,
 1099   1166   
        connection: Arc<ConnectionState>,
 1100   1167   
        sender: H1Sender,
 1101   1168   
    ) -> H1Selection {
 1102         -
        let (installed, availability) = {
        1169  +
        let (installed, supply_update) = {
 1103   1170   
            let mut state = cell.state.lock();
 1104         -
            let installed = state.h1.install_selected(connection, sender);
        1171  +
            let installed = state.h1.insert_selected(connection, sender);
 1105   1172   
            state.assert_consistent();
 1106         -
            (installed, state.take_h1_availability_update())
        1173  +
            (installed, state.take_h1_supply_update())
 1107   1174   
        };
 1108         -
        cell.publish_h1_availability(availability);
        1175  +
        cell.submit_h1_supply_update(supply_update);
 1109   1176   
        match installed {
 1110   1177   
            Ok(owner) => H1Selection::new(cell, owner),
 1111   1178   
            Err(owner) => {
 1112   1179   
                owner
 1113   1180   
                    .connection()
 1114   1181   
                    .logical_close(CloseReason::ProtocolClosed);
 1115   1182   
                drop(owner);
 1116   1183   
                panic!("duplicate HTTP/1 connection identity installed in one cell");
 1117   1184   
            }
 1118   1185   
        }
 1119   1186   
    }
 1120   1187   
 1121   1188   
    /// Installs an H1 record whose launching acquisition already completed.
 1122   1189   
    ///
 1123   1190   
    /// # Panics
 1124   1191   
    ///
 1125   1192   
    /// Panics if this cell already contains the connection identity.
 1126   1193   
    #[cfg(test)]
 1127         -
    pub(in crate::client::pool) fn install_idle_h1(
        1194  +
    pub(in crate::client::pool) fn insert_idle_h1(
 1128   1195   
        cell: &Arc<Self>,
 1129   1196   
        connection: Arc<ConnectionState>,
 1130   1197   
        sender: H1Sender,
 1131   1198   
    ) {
 1132   1199   
        let deadline = cell.idle_deadline();
 1133         -
        let (installed, availability) = {
        1200  +
        let (installed, supply_update) = {
 1134   1201   
            let mut state = cell.state.lock();
 1135         -
            let installed = state.h1.install_idle(connection, sender, deadline);
        1202  +
            let installed = state.h1.insert_idle(connection, sender, deadline);
 1136   1203   
            state.assert_consistent();
 1137         -
            (installed, state.take_h1_availability_update())
        1204  +
            (installed, state.take_h1_supply_update())
 1138   1205   
        };
 1139         -
        cell.publish_h1_availability(availability);
        1206  +
        cell.submit_h1_supply_update(supply_update);
 1140   1207   
        if let Err(owner) = installed {
 1141   1208   
            owner
 1142   1209   
                .connection()
 1143   1210   
                .logical_close(CloseReason::ProtocolClosed);
 1144   1211   
            drop(owner);
 1145   1212   
            panic!("duplicate HTTP/1 connection identity installed in one cell");
 1146   1213   
        }
 1147   1214   
        cell.notify_maintenance(deadline);
 1148   1215   
    }
 1149   1216   
 1150   1217   
    /// Selects the newest reusable H1 sender without origin-wide coordination.
 1151   1218   
    pub(in crate::client::pool) fn select_h1(cell: &Arc<Self>) -> Option<H1Selection> {
 1152         -
        let (owner, availability) = {
        1219  +
        let (owner, supply_update) = {
 1153   1220   
            let mut state = cell.state.lock();
 1154   1221   
            let owner = state.h1.select_idle();
 1155   1222   
            if owner.is_some() {
 1156         -
                state.reuse.consume_local_turn();
        1223  +
                state.h1.consume_local_turn();
 1157   1224   
            }
 1158   1225   
            state.assert_consistent();
 1159         -
            let availability = cell
        1226  +
            let supply_update = cell
 1160   1227   
                .admission
 1161   1228   
                .as_ref()
 1162         -
                .and_then(|_| state.take_h1_availability_update());
 1163         -
            (owner, availability)
        1229  +
                .and_then(|_| state.take_h1_supply_update());
        1230  +
            (owner, supply_update)
 1164   1231   
        };
 1165         -
        cell.publish_h1_availability(availability);
        1232  +
        cell.submit_h1_supply_update(supply_update);
 1166   1233   
        let owner = owner?;
 1167   1234   
        Some(H1Selection::new(cell, owner))
 1168   1235   
    }
 1169   1236   
 1170         -
    /// Reserves this cell's selected connection for one peer reuse operation.
 1171         -
    pub(in crate::client::pool) fn install_h1_reuse(
        1237  +
    /// Commits one admission-selected supplier reservation at this cell.
        1238  +
    pub(in crate::client::pool) fn commit_h1_reservation(
 1172   1239   
        cell: &Arc<Self>,
 1173         -
        origin: Arc<OriginAdmission>,
 1174         -
        prepared: PreparedReuseInstall,
        1240  +
        admission: Arc<OriginAdmission>,
        1241  +
        prepared: PreparedH1Reservation,
 1175   1242   
    ) -> Option<AdmissionAction> {
 1176   1243   
        let decision = {
 1177   1244   
            let mut state = cell.state.lock();
 1178         -
            state.install_reuse(prepared.id)
        1245  +
            state.commit_h1_reservation(prepared.match_id)
 1179   1246   
        };
 1180   1247   
 1181         -
        let result = match decision {
 1182         -
            ReuseInstall::Installed => ReuseInstallResult::Installed,
 1183         -
            ReuseInstall::Candidate(owner) => {
 1184         -
                let provisional = ProvisionalH1::new(cell, owner);
 1185         -
                ReuseInstallResult::Candidate(ReuseCandidate::new(
 1186         -
                    origin.clone(),
 1187         -
                    prepared.id,
 1188         -
                    cell.id.partition(),
 1189         -
                    provisional,
 1190         -
                ))
 1191         -
            }
 1192         -
            ReuseInstall::Rejected(availability) => ReuseInstallResult::Rejected(availability),
 1193         -
        };
 1194         -
        OriginAdmission::finish_h1_reuse_install(&origin, prepared.id, cell.id.partition(), result)
        1248  +
        let decision = decision.map_candidate(|owner| {
        1249  +
            let provisional = ProvisionalH1::new(cell, owner);
        1250  +
            H1Candidate::new(
        1251  +
                admission.clone(),
        1252  +
                prepared.match_id,
        1253  +
                cell.id.partition(),
        1254  +
                provisional,
        1255  +
            )
        1256  +
        });
        1257  +
        OriginAdmission::settle_h1_reservation(
        1258  +
            &admission,
        1259  +
            prepared.match_id,
        1260  +
            cell.id.partition(),
        1261  +
            decision,
        1262  +
        )
 1195   1263   
    }
 1196   1264   
 1197   1265   
    /// Clears an installed or resolving reservation after request cancellation.
 1198         -
    pub(in crate::client::pool) fn cancel_h1_reuse(
        1266  +
    pub(in crate::client::pool) fn cancel_h1_reservation(
 1199   1267   
        &self,
 1200         -
        reuse_id: ReuseId,
 1201         -
    ) -> H1AvailabilitySnapshot {
 1202         -
        self.state.lock().cancel_reuse(reuse_id)
        1268  +
        match_id: H1MatchId,
        1269  +
    ) -> SupplyRevision<H1SupplyStatus> {
        1270  +
        self.state.lock().cancel_h1_reservation(match_id)
 1203   1271   
    }
 1204   1272   
 1205   1273   
    /// Returns a rejected provisional sender through ordinary connection-owning cell handling.
 1206         -
    pub(in crate::client::pool) fn reject_h1_reuse_candidate(
        1274  +
    pub(in crate::client::pool) fn reject_h1_match(
 1207   1275   
        cell: &Arc<Self>,
 1208         -
        reuse_id: ReuseId,
        1276  +
        match_id: H1MatchId,
 1209   1277   
        provisional: ProvisionalH1,
 1210         -
    ) -> H1AvailabilitySnapshot {
        1278  +
    ) -> SupplyRevision<H1SupplyStatus> {
 1211   1279   
        {
 1212   1280   
            let mut state = cell.state.lock();
 1213         -
            state.reject_reuse_candidate(reuse_id);
        1281  +
            state.reject_h1_match(match_id);
 1214   1282   
        }
 1215   1283   
        drop(provisional);
 1216   1284   
        let mut state = cell.state.lock();
 1217   1285   
        state.assert_consistent();
 1218         -
        state.report_h1_availability()
        1286  +
        state.current_h1_supply_revision()
 1219   1287   
    }
 1220   1288   
 1221         -
    /// Revalidates a reuse operation and commits its provisional sender for dispatch.
 1222         -
    pub(in crate::client::pool) fn commit_h1_reuse(
        1289  +
    /// Revalidates one match and commits its provisional sender for dispatch.
        1290  +
    pub(in crate::client::pool) fn commit_h1_match(
 1223   1291   
        cell: &Arc<Self>,
 1224         -
        reuse_id: ReuseId,
        1292  +
        match_id: H1MatchId,
 1225   1293   
        provisional: ProvisionalH1,
 1226   1294   
    ) -> Result<H1Selection, ProvisionalH1> {
 1227   1295   
        let (connection_cell, owner) = provisional.into_parts();
 1228         -
        let committed = cell.state.lock().commit_reuse(reuse_id, &owner);
        1296  +
        let committed = cell.state.lock().commit_h1_match(match_id, &owner);
 1229   1297   
        if committed {
 1230   1298   
            Ok(H1Selection::new(cell, owner))
 1231   1299   
        } else {
 1232   1300   
            Err(ProvisionalH1::from_parts(connection_cell, owner))
 1233   1301   
        }
 1234   1302   
    }
 1235   1303   
 1236   1304   
    /// Closes a selected sender and records fairness only if close wins.
 1237         -
    pub(in crate::client::pool) fn reclaim_h1_reuse(
        1305  +
    pub(in crate::client::pool) fn reclaim_h1_candidate(
 1238   1306   
        cell: &Arc<Self>,
 1239         -
        reuse_id: ReuseId,
        1307  +
        match_id: H1MatchId,
 1240   1308   
        provisional: ProvisionalH1,
 1241         -
    ) -> Result<(H1AvailabilitySnapshot, bool), ProvisionalH1> {
        1309  +
    ) -> Result<(SupplyRevision<H1SupplyStatus>, bool), ProvisionalH1> {
 1242   1310   
        let (connection_cell, owner) = provisional.into_parts();
 1243         -
        if !cell.state.lock().reuse.names(reuse_id) {
        1311  +
        if !cell.state.lock().h1.names_peer_reservation(match_id) {
 1244   1312   
            return Err(ProvisionalH1::from_parts(connection_cell, owner));
 1245   1313   
        }
 1246   1314   
 1247   1315   
        let close_won = Self::retire_h1_owner(cell, owner, CloseReason::Reclaimed);
 1248         -
        let availability = {
        1316  +
        let supply_update = {
 1249   1317   
            let mut state = cell.state.lock();
 1250         -
            let availability = state.finish_reuse(reuse_id, close_won);
        1318  +
            let revision = state.complete_h1_match(match_id, close_won);
 1251   1319   
            state.assert_consistent();
 1252         -
            availability
        1320  +
            revision
 1253   1321   
        };
 1254         -
        Ok((availability, close_won))
        1322  +
        Ok((supply_update, close_won))
 1255   1323   
    }
 1256   1324   
 1257         -
    /// Completes local reuse state after a requesting cell accepts the sender.
 1258         -
    pub(in crate::client::pool) fn complete_h1_reuse(
        1325  +
    /// Completes local match state after a requesting cell accepts the sender.
        1326  +
    pub(in crate::client::pool) fn complete_h1_match(
 1259   1327   
        &self,
 1260         -
        reuse_id: ReuseId,
        1328  +
        match_id: H1MatchId,
 1261   1329   
        transferred: bool,
 1262         -
    ) -> H1AvailabilitySnapshot {
 1263         -
        self.state.lock().finish_reuse(reuse_id, transferred)
        1330  +
    ) -> SupplyRevision<H1SupplyStatus> {
        1331  +
        self.state.lock().complete_h1_match(match_id, transferred)
 1264   1332   
    }
 1265   1333   
 1266         -
    /// Publishes this cell's HTTP/1 availability when the origin is bounded.
 1267         -
    pub(super) fn publish_h1_availability(&self, availability: Option<H1AvailabilitySnapshot>) {
 1268         -
        if let (Some(admission), Some(availability)) = (&self.admission, availability) {
 1269         -
            OriginAdmission::update_h1_availability(
        1334  +
    /// Submits this cell's changed HTTP/1 supply when the origin is bounded.
        1335  +
    pub(super) fn submit_h1_supply_update(&self, revision: Option<SupplyRevision<H1SupplyStatus>>) {
        1336  +
        if let (Some(admission), Some(revision)) = (&self.admission, revision) {
        1337  +
            OriginAdmission::apply_h1_supply_revision(
 1270   1338   
                admission,
 1271   1339   
                self.id.partition(),
 1272   1340   
                self.eligibility_group.clone(),
 1273         -
                availability,
        1341  +
                revision,
 1274   1342   
            );
 1275   1343   
        }
 1276   1344   
    }
 1277   1345   
 1278   1346   
    /// Returns a reusable sender to the oldest compatible waiter or idle set.
 1279   1347   
    ///
 1280   1348   
    /// Demand publication, task wakeup, and any rejected-result fallback all
 1281   1349   
    /// run after the cell lock is released. `owner` remains outside the locked
 1282   1350   
    /// scope so sender or connection drop cannot run while the cell guard is
 1283   1351   
    /// live.
 1284         -
    fn return_h1_owner(cell: &Arc<Self>, owner: OwnedH1) {
        1352  +
    fn return_h1_owner(cell: &Arc<Self>, owner: OwnedH1Sender) {
 1285   1353   
        let connection_id = owner.id();
 1286   1354   
        let mut owner = Some(owner);
 1287   1355   
        let mut installation = None;
 1288   1356   
        let mut intercepted = None;
 1289         -
        let mut rejected_reuse = None;
        1357  +
        let mut rejected_match = None;
 1290   1358   
        let idle_deadline = cell.idle_deadline();
 1291   1359   
        let should_retire = {
 1292   1360   
            let mut state = cell.state.lock();
 1293   1361   
            let returnable = state
 1294   1362   
                .h1
 1295   1363   
                .accepts_return(owner.as_ref().expect("HTTP/1 owner disappeared"));
 1296   1364   
            if !returnable {
 1297         -
                if let Some(reuse_id) = state.reuse.intercept_return() {
 1298         -
                    let snapshot = state.finish_reuse(reuse_id, false);
 1299         -
                    rejected_reuse = Some((reuse_id, snapshot));
        1365  +
                if let Some(match_id) = state.h1.take_return_match() {
        1366  +
                    let revision = state.complete_h1_match(match_id, false);
        1367  +
                    rejected_match = Some((match_id, revision));
 1300   1368   
                } else {
 1301   1369   
                    state.assert_consistent();
 1302   1370   
                }
 1303   1371   
                true
 1304         -
            } else if let Some(reuse_id) = state.reuse.intercept_return() {
        1372  +
            } else if let Some(match_id) = state.h1.take_return_match() {
 1305   1373   
                if state
 1306   1374   
                    .h1
 1307         -
                    .reserve_for_reuse(owner.as_ref().expect("HTTP/1 owner disappeared"))
        1375  +
                    .reserve_for_peer(owner.as_ref().expect("HTTP/1 owner disappeared"))
 1308   1376   
                {
 1309   1377   
                    state.assert_consistent();
 1310   1378   
                    intercepted = Some((
 1311         -
                        reuse_id,
        1379  +
                        match_id,
 1312   1380   
                        ProvisionalH1::new(cell, owner.take().expect("HTTP/1 owner disappeared")),
 1313   1381   
                    ));
 1314   1382   
                    false
 1315   1383   
                } else {
 1316         -
                    let snapshot = state.finish_reuse(reuse_id, false);
 1317         -
                    rejected_reuse = Some((reuse_id, snapshot));
        1384  +
                    let revision = state.complete_h1_match(match_id, false);
        1385  +
                    rejected_match = Some((match_id, revision));
 1318   1386   
                    true
 1319   1387   
                }
 1320         -
            } else if state.waiters.can_accept_h1()
        1388  +
            } else if state.acquisitions.has_h1_compatible_waiter()
 1321   1389   
                && state
 1322   1390   
                    .h1
 1323   1391   
                    .commit_return_to_waiter(owner.as_ref().expect("HTTP/1 owner disappeared"))
 1324   1392   
            {
 1325         -
                state.reuse.consume_local_turn();
        1393  +
                state.h1.consume_local_turn();
 1326   1394   
                state.assert_consistent();
 1327   1395   
                let mut returned = owner.take().expect("HTTP/1 owner disappeared");
 1328   1396   
                returned.mark_reused();
 1329         -
                installation = Some(state.waiters.install_returned_h1(
 1330         -
                    || AcquisitionResult::H1(H1Selection::new(cell, returned)),
        1397  +
                let (waiter, mut install) = state.acquisitions.offer_returned_h1(
        1398  +
                    || AcquisitionOutcome::H1(H1Selection::new(cell, returned)),
 1331   1399   
                    &cell.eligibility_group,
 1332         -
                ));
 1333         -
                let install = installation
 1334         -
                    .as_mut()
 1335         -
                    .expect("HTTP/1 installation disappeared");
 1336         -
                if let Some(waiter) = install.waiter {
 1337         -
                    state.h2.cancel_pending_waiter(waiter);
 1338         -
                }
        1400  +
                );
        1401  +
                state.h2.cancel_pending_waiter(
        1402  +
                    waiter.expect("compatible HTTP/1 waiter disappeared during offer"),
        1403  +
                );
 1339   1404   
                install.demand_updates =
 1340   1405   
                    state.publishable_demand_updates(std::mem::take(&mut install.demand_updates));
        1406  +
                installation = Some(install);
 1341   1407   
                false
 1342   1408   
            } else {
 1343   1409   
                let returned = state.h1.return_idle(
 1344   1410   
                    owner.take().expect("HTTP/1 owner disappeared"),
 1345   1411   
                    idle_deadline,
 1346   1412   
                );
 1347   1413   
                match returned {
 1348   1414   
                    Ok(()) => {
 1349   1415   
                        state.assert_consistent();
 1350   1416   
                        false
 1351   1417   
                    }
 1352   1418   
                    Err(returned) => {
 1353   1419   
                        owner = Some(returned);
 1354   1420   
                        true
 1355   1421   
                    }
 1356   1422   
                }
 1357   1423   
            }
 1358   1424   
        };
 1359   1425   
 1360   1426   
        if should_retire {
 1361   1427   
            if tracing::level_enabled!(tracing::Level::TRACE) {
 1362   1428   
                trace_h1_return(cell, connection_id, H1ReturnTrace::Rejected);
 1363   1429   
            }
 1364         -
            if let Some((reuse_id, snapshot)) = rejected_reuse {
        1430  +
            if let Some((match_id, revision)) = rejected_match {
 1365   1431   
                let admission = cell
 1366   1432   
                    .admission
 1367   1433   
                    .as_ref()
 1368         -
                    .expect("an HTTP/1 reuse operation requires bounded admission");
 1369         -
                OriginAdmission::reject_returned_h1_reuse(
        1434  +
                    .expect("an H1 match requires bounded admission");
        1435  +
                OriginAdmission::reject_returned_h1_match(
 1370   1436   
                    admission,
 1371         -
                    reuse_id,
        1437  +
                    match_id,
 1372   1438   
                    cell.id.partition(),
 1373         -
                    snapshot,
        1439  +
                    revision,
 1374   1440   
                );
 1375   1441   
            }
 1376   1442   
            Self::retire_h1_owner(
 1377   1443   
                cell,
 1378   1444   
                owner.take().expect("retired HTTP/1 owner disappeared"),
 1379   1445   
                CloseReason::ProtocolClosed,
 1380   1446   
            );
 1381   1447   
            return;
 1382   1448   
        }
 1383   1449   
 1384         -
        if let Some((reuse_id, provisional)) = intercepted {
        1450  +
        if let Some((match_id, provisional)) = intercepted {
 1385   1451   
            if tracing::level_enabled!(tracing::Level::TRACE) {
 1386         -
                trace_h1_return(cell, connection_id, H1ReturnTrace::ReuseIntercepted);
        1452  +
                trace_h1_return(
        1453  +
                    cell,
        1454  +
                    connection_id,
        1455  +
                    H1ReturnTrace::PeerReservationIntercepted,
        1456  +
                );
 1387   1457   
            }
 1388   1458   
            let admission = cell
 1389   1459   
                .admission
 1390   1460   
                .as_ref()
 1391         -
                .expect("an HTTP/1 reuse operation requires bounded admission");
 1392         -
            let candidate = ReuseCandidate::new(
        1461  +
                .expect("an H1 match requires bounded admission");
        1462  +
            let candidate = H1Candidate::new(
 1393   1463   
                admission.clone(),
 1394         -
                reuse_id,
        1464  +
                match_id,
 1395   1465   
                cell.id.partition(),
 1396   1466   
                provisional,
 1397   1467   
            );
 1398         -
            let action = OriginAdmission::resolve_h1_reuse(admission, reuse_id, candidate);
 1399         -
            OriginAdmission::drive(action);
        1468  +
            let action = OriginAdmission::resolve_h1_match(admission, match_id, candidate);
        1469  +
            OriginAdmission::run_action_chain(action);
 1400   1470   
            return;
 1401   1471   
        }
 1402   1472   
 1403   1473   
        let Some(installation) = installation else {
 1404   1474   
            if tracing::level_enabled!(tracing::Level::TRACE) {
 1405   1475   
                trace_h1_return(cell, connection_id, H1ReturnTrace::Idle);
 1406   1476   
            }
 1407   1477   
            cell.notify_maintenance(idle_deadline);
 1408   1478   
            if cell.admission.is_some() {
 1409         -
                let availability = cell.state.lock().take_h1_availability_update();
 1410         -
                cell.publish_h1_availability(availability);
        1479  +
                let revision = cell.state.lock().take_h1_supply_update();
        1480  +
                cell.submit_h1_supply_update(revision);
 1411   1481   
            }
 1412   1482   
            return;
 1413   1483   
        };
 1414   1484   
        if tracing::level_enabled!(tracing::Level::TRACE) {
 1415   1485   
            trace_h1_return(cell, connection_id, H1ReturnTrace::LocalDemand);
 1416   1486   
        }
 1417   1487   
        if let Some(admission) = &cell.admission {
 1418   1488   
            for snapshot in installation.demand_updates.into_iter().flatten() {
 1419         -
                OriginAdmission::publish_demand(admission, cell.id.partition(), snapshot);
        1489  +
                OriginAdmission::submit_demand_snapshot(admission, cell.id.partition(), snapshot);
 1420   1490   
            }
 1421   1491   
        }
 1422         -
        drop(installation.returned_event);
        1492  +
        drop(installation.returned_step);
 1423   1493   
        if let Some(waker) = installation.waker {
 1424   1494   
            waker.wake();
 1425   1495   
        }
 1426   1496   
        if cell.admission.is_some() {
 1427         -
            let availability = cell.state.lock().take_h1_availability_update();
 1428         -
            cell.publish_h1_availability(availability);
        1497  +
            let revision = cell.state.lock().take_h1_supply_update();
        1498  +
            cell.submit_h1_supply_update(revision);
 1429   1499   
        }
 1430   1500   
    }
 1431   1501   
 1432   1502   
    /// Retires an externally owned H1 sender and removes its connection-owning cell record.
 1433   1503   
    ///
 1434   1504   
    /// Returns whether this path won the connection's logical-close race.
 1435         -
    fn retire_h1_owner(cell: &Arc<Self>, owner: OwnedH1, reason: CloseReason) -> bool {
        1505  +
    fn retire_h1_owner(cell: &Arc<Self>, owner: OwnedH1Sender, reason: CloseReason) -> bool {
 1436   1506   
        let should_close = {
 1437   1507   
            let mut state = cell.state.lock();
 1438   1508   
            let should_close = state.h1.close_owned(&owner);
 1439   1509   
            state.assert_consistent();
 1440   1510   
            should_close
 1441   1511   
        };
 1442   1512   
 1443   1513   
        let id = owner.id();
 1444   1514   
        let won = should_close && owner.connection().logical_close(reason);
 1445   1515   
        drop(owner);
 1446   1516   
 1447   1517   
        let mut state = cell.state.lock();
 1448         -
        state.h1.finish_close(id);
        1518  +
        state.h1.remove_closed(id);
 1449   1519   
        state.assert_consistent();
 1450         -
        let availability = state.take_h1_availability_update();
        1520  +
        let revision = state.take_h1_supply_update();
 1451   1521   
        drop(state);
 1452         -
        cell.publish_h1_availability(availability);
        1522  +
        cell.submit_h1_supply_update(revision);
 1453   1523   
        won
 1454   1524   
    }
 1455   1525   
 1456   1526   
    /// Begins close for an installed H1 record named without its sender.
 1457   1527   
    ///
 1458   1528   
    /// Returns whether this signal won the connection's logical-close race.
 1459   1529   
    pub(super) fn close_h1(cell: &Arc<Self>, id: ConnectionId, reason: CloseReason) -> bool {
 1460         -
        let Some((close, availability)) = ({
        1530  +
        let Some((close, supply_update)) = ({
 1461   1531   
            let mut state = cell.state.lock();
 1462   1532   
            let close = state.h1.begin_close(id);
 1463   1533   
            state.assert_consistent();
 1464         -
            close.map(|close| (close, state.take_h1_availability_update()))
        1534  +
            close.map(|close| (close, state.take_h1_supply_update()))
 1465   1535   
        }) else {
 1466   1536   
            return false;
 1467   1537   
        };
 1468   1538   
 1469         -
        cell.publish_h1_availability(availability);
        1539  +
        cell.submit_h1_supply_update(supply_update);
 1470   1540   
        let remove_record = close.sender.is_some();
 1471   1541   
        let won = close.connection.logical_close(reason);
 1472   1542   
        drop(close.sender);
 1473   1543   
        if remove_record {
 1474   1544   
            let mut state = cell.state.lock();
 1475         -
            state.h1.finish_close(id);
        1545  +
            state.h1.remove_closed(id);
 1476   1546   
            state.assert_consistent();
 1477   1547   
        }
 1478   1548   
        won
 1479   1549   
    }
 1480   1550   
 1481   1551   
    /// Returns H1 record counts for focused ownership tests.
 1482   1552   
    #[cfg(test)]
 1483   1553   
    pub(super) fn h1_counts(&self) -> (usize, usize) {
 1484   1554   
        self.state.lock().h1.counts()
 1485   1555   
    }
 1486   1556   
 1487   1557   
    /// Returns the sole installed HTTP/1 connection for focused dispatch tests.
 1488   1558   
    #[cfg(all(test, feature = "rt-tokio"))]
 1489   1559   
    pub(in crate::client::pool) fn only_h1_connection_for_test(&self) -> Arc<ConnectionState> {
 1490   1560   
        self.state.lock().h1.only_connection_for_test()
 1491   1561   
    }
 1492   1562   
}
 1493   1563   
 1494   1564   
/// Terminal outcome for one reusable HTTP/1 sender return.
 1495   1565   
enum H1ReturnTrace {
 1496   1566   
    /// The owning cell no longer accepts the sender.
 1497   1567   
    Rejected,
 1498   1568   
    /// Origin admission reserved the sender for peer demand.
 1499         -
    ReuseIntercepted,
        1569  +
    PeerReservationIntercepted,
 1500   1570   
    /// No compatible demand exists, so the sender became idle.
 1501   1571   
    Idle,
 1502   1572   
    /// A waiter in the owning cell accepted the sender.
 1503   1573   
    LocalDemand,
 1504   1574   
}
 1505   1575   
 1506   1576   
/// Emits a committed HTTP/1 return outcome outside the cell lock.
 1507   1577   
// Keep field formatting out of a return transition that can synchronously
 1508   1578   
// enter admission and sender fallbacks.
 1509   1579   
#[inline(never)]
 1510   1580   
fn trace_h1_return(cell: &OriginCell, connection_id: ConnectionId, outcome: H1ReturnTrace) {
 1511   1581   
    match outcome {
 1512   1582   
        H1ReturnTrace::Rejected => tracing::trace!(
 1513   1583   
            connection_id = %connection_id,
 1514   1584   
            connection_partition = ?cell.id.partition(),
 1515   1585   
            origin_scheme = %cell.id.origin().scheme(),
 1516   1586   
            origin_host = cell.id.origin().host(),
 1517   1587   
            origin_port = ?cell.id.origin().port(),
 1518   1588   
            "HTTP/1 return was rejected by its connection-owning cell"
 1519   1589   
        ),
 1520         -
        H1ReturnTrace::ReuseIntercepted => tracing::trace!(
        1590  +
        H1ReturnTrace::PeerReservationIntercepted => tracing::trace!(
 1521   1591   
            connection_id = %connection_id,
 1522   1592   
            connection_partition = ?cell.id.partition(),
 1523   1593   
            origin_scheme = %cell.id.origin().scheme(),
 1524   1594   
            origin_host = cell.id.origin().host(),
 1525   1595   
            origin_port = ?cell.id.origin().port(),
 1526         -
            "HTTP/1 return was intercepted by a cross-cell reuse operation"
        1596  +
            "HTTP/1 return was intercepted by a peer reservation"
 1527   1597   
        ),
 1528   1598   
        H1ReturnTrace::Idle => tracing::trace!(
 1529   1599   
            connection_id = %connection_id,
 1530   1600   
            connection_partition = ?cell.id.partition(),
 1531   1601   
            origin_scheme = %cell.id.origin().scheme(),
 1532   1602   
            origin_host = cell.id.origin().host(),
 1533   1603   
            origin_port = ?cell.id.origin().port(),
 1534   1604   
            "HTTP/1 connection returned to idle storage"
 1535   1605   
        ),
 1536   1606   
        H1ReturnTrace::LocalDemand => tracing::trace!(
 1537   1607   
            connection_id = %connection_id,
 1538   1608   
            request_partition = ?cell.id.partition(),
 1539   1609   
            connection_partition = ?cell.id.partition(),
 1540   1610   
            origin_scheme = %cell.id.origin().scheme(),
 1541   1611   
            origin_host = cell.id.origin().host(),
 1542   1612   
            origin_port = ?cell.id.origin().port(),
 1543   1613   
            "HTTP/1 return satisfied local demand"
 1544   1614   
        ),
 1545   1615   
    }
 1546   1616   
}
 1547   1617   
 1548   1618   
#[cfg(all(test, not(smithy_http_client_loom)))]
 1549   1619   
mod tests {
 1550   1620   
    use super::*;
 1551   1621   
    use crate::client::pool::connection::ConnectionInfo;
 1552   1622   
    use crate::client::pool::PartitionId;
 1553   1623   
 1554   1624   
    #[test]
 1555   1625   
    fn closing_a_selected_record_rejects_its_later_return() {
 1556   1626   
        let info = ConnectionInfo::for_test(ConnectionId::new(1), PartitionId::from_index(1));
 1557   1627   
        let (connection, _physical) = ConnectionState::unbounded(info);
 1558         -
        let mut records = H1Records::default();
        1628  +
        let mut records = H1CellState::default();
 1559   1629   
        let owner = records
 1560         -
            .install_selected(connection, H1Sender::test(11))
        1630  +
            .insert_selected(connection, H1Sender::test(11))
 1561   1631   
            .expect("fresh HTTP/1 record was rejected");
 1562   1632   
 1563   1633   
        assert!(records.accepts_return(&owner));
 1564   1634   
        assert!(records.begin_close(owner.id()).is_some());
 1565   1635   
        assert!(!records.accepts_return(&owner));
 1566   1636   
 1567   1637   
        drop(owner);
 1568         -
        records.finish_close(ConnectionId::new(1));
        1638  +
        records.remove_closed(ConnectionId::new(1));
 1569   1639   
    }
 1570   1640   
    #[test]
 1571         -
    fn reuse_reservation_distinguishes_install_resolution_and_completion() {
 1572         -
        let reuse_id = ReuseId::for_test(1);
 1573         -
        let mut slot = H1ReuseReservation::default();
 1574         -
 1575         -
        assert!(slot.install(reuse_id));
 1576         -
        assert!(!slot.install(ReuseId::for_test(2)));
 1577         -
        assert_eq!(Some(reuse_id), slot.intercept_return());
 1578         -
        assert!(slot.complete_transfer(reuse_id, true));
        1641  +
    fn peer_reservation_distinguishes_waiting_resolution_and_completion() {
        1642  +
        let match_id = H1MatchId::for_test(1);
        1643  +
        let mut slot = H1Reservation::default();
        1644  +
        1645  +
        assert!(slot.reserve_waiting(match_id));
        1646  +
        assert!(!slot.reserve_waiting(H1MatchId::for_test(2)));
        1647  +
        assert_eq!(Some(match_id), slot.take_return_match());
        1648  +
        assert!(slot.complete_transfer(match_id, true));
 1579   1649   
        assert!(slot.local_turn_owed());
 1580   1650   
        assert!(slot.is_available());
 1581   1651   
    }
 1582   1652   
 1583   1653   
    #[test]
 1584   1654   
    fn rejection_does_not_manufacture_a_fairness_turn() {
 1585         -
        let reuse_id = ReuseId::for_test(1);
 1586         -
        let mut slot = H1ReuseReservation::default();
        1655  +
        let match_id = H1MatchId::for_test(1);
        1656  +
        let mut slot = H1Reservation::default();
 1587   1657   
 1588         -
        assert!(slot.install_resolving(reuse_id));
 1589         -
        assert!(slot.reject(reuse_id));
        1658  +
        assert!(slot.reserve_resolving(match_id));
        1659  +
        assert!(slot.release(match_id));
 1590   1660   
        assert!(!slot.local_turn_owed());
 1591   1661   
    }
 1592   1662   
 1593   1663   
    #[test]
 1594   1664   
    fn owed_turn_blocks_only_while_local_h1_demand_can_use_it() {
 1595         -
        let reuse_id = ReuseId::for_test(1);
 1596         -
        let mut slot = H1ReuseReservation::default();
 1597         -
        assert!(slot.install_resolving(reuse_id));
 1598         -
        assert!(slot.complete_transfer(reuse_id, true));
        1665  +
        let match_id = H1MatchId::for_test(1);
        1666  +
        let mut slot = H1Reservation::default();
        1667  +
        assert!(slot.reserve_resolving(match_id));
        1668  +
        assert!(slot.complete_transfer(match_id, true));
 1599   1669   
 1600         -
        assert!(slot.blocks_peer_reuse(true));
 1601         -
        assert!(!slot.blocks_peer_reuse(false));
        1670  +
        assert!(slot.blocks_peer_selection(true));
        1671  +
        assert!(!slot.blocks_peer_selection(false));
 1602   1672   
        assert!(slot.clear_unused_turn(false));
 1603         -
        assert!(!slot.blocks_peer_reuse(true));
        1673  +
        assert!(!slot.blocks_peer_selection(true));
 1604   1674   
    }
 1605   1675   
}

tmp-codegen-diff/aws-sdk/sdk/aws-smithy-http-client/src/client/pool/cell/h2.rs

@@ -1,1 +110,131 @@
    1      1   
/*
    2      2   
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
    3      3   
 * SPDX-License-Identifier: Apache-2.0
    4      4   
 */
    5      5   
    6         -
//! HTTP/2 flight, generation, route, and request-lease ownership.
           6  +
//! HTTP/2 connection generations, routes, and request activation.
    7      7   
//!
    8         -
//! One connection-owning cell stores the authoritative HTTP/2 generation.
    9         -
//! Other cells store only [`H2Route`] identities and revalidate them at the
   10         -
//! connection-owning cell before dispatch. A generation may stop accepting
   11         -
//! new requests while prospective and accepted requests still drain.
           8  +
//! HTTP/2 carries many concurrent request and response exchanges as streams on
           9  +
//! one connection. The pool calls one installed lifetime of that connection a
          10  +
//! generation. The cell that established the connection owns its generation,
          11  +
//! including the Hyper request handle, driver, transport, and capacity.
   12     12   
//!
   13         -
//! [`H2Records`] is the invariant owner under the cell lock. It stores one
   14         -
//! post-ALPN flight, the accepting and draining generations, a local generation
   15         -
//! gate, and at most one peer route. A route carries only connection partition
   16         -
//! and generation identity. [`H2Activation`] is the unlocked value that carries
   17         -
//! a prospective lease and transient sender from selection to Hyper acceptance.
          13  +
//! A flight is the post-ALPN handshake work shared by concurrent requests
          14  +
//! waiting for that generation. A successful flight installs one accepting
          15  +
//! generation; failed or superseded flight work installs nothing.
   18     16   
//!
   19         -
//! ```text
   20         -
//! requesting cell                        connection-owning cell
   21         -
//! peer route + local gate --identity---> exact accepting generation
   22         -
//!                                      |-- increment prospective count
   23         -
//!                                      `-- clone transient sender
   24         -
//!                                               |
   25         -
//!                                               `--> H2Activation
   26         -
//! ```
          17  +
//! An accepting generation may issue new request streams. A draining
          18  +
//! generation issues no new streams but remains recorded while selected or
          19  +
//! accepted requests still own it.
          20  +
//!
          21  +
//! A requesting cell may store an [`H2Route`] to a peer cell's generation.
          22  +
//! The route carries only cell and generation identity. [`H2Activation`]
          23  +
//! revalidates that identity under the connection-cell lock, reserves one
          24  +
//! prospective stream, and carries a transient sender clone to Hyper.
          25  +
//!
          26  +
//! Each Hyper-accepted request owns one claim with independent upload and
          27  +
//! response guards. The generation releases the request only after both sides
          28  +
//! finish, including drop, error, and bodyless completion paths. The detached
          29  +
//! request lifecycle lives in [`request`]; this module retains the cell-locked
          30  +
//! generation, route, and activation-gate state.
          31  +
//!
          32  +
//! [`H2CellState`] is the invariant owner under the cell lock. It stores one
          33  +
//! flight, accepting and draining generations, activation priority, and one
          34  +
//! peer route. A peer route references another cell's generation; it does not
          35  +
//! transfer connection ownership.
   27     36   
//!
   28     37   
//! ```text
   29     38   
//! no flight -- post-ALPN owner task ----------------------> Flight
   30     39   
//! Flight -- successful handshake and install ------------> Accepting
   31     40   
//! Flight -- failure, stale completion, or task drop ------> no flight
   32         -
//! Accepting -- close with retained request leases --------> Draining
   33         -
//! Accepting -- close without retained request leases -----> removed
   34         -
//! Draining -- last prospective or accepted lease --------> removed
          41  +
//! Accepting -- close with retained request claims --------> Draining
          42  +
//! Accepting -- close without retained request claims -----> removed
          43  +
//! Draining -- last prospective or accepted claim --------> removed
   35     44   
//! ```
   36     45   
//!
   37     46   
//! ```text
   38         -
//! no peer route -- publication ---------------------------> PeerRoute(generation)
   39         -
//! PeerRoute(A) -- replacement publication ---------------> PeerRoute(B)
   40         -
//! PeerRoute -- stale activation or local generation -----> no peer route
          47  +
//! requesting cell                        connection-owning cell
          48  +
//! peer route + local gate --identity---> exact accepting generation
          49  +
//!                                      |-- reserve one prospective stream
          50  +
//!                                      `-- clone transient sender
          51  +
//!                                               |
          52  +
//!                                               `--> H2Activation
   41     53   
//! ```
   42     54   
//!
   43     55   
//! Generation installation makes the sender visible before the owner task
   44         -
//! submits the Hyper driver. An activation in that interval is accepted by
   45         -
//! Hyper's dispatch channel and remains pending until the driver is polled.
          56  +
//! submits the Hyper driver. An activation accepted in that interval remains
          57  +
//! pending in Hyper's dispatch channel until the driver is polled.
   46     58   
//!
   47         -
//! Activation reserves a prospective lease before a sender clone leaves the
   48         -
//! cell lock. Hyper acceptance converts that reservation to one accepted
   49         -
//! lease. The accepted lease is released only after both request-send and
   50         -
//! response-receive endpoints terminate.
   51         -
   52         -
use super::super::connection::{ConnectionInfo, ConnectionState, DispatchGuard};
   53         -
use super::super::partition::PartitionId;
   54         -
use super::waiters::{AcquisitionQueue, WaiterInstall};
   55         -
use super::{AcquisitionEvent, AcquisitionResult, CellState, OriginCell, WaiterId};
   56         -
use crate::sync::{Arc, Mutex, Weak};
          59  +
//! Activation reserves its prospective claim before the sender clone leaves
          60  +
//! the cell lock. Hyper acceptance converts that reservation to an accepted
          61  +
//! request claim with independent upload and response guards.
          62  +
          63  +
mod request;
          64  +
          65  +
pub(in crate::client::pool) use request::{
          66  +
    H2Activation, H2DispatchParts, H2ResponseGuard, H2UploadGuard,
          67  +
};
          68  +
use request::{H2ActivationResources, H2ActivationTurnGuard};
          69  +
          70  +
use super::super::admission::{
          71  +
    DemandId, DemandSnapshot, H2SupplyStatus, OriginAdmission, SupplyRevision,
          72  +
};
          73  +
use super::super::connection::{CloseReason, ConnectionState};
          74  +
use super::super::partition::{EligibilityGroup, PartitionId};
          75  +
use super::waiters::{AcquisitionQueue, WaiterResolution};
          76  +
use super::{AcquisitionOutcome, AcquisitionStep, CellState, OriginCell, WaiterId};
          77  +
use crate::sync::{Arc, Weak};
   57     78   
use aws_smithy_runtime_api::client::connection::ConnectionId;
   58     79   
use aws_smithy_types::body::SdkBody;
   59     80   
use std::collections::{BTreeSet, HashMap};
   60     81   
use std::time::SystemTime;
   61     82   
   62     83   
/// Identity of one post-ALPN HTTP/2 establishment flight.
   63     84   
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
   64     85   
pub(in crate::client::pool) struct H2FlightId(u64);
   65     86   
   66     87   
/// Identity of one installed HTTP/2 generation within a cell.
   67     88   
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
   68     89   
pub(in crate::client::pool) struct H2GenerationId(u64);
   69     90   
   70     91   
#[cfg(test)]
   71     92   
impl H2GenerationId {
   72     93   
    /// Creates a generation identity for admission-index tests.
   73     94   
    pub(in crate::client::pool) fn for_test(value: u64) -> Self {
   74     95   
        Self(value)
   75     96   
    }
   76     97   
}
   77     98   
   78     99   
/// Cloneable Hyper request sender retained only by its owning generation.
   79    100   
///
   80         -
/// The test-only variant exercises generation and lease transitions without a
         101  +
/// The test-only variant exercises generation and claim transitions without a
   81    102   
/// parallel fake generation implementation.
   82    103   
#[derive(Clone)]
   83    104   
pub(in crate::client::pool) enum H2Sender {
   84    105   
    /// Sender returned by a successful Hyper HTTP/2 handshake.
   85    106   
    Hyper(hyper::client::conn::http2::SendRequest<SdkBody>),
   86    107   
    /// Synthetic sender identity used by state-machine tests.
   87    108   
    #[cfg(test)]
   88    109   
    Test(u64),
   89    110   
}
   90    111   
@@ -147,168 +3495,3014 @@
  167    188   
                generation,
  168    189   
            },
  169    190   
        }
  170    191   
    }
  171    192   
  172    193   
    /// Returns the exact connection-owning generation named by this route.
  173    194   
    fn id(&self) -> H2RouteId {
  174    195   
        self.id
  175    196   
    }
  176    197   
  177         -
    /// Returns the partition that owns the advertised generation.
         198  +
    /// Returns the partition that owns the routed generation.
  178    199   
    pub(super) fn connection_partition(&self) -> PartitionId {
  179    200   
        self.id.connection_partition
  180    201   
    }
  181    202   
  182         -
    /// Returns the advertised generation identity.
         203  +
    /// Returns the routed generation identity.
  183    204   
    #[cfg(any(debug_assertions, test))]
  184    205   
    pub(in crate::client::pool) fn generation(&self) -> H2GenerationId {
  185    206   
        self.id.generation
  186    207   
    }
  187    208   
  188         -
    /// Attempts to reserve a prospective request lease for one requesting partition.
         209  +
    /// Attempts to reserve a prospective request claim for one requesting partition.
  189    210   
    pub(super) fn activate(&self, request_partition: PartitionId) -> Option<H2Activation> {
  190    211   
        let cell = self.connection_cell.upgrade()?;
  191         -
        let mut activation = OriginCell::activate_h2(&cell, self.id.generation)?;
  192         -
        activation.request_partition = request_partition;
  193         -
        Some(activation)
         212  +
        OriginCell::activate_h2(&cell, self.id.generation, request_partition)
  194    213   
    }
  195    214   
}
  196    215   
  197         -
/// Cell-local ownership of one HTTP/2 flight and installed generations.
         216  +
/// Complete HTTP/2 acquisition state owned by one origin cell.
  198    217   
///
  199    218   
/// At every completed transition:
  200    219   
///
  201    220   
/// - at most one flight and one accepting generation exist;
  202    221   
/// - the accepting identity names an `Accepting` record;
  203    222   
/// - every other generation is `Draining`;
  204         -
/// - prospective and accepted request counts are checked and non-wrapping;
         223  +
/// - prospective and active request counts are checked and non-wrapping;
  205    224   
/// - a draining record remains until both counts reach zero; and
  206         -
/// - each flight participant identity appears at most once.
         225  +
/// - each flight participant identity appears at most once;
         226  +
/// - the local activation gate names the accepting generation;
         227  +
/// - a peer route gate names the routed generation; and
         228  +
/// - at most one peer-route waiter is crossing the connection-cell lock.
  207    229   
#[derive(Debug, Default)]
  208         -
pub(super) struct H2Records {
         230  +
pub(super) struct H2CellState {
  209    231   
    /// Post-ALPN convergence in progress for this cell.
  210    232   
    flight: Option<H2Flight>,
  211    233   
    /// Installed accepting and draining generations by exact identity.
  212    234   
    generations: HashMap<H2GenerationId, H2Generation>,
  213    235   
    /// Sole generation permitted to issue new activations.
  214         -
    accepting: Option<H2GenerationId>,
         236  +
    accepting_generation: Option<H2GenerationId>,
  215    237   
    /// Identity-only route to one peer cell's accepting generation.
  216    238   
    peer_route: Option<PeerH2Route>,
  217    239   
    /// Local activation order for the accepting generation.
  218         -
    gate: GenerationGate,
         240  +
    local_activation_gate: H2ActivationGate,
  219    241   
    /// Next cell-local flight identity.
  220         -
    next_flight: u64,
         242  +
    next_flight_id: u64,
  221    243   
    /// Next cell-local generation identity.
  222         -
    next_generation: u64,
         244  +
    next_generation_id: u64,
  223    245   
}
  224    246   
  225    247   
/// One post-ALPN flight and the waiters awaiting its result.
  226    248   
#[derive(Debug)]
  227    249   
struct H2Flight {
  228    250   
    /// Identity checked by the owner task at completion.
  229    251   
    id: H2FlightId,
  230    252   
    /// Waiters that receive the shared handshake result.
  231    253   
    participants: BTreeSet<WaiterId>,
  232    254   
}
  233    255   
  234    256   
/// One installed multiplexed connection generation.
  235    257   
#[derive(Debug)]
  236    258   
struct H2Generation {
  237    259   
    /// Establishment waiters transferred to this generation but not yet served.
  238    260   
    pending_waiters: BTreeSet<WaiterId>,
  239    261   
    /// Protocol-neutral connection and capacity owner.
  240    262   
    connection: Arc<ConnectionState>,
  241    263   
    /// Authoritative Hyper sender cloned only after generation validation.
  242    264   
    sender: H2Sender,
  243    265   
    /// Whether new activations may be issued.
  244         -
    residence: H2Residence,
         266  +
    state: H2GenerationState,
  245    267   
    /// Activations selected but not yet accepted by Hyper.
  246         -
    prospective: usize,
         268  +
    prospective_requests: usize,
  247    269   
    /// Whether Hyper has accepted a request on this generation.
  248         -
    has_dispatched: bool,
  249         -
    /// Accepted requests whose two lease endpoints have not both terminated.
  250         -
    accepted: usize,
         270  +
    has_accepted_request: bool,
         271  +
    /// Requests accepted by Hyper whose upload and response sides have not both finished.
         272  +
    active_requests: usize,
  251    273   
    /// Expiration deadline while the generation remains accepting.
  252    274   
    idle_deadline: Option<SystemTime>,
  253    275   
}
  254    276   
  255    277   
/// Whether an installed generation may accept new requests.
  256    278   
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
  257         -
enum H2Residence {
  258         -
    /// New requests may reserve prospective leases.
         279  +
enum H2GenerationState {
         280  +
    /// New requests may reserve prospective claims.
  259    281   
    Accepting,
  260         -
    /// No new requests are admitted; retained leases may finish.
         282  +
    /// No new requests are admitted; retained claims may finish.
  261    283   
    Draining,
  262    284   
}
  263    285   
  264    286   
/// Peer route visibility and requesting-cell activation order.
  265    287   
#[derive(Debug)]
  266    288   
struct PeerH2Route {
  267    289   
    /// Exact connection-cell generation visible to this requesting cell.
  268    290   
    route: H2Route,
  269    291   
    /// Local waiter priority for uses of this route.
  270         -
    gate: GenerationGate,
         292  +
    activation_gate: H2ActivationGate,
  271    293   
    /// Waiter whose route activation is crossing the connection-cell lock.
  272         -
    crossing: Option<WaiterId>,
         294  +
    crossing_waiter: Option<WaiterId>,
  273    295   
}
  274    296   
  275    297   
/// Local priority state for one accepting generation.
  276    298   
#[derive(Debug, Default)]
  277         -
enum GenerationGate {
         299  +
enum H2ActivationGate {
  278    300   
    /// No accepting generation is visible.
  279    301   
    #[default]
  280    302   
    Closed,
  281    303   
    /// Waiters committed through `cutoff` precede later arrivals.
  282    304   
    Prioritizing {
  283    305   
        /// Exact generation governed by this gate.
  284    306   
        generation: H2GenerationId,
  285    307   
        /// Newest waiter committed before generation visibility.
  286    308   
        cutoff: WaiterId,
  287    309   
        /// Prioritized waiter whose activation has not accepted or cancelled.
  288         -
        activating: Option<WaiterId>,
         310  +
        active_turn: Option<WaiterId>,
  289    311   
    },
  290         -
    /// The publication cutoff drained; queued work still precedes direct arrivals.
         312  +
    /// The peer-route cutoff drained; queued work still precedes direct arrivals.
  291    313   
    Open { generation: H2GenerationId },
  292    314   
}
  293    315   
  294    316   
/// One activation opportunity returned by a generation gate.
  295    317   
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
  296         -
enum GateTurn {
         318  +
enum H2ActivationTurn {
  297    319   
    /// The gate cannot issue another activation.
  298    320   
    Unavailable,
  299    321   
    /// Any oldest compatible acquisition may activate.
  300    322   
    Open,
  301    323   
    /// Only an acquisition at or before this waiter may activate.
  302    324   
    Through(WaiterId),
  303    325   
}
  304    326   
  305         -
impl GenerationGate {
         327  +
impl H2ActivationGate {
  306    328   
    /// Creates a gate over every waiter committed through `cutoff`.
  307    329   
    fn for_generation(generation: H2GenerationId, cutoff: Option<WaiterId>) -> Self {
  308    330   
        match cutoff {
  309    331   
            Some(cutoff) => Self::Prioritizing {
  310    332   
                generation,
  311    333   
                cutoff,
  312         -
                activating: None,
         334  +
                active_turn: None,
  313    335   
            },
  314    336   
            None => Self::Open { generation },
  315    337   
        }
  316    338   
    }
  317    339   
  318    340   
    fn generation(&self) -> Option<H2GenerationId> {
  319    341   
        match self {
  320    342   
            Self::Closed => None,
  321    343   
            Self::Prioritizing { generation, .. } | Self::Open { generation } => Some(*generation),
  322    344   
        }
  323    345   
    }
  324    346   
  325    347   
    /// Returns the next activation opportunity and opens a drained priority gate.
  326         -
    fn next_turn(&mut self, has_prioritized: bool) -> GateTurn {
         348  +
    fn take_next_turn(&mut self, has_prioritized: bool) -> H2ActivationTurn {
  327    349   
        match self {
  328         -
            Self::Closed => GateTurn::Unavailable,
         350  +
            Self::Closed => H2ActivationTurn::Unavailable,
  329    351   
            Self::Prioritizing {
  330    352   
                cutoff,
  331         -
                activating,
         353  +
                active_turn,
  332    354   
                generation,
  333    355   
            } => {
  334         -
                if activating.is_some() {
  335         -
                    return GateTurn::Unavailable;
         356  +
                if active_turn.is_some() {
         357  +
                    return H2ActivationTurn::Unavailable;
  336    358   
                }
  337    359   
                if !has_prioritized {
  338    360   
                    let generation = *generation;
  339    361   
                    *self = Self::Open { generation };
  340         -
                    GateTurn::Open
         362  +
                    H2ActivationTurn::Open
  341    363   
                } else {
  342         -
                    GateTurn::Through(*cutoff)
         364  +
                    H2ActivationTurn::Through(*cutoff)
  343    365   
                }
  344    366   
            }
  345         -
            Self::Open { .. } => GateTurn::Open,
         367  +
            Self::Open { .. } => H2ActivationTurn::Open,
  346    368   
        }
  347    369   
    }
  348    370   
  349    371   
    /// Records a prioritized activation until it accepts or cancels.
  350         -
    fn begin_gate_activation(&mut self, waiter: WaiterId) -> bool {
  351         -
        let activating = match self {
         372  +
    fn reserve_turn(&mut self, waiter: WaiterId) -> bool {
         373  +
        let active_turn = match self {
  352    374   
            Self::Closed => {
  353    375   
                unreachable!("started an HTTP/2 activation while its gate was closed")
  354    376   
            }
  355         -
            Self::Prioritizing { activating, .. } => activating,
         377  +
            Self::Prioritizing { active_turn, .. } => active_turn,
  356    378   
            Self::Open { .. } => return false,
  357    379   
        };
  358    380   
        assert!(
  359         -
            activating.replace(waiter).is_none(),
         381  +
            active_turn.replace(waiter).is_none(),
  360    382   
            "HTTP/2 generation gate admitted two activation opportunities"
  361    383   
        );
  362    384   
        true
  363    385   
    }
  364    386   
  365    387   
    /// Discharges one exact activation opportunity.
  366         -
    fn finish_gate_activation(&mut self, generation: H2GenerationId, waiter: WaiterId) -> bool {
         388  +
    fn release_turn(&mut self, generation: H2GenerationId, waiter: WaiterId) -> bool {
  367    389   
        if self.generation() != Some(generation) {
  368    390   
            return false;
  369    391   
        }
  370         -
        let activating = match self {
         392  +
        let active_turn = match self {
  371    393   
            Self::Closed => return false,
  372         -
            Self::Prioritizing { activating, .. } => activating,
         394  +
            Self::Prioritizing { active_turn, .. } => active_turn,
  373    395   
            Self::Open { .. } => return false,
  374    396   
        };
  375         -
        if *activating != Some(waiter) {
         397  +
        if *active_turn != Some(waiter) {
  376    398   
            return false;
  377    399   
        }
  378         -
        *activating = None;
         400  +
        *active_turn = None;
  379    401   
        true
  380    402   
    }
  381    403   
  382    404   
    fn priority_cutoff(&self) -> Option<WaiterId> {
  383    405   
        match self {
  384    406   
            Self::Prioritizing { cutoff, .. } => Some(*cutoff),
  385    407   
            Self::Closed | Self::Open { .. } => None,
  386    408   
        }
  387    409   
    }
  388    410   
  389         -
    fn activating(&self) -> Option<WaiterId> {
         411  +
    fn active_turn(&self) -> Option<WaiterId> {
  390    412   
        match self {
  391         -
            Self::Prioritizing { activating, .. } => *activating,
         413  +
            Self::Prioritizing { active_turn, .. } => *active_turn,
  392    414   
            Self::Closed | Self::Open { .. } => None,
  393    415   
        }
  394    416   
    }
  395    417   
  396    418   
    fn is_open_for(&self, generation: H2GenerationId) -> bool {
  397    419   
        matches!(self, Self::Open { generation: current } if *current == generation)
  398    420   
    }
  399    421   
}
  400    422   
  401    423   
/// Result of atomically converging one post-ALPN attempt.
  402    424   
#[derive(Debug)]
  403         -
pub(in crate::client::pool) enum H2FlightInstall {
         425  +
pub(in crate::client::pool) enum H2FlightDecision {
  404    426   
    /// An installed generation can serve this waiter.
  405         -
    Accepting(H2GenerationId),
         427  +
    UseGeneration(H2GenerationId),
  406    428   
    /// The waiter joined the current flight as a result participant.
  407         -
    Joined,
         429  +
    JoinedFlight,
  408    430   
    /// The caller owns the task that must drive this new flight.
  409         -
    Driver(H2FlightId),
         431  +
    RunFlight(H2FlightId),
  410    432   
}
  411    433   
  412    434   
/// Result of joining an accepting generation after ALPN selected HTTP/2.
  413    435   
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
  414         -
pub(in crate::client::pool) enum H2GenerationJoin {
         436  +
pub(in crate::client::pool) enum H2GenerationJoinOutcome {
  415    437   
    /// The waiter was transferred to the named generation.
  416    438   
    Joined,
  417    439   
    /// The named generation stopped accepting before the transfer.
  418    440   
    GenerationChanged,
  419    441   
    /// Another transition already installed the waiter's result.
  420         -
    WaiterCompleted,
         442  +
    WaiterResolved,
  421    443   
}
  422    444   
  423         -
impl H2Records {
  424         -
    /// Installs or joins the one current flight after ALPN selected HTTP/2.
  425         -
    pub(super) fn install_or_join_flight(&mut self, waiter: WaiterId) -> H2FlightInstall {
  426         -
        if let Some(generation) = self.accepting {
  427         -
            return H2FlightInstall::Accepting(generation);
         445  +
impl H2CellState {
         446  +
    /// Uses an accepting generation, joins its flight, or starts a new flight.
         447  +
    pub(super) fn converge_flight(&mut self, waiter: WaiterId) -> H2FlightDecision {
         448  +
        if let Some(generation) = self.accepting_generation {
         449  +
            return H2FlightDecision::UseGeneration(generation);
  428    450   
        }
  429    451   
        if let Some(flight) = &mut self.flight {
  430    452   
            assert!(
  431    453   
                flight.participants.insert(waiter),
  432    454   
                "HTTP/2 waiter joined one flight more than once"
  433    455   
            );
  434    456   
            self.assert_consistent();
  435         -
            return H2FlightInstall::Joined;
         457  +
            return H2FlightDecision::JoinedFlight;
  436    458   
        }
  437    459   
  438    460   
        let id = self.take_flight_id();
  439    461   
        self.flight = Some(H2Flight {
  440    462   
            id,
  441    463   
            participants: BTreeSet::from([waiter]),
  442    464   
        });
  443    465   
        self.assert_consistent();
  444         -
        H2FlightInstall::Driver(id)
         466  +
        H2FlightDecision::RunFlight(id)
  445    467   
    }
  446    468   
  447    469   
    /// Removes one cancelled waiter from its current flight, when present.
  448    470   
    pub(super) fn cancel_flight_participant(&mut self, waiter: WaiterId) {
  449    471   
        if let Some(flight) = &mut self.flight {
  450    472   
            flight.participants.remove(&waiter);
  451    473   
        }
  452    474   
        self.assert_consistent();
  453    475   
    }
  454    476   
  455    477   
    /// Removes a waiter that no longer awaits an accepting generation.
  456    478   
    pub(super) fn cancel_pending_waiter(&mut self, waiter: WaiterId) {
  457         -
        if let Some(generation) = self.accepting {
         479  +
        if let Some(generation) = self.accepting_generation {
  458    480   
            self.generations
  459    481   
                .get_mut(&generation)
  460    482   
                .expect("accepting HTTP/2 generation disappeared")
  461    483   
                .pending_waiters
  462    484   
                .remove(&waiter);
  463    485   
        }
  464    486   
        self.assert_consistent();
  465    487   
    }
  466    488   
  467    489   
    /// Installs the generation produced by the named flight.
  468    490   
    pub(in crate::client::pool) fn complete_flight(
  469    491   
        &mut self,
  470    492   
        flight: H2FlightId,
  471    493   
        connection: Arc<ConnectionState>,
  472    494   
        sender: H2Sender,
  473    495   
        idle_deadline: Option<SystemTime>,
  474    496   
    ) -> Result<H2GenerationId, (Arc<ConnectionState>, H2Sender)> {
  475    497   
        if self.flight.as_ref().map(|current| current.id) != Some(flight)
  476         -
            || self.accepting.is_some()
         498  +
            || self.accepting_generation.is_some()
  477    499   
        {
  478    500   
            return Err((connection, sender));
  479    501   
        }
  480    502   
        let flight = self
  481    503   
            .flight
  482    504   
            .take()
  483    505   
            .expect("validated HTTP/2 flight disappeared");
  484    506   
        let generation =
  485         -
            self.install_generation(flight.participants, connection, sender, idle_deadline);
         507  +
            self.open_generation(flight.participants, connection, sender, idle_deadline);
  486    508   
        self.assert_consistent();
  487    509   
        Ok(generation)
  488    510   
    }
  489    511   
  490    512   
    /// Installs one accepting generation with its transferred waiters.
  491         -
    fn install_generation(
         513  +
    fn open_generation(
  492    514   
        &mut self,
  493    515   
        pending_waiters: BTreeSet<WaiterId>,
  494    516   
        connection: Arc<ConnectionState>,
  495    517   
        sender: H2Sender,
  496    518   
        idle_deadline: Option<SystemTime>,
  497    519   
    ) -> H2GenerationId {
  498    520   
        let generation = self.take_generation_id();
  499    521   
        let replaced = self.generations.insert(
  500    522   
            generation,
  501    523   
            H2Generation {
  502    524   
                pending_waiters,
  503    525   
                connection,
  504    526   
                sender,
  505         -
                residence: H2Residence::Accepting,
  506         -
                prospective: 0,
  507         -
                has_dispatched: false,
  508         -
                accepted: 0,
         527  +
                state: H2GenerationState::Accepting,
         528  +
                prospective_requests: 0,
         529  +
                has_accepted_request: false,
         530  +
                active_requests: 0,
  509    531   
                idle_deadline,
  510    532   
            },
  511    533   
        );
  512    534   
        assert!(replaced.is_none(), "HTTP/2 generation identity was reused");
  513         -
        self.accepting = Some(generation);
         535  +
        self.accepting_generation = Some(generation);
  514    536   
        self.peer_route = None;
  515         -
        debug_assert!(matches!(self.gate, GenerationGate::Closed));
  516         -
        self.gate = GenerationGate::Open { generation };
         537  +
        debug_assert!(matches!(
         538  +
            self.local_activation_gate,
         539  +
            H2ActivationGate::Closed
         540  +
        ));
         541  +
        self.local_activation_gate = H2ActivationGate::Open { generation };
  517    542   
        generation
  518    543   
    }
  519    544   
  520    545   
    /// Retires a failed or dropped flight and returns its participants.
  521    546   
    pub(super) fn fail_flight(&mut self, flight: H2FlightId) -> Option<Vec<WaiterId>> {
  522    547   
        if self.flight.as_ref().map(|current| current.id) != Some(flight) {
  523    548   
            return None;
  524    549   
        }
  525    550   
        let participants = self
  526    551   
            .flight
  527    552   
            .take()
  528    553   
            .expect("validated HTTP/2 flight disappeared")
  529    554   
            .participants
  530    555   
            .into_iter()
  531    556   
            .collect();
  532    557   
        self.assert_consistent();
  533    558   
        Some(participants)
  534    559   
    }
  535    560   
  536    561   
    /// Closes the gate over waiters committed when a generation became visible.
  537    562   
    pub(super) fn prioritize_through(&mut self, cutoff: Option<WaiterId>) {
  538    563   
        let generation = self
  539         -
            .accepting
         564  +
            .accepting_generation
  540    565   
            .expect("HTTP/2 generation gate opened without an accepting generation");
  541         -
        self.gate = GenerationGate::for_generation(generation, cutoff);
         566  +
        self.local_activation_gate = H2ActivationGate::for_generation(generation, cutoff);
  542    567   
        self.assert_consistent();
  543    568   
    }
  544    569   
  545    570   
    /// Extends priority to a post-ALPN waiter that found this generation.
  546    571   
    pub(super) fn prioritize_waiter(
  547    572   
        &mut self,
  548    573   
        generation: H2GenerationId,
  549    574   
        waiter: WaiterId,
  550    575   
    ) -> bool {
  551         -
        if self.accepting != Some(generation) {
         576  +
        if self.accepting_generation != Some(generation) {
  552    577   
            return false;
  553    578   
        }
  554         -
        match &mut self.gate {
  555         -
            GenerationGate::Closed => {
         579  +
        match &mut self.local_activation_gate {
         580  +
            H2ActivationGate::Closed => {
  556    581   
                unreachable!("accepting HTTP/2 generation had no generation gate")
  557    582   
            }
  558         -
            GenerationGate::Prioritizing { cutoff, .. } => {
         583  +
            H2ActivationGate::Prioritizing { cutoff, .. } => {
  559    584   
                *cutoff = (*cutoff).max(waiter);
  560    585   
            }
  561         -
            GenerationGate::Open { .. } => {
  562         -
                self.gate = GenerationGate::Prioritizing {
         586  +
            H2ActivationGate::Open { .. } => {
         587  +
                self.local_activation_gate = H2ActivationGate::Prioritizing {
  563    588   
                    generation,
  564    589   
                    cutoff: waiter,
  565         -
                    activating: None,
         590  +
                    active_turn: None,
  566    591   
                };
  567    592   
            }
  568    593   
        }
  569    594   
        self.generations
  570    595   
            .get_mut(&generation)
  571    596   
            .expect("accepting HTTP/2 generation disappeared")
  572    597   
            .pending_waiters
  573    598   
            .insert(waiter);
  574    599   
        self.assert_consistent();
  575    600   
        true
  576    601   
    }
  577    602   
  578    603   
    /// Returns the next queued activation opportunity.
  579         -
    fn next_gate_turn(&mut self, has_prioritized: bool) -> GateTurn {
  580         -
        self.gate.next_turn(has_prioritized)
         604  +
    fn take_next_turn(&mut self, has_prioritized: bool) -> H2ActivationTurn {
         605  +
        self.local_activation_gate.take_next_turn(has_prioritized)
  581    606   
    }
  582    607   
  583    608   
    /// Records a prioritized activation and releases its transferred waiter.
  584         -
    pub(super) fn begin_gate_activation(&mut self, waiter: WaiterId) -> bool {
  585         -
        let gated = self.gate.begin_gate_activation(waiter);
  586         -
        if let Some(generation) = self.accepting {
         609  +
    pub(super) fn reserve_activation_turn(&mut self, waiter: WaiterId) -> bool {
         610  +
        let gated = self.local_activation_gate.reserve_turn(waiter);
         611  +
        if let Some(generation) = self.accepting_generation {
  587    612   
            self.generations
  588    613   
                .get_mut(&generation)
  589    614   
                .expect("accepting HTTP/2 generation disappeared")
  590    615   
                .pending_waiters
  591    616   
                .remove(&waiter);
  592    617   
        }
  593    618   
        self.assert_consistent();
  594    619   
        gated
  595    620   
    }
  596    621   
  597    622   
    /// Discharges one accepted or cancelled activation opportunity.
  598         -
    pub(super) fn finish_gate_activation(
         623  +
    pub(super) fn release_activation_turn(
  599    624   
        &mut self,
  600    625   
        generation: H2GenerationId,
  601    626   
        waiter: WaiterId,
  602    627   
    ) -> bool {
  603         -
        let finished = self.gate.finish_gate_activation(generation, waiter);
         628  +
        let finished = self.local_activation_gate.release_turn(generation, waiter);
  604    629   
        self.assert_consistent();
  605    630   
        finished
  606    631   
    }
  607    632   
  608    633   
    /// Returns whether a direct arrival may use the accepting generation.
  609    634   
    pub(super) fn direct_is_allowed(&self, queued: bool) -> bool {
  610         -
        !queued && matches!(self.gate, GenerationGate::Open { .. })
         635  +
        !queued && matches!(self.local_activation_gate, H2ActivationGate::Open { .. })
  611    636   
    }
  612    637   
  613         -
    /// Returns the publication cutoff while older waiters remain prioritized.
         638  +
    /// Returns the peer-route cutoff while older waiters remain prioritized.
  614    639   
    fn priority_cutoff(&self) -> Option<WaiterId> {
  615         -
        self.gate.priority_cutoff()
         640  +
        self.local_activation_gate.priority_cutoff()
  616    641   
    }
  617    642   
  618         -
    /// Reserves one prospective lease against an accepting generation.
  619         -
    fn activate(&mut self, generation: H2GenerationId) -> Option<H2ActivationParts> {
  620         -
        if self.accepting != Some(generation) {
         643  +
    /// Reserves one prospective claim against an accepting generation.
         644  +
    fn activate(&mut self, generation: H2GenerationId) -> Option<H2ActivationResources> {
         645  +
        if self.accepting_generation != Some(generation) {
  621    646   
            return None;
  622    647   
        }
  623    648   
        let record = self.generations.get_mut(&generation)?;
  624         -
        if record.residence != H2Residence::Accepting {
         649  +
        if record.state != H2GenerationState::Accepting {
  625    650   
            return None;
  626    651   
        }
  627         -
        record.prospective = record
  628         -
            .prospective
         652  +
        record.prospective_requests = record
         653  +
            .prospective_requests
  629    654   
            .checked_add(1)
  630    655   
            .expect("HTTP/2 prospective request count exhausted");
  631         -
        let parts = H2ActivationParts {
         656  +
        let resources = H2ActivationResources {
  632    657   
            sender: record.sender.clone(),
  633         -
            reused: record.has_dispatched,
         658  +
            reused: record.has_accepted_request,
  634    659   
            connection: record.connection.clone(),
  635    660   
        };
  636    661   
        self.assert_consistent();
  637         -
        Some(parts)
         662  +
        Some(resources)
  638    663   
    }
  639    664   
  640         -
    /// Converts one prospective reservation to an accepted request lease.
         665  +
    /// Converts one prospective reservation to an accepted request claim.
  641    666   
    fn accept(&mut self, generation: H2GenerationId) -> bool {
  642    667   
        let Some(record) = self.generations.get_mut(&generation) else {
  643    668   
            return false;
  644    669   
        };
  645         -
        if record.prospective == 0 {
         670  +
        if record.prospective_requests == 0 {
  646    671   
            return false;
  647    672   
        }
  648         -
        record.prospective -= 1;
  649         -
        record.accepted = record
  650         -
            .accepted
         673  +
        record.prospective_requests -= 1;
         674  +
        record.active_requests = record
         675  +
            .active_requests
  651    676   
            .checked_add(1)
  652         -
            .expect("HTTP/2 accepted request count exhausted");
  653         -
        record.has_dispatched = true;
         677  +
            .expect("HTTP/2 active request count exhausted");
         678  +
        record.has_accepted_request = true;
  654    679   
        self.assert_consistent();
  655    680   
        true
  656    681   
    }
  657    682   
  658    683   
    /// Cancels one prospective reservation and detaches an empty drain record.
  659    684   
    ///
  660    685   
    /// # Panics
  661    686   
    ///
  662    687   
    /// Panics if the activation's exact generation or prospective count is
  663    688   
    /// missing.
  664    689   
    #[must_use]
  665    690   
    fn cancel(&mut self, generation: H2GenerationId) -> Option<H2Generation> {
  666    691   
        let record = self
  667    692   
            .generations
  668    693   
            .get_mut(&generation)
  669    694   
            .expect("HTTP/2 activation generation disappeared before cancellation");
  670    695   
        assert!(
  671         -
            record.prospective > 0,
         696  +
            record.prospective_requests > 0,
  672    697   
            "HTTP/2 prospective request count underflowed"
  673    698   
        );
  674         -
        record.prospective -= 1;
         699  +
        record.prospective_requests -= 1;
  675    700   
        self.remove_finished_drain(generation)
  676    701   
    }
  677    702   
  678    703   
    /// Releases one accepted request and detaches an empty drain record.
  679    704   
    ///
  680    705   
    /// # Panics
  681    706   
    ///
  682         -
    /// Panics if the lease's exact generation or accepted count is missing.
         707  +
    /// Panics if the claim's exact generation or active request count is missing.
  683    708   
    #[must_use]
  684    709   
    fn complete_request(&mut self, generation: H2GenerationId) -> Option<H2Generation> {
  685    710   
        let record = self
  686    711   
            .generations
  687    712   
            .get_mut(&generation)
  688         -
            .expect("HTTP/2 request generation disappeared before lease completion");
         713  +
            .expect("HTTP/2 request generation disappeared before claim release");
  689    714   
        assert!(
  690         -
            record.accepted > 0,
  691         -
            "HTTP/2 accepted request count underflowed"
         715  +
            record.active_requests > 0,
         716  +
            "HTTP/2 active request count underflowed"
  692    717   
        );
  693         -
        record.accepted -= 1;
         718  +
        record.active_requests -= 1;
  694    719   
        self.remove_finished_drain(generation)
  695    720   
    }
  696    721   
  697         -
    /// Moves one exact accepting generation to draining.
         722  +
    /// Stops one exact generation from accepting new request streams.
         723  +
    ///
         724  +
    /// Selected and accepted streams retain the generation in `Draining`
         725  +
    /// until their claims finish. A generation with no request work is removed
         726  +
    /// immediately. Pending establishment participants are returned so they
         727  +
    /// can acquire another connection.
  698    728   
    #[must_use]
  699    729   
    fn begin_close(&mut self, generation: H2GenerationId) -> Option<H2CloseTransition> {
  700         -
        if self.accepting != Some(generation) {
         730  +
        if self.accepting_generation != Some(generation) {
  701    731   
            return None;
  702    732   
        }
  703    733   
        let record = self.generations.get_mut(&generation)?;
  704         -
        if record.residence != H2Residence::Accepting {
         734  +
        if record.state != H2GenerationState::Accepting {
  705    735   
            return None;
  706    736   
        }
  707         -
        record.residence = H2Residence::Draining;
  708         -
        self.accepting = None;
  709         -
        self.gate = GenerationGate::Closed;
         737  +
        record.state = H2GenerationState::Draining;
         738  +
        self.accepting_generation = None;
         739  +
        self.local_activation_gate = H2ActivationGate::Closed;
  710    740   
        let pending_waiters = std::mem::take(&mut record.pending_waiters);
  711         -
        let remove_record = record.prospective == 0 && record.accepted == 0;
         741  +
        let remove_record = record.prospective_requests == 0 && record.active_requests == 0;
  712    742   
        let connection = record.connection.clone();
  713    743   
        let removed_generation = remove_record
  714    744   
            .then(|| self.generations.remove(&generation))
  715    745   
            .flatten();
  716    746   
        Some(H2CloseTransition {
  717    747   
            connection,
  718    748   
            pending_waiters,
  719    749   
            removed_generation,
  720    750   
        })
  721    751   
    }
  722    752   
  723    753   
    /// Detaches an exact accepting generation only while it has no request work.
  724    754   
    #[must_use]
  725    755   
    fn begin_idle_reclaim(&mut self, generation: H2GenerationId) -> Option<H2CloseTransition> {
  726    756   
        self.is_idle(generation)
  727    757   
            .then(|| self.begin_close(generation))
  728    758   
            .flatten()
  729    759   
    }
  730    760   
  731    761   
    /// Returns the accepting generation when one is locally reusable.
  732    762   
    pub(in crate::client::pool) fn accepting(&self) -> Option<H2GenerationId> {
  733         -
        self.accepting
         763  +
        self.accepting_generation
  734    764   
    }
  735    765   
  736    766   
    /// Returns whether an exact generation remains accepting.
  737    767   
    pub(super) fn is_accepting(&self, generation: H2GenerationId) -> bool {
  738         -
        self.accepting == Some(generation)
         768  +
        self.accepting_generation == Some(generation)
  739    769   
    }
  740    770   
  741    771   
    /// Returns the generation peers may discover after the local cutoff drains.
  742         -
    pub(super) fn publishable_generation(&self) -> Option<H2GenerationId> {
  743         -
        let generation = self.accepting?;
         772  +
    pub(super) fn peer_routable_generation(&self) -> Option<H2GenerationId> {
         773  +
        let generation = self.accepting_generation?;
  744    774   
        matches!(
  745         -
            self.gate,
  746         -
            GenerationGate::Open { generation: gate_generation } if gate_generation == generation
         775  +
            self.local_activation_gate,
         776  +
            H2ActivationGate::Open { generation: gate_generation }
         777  +
                if gate_generation == generation
  747    778   
        )
  748    779   
        .then_some(generation)
  749    780   
    }
  750    781   
  751    782   
    /// Returns whether an exact publishable generation has no request work.
  752    783   
    pub(super) fn is_idle(&self, generation: H2GenerationId) -> bool {
  753         -
        if self.publishable_generation() != Some(generation) {
         784  +
        if self.peer_routable_generation() != Some(generation) {
  754    785   
            return false;
  755    786   
        }
  756    787   
        self.generations.get(&generation).is_some_and(|record| {
  757         -
            record.pending_waiters.is_empty() && record.prospective == 0 && record.accepted == 0
         788  +
            record.pending_waiters.is_empty()
         789  +
                && record.prospective_requests == 0
         790  +
                && record.active_requests == 0
  758    791   
        })
  759    792   
    }
  760    793   
  761    794   
    /// Returns whether a local generation or peer route suppresses admission demand.
  762    795   
    pub(super) fn has_visible_h2(&self) -> bool {
  763         -
        self.accepting.is_some() || self.peer_route.is_some()
         796  +
        self.accepting_generation.is_some() || self.peer_route.is_some()
  764    797   
    }
  765    798   
  766    799   
    /// Installs or refreshes one identity-only peer route.
  767         -
    pub(super) fn install_peer_route(&mut self, route: H2Route, cutoff: Option<WaiterId>) {
  768         -
        if self.accepting.is_some() {
         800  +
    pub(super) fn attach_peer_route(&mut self, route: H2Route, cutoff: Option<WaiterId>) {
         801  +
        if self.accepting_generation.is_some() {
  769    802   
            self.peer_route = None;
  770    803   
            self.assert_consistent();
  771    804   
            return;
  772    805   
        }
  773    806   
        let id = route.id();
  774    807   
        match &mut self.peer_route {
  775    808   
            Some(current) if current.route.id() == id => {
  776    809   
                if let Some(cutoff) = cutoff {
  777         -
                    match &mut current.gate {
  778         -
                        GenerationGate::Closed => {
         810  +
                    match &mut current.activation_gate {
         811  +
                        H2ActivationGate::Closed => {
  779    812   
                            unreachable!("visible peer HTTP/2 route had a closed gate")
  780    813   
                        }
  781         -
                        GenerationGate::Prioritizing {
         814  +
                        H2ActivationGate::Prioritizing {
  782    815   
                            cutoff: current, ..
  783    816   
                        } => *current = (*current).max(cutoff),
  784         -
                        GenerationGate::Open { generation } => {
  785         -
                            current.gate = GenerationGate::Prioritizing {
         817  +
                        H2ActivationGate::Open { generation } => {
         818  +
                            current.activation_gate = H2ActivationGate::Prioritizing {
  786    819   
                                generation: *generation,
  787    820   
                                cutoff,
  788         -
                                activating: None,
         821  +
                                active_turn: None,
  789    822   
                            };
  790    823   
                        }
  791    824   
                    }
  792    825   
                }
  793    826   
            }
  794    827   
            _ => {
  795    828   
                self.peer_route = Some(PeerH2Route {
  796         -
                    gate: GenerationGate::for_generation(id.generation, cutoff),
         829  +
                    activation_gate: H2ActivationGate::for_generation(id.generation, cutoff),
  797    830   
                    route,
  798         -
                    crossing: None,
         831  +
                    crossing_waiter: None,
  799    832   
                });
  800    833   
            }
  801    834   
        }
  802    835   
        self.assert_consistent();
  803    836   
    }
  804    837   
  805    838   
    /// Marks one requesting waiter as the route's current activation opportunity.
  806    839   
    fn prepare_peer_activation(
  807    840   
        &mut self,
  808    841   
        waiters: &AcquisitionQueue,
  809    842   
    ) -> Option<PreparedPeerActivation> {
  810    843   
        let peer = self.peer_route.as_mut()?;
  811         -
        if peer.crossing.is_some() {
         844  +
        if peer.crossing_waiter.is_some() {
  812    845   
            return None;
  813    846   
        }
  814    847   
        let has_prioritized = peer
  815         -
            .gate
         848  +
            .activation_gate
  816    849   
            .priority_cutoff()
  817         -
            .is_some_and(|cutoff| waiters.has_h2_candidate_through(cutoff));
  818         -
        let cutoff = match peer.gate.next_turn(has_prioritized) {
  819         -
            GateTurn::Unavailable => return None,
  820         -
            GateTurn::Open => None,
  821         -
            GateTurn::Through(cutoff) => Some(cutoff),
         850  +
            .is_some_and(|cutoff| waiters.has_h2_compatible_waiter_through(cutoff));
         851  +
        let cutoff = match peer.activation_gate.take_next_turn(has_prioritized) {
         852  +
            H2ActivationTurn::Unavailable => return None,
         853  +
            H2ActivationTurn::Open => None,
         854  +
            H2ActivationTurn::Through(cutoff) => Some(cutoff),
  822    855   
        };
  823         -
        let waiter = waiters.oldest_h2_candidate()?;
         856  +
        let waiter = waiters.oldest_h2_compatible_waiter()?;
  824    857   
        if cutoff.is_some_and(|cutoff| waiter > cutoff) {
  825    858   
            return None;
  826    859   
        }
  827         -
        let gated = peer.gate.begin_gate_activation(waiter);
  828         -
        peer.crossing = Some(waiter);
         860  +
        let gated = peer.activation_gate.reserve_turn(waiter);
         861  +
        peer.crossing_waiter = Some(waiter);
  829    862   
        Some(PreparedPeerActivation {
  830    863   
            route: peer.route.clone(),
  831    864   
            waiter,
  832    865   
            cutoff,
  833    866   
            gated,
  834    867   
        })
  835    868   
    }
  836    869   
  837    870   
    /// Revalidates one peer activation after the connection-cell crossing.
  838    871   
    fn peer_activation_is_current(
  839    872   
        &self,
  840    873   
        prepared: &PreparedPeerActivation,
  841    874   
        waiters: &AcquisitionQueue,
  842    875   
    ) -> bool {
  843    876   
        self.peer_route.as_ref().is_some_and(|peer| {
  844    877   
            peer.route.id() == prepared.route.id()
  845         -
                && peer.crossing == Some(prepared.waiter)
  846         -
                && (!prepared.gated || peer.gate.activating() == Some(prepared.waiter))
  847         -
                && waiters.is_oldest_h2_candidate(prepared.waiter)
         878  +
                && peer.crossing_waiter == Some(prepared.waiter)
         879  +
                && (!prepared.gated || peer.activation_gate.active_turn() == Some(prepared.waiter))
         880  +
                && waiters.is_oldest_h2_compatible_waiter(prepared.waiter)
  848    881   
        })
  849    882   
    }
  850    883   
  851    884   
    /// Ends one route crossing after its result is installed or rejected.
  852         -
    fn finish_peer_crossing(&mut self, route: H2RouteId, waiter: WaiterId) -> bool {
         885  +
    fn release_peer_crossing(&mut self, route: H2RouteId, waiter: WaiterId) -> bool {
  853    886   
        let Some(peer) = &mut self.peer_route else {
  854    887   
            return false;
  855    888   
        };
  856         -
        if peer.route.id() != route || peer.crossing != Some(waiter) {
         889  +
        if peer.route.id() != route || peer.crossing_waiter != Some(waiter) {
  857    890   
            return false;
  858    891   
        }
  859         -
        peer.crossing = None;
         892  +
        peer.crossing_waiter = None;
  860    893   
        self.assert_consistent();
  861    894   
        true
  862    895   
    }
  863    896   
  864    897   
    /// Discharges one prioritized peer-route activation opportunity.
  865         -
    fn finish_peer_gate(&mut self, route: H2RouteId, waiter: WaiterId) -> bool {
         898  +
    fn release_peer_turn(&mut self, route: H2RouteId, waiter: WaiterId) -> bool {
  866    899   
        let Some(peer) = &mut self.peer_route else {
  867    900   
            return false;
  868    901   
        };
  869    902   
        if peer.route.id() != route {
  870    903   
            return false;
  871    904   
        }
  872         -
        let finished = peer.gate.finish_gate_activation(route.generation, waiter);
         905  +
        let finished = peer.activation_gate.release_turn(route.generation, waiter);
  873    906   
        self.assert_consistent();
  874    907   
        finished
  875    908   
    }
  876    909   
  877    910   
    /// Clears a peer activation marker when its requesting waiter is cancelled.
  878    911   
    pub(super) fn cancel_peer_activation(&mut self, waiter: WaiterId) -> bool {
  879    912   
        let Some(route) = self.peer_route.as_ref().map(|peer| peer.route.id()) else {
  880    913   
            return false;
  881    914   
        };
  882    915   
        if self
  883    916   
            .peer_route
  884    917   
            .as_ref()
  885         -
            .and_then(|peer| peer.gate.activating())
         918  +
            .and_then(|peer| peer.activation_gate.active_turn())
  886    919   
            != Some(waiter)
  887    920   
        {
  888    921   
            return false;
  889    922   
        }
  890         -
        self.finish_peer_gate(route, waiter)
         923  +
        self.release_peer_turn(route, waiter)
  891    924   
    }
  892    925   
  893    926   
    /// Returns an open peer route when no queued acquisition precedes it.
  894    927   
    fn open_peer_route(&self, queued: bool) -> Option<H2Route> {
  895    928   
        let peer = self.peer_route.as_ref()?;
  896         -
        (!queued && peer.gate.is_open_for(peer.route.id().generation)).then(|| peer.route.clone())
         929  +
        (!queued && peer.activation_gate.is_open_for(peer.route.id().generation))
         930  +
            .then(|| peer.route.clone())
  897    931   
    }
  898    932   
  899    933   
    /// Revalidates a direct peer route after its connection-cell crossing.
  900    934   
    fn direct_peer_route_is_current(&self, route: H2RouteId, queued: bool) -> bool {
  901    935   
        !queued
  902    936   
            && self.peer_route.as_ref().is_some_and(|peer| {
  903         -
                peer.route.id() == route && peer.gate.is_open_for(route.generation)
         937  +
                peer.route.id() == route && peer.activation_gate.is_open_for(route.generation)
  904    938   
            })
  905    939   
    }
  906    940   
  907    941   
    /// Removes one stale exact peer route.
  908         -
    fn clear_peer_route(&mut self, route: H2RouteId) -> bool {
         942  +
    fn detach_peer_route(&mut self, route: H2RouteId) -> bool {
  909    943   
        if self.peer_route.as_ref().map(|peer| peer.route.id()) != Some(route) {
  910    944   
            return false;
  911    945   
        }
  912    946   
        self.peer_route = None;
  913    947   
        self.assert_consistent();
  914    948   
        true
  915    949   
    }
  916    950   
  917    951   
    /// Returns all installed generation identities for shutdown.
  918    952   
    pub(super) fn generation_ids(&self) -> Vec<H2GenerationId> {
  919    953   
        self.generations.keys().copied().collect()
  920    954   
    }
  921    955   
  922    956   
    /// Returns the accepting generation's idle deadline.
  923    957   
    pub(super) fn nearest_idle_deadline(&self) -> Option<SystemTime> {
  924         -
        self.accepting
         958  +
        self.accepting_generation
  925    959   
            .and_then(|generation| self.generations.get(&generation))
  926    960   
            .and_then(|record| record.idle_deadline)
  927    961   
    }
  928    962   
  929    963   
    /// Returns an accepting generation whose idle deadline elapsed.
  930    964   
    pub(super) fn expired(&self, now: SystemTime) -> Option<H2GenerationId> {
  931         -
        let generation = self.accepting?;
         965  +
        let generation = self.accepting_generation?;
  932    966   
        let deadline = self.generations.get(&generation)?.idle_deadline?;
  933    967   
        (deadline <= now).then_some(generation)
  934    968   
    }
  935    969   
  936    970   
    /// Replaces the accepting generation's idle deadline after dispatch.
  937    971   
    pub(super) fn reset_idle_deadline(
  938    972   
        &mut self,
  939    973   
        generation: H2GenerationId,
  940    974   
        deadline: Option<SystemTime>,
  941    975   
    ) -> bool {
  942         -
        if self.accepting != Some(generation) {
         976  +
        if self.accepting_generation != Some(generation) {
  943    977   
            return false;
  944    978   
        }
  945    979   
        let record = self
  946    980   
            .generations
  947    981   
            .get_mut(&generation)
  948    982   
            .expect("accepting HTTP/2 generation disappeared");
  949    983   
        record.idle_deadline = deadline;
  950    984   
        self.assert_consistent();
  951    985   
        true
  952    986   
    }
  953    987   
  954    988   
    /// Detaches a draining generation after its last retained request ends.
  955    989   
    #[must_use]
  956    990   
    fn remove_finished_drain(&mut self, generation: H2GenerationId) -> Option<H2Generation> {
  957    991   
        let remove = self.generations.get(&generation).is_some_and(|record| {
  958         -
            record.residence == H2Residence::Draining
  959         -
                && record.prospective == 0
  960         -
                && record.accepted == 0
         992  +
            record.state == H2GenerationState::Draining
         993  +
                && record.prospective_requests == 0
         994  +
                && record.active_requests == 0
  961    995   
        });
  962    996   
        remove
  963    997   
            .then(|| self.generations.remove(&generation))
  964    998   
            .flatten()
  965    999   
    }
  966   1000   
  967   1001   
    fn take_flight_id(&mut self) -> H2FlightId {
  968         -
        let value = self.next_flight;
  969         -
        self.next_flight = value
        1002  +
        let value = self.next_flight_id;
        1003  +
        self.next_flight_id = value
  970   1004   
            .checked_add(1)
  971   1005   
            .expect("HTTP/2 flight identity exhausted");
  972   1006   
        H2FlightId(value)
  973   1007   
    }
  974   1008   
  975   1009   
    fn take_generation_id(&mut self) -> H2GenerationId {
  976         -
        let value = self.next_generation;
  977         -
        self.next_generation = value
        1010  +
        let value = self.next_generation_id;
        1011  +
        self.next_generation_id = value
  978   1012   
            .checked_add(1)
  979   1013   
            .expect("HTTP/2 generation identity exhausted");
  980   1014   
        H2GenerationId(value)
  981   1015   
    }
  982   1016   
  983   1017   
    /// Checks flight, generation, residence, and request-count relationships.
  984   1018   
    pub(super) fn assert_consistent(&self) {
  985   1019   
        #[cfg(any(debug_assertions, test))]
  986   1020   
        {
  987   1021   
            if std::thread::panicking() {
  988   1022   
                return;
  989   1023   
            }
  990   1024   
            let accepting_records = self
  991   1025   
                .generations
  992   1026   
                .iter()
  993         -
                .filter(|(_, record)| record.residence == H2Residence::Accepting)
        1027  +
                .filter(|(_, record)| record.state == H2GenerationState::Accepting)
  994   1028   
                .map(|(generation, _)| *generation)
  995   1029   
                .collect::<Vec<_>>();
  996         -
            match self.accepting {
        1030  +
            match self.accepting_generation {
  997   1031   
                Some(generation) => assert_eq!(
  998   1032   
                    vec![generation],
  999   1033   
                    accepting_records,
 1000         -
                    "HTTP/2 accepting identity did not match generation residence"
        1034  +
                    "HTTP/2 accepting identity did not match generation state"
 1001   1035   
                ),
 1002   1036   
                None => assert!(
 1003   1037   
                    accepting_records.is_empty(),
 1004   1038   
                    "HTTP/2 accepting generation lacked an accepting identity"
 1005   1039   
                ),
 1006   1040   
            }
 1007   1041   
            assert_eq!(
 1008         -
                self.accepting,
 1009         -
                self.gate.generation(),
        1042  +
                self.accepting_generation,
        1043  +
                self.local_activation_gate.generation(),
 1010   1044   
                "HTTP/2 generation gate did not name the accepting generation"
 1011   1045   
            );
 1012   1046   
            assert!(
 1013         -
                self.flight.is_none() || self.accepting.is_none(),
        1047  +
                self.flight.is_none() || self.accepting_generation.is_none(),
 1014   1048   
                "HTTP/2 flight coexisted with an accepting generation"
 1015   1049   
            );
 1016   1050   
            if let Some(peer) = &self.peer_route {
 1017   1051   
                assert!(
 1018         -
                    self.accepting.is_none(),
        1052  +
                    self.accepting_generation.is_none(),
 1019   1053   
                    "local accepting generation retained a peer route"
 1020   1054   
                );
 1021   1055   
                assert_eq!(
 1022   1056   
                    Some(peer.route.generation()),
 1023         -
                    peer.gate.generation(),
 1024         -
                    "peer route gate did not name the advertised generation"
        1057  +
                    peer.activation_gate.generation(),
        1058  +
                    "peer route gate did not name the routed generation"
 1025   1059   
                );
 1026   1060   
            }
 1027   1061   
 1028   1062   
            for record in self.generations.values() {
 1029         -
                if record.residence == H2Residence::Draining {
        1063  +
                if record.state == H2GenerationState::Draining {
 1030   1064   
                    assert!(
 1031         -
                        record.prospective > 0 || record.accepted > 0,
        1065  +
                        record.prospective_requests > 0 || record.active_requests > 0,
 1032   1066   
                        "empty HTTP/2 draining generation was retained"
 1033   1067   
                    );
 1034   1068   
                    assert!(
 1035   1069   
                        record.pending_waiters.is_empty(),
 1036   1070   
                        "draining HTTP/2 generation retained unserved waiters"
 1037   1071   
                    );
 1038   1072   
                }
 1039   1073   
            }
 1040   1074   
        }
 1041   1075   
    }
 1042   1076   
 1043         -
    /// Checks transferred waiter identities against the cell's acquisition state.
        1077  +
    /// Checks flight and generation waiters against the cell's acquisition state.
 1044   1078   
    #[cfg(any(debug_assertions, test))]
 1045   1079   
    pub(super) fn assert_pending_waiters(&self, waiters: &AcquisitionQueue) {
        1080  +
        if let Some(flight) = &self.flight {
        1081  +
            for waiter in &flight.participants {
        1082  +
                assert!(
        1083  +
                    waiters.is_launching_h2_waiter(*waiter),
        1084  +
                    "HTTP/2 flight retained a waiter that was no longer launchable"
        1085  +
                );
        1086  +
            }
        1087  +
        }
 1046   1088   
        for record in self.generations.values() {
 1047   1089   
            for waiter in &record.pending_waiters {
 1048   1090   
                assert!(
 1049         -
                    waiters.is_launching_h2_candidate(*waiter),
        1091  +
                    waiters.is_launching_h2_waiter(*waiter),
 1050   1092   
                    "HTTP/2 generation retained a waiter that was no longer launchable"
 1051   1093   
                );
 1052   1094   
            }
 1053   1095   
        }
 1054   1096   
    }
 1055   1097   
}
 1056   1098   
 1057   1099   
/// One requesting-cell activation prepared before crossing to its connection cell.
 1058   1100   
struct PreparedPeerActivation {
 1059   1101   
    /// Route validated in the requesting cell before the lock crossing.
 1060   1102   
    route: H2Route,
 1061   1103   
    /// Oldest waiter selected for this activation.
 1062   1104   
    waiter: WaiterId,
 1063         -
    /// Publication cutoff that this waiter must satisfy.
        1105  +
    /// Peer-route cutoff that this waiter must satisfy.
 1064   1106   
    cutoff: Option<WaiterId>,
 1065   1107   
    /// Whether acceptance or cancellation must discharge a priority turn.
 1066   1108   
    gated: bool,
 1067   1109   
}
 1068   1110   
 1069   1111   
/// State detached when an accepting generation begins draining.
 1070   1112   
struct H2CloseTransition {
 1071   1113   
    /// Connection whose logical close follows cell unlock.
 1072   1114   
    connection: Arc<ConnectionState>,
 1073   1115   
    /// Unserved transferred waiters that must re-enter acquisition.
 1074   1116   
    pending_waiters: BTreeSet<WaiterId>,
 1075   1117   
    /// Removed record retained until the cell lock is released.
 1076   1118   
    removed_generation: Option<H2Generation>,
 1077   1119   
}
 1078   1120   
 1079         -
/// Sender and connection cloned while a prospective lease is state-owned.
 1080         -
struct H2ActivationParts {
 1081         -
    /// Transient sender clone for one activation.
 1082         -
    sender: H2Sender,
 1083         -
    /// Whether the generation has accepted an earlier request.
 1084         -
    reused: bool,
 1085         -
    /// Connection retained through prospective dispatch.
 1086         -
    connection: Arc<ConnectionState>,
 1087         -
}
 1088         -
 1089         -
/// Values transferred together when an activation begins Hyper dispatch.
 1090         -
pub(in crate::client::pool) struct H2DispatchParts {
 1091         -
    /// Transient sender clone for one dispatch attempt.
 1092         -
    pub(in crate::client::pool) sender: H2Sender,
 1093         -
    /// Request-send endpoint retained by the request body.
 1094         -
    pub(in crate::client::pool) send_endpoint: H2LeaseEndpoint,
 1095         -
    /// Response-receive endpoint retained by the response body.
 1096         -
    pub(in crate::client::pool) receive_endpoint: H2LeaseEndpoint,
 1097         -
}
 1098         -
 1099         -
/// Prospective request reservation against one exact generation.
 1100         -
///
 1101         -
/// Dropping the activation before Hyper accepts the request cancels the
 1102         -
/// prospective reservation. Acceptance creates endpoint guards and transfers
 1103         -
/// dispatch accounting to the response lifecycle.
 1104         -
pub(in crate::client::pool) struct H2Activation {
 1105         -
    /// Cell that owns the selected generation.
 1106         -
    cell: Arc<OriginCell>,
 1107         -
    /// Partition issuing this request.
 1108         -
    request_partition: PartitionId,
 1109         -
    /// Exact generation that owns the prospective count.
 1110         -
    generation: H2GenerationId,
 1111         -
    /// Transient sender transferred at most once into dispatch.
 1112         -
    sender: Option<H2Sender>,
 1113         -
    /// Whether the generation has accepted an earlier request.
 1114         -
    reused: bool,
 1115         -
    /// Connection retained until acceptance or cancellation.
 1116         -
    connection: Arc<ConnectionState>,
 1117         -
    /// Shared state for the request's send and receive endpoints.
 1118         -
    lease: Arc<H2LeaseCore>,
 1119         -
    /// Requesting-cell priority discharged only at acceptance or cancellation.
 1120         -
    gate: Option<H2GateToken>,
 1121         -
    /// Whether drop must cancel the prospective generation count.
 1122         -
    active: bool,
 1123         -
}
 1124         -
 1125         -
impl std::fmt::Debug for H2Activation {
 1126         -
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 1127         -
        f.debug_struct("H2Activation")
 1128         -
            .field("generation", &self.generation)
 1129         -
            .field("connection_id", &self.connection.id())
 1130         -
            .field("active", &self.active)
 1131         -
            .finish()
 1132         -
    }
 1133         -
}
 1134         -
 1135         -
impl H2Activation {
 1136         -
    /// Builds an activation after the generation records its prospective lease.
 1137         -
    fn new(
 1138         -
        cell: Arc<OriginCell>,
 1139         -
        generation: H2GenerationId,
 1140         -
        parts: H2ActivationParts,
 1141         -
        gate: Option<H2GateToken>,
 1142         -
    ) -> Self {
 1143         -
        let request_partition = cell.id().partition();
 1144         -
        let lease = Arc::new(H2LeaseCore {
 1145         -
            cell: Weak::from_arc(&cell),
 1146         -
            generation,
 1147         -
            state: Mutex::new(H2LeaseState::Prospective {
 1148         -
                send_complete: false,
 1149         -
                receive_complete: false,
 1150         -
            }),
 1151         -
        });
 1152         -
        Self {
 1153         -
            cell,
 1154         -
            request_partition,
 1155         -
            generation,
 1156         -
            sender: Some(parts.sender),
 1157         -
            reused: parts.reused,
 1158         -
            connection: parts.connection,
 1159         -
            lease,
 1160         -
            gate,
 1161         -
            active: true,
 1162         -
        }
 1163         -
    }
 1164         -
 1165         -
    /// Returns the exact selected generation.
 1166         -
    #[cfg(test)]
 1167         -
    pub(in crate::client::pool) fn generation(&self) -> H2GenerationId {
 1168         -
        self.generation
 1169         -
    }
 1170         -
 1171         -
    /// Returns whether Hyper previously accepted a request on this generation.
 1172         -
    pub(in crate::client::pool) fn is_reused(&self) -> bool {
 1173         -
        self.reused
 1174         -
    }
 1175         -
 1176         -
    /// Returns the selected protocol-neutral connection.
 1177         -
    pub(in crate::client::pool) fn connection(&self) -> &Arc<ConnectionState> {
 1178         -
        &self.connection
 1179         -
    }
 1180         -
 1181         -
    /// Transfers the sender and both lease endpoints into one dispatch attempt.
 1182         -
    ///
 1183         -
    /// # Panics
 1184         -
    ///
 1185         -
    /// Panics if dispatch parts were already taken.
 1186         -
    pub(in crate::client::pool) fn take_dispatch_parts(&mut self) -> H2DispatchParts {
 1187         -
        let sender = self
 1188         -
            .sender
 1189         -
            .take()
 1190         -
            .expect("HTTP/2 activation dispatch parts already taken");
 1191         -
        let trace = H2EndpointTrace::new(self);
 1192         -
        H2DispatchParts {
 1193         -
            sender,
 1194         -
            send_endpoint: H2LeaseEndpoint::new(
 1195         -
                self.lease.clone(),
 1196         -
                H2Endpoint::Send,
 1197         -
                Some(trace.clone()),
 1198         -
            ),
 1199         -
            receive_endpoint: H2LeaseEndpoint::new(
 1200         -
                self.lease.clone(),
 1201         -
                H2Endpoint::Receive,
 1202         -
                Some(trace),
 1203         -
            ),
 1204         -
        }
 1205         -
    }
 1206         -
 1207         -
    /// Retains a requesting-cell route opportunity until acceptance or cancellation.
 1208         -
    fn attach_peer_gate(
 1209         -
        &mut self,
 1210         -
        requesting_cell: &Arc<OriginCell>,
 1211         -
        route: H2RouteId,
 1212         -
        waiter: WaiterId,
 1213         -
    ) {
 1214         -
        debug_assert_eq!(
 1215         -
            self.request_partition,
 1216         -
            requesting_cell.id().partition(),
 1217         -
            "peer HTTP/2 activation changed requesting partition"
 1218         -
        );
 1219         -
        assert!(
 1220         -
            self.gate.is_none(),
 1221         -
            "HTTP/2 activation acquired two requesting-cell gates"
 1222         -
        );
 1223         -
        self.gate = Some(H2GateToken::peer(requesting_cell, route, waiter));
 1224         -
    }
 1225         -
 1226         -
    /// Returns close authority for the connection-owning generation.
 1227         -
    pub(in crate::client::pool) fn close_handle(&self) -> H2CloseHandle {
 1228         -
        H2CloseHandle::new(&self.cell, self.generation)
 1229         -
    }
 1230         -
 1231         -
    /// Converts the prospective reservation after Hyper accepts the request.
 1232         -
    ///
 1233         -
    /// # Panics
 1234         -
    ///
 1235         -
    /// Panics if dispatch parts were not transferred or the prospective
 1236         -
    /// reservation disappeared before acceptance.
 1237         -
    pub(in crate::client::pool) fn accept(mut self, dispatch: DispatchGuard) {
 1238         -
        assert!(
 1239         -
            self.sender.is_none(),
 1240         -
            "HTTP/2 activation accepted before dispatch parts were taken"
 1241         -
        );
 1242         -
        assert!(
 1243         -
            OriginCell::accept_h2_activation(&self.cell, self.generation),
 1244         -
            "prospective HTTP/2 activation disappeared before acceptance"
 1245         -
        );
 1246         -
        self.active = false;
 1247         -
        self.lease.accept(dispatch);
 1248         -
        if let Some(gate) = self.gate.take() {
 1249         -
            gate.finish();
 1250         -
        }
 1251         -
        tracing::trace!(
 1252         -
            connection_id = %self.connection.id(),
 1253         -
            request_partition = ?self.request_partition,
 1254         -
            connection_partition = ?self.connection.owner_partition(),
 1255         -
            origin_scheme = %self.connection.info().origin().scheme(),
 1256         -
            origin_host = self.connection.info().origin().host(),
 1257         -
            origin_port = ?self.connection.info().origin().port(),
 1258         -
            h2_generation = ?self.generation,
 1259         -
            "HTTP/2 request accepted"
 1260         -
        );
 1261         -
    }
 1262         -
}
 1263         -
 1264         -
impl Drop for H2Activation {
 1265         -
    fn drop(&mut self) {
 1266         -
        if self.active {
 1267         -
            OriginCell::cancel_h2_activation(&self.cell, self.generation);
 1268         -
            self.lease.cancel();
 1269         -
            if let Some(gate) = self.gate.take() {
 1270         -
                gate.finish();
 1271         -
            }
 1272         -
            tracing::trace!(
 1273         -
                connection_id = %self.connection.id(),
 1274         -
                request_partition = ?self.request_partition,
 1275         -
                connection_partition = ?self.connection.owner_partition(),
 1276         -
                origin_scheme = %self.connection.info().origin().scheme(),
 1277         -
                origin_host = self.connection.info().origin().host(),
 1278         -
                origin_port = ?self.connection.info().origin().port(),
 1279         -
                h2_generation = ?self.generation,
 1280         -
                "HTTP/2 activation cancelled before request acceptance"
 1281         -
            );
 1282         -
        }
 1283         -
    }
 1284         -
}
 1285         -
 1286         -
/// One activation opportunity retained until Hyper acceptance or cancellation.
 1287         -
struct H2GateToken {
 1288         -
    /// Requesting cell whose gate owns this turn.
 1289         -
    cell: Weak<OriginCell>,
 1290         -
    /// Waiter that received the activation opportunity.
 1291         -
    waiter: WaiterId,
 1292         -
    /// Local-generation or peer-route gate identity.
 1293         -
    kind: H2GateKind,
 1294         -
}
 1295         -
 1296         -
/// Cell transition run when an activation accepts or cancels.
 1297         -
enum H2GateKind {
 1298         -
    /// Gate attached to the connection cell's local generation.
 1299         -
    Local { generation: H2GenerationId },
 1300         -
    /// Gate attached to a requesting cell's peer route.
 1301         -
    Peer { route: H2RouteId },
 1302         -
}
 1303         -
 1304         -
impl H2GateToken {
 1305         -
    fn local(cell: &Arc<OriginCell>, generation: H2GenerationId, waiter: WaiterId) -> Self {
 1306         -
        Self {
 1307         -
            cell: Weak::from_arc(cell),
 1308         -
            waiter,
 1309         -
            kind: H2GateKind::Local { generation },
 1310         -
        }
 1311         -
    }
 1312         -
 1313         -
    fn peer(cell: &Arc<OriginCell>, route: H2RouteId, waiter: WaiterId) -> Self {
 1314         -
        Self {
 1315         -
            cell: Weak::from_arc(cell),
 1316         -
            waiter,
 1317         -
            kind: H2GateKind::Peer { route },
 1318         -
        }
 1319         -
    }
 1320         -
 1321         -
    fn finish(self) {
 1322         -
        if let Some(cell) = self.cell.upgrade() {
 1323         -
            match self.kind {
 1324         -
                H2GateKind::Local { generation } => {
 1325         -
                    OriginCell::finish_h2_gate(&cell, generation, self.waiter)
 1326         -
                }
 1327         -
                H2GateKind::Peer { route } => {
 1328         -
                    OriginCell::finish_peer_h2_gate(&cell, route, self.waiter)
 1329         -
                }
 1330         -
            }
 1331         -
        }
 1332         -
    }
 1333         -
}
 1334         -
 1335         -
/// One terminal side of an accepted HTTP/2 request lease.
 1336         -
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
 1337         -
enum H2Endpoint {
 1338         -
    /// Request-body upload lifetime.
 1339         -
    Send,
 1340         -
    /// Response future and response-body lifetime.
 1341         -
    Receive,
 1342         -
}
 1343         -
 1344         -
/// Shared request-lease phase and endpoint bits.
 1345         -
enum H2LeaseState {
 1346         -
    /// Generation count reserved before Hyper accepts the request.
 1347         -
    Prospective {
 1348         -
        /// Whether upload ownership already terminated.
 1349         -
        send_complete: bool,
 1350         -
        /// Whether response ownership already terminated.
 1351         -
        receive_complete: bool,
 1352         -
    },
 1353         -
    /// Hyper accepted the request and connection dispatch is retained.
 1354         -
    Accepted {
 1355         -
        /// Whether upload ownership already terminated.
 1356         -
        send_complete: bool,
 1357         -
        /// Whether response ownership already terminated.
 1358         -
        receive_complete: bool,
 1359         -
        /// Accepted-dispatch accounting released with the second endpoint.
 1360         -
        dispatch: DispatchGuard,
 1361         -
    },
 1362         -
    /// Both endpoints terminated or prospective dispatch was cancelled.
 1363         -
    Complete,
 1364         -
}
 1365         -
 1366         -
/// Shared endpoint state for one prospective or accepted request.
 1367         -
struct H2LeaseCore {
 1368         -
    /// Connection cell updated after both accepted endpoints terminate.
 1369         -
    cell: Weak<OriginCell>,
 1370         -
    /// Exact generation whose request count this lease owns.
 1371         -
    generation: H2GenerationId,
 1372         -
    /// Endpoint phase; never held while locking the cell or connection state.
 1373         -
    state: Mutex<H2LeaseState>,
 1374         -
}
 1375         -
 1376         -
impl H2LeaseCore {
 1377         -
    /// Converts prospective endpoint state to accepted state.
 1378         -
    fn accept(&self, dispatch: DispatchGuard) {
 1379         -
        let mut dispatch = Some(dispatch);
 1380         -
        let complete = {
 1381         -
            let mut state = self.state.lock();
 1382         -
            let (send_complete, receive_complete) = match &*state {
 1383         -
                H2LeaseState::Prospective {
 1384         -
                    send_complete,
 1385         -
                    receive_complete,
 1386         -
                } => (*send_complete, *receive_complete),
 1387         -
                H2LeaseState::Accepted { .. } | H2LeaseState::Complete => {
 1388         -
                    drop(state);
 1389         -
                    panic!("HTTP/2 request lease accepted outside prospective state");
 1390         -
                }
 1391         -
            };
 1392         -
            if send_complete && receive_complete {
 1393         -
                *state = H2LeaseState::Complete;
 1394         -
                true
 1395         -
            } else {
 1396         -
                *state = H2LeaseState::Accepted {
 1397         -
                    send_complete,
 1398         -
                    receive_complete,
 1399         -
                    dispatch: dispatch
 1400         -
                        .take()
 1401         -
                        .expect("HTTP/2 dispatch guard disappeared before acceptance"),
 1402         -
                };
 1403         -
                false
 1404         -
            }
 1405         -
        };
 1406         -
        drop(dispatch);
 1407         -
        if complete {
 1408         -
            self.complete_generation();
 1409         -
        }
 1410         -
    }
 1411         -
 1412         -
    /// Cancels endpoint state after the prospective reservation ends.
 1413         -
    fn cancel(&self) {
 1414         -
        let mut state = self.state.lock();
 1415         -
        if matches!(*state, H2LeaseState::Prospective { .. }) {
 1416         -
            *state = H2LeaseState::Complete;
 1417         -
        }
 1418         -
    }
 1419         -
 1420         -
    /// Marks one endpoint terminal and releases the accepted lease on the second.
 1421         -
    fn complete_endpoint(&self, endpoint: H2Endpoint) -> bool {
 1422         -
        let dispatch = {
 1423         -
            let mut state = self.state.lock();
 1424         -
            match &mut *state {
 1425         -
                H2LeaseState::Prospective {
 1426         -
                    send_complete,
 1427         -
                    receive_complete,
 1428         -
                } => {
 1429         -
                    match endpoint {
 1430         -
                        H2Endpoint::Send => *send_complete = true,
 1431         -
                        H2Endpoint::Receive => *receive_complete = true,
 1432         -
                    }
 1433         -
                    None
 1434         -
                }
 1435         -
                H2LeaseState::Accepted {
 1436         -
                    send_complete,
 1437         -
                    receive_complete,
 1438         -
                    ..
 1439         -
                } => {
 1440         -
                    match endpoint {
 1441         -
                        H2Endpoint::Send => *send_complete = true,
 1442         -
                        H2Endpoint::Receive => *receive_complete = true,
 1443         -
                    }
 1444         -
                    if *send_complete && *receive_complete {
 1445         -
                        let previous = std::mem::replace(&mut *state, H2LeaseState::Complete);
 1446         -
                        let H2LeaseState::Accepted { dispatch, .. } = previous else {
 1447         -
                            unreachable!("completed HTTP/2 lease changed state under its lock");
 1448         -
                        };
 1449         -
                        Some(dispatch)
 1450         -
                    } else {
 1451         -
                        None
 1452         -
                    }
 1453         -
                }
 1454         -
                H2LeaseState::Complete => None,
 1455         -
            }
 1456         -
        };
 1457         -
        let request_complete = dispatch.is_some();
 1458         -
        if let Some(dispatch) = dispatch {
 1459         -
            // Dispatch completion takes the connection lifecycle lock. Keep it
 1460         -
            // outside the request-lease lock so endpoint completion cannot
 1461         -
            // nest pool synchronization.
 1462         -
            drop(dispatch);
 1463         -
            self.complete_generation();
 1464         -
        }
 1465         -
        request_complete
 1466         -
    }
 1467         -
 1468         -
    /// Releases the generation count after the lease lock is released.
 1469         -
    fn complete_generation(&self) {
 1470         -
        if let Some(cell) = self.cell.upgrade() {
 1471         -
            OriginCell::complete_h2_request(&cell, self.generation);
 1472         -
        }
 1473         -
    }
 1474         -
}
 1475         -
 1476         -
/// Structured identity retained by one request-lease endpoint.
 1477         -
#[derive(Clone)]
 1478         -
struct H2EndpointTrace {
 1479         -
    /// Partition that issued the request.
 1480         -
    request_partition: PartitionId,
 1481         -
    /// Stable connection identity and origin metadata.
 1482         -
    connection: Arc<ConnectionInfo>,
 1483         -
    /// Generation that owns the endpoint.
 1484         -
    generation: H2GenerationId,
 1485         -
}
 1486         -
 1487         -
impl H2EndpointTrace {
 1488         -
    fn new(activation: &H2Activation) -> Self {
 1489         -
        Self {
 1490         -
            request_partition: activation.request_partition,
 1491         -
            connection: activation.connection.info().clone(),
 1492         -
            generation: activation.generation,
 1493         -
        }
 1494         -
    }
 1495         -
}
 1496         -
 1497         -
/// Linear guard for one request-lease endpoint.
 1498         -
pub(in crate::client::pool) struct H2LeaseEndpoint {
 1499         -
    /// Shared request-lease state.
 1500         -
    core: Arc<H2LeaseCore>,
 1501         -
    /// Send or receive side owned by this guard.
 1502         -
    endpoint: H2Endpoint,
 1503         -
    /// Structured fields emitted on terminal completion.
 1504         -
    trace: Option<H2EndpointTrace>,
 1505         -
    /// Whether drop must finish this endpoint.
 1506         -
    active: bool,
 1507         -
}
 1508         -
 1509         -
impl H2LeaseEndpoint {
 1510         -
    fn new(core: Arc<H2LeaseCore>, endpoint: H2Endpoint, trace: Option<H2EndpointTrace>) -> Self {
 1511         -
        Self {
 1512         -
            core,
 1513         -
            endpoint,
 1514         -
            trace,
 1515         -
            active: true,
 1516         -
        }
 1517         -
    }
 1518         -
 1519         -
    /// Creates a detached endpoint and observation handle for body tests.
 1520         -
    #[cfg(all(test, not(smithy_http_client_loom), feature = "rt-tokio"))]
 1521         -
    fn for_test(cell: &Arc<OriginCell>, endpoint: H2Endpoint) -> (Self, H2LeaseProbe) {
 1522         -
        let core = Arc::new(H2LeaseCore {
 1523         -
            cell: Weak::from_arc(cell),
 1524         -
            generation: H2GenerationId(0),
 1525         -
            state: Mutex::new(H2LeaseState::Prospective {
 1526         -
                send_complete: false,
 1527         -
                receive_complete: false,
 1528         -
            }),
 1529         -
        });
 1530         -
        (
 1531         -
            Self::new(core.clone(), endpoint, None),
 1532         -
            H2LeaseProbe { core },
 1533         -
        )
 1534         -
    }
 1535         -
 1536         -
    /// Creates a detached send endpoint and observation handle for body tests.
 1537         -
    #[cfg(all(test, not(smithy_http_client_loom), feature = "rt-tokio"))]
 1538         -
    pub(in crate::client::pool) fn send_for_test(cell: &Arc<OriginCell>) -> (Self, H2LeaseProbe) {
 1539         -
        Self::for_test(cell, H2Endpoint::Send)
 1540         -
    }
 1541         -
 1542         -
    /// Creates a detached receive endpoint and observation handle for body tests.
 1543         -
    #[cfg(all(test, not(smithy_http_client_loom), feature = "rt-tokio"))]
 1544         -
    pub(in crate::client::pool) fn receive_for_test(
 1545         -
        cell: &Arc<OriginCell>,
 1546         -
    ) -> (Self, H2LeaseProbe) {
 1547         -
        Self::for_test(cell, H2Endpoint::Receive)
 1548         -
    }
 1549         -
 1550         -
    /// Completes this endpoint before dropping the guard.
 1551         -
    pub(in crate::client::pool) fn complete(mut self) {
 1552         -
        self.finish();
 1553         -
    }
 1554         -
 1555         -
    fn finish(&mut self) {
 1556         -
        if self.active {
 1557         -
            self.active = false;
 1558         -
            let request_complete = self.core.complete_endpoint(self.endpoint);
 1559         -
            if let Some(trace) = self.trace.take() {
 1560         -
                tracing::trace!(
 1561         -
                    connection_id = %trace.connection.id(),
 1562         -
                    request_partition = ?trace.request_partition,
 1563         -
                    connection_partition = ?trace.connection.owner_partition(),
 1564         -
                    origin_scheme = %trace.connection.origin().scheme(),
 1565         -
                    origin_host = trace.connection.origin().host(),
 1566         -
                    origin_port = ?trace.connection.origin().port(),
 1567         -
                    h2_generation = ?trace.generation,
 1568         -
                    endpoint = ?self.endpoint,
 1569         -
                    request_complete,
 1570         -
                    "HTTP/2 request endpoint completed"
 1571         -
                );
 1572         -
            }
 1573         -
        }
 1574         -
    }
 1575         -
}
 1576         -
 1577         -
impl Drop for H2LeaseEndpoint {
 1578         -
    fn drop(&mut self) {
 1579         -
        self.finish();
 1580         -
    }
 1581         -
}
 1582         -
 1583         -
/// Test observation of a request-lease endpoint's production state.
 1584         -
#[cfg(all(test, not(smithy_http_client_loom), feature = "rt-tokio"))]
 1585         -
pub(in crate::client::pool) struct H2LeaseProbe {
 1586         -
    /// Production lease state observed by the test.
 1587         -
    core: Arc<H2LeaseCore>,
 1588         -
}
 1589         -
 1590         -
#[cfg(all(test, not(smithy_http_client_loom), feature = "rt-tokio"))]
 1591         -
impl H2LeaseProbe {
 1592         -
    /// Returns whether the send endpoint reached its terminal transition.
 1593         -
    pub(in crate::client::pool) fn send_complete(&self) -> bool {
 1594         -
        matches!(
 1595         -
            &*self.core.state.lock(),
 1596         -
            H2LeaseState::Prospective {
 1597         -
                send_complete: true,
 1598         -
                ..
 1599         -
            } | H2LeaseState::Accepted {
 1600         -
                send_complete: true,
 1601         -
                ..
 1602         -
            } | H2LeaseState::Complete
 1603         -
        )
 1604         -
    }
 1605         -
 1606         -
    /// Returns whether the receive endpoint reached its terminal transition.
 1607         -
    pub(in crate::client::pool) fn receive_complete(&self) -> bool {
 1608         -
        matches!(
 1609         -
            &*self.core.state.lock(),
 1610         -
            H2LeaseState::Prospective {
 1611         -
                receive_complete: true,
 1612         -
                ..
 1613         -
            } | H2LeaseState::Accepted {
 1614         -
                receive_complete: true,
 1615         -
                ..
 1616         -
            } | H2LeaseState::Complete
 1617         -
        )
 1618         -
    }
 1619         -
}
 1620         -
 1621   1121   
impl OriginCell {
 1622         -
    /// Reserves a prospective request lease from one exact local generation.
        1122  +
    /// Reserves a prospective request claim from one exact local generation.
 1623   1123   
    pub(in crate::client::pool) fn activate_h2(
 1624   1124   
        cell: &Arc<Self>,
 1625   1125   
        generation: H2GenerationId,
        1126  +
        request_partition: PartitionId,
 1626   1127   
    ) -> Option<H2Activation> {
 1627         -
        let (parts, advertisement) = {
        1128  +
        let (parts, revision) = {
 1628   1129   
            let mut state = cell.state.lock();
 1629   1130   
            let parts = state.h2.activate(generation)?;
 1630   1131   
            state.assert_consistent();
 1631         -
            let advertisement = state.take_h2_advertisement_update();
 1632         -
            (parts, advertisement)
        1132  +
            let revision = state.take_h2_supply_update();
        1133  +
            (parts, revision)
 1633   1134   
        };
 1634         -
        Self::publish_h2_advertisement(cell, advertisement);
 1635         -
        Some(H2Activation::new(cell.clone(), generation, parts, None))
        1135  +
        Self::submit_h2_supply_update(cell, revision);
        1136  +
        Some(H2Activation::new(
        1137  +
            cell.clone(),
        1138  +
            generation,
        1139  +
            parts,
        1140  +
            request_partition,
        1141  +
            None,
        1142  +
        ))
 1636   1143   
    }
 1637   1144   
 1638         -
    /// Converts a prospective activation to an accepted generation lease.
        1145  +
    /// Converts a prospective activation to an accepted request claim.
 1639   1146   
    fn accept_h2_activation(cell: &Arc<Self>, generation: H2GenerationId) -> bool {
 1640   1147   
        let deadline = cell.idle_deadline();
 1641   1148   
        let (accepted, reset_deadline) = {
 1642   1149   
            let mut state = cell.state.lock();
 1643   1150   
            let accepted = state.h2.accept(generation);
 1644   1151   
            let reset_deadline = accepted && state.h2.reset_idle_deadline(generation, deadline);
 1645   1152   
            state.assert_consistent();
 1646   1153   
            (accepted, reset_deadline)
 1647   1154   
        };
 1648   1155   
        if reset_deadline {
 1649   1156   
            cell.notify_maintenance(deadline);
 1650   1157   
        }
 1651   1158   
        accepted
 1652   1159   
    }
 1653   1160   
 1654   1161   
    /// Cancels one prospective activation after sender rejection or task drop.
 1655   1162   
    fn cancel_h2_activation(cell: &Arc<Self>, generation: H2GenerationId) {
 1656   1163   
        // Keep the detached generation outside the lock's unwind scope. Its
 1657   1164   
        // sender drop may wake Hyper's driver.
 1658         -
        let (removed, advertisement) = {
        1165  +
        let (removed, revision) = {
 1659   1166   
            let mut state = cell.state.lock();
 1660   1167   
            let removed = state.h2.cancel(generation);
 1661   1168   
            state.assert_consistent();
 1662         -
            let advertisement = state.take_h2_advertisement_update();
 1663         -
            (removed, advertisement)
        1169  +
            let revision = state.take_h2_supply_update();
        1170  +
            (removed, revision)
 1664   1171   
        };
 1665   1172   
        drop(removed);
 1666         -
        Self::publish_h2_advertisement(cell, advertisement);
 1667         -
        Self::service_h2_waiters(cell);
        1173  +
        Self::submit_h2_supply_update(cell, revision);
        1174  +
        Self::offer_local_h2(cell);
 1668   1175   
    }
 1669   1176   
 1670         -
    /// Releases one accepted lease after its second endpoint terminates.
 1671         -
    fn complete_h2_request(cell: &Arc<Self>, generation: H2GenerationId) {
        1177  +
    /// Releases one accepted request after its upload and response sides finish.
        1178  +
    fn release_h2_request(cell: &Arc<Self>, generation: H2GenerationId) {
 1672   1179   
        // Keep the detached generation outside the lock's unwind scope. Its
 1673   1180   
        // sender drop may wake Hyper's driver.
 1674         -
        let (removed, advertisement) = {
        1181  +
        let (removed, revision) = {
 1675   1182   
            let mut state = cell.state.lock();
 1676   1183   
            let removed = state.h2.complete_request(generation);
 1677   1184   
            state.assert_consistent();
 1678         -
            let advertisement = state.take_h2_advertisement_update();
 1679         -
            (removed, advertisement)
        1185  +
            let revision = state.take_h2_supply_update();
        1186  +
            (removed, revision)
 1680   1187   
        };
 1681   1188   
        drop(removed);
 1682         -
        Self::publish_h2_advertisement(cell, advertisement);
        1189  +
        Self::submit_h2_supply_update(cell, revision);
 1683   1190   
    }
 1684   1191   
 1685   1192   
    /// Offers one local activation while preserving the generation cutoff.
 1686         -
    fn service_h2_gate_locked(
        1193  +
    fn offer_local_h2_locked(
 1687   1194   
        cell: &Arc<Self>,
 1688   1195   
        state: &mut CellState,
 1689         -
        returned_event: &mut Option<AcquisitionEvent>,
 1690         -
    ) -> Option<WaiterInstall> {
        1196  +
        returned_step: &mut Option<AcquisitionStep>,
        1197  +
    ) -> Option<WaiterResolution> {
 1691   1198   
        let generation = state.h2.accepting()?;
 1692   1199   
        let has_prioritized = state
 1693   1200   
            .h2
 1694   1201   
            .priority_cutoff()
 1695         -
            .is_some_and(|cutoff| state.waiters.has_h2_candidate_through(cutoff));
 1696         -
        let cutoff = match state.h2.next_gate_turn(has_prioritized) {
 1697         -
            GateTurn::Unavailable => return None,
 1698         -
            GateTurn::Open => None,
 1699         -
            GateTurn::Through(cutoff) => Some(cutoff),
        1202  +
            .is_some_and(|cutoff| state.acquisitions.has_h2_compatible_waiter_through(cutoff));
        1203  +
        let cutoff = match state.h2.take_next_turn(has_prioritized) {
        1204  +
            H2ActivationTurn::Unavailable => return None,
        1205  +
            H2ActivationTurn::Open => None,
        1206  +
            H2ActivationTurn::Through(cutoff) => Some(cutoff),
 1700   1207   
        };
 1701         -
        state.waiters.oldest_h2_candidate()?;
        1208  +
        state.acquisitions.oldest_h2_compatible_waiter()?;
 1702   1209   
 1703   1210   
        let h2 = &mut state.h2;
 1704         -
        let waiters = &mut state.waiters;
 1705         -
        let mut install = waiters.install_h2(
        1211  +
        let acquisitions = &mut state.acquisitions;
        1212  +
        let (waiter, mut install) = acquisitions.offer_h2_activation(
 1706   1213   
            cutoff,
 1707   1214   
            |waiter| {
 1708   1215   
                let parts = h2
 1709   1216   
                    .activate(generation)
 1710   1217   
                    .expect("accepting HTTP/2 generation could not create a gated activation");
 1711         -
                let gated = h2.begin_gate_activation(waiter);
 1712         -
                AcquisitionResult::H2(H2Activation::new(
        1218  +
                let gated = h2.reserve_activation_turn(waiter);
        1219  +
                AcquisitionOutcome::H2(H2Activation::new(
 1713   1220   
                    cell.clone(),
 1714   1221   
                    generation,
 1715   1222   
                    parts,
 1716         -
                    gated.then(|| H2GateToken::local(cell, generation, waiter)),
        1223  +
                    cell.id.partition(),
        1224  +
                    gated.then(|| H2ActivationTurnGuard::local(cell, generation, waiter)),
 1717   1225   
                ))
 1718   1226   
            },
 1719   1227   
            &cell.eligibility_group,
 1720   1228   
        );
 1721         -
        *returned_event = install.returned_event.take();
        1229  +
        *returned_step = install.returned_step.take();
 1722   1230   
        install.demand_updates = state.publishable_demand_updates(install.demand_updates);
 1723   1231   
        state.assert_consistent();
 1724         -
        install.waiter.is_some().then_some(install)
        1232  +
        waiter.is_some().then_some(install)
 1725   1233   
    }
 1726   1234   
 1727   1235   
    /// Runs publication, fallback, and wake work after the cell lock is released.
 1728         -
    fn finish_h2_install(cell: &Arc<Self>, install: Option<WaiterInstall>) {
 1729         -
        let Some(install) = install else {
        1236  +
    fn run_waiter_resolution(cell: &Arc<Self>, resolution: Option<WaiterResolution>) {
        1237  +
        let Some(resolution) = resolution else {
 1730   1238   
            return;
 1731   1239   
        };
 1732   1240   
        if let Some(admission) = &cell.admission {
 1733         -
            for snapshot in install.demand_updates.into_iter().flatten() {
 1734         -
                super::super::admission::OriginAdmission::publish_demand(
 1735         -
                    admission,
 1736         -
                    cell.id.partition(),
 1737         -
                    snapshot,
 1738         -
                );
        1241  +
            for snapshot in resolution.demand_updates.into_iter().flatten() {
        1242  +
                OriginAdmission::submit_demand_snapshot(admission, cell.id.partition(), snapshot);
 1739   1243   
            }
 1740   1244   
        }
 1741         -
        drop(install.returned_event);
 1742         -
        if let Some(waker) = install.waker {
        1245  +
        drop(resolution.returned_step);
        1246  +
        if let Some(waker) = resolution.waker {
 1743   1247   
            waker.wake();
 1744   1248   
        }
 1745   1249   
    }
 1746   1250   
 1747         -
    /// Publishes one complete local-generation report after cell unlock.
 1748         -
    fn publish_h2_advertisement(
 1749         -
        cell: &Arc<Self>,
 1750         -
        snapshot: Option<super::super::admission::H2AdvertisementSnapshot>,
 1751         -
    ) {
 1752         -
        if let (Some(admission), Some(snapshot)) = (&cell.admission, snapshot) {
 1753         -
            super::super::admission::OriginAdmission::update_h2_advertisement(
        1251  +
    /// Submits one changed local-generation status after cell unlock.
        1252  +
    fn submit_h2_supply_update(cell: &Arc<Self>, revision: Option<SupplyRevision<H2SupplyStatus>>) {
        1253  +
        if let (Some(admission), Some(revision)) = (&cell.admission, revision) {
        1254  +
            OriginAdmission::apply_h2_supply_revision(
 1754   1255   
                admission,
 1755   1256   
                cell.id.partition(),
 1756   1257   
                cell.eligibility_group.clone(),
 1757         -
                snapshot,
        1258  +
                revision,
 1758   1259   
            );
 1759   1260   
        }
 1760   1261   
    }
 1761   1262   
 1762   1263   
    /// Publishes the current local demand after its last H2 route disappears.
 1763         -
    fn publish_current_demand(
 1764         -
        cell: &Arc<Self>,
 1765         -
        snapshot: Option<super::super::admission::DemandSnapshot>,
 1766         -
    ) {
        1264  +
    fn publish_current_demand(cell: &Arc<Self>, snapshot: Option<DemandSnapshot>) {
 1767   1265   
        if let (Some(admission), Some(snapshot)) = (&cell.admission, snapshot) {
 1768         -
            super::super::admission::OriginAdmission::publish_demand(
 1769         -
                admission,
 1770         -
                cell.id.partition(),
 1771         -
                snapshot,
 1772         -
            );
        1266  +
            OriginAdmission::submit_demand_snapshot(admission, cell.id.partition(), snapshot);
 1773   1267   
        }
 1774   1268   
    }
 1775   1269   
 1776   1270   
    /// Advances a generation gate after one activation accepts or cancels.
 1777         -
    fn finish_h2_gate(cell: &Arc<Self>, generation: H2GenerationId, waiter: WaiterId) {
 1778         -
        let mut returned_event = None;
 1779         -
        let (install, advertisement) = {
        1271  +
    fn release_local_h2_turn(cell: &Arc<Self>, generation: H2GenerationId, waiter: WaiterId) {
        1272  +
        let mut returned_step = None;
        1273  +
        let (install, revision) = {
 1780   1274   
            let mut state = cell.state.lock();
 1781         -
            if !state.h2.finish_gate_activation(generation, waiter) {
        1275  +
            if !state.h2.release_activation_turn(generation, waiter) {
 1782   1276   
                return;
 1783   1277   
            }
 1784         -
            let install = Self::service_h2_gate_locked(cell, &mut state, &mut returned_event);
        1278  +
            let install = Self::offer_local_h2_locked(cell, &mut state, &mut returned_step);
 1785   1279   
            state.assert_consistent();
 1786         -
            let advertisement = state.take_h2_advertisement_update();
 1787         -
            (install, advertisement)
        1280  +
            let revision = state.take_h2_supply_update();
        1281  +
            (install, revision)
 1788   1282   
        };
 1789         -
        drop(returned_event);
 1790         -
        Self::publish_h2_advertisement(cell, advertisement);
 1791         -
        Self::finish_h2_install(cell, install);
        1283  +
        drop(returned_step);
        1284  +
        Self::submit_h2_supply_update(cell, revision);
        1285  +
        Self::run_waiter_resolution(cell, install);
 1792   1286   
    }
 1793   1287   
 1794   1288   
    /// Offers the accepting local generation to one queued acquisition.
 1795         -
    pub(in crate::client::pool) fn service_h2_waiters(cell: &Arc<Self>) {
 1796         -
        let mut returned_event = None;
 1797         -
        let (install, advertisement) = {
        1289  +
    pub(in crate::client::pool) fn offer_local_h2(cell: &Arc<Self>) {
        1290  +
        let mut returned_step = None;
        1291  +
        let (install, revision) = {
 1798   1292   
            let mut state = cell.state.lock();
 1799         -
            let install = Self::service_h2_gate_locked(cell, &mut state, &mut returned_event);
        1293  +
            let install = Self::offer_local_h2_locked(cell, &mut state, &mut returned_step);
 1800   1294   
            state.assert_consistent();
 1801         -
            let advertisement = state.take_h2_advertisement_update();
 1802         -
            (install, advertisement)
        1295  +
            let revision = state.take_h2_supply_update();
        1296  +
            (install, revision)
 1803   1297   
        };
 1804         -
        drop(returned_event);
 1805         -
        Self::publish_h2_advertisement(cell, advertisement);
 1806         -
        Self::finish_h2_install(cell, install);
        1298  +
        drop(returned_step);
        1299  +
        Self::submit_h2_supply_update(cell, revision);
        1300  +
        Self::run_waiter_resolution(cell, install);
 1807   1301   
    }
 1808   1302   
 1809   1303   
    /// Returns whether one exact connection generation still accepts activations.
 1810   1304   
    pub(in crate::client::pool) fn h2_generation_is_accepting(
 1811   1305   
        cell: &Arc<Self>,
 1812   1306   
        generation: H2GenerationId,
 1813   1307   
    ) -> bool {
 1814   1308   
        cell.state.lock().h2.is_accepting(generation)
 1815   1309   
    }
 1816   1310   
 1817         -
    /// Installs requesting-cell visibility for one peer generation publication.
        1311  +
    /// Attaches requesting-cell visibility for one peer generation route.
 1818   1312   
    ///
 1819   1313   
    /// The named local demand remains queued behind the route gate. Advancing
 1820   1314   
    /// its snapshot version makes admission acknowledgement authoritative
 1821   1315   
    /// without losing the demand if this exact route later becomes stale.
 1822         -
    pub(in crate::client::pool) fn install_h2_route(
        1316  +
    pub(in crate::client::pool) fn attach_h2_route(
 1823   1317   
        cell: &Arc<Self>,
 1824   1318   
        route: H2Route,
 1825         -
        group: &super::super::partition::EligibilityGroup,
 1826         -
        demand: super::super::admission::DemandId,
        1319  +
        group: &EligibilityGroup,
        1320  +
        demand: DemandId,
 1827   1321   
    ) -> bool {
 1828   1322   
        if &cell.eligibility_group != group || route.connection_partition() == cell.id.partition() {
 1829   1323   
            return false;
 1830   1324   
        }
 1831   1325   
        let mut state = cell.state.lock();
 1832         -
        if !state.waiters.suppress_published_demand(demand) {
        1326  +
        if !state.acquisitions.supersede_demand_snapshot(demand) {
 1833   1327   
            return false;
 1834   1328   
        }
 1835         -
        let cutoff = state.waiters.publication_cutoff();
 1836         -
        state.h2.install_peer_route(route, cutoff);
        1329  +
        let cutoff = state.acquisitions.route_cutoff();
        1330  +
        state.h2.attach_peer_route(route, cutoff);
 1837   1331   
        state.assert_consistent();
 1838   1332   
        true
 1839   1333   
    }
 1840   1334   
 1841   1335   
    /// Activates queued requests through the currently visible peer route.
 1842   1336   
    ///
 1843   1337   
    /// Preparing the requesting-cell opportunity, activating the connection
 1844   1338   
    /// generation, and committing the result each use a separate lock scope.
 1845         -
    pub(in crate::client::pool) fn service_peer_h2_waiters(cell: &Arc<Self>) {
        1339  +
    pub(in crate::client::pool) fn offer_peer_h2(cell: &Arc<Self>) {
 1846   1340   
        loop {
 1847   1341   
            let prepared = {
 1848   1342   
                let mut state = cell.state.lock();
 1849         -
                let CellState { h2, waiters, .. } = &mut *state;
 1850         -
                let prepared = h2.prepare_peer_activation(waiters);
        1343  +
                let CellState {
        1344  +
                    h2, acquisitions, ..
        1345  +
                } = &mut *state;
        1346  +
                let prepared = h2.prepare_peer_activation(acquisitions);
 1851   1347   
                state.assert_consistent();
 1852   1348   
                prepared
 1853   1349   
            };
 1854   1350   
            let Some(prepared) = prepared else {
 1855   1351   
                return;
 1856   1352   
            };
 1857   1353   
            let route_id = prepared.route.id();
 1858   1354   
            let Some(activation) = prepared.route.activate(cell.id.partition()) else {
 1859   1355   
                let snapshot = {
 1860   1356   
                    let mut state = cell.state.lock();
 1861         -
                    if !state.h2.clear_peer_route(route_id) {
        1357  +
                    if !state.h2.detach_peer_route(route_id) {
 1862   1358   
                        return;
 1863   1359   
                    }
 1864   1360   
                    let snapshot = state
 1865         -
                        .waiters
        1361  +
                        .acquisitions
 1866   1362   
                        .current_demand_snapshot(&cell.eligibility_group);
 1867   1363   
                    state.assert_consistent();
 1868   1364   
                    snapshot
 1869   1365   
                };
 1870   1366   
                if let (Some(admission), Some(snapshot)) = (&cell.admission, snapshot) {
 1871         -
                    super::super::admission::OriginAdmission::publish_demand(
        1367  +
                    OriginAdmission::submit_demand_snapshot(
 1872   1368   
                        admission,
 1873   1369   
                        cell.id.partition(),
 1874   1370   
                        snapshot,
 1875   1371   
                    );
 1876   1372   
                }
 1877   1373   
                return;
 1878   1374   
            };
 1879   1375   
 1880   1376   
            let mut activation = Some(activation);
 1881         -
            let mut returned_event = None;
        1377  +
            let mut returned_step = None;
 1882   1378   
            let install = {
 1883   1379   
                let mut state = cell.state.lock();
 1884   1380   
                let current = state
 1885   1381   
                    .h2
 1886         -
                    .peer_activation_is_current(&prepared, &state.waiters);
        1382  +
                    .peer_activation_is_current(&prepared, &state.acquisitions);
 1887   1383   
                if !current {
 1888         -
                    state.h2.finish_peer_crossing(route_id, prepared.waiter);
        1384  +
                    state.h2.release_peer_crossing(route_id, prepared.waiter);
 1889   1385   
                    if prepared.gated {
 1890         -
                        state.h2.finish_peer_gate(route_id, prepared.waiter);
        1386  +
                        state.h2.release_peer_turn(route_id, prepared.waiter);
 1891   1387   
                    }
 1892   1388   
                    state.assert_consistent();
 1893   1389   
                    None
 1894   1390   
                } else {
 1895   1391   
                    if prepared.gated {
 1896   1392   
                        activation
 1897   1393   
                            .as_mut()
 1898   1394   
                            .expect("peer HTTP/2 activation disappeared")
 1899         -
                            .attach_peer_gate(cell, route_id, prepared.waiter);
        1395  +
                            .attach_peer_turn(cell, route_id, prepared.waiter);
 1900   1396   
                    }
 1901         -
                    let mut install = state.waiters.install_h2(
        1397  +
                    let (waiter, mut install) = state.acquisitions.offer_h2_activation(
 1902   1398   
                        prepared.cutoff,
 1903   1399   
                        |_| {
 1904         -
                            AcquisitionResult::H2(
        1400  +
                            AcquisitionOutcome::H2(
 1905   1401   
                                activation
 1906   1402   
                                    .take()
 1907   1403   
                                    .expect("peer HTTP/2 activation was installed twice"),
 1908   1404   
                            )
 1909   1405   
                        },
 1910   1406   
                        &cell.eligibility_group,
 1911   1407   
                    );
 1912         -
                    returned_event = install.returned_event.take();
        1408  +
                    returned_step = install.returned_step.take();
 1913   1409   
                    install.demand_updates =
 1914   1410   
                        state.publishable_demand_updates(install.demand_updates);
 1915         -
                    state.h2.finish_peer_crossing(route_id, prepared.waiter);
 1916         -
                    if install.waiter.is_none() && prepared.gated {
 1917         -
                        state.h2.finish_peer_gate(route_id, prepared.waiter);
        1411  +
                    state.h2.release_peer_crossing(route_id, prepared.waiter);
        1412  +
                    if waiter.is_none() && prepared.gated {
        1413  +
                        state.h2.release_peer_turn(route_id, prepared.waiter);
 1918   1414   
                    }
 1919   1415   
                    state.assert_consistent();
 1920         -
                    install.waiter.is_some().then_some(install)
        1416  +
                    waiter.is_some().then_some(install)
 1921   1417   
                }
 1922   1418   
            };
 1923   1419   
            drop(activation);
 1924         -
            drop(returned_event);
        1420  +
            drop(returned_step);
 1925   1421   
            if install.is_some() {
 1926         -
                Self::finish_h2_install(cell, install);
        1422  +
                Self::run_waiter_resolution(cell, install);
 1927   1423   
                return;
 1928   1424   
            }
 1929   1425   
        }
 1930   1426   
    }
 1931   1427   
 1932   1428   
    /// Advances an exact peer-route gate after acceptance or cancellation.
 1933         -
    fn finish_peer_h2_gate(cell: &Arc<Self>, route: H2RouteId, waiter: WaiterId) {
        1429  +
    fn release_peer_h2_turn(cell: &Arc<Self>, route: H2RouteId, waiter: WaiterId) {
 1934   1430   
        let finished = {
 1935   1431   
            let mut state = cell.state.lock();
 1936         -
            let finished = state.h2.finish_peer_gate(route, waiter);
        1432  +
            let finished = state.h2.release_peer_turn(route, waiter);
 1937   1433   
            state.assert_consistent();
 1938   1434   
            finished
 1939   1435   
        };
 1940   1436   
        if finished {
 1941         -
            Self::service_peer_h2_waiters(cell);
        1437  +
            Self::offer_peer_h2(cell);
 1942   1438   
        }
 1943   1439   
    }
 1944   1440   
}
 1945   1441   
 1946   1442   
/// Generation-specific close authority that does not retain the cell.
 1947   1443   
#[derive(Clone, Debug)]
 1948   1444   
pub(in crate::client::pool) struct H2CloseHandle {
 1949   1445   
    /// Cell weak reference so driver lifetime does not retain the pool.
 1950   1446   
    cell: Weak<OriginCell>,
 1951   1447   
    /// Exact generation this handle may close.
 1952   1448   
    generation: H2GenerationId,
 1953   1449   
}
 1954   1450   
 1955   1451   
impl H2CloseHandle {
 1956   1452   
    /// Creates close authority for one installed generation.
 1957   1453   
    pub(in crate::client::pool) fn new(cell: &Arc<OriginCell>, generation: H2GenerationId) -> Self {
 1958   1454   
        Self {
 1959   1455   
            cell: Weak::from_arc(cell),
 1960   1456   
            generation,
 1961   1457   
        }
 1962   1458   
    }
 1963   1459   
 1964   1460   
    /// Begins drain when the cell still contains this generation.
 1965         -
    pub(in crate::client::pool) fn close(
 1966         -
        &self,
 1967         -
        reason: super::super::connection::CloseReason,
 1968         -
    ) -> bool {
        1461  +
    pub(in crate::client::pool) fn close(&self, reason: CloseReason) -> bool {
 1969   1462   
        self.cell
 1970   1463   
            .upgrade()
 1971   1464   
            .is_some_and(|cell| OriginCell::close_h2(&cell, self.generation, reason))
 1972   1465   
    }
 1973   1466   
}
 1974   1467   
 1975   1468   
/// Closes an H2 generation if its owner-runtime driver ends or is dropped.
 1976   1469   
pub(in crate::client::pool) struct H2DriverGuard {
 1977   1470   
    /// Exact-generation close authority.
 1978   1471   
    close: H2CloseHandle,
 1979   1472   
    /// Whether drop must report owner-runtime shutdown.
 1980   1473   
    active: bool,
 1981   1474   
}
 1982   1475   
 1983   1476   
impl H2DriverGuard {
 1984   1477   
    /// Arms generation cleanup before driver submission.
 1985   1478   
    pub(in crate::client::pool) fn new(close: H2CloseHandle) -> Self {
 1986   1479   
        Self {
 1987   1480   
            close,
 1988   1481   
            active: true,
 1989   1482   
        }
 1990   1483   
    }
 1991   1484   
 1992   1485   
    /// Records ordinary driver completion.
 1993   1486   
    pub(in crate::client::pool) fn protocol_closed(mut self) {
 1994   1487   
        self.active = false;
 1995         -
        self.close
 1996         -
            .close(super::super::connection::CloseReason::ProtocolClosed);
        1488  +
        self.close.close(CloseReason::ProtocolClosed);
 1997   1489   
    }
 1998   1490   
}
 1999   1491   
 2000   1492   
impl Drop for H2DriverGuard {
 2001   1493   
    fn drop(&mut self) {
 2002   1494   
        if self.active {
 2003         -
            self.close
 2004         -
                .close(super::super::connection::CloseReason::OwnerRuntimeShutdown);
        1495  +
            self.close.close(CloseReason::OwnerRuntimeShutdown);
 2005   1496   
        }
 2006   1497   
    }
 2007   1498   
}
 2008   1499   
 2009   1500   
impl OriginCell {
 2010   1501   
    /// Selects an accepting local generation before consulting a peer route.
 2011   1502   
    pub(in crate::client::pool) fn select_h2(cell: &Arc<Self>) -> Option<H2Activation> {
 2012   1503   
        let local = {
 2013   1504   
            let mut state = cell.state.lock();
 2014         -
            let queued = state.waiters.has_h2_candidate();
        1505  +
            let queued = state.acquisitions.has_h2_compatible_waiter();
 2015   1506   
            if state.h2.direct_is_allowed(queued) {
 2016   1507   
                let generation = state.h2.accepting()?;
 2017   1508   
                let parts = state.h2.activate(generation)?;
 2018   1509   
                state.assert_consistent();
 2019         -
                let advertisement = state.take_h2_advertisement_update();
 2020         -
                Some((generation, parts, advertisement))
        1510  +
                let revision = state.take_h2_supply_update();
        1511  +
                Some((generation, parts, revision))
 2021   1512   
            } else {
 2022   1513   
                None
 2023   1514   
            }
 2024   1515   
        };
 2025         -
        if let Some((generation, parts, advertisement)) = local {
 2026         -
            Self::publish_h2_advertisement(cell, advertisement);
 2027         -
            return Some(H2Activation::new(cell.clone(), generation, parts, None));
        1516  +
        if let Some((generation, parts, revision)) = local {
        1517  +
            Self::submit_h2_supply_update(cell, revision);
        1518  +
            return Some(H2Activation::new(
        1519  +
                cell.clone(),
        1520  +
                generation,
        1521  +
                parts,
        1522  +
                cell.id.partition(),
        1523  +
                None,
        1524  +
            ));
 2028   1525   
        }
 2029   1526   
 2030   1527   
        let route = {
 2031   1528   
            let state = cell.state.lock();
 2032         -
            state.h2.open_peer_route(state.waiters.has_h2_candidate())?
        1529  +
            state
        1530  +
                .h2
        1531  +
                .open_peer_route(state.acquisitions.has_h2_compatible_waiter())?
 2033   1532   
        };
 2034   1533   
        let route_id = route.id();
 2035   1534   
        let Some(activation) = route.activate(cell.id.partition()) else {
 2036         -
            Self::clear_stale_peer_route(cell, route_id);
        1535  +
            Self::detach_stale_peer_route(cell, route_id);
 2037   1536   
            return None;
 2038   1537   
        };
 2039   1538   
        let current = {
 2040   1539   
            let state = cell.state.lock();
 2041         -
            state
 2042         -
                .h2
 2043         -
                .direct_peer_route_is_current(route_id, state.waiters.has_h2_candidate())
        1540  +
            state.h2.direct_peer_route_is_current(
        1541  +
                route_id,
        1542  +
                state.acquisitions.has_h2_compatible_waiter(),
        1543  +
            )
 2044   1544   
        };
 2045   1545   
        current.then_some(activation)
 2046   1546   
    }
 2047   1547   
 2048   1548   
    /// Removes one exact stale route and republishes the requesting demand.
 2049         -
    fn clear_stale_peer_route(cell: &Arc<Self>, route: H2RouteId) {
        1549  +
    fn detach_stale_peer_route(cell: &Arc<Self>, route: H2RouteId) {
 2050   1550   
        let snapshot = {
 2051   1551   
            let mut state = cell.state.lock();
 2052         -
            if !state.h2.clear_peer_route(route) {
        1552  +
            if !state.h2.detach_peer_route(route) {
 2053   1553   
                return;
 2054   1554   
            }
 2055   1555   
            let snapshot = state
 2056         -
                .waiters
        1556  +
                .acquisitions
 2057   1557   
                .current_demand_snapshot(&cell.eligibility_group);
 2058   1558   
            state.assert_consistent();
 2059   1559   
            snapshot
 2060   1560   
        };
 2061   1561   
        Self::publish_current_demand(cell, snapshot);
 2062   1562   
    }
 2063   1563   
 2064   1564   
    /// Places a post-ALPN waiter behind an already accepting generation.
 2065   1565   
    pub(in crate::client::pool) fn join_h2_generation(
 2066   1566   
        cell: &Arc<Self>,
 2067   1567   
        waiter: WaiterId,
 2068   1568   
        generation: H2GenerationId,
 2069         -
    ) -> H2GenerationJoin {
 2070         -
        let mut returned_event = None;
 2071         -
        let (install, advertisement) = {
        1569  +
    ) -> H2GenerationJoinOutcome {
        1570  +
        let mut returned_step = None;
        1571  +
        let (install, revision) = {
 2072   1572   
            let mut state = cell.state.lock();
 2073         -
            if !state.waiters.is_launching_h2_candidate(waiter) {
 2074         -
                return H2GenerationJoin::WaiterCompleted;
        1573  +
            if !state.acquisitions.is_launching_h2_waiter(waiter) {
        1574  +
                return H2GenerationJoinOutcome::WaiterResolved;
 2075   1575   
            }
 2076   1576   
            if !state.h2.prioritize_waiter(generation, waiter) {
 2077         -
                return H2GenerationJoin::GenerationChanged;
        1577  +
                return H2GenerationJoinOutcome::GenerationChanged;
 2078   1578   
            }
 2079         -
            let install = Self::service_h2_gate_locked(cell, &mut state, &mut returned_event);
        1579  +
            let install = Self::offer_local_h2_locked(cell, &mut state, &mut returned_step);
 2080   1580   
            state.assert_consistent();
 2081         -
            let advertisement = state.take_h2_advertisement_update();
 2082         -
            (install, advertisement)
        1581  +
            let revision = state.take_h2_supply_update();
        1582  +
            (install, revision)
 2083   1583   
        };
 2084         -
        drop(returned_event);
 2085         -
        Self::publish_h2_advertisement(cell, advertisement);
 2086         -
        Self::finish_h2_install(cell, install);
 2087         -
        H2GenerationJoin::Joined
        1584  +
        drop(returned_step);
        1585  +
        Self::submit_h2_supply_update(cell, revision);
        1586  +
        Self::run_waiter_resolution(cell, install);
        1587  +
        H2GenerationJoinOutcome::Joined
 2088   1588   
    }
 2089   1589   
 2090   1590   
    /// Atomically selects, joins, or installs the cell's post-ALPN flight.
 2091         -
    pub(in crate::client::pool) fn install_or_join_h2_flight(
 2092         -
        &self,
 2093         -
        waiter: WaiterId,
 2094         -
    ) -> H2FlightInstall {
        1591  +
    pub(in crate::client::pool) fn converge_h2_flight(&self, waiter: WaiterId) -> H2FlightDecision {
 2095   1592   
        let mut state = self.state.lock();
 2096         -
        let result = state.h2.install_or_join_flight(waiter);
        1593  +
        let result = state.h2.converge_flight(waiter);
 2097   1594   
        state.assert_consistent();
 2098   1595   
        result
 2099   1596   
    }
 2100   1597   
 2101   1598   
    /// Installs one successful flight as the accepting generation.
 2102   1599   
    pub(in crate::client::pool) fn complete_h2_flight(
 2103   1600   
        cell: &Arc<Self>,
 2104   1601   
        flight: H2FlightId,
 2105   1602   
        connection: Arc<ConnectionState>,
 2106   1603   
        sender: H2Sender,
 2107   1604   
        idle_deadline: Option<SystemTime>,
 2108   1605   
    ) -> Result<H2GenerationId, (Arc<ConnectionState>, H2Sender)> {
 2109         -
        let mut returned_event = None;
 2110         -
        let (completion, install, advertisement) = {
        1606  +
        let mut returned_step = None;
        1607  +
        let (completion, install, revision) = {
 2111   1608   
            let mut state = cell.state.lock();
 2112   1609   
            let completion = state
 2113   1610   
                .h2
 2114   1611   
                .complete_flight(flight, connection, sender, idle_deadline);
 2115   1612   
            let install = if completion.is_ok() {
 2116         -
                let cutoff = state.waiters.publication_cutoff();
        1613  +
                let cutoff = state.acquisitions.route_cutoff();
 2117   1614   
                state.h2.prioritize_through(cutoff);
 2118         -
                Self::service_h2_gate_locked(cell, &mut state, &mut returned_event)
        1615  +
                Self::offer_local_h2_locked(cell, &mut state, &mut returned_step)
 2119   1616   
            } else {
 2120   1617   
                None
 2121   1618   
            };
 2122   1619   
            state.assert_consistent();
 2123         -
            let advertisement = state.take_h2_advertisement_update();
 2124         -
            (completion, install, advertisement)
        1620  +
            let revision = state.take_h2_supply_update();
        1621  +
            (completion, install, revision)
 2125   1622   
        };
 2126         -
        drop(returned_event);
 2127         -
        Self::publish_h2_advertisement(cell, advertisement);
 2128         -
        Self::finish_h2_install(cell, install);
        1623  +
        drop(returned_step);
        1624  +
        Self::submit_h2_supply_update(cell, revision);
        1625  +
        Self::run_waiter_resolution(cell, install);
 2129   1626   
        if completion.is_ok() {
 2130   1627   
            cell.notify_maintenance(idle_deadline);
 2131   1628   
        }
 2132   1629   
        completion
 2133   1630   
    }
 2134   1631   
 2135   1632   
    /// Retires one failed flight and returns its retained participants.
 2136   1633   
    pub(in crate::client::pool) fn fail_h2_flight(
 2137   1634   
        &self,
 2138   1635   
        flight: H2FlightId,
 2139   1636   
    ) -> Option<Vec<WaiterId>> {
 2140   1637   
        let mut state = self.state.lock();
 2141   1638   
        let participants = state.h2.fail_flight(flight);
 2142   1639   
        state.assert_consistent();
 2143   1640   
        participants
 2144   1641   
    }
 2145   1642   
 2146         -
    /// Returns the complete current advertisement for an unlocked crossing.
 2147         -
    pub(in crate::client::pool) fn report_h2_advertisement(
        1643  +
    /// Returns the complete current H2 supply revision for an unlocked crossing.
        1644  +
    pub(in crate::client::pool) fn current_h2_supply_revision(
 2148   1645   
        &self,
 2149         -
    ) -> super::super::admission::H2AdvertisementSnapshot {
        1646  +
    ) -> SupplyRevision<H2SupplyStatus> {
 2150   1647   
        let mut state = self.state.lock();
 2151         -
        let snapshot = state.report_h2_advertisement();
        1648  +
        let revision = state.current_h2_supply_revision();
 2152   1649   
        state.assert_consistent();
 2153         -
        snapshot
        1650  +
        revision
 2154   1651   
    }
 2155   1652   
 2156   1653   
    /// Reclaims one exact idle generation and reports its resulting availability.
 2157   1654   
    pub(in crate::client::pool) fn reclaim_idle_h2(
 2158   1655   
        cell: &Arc<Self>,
 2159   1656   
        generation: H2GenerationId,
 2160         -
    ) -> (
 2161         -
        super::super::admission::H2AdvertisementSnapshot,
 2162         -
        Option<ConnectionId>,
 2163         -
    ) {
        1657  +
    ) -> (SupplyRevision<H2SupplyStatus>, Option<ConnectionId>) {
 2164   1658   
        // A zero-request generation may hold the last capacity-owning
 2165   1659   
        // connection reference. Keep it outside the cell-lock unwind scope.
 2166   1660   
        let detached;
 2167         -
        let (advertisement, demand) = {
        1661  +
        let (revision, demand) = {
 2168   1662   
            let mut state = cell.state.lock();
 2169   1663   
            detached = state.h2.begin_idle_reclaim(generation);
 2170         -
            let advertisement = state.report_h2_advertisement();
        1664  +
            let revision = state.current_h2_supply_revision();
 2171   1665   
            let demand = detached.as_ref().and_then(|_| {
 2172   1666   
                state
 2173         -
                    .waiters
        1667  +
                    .acquisitions
 2174   1668   
                    .current_demand_snapshot(&cell.eligibility_group)
 2175   1669   
            });
 2176   1670   
            state.assert_consistent();
 2177         -
            (advertisement, demand)
        1671  +
            (revision, demand)
 2178   1672   
        };
 2179   1673   
        let Some(detached) = detached else {
 2180         -
            return (advertisement, None);
        1674  +
            return (revision, None);
 2181   1675   
        };
 2182   1676   
        let H2CloseTransition {
 2183   1677   
            connection,
 2184   1678   
            pending_waiters,
 2185   1679   
            removed_generation,
 2186   1680   
        } = detached;
 2187   1681   
        assert!(
 2188   1682   
            pending_waiters.is_empty(),
 2189   1683   
            "idle HTTP/2 reclaim detached pending generation waiters"
 2190   1684   
        );
 2191   1685   
        let connection_id = connection.id();
 2192   1686   
        drop(removed_generation);
 2193   1687   
        Self::publish_current_demand(cell, demand);
 2194         -
        let reclaimed = connection.logical_close(super::super::connection::CloseReason::Reclaimed);
 2195         -
        (advertisement, reclaimed.then_some(connection_id))
        1688  +
        let reclaimed = connection.logical_close(CloseReason::Reclaimed);
        1689  +
        (revision, reclaimed.then_some(connection_id))
 2196   1690   
    }
 2197   1691   
 2198   1692   
    /// Moves one exact generation to draining and closes its connection.
 2199   1693   
    pub(super) fn close_h2(
 2200   1694   
        cell: &Arc<Self>,
 2201   1695   
        generation: H2GenerationId,
 2202         -
        reason: super::super::connection::CloseReason,
        1696  +
        reason: CloseReason,
 2203   1697   
    ) -> bool {
 2204   1698   
        // A zero-request generation may hold the last capacity-owning
 2205   1699   
        // connection reference. Keep it outside the cell-lock unwind scope.
 2206   1700   
        let detached;
 2207         -
        let (advertisement, demand) = {
        1701  +
        let (revision, demand) = {
 2208   1702   
            let mut state = cell.state.lock();
 2209   1703   
            detached = state.h2.begin_close(generation);
 2210         -
            let advertisement = detached
        1704  +
            let revision = detached
 2211   1705   
                .as_ref()
 2212         -
                .and_then(|_| state.take_h2_advertisement_update());
        1706  +
                .and_then(|_| state.take_h2_supply_update());
 2213   1707   
            let demand = detached.as_ref().and_then(|_| {
 2214   1708   
                state
 2215         -
                    .waiters
        1709  +
                    .acquisitions
 2216   1710   
                    .current_demand_snapshot(&cell.eligibility_group)
 2217   1711   
            });
 2218   1712   
            state.assert_consistent();
 2219         -
            (advertisement, demand)
        1713  +
            (revision, demand)
 2220   1714   
        };
 2221   1715   
        let Some(detached) = detached else {
 2222   1716   
            return false;
 2223   1717   
        };
 2224   1718   
        let H2CloseTransition {
 2225   1719   
            connection,
 2226   1720   
            pending_waiters,
 2227   1721   
            removed_generation,
 2228   1722   
        } = detached;
 2229   1723   
        drop(removed_generation);
 2230         -
        Self::publish_h2_advertisement(cell, advertisement);
        1724  +
        Self::submit_h2_supply_update(cell, revision);
 2231   1725   
        Self::publish_current_demand(cell, demand);
 2232   1726   
        for waiter in pending_waiters {
 2233         -
            cell.complete_establishment(waiter, AcquisitionResult::Reacquire);
        1727  +
            cell.complete_establishment(waiter, AcquisitionOutcome::RetryAcquisition);
 2234   1728   
        }
 2235   1729   
        connection.logical_close(reason)
 2236   1730   
    }
 2237   1731   
 2238         -
    /// Returns the exact accepting generation for publication.
        1732  +
    /// Returns the exact accepting generation for peer routing.
 2239   1733   
    #[cfg(test)]
 2240   1734   
    pub(in crate::client::pool) fn accepting_h2_generation(&self) -> Option<H2GenerationId> {
 2241   1735   
        self.state.lock().h2.accepting()
 2242   1736   
    }
 2243   1737   
 2244         -
    /// Returns the prospective and accepted request counts for one generation.
        1738  +
    /// Returns the prospective and active request counts for one generation.
 2245   1739   
    #[cfg(test)]
 2246   1740   
    pub(in crate::client::pool) fn h2_request_counts(
 2247   1741   
        &self,
 2248   1742   
        generation: H2GenerationId,
 2249   1743   
    ) -> Option<(usize, usize)> {
 2250   1744   
        self.state
 2251   1745   
            .lock()
 2252   1746   
            .h2
 2253   1747   
            .generations
 2254   1748   
            .get(&generation)
 2255         -
            .map(|record| (record.prospective, record.accepted))
        1749  +
            .map(|record| (record.prospective_requests, record.active_requests))
 2256   1750   
    }
 2257   1751   
 2258   1752   
    /// Installs an accepting generation without a Hyper handshake.
 2259   1753   
    #[cfg(test)]
 2260   1754   
    pub(in crate::client::pool) fn install_h2_for_test(
 2261   1755   
        cell: &Arc<Self>,
 2262   1756   
        connection: Arc<ConnectionState>,
 2263   1757   
        sender_id: u64,
 2264   1758   
        idle_deadline: Option<SystemTime>,
 2265   1759   
    ) -> H2GenerationId {
 2266         -
        let (generation, advertisement) = {
        1760  +
        let (generation, revision) = {
 2267   1761   
            let mut state = cell.state.lock();
 2268         -
            let generation = state.h2.install_generation(
        1762  +
            let generation = state.h2.open_generation(
 2269   1763   
                BTreeSet::new(),
 2270   1764   
                connection,
 2271   1765   
                H2Sender::test(sender_id),
 2272   1766   
                idle_deadline,
 2273   1767   
            );
 2274   1768   
            state.assert_consistent();
 2275         -
            let advertisement = state.take_h2_advertisement_update();
 2276         -
            (generation, advertisement)
        1769  +
            let revision = state.take_h2_supply_update();
        1770  +
            (generation, revision)
 2277   1771   
        };
 2278         -
        Self::publish_h2_advertisement(cell, advertisement);
        1772  +
        Self::submit_h2_supply_update(cell, revision);
 2279   1773   
        cell.notify_maintenance(idle_deadline);
 2280   1774   
        generation
 2281   1775   
    }
 2282   1776   
}
 2283   1777   
 2284   1778   
#[cfg(all(test, not(smithy_http_client_loom)))]
 2285   1779   
mod tests {
 2286   1780   
    use super::*;
 2287   1781   
    use crate::client::pool::admission::ProtocolRequirement;
 2288   1782   
    use crate::client::pool::connection::{CloseReason, ConnectionInfo};
 2289   1783   
    use crate::client::pool::origin::OriginKey;
 2290   1784   
    use crate::client::pool::partition::EligibilityGroup;
 2291   1785   
    use aws_smithy_runtime_api::client::connection::ConnectionId;
 2292   1786   
    use http_1x::uri::Scheme;
 2293   1787   
 2294   1788   
    fn cell() -> Arc<OriginCell> {
 2295   1789   
        Arc::new(OriginCell::new(
 2296   1790   
            PartitionId::from_index(1),
 2297   1791   
            OriginKey::from_parts(Scheme::HTTPS, "example.com", None).unwrap(),
 2298   1792   
            EligibilityGroup::Pool,
 2299   1793   
            None,
 2300   1794   
            None,
 2301   1795   
        ))
 2302   1796   
    }
 2303   1797   
 2304   1798   
    fn bounded_cell(
 2305   1799   
        admission: &Arc<crate::client::pool::admission::OriginAdmission>,
 2306   1800   
        partition: usize,
 2307   1801   
    ) -> Arc<OriginCell> {
 2308   1802   
        crate::client::pool::admission::OriginAdmission::register_cell(
 2309   1803   
            admission,
 2310   1804   
            Arc::new(OriginCell::new(
 2311   1805   
                PartitionId::from_index(partition),
 2312   1806   
                OriginKey::from_parts(Scheme::HTTPS, "example.com", None).unwrap(),
 2313   1807   
                EligibilityGroup::Pool,
 2314   1808   
                Some(admission.clone()),
 2315   1809   
                None,
 2316   1810   
            )),
 2317   1811   
        )
 2318   1812   
    }
 2319   1813   
 2320   1814   
    fn connection(
 2321   1815   
        id: u64,
 2322   1816   
    ) -> (
 2323   1817   
        Arc<ConnectionState>,
 2324   1818   
        super::super::super::connection::PhysicalConnectionGuard,
 2325   1819   
    ) {
 2326   1820   
        ConnectionState::unbounded(ConnectionInfo::for_test(
 2327   1821   
            ConnectionId::new(id),
 2328   1822   
            PartitionId::from_index(1),
 2329   1823   
        ))
 2330   1824   
    }
 2331   1825   
 2332   1826   
    fn begin_waiter(cell: &Arc<OriginCell>) -> WaiterId {
 2333   1827   
        let waiter = OriginCell::register_waiter(cell, ProtocolRequirement::H2Required);
 2334   1828   
        let event = cell
 2335   1829   
            .take_ready_event(waiter)
 2336   1830   
            .expect("unbounded H2 waiter did not receive establishment authority");
 2337         -
        let super::super::AcquisitionEvent::Establish(permit) = event else {
        1831  +
        let super::super::AcquisitionStep::StartEstablishment(permit) = event else {
 2338   1832   
            panic!("new H2 waiter completed before establishment");
 2339   1833   
        };
 2340   1834   
        assert!(cell.start_establishment(waiter));
 2341   1835   
        drop(permit);
 2342   1836   
        waiter
 2343   1837   
    }
 2344   1838   
 2345         -
    fn install_generation(
        1839  +
    fn open_test_generation(
 2346   1840   
        cell: &Arc<OriginCell>,
 2347   1841   
        connection_id: u64,
 2348   1842   
    ) -> (
 2349   1843   
        H2GenerationId,
 2350   1844   
        Arc<ConnectionState>,
 2351   1845   
        super::super::super::connection::PhysicalConnectionGuard,
 2352   1846   
    ) {
 2353   1847   
        let waiter = begin_waiter(cell);
 2354         -
        let H2FlightInstall::Driver(flight) = cell.install_or_join_h2_flight(waiter) else {
        1848  +
        let H2FlightDecision::RunFlight(flight) = cell.converge_h2_flight(waiter) else {
 2355   1849   
            panic!("fresh cell did not create an HTTP/2 flight");
 2356   1850   
        };
 2357   1851   
        let (connection, physical) = connection(connection_id);
 2358   1852   
        let generation = OriginCell::complete_h2_flight(
 2359   1853   
            cell,
 2360   1854   
            flight,
 2361   1855   
            connection.clone(),
 2362   1856   
            H2Sender::test(connection_id),
 2363   1857   
            None,
 2364   1858   
        )
 2365   1859   
        .expect("fresh flight did not install");
 2366   1860   
        let event = cell
 2367   1861   
            .take_ready_event(waiter)
 2368   1862   
            .expect("flight completion did not satisfy its waiter");
 2369         -
        let super::super::AcquisitionEvent::Complete(super::super::AcquisitionResult::H2(
        1863  +
        let super::super::AcquisitionStep::Resolved(super::super::AcquisitionOutcome::H2(
 2370   1864   
            activation,
 2371   1865   
        )) = event
 2372   1866   
        else {
 2373   1867   
            panic!("flight completion produced a non-H2 result");
 2374   1868   
        };
 2375   1869   
        drop(activation);
 2376   1870   
        (generation, connection, physical)
 2377   1871   
    }
 2378   1872   
 2379   1873   
    fn generation_counts(cell: &OriginCell, generation: H2GenerationId) -> (usize, usize) {
 2380   1874   
        cell.h2_request_counts(generation)
 2381   1875   
            .expect("generation was not installed")
 2382   1876   
    }
 2383   1877   
 2384   1878   
    #[test]
 2385   1879   
    fn flight_converges_participants_and_cancellation() {
 2386         -
        let mut records = H2Records::default();
        1880  +
        let mut records = H2CellState::default();
 2387   1881   
        let first = WaiterId(1);
 2388   1882   
        let second = WaiterId(2);
 2389   1883   
 2390         -
        let H2FlightInstall::Driver(flight) = records.install_or_join_flight(first) else {
        1884  +
        let H2FlightDecision::RunFlight(flight) = records.converge_flight(first) else {
 2391   1885   
            panic!("first participant did not become the flight driver");
 2392   1886   
        };
 2393   1887   
        assert!(matches!(
 2394         -
            records.install_or_join_flight(second),
 2395         -
            H2FlightInstall::Joined
        1888  +
            records.converge_flight(second),
        1889  +
            H2FlightDecision::JoinedFlight
 2396   1890   
        ));
 2397   1891   
        records.cancel_flight_participant(second);
 2398   1892   
        assert_eq!(Some(vec![first]), records.fail_flight(flight));
 2399   1893   
        assert!(records.flight.is_none());
 2400   1894   
    }
 2401   1895   
        1896  +
    #[test]
        1897  +
    #[should_panic(expected = "HTTP/2 flight retained a waiter that was no longer launchable")]
        1898  +
    fn flight_participant_must_remain_launchable() {
        1899  +
        let cell = cell();
        1900  +
        let waiter = begin_waiter(&cell);
        1901  +
        assert!(matches!(
        1902  +
            cell.converge_h2_flight(waiter),
        1903  +
            H2FlightDecision::RunFlight(_)
        1904  +
        ));
        1905  +
        1906  +
        let mut state = cell.state.lock();
        1907  +
        let cancellation = state
        1908  +
            .acquisitions
        1909  +
            .cancel_waiter(waiter, &cell.eligibility_group)
        1910  +
            .expect("launching waiter did not cancel");
        1911  +
        drop(cancellation);
        1912  +
        state.h2.assert_pending_waiters(&state.acquisitions);
        1913  +
    }
        1914  +
 2402   1915   
    #[test]
 2403   1916   
    fn accepting_generation_prevents_a_second_flight() {
 2404         -
        let mut records = H2Records::default();
        1917  +
        let mut records = H2CellState::default();
 2405   1918   
        let (connection, _physical) = connection(1);
 2406   1919   
        let generation =
 2407         -
            records.install_generation(BTreeSet::new(), connection, H2Sender::test(1), None);
        1920  +
            records.open_generation(BTreeSet::new(), connection, H2Sender::test(1), None);
 2408   1921   
 2409   1922   
        assert!(matches!(
 2410         -
            records.install_or_join_flight(WaiterId(1)),
 2411         -
            H2FlightInstall::Accepting(current) if current == generation
        1923  +
            records.converge_flight(WaiterId(1)),
        1924  +
            H2FlightDecision::UseGeneration(current) if current == generation
 2412   1925   
        ));
 2413   1926   
        assert!(records.flight.is_none());
 2414   1927   
    }
 2415   1928   
 2416   1929   
    #[test]
 2417   1930   
    fn generation_join_does_not_retain_a_waiter_already_served_by_the_gate() {
 2418   1931   
        let cell = cell();
 2419   1932   
        let first = begin_waiter(&cell);
 2420   1933   
        let second = begin_waiter(&cell);
 2421         -
        let H2FlightInstall::Driver(flight) = cell.install_or_join_h2_flight(first) else {
        1934  +
        let H2FlightDecision::RunFlight(flight) = cell.converge_h2_flight(first) else {
 2422   1935   
            panic!("first waiter did not become the flight driver");
 2423   1936   
        };
 2424   1937   
        assert!(matches!(
 2425         -
            cell.install_or_join_h2_flight(second),
 2426         -
            H2FlightInstall::Joined
        1938  +
            cell.converge_h2_flight(second),
        1939  +
            H2FlightDecision::JoinedFlight
 2427   1940   
        ));
 2428   1941   
 2429   1942   
        let (connection, _physical) = connection(1);
 2430   1943   
        let generation =
 2431   1944   
            OriginCell::complete_h2_flight(&cell, flight, connection, H2Sender::test(1), None)
 2432   1945   
                .expect("flight did not install");
 2433   1946   
 2434   1947   
        assert_eq!(
 2435         -
            H2GenerationJoin::WaiterCompleted,
        1948  +
            H2GenerationJoinOutcome::WaiterResolved,
 2436   1949   
            OriginCell::join_h2_generation(&cell, first, generation)
 2437   1950   
        );
 2438         -
        let super::super::AcquisitionEvent::Complete(super::super::AcquisitionResult::H2(
        1951  +
        let super::super::AcquisitionStep::Resolved(super::super::AcquisitionOutcome::H2(
 2439   1952   
            activation,
 2440   1953   
        )) = cell
 2441   1954   
            .take_ready_event(first)
 2442   1955   
            .expect("generation gate did not serve the first waiter")
 2443   1956   
        else {
 2444   1957   
            panic!("first waiter received a non-H2 result");
 2445   1958   
        };
 2446   1959   
 2447   1960   
        assert!(OriginCell::close_h2(
 2448   1961   
            &cell,
 2449   1962   
            generation,
 2450   1963   
            CloseReason::PoolDropped,
 2451   1964   
        ));
 2452   1965   
        drop(activation);
 2453   1966   
        assert!(matches!(
 2454   1967   
            cell.take_ready_event(second),
 2455         -
            Some(super::super::AcquisitionEvent::Complete(
 2456         -
                super::super::AcquisitionResult::Reacquire
        1968  +
            Some(super::super::AcquisitionStep::Resolved(
        1969  +
                super::super::AcquisitionOutcome::RetryAcquisition
 2457   1970   
            ))
 2458   1971   
        ));
 2459   1972   
        assert_eq!(0, cell.retained_waiters_for_test());
 2460   1973   
    }
 2461   1974   
 2462   1975   
    #[test]
 2463   1976   
    fn generation_gate_services_committed_waiters_before_direct_arrivals() {
 2464   1977   
        let cell = cell();
 2465   1978   
        let first = begin_waiter(&cell);
 2466   1979   
        let second = begin_waiter(&cell);
 2467         -
        let H2FlightInstall::Driver(flight) = cell.install_or_join_h2_flight(first) else {
        1980  +
        let H2FlightDecision::RunFlight(flight) = cell.converge_h2_flight(first) else {
 2468   1981   
            panic!("first participant did not become the flight driver");
 2469   1982   
        };
 2470   1983   
        assert!(matches!(
 2471         -
            cell.install_or_join_h2_flight(second),
 2472         -
            H2FlightInstall::Joined
        1984  +
            cell.converge_h2_flight(second),
        1985  +
            H2FlightDecision::JoinedFlight
 2473   1986   
        ));
 2474   1987   
        let (connection, _physical) = connection(1);
 2475   1988   
        OriginCell::complete_h2_flight(&cell, flight, connection, H2Sender::test(1), None)
 2476   1989   
            .expect("flight did not install");
 2477   1990   
 2478   1991   
        assert!(
 2479   1992   
            OriginCell::select_h2(&cell).is_none(),
 2480   1993   
            "direct arrival bypassed committed waiters"
 2481   1994   
        );
 2482         -
        let super::super::AcquisitionEvent::Complete(super::super::AcquisitionResult::H2(
        1995  +
        let super::super::AcquisitionStep::Resolved(super::super::AcquisitionOutcome::H2(
 2483   1996   
            first_activation,
 2484   1997   
        )) = cell
 2485   1998   
            .take_ready_event(first)
 2486   1999   
            .expect("first waiter was not served")
 2487   2000   
        else {
 2488   2001   
            panic!("first waiter received a non-H2 result");
 2489   2002   
        };
 2490   2003   
        drop(first_activation);
 2491   2004   
 2492   2005   
        assert!(
 2493   2006   
            OriginCell::select_h2(&cell).is_none(),
 2494   2007   
            "direct arrival bypassed the second committed waiter"
 2495   2008   
        );
 2496         -
        let super::super::AcquisitionEvent::Complete(super::super::AcquisitionResult::H2(
        2009  +
        let super::super::AcquisitionStep::Resolved(super::super::AcquisitionOutcome::H2(
 2497   2010   
            second_activation,
 2498   2011   
        )) = cell
 2499   2012   
            .take_ready_event(second)
 2500   2013   
            .expect("second waiter was not served")
 2501   2014   
        else {
 2502   2015   
            panic!("second waiter received a non-H2 result");
 2503   2016   
        };
 2504   2017   
        drop(second_activation);
 2505   2018   
 2506   2019   
        assert!(
 2507   2020   
            OriginCell::select_h2(&cell).is_some(),
 2508   2021   
            "gate did not open after committed waiters drained"
 2509   2022   
        );
 2510   2023   
    }
 2511   2024   
 2512   2025   
    #[test]
 2513   2026   
    fn cancelling_an_open_gate_activation_services_the_next_waiter() {
 2514   2027   
        use crate::client::pool::admission::OriginAdmission;
 2515   2028   
        use std::num::NonZeroUsize;
 2516   2029   
 2517   2030   
        let admission = OriginAdmission::for_test(NonZeroUsize::new(1).unwrap());
 2518   2031   
        let cell = OriginAdmission::register_cell(
 2519   2032   
            &admission,
 2520   2033   
            Arc::new(OriginCell::new(
 2521   2034   
                PartitionId::from_index(1),
 2522   2035   
                OriginKey::from_parts(Scheme::HTTPS, "example.com", None).unwrap(),
 2523   2036   
                EligibilityGroup::Pool,
 2524   2037   
                Some(admission.clone()),
 2525   2038   
                None,
 2526   2039   
            )),
 2527   2040   
        );
 2528   2041   
        let (connection, _physical) = ConnectionState::bounded(
 2529   2042   
            ConnectionInfo::for_test(ConnectionId::new(1), cell.id().partition()),
 2530   2043   
            OriginAdmission::lease_for_test(&admission),
 2531   2044   
        );
 2532   2045   
        let generation = OriginCell::install_h2_for_test(&cell, connection, 1, None);
 2533   2046   
        cell.state.lock().h2.prioritize_through(Some(WaiterId(0)));
 2534   2047   
 2535   2048   
        let first = OriginCell::register_waiter(&cell, ProtocolRequirement::H2Required);
 2536         -
        let super::super::AcquisitionEvent::Complete(super::super::AcquisitionResult::H2(
        2049  +
        let super::super::AcquisitionStep::Resolved(super::super::AcquisitionOutcome::H2(
 2537   2050   
            first_activation,
 2538   2051   
        )) = cell
 2539   2052   
            .take_ready_event(first)
 2540   2053   
            .expect("first waiter was not served")
 2541   2054   
        else {
 2542   2055   
            panic!("first waiter received a non-H2 result");
 2543   2056   
        };
 2544   2057   
        let second = OriginCell::register_waiter(&cell, ProtocolRequirement::H2Required);
 2545   2058   
        let third = OriginCell::register_waiter(&cell, ProtocolRequirement::H2Required);
 2546   2059   
        assert_eq!((1, 0), generation_counts(&cell, generation));
 2547   2060   
 2548   2061   
        drop(first_activation);
 2549   2062   
        assert_eq!((1, 0), generation_counts(&cell, generation));
 2550   2063   
        assert!(OriginCell::cancel_waiter(&cell, second));
 2551   2064   
        assert_eq!(
 2552   2065   
            (1, 0),
 2553   2066   
            generation_counts(&cell, generation),
 2554   2067   
            "cancelling an open-gate activation stranded its successor"
 2555   2068   
        );
 2556   2069   
 2557         -
        let super::super::AcquisitionEvent::Complete(super::super::AcquisitionResult::H2(
        2070  +
        let super::super::AcquisitionStep::Resolved(super::super::AcquisitionOutcome::H2(
 2558   2071   
            third_activation,
 2559   2072   
        )) = cell
 2560   2073   
            .take_ready_event(third)
 2561   2074   
            .expect("successor was not served after cancellation")
 2562   2075   
        else {
 2563   2076   
            panic!("successor received a non-H2 result");
 2564   2077   
        };
 2565   2078   
        drop(third_activation);
 2566   2079   
        assert_eq!((0, 0), generation_counts(&cell, generation));
 2567   2080   
        assert!(OriginCell::close_h2(
 2568   2081   
            &cell,
 2569   2082   
            generation,
 2570   2083   
            CloseReason::PoolDropped,
 2571   2084   
        ));
 2572   2085   
    }
 2573   2086   
 2574   2087   
    #[test]
 2575   2088   
    fn cancelling_an_unserved_flight_participant_prunes_the_generation() {
 2576   2089   
        let cell = cell();
 2577   2090   
        let first = begin_waiter(&cell);
 2578   2091   
        let second = begin_waiter(&cell);
 2579         -
        let H2FlightInstall::Driver(flight) = cell.install_or_join_h2_flight(first) else {
        2092  +
        let H2FlightDecision::RunFlight(flight) = cell.converge_h2_flight(first) else {
 2580   2093   
            panic!("first participant did not become the flight driver");
 2581   2094   
        };
 2582   2095   
        assert!(matches!(
 2583         -
            cell.install_or_join_h2_flight(second),
 2584         -
            H2FlightInstall::Joined
        2096  +
            cell.converge_h2_flight(second),
        2097  +
            H2FlightDecision::JoinedFlight
 2585   2098   
        ));
 2586   2099   
        let (connection, _physical) = connection(1);
 2587   2100   
        let generation =
 2588   2101   
            OriginCell::complete_h2_flight(&cell, flight, connection, H2Sender::test(1), None)
 2589   2102   
                .expect("flight did not install");
 2590         -
        let super::super::AcquisitionEvent::Complete(super::super::AcquisitionResult::H2(
        2103  +
        let super::super::AcquisitionStep::Resolved(super::super::AcquisitionOutcome::H2(
 2591   2104   
            first_activation,
 2592   2105   
        )) = cell
 2593   2106   
            .take_ready_event(first)
 2594   2107   
            .expect("first participant was not served")
 2595   2108   
        else {
 2596   2109   
            panic!("first participant received a non-H2 result");
 2597   2110   
        };
 2598   2111   
 2599   2112   
        assert!(OriginCell::cancel_waiter(&cell, second));
 2600   2113   
        assert!(OriginCell::close_h2(
 2601   2114   
            &cell,
 2602   2115   
            generation,
 2603   2116   
            CloseReason::PoolDropped,
 2604   2117   
        ));
 2605   2118   
        drop(first_activation);
 2606   2119   
        assert_eq!(0, cell.retained_waiters_for_test());
 2607   2120   
    }
 2608   2121   
 2609   2122   
    #[test]
 2610   2123   
    fn returned_h1_prunes_a_transferred_h2_waiter() {
 2611   2124   
        let cell = cell();
 2612   2125   
        let first = begin_waiter(&cell);
 2613   2126   
        let second = OriginCell::register_waiter(&cell, ProtocolRequirement::H1Compatible);
 2614         -
        let super::super::AcquisitionEvent::Establish(permit) = cell
        2127  +
        let super::super::AcquisitionStep::StartEstablishment(permit) = cell
 2615   2128   
            .take_ready_event(second)
 2616   2129   
            .expect("second participant did not receive establishment authority")
 2617   2130   
        else {
 2618   2131   
            panic!("second participant completed before establishment");
 2619   2132   
        };
 2620   2133   
        assert!(cell.start_establishment(second));
 2621   2134   
        drop(permit);
 2622   2135   
 2623         -
        let H2FlightInstall::Driver(flight) = cell.install_or_join_h2_flight(first) else {
        2136  +
        let H2FlightDecision::RunFlight(flight) = cell.converge_h2_flight(first) else {
 2624   2137   
            panic!("first participant did not become the flight driver");
 2625   2138   
        };
 2626   2139   
        assert!(matches!(
 2627         -
            cell.install_or_join_h2_flight(second),
 2628         -
            H2FlightInstall::Joined
        2140  +
            cell.converge_h2_flight(second),
        2141  +
            H2FlightDecision::JoinedFlight
 2629   2142   
        ));
 2630   2143   
        let (h2_connection, _h2_physical) = connection(1);
 2631   2144   
        let generation =
 2632   2145   
            OriginCell::complete_h2_flight(&cell, flight, h2_connection, H2Sender::test(1), None)
 2633   2146   
                .expect("flight did not install");
 2634         -
        let super::super::AcquisitionEvent::Complete(super::super::AcquisitionResult::H2(
        2147  +
        let super::super::AcquisitionStep::Resolved(super::super::AcquisitionOutcome::H2(
 2635   2148   
            first_activation,
 2636   2149   
        )) = cell
 2637   2150   
            .take_ready_event(first)
 2638   2151   
            .expect("first participant was not served")
 2639   2152   
        else {
 2640   2153   
            panic!("first participant received a non-H2 result");
 2641   2154   
        };
 2642   2155   
 2643   2156   
        let (h1_connection, _h1_physical) = connection(2);
 2644         -
        let returning = OriginCell::install_selected_h1(
        2157  +
        let returning = OriginCell::insert_selected_h1(
 2645   2158   
            &cell,
 2646   2159   
            h1_connection,
 2647   2160   
            super::super::h1::H1Sender::test(2),
 2648   2161   
        );
 2649   2162   
        drop(returning);
 2650         -
        let super::super::AcquisitionEvent::Complete(super::super::AcquisitionResult::H1(
        2163  +
        let super::super::AcquisitionStep::Resolved(super::super::AcquisitionOutcome::H1(
 2651   2164   
            selection,
 2652   2165   
        )) = cell
 2653   2166   
            .take_ready_event(second)
 2654   2167   
            .expect("returned HTTP/1 sender did not serve the compatible waiter")
 2655   2168   
        else {
 2656   2169   
            panic!("compatible waiter received a non-H1 result");
 2657   2170   
        };
 2658   2171   
        drop(selection);
 2659   2172   
 2660   2173   
        assert!(OriginCell::close_h2(
 2661   2174   
            &cell,
 2662   2175   
            generation,
 2663   2176   
            CloseReason::PoolDropped,
 2664   2177   
        ));
 2665   2178   
        drop(first_activation);
 2666   2179   
        assert_eq!(0, cell.retained_waiters_for_test());
 2667   2180   
    }
 2668   2181   
 2669   2182   
    #[test]
 2670   2183   
    fn later_local_activation_does_not_hide_an_open_generation_from_peers() {
 2671         -
        let mut records = H2Records::default();
        2184  +
        let mut records = H2CellState::default();
 2672   2185   
        let committed = WaiterId(1);
 2673   2186   
        let later = WaiterId(2);
 2674         -
        let H2FlightInstall::Driver(flight) = records.install_or_join_flight(committed) else {
        2187  +
        let H2FlightDecision::RunFlight(flight) = records.converge_flight(committed) else {
 2675   2188   
            panic!("first participant did not become the flight driver");
 2676   2189   
        };
 2677   2190   
        let (connection, _physical) = connection(1);
 2678   2191   
        let generation = records
 2679   2192   
            .complete_flight(flight, connection, H2Sender::test(1), None)
 2680   2193   
            .expect("flight did not install");
 2681   2194   
        records.prioritize_through(Some(committed));
 2682   2195   
 2683   2196   
        assert_eq!(
 2684         -
            GateTurn::Through(committed),
 2685         -
            records.next_gate_turn(true),
        2197  +
            H2ActivationTurn::Through(committed),
        2198  +
            records.take_next_turn(true),
 2686   2199   
            "committed waiter did not retain initial priority"
 2687   2200   
        );
 2688         -
        records.begin_gate_activation(committed);
 2689         -
        assert_eq!(None, records.publishable_generation());
 2690         -
        assert!(records.finish_gate_activation(generation, committed));
        2201  +
        records.reserve_activation_turn(committed);
        2202  +
        assert_eq!(None, records.peer_routable_generation());
        2203  +
        assert!(records.release_activation_turn(generation, committed));
 2691   2204   
 2692         -
        assert_eq!(GateTurn::Open, records.next_gate_turn(false));
 2693         -
        records.begin_gate_activation(later);
        2205  +
        assert_eq!(H2ActivationTurn::Open, records.take_next_turn(false));
        2206  +
        records.reserve_activation_turn(later);
 2694   2207   
        assert_eq!(
 2695   2208   
            Some(generation),
 2696         -
            records.publishable_generation(),
        2209  +
            records.peer_routable_generation(),
 2697   2210   
            "a post-cutoff local activation hid the generation from peers"
 2698   2211   
        );
 2699   2212   
    }
 2700   2213   
 2701   2214   
    #[test]
 2702   2215   
    fn generation_close_reacquires_unserved_flight_participant() {
 2703   2216   
        let cell = cell();
 2704   2217   
        let first = begin_waiter(&cell);
 2705   2218   
        let second = begin_waiter(&cell);
 2706         -
        let H2FlightInstall::Driver(flight) = cell.install_or_join_h2_flight(first) else {
        2219  +
        let H2FlightDecision::RunFlight(flight) = cell.converge_h2_flight(first) else {
 2707   2220   
            panic!("first participant did not become the flight driver");
 2708   2221   
        };
 2709   2222   
        assert!(matches!(
 2710         -
            cell.install_or_join_h2_flight(second),
 2711         -
            H2FlightInstall::Joined
        2223  +
            cell.converge_h2_flight(second),
        2224  +
            H2FlightDecision::JoinedFlight
 2712   2225   
        ));
 2713   2226   
        let (connection, _physical) = connection(1);
 2714   2227   
        let generation =
 2715   2228   
            OriginCell::complete_h2_flight(&cell, flight, connection, H2Sender::test(1), None)
 2716   2229   
                .expect("flight did not install");
 2717   2230   
 2718         -
        let super::super::AcquisitionEvent::Complete(super::super::AcquisitionResult::H2(
        2231  +
        let super::super::AcquisitionStep::Resolved(super::super::AcquisitionOutcome::H2(
 2719   2232   
            first_activation,
 2720   2233   
        )) = cell
 2721   2234   
            .take_ready_event(first)
 2722   2235   
            .expect("first participant was not served")
 2723   2236   
        else {
 2724   2237   
            panic!("first participant received a non-H2 result");
 2725   2238   
        };
 2726   2239   
        assert!(cell.take_ready_event(second).is_none());
 2727   2240   
 2728   2241   
        assert!(OriginCell::close_h2(
 2729   2242   
            &cell,
 2730   2243   
            generation,
 2731   2244   
            CloseReason::Poisoned
 2732   2245   
        ));
 2733   2246   
        assert!(matches!(
 2734   2247   
            cell.take_ready_event(second),
 2735         -
            Some(super::super::AcquisitionEvent::Complete(
 2736         -
                super::super::AcquisitionResult::Reacquire
        2248  +
            Some(super::super::AcquisitionStep::Resolved(
        2249  +
                super::super::AcquisitionOutcome::RetryAcquisition
 2737   2250   
            ))
 2738   2251   
        ));
 2739   2252   
        drop(first_activation);
 2740   2253   
        assert_eq!(0, cell.retained_waiters_for_test());
 2741   2254   
    }
 2742   2255   
 2743   2256   
    #[test]
 2744   2257   
    fn bounded_generation_close_returns_capacity_and_reacquires_participant() {
 2745   2258   
        use crate::client::pool::admission::OriginAdmission;
 2746   2259   
        use std::num::NonZeroUsize;
 2747   2260   
 2748   2261   
        let admission = OriginAdmission::for_test(NonZeroUsize::new(2).unwrap());
 2749   2262   
        let cell = OriginAdmission::register_cell(
 2750   2263   
            &admission,
 2751   2264   
            Arc::new(OriginCell::new(
 2752   2265   
                PartitionId::from_index(1),
 2753   2266   
                OriginKey::from_parts(Scheme::HTTPS, "example.com", None).unwrap(),
 2754   2267   
                EligibilityGroup::Pool,
 2755   2268   
                Some(admission.clone()),
 2756   2269   
                None,
 2757   2270   
            )),
 2758   2271   
        );
 2759   2272   
        let mut participants = Vec::new();
 2760   2273   
        for _ in 0..2 {
 2761   2274   
            let waiter = OriginCell::register_waiter(&cell, ProtocolRequirement::H2Required);
 2762         -
            let super::super::AcquisitionEvent::Establish(permit) = cell
        2275  +
            let super::super::AcquisitionStep::StartEstablishment(permit) = cell
 2763   2276   
                .take_ready_event(waiter)
 2764   2277   
                .expect("bounded participant did not receive capacity")
 2765   2278   
            else {
 2766   2279   
                panic!("bounded participant completed before establishment");
 2767   2280   
            };
 2768   2281   
            assert!(cell.start_establishment(waiter));
 2769   2282   
            participants.push((waiter, permit));
 2770   2283   
        }
 2771   2284   
        let (first, first_permit) = participants.remove(0);
 2772   2285   
        let (second, second_permit) = participants.remove(0);
 2773         -
        let H2FlightInstall::Driver(flight) = cell.install_or_join_h2_flight(first) else {
        2286  +
        let H2FlightDecision::RunFlight(flight) = cell.converge_h2_flight(first) else {
 2774   2287   
            panic!("first participant did not become the flight driver");
 2775   2288   
        };
 2776   2289   
        assert!(matches!(
 2777         -
            cell.install_or_join_h2_flight(second),
 2778         -
            H2FlightInstall::Joined
        2290  +
            cell.converge_h2_flight(second),
        2291  +
            H2FlightDecision::JoinedFlight
 2779   2292   
        ));
 2780   2293   
        drop(second_permit);
 2781   2294   
        let (connection, _physical) = ConnectionState::bounded(
 2782   2295   
            ConnectionInfo::for_test(ConnectionId::new(1), cell.id().partition()),
 2783   2296   
            first_permit
 2784   2297   
                .into_lease()
 2785   2298   
                .expect("bounded permit had no lease"),
 2786   2299   
        );
 2787   2300   
        let generation =
 2788   2301   
            OriginCell::complete_h2_flight(&cell, flight, connection, H2Sender::test(1), None)
 2789   2302   
                .expect("bounded flight did not install");
 2790         -
        let super::super::AcquisitionEvent::Complete(super::super::AcquisitionResult::H2(
        2303  +
        let super::super::AcquisitionStep::Resolved(super::super::AcquisitionOutcome::H2(
 2791   2304   
            first_activation,
 2792   2305   
        )) = cell
 2793   2306   
            .take_ready_event(first)
 2794   2307   
            .expect("first participant was not served")
 2795   2308   
        else {
 2796   2309   
            panic!("first participant received a non-H2 result");
 2797   2310   
        };
 2798   2311   
 2799   2312   
        assert!(OriginCell::close_h2(
 2800   2313   
            &cell,
 2801   2314   
            generation,
 2802   2315   
            CloseReason::Poisoned
 2803   2316   
        ));
 2804   2317   
        assert!(matches!(
 2805   2318   
            cell.take_ready_event(second),
 2806         -
            Some(super::super::AcquisitionEvent::Complete(
 2807         -
                super::super::AcquisitionResult::Reacquire
        2319  +
            Some(super::super::AcquisitionStep::Resolved(
        2320  +
                super::super::AcquisitionOutcome::RetryAcquisition
 2808   2321   
            ))
 2809   2322   
        ));
 2810   2323   
        drop(first_activation);
 2811   2324   
        assert_eq!(2, admission.available_capacity_for_test());
 2812   2325   
        assert_eq!(0, cell.retained_waiters_for_test());
 2813   2326   
    }
 2814   2327   
 2815   2328   
    #[test]
 2816   2329   
    fn generation_is_reused_only_after_hyper_acceptance() {
 2817   2330   
        let cell = cell();
 2818         -
        let (_generation, connection, _physical) = install_generation(&cell, 1);
        2331  +
        let (_generation, connection, _physical) = open_test_generation(&cell, 1);
 2819   2332   
 2820   2333   
        let mut activation =
 2821   2334   
            OriginCell::select_h2(&cell).expect("accepting generation was not selected");
 2822   2335   
        assert!(!activation.is_reused());
 2823   2336   
        let H2DispatchParts {
 2824   2337   
            sender: _sender,
 2825         -
            send_endpoint: send,
 2826         -
            receive_endpoint: receive,
        2338  +
            upload,
        2339  +
            response,
 2827   2340   
        } = activation.take_dispatch_parts();
 2828   2341   
        let dispatch = ConnectionState::try_commit_dispatch(&connection)
 2829   2342   
            .expect("open connection rejected dispatch");
 2830   2343   
        activation.accept(dispatch);
 2831         -
        drop(send);
 2832         -
        drop(receive);
        2344  +
        drop(upload);
        2345  +
        drop(response);
 2833   2346   
 2834   2347   
        let reused = OriginCell::select_h2(&cell).expect("accepted generation was not reusable");
 2835   2348   
        assert!(reused.is_reused());
 2836   2349   
        drop(reused);
 2837   2350   
    }
 2838   2351   
 2839   2352   
    #[test]
 2840   2353   
    #[should_panic(expected = "HTTP/2 activation dispatch parts already taken")]
 2841   2354   
    fn activation_transfers_dispatch_parts_once() {
 2842   2355   
        let cell = cell();
 2843         -
        let (_generation, _connection, _physical) = install_generation(&cell, 1);
        2356  +
        let (_generation, _connection, _physical) = open_test_generation(&cell, 1);
 2844   2357   
        let mut activation =
 2845   2358   
            OriginCell::select_h2(&cell).expect("accepting generation was not selected");
 2846   2359   
 2847   2360   
        let _first = activation.take_dispatch_parts();
 2848   2361   
        let _second = activation.take_dispatch_parts();
 2849   2362   
    }
 2850   2363   
 2851   2364   
    #[test]
 2852   2365   
    fn prospective_activation_cancellation_returns_its_generation_count() {
 2853   2366   
        let cell = cell();
 2854         -
        let (generation, _connection, _physical) = install_generation(&cell, 1);
        2367  +
        let (generation, _connection, _physical) = open_test_generation(&cell, 1);
 2855   2368   
 2856   2369   
        let activation =
 2857   2370   
            OriginCell::select_h2(&cell).expect("accepting generation was not selected");
 2858   2371   
        assert_eq!((1, 0), generation_counts(&cell, generation));
 2859   2372   
        drop(activation);
 2860   2373   
        assert_eq!((0, 0), generation_counts(&cell, generation));
 2861   2374   
    }
 2862   2375   
 2863   2376   
    #[test]
 2864         -
    fn accepted_lease_waits_for_both_endpoints_in_either_order() {
 2865         -
        for send_first in [true, false] {
        2377  +
    fn accepted_claim_waits_for_both_sides_in_either_order() {
        2378  +
        for upload_first in [true, false] {
 2866   2379   
            let cell = cell();
 2867         -
            let (generation, connection, _physical) = install_generation(&cell, 1);
        2380  +
            let (generation, connection, _physical) = open_test_generation(&cell, 1);
 2868   2381   
            let mut activation =
 2869   2382   
                OriginCell::select_h2(&cell).expect("accepting generation was not selected");
 2870   2383   
            let H2DispatchParts {
 2871   2384   
                sender: _sender,
 2872         -
                send_endpoint: send,
 2873         -
                receive_endpoint: receive,
        2385  +
                upload,
        2386  +
                response,
 2874   2387   
            } = activation.take_dispatch_parts();
 2875   2388   
            let dispatch = ConnectionState::try_commit_dispatch(&connection)
 2876   2389   
                .expect("open connection rejected dispatch");
 2877   2390   
            activation.accept(dispatch);
 2878   2391   
            assert_eq!((0, 1), generation_counts(&cell, generation));
 2879         -
            assert_eq!(1, connection.snapshot().in_flight);
        2392  +
            assert_eq!(1, connection.probe().in_flight);
 2880   2393   
 2881         -
            if send_first {
 2882         -
                drop(send);
        2394  +
            if upload_first {
        2395  +
                drop(upload);
 2883   2396   
                assert_eq!((0, 1), generation_counts(&cell, generation));
 2884         -
                receive.complete();
        2397  +
                response.finish();
 2885   2398   
            } else {
 2886         -
                receive.complete();
        2399  +
                response.finish();
 2887   2400   
                assert_eq!((0, 1), generation_counts(&cell, generation));
 2888         -
                drop(send);
        2401  +
                drop(upload);
 2889   2402   
            }
 2890   2403   
 2891   2404   
            assert_eq!((0, 0), generation_counts(&cell, generation));
 2892         -
            assert_eq!(0, connection.snapshot().in_flight);
        2405  +
            assert_eq!(0, connection.probe().in_flight);
 2893   2406   
        }
 2894   2407   
    }
 2895   2408   
 2896   2409   
    #[test]
 2897   2410   
    fn stale_route_cannot_activate_a_replacement_or_retained_drain() {
 2898   2411   
        let cell = cell();
 2899         -
        let (first, _first_connection, _first_physical) = install_generation(&cell, 1);
        2412  +
        let (first, _first_connection, _first_physical) = open_test_generation(&cell, 1);
 2900   2413   
        let stale_route = H2Route::new(&cell, first);
 2901   2414   
        let retained = OriginCell::select_h2(&cell).expect("first generation was not selectable");
 2902   2415   
        assert!(OriginCell::close_h2(
 2903   2416   
            &cell,
 2904   2417   
            first,
 2905   2418   
            CloseReason::ProtocolClosed
 2906   2419   
        ));
 2907   2420   
 2908         -
        let (second, _second_connection, _second_physical) = install_generation(&cell, 2);
        2421  +
        let (second, _second_connection, _second_physical) = open_test_generation(&cell, 2);
 2909   2422   
        assert_ne!(first, second);
 2910   2423   
        assert!(
 2911   2424   
            stale_route.activate(PartitionId::from_index(2)).is_none(),
 2912   2425   
            "stale route activated a draining or replacement generation"
 2913   2426   
        );
 2914   2427   
        drop(retained);
 2915   2428   
    }
 2916   2429   
 2917   2430   
    #[test]
 2918   2431   
    fn accepting_identity_rejects_a_retained_draining_generation() {
 2919   2432   
        let cell = cell();
 2920         -
        let (first, _first_connection, _first_physical) = install_generation(&cell, 1);
        2433  +
        let (first, _first_connection, _first_physical) = open_test_generation(&cell, 1);
 2921   2434   
        let retained = OriginCell::select_h2(&cell).expect("first generation was not selectable");
 2922   2435   
        assert!(OriginCell::close_h2(
 2923   2436   
            &cell,
 2924   2437   
            first,
 2925   2438   
            CloseReason::ProtocolClosed
 2926   2439   
        ));
 2927         -
        let (second, _second_connection, _second_physical) = install_generation(&cell, 2);
        2440  +
        let (second, _second_connection, _second_physical) = open_test_generation(&cell, 2);
 2928   2441   
 2929   2442   
        assert!(cell.state.lock().h2.is_accepting(second));
 2930   2443   
        assert!(!cell.state.lock().h2.is_accepting(first));
 2931   2444   
        drop(retained);
 2932   2445   
    }
 2933   2446   
 2934   2447   
    #[test]
 2935   2448   
    fn activation_requires_the_exact_accepting_generation() {
 2936   2449   
        let cell = cell();
 2937         -
        let (first, _first_connection, _first_physical) = install_generation(&cell, 1);
        2450  +
        let (first, _first_connection, _first_physical) = open_test_generation(&cell, 1);
 2938   2451   
        let retained = OriginCell::select_h2(&cell).expect("first generation was not selectable");
 2939   2452   
        assert!(OriginCell::close_h2(
 2940   2453   
            &cell,
 2941   2454   
            first,
 2942   2455   
            CloseReason::ProtocolClosed
 2943   2456   
        ));
 2944         -
        let (second, _second_connection, _second_physical) = install_generation(&cell, 2);
        2457  +
        let (second, _second_connection, _second_physical) = open_test_generation(&cell, 2);
 2945   2458   
 2946   2459   
        {
 2947   2460   
            let mut state = cell.state.lock();
 2948   2461   
            let first_record = state
 2949   2462   
                .h2
 2950   2463   
                .generations
 2951   2464   
                .get_mut(&first)
 2952   2465   
                .expect("retained generation disappeared");
 2953         -
            assert_eq!(H2Residence::Draining, first_record.residence);
 2954         -
            first_record.residence = H2Residence::Accepting;
        2466  +
            assert_eq!(H2GenerationState::Draining, first_record.state);
        2467  +
            first_record.state = H2GenerationState::Accepting;
 2955   2468   
            assert!(
 2956   2469   
                state.h2.activate(first).is_none(),
 2957   2470   
                "non-current generation accepted an activation"
 2958   2471   
            );
 2959   2472   
            state
 2960   2473   
                .h2
 2961   2474   
                .generations
 2962   2475   
                .get_mut(&first)
 2963   2476   
                .expect("retained generation disappeared")
 2964         -
                .residence = H2Residence::Draining;
        2477  +
                .state = H2GenerationState::Draining;
 2965   2478   
            state.assert_consistent();
 2966   2479   
        }
 2967   2480   
 2968   2481   
        assert!(cell.state.lock().h2.is_accepting(second));
 2969   2482   
        drop(retained);
 2970   2483   
    }
 2971   2484   
 2972   2485   
    #[test]
 2973         -
    fn activation_requires_accepting_residence() {
        2486  +
    fn activation_requires_accepting_generation_state() {
 2974   2487   
        let cell = cell();
 2975         -
        let (first, _first_connection, _first_physical) = install_generation(&cell, 1);
        2488  +
        let (first, _first_connection, _first_physical) = open_test_generation(&cell, 1);
 2976   2489   
        let retained = OriginCell::select_h2(&cell).expect("first generation was not selectable");
 2977   2490   
        assert!(OriginCell::close_h2(
 2978   2491   
            &cell,
 2979   2492   
            first,
 2980   2493   
            CloseReason::ProtocolClosed
 2981   2494   
        ));
 2982         -
        let (second, _second_connection, _second_physical) = install_generation(&cell, 2);
        2495  +
        let (second, _second_connection, _second_physical) = open_test_generation(&cell, 2);
 2983   2496   
 2984   2497   
        {
 2985   2498   
            let mut state = cell.state.lock();
 2986   2499   
            assert_eq!(
 2987         -
                H2Residence::Draining,
 2988         -
                state.h2.generations[&first].residence
        2500  +
                H2GenerationState::Draining,
        2501  +
                state.h2.generations[&first].state
 2989   2502   
            );
 2990         -
            state.h2.accepting = Some(first);
        2503  +
            state.h2.accepting_generation = Some(first);
 2991   2504   
            assert!(
 2992   2505   
                state.h2.activate(first).is_none(),
 2993   2506   
                "draining generation accepted an activation"
 2994   2507   
            );
 2995         -
            state.h2.accepting = Some(second);
        2508  +
            state.h2.accepting_generation = Some(second);
 2996   2509   
            state.assert_consistent();
 2997   2510   
        }
 2998   2511   
 2999   2512   
        drop(retained);
 3000   2513   
    }
 3001   2514   
 3002   2515   
    #[test]
 3003   2516   
    fn local_generation_excludes_a_peer_route() {
 3004   2517   
        let local_cell = cell();
 3005         -
        let (generation, _connection, _physical) = install_generation(&local_cell, 1);
        2518  +
        let (generation, _connection, _physical) = open_test_generation(&local_cell, 1);
 3006   2519   
        let peer_cell = Arc::new(OriginCell::new(
 3007   2520   
            PartitionId::from_index(2),
 3008   2521   
            local_cell.id().origin().clone(),
 3009   2522   
            EligibilityGroup::Pool,
 3010   2523   
            None,
 3011   2524   
            None,
 3012   2525   
        ));
 3013   2526   
        let route = H2Route::new(&peer_cell, H2GenerationId::for_test(99));
 3014   2527   
 3015   2528   
        let mut state = local_cell.state.lock();
 3016         -
        state.h2.install_peer_route(route, None);
        2529  +
        state.h2.attach_peer_route(route, None);
 3017   2530   
        assert!(state.h2.peer_route.is_none());
 3018   2531   
        assert_eq!(Some(generation), state.h2.accepting());
 3019   2532   
    }
 3020   2533   
 3021   2534   
    #[test]
 3022   2535   
    fn open_generation_allows_concurrent_prospective_activations() {
 3023   2536   
        let cell = cell();
 3024         -
        let (generation, _connection, _physical) = install_generation(&cell, 1);
        2537  +
        let (generation, _connection, _physical) = open_test_generation(&cell, 1);
 3025   2538   
 3026   2539   
        let first = OriginCell::select_h2(&cell).expect("first activation was not selected");
 3027   2540   
        let second =
 3028   2541   
            OriginCell::select_h2(&cell).expect("open generation serialized the second activation");
 3029   2542   
        assert_eq!((2, 0), generation_counts(&cell, generation));
 3030   2543   
        drop(first);
 3031   2544   
        drop(second);
 3032   2545   
        assert_eq!((0, 0), generation_counts(&cell, generation));
 3033   2546   
    }
 3034   2547   
 3035   2548   
    #[test]
 3036   2549   
    fn consistency_checks_do_not_repanic_during_unwind() {
 3037   2550   
        use std::panic::{catch_unwind, AssertUnwindSafe};
 3038   2551   
 3039   2552   
        let cell = cell();
 3040         -
        let (generation, _connection, _physical) = install_generation(&cell, 1);
        2553  +
        let (generation, _connection, _physical) = open_test_generation(&cell, 1);
 3041   2554   
        let activation =
 3042   2555   
            OriginCell::select_h2(&cell).expect("accepting generation did not activate");
 3043   2556   
        let (corrupt_connection, corrupt_physical) = connection(2);
 3044   2557   
        {
 3045   2558   
            let mut state = cell.state.lock();
 3046   2559   
            state.h2.generations.insert(
 3047   2560   
                H2GenerationId(99),
 3048   2561   
                H2Generation {
 3049   2562   
                    pending_waiters: BTreeSet::new(),
 3050   2563   
                    connection: corrupt_connection,
 3051   2564   
                    sender: H2Sender::test(2),
 3052         -
                    residence: H2Residence::Draining,
 3053         -
                    prospective: 0,
 3054         -
                    has_dispatched: false,
 3055         -
                    accepted: 0,
        2565  +
                    state: H2GenerationState::Draining,
        2566  +
                    prospective_requests: 0,
        2567  +
                    has_accepted_request: false,
        2568  +
                    active_requests: 0,
 3056   2569   
                    idle_deadline: None,
 3057   2570   
                },
 3058   2571   
            );
 3059   2572   
        }
 3060   2573   
 3061   2574   
        let result = catch_unwind(AssertUnwindSafe(move || {
 3062   2575   
            let _activation = activation;
 3063   2576   
            panic!("primary test panic");
 3064   2577   
        }));
 3065   2578   
        assert!(result.is_err(), "primary panic was not observed");
 3066   2579   
 3067   2580   
        let removed = {
 3068   2581   
            let mut state = cell.state.lock();
 3069   2582   
            state.h2.generations.remove(&H2GenerationId(99))
 3070   2583   
        };
 3071   2584   
        drop(removed);
 3072   2585   
        drop(corrupt_physical);
 3073   2586   
        assert!(OriginCell::close_h2(
 3074   2587   
            &cell,
 3075   2588   
            generation,
 3076   2589   
            CloseReason::PoolDropped,
 3077   2590   
        ));
 3078   2591   
    }
 3079   2592   
 3080   2593   
    #[test]
 3081   2594   
    fn removed_generation_id_cannot_activate_or_close_the_current_generation() {
 3082   2595   
        let cell = cell();
 3083         -
        let (first, first_connection, _first_physical) = install_generation(&cell, 1);
        2596  +
        let (first, first_connection, _first_physical) = open_test_generation(&cell, 1);
 3084   2597   
        let stale = H2CloseHandle::new(&cell, first);
 3085   2598   
        assert!(stale.close(CloseReason::ProtocolClosed));
 3086   2599   
        assert_eq!(
 3087   2600   
            Some(CloseReason::ProtocolClosed),
 3088         -
            first_connection.snapshot().close_reason
        2601  +
            first_connection.probe().close_reason
 3089   2602   
        );
 3090   2603   
 3091         -
        let (second, second_connection, _second_physical) = install_generation(&cell, 2);
        2604  +
        let (second, second_connection, _second_physical) = open_test_generation(&cell, 2);
 3092   2605   
        assert_ne!(first, second);
 3093         -
        assert!(OriginCell::activate_h2(&cell, first).is_none());
        2606  +
        assert!(OriginCell::activate_h2(&cell, first, cell.id.partition()).is_none());
 3094   2607   
        assert!(!stale.close(CloseReason::Poisoned));
 3095         -
        assert_eq!(None, second_connection.snapshot().close_reason);
        2608  +
        assert_eq!(None, second_connection.probe().close_reason);
 3096   2609   
        assert_eq!(Some(second), cell.accepting_h2_generation());
 3097   2610   
    }
 3098   2611   
 3099   2612   
    #[test]
 3100   2613   
    fn close_drops_detached_capacity_only_after_cell_unlock() {
 3101   2614   
        use crate::client::pool::admission::OriginAdmission;
 3102   2615   
        use std::num::NonZeroUsize;
 3103   2616   
        use std::panic::{catch_unwind, AssertUnwindSafe};
 3104   2617   
 3105   2618   
        let admission = OriginAdmission::for_test(NonZeroUsize::MIN);
 3106   2619   
        let cell = OriginAdmission::register_cell(
 3107   2620   
            &admission,
 3108   2621   
            Arc::new(OriginCell::new(
 3109   2622   
                PartitionId::from_index(1),
 3110   2623   
                OriginKey::from_parts(Scheme::HTTPS, "example.com", None).unwrap(),
 3111   2624   
                EligibilityGroup::Pool,
 3112   2625   
                Some(admission.clone()),
 3113   2626   
                None,
 3114   2627   
            )),
 3115   2628   
        );
 3116   2629   
        let info = ConnectionInfo::for_test(ConnectionId::new(1), PartitionId::from_index(1));
 3117   2630   
        let (bounded_connection, physical) = ConnectionState::pending_open(info);
 3118   2631   
        bounded_connection
 3119   2632   
            .open(Some(OriginAdmission::lease_for_test(&admission)))
 3120   2633   
            .expect("bounded test connection did not open");
 3121   2634   
        let generation =
 3122   2635   
            OriginCell::install_h2_for_test(&cell, bounded_connection.clone(), 1, None);
 3123   2636   
        drop(bounded_connection);
 3124   2637   
        drop(physical);
 3125   2638   
 3126   2639   
        // Keep an unrelated empty drain so the close transition's final
 3127   2640   
        // consistency check panics after detaching the bounded connection.
 3128   2641   
        let (corrupt_connection, corrupt_physical) = connection(2);
 3129   2642   
        {
 3130   2643   
            let mut state = cell.state.lock();
 3131   2644   
            state.h2.generations.insert(
 3132   2645   
                H2GenerationId(99),
 3133   2646   
                H2Generation {
 3134   2647   
                    pending_waiters: BTreeSet::new(),
 3135   2648   
                    connection: corrupt_connection,
 3136   2649   
                    sender: H2Sender::test(2),
 3137         -
                    residence: H2Residence::Draining,
 3138         -
                    prospective: 0,
 3139         -
                    has_dispatched: false,
 3140         -
                    accepted: 0,
        2650  +
                    state: H2GenerationState::Draining,
        2651  +
                    prospective_requests: 0,
        2652  +
                    has_accepted_request: false,
        2653  +
                    active_requests: 0,
 3141   2654   
                    idle_deadline: None,
 3142   2655   
                },
 3143   2656   
            );
 3144   2657   
        }
 3145   2658   
        drop(corrupt_physical);
 3146   2659   
 3147   2660   
        let result = catch_unwind(AssertUnwindSafe(|| {
 3148   2661   
            OriginCell::close_h2(&cell, generation, CloseReason::ProtocolClosed);
 3149   2662   
        }));
 3150   2663   
        assert!(
 3151   2664   
            result.is_err(),
 3152   2665   
            "corrupt H2 state did not reach its consistency assertion"
 3153   2666   
        );
 3154   2667   
 3155   2668   
        // This second lease exists only if unwinding dropped the connection
 3156   2669   
        // after the cell guard, allowing capacity to return through admission.
 3157   2670   
        drop(OriginAdmission::lease_for_test(&admission));
 3158   2671   
    }
 3159   2672   
 3160   2673   
    #[test]
 3161         -
    fn close_retains_an_accepted_generation_until_both_endpoints_finish() {
        2674  +
    fn close_retains_an_accepted_generation_until_both_request_sides_finish() {
 3162   2675   
        let cell = cell();
 3163         -
        let (generation, connection, _physical) = install_generation(&cell, 1);
        2676  +
        let (generation, connection, _physical) = open_test_generation(&cell, 1);
 3164   2677   
        let mut activation =
 3165   2678   
            OriginCell::select_h2(&cell).expect("accepting generation was not selected");
 3166   2679   
        let H2DispatchParts {
 3167   2680   
            sender: _sender,
 3168         -
            send_endpoint: send,
 3169         -
            receive_endpoint: receive,
        2681  +
            upload,
        2682  +
            response,
 3170   2683   
        } = activation.take_dispatch_parts();
 3171   2684   
        let dispatch = ConnectionState::try_commit_dispatch(&connection)
 3172   2685   
            .expect("open connection rejected dispatch");
 3173   2686   
        activation.accept(dispatch);
 3174   2687   
 3175   2688   
        assert!(OriginCell::close_h2(
 3176   2689   
            &cell,
 3177   2690   
            generation,
 3178   2691   
            CloseReason::ProtocolClosed
 3179   2692   
        ));
 3180   2693   
        assert!(cell.state.lock().h2.generations.contains_key(&generation));
 3181         -
        drop(send);
        2694  +
        drop(upload);
 3182   2695   
        assert!(cell.state.lock().h2.generations.contains_key(&generation));
 3183         -
        drop(receive);
        2696  +
        drop(response);
 3184   2697   
        assert!(!cell.state.lock().h2.generations.contains_key(&generation));
 3185         -
        assert_eq!(0, connection.snapshot().in_flight);
        2698  +
        assert_eq!(0, connection.probe().in_flight);
 3186   2699   
    }
 3187   2700   
 3188   2701   
    #[test]
 3189   2702   
    fn driver_task_drop_closes_its_exact_generation() {
 3190   2703   
        let cell = cell();
 3191         -
        let (generation, connection, _physical) = install_generation(&cell, 1);
        2704  +
        let (generation, connection, _physical) = open_test_generation(&cell, 1);
 3192   2705   
        let guard = H2DriverGuard::new(H2CloseHandle::new(&cell, generation));
 3193   2706   
 3194   2707   
        drop(guard);
 3195   2708   
 3196   2709   
        assert_eq!(
 3197   2710   
            Some(CloseReason::OwnerRuntimeShutdown),
 3198         -
            connection.snapshot().close_reason
        2711  +
            connection.probe().close_reason
 3199   2712   
        );
 3200   2713   
        assert_eq!(None, cell.accepting_h2_generation());
 3201   2714   
    }
 3202   2715   
 3203   2716   
    #[test]
 3204   2717   
    fn local_selection_withdraws_idle_h2_from_reclaim() {
 3205   2718   
        use crate::client::pool::admission::OriginAdmission;
 3206   2719   
        use std::num::NonZeroUsize;
 3207   2720   
 3208   2721   
        let admission = OriginAdmission::for_test(NonZeroUsize::MIN);
 3209   2722   
        let connection_cell = bounded_cell(&admission, 1);
 3210   2723   
        let requesting_cell = bounded_cell(&admission, 2);
 3211   2724   
        let info = ConnectionInfo::for_test(ConnectionId::new(1), connection_cell.id().partition());
 3212   2725   
        let (connection, _physical) = ConnectionState::pending_open(info);
 3213   2726   
        connection
 3214   2727   
            .open(Some(OriginAdmission::lease_for_test(&admission)))
 3215   2728   
            .expect("bounded HTTP/2 connection did not open");
 3216   2729   
        let generation =
 3217   2730   
            OriginCell::install_h2_for_test(&connection_cell, connection.clone(), 1, None);
 3218   2731   
        let activation = OriginCell::select_h2(&connection_cell)
 3219   2732   
            .expect("local accepting generation did not activate");
 3220   2733   
 3221   2734   
        let (waiter, demand) =
 3222   2735   
            requesting_cell.register_waiter_without_publish(ProtocolRequirement::H1Required);
 3223         -
        let action = OriginAdmission::publish_action_without_driving(
        2736  +
        let action = OriginAdmission::submit_action_without_running(
 3224   2737   
            &admission,
 3225   2738   
            requesting_cell.id().partition(),
 3226   2739   
            demand,
 3227   2740   
        );
 3228   2741   
        assert!(
 3229   2742   
            action.is_none(),
 3230   2743   
            "busy local HTTP/2 generation remained eligible for reclaim"
 3231   2744   
        );
 3232   2745   
 3233   2746   
        assert!(OriginCell::cancel_waiter(&requesting_cell, waiter));
 3234   2747   
        drop(activation);
 3235         -
        assert_eq!(None, connection.snapshot().close_reason);
        2748  +
        assert_eq!(None, connection.probe().close_reason);
 3236   2749   
        assert!(OriginCell::close_h2(
 3237   2750   
            &connection_cell,
 3238   2751   
            generation,
 3239   2752   
            CloseReason::PoolDropped,
 3240   2753   
        ));
 3241   2754   
    }
 3242   2755   
 3243   2756   
    #[test]
 3244   2757   
    fn h1_required_demand_preserves_h2_without_an_http1_guarantee() {
 3245   2758   
        use crate::client::pool::admission::OriginAdmission;
 3246   2759   
        use std::num::NonZeroUsize;
 3247   2760   
 3248   2761   
        let admission = OriginAdmission::for_test_with_h2_reclaim(NonZeroUsize::MIN, false);
 3249   2762   
        let connection_cell = bounded_cell(&admission, 1);
 3250   2763   
        let requesting_cell = bounded_cell(&admission, 2);
 3251   2764   
        let info = ConnectionInfo::for_test(ConnectionId::new(1), connection_cell.id().partition());
 3252   2765   
        let (connection, _physical) = ConnectionState::pending_open(info);
 3253   2766   
        connection
 3254   2767   
            .open(Some(OriginAdmission::lease_for_test(&admission)))
 3255   2768   
            .expect("bounded HTTP/2 connection did not open");
 3256   2769   
        let generation =
 3257   2770   
            OriginCell::install_h2_for_test(&connection_cell, connection.clone(), 1, None);
 3258   2771   
 3259   2772   
        let waiter = OriginCell::register_waiter(&requesting_cell, ProtocolRequirement::H1Required);
 3260   2773   
        assert!(
 3261   2774   
            requesting_cell.take_ready_event(waiter).is_none(),
 3262   2775   
            "H1-required demand reclaimed capacity without an HTTP/1 guarantee"
 3263   2776   
        );
 3264         -
        assert_eq!(None, connection.snapshot().close_reason);
        2777  +
        assert_eq!(None, connection.probe().close_reason);
 3265   2778   
        assert_eq!(Some(generation), connection_cell.accepting_h2_generation());
 3266   2779   
 3267   2780   
        assert!(OriginCell::close_h2(
 3268   2781   
            &connection_cell,
 3269   2782   
            generation,
 3270   2783   
            CloseReason::ProtocolClosed,
 3271   2784   
        ));
 3272         -
        let super::super::AcquisitionEvent::Establish(permit) = requesting_cell
        2785  +
        let super::super::AcquisitionStep::StartEstablishment(permit) = requesting_cell
 3273   2786   
            .take_ready_event(waiter)
 3274   2787   
            .expect("ordinary H2 close did not release capacity")
 3275   2788   
        else {
 3276   2789   
            panic!("released capacity produced a non-establishment result");
 3277   2790   
        };
 3278   2791   
        drop(permit);
 3279   2792   
        assert_eq!(1, admission.available_capacity_for_test());
 3280   2793   
    }
 3281   2794   
 3282   2795   
    #[test]
 3283   2796   
    fn h1_required_demand_reclaims_idle_h2_capacity() {
 3284   2797   
        use crate::client::pool::admission::OriginAdmission;
 3285   2798   
        use std::num::NonZeroUsize;
 3286   2799   
 3287   2800   
        let admission = OriginAdmission::for_test(NonZeroUsize::MIN);
 3288   2801   
        let connection_cell = bounded_cell(&admission, 1);
 3289   2802   
        let requesting_cell = bounded_cell(&admission, 2);
 3290   2803   
        let info = ConnectionInfo::for_test(ConnectionId::new(1), connection_cell.id().partition());
 3291   2804   
        let (connection, _physical) = ConnectionState::pending_open(info);
 3292   2805   
        connection
 3293   2806   
            .open(Some(OriginAdmission::lease_for_test(&admission)))
 3294   2807   
            .expect("bounded HTTP/2 connection did not open");
 3295   2808   
        OriginCell::install_h2_for_test(&connection_cell, connection.clone(), 1, None);
 3296   2809   
 3297   2810   
        let waiter = OriginCell::register_waiter(&requesting_cell, ProtocolRequirement::H1Required);
 3298         -
        let super::super::AcquisitionEvent::Establish(permit) = requesting_cell
        2811  +
        let super::super::AcquisitionStep::StartEstablishment(permit) = requesting_cell
 3299   2812   
            .take_ready_event(waiter)
 3300   2813   
            .expect("idle HTTP/2 reclaim did not deliver capacity")
 3301   2814   
        else {
 3302   2815   
            panic!("idle HTTP/2 reclaim produced a non-capacity result");
 3303   2816   
        };
 3304   2817   
 3305   2818   
        assert_eq!(
 3306   2819   
            Some(CloseReason::Reclaimed),
 3307         -
            connection.snapshot().close_reason
        2820  +
            connection.probe().close_reason
 3308   2821   
        );
 3309   2822   
        assert_eq!(None, connection_cell.accepting_h2_generation());
 3310   2823   
        drop(permit);
 3311   2824   
        assert_eq!(1, admission.available_capacity_for_test());
 3312   2825   
    }
 3313   2826   
 3314   2827   
    #[test]
 3315   2828   
    fn h1_required_demand_waits_for_active_h2_before_reclaim() {
 3316   2829   
        use crate::client::pool::admission::OriginAdmission;
 3317   2830   
        use std::num::NonZeroUsize;
 3318   2831   
 3319   2832   
        let admission = OriginAdmission::for_test(NonZeroUsize::MIN);
 3320   2833   
        let connection_cell = bounded_cell(&admission, 1);
 3321   2834   
        let requesting_cell = bounded_cell(&admission, 2);
 3322   2835   
        let info = ConnectionInfo::for_test(ConnectionId::new(1), connection_cell.id().partition());
 3323   2836   
        let (connection, _physical) = ConnectionState::pending_open(info);
 3324   2837   
        connection
 3325   2838   
            .open(Some(OriginAdmission::lease_for_test(&admission)))
 3326   2839   
            .expect("bounded HTTP/2 connection did not open");
 3327   2840   
        let generation =
 3328   2841   
            OriginCell::install_h2_for_test(&connection_cell, connection.clone(), 1, None);
 3329         -
        let activation = OriginCell::activate_h2(&connection_cell, generation)
 3330         -
            .expect("accepting HTTP/2 generation did not activate");
        2842  +
        let activation = OriginCell::activate_h2(
        2843  +
            &connection_cell,
        2844  +
            generation,
        2845  +
            requesting_cell.id().partition(),
        2846  +
        )
        2847  +
        .expect("accepting HTTP/2 generation did not activate");
 3331   2848   
 3332   2849   
        let waiter = OriginCell::register_waiter(&requesting_cell, ProtocolRequirement::H1Required);
 3333   2850   
        assert!(
 3334   2851   
            requesting_cell.take_ready_event(waiter).is_none(),
 3335   2852   
            "active HTTP/2 generation was reclaimed"
 3336   2853   
        );
 3337         -
        assert_eq!(None, connection.snapshot().close_reason);
        2854  +
        assert_eq!(None, connection.probe().close_reason);
 3338   2855   
 3339   2856   
        drop(activation);
 3340         -
        let super::super::AcquisitionEvent::Establish(permit) = requesting_cell
        2857  +
        let super::super::AcquisitionStep::StartEstablishment(permit) = requesting_cell
 3341   2858   
            .take_ready_event(waiter)
 3342   2859   
            .expect("idle transition did not resume H1-required demand")
 3343   2860   
        else {
 3344   2861   
            panic!("idle HTTP/2 reclaim produced a non-capacity result");
 3345   2862   
        };
 3346   2863   
        assert_eq!(
 3347   2864   
            Some(CloseReason::Reclaimed),
 3348         -
            connection.snapshot().close_reason
        2865  +
            connection.probe().close_reason
 3349   2866   
        );
 3350   2867   
        drop(permit);
 3351   2868   
        assert_eq!(1, admission.available_capacity_for_test());
 3352   2869   
    }
 3353   2870   
 3354   2871   
    #[test]
 3355   2872   
    fn pool_shutdown_closes_an_accepting_generation() {
 3356   2873   
        let cell = cell();
 3357         -
        let (_generation, connection, _physical) = install_generation(&cell, 1);
        2874  +
        let (_generation, connection, _physical) = open_test_generation(&cell, 1);
 3358   2875   
 3359   2876   
        OriginCell::close_all(&cell, CloseReason::PoolDropped);
 3360   2877   
 3361   2878   
        assert_eq!(
 3362   2879   
            Some(CloseReason::PoolDropped),
 3363         -
            connection.snapshot().close_reason
        2880  +
            connection.probe().close_reason
 3364   2881   
        );
 3365   2882   
        assert_eq!(None, cell.accepting_h2_generation());
 3366   2883   
    }
 3367   2884   
}
 3368   2885   
 3369   2886   
#[cfg(all(test, smithy_http_client_loom))]
 3370   2887   
mod loom_tests {
 3371   2888   
    use super::*;
 3372   2889   
    use crate::client::pool::connection::{CloseReason, ConnectionInfo};
 3373   2890   
    use crate::client::pool::origin::OriginKey;
 3374   2891   
    use crate::client::pool::partition::EligibilityGroup;
 3375   2892   
    use aws_smithy_runtime_api::client::connection::ConnectionId;
 3376   2893   
    use http_1x::uri::Scheme;
 3377   2894   
 3378   2895   
    fn cell() -> Arc<OriginCell> {
 3379   2896   
        Arc::new(OriginCell::new(
 3380   2897   
            PartitionId::from_index(1),
 3381   2898   
            OriginKey::from_parts(Scheme::HTTPS, "example.com", None).unwrap(),
 3382   2899   
            EligibilityGroup::Pool,
 3383   2900   
            None,
 3384   2901   
            None,
 3385   2902   
        ))
 3386   2903   
    }
 3387   2904   
 3388         -
    fn install_generation(cell: &Arc<OriginCell>) -> (H2GenerationId, Arc<ConnectionState>) {
        2905  +
    fn open_test_generation(cell: &Arc<OriginCell>) -> (H2GenerationId, Arc<ConnectionState>) {
 3389   2906   
        let (connection, _physical) = ConnectionState::unbounded(ConnectionInfo::for_test(
 3390   2907   
            ConnectionId::new(1),
 3391   2908   
            PartitionId::from_index(1),
 3392   2909   
        ));
 3393   2910   
        let generation = OriginCell::install_h2_for_test(cell, connection.clone(), 1, None);
 3394   2911   
        (generation, connection)
 3395   2912   
    }
 3396   2913   
 3397   2914   
    #[test]
 3398   2915   
    fn activation_linearizes_against_generation_close() {
 3399   2916   
        loom::model(|| {
 3400   2917   
            let cell = cell();
 3401         -
            let (generation, connection) = install_generation(&cell);
        2918  +
            let (generation, connection) = open_test_generation(&cell);
 3402   2919   
            let activating_cell = cell.clone();
 3403         -
            let activation =
 3404         -
                loom::thread::spawn(move || OriginCell::activate_h2(&activating_cell, generation));
        2920  +
            let activation = loom::thread::spawn(move || {
        2921  +
                OriginCell::activate_h2(
        2922  +
                    &activating_cell,
        2923  +
                    generation,
        2924  +
                    activating_cell.id().partition(),
        2925  +
                )
        2926  +
            });
 3405   2927   
            let closing_cell = cell.clone();
 3406   2928   
            let close = loom::thread::spawn(move || {
 3407   2929   
                OriginCell::close_h2(&closing_cell, generation, CloseReason::Poisoned)
 3408   2930   
            });
 3409   2931   
 3410   2932   
            drop(activation.join().unwrap());
 3411   2933   
            assert!(close.join().unwrap());
 3412         -
            assert_eq!(
 3413         -
                Some(CloseReason::Poisoned),
 3414         -
                connection.snapshot().close_reason
 3415         -
            );
        2934  +
            assert_eq!(Some(CloseReason::Poisoned), connection.probe().close_reason);
 3416   2935   
            assert!(!cell.state.lock().h2.generations.contains_key(&generation));
 3417   2936   
        });
 3418   2937   
    }
 3419   2938   
 3420   2939   
    #[test]
 3421         -
    fn concurrent_lease_endpoint_completion_releases_one_dispatch() {
        2940  +
    fn concurrent_request_side_completion_releases_one_dispatch() {
 3422   2941   
        loom::model(|| {
 3423   2942   
            let cell = cell();
 3424         -
            let (generation, connection) = install_generation(&cell);
 3425         -
            let mut activation =
 3426         -
                OriginCell::activate_h2(&cell, generation).expect("generation did not activate");
        2943  +
            let (generation, connection) = open_test_generation(&cell);
        2944  +
            let mut activation = OriginCell::activate_h2(&cell, generation, cell.id().partition())
        2945  +
                .expect("generation did not activate");
 3427   2946   
            let H2DispatchParts {
 3428   2947   
                sender: _sender,
 3429         -
                send_endpoint: send,
 3430         -
                receive_endpoint: receive,
        2948  +
                upload,
        2949  +
                response,
 3431   2950   
            } = activation.take_dispatch_parts();
 3432   2951   
            let dispatch = ConnectionState::try_commit_dispatch(&connection)
 3433   2952   
                .expect("open connection rejected dispatch");
 3434   2953   
            activation.accept(dispatch);
 3435   2954   
 3436         -
            let send = loom::thread::spawn(move || drop(send));
 3437         -
            let receive = loom::thread::spawn(move || drop(receive));
 3438         -
            send.join().unwrap();
 3439         -
            receive.join().unwrap();
        2955  +
            let upload = loom::thread::spawn(move || drop(upload));
        2956  +
            let response = loom::thread::spawn(move || drop(response));
        2957  +
            upload.join().unwrap();
        2958  +
            response.join().unwrap();
 3440   2959   
 3441         -
            assert_eq!(0, connection.snapshot().in_flight);
        2960  +
            assert_eq!(0, connection.probe().in_flight);
 3442   2961   
            let state = cell.state.lock();
 3443   2962   
            let record = state
 3444   2963   
                .h2
 3445   2964   
                .generations
 3446   2965   
                .get(&generation)
 3447   2966   
                .expect("accepting generation disappeared");
 3448         -
            assert_eq!(0, record.accepted);
        2967  +
            assert_eq!(0, record.active_requests);
 3449   2968   
        });
 3450   2969   
    }
 3451   2970   
 3452   2971   
    #[test]
 3453         -
    fn generation_close_waits_for_both_lease_endpoints() {
        2972  +
    fn generation_close_waits_for_both_request_sides() {
 3454   2973   
        loom::model(|| {
 3455   2974   
            let cell = cell();
 3456   2975   
            let (connection, _physical) = ConnectionState::unbounded(ConnectionInfo::for_test(
 3457   2976   
                ConnectionId::new(1),
 3458   2977   
                PartitionId::from_index(1),
 3459   2978   
            ));
 3460   2979   
            let generation = OriginCell::install_h2_for_test(&cell, connection.clone(), 1, None);
 3461         -
            let mut activation =
 3462         -
                OriginCell::activate_h2(&cell, generation).expect("generation did not activate");
        2980  +
            let mut activation = OriginCell::activate_h2(&cell, generation, cell.id().partition())
        2981  +
                .expect("generation did not activate");
 3463   2982   
            let H2DispatchParts {
 3464   2983   
                sender: _sender,
 3465         -
                send_endpoint: send,
 3466         -
                receive_endpoint: receive,
        2984  +
                upload,
        2985  +
                response,
 3467   2986   
            } = activation.take_dispatch_parts();
 3468   2987   
            let dispatch = ConnectionState::try_commit_dispatch(&connection)
 3469   2988   
                .expect("open connection rejected dispatch");
 3470   2989   
            activation.accept(dispatch);
 3471   2990   
 3472         -
            let first_endpoint = loom::thread::spawn(move || drop(send));
        2991  +
            let first_side = loom::thread::spawn(move || drop(upload));
 3473   2992   
            let closing_cell = cell.clone();
 3474   2993   
            let close = loom::thread::spawn(move || {
 3475   2994   
                OriginCell::close_h2(&closing_cell, generation, CloseReason::ProtocolClosed)
 3476   2995   
            });
 3477         -
            first_endpoint.join().unwrap();
        2996  +
            first_side.join().unwrap();
 3478   2997   
            assert!(close.join().unwrap());
 3479   2998   
 3480         -
            assert_eq!(1, connection.snapshot().in_flight);
        2999  +
            assert_eq!(1, connection.probe().in_flight);
 3481   3000   
            assert_eq!(
 3482   3001   
                Some((0, 1)),
 3483   3002   
                cell.h2_request_counts(generation),
 3484         -
                "draining generation released before its second endpoint"
        3003  +
                "draining generation released before its second request side"
 3485   3004   
            );
 3486         -
            drop(receive);
 3487         -
            assert_eq!(0, connection.snapshot().in_flight);
        3005  +
            drop(response);
        3006  +
            assert_eq!(0, connection.probe().in_flight);
 3488   3007   
            assert_eq!(
 3489   3008   
                None,
 3490   3009   
                cell.h2_request_counts(generation),
 3491   3010   
                "finished draining generation remained installed"
 3492   3011   
            );
 3493   3012   
        });
 3494   3013   
    }
 3495   3014   
}