Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fsapi: migrates PollRead to Poll with Pflag #1599

Merged
merged 5 commits into from
Jul 30, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion imports/wasi_snapshot_preview1/poll.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

"github.com/tetratelabs/wazero/api"
"github.com/tetratelabs/wazero/experimental/sys"
"github.com/tetratelabs/wazero/internal/fsapi"
internalsys "github.com/tetratelabs/wazero/internal/sys"
"github.com/tetratelabs/wazero/internal/wasip1"
"github.com/tetratelabs/wazero/internal/wasm"
Expand Down Expand Up @@ -174,7 +175,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(int32(timeout.Milliseconds()))
stdinReady, errno := stdin.File.Poll(fsapi.POLLIN, int32(timeout.Milliseconds()))
if errno != 0 {
return errno
}
Expand Down
14 changes: 10 additions & 4 deletions imports/wasi_snapshot_preview1/poll_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -554,8 +554,11 @@ type neverReadyTtyStdinFile struct {
ttyStat
}

// PollRead implements the same method as documented on fsapi.File
func (neverReadyTtyStdinFile) PollRead(timeoutMillis int32) (ready bool, errno experimentalsys.Errno) {
// Poll implements the same method as documented on fsapi.File
func (neverReadyTtyStdinFile) Poll(flag fsapi.Pflag, timeoutMillis int32) (ready bool, errno experimentalsys.Errno) {
if flag != fsapi.POLLIN {
return false, experimentalsys.ENOTSUP
}
switch {
case timeoutMillis <= 0:
return
Expand All @@ -570,7 +573,10 @@ type pollStdinFile struct {
ready bool
}

// PollRead implements the same method as documented on fsapi.File
func (p *pollStdinFile) PollRead(int32) (ready bool, errno experimentalsys.Errno) {
// Poll implements the same method as documented on fsapi.File
func (p *pollStdinFile) Poll(flag fsapi.Pflag, timeoutMillis int32) (ready bool, errno experimentalsys.Errno) {
if flag != fsapi.POLLIN {
return false, experimentalsys.ENOTSUP
}
return p.ready, 0
}
4 changes: 2 additions & 2 deletions internal/fsapi/dir.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,8 @@ func (DirFile) Pread([]byte, int64) (int, experimentalsys.Errno) {
return 0, experimentalsys.EISDIR
}

// PollRead implements File.PollRead
func (DirFile) PollRead(int32) (ready bool, errno experimentalsys.Errno) {
// Poll implements File.Poll
func (DirFile) Poll(Pflag, int32) (ready bool, errno experimentalsys.Errno) {
return false, experimentalsys.ENOSYS
}

Expand Down
18 changes: 11 additions & 7 deletions internal/fsapi/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,32 +205,36 @@ type File interface {
// of io.Seeker. See https://pubs.opengroup.org/onlinepubs/9699919799/functions/fseek.html
Seek(offset int64, whence int) (newOffset int64, errno experimentalsys.Errno)

// PollRead returns if the file has data ready to be read or an error.
// Poll returns if the file has data ready to be read or written.
//
// # Parameters
//
// The `timeoutMillis` parameter is how long to block for data to become
// readable, or interrupted, in milliseconds. There are two special values:
// The `flag` parameter determines which event to await, such as POLLIN,
// POLLOUT, or a combination like `POLLIN|POLLOUT`.
//
// 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
//
// # Results
//
// `ready` means there was data ready to read or false if not or when
// `errno` is not zero.
// `ready` means there was data ready to read or written. False can mean no
// event was ready or `errno` is not zero.
//
// A zero `errno` is success. The below are expected otherwise:
// - sys.ENOSYS: the implementation does not support this function.
// - sys.ENOTSUP: the implementation does not the flag combination.
// - sys.EINTR: the call was interrupted prior to an event.
//
// # 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, as data will never become readable.
// immediately true, as data will never become available.
// - See /RATIONALE.md for detailed notes including impact of blocking.
PollRead(timeoutMillis int32) (ready bool, errno experimentalsys.Errno)
Poll(flag Pflag, 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
Expand Down
20 changes: 20 additions & 0 deletions internal/fsapi/poll.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package fsapi

// Pflag are bit flags used for File.Poll. Values, including zero, should not
// be interpreted numerically. Instead, use by constants prefixed with 'POLL'.
//
// # Notes
//
// - This is like `pollfd.events` flags for `poll` in POSIX. See
// https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/poll.h.html
type Pflag uint32

// Only define bitflags we support and are needed by `poll_oneoff` in wasip1
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#eventrwflags
const (
// POLLIN is a read event.
POLLIN Pflag = 1 << iota

// POLLOUT is a write event.
POLLOUT
)
4 changes: 2 additions & 2 deletions internal/fsapi/unimplemented.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,8 +153,8 @@ func (UnimplementedFile) Readdir(int) (dirents []Dirent, errno experimentalsys.E
return nil, experimentalsys.ENOSYS
}

// PollRead implements File.PollRead
func (UnimplementedFile) PollRead(int32) (ready bool, errno experimentalsys.Errno) {
// Poll implements File.Poll
func (UnimplementedFile) Poll(Pflag, int32) (ready bool, errno experimentalsys.Errno) {
return false, experimentalsys.ENOSYS
}

Expand Down
7 changes: 5 additions & 2 deletions internal/sys/stdio.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,11 @@ func (noopStdinFile) Read([]byte) (int, experimentalsys.Errno) {
return 0, 0 // Always EOF
}

// PollRead implements the same method as documented on fsapi.File
func (noopStdinFile) PollRead(int32) (ready bool, errno experimentalsys.Errno) {
// Poll implements the same method as documented on fsapi.File
func (noopStdinFile) Poll(flag fsapi.Pflag, timeoutMillis int32) (ready bool, errno experimentalsys.Errno) {
if flag != fsapi.POLLIN {
return false, experimentalsys.ENOTSUP
}
return true, 0 // always ready to read nothing
}

Expand Down
27 changes: 24 additions & 3 deletions internal/sysfs/file_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,9 @@ func TestFileReadAndPread(t *testing.T) {
}
}

func TestFilePollRead(t *testing.T) {
func TestFilePoll_POLLIN(t *testing.T) {
pflag := fsapi.POLLIN

// Test using os.Pipe as it is known to support poll.
r, w, err := os.Pipe()
require.NoError(t, err)
Expand All @@ -332,7 +334,7 @@ func TestFilePollRead(t *testing.T) {
timeout := int32(0) // return immediately

// When there's nothing in the pipe, it isn't ready.
ready, errno := rF.PollRead(timeout)
ready, errno := rF.Poll(pflag, timeout)
require.EqualErrno(t, 0, errno)
require.False(t, ready)

Expand All @@ -342,7 +344,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.Poll(pflag, timeout)
require.EqualErrno(t, 0, errno)
require.True(t, ready)

Expand All @@ -353,6 +355,25 @@ func TestFilePollRead(t *testing.T) {
require.Equal(t, expected, buf[:len(expected)])
}

func TestFilePoll_POLLOUT(t *testing.T) {
pflag := fsapi.POLLOUT

// Test using os.Pipe as it is known to support poll.
r, w, err := os.Pipe()
require.NoError(t, err)
defer r.Close()
defer w.Close()

wF, err := NewStdioFile(false, w)
require.NoError(t, err)
timeout := int32(0) // return immediately

// We don't yet implement write blocking.
ready, errno := wF.Poll(pflag, timeout)
require.EqualErrno(t, experimentalsys.ENOTSUP, errno)
require.False(t, ready)
}

func requireRead(t *testing.T, f fsapi.File, buf []byte) {
n, errno := f.Read(buf)
require.EqualErrno(t, 0, errno)
Expand Down
6 changes: 3 additions & 3 deletions internal/sysfs/osfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,9 +180,9 @@ func (f *osFile) Seek(offset int64, whence int) (newOffset int64, errno experime
return
}

// PollRead implements the same method as documented on fsapi.File
func (f *osFile) PollRead(timeoutMillis int32) (ready bool, errno experimentalsys.Errno) {
return pollRead(f.fd, timeoutMillis)
// Poll implements the same method as documented on fsapi.File
func (f *osFile) Poll(flag fsapi.Pflag, timeoutMillis int32) (ready bool, errno experimentalsys.Errno) {
return poll(f.fd, flag, timeoutMillis)
}

// Readdir implements File.Readdir. Notably, this uses "Readdir", not
Expand Down
15 changes: 10 additions & 5 deletions internal/sysfs/poll.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,17 @@

package sysfs

import "github.com/tetratelabs/wazero/experimental/sys"
import (
"github.com/tetratelabs/wazero/experimental/sys"
"github.com/tetratelabs/wazero/internal/fsapi"
)

// pollRead implements `PollRead` as documented on fsapi.File via a file
// descriptor.
func pollRead(fd uintptr, timeoutMillis int32) (ready bool, errno sys.Errno) {
// poll implements `Poll` as documented on fsapi.File via a file descriptor.
func poll(fd uintptr, flag fsapi.Pflag, timeoutMillis int32) (ready bool, errno sys.Errno) {
if flag != fsapi.POLLIN {
return false, sys.ENOTSUP
}
fds := []pollFd{newPollFd(fd, _POLLIN, 0)}
count, errno := poll(fds, timeoutMillis)
count, errno := _poll(fds, timeoutMillis)
return count > 0, errno
}
4 changes: 2 additions & 2 deletions internal/sysfs/poll_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ func newPollFd(fd uintptr, events, revents int16) pollFd {
// _POLLIN subscribes a notification when any readable data is available.
const _POLLIN = 0x0001

// poll implements poll on Darwin via the corresponding libc function.
func poll(fds []pollFd, timeoutMillis int32) (n int, errno sys.Errno) {
// _poll implements poll on Darwin via the corresponding libc function.
func _poll(fds []pollFd, timeoutMillis int32) (n int, errno sys.Errno) {
var fdptr *pollFd
nfds := len(fds)
if nfds > 0 {
Expand Down
4 changes: 2 additions & 2 deletions internal/sysfs/poll_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ func newPollFd(fd uintptr, events, revents int16) pollFd {
// _POLLIN subscribes a notification when any readable data is available.
const _POLLIN = 0x0001

// poll implements poll on Linux via ppoll.
func poll(fds []pollFd, timeoutMillis int32) (n int, errno sys.Errno) {
// _poll implements poll on Linux via ppoll.
func _poll(fds []pollFd, timeoutMillis int32) (n int, errno sys.Errno) {
var ts syscall.Timespec
if timeoutMillis >= 0 {
ts = syscall.NsecToTimespec(int64(time.Duration(timeoutMillis) * time.Millisecond))
Expand Down
10 changes: 6 additions & 4 deletions internal/sysfs/poll_test.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//go:build windows || linux || darwin
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this was missing. we really need to finish #1578 because it is very easy to break it more, with each change to things that can use syscalls.


package sysfs

import (
Expand All @@ -9,9 +11,9 @@ import (
"github.com/tetratelabs/wazero/internal/testing/require"
)

func TestPoll(t *testing.T) {
func Test_poll(t *testing.T) {
t.Run("should return immediately with no fds and duration 0", func(t *testing.T) {
n, err := poll([]pollFd{}, 0)
n, err := _poll([]pollFd{}, 0)
require.EqualErrno(t, 0, err)
require.Equal(t, 0, n)
})
Expand All @@ -23,7 +25,7 @@ func TestPoll(t *testing.T) {
// updated by select(2). We are not accounting for this
// in our implementation.
start := time.Now()
n, err := poll([]pollFd{}, dur)
n, err := _poll([]pollFd{}, dur)
took = time.Since(start)
require.EqualErrno(t, 0, err)
require.Equal(t, 0, n)
Expand Down Expand Up @@ -53,7 +55,7 @@ func TestPoll(t *testing.T) {
require.NoError(t, err)

fds := []pollFd{newPollFd(rr.Fd(), _POLLIN, 0)}
n, err := poll(fds, 0)
n, err := _poll(fds, 0)
require.EqualErrno(t, 0, err)
require.Equal(t, 1, n)
})
Expand Down
10 changes: 6 additions & 4 deletions internal/sysfs/poll_unsupported.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@

package sysfs

import "github.com/tetratelabs/wazero/experimental/sys"
import (
"github.com/tetratelabs/wazero/experimental/sys"
"github.com/tetratelabs/wazero/internal/fsapi"
)

// pollRead implements `PollRead` as documented on fsapi.File via a file
// descriptor.
func pollRead(fd uintptr, timeoutMillis int32) (ready bool, errno sys.Errno) {
// poll implements `Poll` as documented on fsapi.File via a file descriptor.
func poll(fd uintptr, flag fsapi.Pflag, timeoutMillis int32) (ready bool, errno sys.Errno) {
return false, sys.ENOSYS
}
4 changes: 2 additions & 2 deletions internal/sysfs/poll_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ func newPollFd(fd uintptr, events, revents int16) pollFd {
// pollInterval is the interval between each calls to peekNamedPipe in selectAllHandles
const pollInterval = 100 * time.Millisecond

// poll implements poll on Windows, for a subset of cases.
// _poll implements poll on Windows, for a subset of cases.
//
// pollWithContext emulates the behavior of POSIX poll(2) on Windows, for a subset of cases,
// and it supports context cancellation.
Expand All @@ -58,7 +58,7 @@ const pollInterval = 100 * time.Millisecond
//
// The duration may be negative, in which case it will wait indefinitely. The given ctx is
// used to allow for cancellation, and it is currently used only in tests.
func poll(fds []pollFd, timeoutMillis int32) (n int, errno sys.Errno) {
func _poll(fds []pollFd, timeoutMillis int32) (n int, errno sys.Errno) {
if fds == nil {
return -1, sys.ENOSYS
}
Expand Down
2 changes: 1 addition & 1 deletion internal/sysfs/sock_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ type winTcpListenerFile struct {
func (f *winTcpListenerFile) Accept() (socketapi.TCPConn, sys.Errno) {
// Ensure we have an incoming connection using winsock_select.
n, errno := syscallConnControl(f.tl, func(fd uintptr) (int, sys.Errno) {
return poll([]pollFd{newPollFd(fd, _POLLIN, 0)}, 0)
return _poll([]pollFd{newPollFd(fd, _POLLIN, 0)}, 0)
})

// Otherwise return immediately.
Expand Down