darwin: add threading support and use it by default

This commit is contained in:
Ayke van Laethem
2025-01-28 15:12:50 +01:00
committed by Ron Evans
parent 93f40992c1
commit 0e43146d32
3 changed files with 44 additions and 4 deletions
+2 -1
View File
@@ -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")
+11
View File
@@ -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
+31 -3
View File
@@ -2,16 +2,28 @@
#define _GNU_SOURCE
#include <pthread.h>
#include <semaphore.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <unistd.h>
// BDWGC also uses SIGRTMIN+6 on Linux, which seems like a reasonable choice.
#ifdef __linux__
#include <semaphore.h>
// BDWGC also uses SIGRTMIN+6 on Linux, which seems like a reasonable choice.
#define taskPauseSignal (SIGRTMIN + 6)
#endif
#elif __APPLE__
#include <dispatch/dispatch.h>
// 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.
@@ -68,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);
@@ -92,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;
}