From c57b87d6d43c278238e4b06d360a90805f3d8ea8 Mon Sep 17 00:00:00 2001 From: Darren Stahl Date: Thu, 3 Aug 2017 15:26:45 -0700 Subject: [PATCH 1/2] Prevent adding to a sync.WaitGroup that has a waiter Signed-off-by: Darren Stahl --- file.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/file.go b/file.go index 2a311d1f..99cc07fb 100644 --- a/file.go +++ b/file.go @@ -78,6 +78,7 @@ func initIo() { type win32File struct { handle syscall.Handle wg sync.WaitGroup + wgLock sync.RWMutex closing atomicBool readDeadline deadlineHandler writeDeadline deadlineHandler @@ -118,7 +119,9 @@ func (f *win32File) closeHandle() { if !f.closing.swap(true) { // cancel all IO and wait for it to complete cancelIoEx(f.handle, nil) + f.wgLock.Lock() f.wg.Wait() + f.wgLock.Unlock() // at this point, no new IO can start syscall.Close(f.handle) f.handle = 0 @@ -134,10 +137,13 @@ func (f *win32File) Close() error { // prepareIo prepares for a new IO operation. // The caller must call f.wg.Done() when the IO is finished, prior to Close() returning. func (f *win32File) prepareIo() (*ioOperation, error) { + f.wgLock.RLock() if f.closing.isSet() { + f.wgLock.RUnlock() return nil, ErrFileClosed } f.wg.Add(1) + f.wgLock.RUnlock() c := &ioOperation{} c.ch = make(chan ioResult) return c, nil From 3d52dc4a972575ca21b9e974def5a7f871cf9a33 Mon Sep 17 00:00:00 2001 From: Darren Stahl Date: Thu, 3 Aug 2017 15:27:15 -0700 Subject: [PATCH 2/2] Test bug: Fix server connections not closing in TestEchoWithMessaging Signed-off-by: Darren Stahl --- file.go | 6 ++++-- pipe_test.go | 4 +++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/file.go b/file.go index 99cc07fb..57ac3696 100644 --- a/file.go +++ b/file.go @@ -115,16 +115,18 @@ func MakeOpenFile(h syscall.Handle) (io.ReadWriteCloser, error) { // closeHandle closes the resources associated with a Win32 handle func (f *win32File) closeHandle() { + f.wgLock.Lock() // Atomically set that we are closing, releasing the resources only once. if !f.closing.swap(true) { + f.wgLock.Unlock() // cancel all IO and wait for it to complete cancelIoEx(f.handle, nil) - f.wgLock.Lock() f.wg.Wait() - f.wgLock.Unlock() // at this point, no new IO can start syscall.Close(f.handle) f.handle = 0 + } else { + f.wgLock.Unlock() } } diff --git a/pipe_test.go b/pipe_test.go index 4c6208c1..3bc02dae 100644 --- a/pipe_test.go +++ b/pipe_test.go @@ -367,11 +367,11 @@ func TestEchoWithMessaging(t *testing.T) { OutputBufferSize: 65536, } l, err := ListenPipe(testPipeName, &c) - if err != nil { t.Fatal(err) } defer l.Close() + listenerDone := make(chan bool) clientDone := make(chan bool) go func() { @@ -380,6 +380,8 @@ func TestEchoWithMessaging(t *testing.T) { if e != nil { t.Fatal(e) } + defer conn.Close() + time.Sleep(500 * time.Millisecond) // make *sure* we don't begin to read before eof signal is sent io.Copy(conn, conn) conn.(CloseWriter).CloseWrite()