From 02c5c4213c60c7570faab2ea53fc7c59548cef06 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Tue, 21 Jan 2025 11:10:18 +0100 Subject: [PATCH] runtime: implement NumCPU for -scheduler=threads For the threads scheduler, it makes sense to have NumCPU available. For all other schedulers, the number of available CPUs is practically limited to one by the scheduler (even though the system might have more CPUs). --- GNUmakefile | 1 + builder/musl.go | 1 + compileopts/config.go | 2 +- src/internal/task/task_threads.c | 11 ++++++++++- src/internal/task/task_threads.go | 10 ++++++++-- src/runtime/debug.go | 9 --------- src/runtime/scheduler_cooperative.go | 5 +++++ src/runtime/scheduler_none.go | 5 +++++ src/runtime/scheduler_threads.go | 5 +++++ 9 files changed, 36 insertions(+), 13 deletions(-) diff --git a/GNUmakefile b/GNUmakefile index d8770c325..58568d3ec 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -972,6 +972,7 @@ endif @cp -rp lib/musl/crt/crt1.c build/release/tinygo/lib/musl/crt @cp -rp lib/musl/COPYRIGHT build/release/tinygo/lib/musl @cp -rp lib/musl/include build/release/tinygo/lib/musl + @cp -rp lib/musl/src/conf build/release/tinygo/lib/musl/src @cp -rp lib/musl/src/ctype build/release/tinygo/lib/musl/src @cp -rp lib/musl/src/env build/release/tinygo/lib/musl/src @cp -rp lib/musl/src/errno build/release/tinygo/lib/musl/src diff --git a/builder/musl.go b/builder/musl.go index 419533255..3f3a8d46c 100644 --- a/builder/musl.go +++ b/builder/musl.go @@ -124,6 +124,7 @@ var libMusl = Library{ librarySources: func(target string) ([]string, error) { arch := compileopts.MuslArchitecture(target) globs := []string{ + "conf/*.c", "ctype/*.c", "env/*.c", "errno/*.c", diff --git a/compileopts/config.go b/compileopts/config.go index 6cc87aabd..61efa439f 100644 --- a/compileopts/config.go +++ b/compileopts/config.go @@ -22,7 +22,7 @@ import ( // builder.Library struct but that's hard to do since we want to know the // library path in advance in several places). var libVersions = map[string]int{ - "musl": 2, + "musl": 3, } // Config keeps all configuration affecting the build in a single struct. diff --git a/src/internal/task/task_threads.c b/src/internal/task/task_threads.c index a14844f2e..6ada95fa8 100644 --- a/src/internal/task/task_threads.c +++ b/src/internal/task/task_threads.c @@ -6,6 +6,7 @@ #include #include #include +#include // BDWGC also uses SIGRTMIN+6 on Linux, which seems like a reasonable choice. #ifdef __linux__ @@ -29,7 +30,7 @@ struct state_pass { void tinygo_task_gc_pause(int sig); // Initialize the main thread. -void tinygo_task_init(void *mainTask, pthread_t *thread, void *context) { +void tinygo_task_init(void *mainTask, pthread_t *thread, int *numCPU, void *context) { // Make sure the current task pointer is set correctly for the main // goroutine as well. current_task = mainTask; @@ -43,6 +44,14 @@ void tinygo_task_init(void *mainTask, pthread_t *thread, void *context) { act.sa_flags = SA_SIGINFO; act.sa_handler = &tinygo_task_gc_pause; sigaction(taskPauseSignal, &act, NULL); + + // Obtain the number of CPUs available on program start (for NumCPU). + int num = sysconf(_SC_NPROCESSORS_ONLN); + if (num <= 0) { + // Fallback in case there is an error. + num = 1; + } + *numCPU = num; } void tinygo_task_exited(void*); diff --git a/src/internal/task/task_threads.go b/src/internal/task/task_threads.go index 1eb1fed7c..db204cb4e 100644 --- a/src/internal/task/task_threads.go +++ b/src/internal/task/task_threads.go @@ -38,6 +38,8 @@ type state struct { // Goroutine counter, starting at 0 for the main goroutine. var goroutineID uintptr +var numCPU int32 + var mainTask Task // Queue of tasks (see QueueNext) that currently exist in the program. @@ -53,7 +55,7 @@ func OnSystemStack() bool { // startup, before starting any other goroutines. func Init(sp uintptr) { mainTask.state.stackTop = sp - tinygo_task_init(&mainTask, &mainTask.state.thread) + tinygo_task_init(&mainTask, &mainTask.state.thread, &numCPU) } // Return the task struct for the current thread. @@ -259,7 +261,7 @@ func runtimePanic(msg string) // that the 't' parameter won't escape (because it will). // //go:linkname tinygo_task_init tinygo_task_init -func tinygo_task_init(t *Task, thread *threadID) +func tinygo_task_init(t *Task, thread *threadID, numCPU *int32) // Here same as for tinygo_task_init. // @@ -273,3 +275,7 @@ func tinygo_task_send_gc_signal(threadID) //export tinygo_task_current func tinygo_task_current() unsafe.Pointer + +func NumCPU() int { + return int(numCPU) +} diff --git a/src/runtime/debug.go b/src/runtime/debug.go index 139e18bcd..230515908 100644 --- a/src/runtime/debug.go +++ b/src/runtime/debug.go @@ -1,14 +1,5 @@ package runtime -// NumCPU returns the number of logical CPUs usable by the current process. -// -// The set of available CPUs is checked by querying the operating system -// at process startup. Changes to operating system CPU allocation after -// process startup are not reflected. -func NumCPU() int { - return 1 -} - // Stub for NumCgoCall, does not return the real value func NumCgoCall() int { return 0 diff --git a/src/runtime/scheduler_cooperative.go b/src/runtime/scheduler_cooperative.go index 82ec24bee..3cb1e3f98 100644 --- a/src/runtime/scheduler_cooperative.go +++ b/src/runtime/scheduler_cooperative.go @@ -60,6 +60,11 @@ func Gosched() { task.Pause() } +// NumCPU returns the number of logical CPUs usable by the current process. +func NumCPU() int { + return 1 +} + // Add this task to the sleep queue, assuming its state is set to sleeping. func addSleepTask(t *task.Task, duration timeUnit) { if schedulerDebug { diff --git a/src/runtime/scheduler_none.go b/src/runtime/scheduler_none.go index 82e720393..dc4b03b6e 100644 --- a/src/runtime/scheduler_none.go +++ b/src/runtime/scheduler_none.go @@ -44,6 +44,11 @@ func Gosched() { // There are no other goroutines, so there's nothing to schedule. } +// NumCPU returns the number of logical CPUs usable by the current process. +func NumCPU() int { + return 1 +} + func addTimer(tim *timerNode) { runtimePanic("timers not supported without a scheduler") } diff --git a/src/runtime/scheduler_threads.go b/src/runtime/scheduler_threads.go index e553a5b9c..292499354 100644 --- a/src/runtime/scheduler_threads.go +++ b/src/runtime/scheduler_threads.go @@ -50,6 +50,11 @@ func Gosched() { // operation, so is probably best not to use. } +// NumCPU returns the number of logical CPUs usable by the current process. +func NumCPU() int { + return task.NumCPU() +} + // Separate goroutine (thread) that runs timer callbacks when they expire. func timerRunner() { for {