|
@@ -23,6 +23,13 @@ type atomicBool int32
|
|
func (b *atomicBool) isSet() bool { return atomic.LoadInt32((*int32)(b)) != 0 }
|
|
func (b *atomicBool) isSet() bool { return atomic.LoadInt32((*int32)(b)) != 0 }
|
|
func (b *atomicBool) setFalse() { atomic.StoreInt32((*int32)(b), 0) }
|
|
func (b *atomicBool) setFalse() { atomic.StoreInt32((*int32)(b), 0) }
|
|
func (b *atomicBool) setTrue() { atomic.StoreInt32((*int32)(b), 1) }
|
|
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 (
|
|
const (
|
|
cFILE_SKIP_COMPLETION_PORT_ON_SUCCESS = 1
|
|
cFILE_SKIP_COMPLETION_PORT_ON_SUCCESS = 1
|
|
@@ -71,7 +78,7 @@ func initIo() {
|
|
type win32File struct {
|
|
type win32File struct {
|
|
handle syscall.Handle
|
|
handle syscall.Handle
|
|
wg sync.WaitGroup
|
|
wg sync.WaitGroup
|
|
- closing bool
|
|
|
|
|
|
+ closing atomicBool
|
|
readDeadline deadlineHandler
|
|
readDeadline deadlineHandler
|
|
writeDeadline deadlineHandler
|
|
writeDeadline deadlineHandler
|
|
}
|
|
}
|
|
@@ -107,9 +114,9 @@ func MakeOpenFile(h syscall.Handle) (io.ReadWriteCloser, error) {
|
|
|
|
|
|
// 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() {
|
|
- 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
|
|
// cancel all IO and wait for it to complete
|
|
- f.closing = true
|
|
|
|
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
|
|
@@ -127,10 +134,10 @@ func (f *win32File) Close() error {
|
|
// prepareIo prepares for a new IO operation.
|
|
// prepareIo prepares for a new IO operation.
|
|
// 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)
|
|
|
|
- if f.closing {
|
|
|
|
|
|
+ if f.closing.isSet() {
|
|
return nil, ErrFileClosed
|
|
return nil, ErrFileClosed
|
|
}
|
|
}
|
|
|
|
+ f.wg.Add(1)
|
|
c := &ioOperation{}
|
|
c := &ioOperation{}
|
|
c.ch = make(chan ioResult)
|
|
c.ch = make(chan ioResult)
|
|
return c, nil
|
|
return c, nil
|
|
@@ -159,7 +166,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.closing.isSet() {
|
|
cancelIoEx(f.handle, &c.o)
|
|
cancelIoEx(f.handle, &c.o)
|
|
}
|
|
}
|
|
|
|
|
|
@@ -175,7 +182,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.closing.isSet() {
|
|
err = ErrFileClosed
|
|
err = ErrFileClosed
|
|
}
|
|
}
|
|
}
|
|
}
|