Example #1
0
// 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
}
Example #2
0
// 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
}
Example #3
0
// 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
}