@@ -91,6 +91,7 @@ type Options struct {
9191
9292 PoolFIFO bool
9393 PoolSize int32
94+ MaxConcurrentDials int
9495 DialTimeout time.Duration
9596 PoolTimeout time.Duration
9697 MinIdleConns int32
@@ -113,13 +114,65 @@ type lastDialErrorWrap struct {
113114 err error
114115}
115116
117+ type wantConn struct {
118+ mu sync.Mutex // protects ctx, done and sending of the result
119+ ctx context.Context // context for dial, cleared after delivered or canceled
120+ cancelCtx context.CancelFunc
121+ done bool // true after delivered or canceled
122+ result chan wantConnResult // channel to deliver connection or error
123+ }
124+
125+ func (w * wantConn ) tryDeliver (cn * Conn , err error ) bool {
126+ w .mu .Lock ()
127+ defer w .mu .Unlock ()
128+ if w .done {
129+ return false
130+ }
131+
132+ w .done = true
133+ w .ctx = nil
134+
135+ w .result <- wantConnResult {cn : cn , err : err }
136+ close (w .result )
137+
138+ return true
139+ }
140+
141+ func (w * wantConn ) cancel (ctx context.Context , p * ConnPool ) {
142+ w .mu .Lock ()
143+ var cn * Conn
144+ if w .done {
145+ select {
146+ case result := <- w .result :
147+ cn = result .cn
148+ default :
149+ }
150+ } else {
151+ close (w .result )
152+ }
153+
154+ w .done = true
155+ w .ctx = nil
156+ w .mu .Unlock ()
157+
158+ if cn != nil {
159+ p .Put (ctx , cn )
160+ }
161+ }
162+
163+ type wantConnResult struct {
164+ cn * Conn
165+ err error
166+ }
167+
116168type ConnPool struct {
117169 cfg * Options
118170
119171 dialErrorsNum uint32 // atomic
120172 lastDialError atomic.Value
121173
122- queue chan struct {}
174+ queue chan struct {}
175+ dialsInProgress chan struct {}
123176
124177 connsMu sync.Mutex
125178 conns map [uint64 ]* Conn
@@ -145,9 +198,10 @@ func NewConnPool(opt *Options) *ConnPool {
145198 p := & ConnPool {
146199 cfg : opt ,
147200
148- queue : make (chan struct {}, opt .PoolSize ),
149- conns : make (map [uint64 ]* Conn ),
150- idleConns : make ([]* Conn , 0 , opt .PoolSize ),
201+ queue : make (chan struct {}, opt .PoolSize ),
202+ conns : make (map [uint64 ]* Conn ),
203+ dialsInProgress : make (chan struct {}, opt .MaxConcurrentDials ),
204+ idleConns : make ([]* Conn , 0 , opt .PoolSize ),
151205 }
152206
153207 // Only create MinIdleConns if explicitly requested (> 0)
@@ -473,9 +527,8 @@ func (p *ConnPool) getConn(ctx context.Context) (*Conn, error) {
473527
474528 atomic .AddUint32 (& p .stats .Misses , 1 )
475529
476- newcn , err := p .newConn (ctx , true )
530+ newcn , err := p .asyncNewConn (ctx )
477531 if err != nil {
478- p .freeTurn ()
479532 return nil , err
480533 }
481534
@@ -495,6 +548,55 @@ func (p *ConnPool) getConn(ctx context.Context) (*Conn, error) {
495548 return newcn , nil
496549}
497550
551+ func (p * ConnPool ) asyncNewConn (ctx context.Context ) (* Conn , error ) {
552+ // First try to acquire permission to create a connection
553+ select {
554+ case p .dialsInProgress <- struct {}{}:
555+ // Got permission, proceed to create connection
556+ case <- ctx .Done ():
557+ p .freeTurn ()
558+ return nil , ctx .Err ()
559+ }
560+
561+ dialCtx , cancel := context .WithTimeout (context .WithoutCancel (ctx ), p .cfg .DialTimeout )
562+
563+ w := & wantConn {
564+ ctx : dialCtx ,
565+ cancelCtx : cancel ,
566+ result : make (chan wantConnResult , 1 ),
567+ }
568+ var err error
569+ defer func () {
570+ if err != nil {
571+ w .cancel (ctx , p )
572+ }
573+ }()
574+
575+ go func (w * wantConn ) {
576+ defer w .cancelCtx ()
577+ defer func () { <- p .dialsInProgress }() // Release connection creation permission
578+
579+ cn , cnErr := p .newConn (w .ctx , true )
580+ delivered := w .tryDeliver (cn , cnErr )
581+ if cnErr == nil && delivered {
582+ return
583+ } else if cnErr == nil && ! delivered {
584+ p .Put (w .ctx , cn )
585+ } else { // freeTurn after error
586+ p .freeTurn ()
587+ }
588+ }(w )
589+
590+ select {
591+ case <- ctx .Done ():
592+ err = ctx .Err ()
593+ return nil , err
594+ case result := <- w .result :
595+ err = result .err
596+ return result .cn , err
597+ }
598+ }
599+
498600func (p * ConnPool ) waitTurn (ctx context.Context ) error {
499601 select {
500602 case <- ctx .Done ():
0 commit comments