From 14e5817f0d3397eb8920c9193869f41fcc82e788 Mon Sep 17 00:00:00 2001 From: INADA Naoki Date: Fri, 28 Sep 2018 21:42:12 +0900 Subject: [PATCH 1/2] Add test for #858 --- connection_test.go | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/connection_test.go b/connection_test.go index dec376117..352c54ed7 100644 --- a/connection_test.go +++ b/connection_test.go @@ -9,6 +9,7 @@ package mysql import ( + "context" "database/sql/driver" "testing" ) @@ -79,3 +80,31 @@ func TestCheckNamedValue(t *testing.T) { t.Fatalf("uint64 high-bit not converted, got %#v %T", value.Value, value.Value) } } + +// TestCleanCancel tests passed context is cancelled at start. +// No packet should be sent. Connection should keep current status. +func TestCleanCancel(t *testing.T) { + mc := &mysqlConn{ + closech: make(chan struct{}), + } + mc.startWatcher() + defer mc.cleanup() + + ctx, cancel := context.WithCancel(context.Background()) + cancel() + + for i := 0; i < 3; i++ { // Repeat same behavior + err := mc.Ping(ctx) + if err != context.Canceled { + t.Errorf("expected context.Canceled, got %#v", err) + } + + if mc.closed.IsSet() { + t.Error("expected mc is not closed, closed actually") + } + + if mc.watching { + t.Error("expected watching is false, but true") + } + } +} From ad1e76e701af38dcef45e5f99ef14f702ed5e73c Mon Sep 17 00:00:00 2001 From: INADA Naoki Date: Fri, 28 Sep 2018 22:01:51 +0900 Subject: [PATCH 2/2] Early return for context cancelled at start Don't break the connection when cancelled context is passed. Fix #858 --- connection.go | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/connection.go b/connection.go index 911be2060..f74235519 100644 --- a/connection.go +++ b/connection.go @@ -595,22 +595,21 @@ func (mc *mysqlConn) watchCancel(ctx context.Context) error { mc.cleanup() return nil } + // When ctx is already cancelled, don't watch it. + if err := ctx.Err(); err != nil { + return err + } + // When ctx is not cancellable, don't watch it. if ctx.Done() == nil { return nil } - - mc.watching = true - select { - default: - case <-ctx.Done(): - return ctx.Err() - } + // When watcher is not alive, can't watch it. if mc.watcher == nil { return nil } + mc.watching = true mc.watcher <- ctx - return nil }