Skip to content

Commit c3a2e91

Browse files
committed
add preliminary semaphore implementation
Signed-off-by: leongross <leon.gross@9elements.com>
1 parent 20fbf32 commit c3a2e91

File tree

5 files changed

+120
-65
lines changed

5 files changed

+120
-65
lines changed

.gitmodules

Lines changed: 28 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,41 @@
11
[submodule "lib/nrfx"]
2-
path = lib/nrfx
3-
url = https://github.com/NordicSemiconductor/nrfx.git
2+
path = lib/nrfx
3+
url = https://github.com/NordicSemiconductor/nrfx.git
44
[submodule "lib/CMSIS"]
5-
path = lib/CMSIS
6-
url = https://github.com/ARM-software/CMSIS.git
5+
path = lib/CMSIS
6+
url = https://github.com/ARM-software/CMSIS.git
77
[submodule "lib/avr"]
8-
path = lib/avr
9-
url = https://github.com/avr-rust/avr-mcu.git
8+
path = lib/avr
9+
url = https://github.com/avr-rust/avr-mcu.git
1010
[submodule "lib/cmsis-svd"]
11-
path = lib/cmsis-svd
12-
url = https://github.com/cmsis-svd/cmsis-svd-data.git
13-
branch = main
11+
path = lib/cmsis-svd
12+
url = https://github.com/cmsis-svd/cmsis-svd-data.git
13+
branch = main
1414
[submodule "lib/wasi-libc"]
15-
path = lib/wasi-libc
16-
url = https://github.com/WebAssembly/wasi-libc
15+
path = lib/wasi-libc
16+
url = https://github.com/WebAssembly/wasi-libc
1717
[submodule "lib/picolibc"]
18-
path = lib/picolibc
19-
url = https://github.com/keith-packard/picolibc.git
18+
path = lib/picolibc
19+
url = https://github.com/keith-packard/picolibc.git
2020
[submodule "lib/stm32-svd"]
21-
path = lib/stm32-svd
22-
url = https://github.com/tinygo-org/stm32-svd
21+
path = lib/stm32-svd
22+
url = https://github.com/tinygo-org/stm32-svd
2323
[submodule "lib/musl"]
24-
path = lib/musl
25-
url = git://git.musl-libc.org/musl
24+
path = lib/musl
25+
url = git://git.musl-libc.org/musl
2626
[submodule "lib/binaryen"]
27-
path = lib/binaryen
28-
url = https://github.com/WebAssembly/binaryen.git
27+
path = lib/binaryen
28+
url = https://github.com/WebAssembly/binaryen.git
2929
[submodule "lib/mingw-w64"]
30-
path = lib/mingw-w64
31-
url = https://github.com/mingw-w64/mingw-w64.git
30+
path = lib/mingw-w64
31+
url = https://github.com/mingw-w64/mingw-w64.git
3232
[submodule "lib/macos-minimal-sdk"]
33-
path = lib/macos-minimal-sdk
34-
url = https://github.com/aykevl/macos-minimal-sdk.git
35-
[submodule "lib/wasi-cli"]
36-
path = lib/wasi-cli
37-
url = https://github.com/WebAssembly/wasi-cli
33+
path = lib/macos-minimal-sdk
34+
url = https://github.com/aykevl/macos-minimal-sdk.git
3835
[submodule "src/net"]
3936
path = src/net
40-
url = https://github.com/tinygo-org/net
37+
url = https://github.com/tinygo-org/net.git
38+
branch = dev
39+
[submodule "lib/wasi-cli"]
40+
path = lib/wasi-cli
41+
url = https://github.com/WebAssembly/wasi-cli

