Commit 187957d3 authored by Ian Lance Taylor's avatar Ian Lance Taylor

os: add deadline methods for File type

Add SetDeadline, SetReadDeadline, and SetWriteDeadline methods to os.File,
just as they exist today for the net package.

Fixes #22114

Change-Id: I4d390d739169b991175baba676010897dc8568fa
Reviewed-on: https://go-review.googlesource.com/71770
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: 's avatarDavid Crawshaw <crawshaw@golang.org>
Reviewed-by: 's avatarJoe Tsai <thebrokentoaster@gmail.com>
parent 1126d148
......@@ -21,6 +21,10 @@ var ErrNetClosing = errors.New("use of closed network connection")
// has been closed.
var ErrFileClosing = errors.New("use of closed file")
// ErrNoDeadline is returned when a request is made to set a deadline
// on a file type that does not use the poller.
var ErrNoDeadline = errors.New("file type does not support deadline")
// Return the appropriate closing error based on isFile.
func errClosing(isFile bool) error {
if isFile {
......
......@@ -149,7 +149,7 @@ func setDeadlineImpl(fd *FD, t time.Time, mode int) error {
}
defer fd.decref()
if fd.pd.runtimeCtx == 0 {
return errors.New("file type does not support deadlines")
return ErrNoDeadline
}
runtime_pollSetDeadline(fd.pd.runtimeCtx, d, mode)
return nil
......
......@@ -6,6 +6,7 @@ package os
import (
"errors"
"internal/poll"
)
// Portable analogs of some common system call errors.
......@@ -15,8 +16,13 @@ var (
ErrExist = errors.New("file already exists")
ErrNotExist = errors.New("file does not exist")
ErrClosed = errors.New("file already closed")
ErrNoDeadline = poll.ErrNoDeadline
)
type timeout interface {
Timeout() bool
}
// PathError records an error and the operation and file path that caused it.
type PathError struct {
Op string
......@@ -26,6 +32,12 @@ type PathError struct {
func (e *PathError) Error() string { return e.Op + " " + e.Path + ": " + e.Err.Error() }
// Timeout reports whether this error represents a timeout.
func (e *PathError) Timeout() bool {
t, ok := e.Err.(timeout)
return ok && t.Timeout()
}
// SyscallError records an error from a specific system call.
type SyscallError struct {
Syscall string
......@@ -34,6 +46,12 @@ type SyscallError struct {
func (e *SyscallError) Error() string { return e.Syscall + ": " + e.Err.Error() }
// Timeout reports whether this error represents a timeout.
func (e *SyscallError) Timeout() bool {
t, ok := e.Err.(timeout)
return ok && t.Timeout()
}
// NewSyscallError returns, as an error, a new SyscallError
// with the given system call name and error details.
// As a convenience, if err is nil, NewSyscallError returns nil.
......@@ -65,6 +83,13 @@ func IsPermission(err error) bool {
return isPermission(err)
}
// IsTimeout returns a boolean indicating whether the error is known
// to report that a timeout occurred.
func IsTimeout(err error) bool {
terr, ok := underlyingError(err).(timeout)
return ok && terr.Timeout()
}
// underlyingError returns the underlying error for known os error types.
func underlyingError(err error) error {
switch err := err.(type) {
......
......@@ -41,6 +41,7 @@ import (
"internal/poll"
"io"
"syscall"
"time"
)
// Name returns the name of the file as presented to Open.
......@@ -316,3 +317,47 @@ func Chmod(name string, mode FileMode) error { return chmod(name, mode) }
// Chmod changes the mode of the file to mode.
// If there is an error, it will be of type *PathError.
func (f *File) Chmod(mode FileMode) error { return f.chmod(mode) }
// SetDeadline sets the read and write deadlines for a File.
// It is equivalent to calling both SetReadDeadline and SetWriteDeadline.
//
// Only some kinds of files support setting a deadline. Calls to SetDeadline
// for files that do not support deadlines will return ErrNoDeadline.
// On most systems ordinary files do not support deadlines, but pipes do.
//
// A deadline is an absolute time after which I/O operations fail with an
// error instead of blocking. The deadline applies to all future and pending
// I/O, not just the immediately following call to Read or Write.
// After a deadline has been exceeded, the connection can be refreshed
// by setting a deadline in the future.
//
// An error returned after a timeout fails will implement the
// Timeout method, and calling the Timeout method will return true.
// The PathError and SyscallError types implement the Timeout method.
// In general, call IsTimeout to test whether an error indicates a timeout.
//
// An idle timeout can be implemented by repeatedly extending
// the deadline after successful Read or Write calls.
//
// A zero value for t means I/O operations will not time out.
func (f *File) SetDeadline(t time.Time) error {
return f.setDeadline(t)
}
// SetReadDeadline sets the deadline for future Read calls and any
// currently-blocked Read call.
// A zero value for t means Read will not time out.
// Not all files support setting deadlines; see SetDeadline.
func (f *File) SetReadDeadline(t time.Time) error {
return f.setReadDeadline(t)
}
// SetWriteDeadline sets the deadline for any future Write calls and any
// currently-blocked Write call.
// Even if Write times out, it may return n > 0, indicating that
// some of the data was successfully written.
// A zero value for t means Write will not time out.
// Not all files support setting deadlines; see SetDeadline.
func (f *File) SetWriteDeadline(t time.Time) error {
return f.setWriteDeadline(t)
}
......@@ -5,6 +5,7 @@
package os
import (
"internal/poll"
"io"
"runtime"
"syscall"
......@@ -491,6 +492,30 @@ func (f *File) Chdir() error {
return nil
}
// setDeadline sets the read and write deadline.
func (f *File) setDeadline(time.Time) error {
if err := f.checkValid("SetDeadline"); err != nil {
return err
}
return poll.ErrNoDeadline
}
// setReadDeadline sets the read deadline.
func (f *File) setReadDeadline(time.Time) error {
if err := f.checkValid("SetReadDeadline"); err != nil {
return err
}
return poll.ErrNoDeadline
}
// setWriteDeadline sets the write deadline.
func (f *File) setWriteDeadline(time.Time) error {
if err := f.checkValid("SetWriteDeadline"); err != nil {
return err
}
return poll.ErrNoDeadline
}
// checkValid checks whether f is valid for use.
// If not, it returns an appropriate error, perhaps incorporating the operation name op.
func (f *File) checkValid(op string) error {
......
......@@ -159,6 +159,30 @@ func (f *File) Chdir() error {
return nil
}
// setDeadline sets the read and write deadline.
func (f *File) setDeadline(t time.Time) error {
if err := f.checkValid("SetDeadline"); err != nil {
return err
}
return f.pfd.SetDeadline(t)
}
// setReadDeadline sets the read deadline.
func (f *File) setReadDeadline(t time.Time) error {
if err := f.checkValid("SetReadDeadline"); err != nil {
return err
}
return f.pfd.SetReadDeadline(t)
}
// setWriteDeadline sets the write deadline.
func (f *File) setWriteDeadline(t time.Time) error {
if err := f.checkValid("SetWriteDeadline"); err != nil {
return err
}
return f.pfd.SetWriteDeadline(t)
}
// checkValid checks whether f is valid for use.
// If not, it returns an appropriate error, perhaps incorporating the operation name op.
func (f *File) checkValid(op string) error {
......
......@@ -75,12 +75,22 @@ func (f *File) Fd() uintptr {
// name. The returned value will be nil if fd is not a valid file
// descriptor.
func NewFile(fd uintptr, name string) *File {
return newFile(fd, name, false)
return newFile(fd, name, kindNewFile)
}
// newFile is like NewFile, but if pollable is true it tries to add the
// file to the runtime poller.
func newFile(fd uintptr, name string, pollable bool) *File {
// newFileKind describes the kind of file to newFile.
type newFileKind int
const (
kindNewFile newFileKind = iota
kindOpenFile
kindPipe
)
// newFile is like NewFile, but if called from OpenFile or Pipe
// (as passed in the kind parameter) it tries to add the file to
// the runtime poller.
func newFile(fd uintptr, name string, kind newFileKind) *File {
fdi := int(fd)
if fdi < 0 {
return nil
......@@ -98,10 +108,11 @@ func newFile(fd uintptr, name string, pollable bool) *File {
// Don't try to use kqueue with regular files on FreeBSD.
// It crashes the system unpredictably while running all.bash.
// Issue 19093.
if runtime.GOOS == "freebsd" {
pollable = false
if runtime.GOOS == "freebsd" && kind == kindOpenFile {
kind = kindNewFile
}
pollable := kind == kindOpenFile || kind == kindPipe
if err := f.pfd.Init("file", pollable); err != nil {
// An error here indicates a failure to register
// with the netpoll system. That can happen for
......@@ -183,7 +194,7 @@ func OpenFile(name string, flag int, perm FileMode) (*File, error) {
syscall.CloseOnExec(r)
}
return newFile(uintptr(r), name, true), nil
return newFile(uintptr(r), name, kindOpenFile), nil
}
// Close closes the File, rendering it unusable for I/O.
......
......@@ -24,5 +24,5 @@ func Pipe() (r *File, w *File, err error) {
syscall.CloseOnExec(p[1])
syscall.ForkLock.RUnlock()
return newFile(uintptr(p[0]), "|0", true), newFile(uintptr(p[1]), "|1", true), nil
return newFile(uintptr(p[0]), "|0", kindPipe), newFile(uintptr(p[1]), "|1", kindPipe), nil
}
......@@ -30,5 +30,5 @@ func Pipe() (r *File, w *File, err error) {
syscall.ForkLock.RUnlock()
}
return newFile(uintptr(p[0]), "|0", true), newFile(uintptr(p[1]), "|1", true), nil
return newFile(uintptr(p[0]), "|0", kindPipe), newFile(uintptr(p[1]), "|1", kindPipe), nil
}
......@@ -29,5 +29,5 @@ func Pipe() (r *File, w *File, err error) {
return nil, nil, NewSyscallError("pipe2", e)
}
return newFile(uintptr(p[0]), "|0", true), newFile(uintptr(p[1]), "|1", true), nil
return newFile(uintptr(p[0]), "|0", kindPipe), newFile(uintptr(p[1]), "|1", kindPipe), nil
}
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment