Skip to content

Commit 06bb0ff

Browse files
committed
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).
1 parent 120d17c commit 06bb0ff

File tree

8 files changed

+35
-13
lines changed

8 files changed

+35
-13
lines changed

builder/musl.go

+1
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ var libMusl = Library{
124124
librarySources: func(target string) ([]string, error) {
125125
arch := compileopts.MuslArchitecture(target)
126126
globs := []string{
127+
"conf/*.c",
127128
"ctype/*.c",
128129
"env/*.c",
129130
"errno/*.c",

compileopts/config.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import (
2222
// builder.Library struct but that's hard to do since we want to know the
2323
// library path in advance in several places).
2424
var libVersions = map[string]int{
25-
"musl": 2,
25+
"musl": 3,
2626
}
2727

2828
// Config keeps all configuration affecting the build in a single struct.

src/internal/task/task_threads.c

+10-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <signal.h>
77
#include <stdint.h>
88
#include <stdio.h>
9+
#include <unistd.h>
910

1011
// BDWGC also uses SIGRTMIN+6 on Linux, which seems like a reasonable choice.
1112
#ifdef __linux__
@@ -29,7 +30,7 @@ struct state_pass {
2930
void tinygo_task_gc_pause(int sig);
3031

3132
// Initialize the main thread.
32-
void tinygo_task_init(void *mainTask, pthread_t *thread, void *context) {
33+
void tinygo_task_init(void *mainTask, pthread_t *thread, int *numCPU, void *context) {
3334
// Make sure the current task pointer is set correctly for the main
3435
// goroutine as well.
3536
current_task = mainTask;
@@ -43,6 +44,14 @@ void tinygo_task_init(void *mainTask, pthread_t *thread, void *context) {
4344
act.sa_flags = SA_SIGINFO;
4445
act.sa_handler = &tinygo_task_gc_pause;
4546
sigaction(taskPauseSignal, &act, NULL);
47+
48+
// Obtain the number of CPUs available on program start (for NumCPU).
49+
int num = sysconf(_SC_NPROCESSORS_ONLN);
50+
if (num <= 0) {
51+
// Fallback in case there is an error.
52+
num = 1;
53+
}
54+
*numCPU = num;
4655
}
4756

4857
void tinygo_task_exited(void*);

src/internal/task/task_threads.go

+8-2
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ type state struct {
3838
// Goroutine counter, starting at 0 for the main goroutine.
3939
var goroutineID uintptr
4040

41+
var numCPU int32
42+
4143
var mainTask Task
4244

4345
// Queue of tasks (see QueueNext) that currently exist in the program.
@@ -53,7 +55,7 @@ func OnSystemStack() bool {
5355
// startup, before starting any other goroutines.
5456
func Init(sp uintptr) {
5557
mainTask.state.stackTop = sp
56-
tinygo_task_init(&mainTask, &mainTask.state.thread)
58+
tinygo_task_init(&mainTask, &mainTask.state.thread, &numCPU)
5759
}
5860

5961
// Return the task struct for the current thread.
@@ -259,7 +261,7 @@ func runtimePanic(msg string)
259261
// that the 't' parameter won't escape (because it will).
260262
//
261263
//go:linkname tinygo_task_init tinygo_task_init
262-
func tinygo_task_init(t *Task, thread *threadID)
264+
func tinygo_task_init(t *Task, thread *threadID, numCPU *int32)
263265

264266
// Here same as for tinygo_task_init.
265267
//
@@ -273,3 +275,7 @@ func tinygo_task_send_gc_signal(threadID)
273275

274276
//export tinygo_task_current
275277
func tinygo_task_current() unsafe.Pointer
278+
279+
func NumCPU() int {
280+
return int(numCPU)
281+
}

src/runtime/debug.go

-9
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,5 @@
11
package runtime
22

3-
// NumCPU returns the number of logical CPUs usable by the current process.
4-
//
5-
// The set of available CPUs is checked by querying the operating system
6-
// at process startup. Changes to operating system CPU allocation after
7-
// process startup are not reflected.
8-
func NumCPU() int {
9-
return 1
10-
}
11-
123
// Stub for NumCgoCall, does not return the real value
134
func NumCgoCall() int {
145
return 0

src/runtime/scheduler_cooperative.go

+5
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@ func Gosched() {
5757
task.Pause()
5858
}
5959

60+
// NumCPU returns the number of logical CPUs usable by the current process.
61+
func NumCPU() int {
62+
return 1
63+
}
64+
6065
// Add this task to the sleep queue, assuming its state is set to sleeping.
6166
func addSleepTask(t *task.Task, duration timeUnit) {
6267
if schedulerDebug {

src/runtime/scheduler_none.go

+5
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,11 @@ func Gosched() {
4141
// There are no other goroutines, so there's nothing to schedule.
4242
}
4343

44+
// NumCPU returns the number of logical CPUs usable by the current process.
45+
func NumCPU() int {
46+
return 1
47+
}
48+
4449
func addTimer(tim *timerNode) {
4550
runtimePanic("timers not supported without a scheduler")
4651
}

src/runtime/scheduler_threads.go

+5
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ func Gosched() {
5050
// operation, so is probably best not to use.
5151
}
5252

53+
// NumCPU returns the number of logical CPUs usable by the current process.
54+
func NumCPU() int {
55+
return task.NumCPU()
56+
}
57+
5358
// Separate goroutine (thread) that runs timer callbacks when they expire.
5459
func timerRunner() {
5560
for {

0 commit comments

Comments
 (0)