2 * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
10 #include "AccessLogEntry.h"
11 #include "base/AsyncCallbacks.h"
12 #include "base/CodeContext.h"
13 #include "CachePeer.h"
14 #include "errorpage.h"
16 #include "HappyConnOpener.h"
17 #include "HttpRequest.h"
18 #include "ip/QosConfig.h"
19 #include "neighbors.h"
21 #include "PeerPoolMgr.h"
22 #include "sbuf/Stream.h"
23 #include "SquidConfig.h"
25 CBDATA_CLASS_INIT(HappyConnOpener
);
27 // HappyOrderEnforcer optimizes enforcement of the "pause before opening a spare
28 // connection" requirements. Its inefficient alternative would add hundreds of
29 // concurrent events to the Squid event queue in many busy configurations, one
30 // concurrent event per concurrent HappyConnOpener job.
32 // EventScheduler::schedule() uses linear search to find the right place for a
33 // new event; having hundreds of concurrent events is prohibitively expensive.
34 // Both alternatives may have comparable high rate of eventAdd() calls, but
35 // HappyOrderEnforcer usually schedules the first or second event (as opposed to
36 // events that would be fired after hundreds of already scheduled events, making
37 // that linear search a lot longer).
39 // EventScheduler::cancel() also uses linear search. HappyOrderEnforcer does not
40 // need to cancel scheduled events, while its inefficient alternative may cancel
41 // events at a rate comparable to the high eventAdd() rate -- many events would
42 // be scheduled in vain because external factors would speed up (or make
43 // unnecessary) spare connection attempts, canceling the wait.
45 // This optimization is possible only where each job needs to pause for the same
46 // amount of time, creating a naturally ordered list of jobs waiting to be
47 // resumed. This is why two HappyOrderEnforcers are needed to efficiently honor
48 // both happy_eyeballs_connect_timeout and happy_eyeballs_connect_gap
51 /// Efficiently drains a FIFO HappyConnOpener queue while delaying each "pop"
52 /// event by the time determined by the top element currently in the queue. Its
53 /// current cbdata-free implementation assumes static storage duration.
54 class HappyOrderEnforcer
57 /// \param aName names scheduled events, for debugging
58 HappyOrderEnforcer(const char *aName
): name(aName
) {}
60 /// resumes jobs that need resuming (if any)
63 /// starts managing the job's wait; the job should expect a call back
64 void enqueue(HappyConnOpener
&);
66 /// stops managing the job's wait; cancels the pending callback, if any
67 void dequeue(HappyConnOpener
&);
69 const char * const name
; ///< waiting event name, for debugging
72 virtual bool readyNow(const HappyConnOpener
&) const = 0;
73 virtual AsyncCall::Pointer
notify(const CbcPointer
<HappyConnOpener
> &) = 0;
75 bool waiting() const { return waitEnd_
> 0; }
76 bool startedWaiting(const HappyAbsoluteTime lastStart
, const int cfgTimeoutMsec
) const;
79 static void NoteWaitOver(void *raw
);
82 HappySpareWaitList jobs_
; ///< queued jobs waiting their turn
83 mutable HappyAbsoluteTime waitEnd_
= 0; ///< expected NoteWaitOver() call time (or zero)
86 std::ostream
&operator <<(std::ostream
&os
, const HappyConnOpenerAnswer
&answer
)
88 if (answer
.error
.set())
91 os
<< (answer
.reused
? "reused " : "new ") << answer
.conn
;
92 if (answer
.n_tries
!= 1)
93 os
<< " after " << answer
.n_tries
;
97 /// enforces happy_eyeballs_connect_timeout
98 class PrimeChanceGiver
: public HappyOrderEnforcer
101 PrimeChanceGiver(): HappyOrderEnforcer("happy_eyeballs_connect_timeout enforcement") {}
103 /* HappyOrderEnforcer API */
104 bool readyNow(const HappyConnOpener
&job
) const override
;
107 /* HappyOrderEnforcer API */
108 AsyncCall::Pointer
notify(const CbcPointer
<HappyConnOpener
> &) override
;
111 /// enforces happy_eyeballs_connect_gap and happy_eyeballs_connect_limit
112 class SpareAllowanceGiver
: public HappyOrderEnforcer
115 SpareAllowanceGiver(): HappyOrderEnforcer("happy_eyeballs_connect_gap/happy_eyeballs_connect_limit enforcement") {}
117 /* HappyOrderEnforcer API */
118 bool readyNow(const HappyConnOpener
&job
) const override
;
120 /// reacts to HappyConnOpener discovering readyNow() conditions for a spare path
121 /// the caller must attempt to open a spare connection immediately
122 void jobGotInstantAllowance();
124 /// reacts to HappyConnOpener getting a spare connection opening result
125 void jobUsedAllowance();
127 /// reacts to HappyConnOpener dropping its spare connection allowance
128 void jobDroppedAllowance();
131 /* HappyOrderEnforcer API */
132 AsyncCall::Pointer
notify(const CbcPointer
<HappyConnOpener
> &) override
;
134 bool concurrencyLimitReached() const;
135 void recordAllowance();
136 void forgetAllowance();
138 /// the time of the last noteSpareAllowance() call
139 HappyAbsoluteTime lastAllowanceStart
= 0;
141 /// the number of noteSpareAllowance() calls not already
142 /// returned via jobUsedAllowance() or jobDroppedAllowance()
143 int concurrencyLevel
= 0;
146 PrimeChanceGiver ThePrimeChanceGiver
;
147 SpareAllowanceGiver TheSpareAllowanceGiver
;
149 /* HappyOrderEnforcer */
152 HappyOrderEnforcer::enqueue(HappyConnOpener
&job
)
154 Must(!job
.spareWaiting
.callback
);
155 jobs_
.emplace_back(&job
);
156 job
.spareWaiting
.position
= std::prev(jobs_
.end());
157 job
.spareWaiting
.codeContext
= CodeContext::Current();
161 HappyOrderEnforcer::dequeue(HappyConnOpener
&job
)
163 if (job
.spareWaiting
.callback
) {
164 job
.spareWaiting
.callback
->cancel("HappyOrderEnforcer::dequeue");
165 job
.spareWaiting
.callback
= nullptr;
167 Must(!jobs_
.empty());
168 jobs_
.erase(job
.spareWaiting
.position
);
173 HappyOrderEnforcer::checkpoint()
175 while (!jobs_
.empty()) {
176 if (const auto jobPtr
= jobs_
.front().valid()) {
179 break; // the next job cannot be ready earlier (FIFO)
180 CallBack(job
.spareWaiting
.codeContext
, [&] {
181 job
.spareWaiting
.callback
= notify(jobPtr
); // and fall through to the next job
189 HappyOrderEnforcer::startedWaiting(const HappyAbsoluteTime lastStart
, const int cfgTimeoutMsec
) const
191 // Normally, the job would not even be queued if there is no timeout. This
192 // check handles reconfiguration that happened after this job was queued.
193 if (cfgTimeoutMsec
<= 0)
196 // convert to seconds and adjust for SMP workers to keep aggregated load in
197 // check despite the lack of coordination among workers
198 const auto tout
= static_cast<HappyAbsoluteTime
>(cfgTimeoutMsec
) * Config
.workers
/ 1000.0;
199 const auto newWaitEnd
= std::min(lastStart
, current_dtime
) + tout
;
200 if (newWaitEnd
<= current_dtime
)
201 return false; // no need to wait
203 // We cannot avoid event accumulation because calling eventDelete() is
204 // unsafe, but any accumulation will be small because it can only be caused
205 // by hot reconfiguration changes or current time jumps.
206 if (!waiting() || newWaitEnd
< waitEnd_
) {
207 const auto waitTime
= newWaitEnd
- current_dtime
;
208 eventAdd(name
, &HappyOrderEnforcer::NoteWaitOver
, const_cast<HappyOrderEnforcer
*>(this), waitTime
, 0, false);
209 waitEnd_
= newWaitEnd
;
217 HappyOrderEnforcer::NoteWaitOver(void *raw
)
220 static_cast<HappyOrderEnforcer
*>(raw
)->noteWaitOver();
224 HappyOrderEnforcer::noteWaitOver()
231 /* PrimeChanceGiver */
234 PrimeChanceGiver::readyNow(const HappyConnOpener
&job
) const
236 return !startedWaiting(job
.primeStart
, Config
.happyEyeballs
.connect_timeout
);
240 PrimeChanceGiver::notify(const CbcPointer
<HappyConnOpener
> &job
)
242 return CallJobHere(17, 5, job
, HappyConnOpener
, noteGavePrimeItsChance
);
245 /* SpareAllowanceGiver */
248 SpareAllowanceGiver::readyNow(const HappyConnOpener
&) const
250 return !concurrencyLimitReached() &&
251 !startedWaiting(lastAllowanceStart
, Config
.happyEyeballs
.connect_gap
);
255 SpareAllowanceGiver::notify(const CbcPointer
<HappyConnOpener
> &job
)
258 return CallJobHere(17, 5, job
, HappyConnOpener
, noteSpareAllowance
);
262 SpareAllowanceGiver::jobGotInstantAllowance()
268 SpareAllowanceGiver::jobUsedAllowance()
274 SpareAllowanceGiver::jobDroppedAllowance()
276 // Without happy_eyeballs_connect_gap, lastAllowanceStart does not matter.
277 // Otherwise, the dropped allowance ought to be the last one, and since it
278 // was allowed, we would still observe the gap even if we do not wait now.
279 lastAllowanceStart
= 0;
284 /// account for the given allowance
286 SpareAllowanceGiver::recordAllowance()
289 lastAllowanceStart
= current_dtime
;
290 // not a checkpoint(): no other spare can become ready here
294 SpareAllowanceGiver::forgetAllowance()
296 Must(concurrencyLevel
);
301 /// whether opening a spare connection now would violate happy_eyeballs_connect_limit
303 SpareAllowanceGiver::concurrencyLimitReached() const
305 if (Config
.happyEyeballs
.connect_limit
< 0)
306 return false; // no limit
308 if (Config
.happyEyeballs
.connect_limit
== 0)
309 return true; // concurrent spares prohibited regardless of spare level
311 // adjust for SMP workers to keep aggregated spare level in check despite
312 // the lack of coordination among workers
313 const auto aggregateLevel
= concurrencyLevel
* Config
.workers
;
314 return aggregateLevel
>= Config
.happyEyeballs
.connect_limit
;
317 /* HappyConnOpenerAnswer */
319 HappyConnOpenerAnswer::~HappyConnOpenerAnswer()
321 // XXX: When multiple copies of an answer exist, this delete in one copy
322 // invalidates error in other copies -- their error.get() returns nil. The
323 // current code "works", but probably only because the initiator gets the
324 // error before any answer copies are deleted. Same in ~EncryptorAnswer.
328 /* HappyConnOpener */
330 HappyConnOpener::HappyConnOpener(const ResolvedPeers::Pointer
&dests
, const AsyncCallback
<Answer
> &callback
, const HttpRequest::Pointer
&request
, const time_t aFwdStart
, const int tries
, const AccessLogEntry::Pointer
&anAle
):
331 AsyncJob("HappyConnOpener"),
335 prime(&HappyConnOpener::notePrimeConnectDone
, "HappyConnOpener::notePrimeConnectDone"),
336 spare(&HappyConnOpener::noteSpareConnectDone
, "HappyConnOpener::noteSpareConnectDone"),
341 assert(destinations
);
344 HappyConnOpener::~HappyConnOpener()
351 HappyConnOpener::setHost(const char *h
)
359 HappyConnOpener::start()
361 destinations
->notificationPending
= false;
362 checkForNewConnection();
366 HappyConnOpener::doneAll() const
369 return true; // (probably found a good path and) informed the requestor
371 // TODO: Expose AsyncCall::canFire() instead so that code like this can
372 // detect gone initiators without the need to explicitly cancel callbacks.
373 if (callback_
->canceled())
374 return true; // the requestor is gone or has lost interest
379 if (ranOutOfTimeOrAttempts())
380 return true; // trying new connection paths prohibited
382 if (destinations
->empty() && destinations
->destinationsFinalized
)
383 return true; // there are no more paths to try
389 HappyConnOpener::swanSong()
393 if (callback_
&& !callback_
->canceled())
397 cancelSpareWait("HappyConnOpener object destructed");
399 // TODO: Find an automated, faster way to kill no-longer-needed jobs.
402 cancelAttempt(prime
, "job finished during a prime attempt");
406 cancelAttempt(spare
, "job finished during a spare attempt");
407 if (gotSpareAllowance
) {
408 TheSpareAllowanceGiver
.jobDroppedAllowance();
409 gotSpareAllowance
= false;
413 AsyncJob::swanSong();
416 /// HappyConnOpener::Attempt printer for debugging
418 operator <<(std::ostream
&os
, const HappyConnOpener::Attempt
&attempt
)
422 else if (attempt
.path
->isOpen())
423 os
<< "FD " << attempt
.path
->fd
;
424 else if (attempt
.connWait
)
425 os
<< attempt
.connWait
;
426 else // destination is known; connection closed (and we are not opening any)
427 os
<< attempt
.path
->id
;
432 HappyConnOpener::status() const
434 // TODO: In a redesigned status() API, the caller may mimic this approach.
442 os
<< "Stopped:" << stopReason
;
444 os
<< "prime:" << prime
;
446 os
<< "spare:" << spare
;
448 os
<< " tries:" << n_tries
;
449 os
<< " dst:" << *destinations
;
450 os
<< ' ' << id
<< ']';
457 * Create "503 Service Unavailable" or "504 Gateway Timeout" error depending
458 * on whether this is a validation request.
460 * RFC 9111 section 5.2.2 Cache-Control response directives
462 * section 5.2.2.2 must-revalidate
463 * " if a cache is disconnected, the cache MUST generate an error response
464 * rather than reuse the stale response. The generated status code
465 * SHOULD be 504 (Gateway Timeout) unless another error status code is
468 * section 5.2.2.8 proxy-revalidate
469 * " analogous to must-revalidate "
471 * section 5.2.2.10 s-maxage
472 * " incorporates the semantics of the
473 * proxy-revalidate response directive "
476 HappyConnOpener::makeError(const err_type type
) const
478 const auto statusCode
= cause
->flags
.needValidation
?
479 Http::scGatewayTimeout
: Http::scServiceUnavailable
;
480 return new ErrorState(type
, statusCode
, cause
.getRaw(), ale
);
483 /// \returns pre-filled Answer if the initiator needs an answer (or nil)
484 HappyConnOpener::Answer
*
485 HappyConnOpener::futureAnswer(const PeerConnectionPointer
&conn
)
487 if (callback_
&& !callback_
->canceled()) {
488 auto &answer
= callback_
.answer();
490 answer
.n_tries
= n_tries
;
493 (void)callback_
.release();
497 /// send a successful result to the initiator (if it still needs an answer)
499 HappyConnOpener::sendSuccess(const PeerConnectionPointer
&conn
, const bool reused
, const char *connKind
)
501 debugs(17, 4, connKind
<< ": " << conn
);
502 if (auto *answer
= futureAnswer(conn
)) {
503 answer
->reused
= reused
;
504 assert(!answer
->error
);
505 ScheduleCallHere(callback_
.release());
509 /// cancels the in-progress attempt, making its path a future candidate
511 HappyConnOpener::cancelAttempt(Attempt
&attempt
, const char *reason
)
514 destinations
->reinstatePath(attempt
.path
); // before attempt.cancel() clears path
515 attempt
.cancel(reason
);
518 /// inform the initiator about our failure to connect (if needed)
520 HappyConnOpener::sendFailure()
522 debugs(17, 3, lastFailedConnection
);
523 if (auto *answer
= futureAnswer(lastFailedConnection
)) {
525 lastError
= makeError(ERR_GATEWAY_FAILURE
);
526 answer
->error
= lastError
;
527 assert(answer
->error
.valid());
528 lastError
= nullptr; // the answer owns it now
529 ScheduleCallHere(callback_
.release());
534 HappyConnOpener::noteCandidatesChange()
536 destinations
->notificationPending
= false;
537 checkForNewConnection();
540 /// starts opening (or reusing) a connection to the given destination
542 HappyConnOpener::startConnecting(Attempt
&attempt
, PeerConnectionPointer
&dest
)
545 Must(!attempt
.connWait
);
548 const auto bumpThroughPeer
= cause
->flags
.sslBumped
&& dest
->getPeer();
549 const auto canReuseOld
= allowPconn_
&& !bumpThroughPeer
;
550 if (!canReuseOld
|| !reuseOldConnection(dest
))
551 openFreshConnection(attempt
, dest
);
554 /// reuses a persistent connection to the given destination (if possible)
555 /// \returns true if and only if reuse was possible
556 /// must be called via startConnecting()
558 HappyConnOpener::reuseOldConnection(PeerConnectionPointer
&dest
)
562 if (const auto pconn
= fwdPconnPool
->pop(dest
, host_
, retriable_
)) {
564 dest
.finalize(pconn
);
565 sendSuccess(dest
, true, "reused connection");
572 /// opens a fresh connection to the given destination
573 /// must be called via startConnecting()
575 HappyConnOpener::openFreshConnection(Attempt
&attempt
, PeerConnectionPointer
&dest
)
577 #if URL_CHECKSUM_DEBUG
578 entry
->mem_obj
->checkUrlChecksum();
581 const auto conn
= dest
->cloneProfile();
582 GetMarkingsToServer(cause
.getRaw(), *conn
);
584 typedef CommCbMemFunT
<HappyConnOpener
, CommConnectCbParams
> Dialer
;
585 AsyncCall::Pointer callConnect
= asyncCall(48, 5, attempt
.callbackMethodName
,
586 Dialer(this, attempt
.callbackMethod
));
587 const time_t connTimeout
= dest
->connectTimeout(fwdStart
);
588 auto cs
= new Comm::ConnOpener(conn
, callConnect
, connTimeout
);
589 if (!conn
->getPeer())
592 attempt
.path
= dest
; // but not the being-opened conn!
593 attempt
.connWait
.start(cs
, callConnect
);
596 /// Comm::ConnOpener callback for the prime connection attempt
598 HappyConnOpener::notePrimeConnectDone(const CommConnectCbParams
¶ms
)
600 handleConnOpenerAnswer(prime
, params
, "new prime connection");
603 /// Comm::ConnOpener callback for the spare connection attempt
605 HappyConnOpener::noteSpareConnectDone(const CommConnectCbParams
¶ms
)
607 if (gotSpareAllowance
) {
608 TheSpareAllowanceGiver
.jobUsedAllowance();
609 gotSpareAllowance
= false;
611 handleConnOpenerAnswer(spare
, params
, "new spare connection");
614 /// prime/spare-agnostic processing of a Comm::ConnOpener result
616 HappyConnOpener::handleConnOpenerAnswer(Attempt
&attempt
, const CommConnectCbParams
¶ms
, const char *what
)
620 // finalize the previously selected path before attempt.finish() forgets it
621 auto handledPath
= attempt
.path
;
622 handledPath
.finalize(params
.conn
); // closed on errors
627 if (params
.flag
== Comm::OK
) {
628 sendSuccess(handledPath
, false, what
);
632 debugs(17, 8, what
<< " failed: " << params
.conn
);
634 // remember the last failure (we forward it if we cannot connect anywhere)
635 lastFailedConnection
= handledPath
;
637 lastError
= nullptr; // in case makeError() throws
638 lastError
= makeError(ERR_CONNECT_FAIL
);
639 lastError
->xerrno
= params
.xerrno
;
641 NoteOutgoingConnectionFailure(params
.conn
->getPeer(), lastError
->httpStatus
);
644 updateSpareWaitAfterPrimeFailure();
646 checkForNewConnection();
649 /// reacts to a prime attempt failure
651 HappyConnOpener::updateSpareWaitAfterPrimeFailure()
657 if (destinations
->doneWithPrimes(*currentPeer
)) {
658 cancelSpareWait("all primes failed");
659 ignoreSpareRestrictions
= true;
660 return; // checkForNewConnection() will open a spare connection ASAP
663 if (spareWaiting
.toGivePrimeItsChance
)
664 stopGivingPrimeItsChance();
666 // may still be spareWaiting.forSpareAllowance or
667 // may still be spareWaiting.forPrimesToFail
670 /// called when the prime attempt has used up its chance for a solo victory
672 HappyConnOpener::stopGivingPrimeItsChance() {
673 Must(spareWaiting
.toGivePrimeItsChance
);
674 spareWaiting
.toGivePrimeItsChance
= false;
675 ThePrimeChanceGiver
.dequeue(*this);
678 /// called when the spare attempt should no longer obey spare connection limits
680 HappyConnOpener::stopWaitingForSpareAllowance() {
681 Must(spareWaiting
.forSpareAllowance
);
682 spareWaiting
.forSpareAllowance
= false;
684 if (spareWaiting
.callback
)
685 TheSpareAllowanceGiver
.jobDroppedAllowance();
686 TheSpareAllowanceGiver
.dequeue(*this); // clears spareWaiting.callback
689 /// stops waiting for the right conditions to open a spare connection
691 HappyConnOpener::cancelSpareWait(const char *reason
)
693 debugs(17, 5, "because " << reason
);
696 if (spareWaiting
.toGivePrimeItsChance
)
697 stopGivingPrimeItsChance();
698 else if (spareWaiting
.forSpareAllowance
)
699 stopWaitingForSpareAllowance();
701 spareWaiting
.clear();
704 /** Called when an external event changes initiator interest, destinations,
705 * prime, spare, or spareWaiting. Leaves HappyConnOpener in one of these
706 * mutually exclusive "stable" states:
708 * 1. Processing a single peer: currentPeer && !done()
709 * 1.1. Connecting: prime || spare
710 * 1.2. Waiting for spare gap and/or paths: !prime && !spare
711 * 2. Waiting for a new peer: destinations->empty() && !destinations->destinationsFinalized && !currentPeer && !done()
712 * 3. Terminating: done()
715 HappyConnOpener::checkForNewConnection()
717 debugs(17, 7, *destinations
);
719 // The order of the top-level if-statements below is important.
722 return; // bail ASAP to minimize our waste and others delays (state #0)
724 if (ranOutOfTimeOrAttempts()) {
725 Must(currentPeer
); // or we would be done() already
726 return; // will continue working (state #1.1)
729 // update stale currentPeer and/or stale spareWaiting
730 if (currentPeer
&& !spare
&& !prime
&& destinations
->doneWithPeer(*currentPeer
)) {
731 debugs(17, 7, "done with peer; " << *currentPeer
);
732 if (spareWaiting
.forNewPeer
)
733 cancelSpareWait("done with peer");
737 currentPeer
= nullptr;
738 ignoreSpareRestrictions
= false;
739 Must(!gotSpareAllowance
);
740 } else if (currentPeer
&& !spareWaiting
.forNewPeer
&& spareWaiting
&& destinations
->doneWithSpares(*currentPeer
)) {
741 cancelSpareWait("no spares are coming");
742 spareWaiting
.forNewPeer
= true;
746 maybeOpenPrimeConnection();
748 if (!spare
&& !done())
749 maybeOpenSpareConnection();
751 // any state is possible at this point
755 HappyConnOpener::noteGavePrimeItsChance()
757 Must(spareWaiting
.toGivePrimeItsChance
);
758 spareWaiting
.clear();
759 checkForNewConnection();
763 HappyConnOpener::noteSpareAllowance()
765 Must(spareWaiting
.forSpareAllowance
);
766 spareWaiting
.clear();
768 if (ranOutOfTimeOrAttempts()) {
769 TheSpareAllowanceGiver
.jobDroppedAllowance();
770 return; // will quit or continue working on prime
773 Must(!gotSpareAllowance
);
774 gotSpareAllowance
= true;
776 auto dest
= destinations
->extractSpare(*currentPeer
); // ought to succeed
777 startConnecting(spare
, dest
);
780 /// starts a prime connection attempt if possible or does nothing otherwise
782 HappyConnOpener::maybeOpenPrimeConnection()
786 if (destinations
->empty())
790 auto newPrime
= destinations
->extractFront();
791 currentPeer
= newPrime
;
793 debugs(17, 7, "new peer " << *currentPeer
);
794 primeStart
= current_dtime
;
795 startConnecting(prime
, newPrime
);
796 if (done()) // probably reused a pconn
800 maybeGivePrimeItsChance();
804 // currentPeer implies there is a spare attempt; meanwhile, the previous
805 // primary attempt has failed; do another attempt on the primary track
806 if (auto dest
= destinations
->extractPrime(*currentPeer
))
807 startConnecting(prime
, dest
);
808 // else wait for more prime paths or their exhaustion
811 /// starts waiting for a spare permission (if spare connections may be possible)
812 /// or does nothing (otherwise)
814 HappyConnOpener::maybeGivePrimeItsChance()
821 if (destinations
->doneWithSpares(*currentPeer
)) {
822 debugs(17, 7, "no spares for " << *currentPeer
);
823 spareWaiting
.forNewPeer
= true;
827 if (Config
.happyEyeballs
.connect_limit
== 0) {
828 debugs(17, 7, "concurrent spares are prohibited");
829 spareWaiting
.forPrimesToFail
= true;
833 if (ThePrimeChanceGiver
.readyNow(*this)) {
834 debugs(17, 7, "no happy_eyeballs_connect_timeout");
838 ThePrimeChanceGiver
.enqueue(*this);
839 spareWaiting
.toGivePrimeItsChance
= true;
840 // wait for a prime connect result or noteGavePrimeItsChance()
843 /// if possible, starts a spare connection attempt
845 HappyConnOpener::maybeOpenSpareConnection()
849 Must(!gotSpareAllowance
);
854 if (ranOutOfTimeOrAttempts())
857 if (destinations
->empty())
860 // jobGotInstantAllowance() call conditions below rely on the readyNow() check here
861 if (!ignoreSpareRestrictions
&& // we have to honor spare restrictions
862 !TheSpareAllowanceGiver
.readyNow(*this) && // all new spares must wait
863 destinations
->haveSpare(*currentPeer
)) { // and we do have a new spare
864 TheSpareAllowanceGiver
.enqueue(*this);
865 spareWaiting
.forSpareAllowance
= true;
869 if (auto dest
= destinations
->extractSpare(*currentPeer
)) {
871 if (!ignoreSpareRestrictions
) {
872 TheSpareAllowanceGiver
.jobGotInstantAllowance();
873 gotSpareAllowance
= true;
876 startConnecting(spare
, dest
);
880 // wait for more spare paths or their exhaustion
883 /// Check for maximum connection tries and forwarding time restrictions
885 HappyConnOpener::ranOutOfTimeOrAttempts() const
887 if (ranOutOfTimeOrAttemptsEarlier_
)
890 if (n_tries
>= Config
.forward_max_tries
) {
891 debugs(17, 5, "maximum allowed tries exhausted");
892 ranOutOfTimeOrAttemptsEarlier_
= "maximum tries";
896 if (FwdState::ForwardTimeout(fwdStart
) <= 0) {
897 debugs(17, 5, "forwarding timeout");
898 ranOutOfTimeOrAttemptsEarlier_
= "forwarding timeout";
905 HappyConnOpener::Attempt::Attempt(const CallbackMethod method
, const char *methodName
):
906 callbackMethod(method
),
907 callbackMethodName(methodName
)
912 HappyConnOpener::Attempt::finish()
919 HappyConnOpener::Attempt::cancel(const char *reason
)
921 connWait
.cancel(reason
);