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:
Crypt Keeper
2023-07-28 10:01:00 +08:00
committed by GitHub
parent 6b4328c66e
commit 180ff682d9
15 changed files with 159 additions and 146 deletions

View File

@@ -174,7 +174,7 @@ func pollOneoffFn(_ context.Context, mod api.Module, params []uint64) sys.Errno
return sys.EBADF
}
// Wait for the timeout to expire, or for some data to become available on Stdin.
stdinReady, errno := stdin.File.PollRead(&timeout)
stdinReady, errno := stdin.File.PollRead(int32(timeout.Milliseconds()))
if errno != 0 {
return errno
}

View File

@@ -555,8 +555,12 @@ type neverReadyTtyStdinFile struct {
}
// PollRead implements the same method as documented on fsapi.File
func (neverReadyTtyStdinFile) PollRead(timeout *time.Duration) (ready bool, errno experimentalsys.Errno) {
time.Sleep(*timeout)
func (neverReadyTtyStdinFile) PollRead(timeoutMillis int32) (ready bool, errno experimentalsys.Errno) {
switch {
case timeoutMillis <= 0:
return
}
time.Sleep(time.Duration(timeoutMillis) * time.Millisecond)
return false, 0
}
@@ -567,6 +571,6 @@ type pollStdinFile struct {
}
// PollRead implements the same method as documented on fsapi.File
func (p *pollStdinFile) PollRead(*time.Duration) (ready bool, errno experimentalsys.Errno) {
func (p *pollStdinFile) PollRead(int32) (ready bool, errno experimentalsys.Errno) {
return p.ready, 0
}

View File

@@ -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
}

View File

@@ -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
//

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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)

View File

@@ -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

View File

@@ -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)
}

View File

@@ -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

View File

@@ -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)
}

View File

@@ -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
}
})

View File

@@ -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
}

View File

@@ -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.

View File

@@ -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) {