Repository: tetratelabs/wazero (go)

Issue:
Flakey test: Test_poll/should_wait_for_the_given_duration
We should probably look out for interrupt and make sure the godoc declares it.

```
--- FAIL: Test_poll (0.00s)
    --- FAIL: Test_poll/should_wait_for_the_given_duration (0.00s)
        require.go:331: expected Errno 0x0(success), but was 0x6(interrupted function)
            /home/runner/work/wazero/wazero/internal/sysfs/poll_test.go:30
FAIL
```

failed on scratch Go 1.21rc3

https://github.com/tetratelabs/wazero/actions/runs/5703851221/job/15456906175?pr=1599

Pull request:
sysfs: restore check for EINTR in poll test, fix Windows tests
EINTR used to be ignored when `select` was used, but the check was
deleted during the migration to `poll`: looks like this made some tests flaky.

Some time-based tests for Windows also seem flakier, so we try to make them slightly more robust.

Fixes #1601 

Signed-off-by: Edoardo Vacchi <evacchi@users.noreply.github.com>


Patch:
diff --git a/Makefile b/Makefile
index 5c7eae41..900e88cd 100644
--- a/Makefile
+++ b/Makefile
@@ -212,6 +212,8 @@ check:
 # This makes sure the intepreter can be used. Most often the package that can
 # drift here is "platform" or "sysfs":
 #
+# Ensure we build on plan9. See #1578
+	@GOARCH=amd64 GOOS=plan9 go build ./...
 # Ensure we build on windows:
 	@GOARCH=amd64 GOOS=windows go build ./...
 # Ensure we build on an arbitrary operating system:
diff --git a/experimental/sys/syscall_errno.go b/experimental/sys/syscall_errno.go
index aa6eb23d..23c4be92 100644
--- a/experimental/sys/syscall_errno.go
+++ b/experimental/sys/syscall_errno.go
@@ -1,51 +1,57 @@
+//go:build !plan9
+
 package sys
 
 import "syscall"
 
