From 73020a3110c13c39a0bcc21499a2d2fd281c39fb Mon Sep 17 00:00:00 2001 From: Tim Ebringer Date: Wed, 19 Jul 2017 11:17:18 -0400 Subject: [PATCH] Bool is not atomic The Go race detector doesn't like concurrent access to bool. This change moves the bool to use atomicBool, and extends atomicBool with the swap() method, to handle conditional use. --- file.go | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/file.go b/file.go index 9c9ee5d2..2a311d1f 100644 --- a/file.go +++ b/file.go @@ -23,6 +23,13 @@ type atomicBool int32 func (b *atomicBool) isSet() bool { return atomic.LoadInt32((*int32)(b)) != 0 } func (b *atomicBool) setFalse() { atomic.StoreInt32((*int32)(b), 0) } func (b *atomicBool) setTrue() { atomic.StoreInt32((*int32)(b), 1) } +func (b *atomicBool) swap(new bool) bool { + var newInt int32 + if new { + newInt = 1 + } + return atomic.SwapInt32((*int32)(b), newInt) == 1 +} const ( cFILE_SKIP_COMPLETION_PORT_ON_SUCCESS = 1 @@ -71,7 +78,7 @@ func initIo() { type win32File struct { handle syscall.Handle wg sync.WaitGroup - closing bool + closing atomicBool readDeadline deadlineHandler writeDeadline deadlineHandler } @@ -107,9 +114,9 @@ func MakeOpenFile(h syscall.Handle) (io.ReadWriteCloser, error) { // closeHandle closes the resources associated with a Win32 handle func (f *win32File) closeHandle() { - if !f.closing { + // Atomically set that we are closing, releasing the resources only once. + if !f.closing.swap(true) { // cancel all IO and wait for it to complete - f.closing = true cancelIoEx(f.handle, nil) f.wg.Wait() // at this point, no new IO can start @@ -127,7 +134,7 @@ 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) { - if f.closing { + if f.closing.isSet() { return nil, ErrFileClosed } f.wg.Add(1) @@ -159,7 +166,7 @@ func (f *win32File) asyncIo(c *ioOperation, d *deadlineHandler, bytes uint32, er return int(bytes), err } - if f.closing { + if f.closing.isSet() { cancelIoEx(f.handle, &c.o) } @@ -175,7 +182,7 @@ func (f *win32File) asyncIo(c *ioOperation, d *deadlineHandler, bytes uint32, er case r = <-c.ch: err = r.err if err == syscall.ERROR_OPERATION_ABORTED { - if f.closing { + if f.closing.isSet() { err = ErrFileClosed } }