github.com/mdaxf/iac@v0.0.0-20240519030858-58a061660378/vendor_skip/go.mongodb.org/mongo-driver/x/mongo/driver/topology/pool.go (about)

     1  // Copyright (C) MongoDB, Inc. 2017-present.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License"); you may
     4  // not use this file except in compliance with the License. You may obtain
     5  // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
     6  
     7  package topology
     8  
     9  import (
    10  	"context"
    11  	"fmt"
    12  	"net"
    13  	"sync"
    14  	"sync/atomic"
    15  	"time"
    16  
    17  	"go.mongodb.org/mongo-driver/bson/primitive"
    18  	"go.mongodb.org/mongo-driver/event"
    19  	"go.mongodb.org/mongo-driver/internal/logger"
    20  	"go.mongodb.org/mongo-driver/mongo/address"
    21  	"go.mongodb.org/mongo-driver/x/mongo/driver"
    22  )
    23  
    24  // Connection pool state constants.
    25  const (
    26  	poolPaused int = iota
    27  	poolReady
    28  	poolClosed
    29  )
    30  
    31  // ErrPoolNotPaused is returned when attempting to mark a connection pool "ready" that is not
    32  // currently "paused".
    33  var ErrPoolNotPaused = PoolError("only a paused pool can be marked ready")
    34  
    35  // ErrPoolClosed is returned when attempting to check out a connection from a closed pool.
    36  var ErrPoolClosed = PoolError("attempted to check out a connection from closed connection pool")
    37  
    38  // ErrConnectionClosed is returned from an attempt to use an already closed connection.
    39  var ErrConnectionClosed = ConnectionError{ConnectionID: "<closed>", message: "connection is closed"}
    40  
    41  // ErrWrongPool is return when a connection is returned to a pool it doesn't belong to.
    42  var ErrWrongPool = PoolError("connection does not belong to this pool")
    43  
    44  // PoolError is an error returned from a Pool method.
    45  type PoolError string
    46  
    47  func (pe PoolError) Error() string { return string(pe) }
    48  
    49  // poolClearedError is an error returned when the connection pool is cleared or currently paused. It
    50  // is a retryable error.
    51  type poolClearedError struct {
    52  	err     error
    53  	address address.Address
    54  }
    55  
    56  func (pce poolClearedError) Error() string {
    57  	return fmt.Sprintf(
    58  		"connection pool for %v was cleared because another operation failed with: %v",
    59  		pce.address,
    60  		pce.err)
    61  }
    62  
    63  // Retryable returns true. All poolClearedErrors are retryable.
    64  func (poolClearedError) Retryable() bool { return true }
    65  
    66  // Assert that poolClearedError is a driver.RetryablePoolError.
    67  var _ driver.RetryablePoolError = poolClearedError{}
    68  
    69  // poolConfig contains all aspects of the pool that can be configured
    70  type poolConfig struct {
    71  	Address          address.Address
    72  	MinPoolSize      uint64
    73  	MaxPoolSize      uint64
    74  	MaxConnecting    uint64
    75  	MaxIdleTime      time.Duration
    76  	MaintainInterval time.Duration
    77  	PoolMonitor      *event.PoolMonitor
    78  	Logger           *logger.Logger
    79  	handshakeErrFn   func(error, uint64, *primitive.ObjectID)
    80  }
    81  
    82  type pool struct {
    83  	// The following integer fields must be accessed using the atomic package
    84  	// and should be at the beginning of the struct.
    85  	// - atomic bug: https://pkg.go.dev/sync/atomic#pkg-note-BUG
    86  	// - suggested layout: https://go101.org/article/memory-layout.html
    87  
    88  	nextID                       uint64 // nextID is the next pool ID for a new connection.
    89  	pinnedCursorConnections      uint64
    90  	pinnedTransactionConnections uint64
    91  
    92  	address       address.Address
    93  	minSize       uint64
    94  	maxSize       uint64
    95  	maxConnecting uint64
    96  	monitor       *event.PoolMonitor
    97  	logger        *logger.Logger
    98  
    99  	// handshakeErrFn is used to handle any errors that happen during connection establishment and
   100  	// handshaking.
   101  	handshakeErrFn func(error, uint64, *primitive.ObjectID)
   102  
   103  	connOpts   []ConnectionOption
   104  	generation *poolGenerationMap
   105  
   106  	maintainInterval time.Duration   // maintainInterval is the maintain() loop interval.
   107  	maintainReady    chan struct{}   // maintainReady is a signal channel that starts the maintain() loop when ready() is called.
   108  	backgroundDone   *sync.WaitGroup // backgroundDone waits for all background goroutines to return.
   109  
   110  	stateMu      sync.RWMutex // stateMu guards state, lastClearErr
   111  	state        int          // state is the current state of the connection pool.
   112  	lastClearErr error        // lastClearErr is the last error that caused the pool to be cleared.
   113  
   114  	// createConnectionsCond is the condition variable that controls when the createConnections()
   115  	// loop runs or waits. Its lock guards cancelBackgroundCtx, conns, and newConnWait. Any changes
   116  	// to the state of the guarded values must be made while holding the lock to prevent undefined
   117  	// behavior in the createConnections() waiting logic.
   118  	createConnectionsCond *sync.Cond
   119  	cancelBackgroundCtx   context.CancelFunc     // cancelBackgroundCtx is called to signal background goroutines to stop.
   120  	conns                 map[uint64]*connection // conns holds all currently open connections.
   121  	newConnWait           wantConnQueue          // newConnWait holds all wantConn requests for new connections.
   122  
   123  	idleMu       sync.Mutex    // idleMu guards idleConns, idleConnWait
   124  	idleConns    []*connection // idleConns holds all idle connections.
   125  	idleConnWait wantConnQueue // idleConnWait holds all wantConn requests for idle connections.
   126  }
   127  
   128  // getState returns the current state of the pool. Callers must not hold the stateMu lock.
   129  func (p *pool) getState() int {
   130  	p.stateMu.RLock()
   131  	defer p.stateMu.RUnlock()
   132  
   133  	return p.state
   134  }
   135  
   136  func mustLogPoolMessage(pool *pool) bool {
   137  	return pool.logger != nil && pool.logger.LevelComponentEnabled(
   138  		logger.LevelDebug, logger.ComponentConnection)
   139  }
   140  
   141  func logPoolMessage(pool *pool, msg string, keysAndValues ...interface{}) {
   142  	host, port, err := net.SplitHostPort(pool.address.String())
   143  	if err != nil {
   144  		host = pool.address.String()
   145  		port = ""
   146  	}
   147  
   148  	pool.logger.Print(logger.LevelDebug,
   149  		logger.ComponentConnection,
   150  		msg,
   151  		logger.SerializeConnection(logger.Connection{
   152  			Message:    msg,
   153  			ServerHost: host,
   154  			ServerPort: port,
   155  		}, keysAndValues...)...)
   156  
   157  }
   158  
   159  type reason struct {
   160  	loggerConn string
   161  	event      string
   162  }
   163  
   164  // connectionPerished checks if a given connection is perished and should be removed from the pool.
   165  func connectionPerished(conn *connection) (reason, bool) {
   166  	switch {
   167  	case conn.closed():
   168  		// A connection would only be closed if it encountered a network error during an operation and closed itself.
   169  		return reason{
   170  			loggerConn: logger.ReasonConnClosedError,
   171  			event:      event.ReasonError,
   172  		}, true
   173  	case conn.idleTimeoutExpired():
   174  		return reason{
   175  			loggerConn: logger.ReasonConnClosedIdle,
   176  			event:      event.ReasonIdle,
   177  		}, true
   178  	case conn.pool.stale(conn):
   179  		return reason{
   180  			loggerConn: logger.ReasonConnClosedStale,
   181  			event:      event.ReasonStale,
   182  		}, true
   183  	}
   184  
   185  	return reason{}, false
   186  }
   187  
   188  // newPool creates a new pool. It will use the provided options when creating connections.
   189  func newPool(config poolConfig, connOpts ...ConnectionOption) *pool {
   190  	if config.MaxIdleTime != time.Duration(0) {
   191  		connOpts = append(connOpts, WithIdleTimeout(func(_ time.Duration) time.Duration { return config.MaxIdleTime }))
   192  	}
   193  
   194  	var maxConnecting uint64 = 2
   195  	if config.MaxConnecting > 0 {
   196  		maxConnecting = config.MaxConnecting
   197  	}
   198  
   199  	maintainInterval := 10 * time.Second
   200  	if config.MaintainInterval != 0 {
   201  		maintainInterval = config.MaintainInterval
   202  	}
   203  
   204  	pool := &pool{
   205  		address:               config.Address,
   206  		minSize:               config.MinPoolSize,
   207  		maxSize:               config.MaxPoolSize,
   208  		maxConnecting:         maxConnecting,
   209  		monitor:               config.PoolMonitor,
   210  		logger:                config.Logger,
   211  		handshakeErrFn:        config.handshakeErrFn,
   212  		connOpts:              connOpts,
   213  		generation:            newPoolGenerationMap(),
   214  		state:                 poolPaused,
   215  		maintainInterval:      maintainInterval,
   216  		maintainReady:         make(chan struct{}, 1),
   217  		backgroundDone:        &sync.WaitGroup{},
   218  		createConnectionsCond: sync.NewCond(&sync.Mutex{}),
   219  		conns:                 make(map[uint64]*connection, config.MaxPoolSize),
   220  		idleConns:             make([]*connection, 0, config.MaxPoolSize),
   221  	}
   222  	// minSize must not exceed maxSize if maxSize is not 0
   223  	if pool.maxSize != 0 && pool.minSize > pool.maxSize {
   224  		pool.minSize = pool.maxSize
   225  	}
   226  	pool.connOpts = append(pool.connOpts, withGenerationNumberFn(func(_ generationNumberFn) generationNumberFn { return pool.getGenerationForNewConnection }))
   227  
   228  	pool.generation.connect()
   229  
   230  	// Create a Context with cancellation that's used to signal the createConnections() and
   231  	// maintain() background goroutines to stop. Also create a "backgroundDone" WaitGroup that is
   232  	// used to wait for the background goroutines to return.
   233  	var ctx context.Context
   234  	ctx, pool.cancelBackgroundCtx = context.WithCancel(context.Background())
   235  
   236  	for i := 0; i < int(pool.maxConnecting); i++ {
   237  		pool.backgroundDone.Add(1)
   238  		go pool.createConnections(ctx, pool.backgroundDone)
   239  	}
   240  
   241  	// If maintainInterval is not positive, don't start the maintain() goroutine. Expect that
   242  	// negative values are only used in testing; this config value is not user-configurable.
   243  	if maintainInterval > 0 {
   244  		pool.backgroundDone.Add(1)
   245  		go pool.maintain(ctx, pool.backgroundDone)
   246  	}
   247  
   248  	if mustLogPoolMessage(pool) {
   249  		keysAndValues := logger.KeyValues{
   250  			logger.KeyMaxIdleTimeMS, config.MaxIdleTime.Milliseconds(),
   251  			logger.KeyMinPoolSize, config.MinPoolSize,
   252  			logger.KeyMaxPoolSize, config.MaxPoolSize,
   253  			logger.KeyMaxConnecting, config.MaxConnecting,
   254  		}
   255  
   256  		logPoolMessage(pool, logger.ConnectionPoolCreated, keysAndValues...)
   257  	}
   258  
   259  	if pool.monitor != nil {
   260  		pool.monitor.Event(&event.PoolEvent{
   261  			Type: event.PoolCreated,
   262  			PoolOptions: &event.MonitorPoolOptions{
   263  				MaxPoolSize: config.MaxPoolSize,
   264  				MinPoolSize: config.MinPoolSize,
   265  			},
   266  			Address: pool.address.String(),
   267  		})
   268  	}
   269  
   270  	return pool
   271  }
   272  
   273  // stale checks if a given connection's generation is below the generation of the pool
   274  func (p *pool) stale(conn *connection) bool {
   275  	return conn == nil || p.generation.stale(conn.desc.ServiceID, conn.generation)
   276  }
   277  
   278  // ready puts the pool into the "ready" state and starts the background connection creation and
   279  // monitoring goroutines. ready must be called before connections can be checked out. An unused,
   280  // connected pool must be closed or it will leak goroutines and will not be garbage collected.
   281  func (p *pool) ready() error {
   282  	// While holding the stateMu lock, set the pool to "ready" if it is currently "paused".
   283  	p.stateMu.Lock()
   284  	if p.state == poolReady {
   285  		p.stateMu.Unlock()
   286  		return nil
   287  	}
   288  	if p.state != poolPaused {
   289  		p.stateMu.Unlock()
   290  		return ErrPoolNotPaused
   291  	}
   292  	p.lastClearErr = nil
   293  	p.state = poolReady
   294  	p.stateMu.Unlock()
   295  
   296  	if mustLogPoolMessage(p) {
   297  		logPoolMessage(p, logger.ConnectionPoolReady)
   298  	}
   299  
   300  	// Send event.PoolReady before resuming the maintain() goroutine to guarantee that the
   301  	// "pool ready" event is always sent before maintain() starts creating connections.
   302  	if p.monitor != nil {
   303  		p.monitor.Event(&event.PoolEvent{
   304  			Type:    event.PoolReady,
   305  			Address: p.address.String(),
   306  		})
   307  	}
   308  
   309  	// Signal maintain() to wake up immediately when marking the pool "ready".
   310  	select {
   311  	case p.maintainReady <- struct{}{}:
   312  	default:
   313  	}
   314  
   315  	return nil
   316  }
   317  
   318  // close closes the pool, closes all connections associated with the pool, and stops all background
   319  // goroutines. All subsequent checkOut requests will return an error. An unused, ready pool must be
   320  // closed or it will leak goroutines and will not be garbage collected.
   321  func (p *pool) close(ctx context.Context) {
   322  	p.stateMu.Lock()
   323  	if p.state == poolClosed {
   324  		p.stateMu.Unlock()
   325  		return
   326  	}
   327  	p.state = poolClosed
   328  	p.stateMu.Unlock()
   329  
   330  	// Call cancelBackgroundCtx() to exit the maintain() and createConnections() background
   331  	// goroutines. Broadcast to the createConnectionsCond to wake up all createConnections()
   332  	// goroutines. We must hold the createConnectionsCond lock here because we're changing the
   333  	// condition by cancelling the "background goroutine" Context, even tho cancelling the Context
   334  	// is also synchronized by a lock. Otherwise, we run into an intermittent bug that prevents the
   335  	// createConnections() goroutines from exiting.
   336  	p.createConnectionsCond.L.Lock()
   337  	p.cancelBackgroundCtx()
   338  	p.createConnectionsCond.Broadcast()
   339  	p.createConnectionsCond.L.Unlock()
   340  
   341  	// Wait for all background goroutines to exit.
   342  	p.backgroundDone.Wait()
   343  
   344  	p.generation.disconnect()
   345  
   346  	if ctx == nil {
   347  		ctx = context.Background()
   348  	}
   349  
   350  	// If we have a deadline then we interpret it as a request to gracefully shutdown. We wait until
   351  	// either all the connections have been checked back into the pool (i.e. total open connections
   352  	// equals idle connections) or until the Context deadline is reached.
   353  	if _, ok := ctx.Deadline(); ok {
   354  		ticker := time.NewTicker(100 * time.Millisecond)
   355  		defer ticker.Stop()
   356  
   357  	graceful:
   358  		for {
   359  			if p.totalConnectionCount() == p.availableConnectionCount() {
   360  				break graceful
   361  			}
   362  
   363  			select {
   364  			case <-ticker.C:
   365  			case <-ctx.Done():
   366  				break graceful
   367  			default:
   368  			}
   369  		}
   370  	}
   371  
   372  	// Empty the idle connections stack and try to deliver ErrPoolClosed to any waiting wantConns
   373  	// from idleConnWait while holding the idleMu lock.
   374  	p.idleMu.Lock()
   375  	p.idleConns = p.idleConns[:0]
   376  	for {
   377  		w := p.idleConnWait.popFront()
   378  		if w == nil {
   379  			break
   380  		}
   381  		w.tryDeliver(nil, ErrPoolClosed)
   382  	}
   383  	p.idleMu.Unlock()
   384  
   385  	// Collect all conns from the pool and try to deliver ErrPoolClosed to any waiting wantConns
   386  	// from newConnWait while holding the createConnectionsCond lock. We can't call removeConnection
   387  	// on the connections while holding any locks, so do that after we release the lock.
   388  	p.createConnectionsCond.L.Lock()
   389  	conns := make([]*connection, 0, len(p.conns))
   390  	for _, conn := range p.conns {
   391  		conns = append(conns, conn)
   392  	}
   393  	for {
   394  		w := p.newConnWait.popFront()
   395  		if w == nil {
   396  			break
   397  		}
   398  		w.tryDeliver(nil, ErrPoolClosed)
   399  	}
   400  	p.createConnectionsCond.L.Unlock()
   401  
   402  	// Now that we're not holding any locks, remove all of the connections we collected from the
   403  	// pool.
   404  	for _, conn := range conns {
   405  		_ = p.removeConnection(conn, reason{
   406  			loggerConn: logger.ReasonConnClosedPoolClosed,
   407  			event:      event.ReasonPoolClosed,
   408  		}, nil)
   409  		_ = p.closeConnection(conn) // We don't care about errors while closing the connection.
   410  	}
   411  
   412  	if mustLogPoolMessage(p) {
   413  		logPoolMessage(p, logger.ConnectionPoolClosed)
   414  	}
   415  
   416  	if p.monitor != nil {
   417  		p.monitor.Event(&event.PoolEvent{
   418  			Type:    event.PoolClosedEvent,
   419  			Address: p.address.String(),
   420  		})
   421  	}
   422  }
   423  
   424  func (p *pool) pinConnectionToCursor() {
   425  	atomic.AddUint64(&p.pinnedCursorConnections, 1)
   426  }
   427  
   428  func (p *pool) unpinConnectionFromCursor() {
   429  	// See https://golang.org/pkg/sync/atomic/#AddUint64 for an explanation of the ^uint64(0) syntax.
   430  	atomic.AddUint64(&p.pinnedCursorConnections, ^uint64(0))
   431  }
   432  
   433  func (p *pool) pinConnectionToTransaction() {
   434  	atomic.AddUint64(&p.pinnedTransactionConnections, 1)
   435  }
   436  
   437  func (p *pool) unpinConnectionFromTransaction() {
   438  	// See https://golang.org/pkg/sync/atomic/#AddUint64 for an explanation of the ^uint64(0) syntax.
   439  	atomic.AddUint64(&p.pinnedTransactionConnections, ^uint64(0))
   440  }
   441  
   442  // checkOut checks out a connection from the pool. If an idle connection is not available, the
   443  // checkOut enters a queue waiting for either the next idle or new connection. If the pool is not
   444  // ready, checkOut returns an error.
   445  // Based partially on https://cs.opensource.google/go/go/+/refs/tags/go1.16.6:src/net/http/transport.go;l=1324
   446  func (p *pool) checkOut(ctx context.Context) (conn *connection, err error) {
   447  	if mustLogPoolMessage(p) {
   448  		logPoolMessage(p, logger.ConnectionCheckoutStarted)
   449  	}
   450  
   451  	// TODO(CSOT): If a Timeout was specified at any level, respect the Timeout is server selection, connection
   452  	// TODO checkout.
   453  	if p.monitor != nil {
   454  		p.monitor.Event(&event.PoolEvent{
   455  			Type:    event.GetStarted,
   456  			Address: p.address.String(),
   457  		})
   458  	}
   459  
   460  	// Check the pool state while holding a stateMu read lock. If the pool state is not "ready",
   461  	// return an error. Do all of this while holding the stateMu read lock to prevent a state change between
   462  	// checking the state and entering the wait queue. Not holding the stateMu read lock here may
   463  	// allow a checkOut() to enter the wait queue after clear() pauses the pool and clears the wait
   464  	// queue, resulting in createConnections() doing work while the pool is "paused".
   465  	p.stateMu.RLock()
   466  	switch p.state {
   467  	case poolClosed:
   468  		p.stateMu.RUnlock()
   469  
   470  		if mustLogPoolMessage(p) {
   471  			keysAndValues := logger.KeyValues{
   472  				logger.KeyReason, logger.ReasonConnCheckoutFailedPoolClosed,
   473  			}
   474  
   475  			logPoolMessage(p, logger.ConnectionCheckoutFailed, keysAndValues...)
   476  		}
   477  
   478  		if p.monitor != nil {
   479  			p.monitor.Event(&event.PoolEvent{
   480  				Type:    event.GetFailed,
   481  				Address: p.address.String(),
   482  				Reason:  event.ReasonPoolClosed,
   483  			})
   484  		}
   485  		return nil, ErrPoolClosed
   486  	case poolPaused:
   487  		err := poolClearedError{err: p.lastClearErr, address: p.address}
   488  		p.stateMu.RUnlock()
   489  
   490  		if mustLogPoolMessage(p) {
   491  			keysAndValues := logger.KeyValues{
   492  				logger.KeyReason, logger.ReasonConnCheckoutFailedError,
   493  			}
   494  
   495  			logPoolMessage(p, logger.ConnectionCheckoutFailed, keysAndValues...)
   496  		}
   497  
   498  		if p.monitor != nil {
   499  			p.monitor.Event(&event.PoolEvent{
   500  				Type:    event.GetFailed,
   501  				Address: p.address.String(),
   502  				Reason:  event.ReasonConnectionErrored,
   503  			})
   504  		}
   505  		return nil, err
   506  	}
   507  
   508  	if ctx == nil {
   509  		ctx = context.Background()
   510  	}
   511  
   512  	// Create a wantConn, which we will use to request an existing idle or new connection. Always
   513  	// cancel the wantConn if checkOut() returned an error to make sure any delivered connections
   514  	// are returned to the pool (e.g. if a connection was delivered immediately after the Context
   515  	// timed out).
   516  	w := newWantConn()
   517  	defer func() {
   518  		if err != nil {
   519  			w.cancel(p, err)
   520  		}
   521  	}()
   522  
   523  	// Get in the queue for an idle connection. If getOrQueueForIdleConn returns true, it was able to
   524  	// immediately deliver an idle connection to the wantConn, so we can return the connection or
   525  	// error from the wantConn without waiting for "ready".
   526  	if delivered := p.getOrQueueForIdleConn(w); delivered {
   527  		// If delivered = true, we didn't enter the wait queue and will return either a connection
   528  		// or an error, so unlock the stateMu lock here.
   529  		p.stateMu.RUnlock()
   530  
   531  		if w.err != nil {
   532  			if mustLogPoolMessage(p) {
   533  				keysAndValues := logger.KeyValues{
   534  					logger.KeyReason, logger.ReasonConnCheckoutFailedError,
   535  				}
   536  
   537  				logPoolMessage(p, logger.ConnectionCheckoutFailed, keysAndValues...)
   538  			}
   539  
   540  			if p.monitor != nil {
   541  				p.monitor.Event(&event.PoolEvent{
   542  					Type:    event.GetFailed,
   543  					Address: p.address.String(),
   544  					Reason:  event.ReasonConnectionErrored,
   545  				})
   546  			}
   547  			return nil, w.err
   548  		}
   549  
   550  		if mustLogPoolMessage(p) {
   551  			keysAndValues := logger.KeyValues{
   552  				logger.KeyDriverConnectionID, w.conn.driverConnectionID,
   553  			}
   554  
   555  			logPoolMessage(p, logger.ConnectionCheckedOut, keysAndValues...)
   556  		}
   557  
   558  		if p.monitor != nil {
   559  			p.monitor.Event(&event.PoolEvent{
   560  				Type:         event.GetSucceeded,
   561  				Address:      p.address.String(),
   562  				ConnectionID: w.conn.driverConnectionID,
   563  			})
   564  		}
   565  
   566  		return w.conn, nil
   567  	}
   568  
   569  	// If we didn't get an immediately available idle connection, also get in the queue for a new
   570  	// connection while we're waiting for an idle connection.
   571  	p.queueForNewConn(w)
   572  	p.stateMu.RUnlock()
   573  
   574  	// Wait for either the wantConn to be ready or for the Context to time out.
   575  	select {
   576  	case <-w.ready:
   577  		if w.err != nil {
   578  			if mustLogPoolMessage(p) {
   579  				keysAndValues := logger.KeyValues{
   580  					logger.KeyReason, logger.ReasonConnCheckoutFailedError,
   581  					logger.KeyError, w.err.Error(),
   582  				}
   583  
   584  				logPoolMessage(p, logger.ConnectionCheckoutFailed, keysAndValues...)
   585  			}
   586  
   587  			if p.monitor != nil {
   588  				p.monitor.Event(&event.PoolEvent{
   589  					Type:    event.GetFailed,
   590  					Address: p.address.String(),
   591  					Reason:  event.ReasonConnectionErrored,
   592  				})
   593  			}
   594  
   595  			return nil, w.err
   596  		}
   597  
   598  		if mustLogPoolMessage(p) {
   599  			keysAndValues := logger.KeyValues{
   600  				logger.KeyDriverConnectionID, w.conn.driverConnectionID,
   601  			}
   602  
   603  			logPoolMessage(p, logger.ConnectionCheckedOut, keysAndValues...)
   604  		}
   605  
   606  		if p.monitor != nil {
   607  			p.monitor.Event(&event.PoolEvent{
   608  				Type:         event.GetSucceeded,
   609  				Address:      p.address.String(),
   610  				ConnectionID: w.conn.driverConnectionID,
   611  			})
   612  		}
   613  		return w.conn, nil
   614  	case <-ctx.Done():
   615  		if mustLogPoolMessage(p) {
   616  			keysAndValues := logger.KeyValues{
   617  				logger.KeyReason, logger.ReasonConnCheckoutFailedTimout,
   618  			}
   619  
   620  			logPoolMessage(p, logger.ConnectionCheckoutFailed, keysAndValues...)
   621  		}
   622  
   623  		if p.monitor != nil {
   624  			p.monitor.Event(&event.PoolEvent{
   625  				Type:    event.GetFailed,
   626  				Address: p.address.String(),
   627  				Reason:  event.ReasonTimedOut,
   628  			})
   629  		}
   630  
   631  		return nil, WaitQueueTimeoutError{
   632  			Wrapped:                      ctx.Err(),
   633  			PinnedCursorConnections:      atomic.LoadUint64(&p.pinnedCursorConnections),
   634  			PinnedTransactionConnections: atomic.LoadUint64(&p.pinnedTransactionConnections),
   635  			maxPoolSize:                  p.maxSize,
   636  			totalConnectionCount:         p.totalConnectionCount(),
   637  		}
   638  	}
   639  }
   640  
   641  // closeConnection closes a connection.
   642  func (p *pool) closeConnection(conn *connection) error {
   643  	if conn.pool != p {
   644  		return ErrWrongPool
   645  	}
   646  
   647  	if atomic.LoadInt64(&conn.state) == connConnected {
   648  		conn.closeConnectContext()
   649  		conn.wait() // Make sure that the connection has finished connecting.
   650  	}
   651  
   652  	err := conn.close()
   653  	if err != nil {
   654  		return ConnectionError{ConnectionID: conn.id, Wrapped: err, message: "failed to close net.Conn"}
   655  	}
   656  
   657  	return nil
   658  }
   659  
   660  func (p *pool) getGenerationForNewConnection(serviceID *primitive.ObjectID) uint64 {
   661  	return p.generation.addConnection(serviceID)
   662  }
   663  
   664  // removeConnection removes a connection from the pool and emits a "ConnectionClosed" event.
   665  func (p *pool) removeConnection(conn *connection, reason reason, err error) error {
   666  	if conn == nil {
   667  		return nil
   668  	}
   669  
   670  	if conn.pool != p {
   671  		return ErrWrongPool
   672  	}
   673  
   674  	p.createConnectionsCond.L.Lock()
   675  	_, ok := p.conns[conn.driverConnectionID]
   676  	if !ok {
   677  		// If the connection has been removed from the pool already, exit without doing any
   678  		// additional state changes.
   679  		p.createConnectionsCond.L.Unlock()
   680  		return nil
   681  	}
   682  	delete(p.conns, conn.driverConnectionID)
   683  	// Signal the createConnectionsCond so any goroutines waiting for a new connection slot in the
   684  	// pool will proceed.
   685  	p.createConnectionsCond.Signal()
   686  	p.createConnectionsCond.L.Unlock()
   687  
   688  	// Only update the generation numbers map if the connection has retrieved its generation number.
   689  	// Otherwise, we'd decrement the count for the generation even though it had never been
   690  	// incremented.
   691  	if conn.hasGenerationNumber() {
   692  		p.generation.removeConnection(conn.desc.ServiceID)
   693  	}
   694  
   695  	if mustLogPoolMessage(p) {
   696  		keysAndValues := logger.KeyValues{
   697  			logger.KeyDriverConnectionID, conn.driverConnectionID,
   698  			logger.KeyReason, reason.loggerConn,
   699  		}
   700  
   701  		if err != nil {
   702  			keysAndValues.Add(logger.KeyError, err.Error())
   703  		}
   704  
   705  		logPoolMessage(p, logger.ConnectionClosed, keysAndValues...)
   706  	}
   707  
   708  	if p.monitor != nil {
   709  		p.monitor.Event(&event.PoolEvent{
   710  			Type:         event.ConnectionClosed,
   711  			Address:      p.address.String(),
   712  			ConnectionID: conn.driverConnectionID,
   713  			Reason:       reason.event,
   714  			Error:        err,
   715  		})
   716  	}
   717  
   718  	return nil
   719  }
   720  
   721  // checkIn returns an idle connection to the pool. If the connection is perished or the pool is
   722  // closed, it is removed from the connection pool and closed.
   723  func (p *pool) checkIn(conn *connection) error {
   724  	if conn == nil {
   725  		return nil
   726  	}
   727  	if conn.pool != p {
   728  		return ErrWrongPool
   729  	}
   730  
   731  	if mustLogPoolMessage(p) {
   732  		keysAndValues := logger.KeyValues{
   733  			logger.KeyDriverConnectionID, conn.driverConnectionID,
   734  		}
   735  
   736  		logPoolMessage(p, logger.ConnectionCheckedIn, keysAndValues...)
   737  	}
   738  
   739  	if p.monitor != nil {
   740  		p.monitor.Event(&event.PoolEvent{
   741  			Type:         event.ConnectionReturned,
   742  			ConnectionID: conn.driverConnectionID,
   743  			Address:      conn.addr.String(),
   744  		})
   745  	}
   746  
   747  	return p.checkInNoEvent(conn)
   748  }
   749  
   750  // checkInNoEvent returns a connection to the pool. It behaves identically to checkIn except it does
   751  // not publish events. It is only intended for use by pool-internal functions.
   752  func (p *pool) checkInNoEvent(conn *connection) error {
   753  	if conn == nil {
   754  		return nil
   755  	}
   756  	if conn.pool != p {
   757  		return ErrWrongPool
   758  	}
   759  
   760  	// Bump the connection idle deadline here because we're about to make the connection "available".
   761  	// The idle deadline is used to determine when a connection has reached its max idle time and
   762  	// should be closed. A connection reaches its max idle time when it has been "available" in the
   763  	// idle connections stack for more than the configured duration (maxIdleTimeMS). Set it before
   764  	// we call connectionPerished(), which checks the idle deadline, because a newly "available"
   765  	// connection should never be perished due to max idle time.
   766  	conn.bumpIdleDeadline()
   767  
   768  	if reason, perished := connectionPerished(conn); perished {
   769  		_ = p.removeConnection(conn, reason, nil)
   770  		go func() {
   771  			_ = p.closeConnection(conn)
   772  		}()
   773  		return nil
   774  	}
   775  
   776  	if conn.pool.getState() == poolClosed {
   777  		_ = p.removeConnection(conn, reason{
   778  			loggerConn: logger.ReasonConnClosedPoolClosed,
   779  			event:      event.ReasonPoolClosed,
   780  		}, nil)
   781  
   782  		go func() {
   783  			_ = p.closeConnection(conn)
   784  		}()
   785  		return nil
   786  	}
   787  
   788  	p.idleMu.Lock()
   789  	defer p.idleMu.Unlock()
   790  
   791  	for {
   792  		w := p.idleConnWait.popFront()
   793  		if w == nil {
   794  			break
   795  		}
   796  		if w.tryDeliver(conn, nil) {
   797  			return nil
   798  		}
   799  	}
   800  
   801  	for _, idle := range p.idleConns {
   802  		if idle == conn {
   803  			return fmt.Errorf("duplicate idle conn %p in idle connections stack", conn)
   804  		}
   805  	}
   806  
   807  	p.idleConns = append(p.idleConns, conn)
   808  	return nil
   809  }
   810  
   811  // clear marks all connections as stale by incrementing the generation number, stops all background
   812  // goroutines, removes all requests from idleConnWait and newConnWait, and sets the pool state to
   813  // "paused". If serviceID is nil, clear marks all connections as stale. If serviceID is not nil,
   814  // clear marks only connections associated with the given serviceID stale (for use in load balancer
   815  // mode).
   816  func (p *pool) clear(err error, serviceID *primitive.ObjectID) {
   817  	if p.getState() == poolClosed {
   818  		return
   819  	}
   820  
   821  	p.generation.clear(serviceID)
   822  
   823  	// If serviceID is nil (i.e. not in load balancer mode), transition the pool to a paused state
   824  	// by stopping all background goroutines, clearing the wait queues, and setting the pool state
   825  	// to "paused".
   826  	sendEvent := true
   827  	if serviceID == nil {
   828  		// While holding the stateMu lock, set the pool state to "paused" if it's currently "ready",
   829  		// and set lastClearErr to the error that caused the pool to be cleared. If the pool is
   830  		// already paused, don't send another "ConnectionPoolCleared" event.
   831  		p.stateMu.Lock()
   832  		if p.state == poolPaused {
   833  			sendEvent = false
   834  		}
   835  		if p.state == poolReady {
   836  			p.state = poolPaused
   837  		}
   838  		p.lastClearErr = err
   839  		p.stateMu.Unlock()
   840  
   841  		pcErr := poolClearedError{err: err, address: p.address}
   842  
   843  		// Clear the idle connections wait queue.
   844  		p.idleMu.Lock()
   845  		for {
   846  			w := p.idleConnWait.popFront()
   847  			if w == nil {
   848  				break
   849  			}
   850  			w.tryDeliver(nil, pcErr)
   851  		}
   852  		p.idleMu.Unlock()
   853  
   854  		// Clear the new connections wait queue. This effectively pauses the createConnections()
   855  		// background goroutine because newConnWait is empty and checkOut() won't insert any more
   856  		// wantConns into newConnWait until the pool is marked "ready" again.
   857  		p.createConnectionsCond.L.Lock()
   858  		for {
   859  			w := p.newConnWait.popFront()
   860  			if w == nil {
   861  				break
   862  			}
   863  			w.tryDeliver(nil, pcErr)
   864  		}
   865  		p.createConnectionsCond.L.Unlock()
   866  	}
   867  
   868  	if mustLogPoolMessage(p) {
   869  		keysAndValues := logger.KeyValues{
   870  			logger.KeyServiceID, serviceID,
   871  		}
   872  
   873  		logPoolMessage(p, logger.ConnectionPoolCleared, keysAndValues...)
   874  	}
   875  
   876  	if sendEvent && p.monitor != nil {
   877  		p.monitor.Event(&event.PoolEvent{
   878  			Type:      event.PoolCleared,
   879  			Address:   p.address.String(),
   880  			ServiceID: serviceID,
   881  		})
   882  	}
   883  }
   884  
   885  // getOrQueueForIdleConn attempts to deliver an idle connection to the given wantConn. If there is
   886  // an idle connection in the idle connections stack, it pops an idle connection, delivers it to the
   887  // wantConn, and returns true. If there are no idle connections in the idle connections stack, it
   888  // adds the wantConn to the idleConnWait queue and returns false.
   889  func (p *pool) getOrQueueForIdleConn(w *wantConn) bool {
   890  	p.idleMu.Lock()
   891  	defer p.idleMu.Unlock()
   892  
   893  	// Try to deliver an idle connection from the idleConns stack first.
   894  	for len(p.idleConns) > 0 {
   895  		conn := p.idleConns[len(p.idleConns)-1]
   896  		p.idleConns = p.idleConns[:len(p.idleConns)-1]
   897  
   898  		if conn == nil {
   899  			continue
   900  		}
   901  
   902  		if reason, perished := connectionPerished(conn); perished {
   903  			_ = conn.pool.removeConnection(conn, reason, nil)
   904  			go func() {
   905  				_ = conn.pool.closeConnection(conn)
   906  			}()
   907  			continue
   908  		}
   909  
   910  		if !w.tryDeliver(conn, nil) {
   911  			// If we couldn't deliver the conn to w, put it back in the idleConns stack.
   912  			p.idleConns = append(p.idleConns, conn)
   913  		}
   914  
   915  		// If we got here, we tried to deliver an idle conn to w. No matter if tryDeliver() returned
   916  		// true or false, w is no longer waiting and doesn't need to be added to any wait queues, so
   917  		// return delivered = true.
   918  		return true
   919  	}
   920  
   921  	p.idleConnWait.cleanFront()
   922  	p.idleConnWait.pushBack(w)
   923  	return false
   924  }
   925  
   926  func (p *pool) queueForNewConn(w *wantConn) {
   927  	p.createConnectionsCond.L.Lock()
   928  	defer p.createConnectionsCond.L.Unlock()
   929  
   930  	p.newConnWait.cleanFront()
   931  	p.newConnWait.pushBack(w)
   932  	p.createConnectionsCond.Signal()
   933  }
   934  
   935  func (p *pool) totalConnectionCount() int {
   936  	p.createConnectionsCond.L.Lock()
   937  	defer p.createConnectionsCond.L.Unlock()
   938  
   939  	return len(p.conns)
   940  }
   941  
   942  func (p *pool) availableConnectionCount() int {
   943  	p.idleMu.Lock()
   944  	defer p.idleMu.Unlock()
   945  
   946  	return len(p.idleConns)
   947  }
   948  
   949  // createConnections creates connections for wantConn requests on the newConnWait queue.
   950  func (p *pool) createConnections(ctx context.Context, wg *sync.WaitGroup) {
   951  	defer wg.Done()
   952  
   953  	// condition returns true if the createConnections() loop should continue and false if it should
   954  	// wait. Note that the condition also listens for Context cancellation, which also causes the
   955  	// loop to continue, allowing for a subsequent check to return from createConnections().
   956  	condition := func() bool {
   957  		checkOutWaiting := p.newConnWait.len() > 0
   958  		poolHasSpace := p.maxSize == 0 || uint64(len(p.conns)) < p.maxSize
   959  		cancelled := ctx.Err() != nil
   960  		return (checkOutWaiting && poolHasSpace) || cancelled
   961  	}
   962  
   963  	// wait waits for there to be an available wantConn and for the pool to have space for a new
   964  	// connection. When the condition becomes true, it creates a new connection and returns the
   965  	// waiting wantConn and new connection. If the Context is cancelled or there are any
   966  	// errors, wait returns with "ok = false".
   967  	wait := func() (*wantConn, *connection, bool) {
   968  		p.createConnectionsCond.L.Lock()
   969  		defer p.createConnectionsCond.L.Unlock()
   970  
   971  		for !condition() {
   972  			p.createConnectionsCond.Wait()
   973  		}
   974  
   975  		if ctx.Err() != nil {
   976  			return nil, nil, false
   977  		}
   978  
   979  		p.newConnWait.cleanFront()
   980  		w := p.newConnWait.popFront()
   981  		if w == nil {
   982  			return nil, nil, false
   983  		}
   984  
   985  		conn := newConnection(p.address, p.connOpts...)
   986  		conn.pool = p
   987  		conn.driverConnectionID = atomic.AddUint64(&p.nextID, 1)
   988  		p.conns[conn.driverConnectionID] = conn
   989  
   990  		return w, conn, true
   991  	}
   992  
   993  	for ctx.Err() == nil {
   994  		w, conn, ok := wait()
   995  		if !ok {
   996  			continue
   997  		}
   998  
   999  		if mustLogPoolMessage(p) {
  1000  			keysAndValues := logger.KeyValues{
  1001  				logger.KeyDriverConnectionID, conn.driverConnectionID,
  1002  			}
  1003  
  1004  			logPoolMessage(p, logger.ConnectionCreated, keysAndValues...)
  1005  		}
  1006  
  1007  		if p.monitor != nil {
  1008  			p.monitor.Event(&event.PoolEvent{
  1009  				Type:         event.ConnectionCreated,
  1010  				Address:      p.address.String(),
  1011  				ConnectionID: conn.driverConnectionID,
  1012  			})
  1013  		}
  1014  
  1015  		// Pass the createConnections context to connect to allow pool close to cancel connection
  1016  		// establishment so shutdown doesn't block indefinitely if connectTimeout=0.
  1017  		err := conn.connect(ctx)
  1018  		if err != nil {
  1019  			w.tryDeliver(nil, err)
  1020  
  1021  			// If there's an error connecting the new connection, call the handshake error handler
  1022  			// that implements the SDAM handshake error handling logic. This must be called after
  1023  			// delivering the connection error to the waiting wantConn. If it's called before, the
  1024  			// handshake error handler may clear the connection pool, leading to a different error
  1025  			// message being delivered to the same waiting wantConn in idleConnWait when the wait
  1026  			// queues are cleared.
  1027  			if p.handshakeErrFn != nil {
  1028  				p.handshakeErrFn(err, conn.generation, conn.desc.ServiceID)
  1029  			}
  1030  
  1031  			_ = p.removeConnection(conn, reason{
  1032  				loggerConn: logger.ReasonConnClosedError,
  1033  				event:      event.ReasonError,
  1034  			}, err)
  1035  
  1036  			_ = p.closeConnection(conn)
  1037  
  1038  			continue
  1039  		}
  1040  
  1041  		if mustLogPoolMessage(p) {
  1042  			keysAndValues := logger.KeyValues{
  1043  				logger.KeyDriverConnectionID, conn.driverConnectionID,
  1044  			}
  1045  
  1046  			logPoolMessage(p, logger.ConnectionReady, keysAndValues...)
  1047  		}
  1048  
  1049  		if p.monitor != nil {
  1050  			p.monitor.Event(&event.PoolEvent{
  1051  				Type:         event.ConnectionReady,
  1052  				Address:      p.address.String(),
  1053  				ConnectionID: conn.driverConnectionID,
  1054  			})
  1055  		}
  1056  
  1057  		if w.tryDeliver(conn, nil) {
  1058  			continue
  1059  		}
  1060  
  1061  		_ = p.checkInNoEvent(conn)
  1062  	}
  1063  }
  1064  
  1065  func (p *pool) maintain(ctx context.Context, wg *sync.WaitGroup) {
  1066  	defer wg.Done()
  1067  
  1068  	ticker := time.NewTicker(p.maintainInterval)
  1069  	defer ticker.Stop()
  1070  
  1071  	// remove removes the *wantConn at index i from the slice and returns the new slice. The order
  1072  	// of the slice is not maintained.
  1073  	remove := func(arr []*wantConn, i int) []*wantConn {
  1074  		end := len(arr) - 1
  1075  		arr[i], arr[end] = arr[end], arr[i]
  1076  		return arr[:end]
  1077  	}
  1078  
  1079  	// removeNotWaiting removes any wantConns that are no longer waiting from given slice of
  1080  	// wantConns. That allows maintain() to use the size of its wantConns slice as an indication of
  1081  	// how many new connection requests are outstanding and subtract that from the number of
  1082  	// connections to ask for when maintaining minPoolSize.
  1083  	removeNotWaiting := func(arr []*wantConn) []*wantConn {
  1084  		for i := len(arr) - 1; i >= 0; i-- {
  1085  			w := arr[i]
  1086  			if !w.waiting() {
  1087  				arr = remove(arr, i)
  1088  			}
  1089  		}
  1090  
  1091  		return arr
  1092  	}
  1093  
  1094  	wantConns := make([]*wantConn, 0, p.minSize)
  1095  	defer func() {
  1096  		for _, w := range wantConns {
  1097  			w.tryDeliver(nil, ErrPoolClosed)
  1098  		}
  1099  	}()
  1100  
  1101  	for {
  1102  		select {
  1103  		case <-ticker.C:
  1104  		case <-p.maintainReady:
  1105  		case <-ctx.Done():
  1106  			return
  1107  		}
  1108  
  1109  		// Only maintain the pool while it's in the "ready" state. If the pool state is not "ready",
  1110  		// wait for the next tick or "ready" signal. Do all of this while holding the stateMu read
  1111  		// lock to prevent a state change between checking the state and entering the wait queue.
  1112  		// Not holding the stateMu read lock here may allow maintain() to request wantConns after
  1113  		// clear() pauses the pool and clears the wait queue, resulting in createConnections()
  1114  		// doing work while the pool is "paused".
  1115  		p.stateMu.RLock()
  1116  		if p.state != poolReady {
  1117  			p.stateMu.RUnlock()
  1118  			continue
  1119  		}
  1120  
  1121  		p.removePerishedConns()
  1122  
  1123  		// Remove any wantConns that are no longer waiting.
  1124  		wantConns = removeNotWaiting(wantConns)
  1125  
  1126  		// Figure out how many more wantConns we need to satisfy minPoolSize. Assume that the
  1127  		// outstanding wantConns (i.e. the ones that weren't removed from the slice) will all return
  1128  		// connections when they're ready, so only add wantConns to make up the difference. Limit
  1129  		// the number of connections requested to max 10 at a time to prevent overshooting
  1130  		// minPoolSize in case other checkOut() calls are requesting new connections, too.
  1131  		total := p.totalConnectionCount()
  1132  		n := int(p.minSize) - total - len(wantConns)
  1133  		if n > 10 {
  1134  			n = 10
  1135  		}
  1136  
  1137  		for i := 0; i < n; i++ {
  1138  			w := newWantConn()
  1139  			p.queueForNewConn(w)
  1140  			wantConns = append(wantConns, w)
  1141  
  1142  			// Start a goroutine for each new wantConn, waiting for it to be ready.
  1143  			go func() {
  1144  				<-w.ready
  1145  				if w.conn != nil {
  1146  					_ = p.checkInNoEvent(w.conn)
  1147  				}
  1148  			}()
  1149  		}
  1150  		p.stateMu.RUnlock()
  1151  	}
  1152  }
  1153  
  1154  func (p *pool) removePerishedConns() {
  1155  	p.idleMu.Lock()
  1156  	defer p.idleMu.Unlock()
  1157  
  1158  	for i := range p.idleConns {
  1159  		conn := p.idleConns[i]
  1160  		if conn == nil {
  1161  			continue
  1162  		}
  1163  
  1164  		if reason, perished := connectionPerished(conn); perished {
  1165  			p.idleConns[i] = nil
  1166  
  1167  			_ = p.removeConnection(conn, reason, nil)
  1168  			go func() {
  1169  				_ = p.closeConnection(conn)
  1170  			}()
  1171  		}
  1172  	}
  1173  
  1174  	p.idleConns = compact(p.idleConns)
  1175  }
  1176  
  1177  // compact removes any nil pointers from the slice and keeps the non-nil pointers, retaining the
  1178  // order of the non-nil pointers.
  1179  func compact(arr []*connection) []*connection {
  1180  	offset := 0
  1181  	for i := range arr {
  1182  		if arr[i] == nil {
  1183  			continue
  1184  		}
  1185  		arr[offset] = arr[i]
  1186  		offset++
  1187  	}
  1188  	return arr[:offset]
  1189  }
  1190  
  1191  // A wantConn records state about a wanted connection (that is, an active call to checkOut).
  1192  // The conn may be gotten by creating a new connection or by finding an idle connection, or a
  1193  // cancellation may make the conn no longer wanted. These three options are racing against each
  1194  // other and use wantConn to coordinate and agree about the winning outcome.
  1195  // Based on https://cs.opensource.google/go/go/+/refs/tags/go1.16.6:src/net/http/transport.go;l=1174-1240
  1196  type wantConn struct {
  1197  	ready chan struct{}
  1198  
  1199  	mu   sync.Mutex // Guards conn, err
  1200  	conn *connection
  1201  	err  error
  1202  }
  1203  
  1204  func newWantConn() *wantConn {
  1205  	return &wantConn{
  1206  		ready: make(chan struct{}, 1),
  1207  	}
  1208  }
  1209  
  1210  // waiting reports whether w is still waiting for an answer (connection or error).
  1211  func (w *wantConn) waiting() bool {
  1212  	select {
  1213  	case <-w.ready:
  1214  		return false
  1215  	default:
  1216  		return true
  1217  	}
  1218  }
  1219  
  1220  // tryDeliver attempts to deliver conn, err to w and reports whether it succeeded.
  1221  func (w *wantConn) tryDeliver(conn *connection, err error) bool {
  1222  	w.mu.Lock()
  1223  	defer w.mu.Unlock()
  1224  
  1225  	if w.conn != nil || w.err != nil {
  1226  		return false
  1227  	}
  1228  
  1229  	w.conn = conn
  1230  	w.err = err
  1231  	if w.conn == nil && w.err == nil {
  1232  		panic("x/mongo/driver/topology: internal error: misuse of tryDeliver")
  1233  	}
  1234  
  1235  	close(w.ready)
  1236  
  1237  	return true
  1238  }
  1239  
  1240  // cancel marks w as no longer wanting a result (for example, due to cancellation). If a connection
  1241  // has been delivered already, cancel returns it with p.checkInNoEvent(). Note that the caller must
  1242  // not hold any locks on the pool while calling cancel.
  1243  func (w *wantConn) cancel(p *pool, err error) {
  1244  	if err == nil {
  1245  		panic("x/mongo/driver/topology: internal error: misuse of cancel")
  1246  	}
  1247  
  1248  	w.mu.Lock()
  1249  	if w.conn == nil && w.err == nil {
  1250  		close(w.ready) // catch misbehavior in future delivery
  1251  	}
  1252  	conn := w.conn
  1253  	w.conn = nil
  1254  	w.err = err
  1255  	w.mu.Unlock()
  1256  
  1257  	if conn != nil {
  1258  		_ = p.checkInNoEvent(conn)
  1259  	}
  1260  }
  1261  
  1262  // A wantConnQueue is a queue of wantConns.
  1263  // Based on https://cs.opensource.google/go/go/+/refs/tags/go1.16.6:src/net/http/transport.go;l=1242-1306
  1264  type wantConnQueue struct {
  1265  	// This is a queue, not a deque.
  1266  	// It is split into two stages - head[headPos:] and tail.
  1267  	// popFront is trivial (headPos++) on the first stage, and
  1268  	// pushBack is trivial (append) on the second stage.
  1269  	// If the first stage is empty, popFront can swap the
  1270  	// first and second stages to remedy the situation.
  1271  	//
  1272  	// This two-stage split is analogous to the use of two lists
  1273  	// in Okasaki's purely functional queue but without the
  1274  	// overhead of reversing the list when swapping stages.
  1275  	head    []*wantConn
  1276  	headPos int
  1277  	tail    []*wantConn
  1278  }
  1279  
  1280  // len returns the number of items in the queue.
  1281  func (q *wantConnQueue) len() int {
  1282  	return len(q.head) - q.headPos + len(q.tail)
  1283  }
  1284  
  1285  // pushBack adds w to the back of the queue.
  1286  func (q *wantConnQueue) pushBack(w *wantConn) {
  1287  	q.tail = append(q.tail, w)
  1288  }
  1289  
  1290  // popFront removes and returns the wantConn at the front of the queue.
  1291  func (q *wantConnQueue) popFront() *wantConn {
  1292  	if q.headPos >= len(q.head) {
  1293  		if len(q.tail) == 0 {
  1294  			return nil
  1295  		}
  1296  		// Pick up tail as new head, clear tail.
  1297  		q.head, q.headPos, q.tail = q.tail, 0, q.head[:0]
  1298  	}
  1299  	w := q.head[q.headPos]
  1300  	q.head[q.headPos] = nil
  1301  	q.headPos++
  1302  	return w
  1303  }
  1304  
  1305  // peekFront returns the wantConn at the front of the queue without removing it.
  1306  func (q *wantConnQueue) peekFront() *wantConn {
  1307  	if q.headPos < len(q.head) {
  1308  		return q.head[q.headPos]
  1309  	}
  1310  	if len(q.tail) > 0 {
  1311  		return q.tail[0]
  1312  	}
  1313  	return nil
  1314  }
  1315  
  1316  // cleanFront pops any wantConns that are no longer waiting from the head of the queue.
  1317  func (q *wantConnQueue) cleanFront() {
  1318  	for {
  1319  		w := q.peekFront()
  1320  		if w == nil || w.waiting() {
  1321  			return
  1322  		}
  1323  		q.popFront()
  1324  	}
  1325  }