-
Notifications
You must be signed in to change notification settings - Fork 915
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
WIP map every goroutine to a new OS thread
- Loading branch information
Showing
13 changed files
with
984 additions
and
2 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
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,9 @@ | ||
//go:build linux && !baremetal | ||
|
||
package task | ||
|
||
import "unsafe" | ||
|
||
// Musl uses a pointer (or unsigned long for C++) so unsafe.Pointer should be | ||
// fine. | ||
type threadID unsafe.Pointer |
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,32 @@ | ||
package task | ||
|
||
// Barebones semaphore implementation. | ||
// The main limitation is that if there are multiple waiters, a single Post() | ||
// call won't do anything. Only when Post() has been called to awaken all | ||
// waiters will the waiters proceed. | ||
// This limitation is not a problem when there will only be a single waiter. | ||
type Semaphore struct { | ||
futex Futex | ||
} | ||
|
||
// Post (unlock) the semaphore, incrementing the value in the semaphore. | ||
func (s *Semaphore) Post() { | ||
newValue := s.futex.Add(1) | ||
if newValue == 0 { | ||
s.futex.WakeAll() | ||
} | ||
} | ||
|
||
// Wait (lock) the semaphore, decrementing the value in the semaphore. | ||
func (s *Semaphore) Wait() { | ||
delta := int32(-1) | ||
value := s.futex.Add(uint32(delta)) | ||
for { | ||
if int32(value) >= 0 { | ||
// Semaphore unlocked! | ||
return | ||
} | ||
s.futex.Wait(value) | ||
value = s.futex.Load() | ||
} | ||
} |
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,106 @@ | ||
//go:build none | ||
|
||
#define _GNU_SOURCE | ||
#include <pthread.h> | ||
#include <semaphore.h> | ||
#include <signal.h> | ||
#include <stdint.h> | ||
#include <stdio.h> | ||
|
||
// BDWGC also uses SIGRTMIN+6 on Linux, which seems like a reasonable choice. | ||
#ifdef __linux__ | ||
#define taskPauseSignal (SIGRTMIN + 6) | ||
#endif | ||
|
||
// Pointer to the current task.Task structure. | ||
// Ideally the entire task.Task structure would be a thread-local variable but | ||
// this also works. | ||
static __thread void *current_task; | ||
|
||
struct state_pass { | ||
void *(*start)(void*); | ||
void *args; | ||
void *task; | ||
sem_t startlock; | ||
}; | ||
|
||
// Handle the GC pause in Go. | ||
void tinygo_task_gc_pause(int sig); | ||
|
||
// Initialize threads from the C side. | ||
void tinygo_task_init(void *mainTask, void *context) { | ||
// Make sure the current task pointer is set correctly for the main | ||
// goroutine as well. | ||
current_task = mainTask; | ||
|
||
// 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; | ||
sigaction(taskPauseSignal, &act, NULL); | ||
} | ||
|
||
void tinygo_task_exited(void*); | ||
|
||
// Helper to start a goroutine while also storing the 'task' structure. | ||
static void* start_wrapper(void *arg) { | ||
struct state_pass *state = arg; | ||
void *(*start)(void*) = state->start; | ||
void *args = state->args; | ||
current_task = state->task; | ||
|
||
// Notify the caller that the thread has successfully started and | ||
// initialized. | ||
sem_post(&state->startlock); | ||
|
||
// Run the goroutine function. | ||
start(args); | ||
|
||
// Notify the Go side this thread will exit. | ||
tinygo_task_exited(current_task); | ||
|
||
return NULL; | ||
}; | ||
|
||
// Start a new goroutine in an OS thread. | ||
int tinygo_task_start(uintptr_t fn, void *args, void *task, pthread_t *thread, uint64_t id, void *context) { | ||
// Sanity check. Should get optimized away. | ||
if (sizeof(pthread_t) != sizeof(void*)) { | ||
__builtin_trap(); | ||
} | ||
|
||
struct state_pass state = { | ||
.start = (void*)fn, | ||
.args = args, | ||
.task = task, | ||
}; | ||
sem_init(&state.startlock, 0, 0); | ||
int result = pthread_create(thread, NULL, &start_wrapper, &state); | ||
|
||
// Wait until the thread has been crated and read all state_pass variables. | ||
sem_wait(&state.startlock); | ||
|
||
return result; | ||
} | ||
|
||
// Return the current task (for task.Current()). | ||
void* tinygo_task_current(void) { | ||
return current_task; | ||
} | ||
|
||
// Obtain the highest address of the stack. | ||
uintptr_t tinygo_task_stacktop(void) { | ||
pthread_attr_t attr; | ||
pthread_getattr_np(pthread_self(), &attr); | ||
void *stackbase; | ||
size_t stacksize; | ||
pthread_attr_getstack(&attr, &stackbase, &stacksize); | ||
pthread_attr_destroy(&attr); | ||
return (uintptr_t)stackbase + (uintptr_t)stacksize; | ||
} | ||
|
||
// Send a signal to cause the task to pause for the GC mark phase. | ||
void tinygo_task_send_gc_signal(pthread_t thread) { | ||
pthread_kill(thread, taskPauseSignal); | ||
} |
Oops, something went wrong.