wasi: add nonblock_test.go from gotip, fix nonblock read on Unix-like (#1517)
Some checks failed
Release CLI / Pre-release build (push) Has been cancelled
Release CLI / Pre-release test (macos-12) (push) Has been cancelled
Release CLI / Pre-release test (ubuntu-22.04) (push) Has been cancelled
Release CLI / Pre-release test (windows-2022) (push) Has been cancelled
Release CLI / Release (push) Has been cancelled

Signed-off-by: Edoardo Vacchi <evacchi@users.noreply.github.com>
This commit is contained in:
Edoardo Vacchi
2023-06-15 01:08:44 +02:00
committed by GitHub
parent f385873239
commit b01ba67fdc
7 changed files with 259 additions and 8 deletions

View File

@@ -28,7 +28,7 @@ var (
emptyFile = "empty.txt"
)
func TestFileSetNonblock(t *testing.T) {
func TestStdioFileSetNonblock(t *testing.T) {
// Test using os.Pipe as it is known to support non-blocking reads.
r, w, err := os.Pipe()
require.NoError(t, err)
@@ -47,6 +47,54 @@ func TestFileSetNonblock(t *testing.T) {
require.False(t, rF.IsNonblock())
}
func TestRegularFileSetNonblock(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("Nonblock on regular files is not supported on Windows")
}
// Test using os.Pipe as it is known to support non-blocking reads.
r, w, err := os.Pipe()
require.NoError(t, err)
defer r.Close()
defer w.Close()
rF := newOsFile("", syscall.O_RDONLY, 0, r)
errno := rF.SetNonblock(true)
require.EqualErrno(t, 0, errno)
require.True(t, rF.IsNonblock())
// Read from the file without ever writing to it should not block.
buf := make([]byte, 8)
_, e := rF.Read(buf)
require.EqualErrno(t, syscall.EAGAIN, e)
errno = rF.SetNonblock(false)
require.EqualErrno(t, 0, errno)
require.False(t, rF.IsNonblock())
}
func TestReadFdNonblock(t *testing.T) {
// Test using os.Pipe as it is known to support non-blocking reads.
r, w, err := os.Pipe()
require.NoError(t, err)
defer r.Close()
defer w.Close()
fd := r.Fd()
err = setNonblock(fd, true)
require.NoError(t, err)
// Read from the file without ever writing to it should not block.
buf := make([]byte, 8)
_, e := readFd(fd, buf)
if runtime.GOOS == "windows" {
require.EqualErrno(t, syscall.ENOSYS, e)
} else {
require.EqualErrno(t, syscall.EAGAIN, e)
}
}
func TestFileSetAppend(t *testing.T) {
tmpDir := t.TempDir()

View File

@@ -0,0 +1,21 @@
//go:build unix || darwin || linux
package sysfs
import (
"syscall"
"github.com/tetratelabs/wazero/internal/platform"
)
const NonBlockingFileIoSupported = true
// readFd exposes syscall.Read.
func readFd(fd uintptr, buf []byte) (int, syscall.Errno) {
if len(buf) == 0 {
return 0, 0 // Short-circuit 0-len reads.
}
n, err := syscall.Read(int(fd), buf)
errno := platform.UnwrapOSError(err)
return n, errno
}

View File

@@ -0,0 +1,12 @@
//go:build !unix && !linux && !darwin
package sysfs
import "syscall"
const NonBlockingFileIoSupported = false
// readFd returns ENOSYS on unsupported platforms.
func readFd(fd uintptr, buf []byte) (int, syscall.Errno) {
return -1, syscall.ENOSYS
}

View File

@@ -13,7 +13,7 @@ import (
func newOsFile(openPath string, openFlag int, openPerm fs.FileMode, f *os.File) fsapi.File {
return &windowsOsFile{
osFile: osFile{path: openPath, flag: openFlag, perm: openPerm, file: f},
osFile: osFile{path: openPath, flag: openFlag, perm: openPerm, file: f, fd: f.Fd()},
}
}

View File

@@ -12,7 +12,7 @@ import (
)
func newDefaultOsFile(openPath string, openFlag int, openPerm fs.FileMode, f *os.File) fsapi.File {
return &osFile{path: openPath, flag: openFlag, perm: openPerm, file: f}
return &osFile{path: openPath, flag: openFlag, perm: openPerm, file: f, fd: f.Fd()}
}
// osFile is a file opened with this package, and uses os.File or syscalls to
@@ -22,6 +22,7 @@ type osFile struct {
flag int
perm fs.FileMode
file *os.File
fd uintptr
// closed is true when closed was called. This ensures proper syscall.EBADF
closed bool
@@ -92,7 +93,7 @@ func (f *osFile) SetNonblock(enable bool) (errno syscall.Errno) {
} else {
f.flag &= ^fsapi.O_NONBLOCK
}
if err := setNonblock(f.file.Fd(), enable); err != nil {
if err := setNonblock(f.fd, enable); err != nil {
return fileError(f, f.closed, platform.UnwrapOSError(err))
}
return 0
@@ -126,7 +127,15 @@ func (f *osFile) Stat() (fsapi.Stat_t, syscall.Errno) {
// Read implements the same method as documented on fsapi.File
func (f *osFile) Read(buf []byte) (n int, errno syscall.Errno) {
if n, errno = read(f.file, buf); errno != 0 {
if len(buf) == 0 {
return 0, 0 // Short-circuit 0-len reads.
}
if NonBlockingFileIoSupported && f.IsNonblock() {
n, errno = readFd(f.fd, buf)
} else {
n, errno = read(f.file, buf)
}
if errno != 0 {
// Defer validation overhead until we've already had an error.
errno = fileError(f, f.closed, errno)
}
@@ -160,7 +169,7 @@ func (f *osFile) Seek(offset int64, whence int) (newOffset int64, errno syscall.
// PollRead implements the same method as documented on fsapi.File
func (f *osFile) PollRead(timeout *time.Duration) (ready bool, errno syscall.Errno) {
fdSet := platform.FdSet{}
fd := int(f.file.Fd())
fd := int(f.fd)
fdSet.Set(fd)
nfds := fd + 1 // See https://man7.org/linux/man-pages/man2/select.2.html#:~:text=condition%20has%20occurred.-,nfds,-This%20argument%20should
count, err := _select(nfds, &fdSet, nil, nil, timeout)
@@ -232,7 +241,7 @@ func (f *osFile) Chown(uid, gid int) syscall.Errno {
return syscall.EBADF
}
return fchown(f.file.Fd(), uid, gid)
return fchown(f.fd, uid, gid)
}
// Utimens implements the same method as documented on fsapi.File
@@ -241,7 +250,7 @@ func (f *osFile) Utimens(times *[2]syscall.Timespec) syscall.Errno {
return syscall.EBADF
}
err := futimens(f.file.Fd(), times)
err := futimens(f.fd, times)
return platform.UnwrapOSError(err)
}