-func syscallToErrno(errno syscall.Errno) Errno {
+func syscallToErrno(err error) (Errno, bool) {
+	errno, ok := err.(syscall.Errno)
+	if !ok {
+		return 0, false
+	}
 	switch errno {
 	case 0:
-		return 0
+		return 0, true
 	case syscall.EACCES:
-		return EACCES
+		return EACCES, true
 	case syscall.EAGAIN:
-		return EAGAIN
+		return EAGAIN, true
 	case syscall.EBADF:
-		return EBADF
+		return EBADF, true
 	case syscall.EEXIST:
-		return EEXIST
+		return EEXIST, true
 	case syscall.EFAULT:
-		return EFAULT
+		return EFAULT, true
 	case syscall.EINTR:
-		return EINTR
+		return EINTR, true
 	case syscall.EINVAL:
-		return EINVAL
+		return EINVAL, true
 	case syscall.EIO:
-		return EIO
+		return EIO, true
 	case syscall.EISDIR:
-		return EISDIR
+		return EISDIR, true
 	case syscall.ELOOP:
-		return ELOOP
+		return ELOOP, true
 	case syscall.ENAMETOOLONG:
-		return ENAMETOOLONG
+		return ENAMETOOLONG, true
 	case syscall.ENOENT:
-		return ENOENT
+		return ENOENT, true
 	case syscall.ENOSYS:
-		return ENOSYS
+		return ENOSYS, true
 	case syscall.ENOTDIR:
-		return ENOTDIR
+		return ENOTDIR, true
 	case syscall.ENOTEMPTY:
-		return ENOTEMPTY
+		return ENOTEMPTY, true
 	case syscall.ENOTSOCK:
-		return ENOTSOCK
+		return ENOTSOCK, true
 	case syscall.ENOTSUP:
-		return ENOTSUP
+		return ENOTSUP, true
 	case syscall.EPERM:
-		return EPERM
+		return EPERM, true
 	case syscall.EROFS:
-		return EROFS
+		return EROFS, true
 	default:
-		return EIO
+		return EIO, true
 	}
 }
 
diff --git a/experimental/sys/syscall_errno_notwindows.go b/experimental/sys/syscall_errno_notwindows.go
index 56c8441d..8a88ed76 100644
--- a/experimental/sys/syscall_errno_notwindows.go
+++ b/experimental/sys/syscall_errno_notwindows.go
@@ -2,15 +2,12 @@
 
 package sys
 
-import "syscall"
-
 func errorToErrno(err error) Errno {
-	switch err := err.(type) {
-	case Errno:
-		return err
-	case syscall.Errno:
-		return syscallToErrno(err)
-	default:
-		return EIO
+	if errno, ok := err.(Errno); ok {
+		return errno
+	}
+	if errno, ok := syscallToErrno(err); ok {
+		return errno
 	}
+	return EIO
 }
diff --git a/experimental/sys/syscall_errno_plan9.go b/experimental/sys/syscall_errno_plan9.go
new file mode 100644
index 00000000..0e454f0a
--- /dev/null
+++ b/experimental/sys/syscall_errno_plan9.go
@@ -0,0 +1,5 @@
+package sys
+
+func syscallToErrno(err error) (Errno, bool) {
+	return 0, false
+}
diff --git a/experimental/sys/syscall_errno_windows.go b/experimental/sys/syscall_errno_windows.go
index 4c512133..761a1f9d 100644
--- a/experimental/sys/syscall_errno_windows.go
+++ b/experimental/sys/syscall_errno_windows.go
@@ -54,7 +54,8 @@ func errorToErrno(err error) Errno {
 		case _ERROR_NEGATIVE_SEEK, _ERROR_INVALID_NAME:
 			return EINVAL
 		}
-		return syscallToErrno(err)
+		errno, _ := syscallToErrno(err)
+		return errno
 	default:
 		return EIO
 	}
diff --git a/imports/wasi_snapshot_preview1/sock.go b/imports/wasi_snapshot_preview1/sock.go
index a3c20ad5..756c0d39 100644
--- a/imports/wasi_snapshot_preview1/sock.go
+++ b/imports/wasi_snapshot_preview1/sock.go
@@ -2,7 +2,6 @@ package wasi_snapshot_preview1
 
 import (
 	"context"
-	"syscall"
 
 	"github.com/tetratelabs/wazero/api"
 	"github.com/tetratelabs/wazero/experimental/sys"
@@ -175,11 +174,11 @@ func sockShutdownFn(_ context.Context, mod api.Module, params []uint64) sys.Errn
 
 	switch how {
 	case wasip1.SD_RD | wasip1.SD_WR:
-		sysHow = syscall.SHUT_RD | syscall.SHUT_WR
+		sysHow = socketapi.SHUT_RD | socketapi.SHUT_WR
 	case wasip1.SD_RD:
-		sysHow = syscall.SHUT_RD
+		sysHow = socketapi.SHUT_RD
 	case wasip1.SD_WR:
-		sysHow = syscall.SHUT_WR
+		sysHow = socketapi.SHUT_WR
 	default:
 		return sys.EINVAL
 	}
diff --git a/internal/sock/sock_plan9.go b/internal/sock/sock_plan9.go
new file mode 100644
index 00000000..b00a743f
--- /dev/null
+++ b/internal/sock/sock_plan9.go
@@ -0,0 +1,8 @@
+package sock
+
+// plan9 doesn't declare these constants
+const (
+	SHUT_RD = iota
+	SHUT_RDWR
+	SHUT_WR
+)
diff --git a/internal/sock/sock_supported.go b/internal/sock/sock_supported.go
new file mode 100644
index 00000000..a35376d8
--- /dev/null
+++ b/internal/sock/sock_supported.go
@@ -0,0 +1,11 @@
+//go:build !plan9
+
+package sock
+
+import "syscall"
+
+const (
+	SHUT_RD   = syscall.SHUT_RD
+	SHUT_RDWR = syscall.SHUT_RDWR
+	SHUT_WR   = syscall.SHUT_WR
+)
diff --git a/internal/sys/fs.go b/internal/sys/fs.go
index f85e2ff2..26d993b7 100644
--- a/internal/sys/fs.go
+++ b/internal/sys/fs.go
@@ -330,8 +330,8 @@ func (c *FSContext) Renumber(from, to int32) sys.Errno {
 	return 0
 }
 
-// SockAccept accepts a socketapi.TCPConn into the file table and returns
-// its file descriptor.
+// SockAccept accepts a sock.TCPConn into the file table and returns its file
+// descriptor.
 func (c *FSContext) SockAccept(sockFD int32, nonblock bool) (int32, sys.Errno) {
 	var sock socketapi.TCPSock
 	if e, ok := c.LookupFile(sockFD); !ok || !e.IsPreopen {
diff --git a/internal/sysfs/dirfs.go b/internal/sysfs/dirfs.go
index f681e97b..714610a4 100644
--- a/internal/sysfs/dirfs.go
+++ b/internal/sysfs/dirfs.go
@@ -3,7 +3,6 @@ package sysfs
 import (
 	"io/fs"
 	"os"
-	"syscall"
 
 	experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
 	"github.com/tetratelabs/wazero/internal/fsapi"
@@ -96,11 +95,6 @@ func (d *dirFS) Rmdir(path string) experimentalsys.Errno {
 	return rmdir(d.join(path))
 }
 
-func rmdir(path string) experimentalsys.Errno {
-	err := syscall.Rmdir(path)
-	return experimentalsys.UnwrapOSError(err)
-}
-
 // Unlink implements the same method as documented on fsapi.FS
 func (d *dirFS) Unlink(path string) (err experimentalsys.Errno) {
 	return unlink(d.join(path))
diff --git a/internal/sysfs/file_unix.go b/internal/sysfs/file_unix.go
index 4ed51a9f..f56439e0 100644
--- a/internal/sysfs/file_unix.go
+++ b/internal/sysfs/file_unix.go
@@ -13,6 +13,11 @@ const (
 	nonBlockingFileWriteSupported = true
 )
 
+func rmdir(path string) sys.Errno {
+	err := syscall.Rmdir(path)
+	return sys.UnwrapOSError(err)
+}
+
 // readFd exposes syscall.Read.
 func readFd(fd uintptr, buf []byte) (int, sys.Errno) {
 	if len(buf) == 0 {
diff --git a/internal/sysfs/file_unsupported.go b/internal/sysfs/file_unsupported.go
index eb8b5537..54e224bb 100644
--- a/internal/sysfs/file_unsupported.go
+++ b/internal/sysfs/file_unsupported.go
@@ -2,13 +2,21 @@
 
 package sysfs
 
-import "github.com/tetratelabs/wazero/experimental/sys"
+import (
+	"os"
+
+	"github.com/tetratelabs/wazero/experimental/sys"
+)
 
 const (
 	nonBlockingFileReadSupported  = false
 	nonBlockingFileWriteSupported = false
 )
 
+func rmdir(path string) sys.Errno {
+	return sys.UnwrapOSError(os.Remove(path))
+}
+
 // readFd returns ENOSYS on unsupported platforms.
 func readFd(fd uintptr, buf []byte) (int, sys.Errno) {
 	return -1, sys.ENOSYS
diff --git a/internal/sysfs/file_windows.go b/internal/sysfs/file_windows.go
index c07d2b92..3ad9648e 100644
--- a/internal/sysfs/file_windows.go
+++ b/internal/sysfs/file_windows.go
@@ -81,3 +81,8 @@ func peekNamedPipe(handle syscall.Handle) (uint32, syscall.Errno) {
 		0)                      // [out, optional] LPDWORD lpBytesLeftThisMessage
 	return totalBytesAvail, errno
 }
+
+func rmdir(path string) sys.Errno {
+	err := syscall.Rmdir(path)
+	return sys.UnwrapOSError(err)
+}
diff --git a/internal/sysfs/ino.go b/internal/sysfs/ino.go
new file mode 100644
index 00000000..703f1136
--- /dev/null
+++ b/internal/sysfs/ino.go
@@ -0,0 +1,22 @@
+//go:build !windows && !plan9
+
+package sysfs
+
+import (
+	"io/fs"
+	"syscall"
+
+	experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
+	"github.com/tetratelabs/wazero/sys"
+)
+
+func inoFromFileInfo(_ string, info fs.FileInfo) (sys.Inode, experimentalsys.Errno) {
+	switch v := info.Sys().(type) {
+	case *sys.Stat_t:
+		return v.Ino, 0
+	case *syscall.Stat_t:
+		return v.Ino, 0
+	default:
+		return 0, 0
+	}
+}
diff --git a/internal/sysfs/ino_plan9.go b/internal/sysfs/ino_plan9.go
new file mode 100644
index 00000000..9c669a47
--- /dev/null
+++ b/internal/sysfs/ino_plan9.go
@@ -0,0 +1,15 @@
+package sysfs
+
+import (
+	"io/fs"
+
+	experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
+	"github.com/tetratelabs/wazero/sys"
+)
+
+func inoFromFileInfo(_ string, info fs.FileInfo) (sys.Inode, experimentalsys.Errno) {
+	if v, ok := info.Sys().(*sys.Stat_t); ok {
+		return v.Ino, 0
+	}
+	return 0, 0
+}
diff --git a/internal/sysfs/ino_windows.go b/internal/sysfs/ino_windows.go
new file mode 100644
index 00000000..b880e3b0
--- /dev/null
+++ b/internal/sysfs/ino_windows.go
@@ -0,0 +1,28 @@
+package sysfs
+
+import (
+	"io/fs"
+	"path"
+
+	experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
+	"github.com/tetratelabs/wazero/sys"
+)
+
+// inoFromFileInfo uses stat to get the inode information of the file.
+func inoFromFileInfo(dirPath string, info fs.FileInfo) (ino sys.Inode, errno experimentalsys.Errno) {
+	if v, ok := info.Sys().(*sys.Stat_t); ok {
+		return v.Ino, 0
+	}
+	if dirPath == "" {
+		// This is a fs.File backed implementation which doesn't have access to
+		// the original file path.
+		return
+	}
+	// Ino is no not in Win32FileAttributeData
+	inoPath := path.Clean(path.Join(dirPath, info.Name()))
+	var st sys.Stat_t
+	if st, errno = lstat(inoPath); errno == 0 {
+		ino = st.Ino
+	}
+	return
+}
diff --git a/internal/sysfs/nonblock_plan9.go b/internal/sysfs/nonblock_plan9.go
new file mode 100644
index 00000000..8e94e592
--- /dev/null
+++ b/internal/sysfs/nonblock_plan9.go
@@ -0,0 +1,11 @@
+package sysfs
+
+import "github.com/tetratelabs/wazero/experimental/sys"
+
+func setNonblock(fd uintptr, enable bool) sys.Errno {
+	return sys.ENOSYS
+}
+
+func isNonblock(f *osFile) bool {
+	return false
+}
diff --git a/internal/sysfs/nonblock_unix.go b/internal/sysfs/nonblock_unix.go
index fdee885a..c7ac1859 100644
--- a/internal/sysfs/nonblock_unix.go
+++ b/internal/sysfs/nonblock_unix.go
@@ -1,15 +1,16 @@
-//go:build !windows
+//go:build !windows && !plan9
 
 package sysfs
 
 import (
 	"syscall"
 
+	"github.com/tetratelabs/wazero/experimental/sys"
 	"github.com/tetratelabs/wazero/internal/fsapi"
 )
 
-func setNonblock(fd uintptr, enable bool) error {
-	return syscall.SetNonblock(int(fd), enable)
+func setNonblock(fd uintptr, enable bool) sys.Errno {
+	return sys.UnwrapOSError(syscall.SetNonblock(int(fd), enable))
 }
 
 func isNonblock(f *osFile) bool {
diff --git a/internal/sysfs/nonblock_windows.go b/internal/sysfs/nonblock_windows.go
index 3acbf272..8c6d952b 100644
--- a/internal/sysfs/nonblock_windows.go
+++ b/internal/sysfs/nonblock_windows.go
@@ -1,17 +1,16 @@
-//go:build windows
-
 package sysfs
 
 import (
 	"io/fs"
 	"syscall"
 
+	"github.com/tetratelabs/wazero/experimental/sys"
 	"github.com/tetratelabs/wazero/internal/fsapi"
 )
 
-func setNonblock(fd uintptr, enable bool) error {
+func setNonblock(fd uintptr, enable bool) sys.Errno {
 	// We invoke the syscall, but this is currently no-op.
-	return syscall.SetNonblock(syscall.Handle(fd), enable)
+	return sys.UnwrapOSError(syscall.SetNonblock(syscall.Handle(fd), enable))
 }
 
 func isNonblock(f *osFile) bool {
diff --git a/internal/sysfs/osfile.go b/internal/sysfs/osfile.go
index ead9e49a..36d6ecc4 100644
--- a/internal/sysfs/osfile.go
+++ b/internal/sysfs/osfile.go
@@ -115,8 +115,8 @@ func (f *osFile) SetNonblock(enable bool) (errno experimentalsys.Errno) {
 	} else {
 		f.flag &= ^fsapi.O_NONBLOCK
 	}
-	if err := setNonblock(f.fd, enable); err != nil {
-		return fileError(f, f.closed, experimentalsys.UnwrapOSError(err))
+	if errno = setNonblock(f.fd, enable); errno != 0 {
+		return fileError(f, f.closed, errno)
 	}
 	return 0
 }
diff --git a/internal/sysfs/rename.go b/internal/sysfs/rename.go
index 5ec22539..f7d84ef7 100644
--- a/internal/sysfs/rename.go
+++ b/internal/sysfs/rename.go
@@ -1,4 +1,4 @@
-//go:build !windows
+//go:build !windows && !plan9
 
 package sysfs
 
diff --git a/internal/sysfs/rename_plan9.go b/internal/sysfs/rename_plan9.go
new file mode 100644
index 00000000..474cc759
--- /dev/null
+++ b/internal/sysfs/rename_plan9.go
@@ -0,0 +1,14 @@
+package sysfs
+
+import (
+	"os"
+
+	"github.com/tetratelabs/wazero/experimental/sys"
+)
+
+func rename(from, to string) sys.Errno {
+	if from == to {
+		return 0
+	}
+	return sys.UnwrapOSError(os.Rename(from, to))
+}
diff --git a/internal/sysfs/stat_bsd.go b/internal/sysfs/stat_bsd.go
index aeb74197..254e204c 100644
--- a/internal/sysfs/stat_bsd.go
+++ b/internal/sysfs/stat_bsd.go
@@ -5,7 +5,6 @@ package sysfs
 import (
 	"io/fs"
 	"os"
-	"syscall"
 
 	experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
 	"github.com/tetratelabs/wazero/sys"
@@ -36,14 +35,3 @@ func stat(path string) (sys.Stat_t, experimentalsys.Errno) {
 func statFile(f fs.File) (sys.Stat_t, experimentalsys.Errno) {
 	return defaultStatFile(f)
 }
-
-func inoFromFileInfo(_ string, info fs.FileInfo) (sys.Inode, experimentalsys.Errno) {
-	switch v := info.Sys().(type) {
-	case *sys.Stat_t:
-		return v.Ino, 0
-	case *syscall.Stat_t:
-		return v.Ino, 0
-	default:
-		return 0, 0
-	}
-}
diff --git a/internal/sysfs/stat_linux.go b/internal/sysfs/stat_linux.go
index 426b2850..fd289756 100644
--- a/internal/sysfs/stat_linux.go
+++ b/internal/sysfs/stat_linux.go
@@ -8,7 +8,6 @@ package sysfs
 import (
 	"io/fs"
 	"os"
-	"syscall"
 
 	experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
 	"github.com/tetratelabs/wazero/sys"
@@ -39,14 +38,3 @@ func stat(path string) (sys.Stat_t, experimentalsys.Errno) {
 func statFile(f fs.File) (sys.Stat_t, experimentalsys.Errno) {
 	return defaultStatFile(f)
 }
-
-func inoFromFileInfo(_ string, info fs.FileInfo) (sys.Inode, experimentalsys.Errno) {
-	switch v := info.Sys().(type) {
-	case *sys.Stat_t:
-		return v.Ino, 0
-	case *syscall.Stat_t:
-		return v.Ino, 0
-	default:
-		return 0, 0
-	}
-}
diff --git a/internal/sysfs/stat_unsupported.go b/internal/sysfs/stat_unsupported.go
index b2200415..4b05a897 100644
--- a/internal/sysfs/stat_unsupported.go
+++ b/internal/sysfs/stat_unsupported.go
@@ -5,7 +5,6 @@ package sysfs
 import (
 	"io/fs"
 	"os"
-	"syscall"
 
 	experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
 	"github.com/tetratelabs/wazero/sys"
@@ -39,10 +38,3 @@ func stat(path string) (sys.Stat_t, experimentalsys.Errno) {
 func statFile(f fs.File) (sys.Stat_t, experimentalsys.Errno) {
 	return defaultStatFile(f)
 }
-
-func inoFromFileInfo(_ string, info fs.FileInfo) (sys.Inode, experimentalsys.Errno) {
-	if st, ok := info.Sys().(*syscall.Stat_t); ok {
-		return st.Ino, 0
-	}
-	return 0, 0
-}
diff --git a/internal/sysfs/stat_windows.go b/internal/sysfs/stat_windows.go
index db28300b..4456dd78 100644
--- a/internal/sysfs/stat_windows.go
+++ b/internal/sysfs/stat_windows.go
@@ -4,7 +4,6 @@ package sysfs
 
 import (
 	"io/fs"
-	"path"
 	"syscall"
 
 	experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
@@ -77,22 +76,6 @@ func statFile(f fs.File) (sys.Stat_t, experimentalsys.Errno) {
 	return defaultStatFile(f)
 }
 
-// inoFromFileInfo uses stat to get the inode information of the file.
-func inoFromFileInfo(dirPath string, info fs.FileInfo) (ino sys.Inode, errno experimentalsys.Errno) {
-	if dirPath == "" {
-		// This is a fs.File backed implementation which doesn't have access to
-		// the original file path.
-		return
-	}
-	// Ino is no not in Win32FileAttributeData
-	inoPath := path.Clean(path.Join(dirPath, info.Name()))
-	var st sys.Stat_t
-	if st, errno = lstat(inoPath); errno == 0 {
-		ino = st.Ino
-	}
-	return
-}
-
 func statHandle(h syscall.Handle) (sys.Stat_t, experimentalsys.Errno) {
 	winFt, err := syscall.GetFileType(h)
 	if err != nil {
diff --git a/internal/sysfs/unlink.go b/internal/sysfs/unlink.go
index 4f20b00e..4f7dbe3f 100644
--- a/internal/sysfs/unlink.go
+++ b/internal/sysfs/unlink.go
@@ -1,4 +1,4 @@
-//go:build !windows
+//go:build !windows && !plan9
 
 package sysfs
 
diff --git a/internal/sysfs/unlink_plan9.go b/internal/sysfs/unlink_plan9.go
new file mode 100644
index 00000000..16ed06ab
--- /dev/null
+++ b/internal/sysfs/unlink_plan9.go
@@ -0,0 +1,12 @@
+package sysfs
+
+import (
+	"syscall"
+
+	"github.com/tetratelabs/wazero/experimental/sys"
+)
+
+func unlink(name string) sys.Errno {
+	err := syscall.Remove(name)
+	return sys.UnwrapOSError(err)
+}
diff --git a/internal/sysfs/unlink_windows.go b/internal/sysfs/unlink_windows.go
index e247809e..be31c7b9 100644
--- a/internal/sysfs/unlink_windows.go
+++ b/internal/sysfs/unlink_windows.go
@@ -1,5 +1,3 @@
-//go:build windows
-
 package sysfs
 
 import (


Test patch:
diff --git a/internal/sysfs/file_test.go b/internal/sysfs/file_test.go
index 4e94093f..c3ab2371 100644
--- a/internal/sysfs/file_test.go
+++ b/internal/sysfs/file_test.go
@@ -77,13 +77,13 @@ func TestReadFdNonblock(t *testing.T) {
 	defer w.Close()
 
 	fd := r.Fd()
-	err = setNonblock(fd, true)
-	require.NoError(t, err)
+	errno := setNonblock(fd, true)
+	require.EqualErrno(t, 0, errno)
 
 	// Read from the file without ever writing to it should not block.
 	buf := make([]byte, 8)
-	_, e := readFd(fd, buf)
-	require.EqualErrno(t, experimentalsys.EAGAIN, e)
+	_, errno = readFd(fd, buf)
+	require.EqualErrno(t, experimentalsys.EAGAIN, errno)
 }
 
 func TestWriteFdNonblock(t *testing.T) {
@@ -94,9 +94,9 @@ func TestWriteFdNonblock(t *testing.T) {
 	defer w.Close()
 
 	fd := w.Fd()
-	err = setNonblock(fd, true)
+	errno := setNonblock(fd, true)
 
-	require.NoError(t, err)
+	require.EqualErrno(t, 0, errno)
 
 	// Create a buffer (the content is not relevant)
 	buf := make([]byte, 1024)
diff --git a/internal/sysfs/poll_test.go b/internal/sysfs/poll_test.go
index 192b7b0e..6f6b3559 100644
--- a/internal/sysfs/poll_test.go
+++ b/internal/sysfs/poll_test.go
@@ -8,40 +8,56 @@ import (
 	"testing"
 	"time"
 
+	"github.com/tetratelabs/wazero/experimental/sys"
 	"github.com/tetratelabs/wazero/internal/testing/require"
 )
 
 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)
-		require.EqualErrno(t, 0, err)
-		require.Equal(t, 0, n)
+		for {
+			n, err := _poll([]pollFd{}, 0)
+			if err == sys.EINTR {
+				t.Logf("Select interrupted")
+				continue
+			}
+			require.EqualErrno(t, 0, err)
+			require.Equal(t, 0, n)
+			return
+		}
 	})
 
 	t.Run("should wait for the given duration", func(t *testing.T) {
 		dur := int32(250)
 		var took time.Duration
-		// 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 := _poll([]pollFd{}, dur)
-		took = time.Since(start)
-		require.EqualErrno(t, 0, err)
-		require.Equal(t, 0, n)
-
-		// On some platforms the actual timeout might be arbitrarily
-		// less than requested.
-		if took < time.Duration(dur)*time.Millisecond {
-			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)
-			} else {
-				t.Logf("Select: slept for %v, requested %v", took, dur)
+		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 := _poll([]pollFd{}, dur)
+			took = time.Since(start)
+			if err == sys.EINTR {
+				t.Logf("Select interrupted after %v", took)
+				continue
 			}
+			require.EqualErrno(t, 0, err)
+			require.Equal(t, 0, n)
+
+			// On some platforms the actual timeout might be arbitrarily
+			// less than requested.
+			if took < time.Duration(dur)*time.Millisecond {
+				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)
+				} else {
+					t.Logf("Select: slept for %v, requested %v", took, dur)
+				}
+			}
+			return
 		}
 	})
 
@@ -54,9 +70,16 @@ func Test_poll(t *testing.T) {
 		_, err = ww.Write([]byte("TEST"))
 		require.NoError(t, err)
 
-		fds := []pollFd{newPollFd(rr.Fd(), _POLLIN, 0)}
-		n, err := _poll(fds, 0)
-		require.EqualErrno(t, 0, err)
-		require.Equal(t, 1, n)
+		for {
+			fds := []pollFd{newPollFd(rr.Fd(), _POLLIN, 0)}
+			if err == sys.EINTR {
+				t.Log("Select interrupted")
+				continue
+			}
+			n, err := _poll(fds, 0)
+			require.EqualErrno(t, 0, err)
+			require.Equal(t, 1, n)
+			return
+		}
 	})
 }
diff --git a/internal/sysfs/poll_windows_test.go b/internal/sysfs/poll_windows_test.go
index d137d6f8..46e3e9a2 100644
--- a/internal/sysfs/poll_windows_test.go
+++ b/internal/sysfs/poll_windows_test.go
@@ -233,10 +233,6 @@ func TestPoll_Windows(t *testing.T) {
 		ch := make(chan result, 1)
 		go pollToChannel(r.Fd(), 200, ch)
 
-		// Wait a little, then ensure a message has been written to the channel.
-		<-time.After(300 * time.Millisecond)
-		require.Equal(t, 1, len(ch))
-
 		// Ensure that the timer has expired.
 		res := <-ch
 		require.Zero(t, res.err)
@@ -249,7 +245,7 @@ func TestPoll_Windows(t *testing.T) {
 		wh := syscall.Handle(w.Fd())
 
 		ch := make(chan result, 1)
-		go pollToChannel(r.Fd(), 600, ch)
+		go pollToChannel(r.Fd(), 800, ch)
 
 		<-time.After(300 * time.Millisecond)
 		require.Equal(t, 0, len(ch))