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)
|
2842 + | let activation = OriginCell::activate_h2(
|
2843 + | &connection_cell,
|
2844 + | generation,
|
2845 + | requesting_cell.id().partition(),
|
2846 + | )
|
3330 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 | }
|