forked from mirror/redis
Fix deadlock when Client is configured with IdleTimeout. Fixes #25.
This commit is contained in:
parent
9d23f2cf26
commit
75754ff436
26
v2/pool.go
26
v2/pool.go
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Reference in New Issue