mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-06 12:03:41 +00:00
runtime,syscall,internal/poll,os: wasip1 poll_oneoff scheduler integration + net.FileListener (#5386)
* runtime,syscall,internal/poll,os: wasip1 poll_oneoff scheduler integration + net.FileListener On wasip1 today every syscall.Read/Write blocks the entire wasm module — the cooperative scheduler invokes poll_oneoff only for sleep/timer wakeups, and there's no path from the net package to a working TCP server. This change fixes both: it threads poll_oneoff through the scheduler's idle path so a goroutine doing FD I/O parks instead of blocking the module, and it provides enough internal/poll / os / syscall surface that upstream Go's net.FileListener / net.FileConn works on a host-pre-opened TCP socket. * runtime: keep scheduler_cooperative idle-wait calls direct TestBinarySize/hifive1b/examples/echo regressed by 32 bytes after the previous commit routed the scheduler's idle wait through a schedulerIdleWait helper. The extra call frame + branch landed on every non-wasip1 cooperative target, where the original direct sleepTicks / waitForEvents calls compile to a single inlined call.
This commit is contained in:
+16
-3
@@ -37,9 +37,19 @@ func rename(oldname, newname string) error {
|
||||
// can overwrite this data, which could cause the finalizer
|
||||
// to close the wrong file descriptor.
|
||||
type file struct {
|
||||
handle FileHandle
|
||||
name string
|
||||
dirinfo *dirInfo // nil unless directory being read
|
||||
handle FileHandle
|
||||
name string
|
||||
dirinfo *dirInfo // nil unless directory being read
|
||||
|
||||
// pfd is set on wasip1 by (*File).PollFD to a *poll.FD that wraps
|
||||
// the underlying syscall FD. When set, Close routes through it so
|
||||
// the refcount semantics shared with net.FileListener / net.FileConn
|
||||
// are honoured. On non-wasip1 builds pfd is a literal empty struct
|
||||
// (see pollfd_other.go) and stays at zero bytes — provided it's not
|
||||
// the last field of this struct, which is why it lives here above
|
||||
// appendMode rather than at the end.
|
||||
pfd pollFD
|
||||
|
||||
appendMode bool
|
||||
}
|
||||
|
||||
@@ -48,6 +58,9 @@ func (f *file) close() (err error) {
|
||||
f.dirinfo.close()
|
||||
f.dirinfo = nil
|
||||
}
|
||||
if f.pfd.Exist() {
|
||||
return f.pfd.Close()
|
||||
}
|
||||
return f.handle.Close()
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
//go:build wasip1
|
||||
|
||||
package os
|
||||
|
||||
import "internal/poll"
|
||||
|
||||
// PollFD returns the *poll.FD wrapping this file's underlying syscall
|
||||
// FD. The first call lazily allocates and caches the *poll.FD on the
|
||||
// File; subsequent calls return the same pointer so that refcount
|
||||
// semantics shared with net.FileListener / net.FileConn (via
|
||||
// poll.FD.Copy) work correctly:
|
||||
//
|
||||
// - net.FileListener(f) calls f.PollFD().Copy(); the Copy increments
|
||||
// the refcount via the cached *poll.FD's SysFile.
|
||||
// - f.Close() routes through the cached *poll.FD's Close (see
|
||||
// file_unix.go's file.close), which decrements the refcount and
|
||||
// only releases the syscall FD when the count reaches zero.
|
||||
// - The eventual Listener.Close / Conn.Close decrements the refcount
|
||||
// from the other side.
|
||||
//
|
||||
// PollFD is intended for use by upstream Go's net/file_wasip1.go (which
|
||||
// reaches it via a //go:linkname-style type assertion in this package).
|
||||
func (f *File) PollFD() *poll.FD {
|
||||
if f.handle == nil {
|
||||
return nil
|
||||
}
|
||||
if f.pfd != nil {
|
||||
return f.pfd
|
||||
}
|
||||
pfd := &poll.FD{
|
||||
Sysfd: int(f.handle.(interface{ Fd() uintptr }).Fd()),
|
||||
IsStream: true,
|
||||
}
|
||||
pfd.SysFile.RefCount = 1
|
||||
pfd.SysFile.RefCountPtr = &pfd.SysFile.RefCount
|
||||
f.pfd = pfd
|
||||
return pfd
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
//go:build !wasip1
|
||||
|
||||
package os
|
||||
|
||||
// pollFD is a literal empty struct on non-wasip1 targets. As long as the
|
||||
// field is not the last in its containing struct, Go gives a zero-sized
|
||||
// non-trailing field a true zero byte layout — file then occupies exactly
|
||||
// the same space as it would without the field at all.
|
||||
type pollFD struct{}
|
||||
|
||||
func (pollFD) Close() error { return nil }
|
||||
func (pollFD) Exist() bool { return false }
|
||||
@@ -0,0 +1,12 @@
|
||||
//go:build wasip1
|
||||
|
||||
package os
|
||||
|
||||
import "internal/poll"
|
||||
|
||||
// pollFD on wasip1 is the *poll.FD that backs net.FileListener /
|
||||
// net.FileConn handoffs. The alias makes file.pfd directly typed as
|
||||
// *poll.FD so PollFD reads/writes need no type conversion. The Exist
|
||||
// method on *poll.FD (defined in internal/poll) absorbs the nil-check
|
||||
// that file.close needs on the shared code path.
|
||||
type pollFD = *poll.FD
|
||||
Reference in New Issue
Block a user