diff --git a/compileopts/target.go b/compileopts/target.go index 9aeb8d5776..662e6fb2b4 100644 --- a/compileopts/target.go +++ b/compileopts/target.go @@ -385,7 +385,7 @@ func defaultTarget(options *Options) (*TargetSpec, error) { platformVersion = "11.0.0" // first macosx platform with arm64 support } llvmvendor = "apple" - spec.Scheduler = "tasks" + spec.Scheduler = "threads" spec.Linker = "ld.lld" spec.Libc = "darwin-libSystem" // Use macosx* instead of darwin, otherwise darwin/arm64 will refer to @@ -399,6 +399,7 @@ func defaultTarget(options *Options) (*TargetSpec, error) { ) spec.ExtraFiles = append(spec.ExtraFiles, "src/internal/futex/futex_darwin.c", + "src/internal/task/task_threads.c", "src/runtime/os_darwin.c", "src/runtime/runtime_unix.c", "src/runtime/signal.c") diff --git a/src/internal/task/darwin.go b/src/internal/task/darwin.go new file mode 100644 index 0000000000..47d9172650 --- /dev/null +++ b/src/internal/task/darwin.go @@ -0,0 +1,11 @@ +//go:build darwin + +package task + +import "unsafe" + +// MacOS uses a pointer so unsafe.Pointer should be fine: +// +// typedef struct _opaque_pthread_t *__darwin_pthread_t; +// typedef __darwin_pthread_t pthread_t; +type threadID unsafe.Pointer diff --git a/src/internal/task/task_threads.c b/src/internal/task/task_threads.c index 6ada95fa84..c0eefbd29d 100644 --- a/src/internal/task/task_threads.c +++ b/src/internal/task/task_threads.c @@ -2,16 +2,28 @@ #define _GNU_SOURCE #include -#include #include #include #include #include -// BDWGC also uses SIGRTMIN+6 on Linux, which seems like a reasonable choice. #ifdef __linux__ +#include + +// BDWGC also uses SIGRTMIN+6 on Linux, which seems like a reasonable choice. #define taskPauseSignal (SIGRTMIN + 6) -#endif + +#elif __APPLE__ +#include +// SIGIO is for interrupt-driven I/O. +// I don't think anybody should be using this nowadays, so I think we can +// repurpose it as a signal for GC. +// BDWGC uses a special way to pause/resume other threads on MacOS, which may be +// better but needs more work. Using signal keeps the code similar between Linux +// and MacOS. +#define taskPauseSignal SIGIO + +#endif // __linux__, __APPLE__ // Pointer to the current task.Task structure. // Ideally the entire task.Task structure would be a thread-local variable but @@ -23,7 +35,11 @@ struct state_pass { void *args; void *task; uintptr_t *stackTop; + #if __APPLE__ + dispatch_semaphore_t startlock; + #else sem_t startlock; + #endif }; // Handle the GC pause in Go. @@ -41,8 +57,7 @@ void tinygo_task_init(void *mainTask, pthread_t *thread, int *numCPU, void *cont // Register the "GC pause" signal for the entire process. // Using pthread_kill, we can still send the signal to a specific thread. struct sigaction act = { 0 }; - act.sa_flags = SA_SIGINFO; - act.sa_handler = &tinygo_task_gc_pause; + act.sa_handler = tinygo_task_gc_pause; sigaction(taskPauseSignal, &act, NULL); // Obtain the number of CPUs available on program start (for NumCPU). @@ -69,7 +84,11 @@ static void* start_wrapper(void *arg) { // Notify the caller that the thread has successfully started and // initialized. + #if __APPLE__ + dispatch_semaphore_signal(state->startlock); + #else sem_post(&state->startlock); + #endif // Run the goroutine function. start(args); @@ -93,11 +112,19 @@ int tinygo_task_start(uintptr_t fn, void *args, void *task, pthread_t *thread, u .task = task, .stackTop = stackTop, }; + #if __APPLE__ + state.startlock = dispatch_semaphore_create(0); + #else sem_init(&state.startlock, 0, 0); + #endif int result = pthread_create(thread, NULL, &start_wrapper, &state); // Wait until the thread has been created and read all state_pass variables. + #if __APPLE__ + dispatch_semaphore_wait(state.startlock, DISPATCH_TIME_FOREVER); + #else sem_wait(&state.startlock); + #endif return result; } diff --git a/src/internal/task/task_threads.go b/src/internal/task/task_threads.go index db204cb4eb..b5d3c5ca47 100644 --- a/src/internal/task/task_threads.go +++ b/src/internal/task/task_threads.go @@ -220,7 +220,7 @@ func gcScanGlobals() var stackScanLock PMutex //export tinygo_task_gc_pause -func tingyo_task_gc_pause() { +func tingyo_task_gc_pause(sig int32) { // Wait until we get the signal to start scanning the stack. Current().state.gcSem.Wait()