sysfs: changes PollRead to accept int32 timeoutMillis (#1597)
Signed-off-by: Adrian Cole <adrian@tetrate.io> Co-authored-by: Takeshi Yoneda <takeshi@tetrate.io>
This commit is contained in:
@@ -3,7 +3,6 @@ package fsapi
|
||||
import (
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"time"
|
||||
|
||||
experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
|
||||
"github.com/tetratelabs/wazero/sys"
|
||||
@@ -89,7 +88,7 @@ func (DirFile) Pread([]byte, int64) (int, experimentalsys.Errno) {
|
||||
}
|
||||
|
||||
// PollRead implements File.PollRead
|
||||
func (DirFile) PollRead(*time.Duration) (ready bool, errno experimentalsys.Errno) {
|
||||
func (DirFile) PollRead(int32) (ready bool, errno experimentalsys.Errno) {
|
||||
return false, experimentalsys.ENOSYS
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ package fsapi
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
|
||||
"github.com/tetratelabs/wazero/sys"
|
||||
@@ -16,10 +15,10 @@ import (
|
||||
//
|
||||
// # Errors
|
||||
//
|
||||
// All methods that can return an error return a Errno, which is zero
|
||||
// All methods that can return an error return a sys.Errno, which is zero
|
||||
// on success.
|
||||
//
|
||||
// Restricting to Errno matches current WebAssembly host functions,
|
||||
// Restricting to sys.Errno matches current WebAssembly host functions,
|
||||
// which are constrained to well-known error codes. For example, `GOOS=js` maps
|
||||
// hard coded values and panics otherwise. More commonly, WASI maps syscall
|
||||
// errors to u32 numeric values.
|
||||
@@ -86,9 +85,9 @@ type File interface {
|
||||
//
|
||||
// # Errors
|
||||
//
|
||||
// A zero Errno is success. The below are expected otherwise:
|
||||
// - ENOSYS: the implementation does not support this function.
|
||||
// - EBADF: the file or directory was closed.
|
||||
// A zero sys.Errno is success. The below are expected otherwise:
|
||||
// - sys.ENOSYS: the implementation does not support this function.
|
||||
// - sys.EBADF: the file or directory was closed.
|
||||
//
|
||||
// # Notes
|
||||
//
|
||||
@@ -109,9 +108,9 @@ type File interface {
|
||||
//
|
||||
// # Errors
|
||||
//
|
||||
// A zero Errno is success. The below are expected otherwise:
|
||||
// - ENOSYS: the implementation does not support this function.
|
||||
// - EBADF: the file or directory was closed.
|
||||
// A zero sys.Errno is success. The below are expected otherwise:
|
||||
// - sys.ENOSYS: the implementation does not support this function.
|
||||
// - sys.EBADF: the file or directory was closed.
|
||||
//
|
||||
// # Notes
|
||||
//
|
||||
@@ -124,9 +123,9 @@ type File interface {
|
||||
//
|
||||
// # Errors
|
||||
//
|
||||
// A zero Errno is success. The below are expected otherwise:
|
||||
// - ENOSYS: the implementation does not support this function.
|
||||
// - EBADF: the file or directory was closed.
|
||||
// A zero sys.Errno is success. The below are expected otherwise:
|
||||
// - sys.ENOSYS: the implementation does not support this function.
|
||||
// - sys.EBADF: the file or directory was closed.
|
||||
//
|
||||
// # Notes
|
||||
//
|
||||
@@ -142,10 +141,10 @@ type File interface {
|
||||
//
|
||||
// # Errors
|
||||
//
|
||||
// A zero Errno is success. The below are expected otherwise:
|
||||
// - ENOSYS: the implementation does not support this function.
|
||||
// - EBADF: the file or directory was closed or not readable.
|
||||
// - EISDIR: the file was a directory.
|
||||
// A zero sys.Errno is success. The below are expected otherwise:
|
||||
// - sys.ENOSYS: the implementation does not support this function.
|
||||
// - sys.EBADF: the file or directory was closed or not readable.
|
||||
// - sys.EISDIR: the file was a directory.
|
||||
//
|
||||
// # Notes
|
||||
//
|
||||
@@ -160,11 +159,11 @@ type File interface {
|
||||
//
|
||||
// # Errors
|
||||
//
|
||||
// A zero Errno is success. The below are expected otherwise:
|
||||
// - ENOSYS: the implementation does not support this function.
|
||||
// - EBADF: the file or directory was closed or not readable.
|
||||
// - EINVAL: the offset was negative.
|
||||
// - EISDIR: the file was a directory.
|
||||
// A zero sys.Errno is success. The below are expected otherwise:
|
||||
// - sys.ENOSYS: the implementation does not support this function.
|
||||
// - sys.EBADF: the file or directory was closed or not readable.
|
||||
// - sys.EINVAL: the offset was negative.
|
||||
// - sys.EISDIR: the file was a directory.
|
||||
//
|
||||
// # Notes
|
||||
//
|
||||
@@ -195,10 +194,10 @@ type File interface {
|
||||
//
|
||||
// # Errors
|
||||
//
|
||||
// A zero Errno is success. The below are expected otherwise:
|
||||
// - ENOSYS: the implementation does not support this function.
|
||||
// - EBADF: the file or directory was closed or not readable.
|
||||
// - EINVAL: the offset was negative.
|
||||
// A zero sys.Errno is success. The below are expected otherwise:
|
||||
// - sys.ENOSYS: the implementation does not support this function.
|
||||
// - sys.EBADF: the file or directory was closed or not readable.
|
||||
// - sys.EINVAL: the offset was negative.
|
||||
//
|
||||
// # Notes
|
||||
//
|
||||
@@ -210,21 +209,24 @@ type File interface {
|
||||
//
|
||||
// # Parameters
|
||||
//
|
||||
// The `timeout` parameter when nil blocks up to forever.
|
||||
// The `timeoutMillis` parameter is how long to block for data to become
|
||||
// readable, or interrupted, in milliseconds. There are two special values:
|
||||
// - zero returns immediately
|
||||
// - any negative value blocks any amount of time
|
||||
//
|
||||
// # Errors
|
||||
//
|
||||
// A zero Errno is success. The below are expected otherwise:
|
||||
// - ENOSYS: the implementation does not support this function.
|
||||
// A zero sys.Errno is success. The below are expected otherwise:
|
||||
// - sys.ENOSYS: the implementation does not support this function.
|
||||
// - sys.EINTR: the call was interrupted prior to data being readable.
|
||||
//
|
||||
// # Notes
|
||||
//
|
||||
// - This is like `poll` in POSIX, for a single file.
|
||||
// See https://pubs.opengroup.org/onlinepubs/9699919799/functions/poll.html
|
||||
// - No-op files, such as those which read from /dev/null, should return
|
||||
// immediately true to avoid hangs (because data will never become
|
||||
// available).
|
||||
PollRead(timeout *time.Duration) (ready bool, errno experimentalsys.Errno)
|
||||
// immediately true, as data will never become readable.
|
||||
PollRead(timeoutMillis int32) (ready bool, errno experimentalsys.Errno)
|
||||
|
||||
// Readdir reads the contents of the directory associated with file and
|
||||
// returns a slice of up to n Dirent values in an arbitrary order. This is
|
||||
@@ -235,10 +237,10 @@ type File interface {
|
||||
//
|
||||
// # Errors
|
||||
//
|
||||
// A zero Errno is success. The below are expected otherwise:
|
||||
// - ENOSYS: the implementation does not support this function.
|
||||
// - EBADF: the file was closed or not a directory.
|
||||
// - ENOENT: the directory could not be read (e.g. deleted).
|
||||
// A zero sys.Errno is success. The below are expected otherwise:
|
||||
// - sys.ENOSYS: the implementation does not support this function.
|
||||
// - sys.EBADF: the file was closed or not a directory.
|
||||
// - sys.ENOENT: the directory could not be read (e.g. deleted).
|
||||
//
|
||||
// # Notes
|
||||
//
|
||||
@@ -255,9 +257,9 @@ type File interface {
|
||||
//
|
||||
// # Errors
|
||||
//
|
||||
// A zero Errno is success. The below are expected otherwise:
|
||||
// - ENOSYS: the implementation does not support this function.
|
||||
// - EBADF: the file was closed, not writeable, or a directory.
|
||||
// A zero sys.Errno is success. The below are expected otherwise:
|
||||
// - sys.ENOSYS: the implementation does not support this function.
|
||||
// - sys.EBADF: the file was closed, not writeable, or a directory.
|
||||
//
|
||||
// # Notes
|
||||
//
|
||||
@@ -270,11 +272,11 @@ type File interface {
|
||||
//
|
||||
// # Errors
|
||||
//
|
||||
// A zero Errno is success. The below are expected otherwise:
|
||||
// - ENOSYS: the implementation does not support this function.
|
||||
// - EBADF: the file or directory was closed or not writeable.
|
||||
// - EINVAL: the offset was negative.
|
||||
// - EISDIR: the file was a directory.
|
||||
// A zero sys.Errno is success. The below are expected otherwise:
|
||||
// - sys.ENOSYS: the implementation does not support this function.
|
||||
// - sys.EBADF: the file or directory was closed or not writeable.
|
||||
// - sys.EINVAL: the offset was negative.
|
||||
// - sys.EISDIR: the file was a directory.
|
||||
//
|
||||
// # Notes
|
||||
//
|
||||
@@ -286,11 +288,11 @@ type File interface {
|
||||
//
|
||||
// # Errors
|
||||
//
|
||||
// A zero Errno is success. The below are expected otherwise:
|
||||
// - ENOSYS: the implementation does not support this function.
|
||||
// - EBADF: the file or directory was closed.
|
||||
// - EINVAL: the `size` is negative.
|
||||
// - EISDIR: the file was a directory.
|
||||
// A zero sys.Errno is success. The below are expected otherwise:
|
||||
// - sys.ENOSYS: the implementation does not support this function.
|
||||
// - sys.EBADF: the file or directory was closed.
|
||||
// - sys.EINVAL: the `size` is negative.
|
||||
// - sys.EISDIR: the file was a directory.
|
||||
//
|
||||
// # Notes
|
||||
//
|
||||
@@ -303,8 +305,8 @@ type File interface {
|
||||
//
|
||||
// # Errors
|
||||
//
|
||||
// A zero Errno is success. The below are expected otherwise:
|
||||
// - EBADF: the file or directory was closed.
|
||||
// A zero sys.Errno is success. The below are expected otherwise:
|
||||
// - sys.EBADF: the file or directory was closed.
|
||||
//
|
||||
// # Notes
|
||||
//
|
||||
@@ -319,8 +321,8 @@ type File interface {
|
||||
//
|
||||
// # Errors
|
||||
//
|
||||
// A zero Errno is success. The below are expected otherwise:
|
||||
// - EBADF: the file or directory was closed.
|
||||
// A zero sys.Errno is success. The below are expected otherwise:
|
||||
// - sys.EBADF: the file or directory was closed.
|
||||
//
|
||||
// # Notes
|
||||
//
|
||||
@@ -343,9 +345,9 @@ type File interface {
|
||||
//
|
||||
// # Errors
|
||||
//
|
||||
// A zero Errno is success. The below are expected otherwise:
|
||||
// - ENOSYS: the implementation does not support this function.
|
||||
// - EBADF: the file or directory was closed.
|
||||
// A zero sys.Errno is success. The below are expected otherwise:
|
||||
// - sys.ENOSYS: the implementation does not support this function.
|
||||
// - sys.EBADF: the file or directory was closed.
|
||||
//
|
||||
// # Notes
|
||||
//
|
||||
@@ -357,7 +359,7 @@ type File interface {
|
||||
|
||||
// Close closes the underlying file.
|
||||
//
|
||||
// A zero Errno is returned if unimplemented or success.
|
||||
// A zero sys.Errno is returned if unimplemented or success.
|
||||
//
|
||||
// # Notes
|
||||
//
|
||||
|
||||
@@ -3,7 +3,6 @@ package fsapi
|
||||
import (
|
||||
"io/fs"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
|
||||
"github.com/tetratelabs/wazero/sys"
|
||||
@@ -155,7 +154,7 @@ func (UnimplementedFile) Readdir(int) (dirents []Dirent, errno experimentalsys.E
|
||||
}
|
||||
|
||||
// PollRead implements File.PollRead
|
||||
func (UnimplementedFile) PollRead(*time.Duration) (ready bool, errno experimentalsys.Errno) {
|
||||
func (UnimplementedFile) PollRead(int32) (ready bool, errno experimentalsys.Errno) {
|
||||
return false, experimentalsys.ENOSYS
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ package sys
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
|
||||
"github.com/tetratelabs/wazero/internal/fsapi"
|
||||
@@ -50,7 +49,7 @@ func (noopStdinFile) Read([]byte) (int, experimentalsys.Errno) {
|
||||
}
|
||||
|
||||
// PollRead implements the same method as documented on fsapi.File
|
||||
func (noopStdinFile) PollRead(*time.Duration) (ready bool, errno experimentalsys.Errno) {
|
||||
func (noopStdinFile) PollRead(int32) (ready bool, errno experimentalsys.Errno) {
|
||||
return true, 0 // always ready to read nothing
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"runtime"
|
||||
"testing"
|
||||
gofstest "testing/fstest"
|
||||
"time"
|
||||
|
||||
experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
|
||||
"github.com/tetratelabs/wazero/internal/fsapi"
|
||||
@@ -330,10 +329,10 @@ func TestFilePollRead(t *testing.T) {
|
||||
rF, err := NewStdioFile(true, r)
|
||||
require.NoError(t, err)
|
||||
buf := make([]byte, 10)
|
||||
timeout := time.Duration(0) // return immediately
|
||||
timeout := int32(0) // return immediately
|
||||
|
||||
// When there's nothing in the pipe, it isn't ready.
|
||||
ready, errno := rF.PollRead(&timeout)
|
||||
ready, errno := rF.PollRead(timeout)
|
||||
require.EqualErrno(t, 0, errno)
|
||||
require.False(t, ready)
|
||||
|
||||
@@ -343,7 +342,7 @@ func TestFilePollRead(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
// We should now be able to poll ready
|
||||
ready, errno = rF.PollRead(&timeout)
|
||||
ready, errno = rF.PollRead(timeout)
|
||||
require.EqualErrno(t, 0, errno)
|
||||
require.True(t, ready)
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"os"
|
||||
"runtime"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
|
||||
"github.com/tetratelabs/wazero/internal/fsapi"
|
||||
@@ -183,17 +182,22 @@ func (f *osFile) Seek(offset int64, whence int) (newOffset int64, errno experime
|
||||
}
|
||||
|
||||
// PollRead implements the same method as documented on fsapi.File
|
||||
func (f *osFile) PollRead(timeout *time.Duration) (ready bool, errno experimentalsys.Errno) {
|
||||
func (f *osFile) PollRead(timeoutMillis int32) (ready bool, errno experimentalsys.Errno) {
|
||||
fdSet := platform.FdSet{}
|
||||
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)
|
||||
|
||||
// Coerce negative timeout to -1 as that's defined in POSIX
|
||||
if timeoutMillis < 0 {
|
||||
timeoutMillis = -1
|
||||
}
|
||||
ready, err := _select(nfds, &fdSet, nil, nil, timeoutMillis)
|
||||
if errno = experimentalsys.UnwrapOSError(err); errno != 0 {
|
||||
// Defer validation overhead until we've already had an error.
|
||||
errno = fileError(f, f.closed, errno)
|
||||
}
|
||||
return count > 0, errno
|
||||
return ready, errno
|
||||
}
|
||||
|
||||
// Readdir implements File.Readdir. Notably, this uses "Readdir", not
|
||||
|
||||
@@ -1,27 +1,25 @@
|
||||
package sysfs
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/tetratelabs/wazero/experimental/sys"
|
||||
"github.com/tetratelabs/wazero/internal/platform"
|
||||
)
|
||||
|
||||
// _select exposes the select(2) syscall. This is named as such to avoid
|
||||
// colliding with they keyword select while not exporting the function.
|
||||
// _select waits until one or more of the file descriptors become ready for
|
||||
// reading or writing.
|
||||
//
|
||||
// # Notes on Parameters
|
||||
// # Parameters
|
||||
//
|
||||
// For convenience, we expose a pointer to a time.Duration instead of a pointer to a syscall.Timeval.
|
||||
// It must be a pointer because `nil` means "wait forever".
|
||||
// The `timeoutMillis` parameter is how long to block for an event, or
|
||||
// interrupted, in milliseconds. There are two special values:
|
||||
// - zero returns immediately
|
||||
// - any negative value blocks any amount of time
|
||||
//
|
||||
// However, notice that select(2) may mutate the pointed Timeval on some platforms,
|
||||
// for instance if the call returns early.
|
||||
// A zero sys.Errno is success. The below are expected otherwise:
|
||||
// - sys.ENOSYS: the implementation does not support this function.
|
||||
// - sys.EINTR: the call was interrupted prior to an event.
|
||||
//
|
||||
// This implementation *will not* update the pointed time.Duration value accordingly.
|
||||
//
|
||||
// See also: https://github.com/golang/sys/blob/master/unix/syscall_unix_test.go#L606-L617
|
||||
//
|
||||
// # Notes on the Syscall
|
||||
// # Impact of blocking
|
||||
//
|
||||
// Because this is a blocking syscall, it will also block the carrier thread of the goroutine,
|
||||
// preventing any means to support context cancellation directly.
|
||||
@@ -31,6 +29,14 @@ import (
|
||||
// e.g. the read-end of a pipe or an eventfd on Linux.
|
||||
// When the context is canceled, we may unblock a Select call by writing to the fd, causing it to return immediately.
|
||||
// This however requires to do a bit of housekeeping to hide the "special" FD from the end-user.
|
||||
func _select(n int, r, w, e *platform.FdSet, timeout *time.Duration) (int, error) {
|
||||
return syscall_select(n, r, w, e, timeout)
|
||||
//
|
||||
// # Notes
|
||||
//
|
||||
// - This is like `select` in POSIX except it returns if any are ready
|
||||
// instead of a specific file descriptor. See
|
||||
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/select.html
|
||||
// - This is named _select to avoid collision on the select keyword, while
|
||||
// not exporting the function.
|
||||
func _select(n int, r, w, e *platform.FdSet, timeoutNanos int32) (ready bool, errno sys.Errno) {
|
||||
return syscall_select(n, r, w, e, timeoutNanos)
|
||||
}
|
||||
|
||||
@@ -2,22 +2,23 @@ package sysfs
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/tetratelabs/wazero/experimental/sys"
|
||||
"github.com/tetratelabs/wazero/internal/platform"
|
||||
)
|
||||
|
||||
// syscall_select invokes select on Darwin, with the given timeout Duration.
|
||||
// We implement our own version instead of relying on syscall.Select because the latter
|
||||
// only returns the error and discards the result.
|
||||
func syscall_select(n int, r, w, e *platform.FdSet, timeout *time.Duration) (int, error) {
|
||||
// syscall_select implements _select on Darwin
|
||||
//
|
||||
// Note: We implement our own version instead of relying on syscall.Select
|
||||
// because the latter only returns the error and discards the result.
|
||||
func syscall_select(n int, r, w, e *platform.FdSet, timeoutNanos int32) (ready bool, errno sys.Errno) {
|
||||
var t *syscall.Timeval
|
||||
if timeout != nil {
|
||||
tv := syscall.NsecToTimeval(timeout.Nanoseconds())
|
||||
if timeoutNanos >= 0 {
|
||||
tv := syscall.NsecToTimeval(int64(timeoutNanos))
|
||||
t = &tv
|
||||
}
|
||||
result, _, errno := syscall_syscall6(
|
||||
r1, _, err := syscall_syscall6(
|
||||
libc_select_trampoline_addr,
|
||||
uintptr(n),
|
||||
uintptr(unsafe.Pointer(r)),
|
||||
@@ -25,11 +26,7 @@ func syscall_select(n int, r, w, e *platform.FdSet, timeout *time.Duration) (int
|
||||
uintptr(unsafe.Pointer(e)),
|
||||
uintptr(unsafe.Pointer(t)),
|
||||
0)
|
||||
res := int(result)
|
||||
if errno == 0 {
|
||||
return res, nil
|
||||
}
|
||||
return res, errno
|
||||
return r1 > 0, sys.UnwrapOSError(err)
|
||||
}
|
||||
|
||||
// libc_select_trampoline_addr is the address of the
|
||||
|
||||
@@ -2,17 +2,18 @@ package sysfs
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/tetratelabs/wazero/experimental/sys"
|
||||
"github.com/tetratelabs/wazero/internal/platform"
|
||||
)
|
||||
|
||||
// syscall_select invokes select on Unix (unless Darwin), with the given timeout Duration.
|
||||
func syscall_select(n int, r, w, e *platform.FdSet, timeout *time.Duration) (int, error) {
|
||||
// syscall_select implements _select on Linux
|
||||
func syscall_select(n int, r, w, e *platform.FdSet, timeoutNanos int32) (bool, sys.Errno) {
|
||||
var t *syscall.Timeval
|
||||
if timeout != nil {
|
||||
tv := syscall.NsecToTimeval(timeout.Nanoseconds())
|
||||
if timeoutNanos >= 0 {
|
||||
tv := syscall.NsecToTimeval(int64(timeoutNanos))
|
||||
t = &tv
|
||||
}
|
||||
return syscall.Select(n, (*syscall.FdSet)(r), (*syscall.FdSet)(w), (*syscall.FdSet)(e), t)
|
||||
n, err := syscall.Select(n, (*syscall.FdSet)(r), (*syscall.FdSet)(w), (*syscall.FdSet)(e), t)
|
||||
return n > 0, sys.UnwrapOSError(err)
|
||||
}
|
||||
|
||||
@@ -12,50 +12,50 @@ import (
|
||||
)
|
||||
|
||||
func TestSelect(t *testing.T) {
|
||||
t.Run("should return immediately with no fds and duration 0", func(t *testing.T) {
|
||||
t.Run("should return immediately with no fds and timeoutNanos 0", func(t *testing.T) {
|
||||
for {
|
||||
dur := time.Duration(0)
|
||||
n, err := _select(0, nil, nil, nil, &dur)
|
||||
if err == sys.EINTR {
|
||||
timeoutNanos := int32(0)
|
||||
ready, errno := _select(0, nil, nil, nil, timeoutNanos)
|
||||
if errno == sys.EINTR {
|
||||
t.Logf("Select interrupted")
|
||||
continue
|
||||
}
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 0, n)
|
||||
require.EqualErrno(t, 0, errno)
|
||||
require.False(t, ready)
|
||||
break
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("should wait for the given duration", func(t *testing.T) {
|
||||
dur := 250 * time.Millisecond
|
||||
timeoutNanos := int32(250 * time.Millisecond)
|
||||
var took time.Duration
|
||||
for {
|
||||
// On some platforms (e.g. Linux), the passed-in timeval is
|
||||
// updated by select(2). We are not accounting for this
|
||||
// in our implementation.
|
||||
start := time.Now()
|
||||
n, err := _select(0, nil, nil, nil, &dur)
|
||||
ready, errno := _select(0, nil, nil, nil, timeoutNanos)
|
||||
took = time.Since(start)
|
||||
if err == sys.EINTR {
|
||||
if errno == sys.EINTR {
|
||||
t.Logf("Select interrupted after %v", took)
|
||||
continue
|
||||
}
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 0, n)
|
||||
require.EqualErrno(t, 0, errno)
|
||||
require.False(t, ready)
|
||||
break
|
||||
}
|
||||
|
||||
// On some platforms the actual timeout might be arbitrarily
|
||||
// less than requested.
|
||||
if took < dur {
|
||||
if tookNanos := int32(took.Nanoseconds()); tookNanos < timeoutNanos {
|
||||
if runtime.GOOS == "linux" {
|
||||
// Linux promises to only return early if a file descriptor
|
||||
// becomes ready (not applicable here), or the call
|
||||
// is interrupted by a signal handler (explicitly retried in the loop above),
|
||||
// or the timeout expires.
|
||||
t.Errorf("Select: slept for %v, expected %v", took, dur)
|
||||
t.Errorf("Select: slept for %v, expected %v", tookNanos, timeoutNanos)
|
||||
} else {
|
||||
t.Logf("Select: slept for %v, requested %v", took, dur)
|
||||
t.Logf("Select: slept for %v, requested %v", tookNanos, timeoutNanos)
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -74,13 +74,13 @@ func TestSelect(t *testing.T) {
|
||||
rFdSet.Set(fd)
|
||||
|
||||
for {
|
||||
n, err := _select(fd+1, rFdSet, nil, nil, nil)
|
||||
if err == sys.EINTR {
|
||||
ready, errno := _select(fd+1, rFdSet, nil, nil, -1)
|
||||
if errno == sys.EINTR {
|
||||
t.Log("Select interrupted")
|
||||
continue
|
||||
}
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, n)
|
||||
require.EqualErrno(t, 0, errno)
|
||||
require.True(t, ready)
|
||||
break
|
||||
}
|
||||
})
|
||||
|
||||
@@ -3,12 +3,10 @@
|
||||
package sysfs
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/tetratelabs/wazero/experimental/sys"
|
||||
"github.com/tetratelabs/wazero/internal/platform"
|
||||
)
|
||||
|
||||
func syscall_select(n int, r, w, e *platform.FdSet, timeout *time.Duration) (int, error) {
|
||||
return -1, sys.ENOSYS
|
||||
func syscall_select(n int, r, w, e *platform.FdSet, timeoutNanos int32) (ready bool, errno sys.Errno) {
|
||||
return false, sys.ENOSYS
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ const pollInterval = 100 * time.Millisecond
|
||||
// zeroDuration is the zero value for time.Duration. It is used in selectAllHandles.
|
||||
var zeroDuration = time.Duration(0)
|
||||
|
||||
// syscall_select emulates the select syscall on Windows, for a subset of cases.
|
||||
// syscall_select implements _select on Windows, for a subset of cases.
|
||||
//
|
||||
// r, w, e may contain any number of file handles, but regular files and pipes are only processed for r (Read).
|
||||
// Stdin is a pipe, thus it is checked for readiness when present. Pipes are checked using PeekNamedPipe.
|
||||
@@ -27,21 +27,26 @@ var zeroDuration = time.Duration(0)
|
||||
//
|
||||
// Note: ideas taken from https://stackoverflow.com/questions/6839508/test-if-stdin-has-input-for-c-windows-and-or-linux
|
||||
// PeekNamedPipe: https://learn.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-peeknamedpipe
|
||||
func syscall_select(n int, r, w, e *platform.FdSet, timeout *time.Duration) (int, error) {
|
||||
func syscall_select(n int, r, w, e *platform.FdSet, timeoutNanos int32) (ready bool, errno sys.Errno) {
|
||||
var timeout *time.Duration
|
||||
if timeoutNanos >= 0 {
|
||||
duration := time.Duration(timeoutNanos)
|
||||
timeout = &duration
|
||||
}
|
||||
|
||||
// TODO: This impl was left alone because it will change soon.
|
||||
// See https://github.com/tetratelabs/wazero/pull/1596
|
||||
if n == 0 {
|
||||
// Don't block indefinitely.
|
||||
if timeout == nil {
|
||||
return -1, sys.ENOSYS
|
||||
return false, sys.ENOSYS
|
||||
}
|
||||
time.Sleep(*timeout)
|
||||
return 0, nil
|
||||
return false, 0
|
||||
}
|
||||
|
||||
n, errno := selectAllHandles(context.TODO(), r, w, e, timeout)
|
||||
if errno == 0 {
|
||||
return n, nil
|
||||
}
|
||||
return n, errno
|
||||
n, errno = selectAllHandles(context.TODO(), r, w, e, timeout)
|
||||
return n > 0, errno
|
||||
}
|
||||
|
||||
// selectAllHandles emulates a general-purpose POSIX select on Windows.
|
||||
|
||||
@@ -38,18 +38,18 @@ func TestSelect_Windows(t *testing.T) {
|
||||
close(ch)
|
||||
}
|
||||
|
||||
t.Run("syscall_select returns sys.ENOSYS when n == 0 and duration is nil", func(t *testing.T) {
|
||||
n, errno := syscall_select(0, nil, nil, nil, nil)
|
||||
require.Equal(t, -1, n)
|
||||
t.Run("syscall_select returns sys.ENOSYS when n == 0 and timeoutNanos is negative", func(t *testing.T) {
|
||||
ready, errno := syscall_select(0, nil, nil, nil, -1)
|
||||
require.EqualErrno(t, sys.ENOSYS, errno)
|
||||
require.False(t, ready)
|
||||
})
|
||||
|
||||
t.Run("syscall_select propagates error when peekAllPipes returns an error", func(t *testing.T) {
|
||||
t.Run("syscall_select propagates error when peekAllPipes returns an negative", func(t *testing.T) {
|
||||
fdSet := platform.FdSet{}
|
||||
fdSet.Pipes().Set(-1)
|
||||
n, errno := syscall_select(0, &fdSet, nil, nil, nil)
|
||||
require.Equal(t, -1, n)
|
||||
ready, errno := syscall_select(0, &fdSet, nil, nil, -1)
|
||||
require.EqualErrno(t, sys.ENOSYS, errno)
|
||||
require.False(t, ready)
|
||||
})
|
||||
|
||||
t.Run("peekNamedPipe should report the correct state of incoming data in the pipe", func(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user