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:
		
							
								
								
									
										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.
 | 
			
		||||
// It takes ownership of this handle and will close it if it is garbage collected.
 | 
			
		||||
type win32File struct {
 | 
			
		||||
	sync.Mutex
 | 
			
		||||
	handle        syscall.Handle
 | 
			
		||||
	wg            sync.WaitGroup
 | 
			
		||||
	closing       bool
 | 
			
		||||
@@ -105,17 +106,28 @@ func MakeOpenFile(h syscall.Handle) (io.ReadWriteCloser, error) {
 | 
			
		||||
	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
 | 
			
		||||
func (f *win32File) closeHandle() {
 | 
			
		||||
	f.Lock()
 | 
			
		||||
	if !f.closing {
 | 
			
		||||
		// cancel all IO and wait for it to complete
 | 
			
		||||
		f.closing = true
 | 
			
		||||
		f.Unlock()
 | 
			
		||||
		cancelIoEx(f.handle, nil)
 | 
			
		||||
		f.wg.Wait()
 | 
			
		||||
		// at this point, no new IO can start
 | 
			
		||||
		syscall.Close(f.handle)
 | 
			
		||||
		f.handle = 0
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	f.Unlock()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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.
 | 
			
		||||
func (f *win32File) prepareIo() (*ioOperation, error) {
 | 
			
		||||
	f.wg.Add(1)
 | 
			
		||||
	if f.closing {
 | 
			
		||||
	if f.isClosing() {
 | 
			
		||||
		return nil, ErrFileClosed
 | 
			
		||||
	}
 | 
			
		||||
	c := &ioOperation{}
 | 
			
		||||
@@ -159,7 +171,7 @@ func (f *win32File) asyncIo(c *ioOperation, d *deadlineHandler, bytes uint32, er
 | 
			
		||||
		return int(bytes), err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if f.closing {
 | 
			
		||||
	if f.isClosing() {
 | 
			
		||||
		cancelIoEx(f.handle, &c.o)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -175,7 +187,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.isClosing() {
 | 
			
		||||
				err = ErrFileClosed
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user