diff --git a/.gitmodules b/.gitmodules index 29778d6709..91bd14a7d7 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,40 +1,41 @@ [submodule "lib/nrfx"] - path = lib/nrfx - url = https://github.com/NordicSemiconductor/nrfx.git + path = lib/nrfx + url = https://github.com/NordicSemiconductor/nrfx.git [submodule "lib/CMSIS"] - path = lib/CMSIS - url = https://github.com/ARM-software/CMSIS.git + path = lib/CMSIS + url = https://github.com/ARM-software/CMSIS.git [submodule "lib/avr"] - path = lib/avr - url = https://github.com/avr-rust/avr-mcu.git + path = lib/avr + url = https://github.com/avr-rust/avr-mcu.git [submodule "lib/cmsis-svd"] - path = lib/cmsis-svd - url = https://github.com/cmsis-svd/cmsis-svd-data.git - branch = main + path = lib/cmsis-svd + url = https://github.com/cmsis-svd/cmsis-svd-data.git + branch = main [submodule "lib/wasi-libc"] - path = lib/wasi-libc - url = https://github.com/WebAssembly/wasi-libc + path = lib/wasi-libc + url = https://github.com/WebAssembly/wasi-libc [submodule "lib/picolibc"] - path = lib/picolibc - url = https://github.com/keith-packard/picolibc.git + path = lib/picolibc + url = https://github.com/keith-packard/picolibc.git [submodule "lib/stm32-svd"] - path = lib/stm32-svd - url = https://github.com/tinygo-org/stm32-svd + path = lib/stm32-svd + url = https://github.com/tinygo-org/stm32-svd [submodule "lib/musl"] - path = lib/musl - url = git://git.musl-libc.org/musl + path = lib/musl + url = git://git.musl-libc.org/musl [submodule "lib/binaryen"] - path = lib/binaryen - url = https://github.com/WebAssembly/binaryen.git + path = lib/binaryen + url = https://github.com/WebAssembly/binaryen.git [submodule "lib/mingw-w64"] - path = lib/mingw-w64 - url = https://github.com/mingw-w64/mingw-w64.git + path = lib/mingw-w64 + url = https://github.com/mingw-w64/mingw-w64.git [submodule "lib/macos-minimal-sdk"] - path = lib/macos-minimal-sdk - url = https://github.com/aykevl/macos-minimal-sdk.git -[submodule "lib/wasi-cli"] - path = lib/wasi-cli - url = https://github.com/WebAssembly/wasi-cli + path = lib/macos-minimal-sdk + url = https://github.com/aykevl/macos-minimal-sdk.git [submodule "src/net"] path = src/net - url = https://github.com/tinygo-org/net + url = https://github.com/tinygo-org/net.git + branch = dev +[submodule "lib/wasi-cli"] + path = lib/wasi-cli + url = https://github.com/WebAssembly/wasi-cli diff --git a/builder/musl.go b/builder/musl.go index ecae118e47..f00b6e0924 100644 --- a/builder/musl.go +++ b/builder/musl.go @@ -116,6 +116,7 @@ var libMusl = Library{ "env/*.c", "errno/*.c", "exit/*.c", + "fcntl/*.c", "internal/defsysinfo.c", "internal/libc.c", "internal/syscall_ret.c", diff --git a/src/runtime/netpoll_generic.go b/src/runtime/netpoll_generic.go index 123d190770..f5f26d84d9 100644 --- a/src/runtime/netpoll_generic.go +++ b/src/runtime/netpoll_generic.go @@ -6,6 +6,13 @@ package runtime +const ( + pollNoError = 0 // no error + pollErrClosing = 1 // descriptor is closed + pollErrTimeout = 2 // I/O timeout + pollErrNotPollable = 3 // general error polling descriptor +) + // Network poller descriptor. // // No heap pointers. @@ -16,13 +23,13 @@ type pollDesc struct{} //go:linkname poll_runtime_pollReset internal/poll.runtime_pollReset func poll_runtime_pollReset(pd *pollDesc, mode int) int { println("poll_runtime_pollReset not implemented", pd, mode) - return 1 + return pollErrClosing } //go:linkname poll_runtime_pollWait internal/poll.runtime_pollWait func poll_runtime_pollWait(pd *pollDesc, mode int) int { println("poll_runtime_pollWait not implemented", pd, mode) - return 1 + return pollErrClosing } //go:linkname poll_runtime_pollSetDeadline internal/poll.runtime_pollSetDeadline diff --git a/src/runtime/sync.go b/src/runtime/sync.go index 98c7cf92e1..3d302ec7d2 100644 --- a/src/runtime/sync.go +++ b/src/runtime/sync.go @@ -1,17 +1,72 @@ +// Copyright 2009 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 ( + "sync/atomic" + "unsafe" +) + // This file contains stub implementations for internal/poll. +// The official golang implementation states: +// +// "That is, don't think of these as semaphores. +// Think of them as a way to implement sleep and wakeup +// such that every sleep is paired with a single wakeup, +// even if, due to races, the wakeup happens before the sleep." +// +// This is an experimental and probably incomplete implementation of the +// semaphore system, tailed to the network use case. That means, that it does not +// implement the modularity that the semacquire/semacquire1 implementation model +// offers, which in fact is emitted here entirely. +// This means we assume the following constant settings from the golang standard +// library: lifo=false,profile=semaBlock,skipframe=0,reason=waitReasonSemaquire + +type semaRoot struct { + nwait atomic.Uint32 +} + +var semtable semTable + +// Prime to not correlate with any user patterns. +const semTabSize = 251 + +type semTable [semTabSize]struct { + root semaRoot + pad [64 - unsafe.Sizeof(semaRoot{})]byte // only 64 x86_64, make this variable +} + +func (t *semTable) rootFor(addr *uint32) *semaRoot { + return &t[(uintptr(unsafe.Pointer(addr))>>3)%semTabSize].root +} //go:linkname semacquire internal/poll.runtime_Semacquire func semacquire(sema *uint32) { - // TODO the "net" pkg calls this, so panic() isn't an option. Right - // now, just ignore the call. - // panic("todo: semacquire") + if cansemacquire(sema) { + return + } +} + +// Copied from src/runtime/sema.go +func cansemacquire(addr *uint32) bool { + for { + v := atomic.LoadUint32(addr) + if v == 0 { + return false + } + if atomic.CompareAndSwapUint32(addr, v, v-1) { + return true + } + } } //go:linkname semrelease internal/poll.runtime_Semrelease func semrelease(sema *uint32) { - // TODO the "net" pkg calls this, so panic() isn't an option. Right - // now, just ignore the call. - // panic("todo: semrelease") + root := semtable.rootFor(sema) + atomic.AddUint32(sema, 1) + if root.nwait.Load() == 0 { + return + } } diff --git a/testdata/net.go b/testdata/net.go index 746becbce4..2a7908fa1c 100644 --- a/testdata/net.go +++ b/testdata/net.go @@ -1,25 +1,19 @@ package main import ( - "bytes" - "fmt" + "io" "net" "strconv" - "time" ) // Test golang network package integration for tinygo. // This test is not exhaustive and only tests the basic functionality of the package. -const ( - TEST_PORT = 9000 -) - var ( testsPassed uint + lnPort int err error - sendBuf = &bytes.Buffer{} - recvBuf = &bytes.Buffer{} + recvBuf []byte ) var ( @@ -28,29 +22,30 @@ var ( func TestDialListen() { // listen thread + listenReady := make(chan bool, 1) go func() { - ln, err := net.Listen("tcp", ":"+strconv.FormatInt(TEST_PORT, 10)) + ln, err := net.Listen("tcp4", ":0") if err != nil { - fmt.Printf("error listening: %v\n", err) + println("error listening: ", err) return } + lnPort = ln.Addr().(*net.TCPAddr).Port + listenReady <- true conn, err := ln.Accept() if err != nil { - fmt.Printf("error accepting: %v\n", err) + println("error accepting:", err) return } - recvBuf.Reset() - _, err = conn.Read(recvBuf.Bytes()) - if err != nil { - fmt.Printf("error reading: %v\n", err) + recvBuf = make([]byte, len(testDialListenData)) + if _, err := io.ReadFull(conn, recvBuf); err != nil { + println("error reading: ", err) return } - // TODO: this is racy - if recvBuf.String() != string(testDialListenData) { - fmt.Printf("error: received data does not match sent data: '%s' != '%s'\n", recvBuf.String(), string(testDialListenData)) + if string(recvBuf) != string(testDialListenData) { + println("error: received data does not match sent data", string(recvBuf), " != ", string(testDialListenData)) return } conn.Close() @@ -58,25 +53,21 @@ func TestDialListen() { return }() - // hacky way to wait for the listener to start - time.Sleep(1 * time.Second) - - sendBuf.Reset() - fmt.Fprint(sendBuf, testDialListenData) - conn, err := net.Dial("tcp4", "127.0.0.1:"+strconv.FormatInt(TEST_PORT, 10)) + <-listenReady + conn, err := net.Dial("tcp4", "127.0.0.1:"+strconv.FormatInt(int64(lnPort), 10)) if err != nil { - fmt.Printf("error dialing: %v\n", err) + println("error dialing: ", err) return } - if _, err = conn.Write(sendBuf.Bytes()); err != nil { - fmt.Printf("error writing: %v\n", err) + if _, err = conn.Write(testDialListenData); err != nil { + println("error writing: ", err) return } } func main() { - fmt.Printf("test: net start\n") + println("test: net start") TestDialListen() - fmt.Printf("test: net end\n") + println("test: net end") }