From 20d22a67e3f0f7f98d3c4ef9d4f2e8944b50f1c4 Mon Sep 17 00:00:00 2001 From: Jack Christensen Date: Fri, 4 Mar 2016 16:19:48 -0600 Subject: [PATCH] Prevent deadlock on ConnPool.Acquire with Reset If no connections are available, and the pool is reset, there will be no connections at all in the pool. So the pool needs to be able to create a connection after waiting for a connection to be released. --- conn_pool.go | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/conn_pool.go b/conn_pool.go index 695dc564..b0aa4278 100644 --- a/conn_pool.go +++ b/conn_pool.go @@ -73,48 +73,49 @@ func NewConnPool(config ConnPoolConfig) (p *ConnPool, err error) { } // Acquire takes exclusive use of a connection until it is released. -func (p *ConnPool) Acquire() (c *Conn, err error) { +func (p *ConnPool) Acquire() (*Conn, error) { p.cond.L.Lock() - defer p.cond.L.Unlock() + c, err := p.acquire() + p.cond.L.Unlock() + return c, err +} +// acquire performs acquision assuming pool is already locked +func (p *ConnPool) acquire() (*Conn, error) { if p.closed { return nil, errors.New("cannot acquire from closed pool") } // A connection is available if len(p.availableConnections) > 0 { - c = p.availableConnections[len(p.availableConnections)-1] + c := p.availableConnections[len(p.availableConnections)-1] c.poolResetCount = p.resetCount p.availableConnections = p.availableConnections[:len(p.availableConnections)-1] - return + return c, nil } // No connections are available, but we can create more if len(p.allConnections) < p.maxConnections { - c, err = p.createConnection() + c, err := p.createConnection() if err != nil { - return + return nil, err } c.poolResetCount = p.resetCount p.allConnections = append(p.allConnections, c) - return + return c, nil } // All connections are in use and we cannot create more - if len(p.availableConnections) == 0 { - if p.logLevel >= LogLevelWarn { - p.logger.Warn("All connections in pool are busy - waiting...") - } - for len(p.availableConnections) == 0 { - p.cond.Wait() - } + if p.logLevel >= LogLevelWarn { + p.logger.Warn("All connections in pool are busy - waiting...") } - c = p.availableConnections[len(p.availableConnections)-1] - c.poolResetCount = p.resetCount - p.availableConnections = p.availableConnections[:len(p.availableConnections)-1] + // Wait until there is an available connection OR room to create a new connection + for len(p.availableConnections) == 0 && len(p.allConnections) == p.maxConnections { + p.cond.Wait() + } - return + return p.acquire() } // Release gives up use of a connection.