Ver Fonte

clientv3: fix deadlock on Get with concurrent Close

Anthony Romano há 9 anos atrás
pai
commit
1d88130522
2 ficheiros alterados com 10 adições e 14 exclusões
  1. 4 10
      clientv3/integration/kv_test.go
  2. 6 4
      clientv3/remote_client.go

+ 4 - 10
clientv3/integration/kv_test.go

@@ -288,25 +288,19 @@ func TestKVGetErrConnClosed(t *testing.T) {
 	cli := clus.Client(0)
 	cli := clus.Client(0)
 	kv := clientv3.NewKV(cli)
 	kv := clientv3.NewKV(cli)
 
 
-	closed, donec := make(chan struct{}), make(chan struct{})
+	donec := make(chan struct{})
 	go func() {
 	go func() {
-		select {
-		case <-time.After(3 * time.Second):
-			t.Fatal("cli.Close took too long")
-		case <-closed:
-		}
-
-		if _, err := kv.Get(context.TODO(), "foo"); err != rpctypes.ErrConnClosed {
+		defer close(donec)
+		_, err := kv.Get(context.TODO(), "foo")
+		if err != nil && err != rpctypes.ErrConnClosed {
 			t.Fatalf("expected %v, got %v", rpctypes.ErrConnClosed, err)
 			t.Fatalf("expected %v, got %v", rpctypes.ErrConnClosed, err)
 		}
 		}
-		close(donec)
 	}()
 	}()
 
 
 	if err := cli.Close(); err != nil {
 	if err := cli.Close(); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 	clus.TakeClient(0)
 	clus.TakeClient(0)
-	close(closed)
 
 
 	select {
 	select {
 	case <-time.After(3 * time.Second):
 	case <-time.After(3 * time.Second):

+ 6 - 4
clientv3/remote_client.go

@@ -80,21 +80,23 @@ func (r *remoteClient) tryUpdate() bool {
 	return true
 	return true
 }
 }
 
 
+// acquire gets the client read lock on an established connection or
+// returns an error without holding the lock.
 func (r *remoteClient) acquire(ctx context.Context) error {
 func (r *remoteClient) acquire(ctx context.Context) error {
 	for {
 	for {
+		r.mu.Lock()
 		r.client.mu.RLock()
 		r.client.mu.RLock()
 		closed := r.client.cancel == nil
 		closed := r.client.cancel == nil
 		c := r.client.conn
 		c := r.client.conn
-		r.mu.Lock()
 		match := r.conn == c
 		match := r.conn == c
 		r.mu.Unlock()
 		r.mu.Unlock()
-		if closed {
-			return rpctypes.ErrConnClosed
-		}
 		if match {
 		if match {
 			return nil
 			return nil
 		}
 		}
 		r.client.mu.RUnlock()
 		r.client.mu.RUnlock()
+		if closed {
+			return rpctypes.ErrConnClosed
+		}
 		if err := r.reconnectWait(ctx, nil); err != nil {
 		if err := r.reconnectWait(ctx, nil); err != nil {
 			return err
 			return err
 		}
 		}