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).
This commit is contained in:
Ayke van Laethem
2025-01-21 11:10:18 +01:00
committed by Ron Evans
parent 25b83920b6
commit 02c5c4213c
9 changed files with 36 additions and 13 deletions
+1
View File
@@ -972,6 +972,7 @@ endif
@cp -rp lib/musl/crt/crt1.c build/release/tinygo/lib/musl/crt @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/COPYRIGHT build/release/tinygo/lib/musl
@cp -rp lib/musl/include 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/ctype build/release/tinygo/lib/musl/src
@cp -rp lib/musl/src/env 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 @cp -rp lib/musl/src/errno build/release/tinygo/lib/musl/src
+1
View File
@@ -124,6 +124,7 @@ var libMusl = Library{
librarySources: func(target string) ([]string, error) { librarySources: func(target string) ([]string, error) {
arch := compileopts.MuslArchitecture(target) arch := compileopts.MuslArchitecture(target)
globs := []string{ globs := []string{
"conf/*.c",
"ctype/*.c", "ctype/*.c",
"env/*.c", "env/*.c",
"errno/*.c", "errno/*.c",
+1 -1
View File
@@ -22,7 +22,7 @@ import (
// builder.Library struct but that's hard to do since we want to know the // builder.Library struct but that's hard to do since we want to know the
// library path in advance in several places). // library path in advance in several places).
var libVersions = map[string]int{ var libVersions = map[string]int{
"musl": 2, "musl": 3,
} }
// Config keeps all configuration affecting the build in a single struct. // Config keeps all configuration affecting the build in a single struct.
+10 -1
View File
@@ -6,6 +6,7 @@
#include <signal.h> #include <signal.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <unistd.h>
// BDWGC also uses SIGRTMIN+6 on Linux, which seems like a reasonable choice. // BDWGC also uses SIGRTMIN+6 on Linux, which seems like a reasonable choice.
#ifdef __linux__ #ifdef __linux__
@@ -29,7 +30,7 @@ struct state_pass {
void tinygo_task_gc_pause(int sig); void tinygo_task_gc_pause(int sig);
// Initialize the main thread. // 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 // Make sure the current task pointer is set correctly for the main
// goroutine as well. // goroutine as well.
current_task = mainTask; 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_flags = SA_SIGINFO;
act.sa_handler = &tinygo_task_gc_pause; act.sa_handler = &tinygo_task_gc_pause;
sigaction(taskPauseSignal, &act, NULL); 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*); void tinygo_task_exited(void*);
+8 -2
View File
@@ -38,6 +38,8 @@ type state struct {
// Goroutine counter, starting at 0 for the main goroutine. // Goroutine counter, starting at 0 for the main goroutine.
var goroutineID uintptr var goroutineID uintptr
var numCPU int32
var mainTask Task var mainTask Task
// Queue of tasks (see QueueNext) that currently exist in the program. // Queue of tasks (see QueueNext) that currently exist in the program.
@@ -53,7 +55,7 @@ func OnSystemStack() bool {
// startup, before starting any other goroutines. // startup, before starting any other goroutines.
func Init(sp uintptr) { func Init(sp uintptr) {
mainTask.state.stackTop = sp 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. // 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). // that the 't' parameter won't escape (because it will).
// //
//go:linkname tinygo_task_init tinygo_task_init //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. // Here same as for tinygo_task_init.
// //
@@ -273,3 +275,7 @@ func tinygo_task_send_gc_signal(threadID)
//export tinygo_task_current //export tinygo_task_current
func tinygo_task_current() unsafe.Pointer func tinygo_task_current() unsafe.Pointer
func NumCPU() int {
return int(numCPU)
}
-9
View File
@@ -1,14 +1,5 @@
package runtime 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 // Stub for NumCgoCall, does not return the real value
func NumCgoCall() int { func NumCgoCall() int {
return 0 return 0
+5
View File
@@ -60,6 +60,11 @@ func Gosched() {
task.Pause() 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. // Add this task to the sleep queue, assuming its state is set to sleeping.
func addSleepTask(t *task.Task, duration timeUnit) { func addSleepTask(t *task.Task, duration timeUnit) {
if schedulerDebug { if schedulerDebug {
+5
View File
@@ -44,6 +44,11 @@ func Gosched() {
// There are no other goroutines, so there's nothing to schedule. // 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) { func addTimer(tim *timerNode) {
runtimePanic("timers not supported without a scheduler") runtimePanic("timers not supported without a scheduler")
} }
+5
View File
@@ -50,6 +50,11 @@ func Gosched() {
// operation, so is probably best not to use. // 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. // Separate goroutine (thread) that runs timer callbacks when they expire.
func timerRunner() { func timerRunner() {
for { for {