builder/musl.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ var libMusl = Library{
116116
"env/*.c",
117117
"errno/*.c",
118118
"exit/*.c",
119+
"fcntl/*.c",
119120
"internal/defsysinfo.c",
120121
"internal/libc.c",
121122
"internal/syscall_ret.c",

src/runtime/netpoll_generic.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,13 @@
66

77
package runtime
88

9+
const (
10+
pollNoError = 0 // no error
11+
pollErrClosing = 1 // descriptor is closed
12+
pollErrTimeout = 2 // I/O timeout
13+
pollErrNotPollable = 3 // general error polling descriptor
14+
)
15+
916
// Network poller descriptor.
1017
//
1118
// No heap pointers.
@@ -16,13 +23,13 @@ type pollDesc struct{}
1623
//go:linkname poll_runtime_pollReset internal/poll.runtime_pollReset
1724
func poll_runtime_pollReset(pd *pollDesc, mode int) int {
1825
println("poll_runtime_pollReset not implemented", pd, mode)
19-
return 1
26+
return pollErrClosing
2027
}
2128

2229
//go:linkname poll_runtime_pollWait internal/poll.runtime_pollWait
2330
func poll_runtime_pollWait(pd *pollDesc, mode int) int {
2431
println("poll_runtime_pollWait not implemented", pd, mode)
25-
return 1
32+
return pollErrClosing
2633
}
2734

2835
//go:linkname poll_runtime_pollSetDeadline internal/poll.runtime_pollSetDeadline

src/runtime/sync.go

Lines changed: 61 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,72 @@
1+
// Copyright 2009 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
15
package runtime
26

7+
import (
8+
"sync/atomic"
9+
"unsafe"
10+
)
11+
312
// This file contains stub implementations for internal/poll.
13+
// The official golang implementation states:
14+
//
15+
// "That is, don't think of these as semaphores.
16+
// Think of them as a way to implement sleep and wakeup
17+
// such that every sleep is paired with a single wakeup,
18+
// even if, due to races, the wakeup happens before the sleep."
19+
//
20+
// This is an experimental and probably incomplete implementation of the
21+
// semaphore system, tailed to the network use case. That means, that it does not
22+
// implement the modularity that the semacquire/semacquire1 implementation model
23+
// offers, which in fact is emitted here entirely.
24+
// This means we assume the following constant settings from the golang standard
25+
// library: lifo=false,profile=semaBlock,skipframe=0,reason=waitReasonSemaquire
26+
27+
type semaRoot struct {
28+
nwait atomic.Uint32
29+
}
30+
31+
var semtable semTable
32+
33+
// Prime to not correlate with any user patterns.
34+
const semTabSize = 251
35+
36+
type semTable [semTabSize]struct {
37+
root semaRoot
38+
pad [64 - unsafe.Sizeof(semaRoot{})]byte // only 64 x86_64, make this variable
39+
}
40+
41+
func (t *semTable) rootFor(addr *uint32) *semaRoot {
42+
return &t[(uintptr(unsafe.Pointer(addr))>>3)%semTabSize].root
43+
}
444

545
//go:linkname semacquire internal/poll.runtime_Semacquire
646
func semacquire(sema *uint32) {
7-
// TODO the "net" pkg calls this, so panic() isn't an option. Right
8-
// now, just ignore the call.
9-
// panic("todo: semacquire")
47+
if cansemacquire(sema) {
48+
return
49+
}
50+
}
51+
52+
// Copied from src/runtime/sema.go
53+
func cansemacquire(addr *uint32) bool {
54+
for {
55+
v := atomic.LoadUint32(addr)
56+
if v == 0 {
57+
return false
58+
}
59+
if atomic.CompareAndSwapUint32(addr, v, v-1) {
60+
return true
61+
}
62+
}
1063
}
1164

1265
//go:linkname semrelease internal/poll.runtime_Semrelease
1366
func semrelease(sema *uint32) {
14-
// TODO the "net" pkg calls this, so panic() isn't an option. Right
15-
// now, just ignore the call.
16-
// panic("todo: semrelease")
67+
root := semtable.rootFor(sema)
68+
atomic.AddUint32(sema, 1)
69+
if root.nwait.Load() == 0 {
70+
return
71+
}
1772
}

testdata/net.go

Lines changed: 21 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,19 @@
11
package main
22

33
import (
4-
"bytes"
5-
"fmt"
4+
"io"
65
"net"
76
"strconv"
8-
"time"
97
)
108

119
// Test golang network package integration for tinygo.
1210
// This test is not exhaustive and only tests the basic functionality of the package.
1311

14-
const (
15-
TEST_PORT = 9000
16-
)
17-
1812
var (
1913
testsPassed uint
14+
lnPort int
2015
err error
21-
sendBuf = &bytes.Buffer{}
22-
recvBuf = &bytes.Buffer{}
16+
recvBuf []byte
2317
)
2418

2519
var (
@@ -28,55 +22,52 @@ var (
2822

2923
func TestDialListen() {
3024
// listen thread
25+
listenReady := make(chan bool, 1)
3126
go func() {
32-
ln, err := net.Listen("tcp", ":"+strconv.FormatInt(TEST_PORT, 10))
27+
ln, err := net.Listen("tcp4", ":0")
3328
if err != nil {
34-
fmt.Printf("error listening: %v\n", err)
29+
println("error listening: ", err)
3530
return
3631
}
32+
lnPort = ln.Addr().(*net.TCPAddr).Port
3733

34+
listenReady <- true
3835
conn, err := ln.Accept()
3936
if err != nil {
40-
fmt.Printf("error accepting: %v\n", err)
37+
println("error accepting:", err)
4138
return
4239
}
4340

44-
recvBuf.Reset()
45-
_, err = conn.Read(recvBuf.Bytes())
46-
if err != nil {
47-
fmt.Printf("error reading: %v\n", err)
41+
recvBuf = make([]byte, len(testDialListenData))
42+
if _, err := io.ReadFull(conn, recvBuf); err != nil {
43+
println("error reading: ", err)
4844
return
4945
}
5046

51-
// TODO: this is racy
52-
if recvBuf.String() != string(testDialListenData) {
53-
fmt.Printf("error: received data does not match sent data: '%s' != '%s'\n", recvBuf.String(), string(testDialListenData))
47+
if string(recvBuf) != string(testDialListenData) {
48+
println("error: received data does not match sent data", string(recvBuf), " != ", string(testDialListenData))
5449
return
5550
}
5651
conn.Close()
5752

5853
return
5954
}()
6055

61-
// hacky way to wait for the listener to start
62-
time.Sleep(1 * time.Second)
63-
64-
sendBuf.Reset()
65-
fmt.Fprint(sendBuf, testDialListenData)
66-
conn, err := net.Dial("tcp4", "127.0.0.1:"+strconv.FormatInt(TEST_PORT, 10))
56+
<-listenReady
57+
conn, err := net.Dial("tcp4", "127.0.0.1:"+strconv.FormatInt(int64(lnPort), 10))
6758
if err != nil {
68-
fmt.Printf("error dialing: %v\n", err)
59+
println("error dialing: ", err)
6960
return
7061
}
7162

72-
if _, err = conn.Write(sendBuf.Bytes()); err != nil {
73-
fmt.Printf("error writing: %v\n", err)
63+
if _, err = conn.Write(testDialListenData); err != nil {
64+
println("error writing: ", err)
7465
return
7566
}
7667
}
7768

7869
func main() {
79-
fmt.Printf("test: net start\n")
70+
println("test: net start")
8071
TestDialListen()
81-
fmt.Printf("test: net end\n")
72+
println("test: net end")
8273
}

0 commit comments

Comments
 (0)