Skip to content

Commit

Permalink
runtime: add support for os/signal
Browse files Browse the repository at this point in the history
This adds support for enabling and listening to signals on Linux and
MacOS.

TODO: also support disabling signals.
  • Loading branch information
aykevl committed Aug 2, 2024
1 parent 4884d68 commit d49e3db
Show file tree
Hide file tree
Showing 10 changed files with 108 additions and 15 deletions.
1 change: 1 addition & 0 deletions builder/musl.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ var Musl = Library{
"malloc/mallocng/*.c",
"mman/*.c",
"math/*.c",
"signal/" + arch + "/*.s",
"signal/*.c",
"stdio/*.c",
"string/*.c",
Expand Down
2 changes: 2 additions & 0 deletions compileopts/target.go
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,7 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
"-arch", arch,
"-platform_version", "macos", platformVersion, platformVersion,
)
spec.ExtraFiles = append(spec.ExtraFiles, "src/runtime/signal.c")
} else if goos == "linux" {
spec.Linker = "ld.lld"
spec.RTLib = "compiler-rt"
Expand All @@ -375,6 +376,7 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
// proper threading.
spec.CFlags = append(spec.CFlags, "-mno-outline-atomics")
}
spec.ExtraFiles = append(spec.ExtraFiles, "src/runtime/signal.c")
} else if goos == "windows" {
spec.Linker = "ld.lld"
spec.Libc = "mingw-w64"
Expand Down
2 changes: 1 addition & 1 deletion lib/macos-minimal-sdk
9 changes: 9 additions & 0 deletions main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ func TestBuild(t *testing.T) {
"oldgo/",
"print.go",
"reflect.go",
"signal/",
"slice.go",
"sort.go",
"stdlib.go",
Expand Down Expand Up @@ -202,6 +203,7 @@ func runPlatTests(options compileopts.Options, tests []string, t *testing.T) {
// isWebAssembly := strings.HasPrefix(spec.Triple, "wasm")
isWASI := strings.HasPrefix(options.Target, "wasi")
isWebAssembly := isWASI || strings.HasPrefix(options.Target, "wasm") || (options.Target == "" && strings.HasPrefix(options.GOARCH, "wasm"))
isBaremetal := options.Target == "simavr" || options.Target == "cortex-m-qemu" || options.Target == "riscv-qemu"

for _, name := range tests {
if options.GOOS == "linux" && (options.GOARCH == "arm" || options.GOARCH == "386") {
Expand Down Expand Up @@ -254,6 +256,13 @@ func runPlatTests(options compileopts.Options, tests []string, t *testing.T) {
continue
}
}
if isWebAssembly || isBaremetal || options.GOOS == "windows" {
switch name {
case "signal/":
// Signals only work on POSIX-like systems.
continue
}
}

name := name // redefine to avoid race condition
t.Run(name, func(t *testing.T) {
Expand Down
14 changes: 0 additions & 14 deletions src/os/signal/signal.go

This file was deleted.

38 changes: 38 additions & 0 deletions src/runtime/runtime_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -256,3 +256,41 @@ func growHeap() bool {
setHeapEnd(heapStart + heapSize)
return true
}

func init() {
// Set up a channel to receive signals into.
// A channel size of 1 should be sufficient in most cases, but using 4 just
// to be sure.
signalChan = make(chan uint32, 4)
}

var signalChan chan uint32

//go:linkname signal_enable os/signal.signal_enable
func signal_enable(s uint32) {
// It's easier to implement this function in C.
tinygo_signal_enable(s)
}

//export tinygo_signal_enable
func tinygo_signal_enable(s uint32)

// void tinygo_signal_handler(int sig);
//
//export tinygo_signal_handler
func tinygo_signal_handler(s int32) {
select {
case signalChan <- uint32(s):
default:
// TODO: we should handle this in a better way somehow.
// Maybe just ignore the signal, assuming that nothing is reading from
// the notify channel?
runtimePanic("could not deliver signal: runtime internal channel is full")
}
}

//go:linkname signal_recv os/signal.signal_recv
func signal_recv() uint32 {
// Function called from os/signal to get the next received signal.
return <-signalChan
}
19 changes: 19 additions & 0 deletions src/runtime/signal.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//go:build none

// Ignore the //go:build above. This file is manually included on Linux and
// MacOS to provide os/signal support.

#include <stdint.h>
#include <signal.h>
#include <unistd.h>

// Signal handler in the runtime.
void tinygo_signal_handler(int sig);

// Enable a signal from the runtime.
void tinygo_signal_enable(uint32_t sig) {
struct sigaction act = { 0 };
act.sa_flags = SA_SIGINFO;
act.sa_handler = &tinygo_signal_handler;
sigaction(sig, &act, NULL);
}
2 changes: 2 additions & 0 deletions src/syscall/syscall_libc_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ const (
SIGPIPE Signal = 13 /* write on a pipe with no one to read it */
SIGTERM Signal = 15 /* software termination signal from kill */
SIGCHLD Signal = 20 /* to parent on child stop or exit */
SIGUSR1 Signal = 30 /* user defined signal 1 */
SIGUSR2 Signal = 31 /* user defined signal 2 */
)

func (s Signal) Signal() {}
Expand Down
2 changes: 2 additions & 0 deletions testdata/signal/out.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
got expected signal
exiting signal program
34 changes: 34 additions & 0 deletions testdata/signal/signal.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package main

// Test POSIX signals.
// TODO: run `tinygo test os/signal` instead, once CGo errno return values are
// supported.

import (
"os"
"os/signal"
"syscall"
"time"
)

func main() {
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGUSR1)

// Wait for signals to arrive.
go func() {
for sig := range c {
if sig == syscall.SIGUSR1 {
println("got expected signal")
} else {
println("got signal:", sig.String())
}
}
}()

// Send the signal.
syscall.Kill(syscall.Getpid(), syscall.SIGUSR1)

time.Sleep(time.Millisecond * 100)
println("exiting signal program")
}

0 comments on commit d49e3db

Please sign in to comment.