go-winio: Prevent Data Race when accessing closing
The race usually happens when `closeHandle()` and `prepareIo()` are called concurrently; the former tries to set `closing` to `true` the latter tries to read its value. In order to avoid this issue, we added a lock around the variable. Signed-off-by: Kenfe-Mickael Laventure <mickael.laventure@gmail.com>
This commit is contained in:
parent
db1b0a2a5a
commit
7f786cf075
18
vendor/github.com/Microsoft/go-winio/file.go
generated
vendored
18
vendor/github.com/Microsoft/go-winio/file.go
generated
vendored
@ -69,6 +69,7 @@ func initIo() {
|
|||||||
// win32File implements Reader, Writer, and Closer on a Win32 handle without blocking in a syscall.
|
// win32File implements Reader, Writer, and Closer on a Win32 handle without blocking in a syscall.
|
||||||
// It takes ownership of this handle and will close it if it is garbage collected.
|
// It takes ownership of this handle and will close it if it is garbage collected.
|
||||||
type win32File struct {
|
type win32File struct {
|
||||||
|
sync.Mutex
|
||||||
handle syscall.Handle
|
handle syscall.Handle
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
closing bool
|
closing bool
|
||||||
@ -105,17 +106,28 @@ func MakeOpenFile(h syscall.Handle) (io.ReadWriteCloser, error) {
|
|||||||
return makeWin32File(h)
|
return makeWin32File(h)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *win32File) isClosing() bool {
|
||||||
|
f.Lock()
|
||||||
|
closing := f.closing
|
||||||
|
f.Unlock()
|
||||||
|
return closing
|
||||||
|
}
|
||||||
|
|
||||||
// closeHandle closes the resources associated with a Win32 handle
|
// closeHandle closes the resources associated with a Win32 handle
|
||||||
func (f *win32File) closeHandle() {
|
func (f *win32File) closeHandle() {
|
||||||
|
f.Lock()
|
||||||
if !f.closing {
|
if !f.closing {
|
||||||
// cancel all IO and wait for it to complete
|
// cancel all IO and wait for it to complete
|
||||||
f.closing = true
|
f.closing = true
|
||||||
|
f.Unlock()
|
||||||
cancelIoEx(f.handle, nil)
|
cancelIoEx(f.handle, nil)
|
||||||
f.wg.Wait()
|
f.wg.Wait()
|
||||||
// at this point, no new IO can start
|
// at this point, no new IO can start
|
||||||
syscall.Close(f.handle)
|
syscall.Close(f.handle)
|
||||||
f.handle = 0
|
f.handle = 0
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
f.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close closes a win32File.
|
// Close closes a win32File.
|
||||||
@ -128,7 +140,7 @@ func (f *win32File) Close() error {
|
|||||||
// The caller must call f.wg.Done() when the IO is finished, prior to Close() returning.
|
// The caller must call f.wg.Done() when the IO is finished, prior to Close() returning.
|
||||||
func (f *win32File) prepareIo() (*ioOperation, error) {
|
func (f *win32File) prepareIo() (*ioOperation, error) {
|
||||||
f.wg.Add(1)
|
f.wg.Add(1)
|
||||||
if f.closing {
|
if f.isClosing() {
|
||||||
return nil, ErrFileClosed
|
return nil, ErrFileClosed
|
||||||
}
|
}
|
||||||
c := &ioOperation{}
|
c := &ioOperation{}
|
||||||
@ -159,7 +171,7 @@ func (f *win32File) asyncIo(c *ioOperation, d *deadlineHandler, bytes uint32, er
|
|||||||
return int(bytes), err
|
return int(bytes), err
|
||||||
}
|
}
|
||||||
|
|
||||||
if f.closing {
|
if f.isClosing() {
|
||||||
cancelIoEx(f.handle, &c.o)
|
cancelIoEx(f.handle, &c.o)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -175,7 +187,7 @@ func (f *win32File) asyncIo(c *ioOperation, d *deadlineHandler, bytes uint32, er
|
|||||||
case r = <-c.ch:
|
case r = <-c.ch:
|
||||||
err = r.err
|
err = r.err
|
||||||
if err == syscall.ERROR_OPERATION_ABORTED {
|
if err == syscall.ERROR_OPERATION_ABORTED {
|
||||||
if f.closing {
|
if f.isClosing() {
|
||||||
err = ErrFileClosed
|
err = ErrFileClosed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user