mirror of
https://github.com/golang/go
synced 2024-09-29 09:24:28 -06:00
runtime: add remaining wasip1 files
Implements OS interactions and memory management. For #58141 Co-authored-by: Richard Musiol <neelance@gmail.com> Co-authored-by: Achille Roussel <achille.roussel@gmail.com> Co-authored-by: Julien Fabre <ju.pryz@gmail.com> Co-authored-by: Evan Phoenix <evan@phx.io> Change-Id: I876e7b033090c2fe2d76d2535bb63d52efa36185 Reviewed-on: https://go-review.googlesource.com/c/go/+/479618 TryBot-Result: Gopher Robot <gobot@golang.org> Reviewed-by: Cherry Mui <cherryyz@google.com> Reviewed-by: Ian Lance Taylor <iant@google.com> Run-TryBot: Cherry Mui <cherryyz@google.com> Auto-Submit: Ian Lance Taylor <iant@golang.org> Run-TryBot: Ian Lance Taylor <iant@golang.org>
This commit is contained in:
parent
c02fa75086
commit
be50055eef
@ -78,6 +78,24 @@ func MustHaveGoRun(t testing.TB) {
|
||||
}
|
||||
}
|
||||
|
||||
// HasParallelism reports whether the current system can execute multiple
|
||||
// threads in parallel.
|
||||
func HasParallelism() bool {
|
||||
switch runtime.GOOS {
|
||||
case "js", "wasip1":
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// MustHaveParallelism checks that the current system can execute multiple
|
||||
// threads in parallel. If not, MustHaveParallelism calls t.Skip with an explanation.
|
||||
func MustHaveParallelism(t testing.TB) {
|
||||
if !HasParallelism() {
|
||||
t.Skipf("skipping test: no parallelism available on %s/%s", runtime.GOOS, runtime.GOARCH)
|
||||
}
|
||||
}
|
||||
|
||||
// GoToolPath reports the path to the Go tool.
|
||||
// It is a convenience wrapper around GoTool.
|
||||
// If the tool is unavailable GoToolPath calls t.Skip.
|
||||
|
107
src/runtime/lock_wasip1.go
Normal file
107
src/runtime/lock_wasip1.go
Normal file
@ -0,0 +1,107 @@
|
||||
// Copyright 2023 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build wasip1
|
||||
|
||||
package runtime
|
||||
|
||||
// wasm has no support for threads yet. There is no preemption.
|
||||
// See proposal: https://github.com/WebAssembly/threads
|
||||
// Waiting for a mutex or timeout is implemented as a busy loop
|
||||
// while allowing other goroutines to run.
|
||||
|
||||
const (
|
||||
mutex_unlocked = 0
|
||||
mutex_locked = 1
|
||||
|
||||
active_spin = 4
|
||||
active_spin_cnt = 30
|
||||
)
|
||||
|
||||
func lock(l *mutex) {
|
||||
lockWithRank(l, getLockRank(l))
|
||||
}
|
||||
|
||||
func lock2(l *mutex) {
|
||||
if l.key == mutex_locked {
|
||||
// wasm is single-threaded so we should never
|
||||
// observe this.
|
||||
throw("self deadlock")
|
||||
}
|
||||
gp := getg()
|
||||
if gp.m.locks < 0 {
|
||||
throw("lock count")
|
||||
}
|
||||
gp.m.locks++
|
||||
l.key = mutex_locked
|
||||
}
|
||||
|
||||
func unlock(l *mutex) {
|
||||
unlockWithRank(l)
|
||||
}
|
||||
|
||||
func unlock2(l *mutex) {
|
||||
if l.key == mutex_unlocked {
|
||||
throw("unlock of unlocked lock")
|
||||
}
|
||||
gp := getg()
|
||||
gp.m.locks--
|
||||
if gp.m.locks < 0 {
|
||||
throw("lock count")
|
||||
}
|
||||
l.key = mutex_unlocked
|
||||
}
|
||||
|
||||
// One-time notifications.
|
||||
func noteclear(n *note) {
|
||||
n.key = 0
|
||||
}
|
||||
|
||||
func notewakeup(n *note) {
|
||||
if n.key != 0 {
|
||||
print("notewakeup - double wakeup (", n.key, ")\n")
|
||||
throw("notewakeup - double wakeup")
|
||||
}
|
||||
n.key = 1
|
||||
}
|
||||
|
||||
func notesleep(n *note) {
|
||||
throw("notesleep not supported by wasi")
|
||||
}
|
||||
|
||||
func notetsleep(n *note, ns int64) bool {
|
||||
throw("notetsleep not supported by wasi")
|
||||
return false
|
||||
}
|
||||
|
||||
// same as runtime·notetsleep, but called on user g (not g0)
|
||||
func notetsleepg(n *note, ns int64) bool {
|
||||
gp := getg()
|
||||
if gp == gp.m.g0 {
|
||||
throw("notetsleepg on g0")
|
||||
}
|
||||
|
||||
deadline := nanotime() + ns
|
||||
for {
|
||||
if n.key != 0 {
|
||||
return true
|
||||
}
|
||||
if sched_yield() != 0 {
|
||||
throw("sched_yield failed")
|
||||
}
|
||||
Gosched()
|
||||
if ns >= 0 && nanotime() >= deadline {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func beforeIdle(int64, int64) (*g, bool) {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func checkTimeouts() {}
|
||||
|
||||
//go:wasmimport wasi_snapshot_preview1 sched_yield
|
||||
func sched_yield() errno
|
@ -2,27 +2,10 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build js && wasm
|
||||
//go:build js
|
||||
|
||||
package runtime
|
||||
|
||||
import "unsafe"
|
||||
|
||||
func sbrk(n uintptr) unsafe.Pointer {
|
||||
grow := divRoundUp(n, physPageSize)
|
||||
size := currentMemory()
|
||||
|
||||
if growMemory(int32(grow)) < 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
resetMemoryDataView()
|
||||
return unsafe.Pointer(uintptr(size) * physPageSize)
|
||||
}
|
||||
|
||||
func currentMemory() int32
|
||||
func growMemory(pages int32) int32
|
||||
|
||||
// resetMemoryDataView signals the JS front-end that WebAssembly's memory.grow instruction has been used.
|
||||
// This allows the front-end to replace the old DataView object with a new one.
|
||||
//
|
||||
|
13
src/runtime/mem_wasip1.go
Normal file
13
src/runtime/mem_wasip1.go
Normal file
@ -0,0 +1,13 @@
|
||||
// Copyright 2023 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build wasip1
|
||||
|
||||
package runtime
|
||||
|
||||
func resetMemoryDataView() {
|
||||
// This function is a no-op on WASI, it is only used to notify the browser
|
||||
// that its view of the WASM memory needs to be updated when compiling for
|
||||
// GOOS=js.
|
||||
}
|
20
src/runtime/mem_wasm.go
Normal file
20
src/runtime/mem_wasm.go
Normal file
@ -0,0 +1,20 @@
|
||||
// Copyright 2023 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package runtime
|
||||
|
||||
import "unsafe"
|
||||
|
||||
func sbrk(n uintptr) unsafe.Pointer {
|
||||
grow := divRoundUp(n, physPageSize)
|
||||
size := growMemory(int32(grow))
|
||||
if size < 0 {
|
||||
return nil
|
||||
}
|
||||
resetMemoryDataView()
|
||||
return unsafe.Pointer(uintptr(size) * physPageSize)
|
||||
}
|
||||
|
||||
// Implemented in src/runtime/sys_wasm.s
|
||||
func growMemory(pages int32) int32
|
@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build unix || (js && wasm) || windows
|
||||
//go:build unix || (js && wasm) || wasip1 || windows
|
||||
|
||||
package runtime
|
||||
|
||||
|
@ -2,10 +2,10 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Fake network poller for wasm/js.
|
||||
// Should never be used, because wasm/js network connections do not honor "SetNonblock".
|
||||
// Fake network poller for js/wasm and wasip1/wasm.
|
||||
// Should never be used, because wasm network connections do not honor "SetNonblock".
|
||||
|
||||
//go:build js && wasm
|
||||
//go:build (js && wasm) || wasip1
|
||||
|
||||
package runtime
|
||||
|
||||
|
@ -7,7 +7,6 @@
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"runtime/internal/atomic"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
@ -21,154 +20,18 @@ func write1(fd uintptr, p unsafe.Pointer, n int32) int32 {
|
||||
return n
|
||||
}
|
||||
|
||||
// Stubs so tests can link correctly. These should never be called.
|
||||
func open(name *byte, mode, perm int32) int32 { panic("not implemented") }
|
||||
func closefd(fd int32) int32 { panic("not implemented") }
|
||||
func read(fd int32, p unsafe.Pointer, n int32) int32 { panic("not implemented") }
|
||||
|
||||
//go:wasmimport gojs runtime.wasmWrite
|
||||
//go:noescape
|
||||
func wasmWrite(fd uintptr, p unsafe.Pointer, n int32)
|
||||
|
||||
func usleep(usec uint32)
|
||||
|
||||
//go:nosplit
|
||||
func usleep_no_g(usec uint32) {
|
||||
usleep(usec)
|
||||
}
|
||||
|
||||
func exitThread(wait *atomic.Uint32)
|
||||
|
||||
type mOS struct{}
|
||||
|
||||
func osyield()
|
||||
|
||||
//go:nosplit
|
||||
func osyield_no_g() {
|
||||
osyield()
|
||||
}
|
||||
|
||||
const _SIGSEGV = 0xb
|
||||
|
||||
func sigpanic() {
|
||||
gp := getg()
|
||||
if !canpanic() {
|
||||
throw("unexpected signal during runtime execution")
|
||||
}
|
||||
|
||||
// js only invokes the exception handler for memory faults.
|
||||
gp.sig = _SIGSEGV
|
||||
panicmem()
|
||||
}
|
||||
|
||||
type sigset struct{}
|
||||
|
||||
// Called to initialize a new m (including the bootstrap m).
|
||||
// Called on the parent thread (main thread in case of bootstrap), can allocate memory.
|
||||
func mpreinit(mp *m) {
|
||||
mp.gsignal = malg(32 * 1024)
|
||||
mp.gsignal.m = mp
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
func sigsave(p *sigset) {
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
func msigrestore(sigmask sigset) {
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
//go:nowritebarrierrec
|
||||
func clearSignalHandlers() {
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
func sigblock(exiting bool) {
|
||||
}
|
||||
|
||||
// Called to initialize a new m (including the bootstrap m).
|
||||
// Called on the new thread, cannot allocate memory.
|
||||
func minit() {
|
||||
}
|
||||
|
||||
// Called from dropm to undo the effect of an minit.
|
||||
func unminit() {
|
||||
}
|
||||
|
||||
// Called from exitm, but not from drop, to undo the effect of thread-owned
|
||||
// resources in minit, semacreate, or elsewhere. Do not take locks after calling this.
|
||||
func mdestroy(mp *m) {
|
||||
}
|
||||
|
||||
func osinit() {
|
||||
// https://webassembly.github.io/spec/core/exec/runtime.html#memory-instances
|
||||
physPageSize = 64 * 1024
|
||||
initBloc()
|
||||
ncpu = 1
|
||||
getg().m.procid = 2
|
||||
}
|
||||
|
||||
// wasm has no signals
|
||||
const _NSIG = 0
|
||||
|
||||
func signame(sig uint32) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func crash() {
|
||||
*(*int32)(nil) = 0
|
||||
func usleep(usec uint32) {
|
||||
// TODO(neelance): implement usleep
|
||||
}
|
||||
|
||||
//go:wasmimport gojs runtime.getRandomData
|
||||
//go:noescape
|
||||
func getRandomData(r []byte)
|
||||
|
||||
func goenvs() {
|
||||
goenvs_unix()
|
||||
}
|
||||
|
||||
func initsig(preinit bool) {
|
||||
}
|
||||
|
||||
// May run with m.p==nil, so write barriers are not allowed.
|
||||
//
|
||||
//go:nowritebarrier
|
||||
func newosproc(mp *m) {
|
||||
throw("newosproc: not implemented")
|
||||
}
|
||||
|
||||
func setProcessCPUProfiler(hz int32) {}
|
||||
func setThreadCPUProfiler(hz int32) {}
|
||||
func sigdisable(uint32) {}
|
||||
func sigenable(uint32) {}
|
||||
func sigignore(uint32) {}
|
||||
|
||||
//go:linkname os_sigpipe os.sigpipe
|
||||
func os_sigpipe() {
|
||||
throw("too many writes on closed pipe")
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
func cputicks() int64 {
|
||||
// Currently cputicks() is used in blocking profiler and to seed runtime·fastrand().
|
||||
// runtime·nanotime() is a poor approximation of CPU ticks that is enough for the profiler.
|
||||
return nanotime()
|
||||
}
|
||||
|
||||
//go:linkname syscall_now syscall.now
|
||||
func syscall_now() (sec int64, nsec int32) {
|
||||
sec, nsec, _ = time_now()
|
||||
return
|
||||
}
|
||||
|
||||
// gsignalStack is unused on js.
|
||||
type gsignalStack struct{}
|
||||
|
||||
const preemptMSupported = false
|
||||
|
||||
func preemptM(mp *m) {
|
||||
// No threads, so nothing to do.
|
||||
}
|
||||
|
||||
// getcallerfp returns the address of the frame pointer in the callers frame or 0 if not implemented.
|
||||
func getcallerfp() uintptr { return 0 }
|
||||
|
251
src/runtime/os_wasip1.go
Normal file
251
src/runtime/os_wasip1.go
Normal file
@ -0,0 +1,251 @@
|
||||
// Copyright 2023 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build wasip1
|
||||
|
||||
package runtime
|
||||
|
||||
import "unsafe"
|
||||
|
||||
// GOARCH=wasm currently has 64 bits pointers, but the WebAssembly host expects
|
||||
// pointers to be 32 bits so we use this type alias to represent pointers in
|
||||
// structs and arrays passed as arguments to WASI functions.
|
||||
//
|
||||
// Note that the use of an integer type prevents the compiler from tracking
|
||||
// pointers passed to WASI functions, so we must use KeepAlive to explicitly
|
||||
// retain the objects that could otherwise be reclaimed by the GC.
|
||||
type uintptr32 = uint32
|
||||
|
||||
// https://github.com/WebAssembly/WASI/blob/a2b96e81c0586125cc4dc79a5be0b78d9a059925/legacy/preview1/docs.md#-size-u32
|
||||
type size = uint32
|
||||
|
||||
// https://github.com/WebAssembly/WASI/blob/a2b96e81c0586125cc4dc79a5be0b78d9a059925/legacy/preview1/docs.md#-errno-variant
|
||||
type errno = uint32
|
||||
|
||||
// https://github.com/WebAssembly/WASI/blob/a2b96e81c0586125cc4dc79a5be0b78d9a059925/legacy/preview1/docs.md#-filesize-u64
|
||||
type filesize = uint64
|
||||
|
||||
// https://github.com/WebAssembly/WASI/blob/a2b96e81c0586125cc4dc79a5be0b78d9a059925/legacy/preview1/docs.md#-timestamp-u64
|
||||
type timestamp = uint64
|
||||
|
||||
// https://github.com/WebAssembly/WASI/blob/a2b96e81c0586125cc4dc79a5be0b78d9a059925/legacy/preview1/docs.md#-clockid-variant
|
||||
type clockid = uint32
|
||||
|
||||
const (
|
||||
clockRealtime clockid = 0
|
||||
clockMonotonic clockid = 1
|
||||
)
|
||||
|
||||
// https://github.com/WebAssembly/WASI/blob/a2b96e81c0586125cc4dc79a5be0b78d9a059925/legacy/preview1/docs.md#-iovec-record
|
||||
type iovec struct {
|
||||
buf uintptr32
|
||||
bufLen size
|
||||
}
|
||||
|
||||
//go:wasmimport wasi_snapshot_preview1 proc_exit
|
||||
func exit(code int32)
|
||||
|
||||
//go:wasmimport wasi_snapshot_preview1 args_get
|
||||
//go:noescape
|
||||
func args_get(argv *uintptr32, argvBuf *byte) errno
|
||||
|
||||
//go:wasmimport wasi_snapshot_preview1 args_sizes_get
|
||||
//go:noescape
|
||||
func args_sizes_get(argc *size, argvBufLen *size) errno
|
||||
|
||||
//go:wasmimport wasi_snapshot_preview1 clock_time_get
|
||||
//go:noescape
|
||||
func clock_time_get(clock_id clockid, precision timestamp, time *timestamp) errno
|
||||
|
||||
//go:wasmimport wasi_snapshot_preview1 environ_get
|
||||
//go:noescape
|
||||
func environ_get(environ *uintptr32, environBuf *byte) errno
|
||||
|
||||
//go:wasmimport wasi_snapshot_preview1 environ_sizes_get
|
||||
//go:noescape
|
||||
func environ_sizes_get(environCount *size, environBufLen *size) errno
|
||||
|
||||
//go:wasmimport wasi_snapshot_preview1 fd_write
|
||||
//go:noescape
|
||||
func fd_write(fd int32, iovs *iovec, iovsLen size, nwritten *size) errno
|
||||
|
||||
//go:wasmimport wasi_snapshot_preview1 random_get
|
||||
//go:noescape
|
||||
func random_get(buf *byte, bufLen size) errno
|
||||
|
||||
type eventtype = uint8
|
||||
|
||||
const (
|
||||
eventtypeClock eventtype = iota
|
||||
eventtypeFdRead
|
||||
eventtypeFdWrite
|
||||
)
|
||||
|
||||
type eventrwflags = uint16
|
||||
|
||||
const (
|
||||
fdReadwriteHangup eventrwflags = 1 << iota
|
||||
)
|
||||
|
||||
type userdata = uint64
|
||||
|
||||
// The go:wasmimport directive currently does not accept values of type uint16
|
||||
// in arguments or returns of the function signature. Most WASI imports return
|
||||
// an errno value, which we have to define as uint32 because of that limitation.
|
||||
// However, the WASI errno type is intended to be a 16 bits integer, and in the
|
||||
// event struct the error field should be of type errno. If we used the errno
|
||||
// type for the error field it would result in a mismatching field alignment and
|
||||
// struct size because errno is declared as a 32 bits type, so we declare the
|
||||
// error field as a plain uint16.
|
||||
type event struct {
|
||||
userdata userdata
|
||||
error uint16
|
||||
typ eventtype
|
||||
fdReadwrite eventFdReadwrite
|
||||
}
|
||||
|
||||
type eventFdReadwrite struct {
|
||||
nbytes filesize
|
||||
flags eventrwflags
|
||||
}
|
||||
|
||||
type subclockflags = uint16
|
||||
|
||||
const (
|
||||
subscriptionClockAbstime subclockflags = 1 << iota
|
||||
)
|
||||
|
||||
type subscriptionClock struct {
|
||||
id clockid
|
||||
timeout timestamp
|
||||
precision timestamp
|
||||
flags subclockflags
|
||||
}
|
||||
|
||||
type subscription struct {
|
||||
userdata userdata
|
||||
u subscriptionUnion
|
||||
}
|
||||
|
||||
type subscriptionUnion [5]uint64
|
||||
|
||||
func (u *subscriptionUnion) eventtype() *eventtype {
|
||||
return (*eventtype)(unsafe.Pointer(&u[0]))
|
||||
}
|
||||
|
||||
func (u *subscriptionUnion) subscriptionClock() *subscriptionClock {
|
||||
return (*subscriptionClock)(unsafe.Pointer(&u[1]))
|
||||
}
|
||||
|
||||
//go:wasmimport wasi_snapshot_preview1 poll_oneoff
|
||||
//go:noescape
|
||||
func poll_oneoff(in *subscription, out *event, nsubscriptions size, nevents *size) errno
|
||||
|
||||
func write1(fd uintptr, p unsafe.Pointer, n int32) int32 {
|
||||
iov := iovec{
|
||||
buf: uintptr32(uintptr(p)),
|
||||
bufLen: size(n),
|
||||
}
|
||||
var nwritten size
|
||||
if fd_write(int32(fd), &iov, 1, &nwritten) != 0 {
|
||||
throw("fd_write failed")
|
||||
}
|
||||
return int32(nwritten)
|
||||
}
|
||||
|
||||
func usleep(usec uint32) {
|
||||
var in subscription
|
||||
var out event
|
||||
var nevents size
|
||||
|
||||
eventtype := in.u.eventtype()
|
||||
*eventtype = eventtypeClock
|
||||
|
||||
subscription := in.u.subscriptionClock()
|
||||
subscription.id = clockMonotonic
|
||||
subscription.timeout = timestamp(usec) * 1e3
|
||||
subscription.precision = 1e3
|
||||
|
||||
if poll_oneoff(&in, &out, 1, &nevents) != 0 {
|
||||
throw("wasi_snapshot_preview1.poll_oneoff")
|
||||
}
|
||||
}
|
||||
|
||||
func getRandomData(r []byte) {
|
||||
if random_get(&r[0], size(len(r))) != 0 {
|
||||
throw("random_get failed")
|
||||
}
|
||||
}
|
||||
|
||||
func goenvs() {
|
||||
// arguments
|
||||
var argc size
|
||||
var argvBufLen size
|
||||
if args_sizes_get(&argc, &argvBufLen) != 0 {
|
||||
throw("args_sizes_get failed")
|
||||
}
|
||||
|
||||
argslice = make([]string, argc)
|
||||
if argc > 0 {
|
||||
argv := make([]uintptr32, argc)
|
||||
argvBuf := make([]byte, argvBufLen)
|
||||
if args_get(&argv[0], &argvBuf[0]) != 0 {
|
||||
throw("args_get failed")
|
||||
}
|
||||
|
||||
for i := range argslice {
|
||||
start := argv[i] - uintptr32(uintptr(unsafe.Pointer(&argvBuf[0])))
|
||||
end := start
|
||||
for argvBuf[end] != 0 {
|
||||
end++
|
||||
}
|
||||
argslice[i] = string(argvBuf[start:end])
|
||||
}
|
||||
}
|
||||
|
||||
// environment
|
||||
var environCount size
|
||||
var environBufLen size
|
||||
if environ_sizes_get(&environCount, &environBufLen) != 0 {
|
||||
throw("environ_sizes_get failed")
|
||||
}
|
||||
|
||||
envs = make([]string, environCount)
|
||||
if environCount > 0 {
|
||||
environ := make([]uintptr32, environCount)
|
||||
environBuf := make([]byte, environBufLen)
|
||||
if environ_get(&environ[0], &environBuf[0]) != 0 {
|
||||
throw("environ_get failed")
|
||||
}
|
||||
|
||||
for i := range envs {
|
||||
start := environ[i] - uintptr32(uintptr(unsafe.Pointer(&environBuf[0])))
|
||||
end := start
|
||||
for environBuf[end] != 0 {
|
||||
end++
|
||||
}
|
||||
envs[i] = string(environBuf[start:end])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func walltime() (sec int64, nsec int32) {
|
||||
return walltime1()
|
||||
}
|
||||
|
||||
func walltime1() (sec int64, nsec int32) {
|
||||
var time timestamp
|
||||
if clock_time_get(clockRealtime, 0, &time) != 0 {
|
||||
throw("clock_time_get failed")
|
||||
}
|
||||
return int64(time / 1000000000), int32(time % 1000000000)
|
||||
}
|
||||
|
||||
func nanotime1() int64 {
|
||||
var time timestamp
|
||||
if clock_time_get(clockMonotonic, 0, &time) != 0 {
|
||||
throw("clock_time_get failed")
|
||||
}
|
||||
return int64(time)
|
||||
}
|
152
src/runtime/os_wasm.go
Normal file
152
src/runtime/os_wasm.go
Normal file
@ -0,0 +1,152 @@
|
||||
// Copyright 2023 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"runtime/internal/atomic"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func osinit() {
|
||||
// https://webassembly.github.io/spec/core/exec/runtime.html#memory-instances
|
||||
physPageSize = 64 * 1024
|
||||
initBloc()
|
||||
ncpu = 1
|
||||
getg().m.procid = 2
|
||||
}
|
||||
|
||||
const _SIGSEGV = 0xb
|
||||
|
||||
func sigpanic() {
|
||||
gp := getg()
|
||||
if !canpanic() {
|
||||
throw("unexpected signal during runtime execution")
|
||||
}
|
||||
|
||||
// js only invokes the exception handler for memory faults.
|
||||
gp.sig = _SIGSEGV
|
||||
panicmem()
|
||||
}
|
||||
|
||||
// func exitThread(wait *uint32)
|
||||
// FIXME: wasm doesn't have atomic yet
|
||||
func exitThread(wait *atomic.Uint32)
|
||||
|
||||
type mOS struct{}
|
||||
|
||||
func osyield()
|
||||
|
||||
//go:nosplit
|
||||
func osyield_no_g() {
|
||||
osyield()
|
||||
}
|
||||
|
||||
type sigset struct{}
|
||||
|
||||
// Called to initialize a new m (including the bootstrap m).
|
||||
// Called on the parent thread (main thread in case of bootstrap), can allocate memory.
|
||||
func mpreinit(mp *m) {
|
||||
mp.gsignal = malg(32 * 1024)
|
||||
mp.gsignal.m = mp
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
func usleep_no_g(usec uint32) {
|
||||
usleep(usec)
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
func sigsave(p *sigset) {
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
func msigrestore(sigmask sigset) {
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
//go:nowritebarrierrec
|
||||
func clearSignalHandlers() {
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
func sigblock(exiting bool) {
|
||||
}
|
||||
|
||||
// Called to initialize a new m (including the bootstrap m).
|
||||
// Called on the new thread, cannot allocate memory.
|
||||
func minit() {
|
||||
}
|
||||
|
||||
// Called from dropm to undo the effect of an minit.
|
||||
func unminit() {
|
||||
}
|
||||
|
||||
// Called from exitm, but not from drop, to undo the effect of thread-owned
|
||||
// resources in minit, semacreate, or elsewhere. Do not take locks after calling this.
|
||||
func mdestroy(mp *m) {
|
||||
}
|
||||
|
||||
// wasm has no signals
|
||||
const _NSIG = 0
|
||||
|
||||
func signame(sig uint32) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func crash() {
|
||||
*(*int32)(nil) = 0
|
||||
}
|
||||
|
||||
func initsig(preinit bool) {
|
||||
}
|
||||
|
||||
// May run with m.p==nil, so write barriers are not allowed.
|
||||
//
|
||||
//go:nowritebarrier
|
||||
func newosproc(mp *m) {
|
||||
throw("newosproc: not implemented")
|
||||
}
|
||||
|
||||
//go:linkname os_sigpipe os.sigpipe
|
||||
func os_sigpipe() {
|
||||
throw("too many writes on closed pipe")
|
||||
}
|
||||
|
||||
//go:linkname syscall_now syscall.now
|
||||
func syscall_now() (sec int64, nsec int32) {
|
||||
sec, nsec, _ = time_now()
|
||||
return
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
func cputicks() int64 {
|
||||
// Currently cputicks() is used in blocking profiler and to seed runtime·fastrand().
|
||||
// runtime·nanotime() is a poor approximation of CPU ticks that is enough for the profiler.
|
||||
// TODO: need more entropy to better seed fastrand.
|
||||
return nanotime()
|
||||
}
|
||||
|
||||
// gsignalStack is unused on js.
|
||||
type gsignalStack struct{}
|
||||
|
||||
const preemptMSupported = false
|
||||
|
||||
func preemptM(mp *m) {
|
||||
// No threads, so nothing to do.
|
||||
}
|
||||
|
||||
// getcallerfp returns the address of the frame pointer in the callers frame or 0 if not implemented.
|
||||
func getcallerfp() uintptr { return 0 }
|
||||
|
||||
func setProcessCPUProfiler(hz int32) {}
|
||||
func setThreadCPUProfiler(hz int32) {}
|
||||
func sigdisable(uint32) {}
|
||||
func sigenable(uint32) {}
|
||||
func sigignore(uint32) {}
|
||||
|
||||
// Stubs so tests can link correctly. These should never be called.
|
||||
func open(name *byte, mode, perm int32) int32 { panic("not implemented") }
|
||||
func closefd(fd int32) int32 { panic("not implemented") }
|
||||
func read(fd int32, p unsafe.Pointer, n int32) int32 { panic("not implemented") }
|
@ -448,6 +448,8 @@ func testCPUProfile(t *testing.T, matches profileMatchFunc, f func(dur time.Dura
|
||||
t.Logf("uname -a: %v", vers)
|
||||
case "plan9":
|
||||
t.Skip("skipping on plan9")
|
||||
case "wasip1":
|
||||
t.Skip("skipping on wasip1")
|
||||
}
|
||||
|
||||
broken := cpuProfilingBroken()
|
||||
@ -1457,6 +1459,8 @@ func containsCountsLabels(prof *profile.Profile, countLabels map[int64]map[strin
|
||||
}
|
||||
|
||||
func TestGoroutineProfileConcurrency(t *testing.T) {
|
||||
testenv.MustHaveParallelism(t)
|
||||
|
||||
goroutineProf := Lookup("goroutine")
|
||||
|
||||
profilerCalls := func(s string) int {
|
||||
@ -1785,6 +1789,7 @@ func TestCPUProfileLabel(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestLabelRace(t *testing.T) {
|
||||
testenv.MustHaveParallelism(t)
|
||||
// Test the race detector annotations for synchronization
|
||||
// between setting labels and consuming them from the
|
||||
// profile.
|
||||
@ -1809,6 +1814,7 @@ func TestLabelRace(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGoroutineProfileLabelRace(t *testing.T) {
|
||||
testenv.MustHaveParallelism(t)
|
||||
// Test the race detector annotations for synchronization
|
||||
// between setting labels and consuming them from the
|
||||
// goroutine profile. See issue #50292.
|
||||
|
@ -131,7 +131,7 @@ func testPCs(t *testing.T) (addr1, addr2 uint64, map1, map2 *profile.Mapping) {
|
||||
BuildID: peBuildID(exe),
|
||||
HasFunctions: true,
|
||||
}
|
||||
case "js":
|
||||
case "js", "wasip1":
|
||||
addr1 = uint64(abi.FuncPCABIInternal(f1))
|
||||
addr2 = uint64(abi.FuncPCABIInternal(f2))
|
||||
default:
|
||||
|
@ -207,8 +207,8 @@ func TestSetPanicOnFault(t *testing.T) {
|
||||
//
|
||||
//go:nocheckptr
|
||||
func testSetPanicOnFault(t *testing.T, addr uintptr, nfault *int) {
|
||||
if GOOS == "js" {
|
||||
t.Skip("js does not support catching faults")
|
||||
if GOOS == "js" || GOOS == "wasip1" {
|
||||
t.Skip(GOOS + " does not support catching faults")
|
||||
}
|
||||
|
||||
defer func() {
|
||||
|
@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !plan9 && !windows && !js
|
||||
//go:build !plan9 && !windows && !js && !wasip1
|
||||
|
||||
package runtime_test
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !aix && !darwin && !js && !openbsd && !plan9 && !solaris && !windows
|
||||
//go:build !aix && !darwin && !js && !openbsd && !plan9 && !solaris && !wasip1 && !windows
|
||||
|
||||
package runtime
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !aix && !darwin && !freebsd && !openbsd && !plan9 && !solaris
|
||||
//go:build !aix && !darwin && !freebsd && !openbsd && !plan9 && !solaris && !wasip1
|
||||
|
||||
package runtime
|
||||
|
||||
|
@ -86,15 +86,6 @@ TEXT runtime·exitThread(SB), NOSPLIT, $0-0
|
||||
TEXT runtime·osyield(SB), NOSPLIT, $0-0
|
||||
UNDEF
|
||||
|
||||
TEXT runtime·usleep(SB), NOSPLIT, $0-0
|
||||
RET // TODO(neelance): implement usleep
|
||||
|
||||
TEXT runtime·currentMemory(SB), NOSPLIT, $0
|
||||
Get SP
|
||||
CurrentMemory
|
||||
I32Store ret+0(FP)
|
||||
RET
|
||||
|
||||
TEXT runtime·growMemory(SB), NOSPLIT, $0
|
||||
Get SP
|
||||
I32Load pages+0(FP)
|
||||
|
@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !aix && !darwin && !freebsd && !openbsd && !solaris && !windows && !(linux && amd64)
|
||||
//go:build !aix && !darwin && !freebsd && !openbsd && !solaris && !wasip1 && !windows && !(linux && amd64)
|
||||
|
||||
package runtime
|
||||
|
||||
|
@ -184,8 +184,9 @@ func testBrokenTimestamps(t *testing.T, data []byte) {
|
||||
}
|
||||
|
||||
func TestTraceStress(t *testing.T) {
|
||||
if runtime.GOOS == "js" {
|
||||
t.Skip("no os.Pipe on js")
|
||||
switch runtime.GOOS {
|
||||
case "js", "wasip1":
|
||||
t.Skip("no os.Pipe on " + runtime.GOOS)
|
||||
}
|
||||
if IsEnabled() {
|
||||
t.Skip("skipping because -test.trace is set")
|
||||
@ -348,8 +349,9 @@ func isMemoryConstrained() bool {
|
||||
// Do a bunch of various stuff (timers, GC, network, etc) in a separate goroutine.
|
||||
// And concurrently with all that start/stop trace 3 times.
|
||||
func TestTraceStressStartStop(t *testing.T) {
|
||||
if runtime.GOOS == "js" {
|
||||
t.Skip("no os.Pipe on js")
|
||||
switch runtime.GOOS {
|
||||
case "js", "wasip1":
|
||||
t.Skip("no os.Pipe on " + runtime.GOOS)
|
||||
}
|
||||
if IsEnabled() {
|
||||
t.Skip("skipping because -test.trace is set")
|
||||
|
Loading…
Reference in New Issue
Block a user