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

View File

@ -138,6 +138,16 @@ func (t *RedisConnectorTest) TestPipelineClose(c *C) {
c.Assert(client.Close(), IsNil) 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 { type RedisConnPoolTest struct {