Skip to content

Commit

Permalink
add preliminary semaphore implementation
Browse files Browse the repository at this point in the history
Signed-off-by: leongross <leon.gross@9elements.com>
  • Loading branch information
leongross committed Nov 8, 2024
1 parent 20fbf32 commit c3a2e91
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 65 deletions.
55 changes: 28 additions & 27 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions builder/musl.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
11 changes: 9 additions & 2 deletions src/runtime/netpoll_generic.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
Expand Down
67 changes: 61 additions & 6 deletions src/runtime/sync.go
Original file line number Diff line number Diff line change
@@ -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
}
}
51 changes: 21 additions & 30 deletions testdata/net.go
Original file line number Diff line number Diff line change
@@ -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 (
Expand All @@ -28,55 +22,52 @@ 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()

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")
}

0 comments on commit c3a2e91

Please sign in to comment.