// completeRequest looks at iodata to see if a request is pending. If so, it waits for it to either complete or to // abort due to hitting the specified deadline. Deadline may be set to nil to wait forever. If no request is pending, // the content of iodata is returned. func (c *PipeConn) completeRequest(data iodata, deadline *time.Time, overlapped *syscall.Overlapped) (int, error) { if data.err == error_io_incomplete || data.err == syscall.ERROR_IO_PENDING { var timer <-chan time.Time if deadline != nil { if timeDiff := deadline.Sub(time.Now()); timeDiff > 0 { timer = time.After(timeDiff) } } done := make(chan iodata) go func() { n, err := waitForCompletion(c.handle, overlapped) done <- iodata{n, err} }() select { case data = <-done: case <-timer: syscall.CancelIoEx(c.handle, overlapped) data = iodata{0, timeout(c.addr.String())} } } // Windows will produce ERROR_BROKEN_PIPE upon closing // a handle on the other end of a connection. Go RPC // expects an io.EOF error in this case. if data.err == syscall.ERROR_BROKEN_PIPE { data.err = io.EOF } return int(data.n), data.err }
// ExecIO executes a single IO operation oi. It submits and cancels // IO in the current thread for systems where Windows CancelIoEx API // is available. Alternatively, it passes the request onto // a special goroutine and waits for completion or cancels request. // deadline is unix nanos. func (s *ioSrv) ExecIO(oi anOpIface, deadline int64) (int, error) { var err error o := oi.Op() // Calculate timeout delta. var delta int64 if deadline != 0 { delta = deadline - time.Now().UnixNano() if delta <= 0 { return 0, &OpError{oi.Name(), o.fd.net, o.fd.laddr, errTimeout} } } // Start IO. if canCancelIO { err = oi.Submit() } else { // Send request to a special dedicated thread, // so it can stop the IO with CancelIO later. s.submchan <- oi err = <-o.errnoc } switch err { case nil: // IO completed immediately, but we need to get our completion message anyway. case syscall.ERROR_IO_PENDING: // IO started, and we have to wait for its completion. err = nil default: return 0, &OpError{oi.Name(), o.fd.net, o.fd.laddr, err} } // Setup timer, if deadline is given. var timer <-chan time.Time if delta > 0 { t := time.NewTimer(time.Duration(delta) * time.Nanosecond) defer t.Stop() timer = t.C } // Wait for our request to complete. var r ioResult var cancelled, timeout bool select { case r = <-o.resultc: case <-timer: cancelled = true timeout = true case <-o.fd.closec: cancelled = true } if cancelled { // Cancel it. if canCancelIO { err := syscall.CancelIoEx(syscall.Handle(o.Op().fd.sysfd), &o.o) // Assuming ERROR_NOT_FOUND is returned, if IO is completed. if err != nil && err != syscall.ERROR_NOT_FOUND { // TODO(brainman): maybe do something else, but panic. panic(err) } } else { s.canchan <- oi <-o.errnoc } // Wait for IO to be canceled or complete successfully. r = <-o.resultc if r.err == syscall.ERROR_OPERATION_ABORTED { // IO Canceled if timeout { r.err = errTimeout } else { r.err = errClosing } } } if r.err != nil { err = &OpError{oi.Name(), o.fd.net, o.fd.laddr, r.err} } return int(r.qty), err }
// ExecIO executes a single IO operation o. It submits and cancels // IO in the current thread for systems where Windows CancelIoEx API // is available. Alternatively, it passes the request onto // runtime netpoll and waits for completion or cancels request. func (s *ioSrv) ExecIO(o *operation, name string, submit func(o *operation) error) (int, error) { fd := o.fd // Notify runtime netpoll about starting IO. err := fd.pd.Prepare(int(o.mode)) if err != nil { return 0, &OpError{name, fd.net, fd.laddr, err} } // Start IO. if canCancelIO { err = submit(o) } else { // Send request to a special dedicated thread, // so it can stop the IO with CancelIO later. s.req <- ioSrvReq{o, submit} err = <-o.errc } switch err { case nil: // IO completed immediately if o.fd.skipSyncNotif { // No completion message will follow, so return immediately. return int(o.qty), nil } // Need to get our completion message anyway. case syscall.ERROR_IO_PENDING: // IO started, and we have to wait for its completion. err = nil default: return 0, &OpError{name, fd.net, fd.laddr, err} } // Wait for our request to complete. err = fd.pd.Wait(int(o.mode)) if err == nil { // All is good. Extract our IO results and return. if o.errno != 0 { err = syscall.Errno(o.errno) return 0, &OpError{name, fd.net, fd.laddr, err} } return int(o.qty), nil } // IO is interrupted by "close" or "timeout" netpollErr := err switch netpollErr { case errClosing, errTimeout: // will deal with those. default: panic("net: unexpected runtime.netpoll error: " + netpollErr.Error()) } // Cancel our request. if canCancelIO { err := syscall.CancelIoEx(fd.sysfd, &o.o) // Assuming ERROR_NOT_FOUND is returned, if IO is completed. if err != nil && err != syscall.ERROR_NOT_FOUND { // TODO(brainman): maybe do something else, but panic. panic(err) } } else { s.req <- ioSrvReq{o, nil} <-o.errc } // Wait for cancellation to complete. fd.pd.WaitCanceled(int(o.mode)) if o.errno != 0 { err = syscall.Errno(o.errno) if err == syscall.ERROR_OPERATION_ABORTED { // IO Canceled err = netpollErr } return 0, &OpError{name, fd.net, fd.laddr, err} } // We issued cancellation request. But, it seems, IO operation succeeded // before cancellation request run. We need to treat IO operation as // succeeded (the bytes are actually sent/recv from network). return int(o.qty), nil }