@@ -10,6 +10,7 @@ import (
10
10
"internal/syscall/windows"
11
11
"io"
12
12
"sync"
13
+ "sync/atomic"
13
14
"syscall"
14
15
"unicode/utf16"
15
16
"unicode/utf8"
@@ -98,6 +99,12 @@ func (o *operation) setEvent() {
98
99
o .o .HEvent = h | 1
99
100
}
100
101
102
+ func (o * operation ) close () {
103
+ if o .o .HEvent != 0 {
104
+ syscall .CloseHandle (o .o .HEvent )
105
+ }
106
+ }
107
+
101
108
func (o * operation ) overlapped () * syscall.Overlapped {
102
109
if o .fd .isBlocking {
103
110
// Don't return the overlapped object if the file handle
@@ -169,7 +176,7 @@ func waitIO(o *operation) error {
169
176
panic ("can't wait on blocking operations" )
170
177
}
171
178
fd := o .fd
172
- if ! fd .pd . pollable () {
179
+ if ! fd .pollable () {
173
180
// The overlapped handle is not added to the runtime poller,
174
181
// the only way to wait for the IO to complete is block until
175
182
// the overlapped event is signaled.
@@ -190,7 +197,7 @@ func waitIO(o *operation) error {
190
197
// cancelIO cancels the IO operation o and waits for it to complete.
191
198
func cancelIO (o * operation ) {
192
199
fd := o .fd
193
- if ! fd .pd . pollable () {
200
+ if ! fd .pollable () {
194
201
return
195
202
}
196
203
// Cancel our request.
@@ -209,14 +216,13 @@ func cancelIO(o *operation) {
209
216
// to avoid reusing the values from a previous call.
210
217
func execIO (o * operation , submit func (o * operation ) error ) (int , error ) {
211
218
fd := o .fd
212
- fd .initIO ()
213
219
// Notify runtime netpoll about starting IO.
214
220
err := fd .pd .prepare (int (o .mode ), fd .isFile )
215
221
if err != nil {
216
222
return 0 , err
217
223
}
218
224
// Start IO.
219
- if ! fd .isBlocking && o .o .HEvent == 0 && ! fd .pd . pollable () {
225
+ if ! fd .isBlocking && o .o .HEvent == 0 && ! fd .pollable () {
220
226
// If the handle is opened for overlapped IO but we can't
221
227
// use the runtime poller, then we need to use an
222
228
// event to wait for the IO to complete.
@@ -244,10 +250,11 @@ func execIO(o *operation, submit func(o *operation) error) (int, error) {
244
250
err = windows .WSAGetOverlappedResult (fd .Sysfd , & o .o , & o .qty , false , & o .flags )
245
251
}
246
252
}
247
- // ERROR_OPERATION_ABORTED may have been caused by us. In that case,
248
- // map it to our own error. Don't do more than that, each submitted
249
- // function may have its own meaning for each error.
250
- if err == syscall .ERROR_OPERATION_ABORTED {
253
+ switch err {
254
+ case syscall .ERROR_OPERATION_ABORTED :
255
+ // ERROR_OPERATION_ABORTED may have been caused by us. In that case,
256
+ // map it to our own error. Don't do more than that, each submitted
257
+ // function may have its own meaning for each error.
251
258
if waitErr != nil {
252
259
// IO canceled by the poller while waiting for completion.
253
260
err = waitErr
@@ -257,6 +264,12 @@ func execIO(o *operation, submit func(o *operation) error) (int, error) {
257
264
// we assume it is interrupted by Close.
258
265
err = errClosing (fd .isFile )
259
266
}
267
+ case windows .ERROR_IO_INCOMPLETE :
268
+ // waitIO couldn't wait for the IO to complete.
269
+ if waitErr != nil {
270
+ // The wait error will be more informative.
271
+ err = waitErr
272
+ }
260
273
}
261
274
return int (o .qty ), err
262
275
}
@@ -314,9 +327,7 @@ type FD struct {
314
327
// Whether FILE_FLAG_OVERLAPPED was not set when opening the file.
315
328
isBlocking bool
316
329
317
- // Initialization parameters.
318
- initIOOnce sync.Once
319
- initIOErr error // only used in the net package
330
+ disassociated atomic.Bool
320
331
}
321
332
322
333
// setOffset sets the offset fields of the overlapped object
@@ -343,6 +354,12 @@ func (fd *FD) addOffset(off int) {
343
354
fd .setOffset (fd .offset + int64 (off ))
344
355
}
345
356
357
+ // pollable should be used instead of fd.pd.pollable(),
358
+ // as it is aware of the disassociated state.
359
+ func (fd * FD ) pollable () bool {
360
+ return fd .pd .pollable () && ! fd .disassociated .Load ()
361
+ }
362
+
346
363
// fileKind describes the kind of file.
347
364
type fileKind byte
348
365
@@ -353,35 +370,6 @@ const (
353
370
kindPipe
354
371
)
355
372
356
- func (fd * FD ) initIO () error {
357
- if fd .isBlocking {
358
- return nil
359
- }
360
- fd .initIOOnce .Do (func () {
361
- if fd .closing () {
362
- // Closing, nothing to do.
363
- return
364
- }
365
- // The runtime poller will ignore I/O completion
366
- // notifications not initiated by this package,
367
- // so it is safe to add handles owned by the caller.
368
- fd .initIOErr = fd .pd .init (fd )
369
- if fd .initIOErr != nil {
370
- return
371
- }
372
- fd .rop .runtimeCtx = fd .pd .runtimeCtx
373
- fd .wop .runtimeCtx = fd .pd .runtimeCtx
374
- if fd .kind != kindNet || socketCanUseSetFileCompletionNotificationModes {
375
- // Non-socket handles can use SetFileCompletionNotificationModes without problems.
376
- err := syscall .SetFileCompletionNotificationModes (fd .Sysfd ,
377
- syscall .FILE_SKIP_SET_EVENT_ON_HANDLE | syscall .FILE_SKIP_COMPLETION_PORT_ON_SUCCESS ,
378
- )
379
- fd .skipSyncNotif = err == nil
380
- }
381
- })
382
- return fd .initIOErr
383
- }
384
-
385
373
// Init initializes the FD. The Sysfd field should already be set.
386
374
// This can be called multiple times on a single FD.
387
375
// The net argument is a network name from the net package (e.g., "tcp"),
@@ -411,27 +399,55 @@ func (fd *FD) Init(net string, pollable bool) error {
411
399
fd .rop .fd = fd
412
400
fd .wop .fd = fd
413
401
414
- // A file handle (and its duplicated handles) can only be associated
415
- // with one IOCP. A new association will fail if the handle is already
416
- // associated. Defer the association until the first I/O operation so that
417
- // overlapped handles passed in os.NewFile have a chance to be used
418
- // with an external IOCP. This is common case, for example, when calling
419
- // os.NewFile on a handle just to pass it to a exec.Command standard
420
- // input/output/error. If the association fails, the I/O operations
421
- // will be performed synchronously.
422
- if fd .kind == kindNet {
423
- // The net package is the only consumer that requires overlapped
424
- // handles and that cares about handle IOCP association errors.
425
- // We can should do the IOCP association here.
426
- return fd .initIO ()
402
+ // It is safe to add overlapped handles that also perform I/O
403
+ // outside of the runtime poller. The runtime poller will ignore
404
+ // I/O completion notifications not initiated by us.
405
+ err := fd .pd .init (fd )
406
+ if err != nil {
407
+ return err
408
+ }
409
+ fd .rop .runtimeCtx = fd .pd .runtimeCtx
410
+ fd .wop .runtimeCtx = fd .pd .runtimeCtx
411
+ if fd .kind != kindNet || socketCanUseSetFileCompletionNotificationModes {
412
+ // Non-socket handles can use SetFileCompletionNotificationModes without problems.
413
+ err := syscall .SetFileCompletionNotificationModes (fd .Sysfd ,
414
+ syscall .FILE_SKIP_SET_EVENT_ON_HANDLE | syscall .FILE_SKIP_COMPLETION_PORT_ON_SUCCESS ,
415
+ )
416
+ fd .skipSyncNotif = err == nil
417
+ }
418
+ return nil
419
+ }
420
+
421
+ // DisassociateIOCP disassociates the file handle from the IOCP.
422
+ // The disassociate operation will not succeed if there is any
423
+ // in-progress IO operation on the file handle.
424
+ func (fd * FD ) DisassociateIOCP () error {
425
+ if err := fd .incref (); err != nil {
426
+ return err
427
+ }
428
+ defer fd .decref ()
429
+
430
+ if fd .isBlocking || ! fd .pollable () {
431
+ // Nothing to disassociate.
432
+ return nil
427
433
}
434
+
435
+ info := windows.FILE_COMPLETION_INFORMATION {}
436
+ if err := windows .NtSetInformationFile (fd .Sysfd , & windows.IO_STATUS_BLOCK {}, unsafe .Pointer (& info ), uint32 (unsafe .Sizeof (info )), windows .FileReplaceCompletionInformation ); err != nil {
437
+ return err
438
+ }
439
+ fd .disassociated .Store (true )
440
+ // Don't call fd.pd.close(), it would be too racy.
441
+ // There is no harm on leaving fd.pd open until Close is called.
428
442
return nil
429
443
}
430
444
431
445
func (fd * FD ) destroy () error {
432
446
if fd .Sysfd == syscall .InvalidHandle {
433
447
return syscall .EINVAL
434
448
}
449
+ fd .rop .close ()
450
+ fd .wop .close ()
435
451
// Poller may want to unregister fd in readiness notification mechanism,
436
452
// so this must be executed before fd.CloseFunc.
437
453
fd .pd .close ()
@@ -454,12 +470,7 @@ func (fd *FD) Close() error {
454
470
if ! fd .fdmu .increfAndClose () {
455
471
return errClosing (fd .isFile )
456
472
}
457
- // There is a potential race between a concurrent call to fd.initIO,
458
- // which calls fd.pd.init, and the call to fd.pd.evict below.
459
- // This is solved by calling fd.initIO ourselves, which will
460
- // block until the concurrent fd.initIO has completed. Note
461
- // that fd.initIO is no-op if first called from here.
462
- fd .initIO ()
473
+
463
474
if fd .kind == kindPipe {
464
475
syscall .CancelIoEx (fd .Sysfd , nil )
465
476
}
0 commit comments