Skip to content

Commit

Permalink
fix: poll_oneoff test behavior
Browse files Browse the repository at this point in the history
* Adjust expected event order in a testcase since clock events are now acknowledged outside the loop.
* Always write nevents=0 first before start polling to detect faulty memory early.
* Poll on blocking stdin FdRead.
* Minor nits including if-else criteria/order.

Signed-off-by: Gaukas Wang <[email protected]>
  • Loading branch information
gaukas committed Jan 28, 2024
1 parent 82e6955 commit ae81938
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 38 deletions.
16 changes: 8 additions & 8 deletions imports/wasi_snapshot_preview1/poll_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -376,18 +376,18 @@ func Test_pollOneoff_Stdin(t *testing.T) {
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
// First an illegal file with custom user data should be acknowledged.
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,

// 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
// Clock is acknowledged then.
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,
Expand Down
88 changes: 58 additions & 30 deletions imports/wasi_snapshot_preview1/w_poll_oneoff.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,17 @@ func alternativePollOneoffFn(_ context.Context, mod api.Module, params []uint64)
return sys.EFAULT
}

// start by writing 0 to resultNevents
if !mod.Memory().WriteUint32Le(resultNevents, 0) {
return sys.EFAULT
}

// Extract FS context, used in the body of the for loop for FS access.
fsc := mod.(*wasm.ModuleInstance).Sys.FS()

// Slice of events that are processed out of the loop (blocking stdin subscribers).
var blockingStdinSubs []*event
// The timeout is initialized at max Duration, the loop will find the minimum.
var timeout time.Duration = 1<<63 - 1

// Count of all the subscriptions that have been already written back to outBuf.
// nevents*32 returns at all times the offset where the next event should be written:
// this way we ensure that there are no gaps between records.
Expand Down Expand Up @@ -116,9 +121,15 @@ func alternativePollOneoffFn(_ context.Context, mod api.Module, params []uint64)
evt.errno = wasip1.ErrnoBadf
writeEvent(outBuf[outOffset:], evt)
nevents++
} else if guestFd == internalsys.FdStdin { // stdin is always ready to read (can read 0/EOF etc)
writeEvent(outBuf[outOffset:], evt)
nevents++
} else if guestFd == internalsys.FdStdin { // stdin is always checked with Poll function later.
if file.File.IsNonblock() { // non-blocking stdin is always ready to read
writeEvent(outBuf[outOffset:], evt)
nevents++
} else {
// if the fd is Stdin, and it is in blocking mode,
// do not ack yet, append to a slice for delayed evaluation.
blockingStdinSubs = append(blockingStdinSubs, evt)
}
} else if hostFd := file.File.Fd(); hostFd == 0 {
evt.errno = wasip1.ErrnoNotsup
writeEvent(outBuf[outOffset:], evt)
Expand Down Expand Up @@ -177,40 +188,57 @@ func alternativePollOneoffFn(_ context.Context, mod api.Module, params []uint64)
writeEvent(outBuf[nevents*32:], clkevent)
nevents++
}
// write nevents to resultNevents
if !mem.WriteUint32Le(resultNevents, nevents) {
return sys.EFAULT
}
return 0
}

// If there are I/O subscriptions, we call poll on the I/O fds with the updated timeout.
if len(hostPollSubs) > 0 {
pollNevents, err := internalsysfs.Poll(hostPollSubs, int32(timeout.Milliseconds()))
if err != 0 {
return err
}

pollNevents, err := internalsysfs.Poll(hostPollSubs, int32(timeout.Milliseconds()))
if err != 0 {
return err
if pollNevents > 0 { // if there are events triggered
// iterate over hostPollSubs and if the revent is set, write back
// the event
for i, pollFd := range hostPollSubs {
if pollFd.Revents&pollFd.Events != 0 {
// write back the event
writeEvent(outBuf[nevents*32:], ioEvents[i])
nevents++
} else if pollFd.Revents != 0 {
// write back the event
writeEvent(outBuf[nevents*32:], ioEvents[i])
nevents++
}
}
} else { // otherwise it means that the timeout expired
// Ack the clock event if there is one (it can also be a default max timeout)
if clkevent != nil {
writeEvent(outBuf[nevents*32:], clkevent)
nevents++
}
}
}

if pollNevents > 0 { // if there are events triggered
// iterate over hostPollSubs and if the revent is set, write back
// the event
for i, pollFd := range hostPollSubs {
if pollFd.Revents&pollFd.Events != 0 {
// write back the event
writeEvent(outBuf[nevents*32:], ioEvents[i])
nevents++
} else if pollFd.Revents != 0 {
// write back the event
writeEvent(outBuf[nevents*32:], ioEvents[i])
// If there are blocking stdin subscribers, check for data with given timeout.
if len(blockingStdinSubs) > 0 {
stdin, ok := fsc.LookupFile(internalsys.FdStdin)
if !ok {
return sys.EBADF
}

// Wait for the timeout to expire, or for some data to become available on Stdin.
if stdinReady, errno := stdin.File.Poll(fsapi.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++
}
}
} else { // otherwise it means that the timeout expired
// Ack the clock event if there is one (it can also be a default max timeout)
if clkevent != nil {
writeEvent(outBuf[nevents*32:], clkevent)
nevents++
}
}

// write nevents to resultNevents
Expand Down

0 comments on commit ae81938

Please sign in to comment.