diff options
author | Ayke van Laethem <[email protected]> | 2024-11-30 09:24:22 +0100 |
---|---|---|
committer | Ayke <[email protected]> | 2024-12-01 07:40:22 +0100 |
commit | 09a22ac4b48b5a008cedc527c70cf41117a6b338 (patch) | |
tree | cf281ce7ff1e8287fd4a04362d118f168893def1 /src | |
parent | aed555d858c163fd96c12db3d5ec376cc5261e73 (diff) | |
download | tinygo-09a22ac4b48b5a008cedc527c70cf41117a6b338.tar.gz tinygo-09a22ac4b48b5a008cedc527c70cf41117a6b338.zip |
runtime: make signals parallelism-safe
Diffstat (limited to 'src')
-rw-r--r-- | src/runtime/runtime_unix.go | 24 |
1 files changed, 18 insertions, 6 deletions
diff --git a/src/runtime/runtime_unix.go b/src/runtime/runtime_unix.go index d9cf7e2c0..e6f81778d 100644 --- a/src/runtime/runtime_unix.go +++ b/src/runtime/runtime_unix.go @@ -362,7 +362,12 @@ func signal_enable(s uint32) { // receivedSignals into a uint32 array. runtimePanicAt(returnAddress(0), "unsupported signal number") } + + // This is intentonally a non-atomic store. This is safe, since hasSignals + // is only used in waitForEvents which is only called when there's a + // scheduler (and therefore there is no parallelism). hasSignals = true + // It's easier to implement this function in C. tinygo_signal_enable(s) } @@ -391,6 +396,9 @@ func signal_disable(s uint32) { func signal_waitUntilIdle() { // Wait until signal_recv has processed all signals. for receivedSignals.Load() != 0 { + // TODO: this becomes a busy loop when using threads. + // We might want to pause until signal_recv has no more incoming signals + // to process. Gosched() } } @@ -434,7 +442,7 @@ func tinygo_signal_handler(s int32) { // Task waiting for a signal to arrive, or nil if it is running or there are no // signals. -var signalRecvWaiter *task.Task +var signalRecvWaiter atomic.Pointer[task.Task] //go:linkname signal_recv os/signal.signal_recv func signal_recv() uint32 { @@ -443,7 +451,10 @@ func signal_recv() uint32 { val := receivedSignals.Load() if val == 0 { // There are no signals to receive. Sleep until there are. - signalRecvWaiter = task.Current() + if signalRecvWaiter.Swap(task.Current()) != nil { + // We expect only a single goroutine to call signal_recv. + runtimePanic("signal_recv called concurrently") + } task.Pause() continue } @@ -474,10 +485,11 @@ func signal_recv() uint32 { // Return true if it was reactivated (and therefore the scheduler should run // again), and false otherwise. func checkSignals() bool { - if receivedSignals.Load() != 0 && signalRecvWaiter != nil { - scheduleTask(signalRecvWaiter) - signalRecvWaiter = nil - return true + if receivedSignals.Load() != 0 { + if waiter := signalRecvWaiter.Swap(nil); waiter != nil { + scheduleTask(waiter) + return true + } } return false } |