wasi: add more test cases to poll_oneoff, cleanup impl (#1612)
Signed-off-by: Edoardo Vacchi <evacchi@users.noreply.github.com>
This commit is contained in:
@@ -46,7 +46,6 @@ type event struct {
|
|||||||
eventType byte
|
eventType byte
|
||||||
userData []byte
|
userData []byte
|
||||||
errno wasip1.Errno
|
errno wasip1.Errno
|
||||||
outOffset uint32
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func pollOneoffFn(_ context.Context, mod api.Module, params []uint64) sys.Errno {
|
func pollOneoffFn(_ context.Context, mod api.Module, params []uint64) sys.Errno {
|
||||||
@@ -90,16 +89,16 @@ func pollOneoffFn(_ context.Context, mod api.Module, params []uint64) sys.Errno
|
|||||||
var blockingStdinSubs []*event
|
var blockingStdinSubs []*event
|
||||||
// The timeout is initialized at max Duration, the loop will find the minimum.
|
// The timeout is initialized at max Duration, the loop will find the minimum.
|
||||||
var timeout time.Duration = 1<<63 - 1
|
var timeout time.Duration = 1<<63 - 1
|
||||||
// Count of all the clock subscribers that have been already written back to outBuf.
|
// Count of all the subscriptions that have been already written back to outBuf.
|
||||||
clockEvents := uint32(0)
|
// nevents*32 returns at all times the offset where the next event should be written:
|
||||||
// Count of all the non-clock subscribers that have been already written back to outBuf.
|
// this way we ensure that there are no gaps between records.
|
||||||
readySubs := uint32(0)
|
nevents := uint32(0)
|
||||||
|
|
||||||
// Layout is subscription_u: Union
|
// Layout is subscription_u: Union
|
||||||
// https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#subscription_u
|
// https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#subscription_u
|
||||||
for i := uint32(0); i < nsubscriptions; i++ {
|
for i := uint32(0); i < nsubscriptions; i++ {
|
||||||
inOffset := i * 48
|
inOffset := i * 48
|
||||||
outOffset := i * 32
|
outOffset := nevents * 32
|
||||||
|
|
||||||
eventType := inBuf[inOffset+8] // +8 past userdata
|
eventType := inBuf[inOffset+8] // +8 past userdata
|
||||||
// +8 past userdata +8 contents_offset
|
// +8 past userdata +8 contents_offset
|
||||||
@@ -110,12 +109,10 @@ func pollOneoffFn(_ context.Context, mod api.Module, params []uint64) sys.Errno
|
|||||||
eventType: eventType,
|
eventType: eventType,
|
||||||
userData: userData,
|
userData: userData,
|
||||||
errno: wasip1.ErrnoSuccess,
|
errno: wasip1.ErrnoSuccess,
|
||||||
outOffset: outOffset,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch eventType {
|
switch eventType {
|
||||||
case wasip1.EventTypeClock: // handle later
|
case wasip1.EventTypeClock: // handle later
|
||||||
clockEvents++
|
|
||||||
newTimeout, err := processClockEvent(argBuf)
|
newTimeout, err := processClockEvent(argBuf)
|
||||||
if err != 0 {
|
if err != 0 {
|
||||||
return err
|
return err
|
||||||
@@ -125,7 +122,8 @@ func pollOneoffFn(_ context.Context, mod api.Module, params []uint64) sys.Errno
|
|||||||
timeout = newTimeout
|
timeout = newTimeout
|
||||||
}
|
}
|
||||||
// Ack the clock event to the outBuf.
|
// Ack the clock event to the outBuf.
|
||||||
writeEvent(outBuf, evt)
|
writeEvent(outBuf[outOffset:], evt)
|
||||||
|
nevents++
|
||||||
case wasip1.EventTypeFdRead:
|
case wasip1.EventTypeFdRead:
|
||||||
fd := int32(le.Uint32(argBuf))
|
fd := int32(le.Uint32(argBuf))
|
||||||
if fd < 0 {
|
if fd < 0 {
|
||||||
@@ -133,16 +131,15 @@ func pollOneoffFn(_ context.Context, mod api.Module, params []uint64) sys.Errno
|
|||||||
}
|
}
|
||||||
if file, ok := fsc.LookupFile(fd); !ok {
|
if file, ok := fsc.LookupFile(fd); !ok {
|
||||||
evt.errno = wasip1.ErrnoBadf
|
evt.errno = wasip1.ErrnoBadf
|
||||||
writeEvent(outBuf, evt)
|
writeEvent(outBuf[outOffset:], evt)
|
||||||
readySubs++
|
nevents++
|
||||||
continue
|
} else if fd != internalsys.FdStdin && file.File.IsNonblock() {
|
||||||
} else if fd == internalsys.FdStdin && !file.File.IsNonblock() {
|
writeEvent(outBuf[outOffset:], evt)
|
||||||
// if the fd is Stdin, and it is in non-blocking mode,
|
nevents++
|
||||||
|
} else {
|
||||||
|
// if the fd is Stdin, and it is in blocking mode,
|
||||||
// do not ack yet, append to a slice for delayed evaluation.
|
// do not ack yet, append to a slice for delayed evaluation.
|
||||||
blockingStdinSubs = append(blockingStdinSubs, evt)
|
blockingStdinSubs = append(blockingStdinSubs, evt)
|
||||||
} else {
|
|
||||||
writeEvent(outBuf, evt)
|
|
||||||
readySubs++
|
|
||||||
}
|
}
|
||||||
case wasip1.EventTypeFdWrite:
|
case wasip1.EventTypeFdWrite:
|
||||||
fd := int32(le.Uint32(argBuf))
|
fd := int32(le.Uint32(argBuf))
|
||||||
@@ -154,47 +151,46 @@ func pollOneoffFn(_ context.Context, mod api.Module, params []uint64) sys.Errno
|
|||||||
} else {
|
} else {
|
||||||
evt.errno = wasip1.ErrnoBadf
|
evt.errno = wasip1.ErrnoBadf
|
||||||
}
|
}
|
||||||
readySubs++
|
nevents++
|
||||||
writeEvent(outBuf, evt)
|
writeEvent(outBuf[outOffset:], evt)
|
||||||
default:
|
default:
|
||||||
return sys.EINVAL
|
return sys.EINVAL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there are subscribers with data ready, we have already written them to outBuf,
|
sysCtx := mod.(*wasm.ModuleInstance).Sys
|
||||||
// and we don't need to wait for the timeout: clear it.
|
if nevents == nsubscriptions {
|
||||||
if readySubs != 0 {
|
// We already wrote back all the results. We already wrote this number
|
||||||
timeout = 0
|
// earlier to offset `resultNevents`.
|
||||||
|
// We only need to observe the timeout (nonzero if there are clock subscriptions)
|
||||||
|
// and return.
|
||||||
|
if timeout > 0 {
|
||||||
|
sysCtx.Nanosleep(int64(timeout))
|
||||||
|
}
|
||||||
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there are blocking stdin subscribers, check for data with given timeout.
|
// If there are blocking stdin subscribers, check for data with given timeout.
|
||||||
if len(blockingStdinSubs) > 0 {
|
stdin, ok := fsc.LookupFile(internalsys.FdStdin)
|
||||||
stdin, ok := fsc.LookupFile(internalsys.FdStdin)
|
if !ok {
|
||||||
if !ok {
|
return sys.EBADF
|
||||||
return sys.EBADF
|
}
|
||||||
|
// Wait for the timeout to expire, or for some data to become available on Stdin.
|
||||||
|
|
||||||
|
if stdinReady, errno := stdin.File.Poll(sys.POLLIN, int32(timeout.Milliseconds())); errno != 0 {
|
||||||
|
return errno
|
||||||
|
} else if stdinReady {
|
||||||
|
// stdin has data ready to for reading, write back all the events
|
||||||
|
for i := range blockingStdinSubs {
|
||||||
|
evt := blockingStdinSubs[i]
|
||||||
|
evt.errno = 0
|
||||||
|
writeEvent(outBuf[nevents*32:], evt)
|
||||||
|
nevents++
|
||||||
}
|
}
|
||||||
// Wait for the timeout to expire, or for some data to become available on Stdin.
|
|
||||||
stdinReady, errno := stdin.File.Poll(sys.POLLIN, int32(timeout.Milliseconds()))
|
|
||||||
if errno != 0 {
|
|
||||||
return errno
|
|
||||||
}
|
|
||||||
if stdinReady {
|
|
||||||
// stdin has data ready to for reading, write back all the events
|
|
||||||
for i := range blockingStdinSubs {
|
|
||||||
readySubs++
|
|
||||||
evt := blockingStdinSubs[i]
|
|
||||||
evt.errno = 0
|
|
||||||
writeEvent(outBuf, evt)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// No subscribers, just wait for the given timeout.
|
|
||||||
sysCtx := mod.(*wasm.ModuleInstance).Sys
|
|
||||||
sysCtx.Nanosleep(int64(timeout))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if readySubs != nsubscriptions {
|
if nevents != nsubscriptions {
|
||||||
if !mod.Memory().WriteUint32Le(resultNevents, readySubs+clockEvents) {
|
if !mod.Memory().WriteUint32Le(resultNevents, nevents) {
|
||||||
return sys.EFAULT
|
return sys.EFAULT
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -234,9 +230,9 @@ func processClockEvent(inBuf []byte) (time.Duration, sys.Errno) {
|
|||||||
// writeEvent writes the event corresponding to the processed subscription.
|
// writeEvent writes the event corresponding to the processed subscription.
|
||||||
// https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-event-struct
|
// https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-event-struct
|
||||||
func writeEvent(outBuf []byte, evt *event) {
|
func writeEvent(outBuf []byte, evt *event) {
|
||||||
copy(outBuf[evt.outOffset:], evt.userData) // userdata
|
copy(outBuf, evt.userData) // userdata
|
||||||
outBuf[evt.outOffset+8] = byte(evt.errno) // uint16, but safe as < 255
|
outBuf[8] = byte(evt.errno) // uint16, but safe as < 255
|
||||||
outBuf[evt.outOffset+9] = 0
|
outBuf[9] = 0
|
||||||
le.PutUint32(outBuf[evt.outOffset+10:], uint32(evt.eventType))
|
le.PutUint32(outBuf[10:], uint32(evt.eventType))
|
||||||
// TODO: When FD events are supported, write outOffset+16
|
// TODO: When FD events are supported, write outOffset+16
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package wasi_snapshot_preview1_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"io/fs"
|
"io/fs"
|
||||||
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@@ -150,6 +151,12 @@ func Test_pollOneoff_Errors(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Test_pollOneoff_Stdin(t *testing.T) {
|
func Test_pollOneoff_Stdin(t *testing.T) {
|
||||||
|
w, r, err := os.Pipe()
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer w.Close()
|
||||||
|
defer r.Close()
|
||||||
|
_, _ = w.Write([]byte("wazero"))
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
in, out, nsubscriptions, resultNevents uint32
|
in, out, nsubscriptions, resultNevents uint32
|
||||||
@@ -192,7 +199,6 @@ func Test_pollOneoff_Stdin(t *testing.T) {
|
|||||||
mem: concat(
|
mem: concat(
|
||||||
clockNsSub(20*1000*1000),
|
clockNsSub(20*1000*1000),
|
||||||
fdReadSub,
|
fdReadSub,
|
||||||
singleton('?'),
|
|
||||||
),
|
),
|
||||||
expectedErrno: wasip1.ErrnoSuccess,
|
expectedErrno: wasip1.ErrnoSuccess,
|
||||||
out: 128, // past in
|
out: 128, // past in
|
||||||
@@ -227,7 +233,6 @@ func Test_pollOneoff_Stdin(t *testing.T) {
|
|||||||
mem: concat(
|
mem: concat(
|
||||||
clockNsSub(20*1000*1000),
|
clockNsSub(20*1000*1000),
|
||||||
fdReadSub,
|
fdReadSub,
|
||||||
singleton('?'),
|
|
||||||
),
|
),
|
||||||
expectedErrno: wasip1.ErrnoSuccess,
|
expectedErrno: wasip1.ErrnoSuccess,
|
||||||
out: 128, // past in
|
out: 128, // past in
|
||||||
@@ -262,7 +267,6 @@ func Test_pollOneoff_Stdin(t *testing.T) {
|
|||||||
mem: concat(
|
mem: concat(
|
||||||
clockNsSub(20*1000*1000),
|
clockNsSub(20*1000*1000),
|
||||||
fdReadSub,
|
fdReadSub,
|
||||||
singleton('?'),
|
|
||||||
),
|
),
|
||||||
expectedErrno: wasip1.ErrnoSuccess,
|
expectedErrno: wasip1.ErrnoSuccess,
|
||||||
out: 128, // past in
|
out: 128, // past in
|
||||||
@@ -297,7 +301,6 @@ func Test_pollOneoff_Stdin(t *testing.T) {
|
|||||||
mem: concat(
|
mem: concat(
|
||||||
clockNsSub(20*1000*1000),
|
clockNsSub(20*1000*1000),
|
||||||
fdReadSub,
|
fdReadSub,
|
||||||
singleton('?'),
|
|
||||||
),
|
),
|
||||||
expectedErrno: wasip1.ErrnoSuccess,
|
expectedErrno: wasip1.ErrnoSuccess,
|
||||||
out: 128, // past in
|
out: 128, // past in
|
||||||
@@ -332,7 +335,6 @@ func Test_pollOneoff_Stdin(t *testing.T) {
|
|||||||
mem: concat(
|
mem: concat(
|
||||||
clockNsSub(20*1000*1000),
|
clockNsSub(20*1000*1000),
|
||||||
fdReadSub,
|
fdReadSub,
|
||||||
singleton('?'),
|
|
||||||
),
|
),
|
||||||
|
|
||||||
expectedErrno: wasip1.ErrnoSuccess,
|
expectedErrno: wasip1.ErrnoSuccess,
|
||||||
@@ -357,6 +359,52 @@ func Test_pollOneoff_Stdin(t *testing.T) {
|
|||||||
expectedLog: `
|
expectedLog: `
|
||||||
==> wasi_snapshot_preview1.poll_oneoff(in=0,out=128,nsubscriptions=2)
|
==> wasi_snapshot_preview1.poll_oneoff(in=0,out=128,nsubscriptions=2)
|
||||||
<== (nevents=1,errno=ESUCCESS)
|
<== (nevents=1,errno=ESUCCESS)
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "pollable pipe, multiple subs, events returned out of order",
|
||||||
|
nsubscriptions: 3,
|
||||||
|
expectedNevents: 3,
|
||||||
|
mem: concat(
|
||||||
|
fdReadSub,
|
||||||
|
clockNsSub(20*1000*1000),
|
||||||
|
// Illegal file fd with custom user data to recognize it in the event buffer.
|
||||||
|
fdReadSubFdWithUserData(100, []byte{0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77})),
|
||||||
|
stdin: &sys.StdinFile{Reader: w},
|
||||||
|
expectedErrno: wasip1.ErrnoSuccess,
|
||||||
|
out: 128, // past in
|
||||||
|
resultNevents: 512, // past out
|
||||||
|
expectedMem: []byte{
|
||||||
|
// Clock is acknowledged first.
|
||||||
|
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // userdata
|
||||||
|
byte(wasip1.ErrnoSuccess), 0x0, // errno is 16 bit
|
||||||
|
wasip1.EventTypeClock, 0x0, 0x0, 0x0, // 4 bytes for type enum
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0,
|
||||||
|
|
||||||
|
// Then an illegal file with custom user data.
|
||||||
|
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, // userdata
|
||||||
|
byte(wasip1.ErrnoBadf), 0x0, // errno is 16 bit
|
||||||
|
wasip1.EventTypeFdRead, 0x0, 0x0, 0x0, // 4 bytes for type enum
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0,
|
||||||
|
|
||||||
|
// Stdin pipes are delayed to invoke sysfs.poll
|
||||||
|
// thus, they are written back last.
|
||||||
|
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // userdata
|
||||||
|
byte(wasip1.ErrnoSuccess), 0x0, // errno is 16 bit
|
||||||
|
wasip1.EventTypeFdRead, 0x0, 0x0, 0x0, // 4 bytes for type enum
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0,
|
||||||
|
|
||||||
|
'?', // stopped after encoding
|
||||||
|
},
|
||||||
|
expectedLog: `
|
||||||
|
==> wasi_snapshot_preview1.poll_oneoff(in=0,out=128,nsubscriptions=3)
|
||||||
|
<== (nevents=3,errno=ESUCCESS)
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -420,7 +468,6 @@ func Test_pollOneoff_Zero(t *testing.T) {
|
|||||||
concat(
|
concat(
|
||||||
clockNsSub(20*1000*1000),
|
clockNsSub(20*1000*1000),
|
||||||
fdReadSub,
|
fdReadSub,
|
||||||
singleton('?'),
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -460,7 +507,6 @@ func Test_pollOneoff_Zero(t *testing.T) {
|
|||||||
concat(
|
concat(
|
||||||
clockNsSub(20*1000*1000),
|
clockNsSub(20*1000*1000),
|
||||||
fdReadSub,
|
fdReadSub,
|
||||||
singleton('?'),
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -491,10 +537,6 @@ func Test_pollOneoff_Zero(t *testing.T) {
|
|||||||
require.Equal(t, uint32(1), nevents)
|
require.Equal(t, uint32(1), nevents)
|
||||||
}
|
}
|
||||||
|
|
||||||
func singleton(b byte) []byte {
|
|
||||||
return []byte{b}
|
|
||||||
}
|
|
||||||
|
|
||||||
func concat(bytes ...[]byte) []byte {
|
func concat(bytes ...[]byte) []byte {
|
||||||
var res []byte
|
var res []byte
|
||||||
for i := range bytes {
|
for i := range bytes {
|
||||||
@@ -522,9 +564,26 @@ func fdReadSubFd(fd byte) []byte {
|
|||||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // userdata
|
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // userdata
|
||||||
wasip1.EventTypeFdRead, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
wasip1.EventTypeFdRead, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
fd, 0x0, 0x0, 0x0, // valid readable FD
|
fd, 0x0, 0x0, 0x0, // valid readable FD
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, // pad to 32 bytes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func fdReadSubFdWithUserData(fd byte, userdata []byte) []byte {
|
||||||
|
return concat(
|
||||||
|
userdata,
|
||||||
|
[]byte{
|
||||||
|
wasip1.EventTypeFdRead, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
fd, 0x0, 0x0, 0x0, // valid readable FD
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, // pad to 32 bytes
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// subscription for an EventTypeFdRead on stdin
|
// subscription for an EventTypeFdRead on stdin
|
||||||
var fdReadSub = fdReadSubFd(byte(sys.FdStdin))
|
var fdReadSub = fdReadSubFd(byte(sys.FdStdin))
|
||||||
|
|
||||||
|
|||||||
@@ -103,3 +103,30 @@ func TestTcpConnFile_Stat(t *testing.T) {
|
|||||||
_, errno := file.Stat()
|
_, errno := file.Stat()
|
||||||
require.Zero(t, errno, "Stat should not fail")
|
require.Zero(t, errno, "Stat should not fail")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTcpConnFile_SetNonblock(t *testing.T) {
|
||||||
|
listen, err := net.Listen("tcp", "127.0.0.1:0")
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer listen.Close()
|
||||||
|
|
||||||
|
lf := newTCPListenerFile(listen.(*net.TCPListener))
|
||||||
|
|
||||||
|
tcpAddr, err := net.ResolveTCPAddr("tcp", listen.Addr().String())
|
||||||
|
require.NoError(t, err)
|
||||||
|
tcp, err := net.DialTCP("tcp", nil, tcpAddr)
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer tcp.Close() //nolint
|
||||||
|
|
||||||
|
errno := lf.SetNonblock(true)
|
||||||
|
require.EqualErrno(t, 0, errno)
|
||||||
|
require.True(t, lf.IsNonblock())
|
||||||
|
|
||||||
|
conn, errno := lf.Accept()
|
||||||
|
require.EqualErrno(t, 0, errno)
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
file := newTcpConn(tcp)
|
||||||
|
errno = file.SetNonblock(true)
|
||||||
|
require.EqualErrno(t, 0, errno)
|
||||||
|
require.True(t, file.IsNonblock())
|
||||||
|
}
|
||||||
|
|||||||
@@ -41,8 +41,9 @@ var _ socketapi.TCPSock = (*tcpListenerFile)(nil)
|
|||||||
type tcpListenerFile struct {
|
type tcpListenerFile struct {
|
||||||
baseSockFile
|
baseSockFile
|
||||||
|
|
||||||
fd uintptr
|
fd uintptr
|
||||||
addr *net.TCPAddr
|
addr *net.TCPAddr
|
||||||
|
nonblock bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Accept implements the same method as documented on socketapi.TCPSock
|
// Accept implements the same method as documented on socketapi.TCPSock
|
||||||
@@ -57,9 +58,14 @@ func (f *tcpListenerFile) Accept() (socketapi.TCPConn, sys.Errno) {
|
|||||||
|
|
||||||
// SetNonblock implements the same method as documented on sys.File
|
// SetNonblock implements the same method as documented on sys.File
|
||||||
func (f *tcpListenerFile) SetNonblock(enabled bool) sys.Errno {
|
func (f *tcpListenerFile) SetNonblock(enabled bool) sys.Errno {
|
||||||
|
f.nonblock = enabled
|
||||||
return sys.UnwrapOSError(setNonblock(f.fd, enabled))
|
return sys.UnwrapOSError(setNonblock(f.fd, enabled))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *tcpListenerFile) IsNonblock() bool {
|
||||||
|
return f.nonblock
|
||||||
|
}
|
||||||
|
|
||||||
// Close implements the same method as documented on sys.File
|
// Close implements the same method as documented on sys.File
|
||||||
func (f *tcpListenerFile) Close() sys.Errno {
|
func (f *tcpListenerFile) Close() sys.Errno {
|
||||||
return sys.UnwrapOSError(syscall.Close(int(f.fd)))
|
return sys.UnwrapOSError(syscall.Close(int(f.fd)))
|
||||||
@@ -75,7 +81,8 @@ var _ socketapi.TCPConn = (*tcpConnFile)(nil)
|
|||||||
type tcpConnFile struct {
|
type tcpConnFile struct {
|
||||||
baseSockFile
|
baseSockFile
|
||||||
|
|
||||||
fd uintptr
|
fd uintptr
|
||||||
|
nonblock bool
|
||||||
|
|
||||||
// closed is true when closed was called. This ensures proper sys.EBADF
|
// closed is true when closed was called. This ensures proper sys.EBADF
|
||||||
closed bool
|
closed bool
|
||||||
@@ -91,9 +98,15 @@ func newTcpConn(tc *net.TCPConn) socketapi.TCPConn {
|
|||||||
|
|
||||||
// SetNonblock implements the same method as documented on sys.File
|
// SetNonblock implements the same method as documented on sys.File
|
||||||
func (f *tcpConnFile) SetNonblock(enabled bool) (errno sys.Errno) {
|
func (f *tcpConnFile) SetNonblock(enabled bool) (errno sys.Errno) {
|
||||||
|
f.nonblock = enabled
|
||||||
return sys.UnwrapOSError(setNonblock(f.fd, enabled))
|
return sys.UnwrapOSError(setNonblock(f.fd, enabled))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsNonblock implements the same method as documented on sys.File
|
||||||
|
func (f *tcpConnFile) IsNonblock() bool {
|
||||||
|
return f.nonblock
|
||||||
|
}
|
||||||
|
|
||||||
// Read implements the same method as documented on sys.File
|
// Read implements the same method as documented on sys.File
|
||||||
func (f *tcpConnFile) Read(buf []byte) (n int, errno sys.Errno) {
|
func (f *tcpConnFile) Read(buf []byte) (n int, errno sys.Errno) {
|
||||||
n, err := syscall.Read(int(f.fd), buf)
|
n, err := syscall.Read(int(f.fd), buf)
|
||||||
|
|||||||
@@ -90,6 +90,16 @@ func syscallConnControl(conn syscall.Conn, fn func(fd uintptr) (int, sys.Errno))
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func _pollSock(conn syscall.Conn, flag sys.Pflag, timeoutMillis int32) (bool, sys.Errno) {
|
||||||
|
if flag != sys.POLLIN {
|
||||||
|
return false, sys.ENOTSUP
|
||||||
|
}
|
||||||
|
n, errno := syscallConnControl(conn, func(fd uintptr) (int, sys.Errno) {
|
||||||
|
return _poll([]pollFd{newPollFd(fd, _POLLIN, 0)}, timeoutMillis)
|
||||||
|
})
|
||||||
|
return n > 0, errno
|
||||||
|
}
|
||||||
|
|
||||||
// newTCPListenerFile is a constructor for a socketapi.TCPSock.
|
// newTCPListenerFile is a constructor for a socketapi.TCPSock.
|
||||||
//
|
//
|
||||||
// Note: currently the Windows implementation of socketapi.TCPSock
|
// Note: currently the Windows implementation of socketapi.TCPSock
|
||||||
@@ -99,9 +109,7 @@ func syscallConnControl(conn syscall.Conn, fn func(fd uintptr) (int, sys.Errno))
|
|||||||
// standard library, instead of invoke syscalls/Win32 APIs
|
// standard library, instead of invoke syscalls/Win32 APIs
|
||||||
// because they are sensibly different from Unix's.
|
// because they are sensibly different from Unix's.
|
||||||
func newTCPListenerFile(tl *net.TCPListener) socketapi.TCPSock {
|
func newTCPListenerFile(tl *net.TCPListener) socketapi.TCPSock {
|
||||||
w := &winTcpListenerFile{tl: tl}
|
return &winTcpListenerFile{tl: tl}
|
||||||
_ = w.SetNonblock(true)
|
|
||||||
return w
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ socketapi.TCPSock = (*winTcpListenerFile)(nil)
|
var _ socketapi.TCPSock = (*winTcpListenerFile)(nil)
|
||||||
@@ -116,14 +124,11 @@ type winTcpListenerFile struct {
|
|||||||
|
|
||||||
// Accept implements the same method as documented on socketapi.TCPSock
|
// Accept implements the same method as documented on socketapi.TCPSock
|
||||||
func (f *winTcpListenerFile) Accept() (socketapi.TCPConn, sys.Errno) {
|
func (f *winTcpListenerFile) Accept() (socketapi.TCPConn, sys.Errno) {
|
||||||
// Ensure we have an incoming connection using winsock_select.
|
// Ensure we have an incoming connection using winsock_select, otherwise return immediately.
|
||||||
n, errno := syscallConnControl(f.tl, func(fd uintptr) (int, sys.Errno) {
|
if f.nonblock {
|
||||||
return _poll([]pollFd{newPollFd(fd, _POLLIN, 0)}, 0)
|
if ready, errno := _pollSock(f.tl, sys.POLLIN, 0); !ready || errno != 0 {
|
||||||
})
|
return nil, sys.EAGAIN
|
||||||
|
}
|
||||||
// Otherwise return immediately.
|
|
||||||
if n == 0 || errno != 0 {
|
|
||||||
return nil, sys.EAGAIN
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Accept normally blocks goroutines, but we
|
// Accept normally blocks goroutines, but we
|
||||||
@@ -186,6 +191,7 @@ func newTcpConn(tc *net.TCPConn) socketapi.TCPConn {
|
|||||||
|
|
||||||
// SetNonblock implements the same method as documented on sys.File
|
// SetNonblock implements the same method as documented on sys.File
|
||||||
func (f *winTcpConnFile) SetNonblock(enabled bool) (errno sys.Errno) {
|
func (f *winTcpConnFile) SetNonblock(enabled bool) (errno sys.Errno) {
|
||||||
|
f.nonblock = true
|
||||||
_, errno = syscallConnControl(f.tc, func(fd uintptr) (int, sys.Errno) {
|
_, errno = syscallConnControl(f.tc, func(fd uintptr) (int, sys.Errno) {
|
||||||
return 0, sys.UnwrapOSError(setNonblockSocket(syscall.Handle(fd), enabled))
|
return 0, sys.UnwrapOSError(setNonblockSocket(syscall.Handle(fd), enabled))
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user