Fix deadlock when Client is configured with IdleTimeout. Fixes #25.

This commit is contained in:
Vladimir Mihailenco 2014-02-06 15:30:49 +02:00
parent 9d23f2cf26
commit 75754ff436
2 changed files with 24 additions and 12 deletions

View File

@ -120,8 +120,8 @@ func (p *connPool) Get() (*conn, bool, error) {
break
}
if time.Since(cn.usedAt) > p.idleTimeout {
if err := p.Remove(cn); err != nil {
glog.Errorf("Remove failed: %s", err)
if err := p.remove(cn); err != nil {
glog.Errorf("remove failed: %s", err)
}
}
}
@ -183,23 +183,28 @@ func (p *connPool) Put(cn *conn) error {
return nil
}
func (p *connPool) Remove(cn *conn) (err error) {
func (p *connPool) Remove(cn *conn) error {
p.cond.L.Lock()
if p.closed {
// Noop, connection is already closed.
p.cond.L.Unlock()
return nil
}
if cn != nil {
err = cn.Close()
}
p.conns.Remove(cn.elem)
cn.elem = nil
err := p.remove(cn)
p.cond.Signal()
p.cond.L.Unlock()
return err
}
func (p *connPool) remove(cn *conn) error {
p.conns.Remove(cn.elem)
cn.elem = nil
if !cn.inUse {
p.idleNum--
}
return cn.Close()
}
// Returns number of idle connections.
func (p *connPool) Len() int {
defer p.cond.L.Unlock()
@ -223,14 +228,11 @@ func (p *connPool) Close() error {
p.closed = true
var retErr error
for e := p.conns.Front(); e != nil; e = e.Next() {
cn := e.Value.(*conn)
if err := cn.Close(); err != nil {
if err := p.remove(e.Value.(*conn)); err != nil {
glog.Errorf("cn.Close failed: %s", err)
retErr = err
}
cn.elem = nil
}
p.conns = nil
return retErr
}

View File

@ -138,6 +138,16 @@ func (t *RedisConnectorTest) TestPipelineClose(c *C) {
c.Assert(client.Close(), IsNil)
}
func (t *RedisConnectorTest) TestIdleTimeout(c *C) {
client := redis.NewTCPClient(&redis.Options{
Addr: redisAddr,
IdleTimeout: time.Nanosecond,
})
for i := 0; i < 10; i++ {
c.Assert(client.Ping().Err(), IsNil)
}
}
//------------------------------------------------------------------------------
type RedisConnPoolTest struct {