2 * Copyright (C) 1996-2021 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/CodeContext.h"
12 #include "CachePeer.h"
13 #include "errorpage.h"
15 #include "HappyConnOpener.h"
16 #include "HttpRequest.h"
17 #include "ip/QosConfig.h"
18 #include "neighbors.h"
20 #include "PeerPoolMgr.h"
21 #include "SquidConfig.h"
23 CBDATA_CLASS_INIT(HappyConnOpener
);
25 // HappyOrderEnforcer optimizes enforcement of the "pause before opening a spare
26 // connection" requirements. Its inefficient alternative would add hundreds of
27 // concurrent events to the Squid event queue in many busy configurations, one
28 // concurrent event per concurrent HappyConnOpener job.
30 // EventScheduler::schedule() uses linear search to find the right place for a
31 // new event; having hundreds of concurrent events is prohibitively expensive.
32 // Both alternatives may have comparable high rate of eventAdd() calls, but
33 // HappyOrderEnforcer usually schedules the first or second event (as opposed to
34 // events that would be fired after hundreds of already scheduled events, making
35 // that linear search a lot longer).
37 // EventScheduler::cancel() also uses linear search. HappyOrderEnforcer does not
38 // need to cancel scheduled events, while its inefficient alternative may cancel
39 // events at a rate comparable to the high eventAdd() rate -- many events would
40 // be scheduled in vain because external factors would speed up (or make
41 // unnecessary) spare connection attempts, canceling the wait.
43 // This optimization is possible only where each job needs to pause for the same
44 // amount of time, creating a naturally ordered list of jobs waiting to be
45 // resumed. This is why two HappyOrderEnforcers are needed to efficiently honor
46 // both happy_eyeballs_connect_timeout and happy_eyeballs_connect_gap
49 /// Efficiently drains a FIFO HappyConnOpener queue while delaying each "pop"
50 /// event by the time determined by the top element currently in the queue. Its
51 /// current cbdata-free implementation assumes static storage duration.
52 class HappyOrderEnforcer
55 /// \param aName names scheduled events, for debugging
56 HappyOrderEnforcer(const char *aName
): name(aName
) {}
58 /// resumes jobs that need resuming (if any)
61 /// starts managing the job's wait; the job should expect a call back
62 void enqueue(HappyConnOpener
&);
64 /// stops managing the job's wait; cancels the pending callback, if any
65 void dequeue(HappyConnOpener
&);
67 const char * const name
; ///< waiting event name, for debugging
70 virtual bool readyNow(const HappyConnOpener
&) const = 0;
71 virtual AsyncCall::Pointer
notify(const CbcPointer
<HappyConnOpener
> &) = 0;
73 bool waiting() const { return waitEnd_
> 0; }
74 bool startedWaiting(const HappyAbsoluteTime lastStart
, const int cfgTimeoutMsec
) const;
77 static void NoteWaitOver(void *raw
);
80 HappySpareWaitList jobs_
; ///< queued jobs waiting their turn
81 mutable HappyAbsoluteTime waitEnd_
= 0; ///< expected NoteWaitOver() call time (or zero)
84 std::ostream
&operator <<(std::ostream
&os
, const HappyConnOpenerAnswer
&answer
)
86 if (answer
.error
.set())
89 os
<< (answer
.reused
? "reused " : "new ") << answer
.conn
;
90 if (answer
.n_tries
!= 1)
91 os
<< " after " << answer
.n_tries
;
95 /// enforces happy_eyeballs_connect_timeout
96 class PrimeChanceGiver
: public HappyOrderEnforcer
99 PrimeChanceGiver(): HappyOrderEnforcer("happy_eyeballs_connect_timeout enforcement") {}
101 /* HappyOrderEnforcer API */
102 virtual bool readyNow(const HappyConnOpener
&job
) const override
;
105 /* HappyOrderEnforcer API */
106 virtual AsyncCall::Pointer
notify(const CbcPointer
<HappyConnOpener
> &) override
;
109 /// enforces happy_eyeballs_connect_gap and happy_eyeballs_connect_limit
110 class SpareAllowanceGiver
: public HappyOrderEnforcer
113 SpareAllowanceGiver(): HappyOrderEnforcer("happy_eyeballs_connect_gap/happy_eyeballs_connect_limit enforcement") {}
115 /* HappyOrderEnforcer API */
116 virtual bool readyNow(const HappyConnOpener
&job
) const override
;
118 /// reacts to HappyConnOpener discovering readyNow() conditions for a spare path
119 /// the caller must attempt to open a spare connection immediately
120 void jobGotInstantAllowance();
122 /// reacts to HappyConnOpener getting a spare connection opening result
123 void jobUsedAllowance();
125 /// reacts to HappyConnOpener dropping its spare connection allowance
126 void jobDroppedAllowance();
129 /* HappyOrderEnforcer API */
130 virtual AsyncCall::Pointer
notify(const CbcPointer
<HappyConnOpener
> &) override
;
132 bool concurrencyLimitReached() const;
133 void recordAllowance();
134 void forgetAllowance();
136 /// the time of the last noteSpareAllowance() call
137 HappyAbsoluteTime lastAllowanceStart
= 0;
139 /// the number of noteSpareAllowance() calls not already
140 /// returned via jobUsedAllowance() or jobDroppedAllowance()
141 int concurrencyLevel
= 0;
144 PrimeChanceGiver ThePrimeChanceGiver
;
145 SpareAllowanceGiver TheSpareAllowanceGiver
;
147 /* HappyOrderEnforcer */
150 HappyOrderEnforcer::enqueue(HappyConnOpener
&job
)
152 Must(!job
.spareWaiting
.callback
);
153 jobs_
.emplace_back(&job
);
154 job
.spareWaiting
.position
= std::prev(jobs_
.end());
155 job
.spareWaiting
.codeContext
= CodeContext::Current();
159 HappyOrderEnforcer::dequeue(HappyConnOpener
&job
)
161 if (job
.spareWaiting
.callback
) {
162 job
.spareWaiting
.callback
->cancel("HappyOrderEnforcer::dequeue");
163 job
.spareWaiting
.callback
= nullptr;
165 Must(!jobs_
.empty());
166 jobs_
.erase(job
.spareWaiting
.position
);
171 HappyOrderEnforcer::checkpoint()
173 while (!jobs_
.empty()) {
174 if (const auto jobPtr
= jobs_
.front().valid()) {
177 break; // the next job cannot be ready earlier (FIFO)
178 CallBack(job
.spareWaiting
.codeContext
, [&] {
179 job
.spareWaiting
.callback
= notify(jobPtr
); // and fall through to the next job
187 HappyOrderEnforcer::startedWaiting(const HappyAbsoluteTime lastStart
, const int cfgTimeoutMsec
) const
189 // Normally, the job would not even be queued if there is no timeout. This
190 // check handles reconfiguration that happened after this job was queued.
191 if (cfgTimeoutMsec
<= 0)
194 // convert to seconds and adjust for SMP workers to keep aggregated load in
195 // check despite the lack of coordination among workers
196 const auto tout
= static_cast<HappyAbsoluteTime
>(cfgTimeoutMsec
) * Config
.workers
/ 1000.0;
197 const auto newWaitEnd
= std::min(lastStart
, current_dtime
) + tout
;
198 if (newWaitEnd
<= current_dtime
)
199 return false; // no need to wait
201 // We cannot avoid event accumulation because calling eventDelete() is
202 // unsafe, but any accumulation will be small because it can only be caused
203 // by hot reconfiguration changes or current time jumps.
204 if (!waiting() || newWaitEnd
< waitEnd_
) {
205 const auto waitTime
= newWaitEnd
- current_dtime
;
206 eventAdd(name
, &HappyOrderEnforcer::NoteWaitOver
, const_cast<HappyOrderEnforcer
*>(this), waitTime
, 0, false);
207 waitEnd_
= newWaitEnd
;
215 HappyOrderEnforcer::NoteWaitOver(void *raw
)
218 static_cast<HappyOrderEnforcer
*>(raw
)->noteWaitOver();
222 HappyOrderEnforcer::noteWaitOver()
229 /* PrimeChanceGiver */
232 PrimeChanceGiver::readyNow(const HappyConnOpener
&job
) const
234 return !startedWaiting(job
.primeStart
, Config
.happyEyeballs
.connect_timeout
);
238 PrimeChanceGiver::notify(const CbcPointer
<HappyConnOpener
> &job
)
240 return CallJobHere(17, 5, job
, HappyConnOpener
, noteGavePrimeItsChance
);
243 /* SpareAllowanceGiver */
246 SpareAllowanceGiver::readyNow(const HappyConnOpener
&) const
248 return !concurrencyLimitReached() &&
249 !startedWaiting(lastAllowanceStart
, Config
.happyEyeballs
.connect_gap
);
253 SpareAllowanceGiver::notify(const CbcPointer
<HappyConnOpener
> &job
)
256 return CallJobHere(17, 5, job
, HappyConnOpener
, noteSpareAllowance
);
260 SpareAllowanceGiver::jobGotInstantAllowance()
266 SpareAllowanceGiver::jobUsedAllowance()
272 SpareAllowanceGiver::jobDroppedAllowance()
274 // Without happy_eyeballs_connect_gap, lastAllowanceStart does not matter.
275 // Otherwise, the dropped allowance ought to be the last one, and since it
276 // was allowed, we would still observe the gap even if we do not wait now.
277 lastAllowanceStart
= 0;
282 /// account for the given allowance
284 SpareAllowanceGiver::recordAllowance()
287 lastAllowanceStart
= current_dtime
;
288 // not a checkpoint(): no other spare can become ready here
292 SpareAllowanceGiver::forgetAllowance()
294 Must(concurrencyLevel
);
299 /// whether opening a spare connection now would violate happy_eyeballs_connect_limit
301 SpareAllowanceGiver::concurrencyLimitReached() const
303 if (Config
.happyEyeballs
.connect_limit
< 0)
304 return false; // no limit
306 if (Config
.happyEyeballs
.connect_limit
== 0)
307 return true; // concurrent spares prohibited regardless of spare level
309 // adjust for SMP workers to keep aggregated spare level in check despite
310 // the lack of coordination among workers
311 const auto aggregateLevel
= concurrencyLevel
* Config
.workers
;
312 return aggregateLevel
>= Config
.happyEyeballs
.connect_limit
;
315 /* HappyConnOpenerAnswer */
317 HappyConnOpenerAnswer::~HappyConnOpenerAnswer()
319 // XXX: When multiple copies of an answer exist, this delete in one copy
320 // invalidates error in other copies -- their error.get() returns nil. The
321 // current code "works", but probably only because the initiator gets the
322 // error before any answer copies are deleted. Same in ~EncryptorAnswer.
326 /* HappyConnOpener */
328 HappyConnOpener::HappyConnOpener(const ResolvedPeers::Pointer
&dests
, const AsyncCall::Pointer
&aCall
, HttpRequest::Pointer
&request
, const time_t aFwdStart
, int tries
, const AccessLogEntry::Pointer
&anAle
):
329 AsyncJob("HappyConnOpener"),
337 assert(destinations
);
338 assert(dynamic_cast<Answer
*>(callback_
->getDialer()));
341 HappyConnOpener::~HappyConnOpener()
348 HappyConnOpener::setHost(const char *h
)
356 HappyConnOpener::start()
358 destinations
->notificationPending
= false;
359 checkForNewConnection();
363 HappyConnOpener::doneAll() const
366 return true; // (probably found a good path and) informed the requestor
368 // TODO: Expose AsyncCall::canFire() instead so that code like this can
369 // detect gone initiators without the need to explicitly cancel callbacks.
370 if (callback_
->canceled())
371 return true; // the requestor is gone or has lost interest
376 if (ranOutOfTimeOrAttempts())
377 return true; // trying new connection paths prohibited
379 if (destinations
->empty() && destinations
->destinationsFinalized
)
380 return true; // there are no more paths to try
386 HappyConnOpener::swanSong()
390 if (callback_
&& !callback_
->canceled())
394 cancelSpareWait("HappyConnOpener object destructed");
396 // TODO: Find an automated, faster way to kill no-longer-needed jobs.
399 cancelAttempt(prime
, "job finished during a prime attempt");
403 cancelAttempt(spare
, "job finished during a spare attempt");
404 if (gotSpareAllowance
) {
405 TheSpareAllowanceGiver
.jobDroppedAllowance();
406 gotSpareAllowance
= false;
410 AsyncJob::swanSong();
414 HappyConnOpener::status() const
421 buf
.appendf("Stopped, reason:%s", stopReason
);
423 if (prime
.path
&& prime
.path
->isOpen())
424 buf
.appendf(" prime FD %d", prime
.path
->fd
);
425 else if (prime
.connector
)
426 buf
.appendf(" prime call%ud", prime
.connector
->id
.value
);
429 if (spare
.path
&& spare
.path
->isOpen())
430 buf
.appendf(" spare FD %d", spare
.path
->fd
);
431 else if (spare
.connector
)
432 buf
.appendf(" spare call%ud", spare
.connector
->id
.value
);
435 buf
.appendf(" tries %d", n_tries
);
436 buf
.appendf(" %s%u]", id
.prefix(), id
.value
);
439 return buf
.content();
442 /// Create "503 Service Unavailable" or "504 Gateway Timeout" error depending
443 /// on whether this is a validation request. RFC 7234 section 5.2.2 says that
444 /// we MUST reply with "504 Gateway Timeout" if validation fails and cached
445 /// reply has proxy-revalidate, must-revalidate or s-maxage Cache-Control
448 HappyConnOpener::makeError(const err_type type
) const
450 const auto statusCode
= cause
->flags
.needValidation
?
451 Http::scGatewayTimeout
: Http::scServiceUnavailable
;
452 return new ErrorState(type
, statusCode
, cause
.getRaw(), ale
);
455 /// \returns pre-filled Answer if the initiator needs an answer (or nil)
456 HappyConnOpener::Answer
*
457 HappyConnOpener::futureAnswer(const PeerConnectionPointer
&conn
)
459 if (callback_
&& !callback_
->canceled()) {
460 const auto answer
= dynamic_cast<Answer
*>(callback_
->getDialer());
463 answer
->n_tries
= n_tries
;
469 /// send a successful result to the initiator (if it still needs an answer)
471 HappyConnOpener::sendSuccess(const PeerConnectionPointer
&conn
, const bool reused
, const char *connKind
)
473 debugs(17, 4, connKind
<< ": " << conn
);
474 if (auto *answer
= futureAnswer(conn
)) {
475 answer
->reused
= reused
;
476 assert(!answer
->error
);
477 ScheduleCallHere(callback_
);
482 /// cancels the in-progress attempt, making its path a future candidate
484 HappyConnOpener::cancelAttempt(Attempt
&attempt
, const char *reason
)
487 destinations
->reinstatePath(attempt
.path
); // before attempt.cancel() clears path
488 attempt
.cancel(reason
);
491 /// inform the initiator about our failure to connect (if needed)
493 HappyConnOpener::sendFailure()
495 debugs(17, 3, lastFailedConnection
);
496 if (auto *answer
= futureAnswer(lastFailedConnection
)) {
498 lastError
= makeError(ERR_GATEWAY_FAILURE
);
499 answer
->error
= lastError
;
500 assert(answer
->error
.valid());
501 lastError
= nullptr; // the answer owns it now
502 ScheduleCallHere(callback_
);
508 HappyConnOpener::noteCandidatesChange()
510 destinations
->notificationPending
= false;
511 checkForNewConnection();
514 /// starts opening (or reusing) a connection to the given destination
516 HappyConnOpener::startConnecting(Attempt
&attempt
, PeerConnectionPointer
&dest
)
519 Must(!attempt
.connector
);
522 const auto bumpThroughPeer
= cause
->flags
.sslBumped
&& dest
->getPeer();
523 const auto canReuseOld
= allowPconn_
&& !bumpThroughPeer
;
524 if (!canReuseOld
|| !reuseOldConnection(dest
))
525 openFreshConnection(attempt
, dest
);
528 /// reuses a persistent connection to the given destination (if possible)
529 /// \returns true if and only if reuse was possible
530 /// must be called via startConnecting()
532 HappyConnOpener::reuseOldConnection(PeerConnectionPointer
&dest
)
536 if (const auto pconn
= fwdPconnPool
->pop(dest
, host_
, retriable_
)) {
538 dest
.finalize(pconn
);
539 sendSuccess(dest
, true, "reused connection");
546 /// opens a fresh connection to the given destination
547 /// must be called via startConnecting()
549 HappyConnOpener::openFreshConnection(Attempt
&attempt
, PeerConnectionPointer
&dest
)
551 #if URL_CHECKSUM_DEBUG
552 entry
->mem_obj
->checkUrlChecksum();
555 GetMarkingsToServer(cause
.getRaw(), *dest
);
557 // ConnOpener modifies its destination argument so we reset the source port
558 // in case we are reusing the destination already used by our predecessor.
562 typedef CommCbMemFunT
<HappyConnOpener
, CommConnectCbParams
> Dialer
;
563 AsyncCall::Pointer callConnect
= JobCallback(48, 5, Dialer
, this, HappyConnOpener::connectDone
);
564 const time_t connTimeout
= dest
->connectTimeout(fwdStart
);
565 Comm::ConnOpener
*cs
= new Comm::ConnOpener(dest
, callConnect
, connTimeout
);
566 if (!dest
->getPeer())
570 attempt
.connector
= callConnect
;
576 /// called by Comm::ConnOpener objects after a prime or spare connection attempt
577 /// completes (successfully or not)
579 HappyConnOpener::connectDone(const CommConnectCbParams
¶ms
)
582 const bool itWasPrime
= (params
.conn
== prime
.path
);
583 const bool itWasSpare
= (params
.conn
== spare
.path
);
584 Must(itWasPrime
!= itWasSpare
);
586 PeerConnectionPointer handledPath
;
588 handledPath
= prime
.path
;
591 handledPath
= spare
.path
;
593 if (gotSpareAllowance
) {
594 TheSpareAllowanceGiver
.jobUsedAllowance();
595 gotSpareAllowance
= false;
599 const char *what
= itWasPrime
? "new prime connection" : "new spare connection";
600 if (params
.flag
== Comm::OK
) {
601 sendSuccess(handledPath
, false, what
);
605 debugs(17, 8, what
<< " failed: " << params
.conn
);
606 if (const auto peer
= params
.conn
->getPeer())
607 peerConnectFailed(peer
);
608 params
.conn
->close(); // TODO: Comm::ConnOpener should do this instead.
610 // remember the last failure (we forward it if we cannot connect anywhere)
611 lastFailedConnection
= handledPath
;
613 lastError
= nullptr; // in case makeError() throws
614 lastError
= makeError(ERR_CONNECT_FAIL
);
615 lastError
->xerrno
= params
.xerrno
;
618 updateSpareWaitAfterPrimeFailure();
620 checkForNewConnection();
623 /// reacts to a prime attempt failure
625 HappyConnOpener::updateSpareWaitAfterPrimeFailure()
631 if (destinations
->doneWithPrimes(*currentPeer
)) {
632 cancelSpareWait("all primes failed");
633 ignoreSpareRestrictions
= true;
634 return; // checkForNewConnection() will open a spare connection ASAP
637 if (spareWaiting
.toGivePrimeItsChance
)
638 stopGivingPrimeItsChance();
640 // may still be spareWaiting.forSpareAllowance or
641 // may still be spareWaiting.forPrimesToFail
644 /// called when the prime attempt has used up its chance for a solo victory
646 HappyConnOpener::stopGivingPrimeItsChance() {
647 Must(spareWaiting
.toGivePrimeItsChance
);
648 spareWaiting
.toGivePrimeItsChance
= false;
649 ThePrimeChanceGiver
.dequeue(*this);
652 /// called when the spare attempt should no longer obey spare connection limits
654 HappyConnOpener::stopWaitingForSpareAllowance() {
655 Must(spareWaiting
.forSpareAllowance
);
656 spareWaiting
.forSpareAllowance
= false;
658 if (spareWaiting
.callback
)
659 TheSpareAllowanceGiver
.jobDroppedAllowance();
660 TheSpareAllowanceGiver
.dequeue(*this); // clears spareWaiting.callback
663 /// stops waiting for the right conditions to open a spare connection
665 HappyConnOpener::cancelSpareWait(const char *reason
)
667 debugs(17, 5, "because " << reason
);
670 if (spareWaiting
.toGivePrimeItsChance
)
671 stopGivingPrimeItsChance();
672 else if (spareWaiting
.forSpareAllowance
)
673 stopWaitingForSpareAllowance();
675 spareWaiting
.clear();
678 /** Called when an external event changes initiator interest, destinations,
679 * prime, spare, or spareWaiting. Leaves HappyConnOpener in one of these
680 * (mutually exclusive beyond the exceptional state #0) "stable" states:
682 * 0. Exceptional termination: done()
683 * 1. Processing a single peer: currentPeer
684 * 1.1. Connecting: prime || spare
685 * 1.2. Waiting for spare gap and/or paths: !prime && !spare
686 * 2. Waiting for a new peer: destinations->empty() && !destinations->destinationsFinalized && !currentPeer
687 * 3. Finished: destinations->empty() && destinations->destinationsFinalized && !currentPeer
690 HappyConnOpener::checkForNewConnection()
692 debugs(17, 7, *destinations
);
694 // The order of the top-level if-statements below is important.
697 return; // bail ASAP to minimize our waste and others delays (state #0)
699 if (ranOutOfTimeOrAttempts()) {
700 Must(currentPeer
); // or we would be done() already
701 return; // will continue working (state #1.1)
704 // update stale currentPeer and/or stale spareWaiting
705 if (currentPeer
&& !spare
&& !prime
&& destinations
->doneWithPeer(*currentPeer
)) {
706 debugs(17, 7, "done with peer; " << *currentPeer
);
707 if (spareWaiting
.forNewPeer
)
708 cancelSpareWait("done with peer");
712 currentPeer
= nullptr;
713 ignoreSpareRestrictions
= false;
714 Must(!gotSpareAllowance
);
715 } else if (currentPeer
&& !spareWaiting
.forNewPeer
&& spareWaiting
&& destinations
->doneWithSpares(*currentPeer
)) {
716 cancelSpareWait("no spares are coming");
717 spareWaiting
.forNewPeer
= true;
720 // open a new prime and/or a new spare connection if needed
721 if (!destinations
->empty()) {
723 auto newPrime
= destinations
->extractFront();
724 currentPeer
= newPrime
;
726 debugs(17, 7, "new peer " << *currentPeer
);
727 primeStart
= current_dtime
;
728 startConnecting(prime
, newPrime
);
729 // TODO: if reuseOldConnection() in startConnecting() above succeeds,
730 // then we should not get here, and Must(prime) below will fail.
731 maybeGivePrimeItsChance();
732 Must(prime
); // entering state #1.1
735 maybeOpenAnotherPrimeConnection(); // may make destinations empty()
738 if (!spare
&& !spareWaiting
)
739 maybeOpenSpareConnection(); // may make destinations empty()
745 debugs(17, 7, "working on " << *currentPeer
);
746 return; // remaining in state #1.1 or #1.2
749 if (!destinations
->destinationsFinalized
) {
750 debugs(17, 7, "waiting for more peers");
751 return; // remaining in state #2
754 debugs(17, 7, "done; no more peers");
760 HappyConnOpener::noteGavePrimeItsChance()
762 Must(spareWaiting
.toGivePrimeItsChance
);
763 spareWaiting
.clear();
764 checkForNewConnection();
768 HappyConnOpener::noteSpareAllowance()
770 Must(spareWaiting
.forSpareAllowance
);
771 spareWaiting
.clear();
773 if (ranOutOfTimeOrAttempts()) {
774 TheSpareAllowanceGiver
.jobDroppedAllowance();
775 return; // will quit or continue working on prime
778 Must(!gotSpareAllowance
);
779 gotSpareAllowance
= true;
781 auto dest
= destinations
->extractSpare(*currentPeer
); // ought to succeed
782 startConnecting(spare
, dest
);
785 /// starts a prime connection attempt if possible or does nothing otherwise
787 HappyConnOpener::maybeOpenAnotherPrimeConnection()
790 if (auto dest
= destinations
->extractPrime(*currentPeer
))
791 startConnecting(prime
, dest
);
792 // else wait for more prime paths or their exhaustion
795 /// starts waiting for a spare permission (if spare connections may be possible)
796 /// or does nothing (otherwise)
798 HappyConnOpener::maybeGivePrimeItsChance()
805 if (destinations
->doneWithSpares(*currentPeer
)) {
806 debugs(17, 7, "no spares for " << *currentPeer
);
807 spareWaiting
.forNewPeer
= true;
811 if (Config
.happyEyeballs
.connect_limit
== 0) {
812 debugs(17, 7, "concurrent spares are prohibited");
813 spareWaiting
.forPrimesToFail
= true;
817 if (ThePrimeChanceGiver
.readyNow(*this)) {
818 debugs(17, 7, "no happy_eyeballs_connect_timeout");
822 ThePrimeChanceGiver
.enqueue(*this);
823 spareWaiting
.toGivePrimeItsChance
= true;
824 // wait for a prime connect result or noteGavePrimeItsChance()
827 /// if possible, starts a spare connection attempt
829 HappyConnOpener::maybeOpenSpareConnection()
834 Must(!gotSpareAllowance
);
836 if (ranOutOfTimeOrAttempts())
837 return; // will quit or continue working on prime
839 // jobGotInstantAllowance() call conditions below rely on the readyNow() check here
840 if (!ignoreSpareRestrictions
&& // we have to honor spare restrictions
841 !TheSpareAllowanceGiver
.readyNow(*this) && // all new spares must wait
842 destinations
->haveSpare(*currentPeer
)) { // and we do have a new spare
843 TheSpareAllowanceGiver
.enqueue(*this);
844 spareWaiting
.forSpareAllowance
= true;
848 if (auto dest
= destinations
->extractSpare(*currentPeer
)) {
850 if (!ignoreSpareRestrictions
) {
851 TheSpareAllowanceGiver
.jobGotInstantAllowance();
852 gotSpareAllowance
= true;
855 startConnecting(spare
, dest
);
859 // wait for more spare paths or their exhaustion
862 /// Check for maximum connection tries and forwarding time restrictions
864 HappyConnOpener::ranOutOfTimeOrAttempts() const
866 if (ranOutOfTimeOrAttemptsEarlier_
)
869 if (n_tries
>= Config
.forward_max_tries
) {
870 debugs(17, 5, "maximum allowed tries exhausted");
871 ranOutOfTimeOrAttemptsEarlier_
= "maximum tries";
875 if (FwdState::ForwardTimeout(fwdStart
) <= 0) {
876 debugs(17, 5, "forwarding timeout");
877 ranOutOfTimeOrAttemptsEarlier_
= "forwarding timeout";
885 HappyConnOpener::Attempt::cancel(const char *reason
)
888 connector
->cancel(reason
);
889 CallJobHere(17, 3, opener
, Comm::ConnOpener
, noteAbort
);