-
Notifications
You must be signed in to change notification settings - Fork 923
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
runtime: implement race-free signals using futexes
- Loading branch information
Showing
11 changed files
with
238 additions
and
178 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Submodule macos-minimal-sdk
updated
from 4e4113 to 9b6940
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
package futex | ||
|
||
// Cross platform futex implementation. | ||
// Futexes are supported on all major operating systems and on WebAssembly. | ||
// | ||
// For more information, see: https://outerproduct.net/futex-dictionary.html | ||
|
||
import ( | ||
"sync/atomic" | ||
"unsafe" | ||
) | ||
|
||
// A futex is a way for userspace to wait with the pointer as the key, and for | ||
// another thread to wake one or all waiting threads keyed on the same pointer. | ||
// | ||
// A futex does not change the underlying value, it only reads it before going | ||
// to sleep (atomically) to prevent lost wake-ups. | ||
type Futex struct { | ||
atomic.Uint32 | ||
} | ||
|
||
// Atomically check for cmp to still be equal to the futex value and if so, go | ||
// to sleep. Return true if we were definitely awoken by a call to Wake or | ||
// WakeAll, and false if we can't be sure of that. | ||
func (f *Futex) Wait(cmp uint32) bool { | ||
tinygo_futex_wait((*uint32)(unsafe.Pointer(&f.Uint32)), cmp) | ||
|
||
// We *could* detect a zero return value from the futex system call which | ||
// would indicate we got awoken by a Wake or WakeAll call. However, this is | ||
// what the manual page has to say: | ||
// | ||
// > Note that a wake-up can also be caused by common futex usage patterns | ||
// > in unrelated code that happened to have previously used the futex | ||
// > word's memory location (e.g., typical futex-based implementations of | ||
// > Pthreads mutexes can cause this under some conditions). Therefore, | ||
// > callers should always conservatively assume that a return value of 0 | ||
// > can mean a spurious wake-up, and use the futex word's value (i.e., the | ||
// > user-space synchronization scheme) to decide whether to continue to | ||
// > block or not. | ||
// | ||
// I'm not sure whether we do anything like pthread does, so to be on the | ||
// safe side we say we don't know whether the wakeup was spurious or not and | ||
// return false. | ||
return false | ||
} | ||
|
||
// Like Wait, but times out after the number of nanoseconds in timeout. | ||
// If timeout is 0, it may or may not be treated as Wait with infinite timeout. | ||
// Therefore, make sure the timeout value is non-zero. | ||
func (f *Futex) WaitUntil(cmp uint32, timeout uint64) { | ||
tinygo_futex_wait_timeout((*uint32)(unsafe.Pointer(&f.Uint32)), cmp, timeout) | ||
} | ||
|
||
// Wake a single waiter. | ||
func (f *Futex) Wake() { | ||
tinygo_futex_wake((*uint32)(unsafe.Pointer(&f.Uint32))) | ||
} | ||
|
||
// Wake all waiters. | ||
func (f *Futex) WakeAll() { | ||
tinygo_futex_wake_all((*uint32)(unsafe.Pointer(&f.Uint32))) | ||
} | ||
|
||
//export tinygo_futex_wait | ||
func tinygo_futex_wait(addr *uint32, cmp uint32) | ||
|
||
//export tinygo_futex_wait_timeout | ||
func tinygo_futex_wait_timeout(addr *uint32, cmp uint32, timeout uint64) | ||
|
||
//export tinygo_futex_wake | ||
func tinygo_futex_wake(addr *uint32) | ||
|
||
//export tinygo_futex_wake_all | ||
func tinygo_futex_wake_all(addr *uint32) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
//go:build none | ||
|
||
// This file is manually included, to avoid CGo which would cause a circular | ||
// import. | ||
|
||
#include <stdint.h> | ||
|
||
int __ulock_wait(uint32_t operation, void *addr, uint64_t value, uint32_t timeout_us); | ||
int __ulock_wait2(uint32_t operation, void *addr, uint64_t value, uint64_t timeout_ns, uint64_t value2); | ||
int __ulock_wake(uint32_t operation, void *addr, uint64_t wake_value); | ||
|
||
// Operation code. | ||
#define UL_COMPARE_AND_WAIT 1 | ||
|
||
// Flags to the operation value. | ||
#define ULF_WAKE_ALL 0x00000100 | ||
#define ULF_NO_ERRNO 0x01000000 | ||
|
||
void tinygo_futex_wait(uint32_t *addr, uint32_t cmp) { | ||
__ulock_wait(UL_COMPARE_AND_WAIT|ULF_NO_ERRNO, addr, (uint64_t)cmp, 0); | ||
} | ||
|
||
void tinygo_futex_wait_timeout(uint32_t *addr, uint32_t cmp, uint64_t timeout) { | ||
// Note: __ulock_wait2 is available since MacOS 11. | ||
// I think that's fine, since the version before that (MacOS 10.15) is EOL | ||
// since 2022. Though if needed, we could certainly use __ulock_wait instead | ||
// and deal with the smaller timeout value. | ||
__ulock_wait2(UL_COMPARE_AND_WAIT|ULF_NO_ERRNO, addr, (uint64_t)cmp, timeout, 0); | ||
} | ||
|
||
void tinygo_futex_wake(uint32_t *addr) { | ||
__ulock_wake(UL_COMPARE_AND_WAIT|ULF_NO_ERRNO, addr, 0); | ||
} | ||
|
||
void tinygo_futex_wake_all(uint32_t *addr) { | ||
__ulock_wake(UL_COMPARE_AND_WAIT|ULF_NO_ERRNO|ULF_WAKE_ALL, addr, 0); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
//go:build none | ||
|
||
// This file is manually included, to avoid CGo which would cause a circular | ||
// import. | ||
|
||
#include <limits.h> | ||
#include <stdint.h> | ||
#include <sys/syscall.h> | ||
#include <time.h> | ||
#include <unistd.h> | ||
|
||
#define FUTEX_WAIT 0 | ||
#define FUTEX_WAKE 1 | ||
#define FUTEX_PRIVATE_FLAG 128 | ||
|
||
void tinygo_futex_wait(uint32_t *addr, uint32_t cmp) { | ||
syscall(SYS_futex, addr, FUTEX_WAIT|FUTEX_PRIVATE_FLAG, cmp, NULL, NULL, 0); | ||
} | ||
|
||
void tinygo_futex_wait_timeout(uint32_t *addr, uint32_t cmp, uint64_t timeout) { | ||
struct timespec ts = {0}; | ||
ts.tv_sec = timeout / 1000000000; | ||
ts.tv_nsec = timeout % 1000000000; | ||
syscall(SYS_futex, addr, FUTEX_WAIT|FUTEX_PRIVATE_FLAG, cmp, &ts, NULL, 0); | ||
} | ||
|
||
void tinygo_futex_wake(uint32_t *addr) { | ||
syscall(SYS_futex, addr, FUTEX_WAKE|FUTEX_PRIVATE_FLAG, 1, NULL, NULL, 0); | ||
} | ||
|
||
void tinygo_futex_wake_all(uint32_t *addr) { | ||
syscall(SYS_futex, addr, FUTEX_WAKE|FUTEX_PRIVATE_FLAG, INT_MAX, NULL, NULL, 0); | ||
} |
Oops, something went wrong.