diff options
| author | qmuntal <quimmuntal@gmail.com> | 2026-02-17 18:23:43 +0100 |
|---|---|---|
| committer | Quim Muntal <quimmuntal@gmail.com> | 2026-03-18 22:58:11 -0700 |
| commit | bf1c1b3bde50f33593cd0db8f19812ea957964f5 (patch) | |
| tree | 8e9b7ddccc889900dec98b2ebff7f03ef52afcc2 /src/runtime | |
| parent | c1c0af1e16c4c932d4c05442d9717dac0dc08a79 (diff) | |
| download | go-bf1c1b3bde50f33593cd0db8f19812ea957964f5.tar.xz | |
runtime,runtime/cgo: do cgo thread initialization in Go on Windows
Windows doesn't require any special handling for cgo threads. They
can be created in the same way as in non-cgo code.
In fact, the code to create threads in runtime and in runtime/cgo is
basically the same, except that the latter does some retries on failure.
Cq-Include-Trybots: luci.golang.try:gotip-windows-amd64-longtest,gotip-windows-amd64-race,gotip-windows-arm64
Change-Id: I49d4de93d4d3b07a4c89e2bfb6b7302c6dfb9877
Reviewed-on: https://go-review.googlesource.com/c/go/+/746300
Reviewed-by: Cherry Mui <cherryyz@google.com>
Reviewed-by: Michael Pratt <mpratt@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Diffstat (limited to 'src/runtime')
| -rw-r--r-- | src/runtime/cgo/callbacks.go | 32 | ||||
| -rw-r--r-- | src/runtime/cgo/gcc_libinit_unix.c (renamed from src/runtime/cgo/gcc_libinit.c) | 20 | ||||
| -rw-r--r-- | src/runtime/cgo/gcc_libinit_windows.c | 42 | ||||
| -rw-r--r-- | src/runtime/cgo/gcc_unix.c | 2 | ||||
| -rw-r--r-- | src/runtime/cgo/gcc_util.c | 19 | ||||
| -rw-r--r-- | src/runtime/cgo/gcc_windows_386.c | 54 | ||||
| -rw-r--r-- | src/runtime/cgo/gcc_windows_amd64.c | 53 | ||||
| -rw-r--r-- | src/runtime/cgo/gcc_windows_arm64.c | 41 | ||||
| -rw-r--r-- | src/runtime/cgo/libcgo_windows.h | 6 | ||||
| -rw-r--r-- | src/runtime/cgo/pthread_unix.c | 4 | ||||
| -rw-r--r-- | src/runtime/os_windows.go | 41 | ||||
| -rw-r--r-- | src/runtime/proc.go | 11 |
12 files changed, 76 insertions, 249 deletions
diff --git a/src/runtime/cgo/callbacks.go b/src/runtime/cgo/callbacks.go index 986f61914f..41907b2d42 100644 --- a/src/runtime/cgo/callbacks.go +++ b/src/runtime/cgo/callbacks.go @@ -47,29 +47,23 @@ func _cgo_panic(a *struct{ cstr *byte }) { _runtime_cgo_panic_internal(a.cstr) } -//go:cgo_import_static x_cgo_init -//go:linkname x_cgo_init x_cgo_init +//go:cgo_import_static _cgo_init //go:linkname _cgo_init _cgo_init -var x_cgo_init byte -var _cgo_init = &x_cgo_init +var _cgo_init unsafe.Pointer -//go:cgo_import_static x_cgo_thread_start -//go:linkname x_cgo_thread_start x_cgo_thread_start +//go:cgo_import_static _cgo_thread_start //go:linkname _cgo_thread_start _cgo_thread_start -var x_cgo_thread_start byte -var _cgo_thread_start = &x_cgo_thread_start +var _cgo_thread_start unsafe.Pointer // Creates a new system thread without updating any Go state. // // This method is invoked during shared library loading to create a new OS // thread to perform the runtime initialization. This method is similar to -// _cgo_sys_thread_start except that it doesn't update any Go state. +// x_cgo_thread_start except that it doesn't update any Go state. -//go:cgo_import_static x_cgo_sys_thread_create -//go:linkname x_cgo_sys_thread_create x_cgo_sys_thread_create +//go:cgo_import_static _cgo_sys_thread_create //go:linkname _cgo_sys_thread_create _cgo_sys_thread_create -var x_cgo_sys_thread_create byte -var _cgo_sys_thread_create = &x_cgo_sys_thread_create +var _cgo_sys_thread_create unsafe.Pointer // Indicates whether a dummy thread key has been created or not. // @@ -101,11 +95,9 @@ var _set_crosscall2 = set_crosscall2 // Store the g into the thread-specific value. // So that pthread_key_destructor will dropm when the thread is exiting. -//go:cgo_import_static x_cgo_bindm -//go:linkname x_cgo_bindm x_cgo_bindm +//go:cgo_import_static _cgo_bindm //go:linkname _cgo_bindm _cgo_bindm -var x_cgo_bindm byte -var _cgo_bindm = &x_cgo_bindm +var _cgo_bindm unsafe.Pointer // Notifies that the runtime has been initialized. // @@ -162,8 +154,6 @@ var _cgo_yield unsafe.Pointer // x_cgo_getstackbound gets the thread's C stack size and // set the G's stack bound based on the stack size. -//go:cgo_import_static x_cgo_getstackbound -//go:linkname x_cgo_getstackbound x_cgo_getstackbound +//go:cgo_import_static _cgo_getstackbound //go:linkname _cgo_getstackbound _cgo_getstackbound -var x_cgo_getstackbound byte -var _cgo_getstackbound = &x_cgo_getstackbound +var _cgo_getstackbound unsafe.Pointer diff --git a/src/runtime/cgo/gcc_libinit.c b/src/runtime/cgo/gcc_libinit_unix.c index 9d2402636e..0ff0454931 100644 --- a/src/runtime/cgo/gcc_libinit.c +++ b/src/runtime/cgo/gcc_libinit_unix.c @@ -93,6 +93,8 @@ void x_cgo_bindm(void* g) { pthread_setspecific(pthread_g, g); } +void (* _cgo_bindm)(void*) = x_cgo_bindm; + void x_cgo_notify_runtime_init_done(void* dummy __attribute__ ((unused))) { pthread_mutex_lock(&runtime_init_mu); @@ -176,3 +178,21 @@ pthread_key_destructor(void* g) { x_crosscall2_ptr(NULL, g, 0, 0); } } + +void +x_cgo_thread_start(ThreadStart *arg) +{ + ThreadStart *ts; + + /* Make our own copy that can persist after we return. */ + ts = malloc(sizeof *ts); + if(ts == nil) { + fprintf(stderr, "runtime/cgo: out of memory in thread_start\n"); + abort(); + } + *ts = *arg; + + _cgo_sys_thread_start(ts); /* OS-dependent half */ +} + +void (* _cgo_thread_start)(ThreadStart*) = x_cgo_thread_start; diff --git a/src/runtime/cgo/gcc_libinit_windows.c b/src/runtime/cgo/gcc_libinit_windows.c index 7a1dbc0119..78b32254cf 100644 --- a/src/runtime/cgo/gcc_libinit_windows.c +++ b/src/runtime/cgo/gcc_libinit_windows.c @@ -13,7 +13,6 @@ #include <stdlib.h> #include "libcgo.h" -#include "libcgo_windows.h" static volatile LONG runtime_init_once_gate = 0; static volatile LONG runtime_init_once_done = 0; @@ -26,6 +25,11 @@ static int runtime_init_done; // No pthreads on Windows, these are always zero. uintptr_t x_cgo_pthread_key_created; void (*x_crosscall2_ptr)(void (*fn)(void *), void *, int, size_t); +void (*_cgo_init)(G*, void (*)(void*), void **, void **); +void (*_cgo_thread_start)(ThreadStart *); +void (*_cgo_sys_thread_create)(void* (*func)(void*)); +void (*_cgo_getstackbound)(uintptr[2]); +void (*_cgo_bindm)(void*); // Pre-initialize the runtime synchronization objects void @@ -56,11 +60,6 @@ _cgo_maybe_run_preinit() { } } -void -x_cgo_sys_thread_create(unsigned long (__stdcall *func)(void*)) { - _cgo_beginthread(func, NULL); -} - int _cgo_is_runtime_initialized() { int status; @@ -90,12 +89,6 @@ _cgo_wait_runtime_init_done(void) { return 0; } -// Should not be used since x_cgo_pthread_key_created will always be zero. -void x_cgo_bindm(void* dummy) { - fprintf(stderr, "unexpected cgo_bindm on Windows\n"); - abort(); -} - void x_cgo_notify_runtime_init_done(void* dummy) { _cgo_maybe_run_preinit(); @@ -189,28 +182,3 @@ void x_cgo_call_symbolizer_function(struct cgoSymbolizerArg* arg) { (*pfn)(arg); } - -void _cgo_beginthread(unsigned long (__stdcall *func)(void*), void* arg) { - int tries; - HANDLE thandle; - - for (tries = 0; tries < 20; tries++) { - thandle = CreateThread(NULL, 0, func, arg, 0, NULL); - if (thandle == 0 && GetLastError() == ERROR_ACCESS_DENIED) { - // "Insufficient resources", try again in a bit. - // - // Note that the first Sleep(0) is a yield. - Sleep(tries); // milliseconds - continue; - } else if (thandle == 0) { - break; - } - CloseHandle(thandle); - return; // Success! - } - - fprintf(stderr, "runtime: failed to create new OS thread (%lu)\n", GetLastError()); - abort(); -} - -void x_cgo_getstackbound(uintptr bounds[2]) {} // no-op for now diff --git a/src/runtime/cgo/gcc_unix.c b/src/runtime/cgo/gcc_unix.c index c40b9038c3..cbe20710b0 100644 --- a/src/runtime/cgo/gcc_unix.c +++ b/src/runtime/cgo/gcc_unix.c @@ -48,6 +48,8 @@ x_cgo_init(G *g, void (*setg)(void*), void **tlsg, void **tlsbase) } } +void (* _cgo_init)(G*, void (*)(void*), void **, void **) = x_cgo_init; + void* threadentry(void *v) { diff --git a/src/runtime/cgo/gcc_util.c b/src/runtime/cgo/gcc_util.c index 3fcb48cc8d..bb81e5b14f 100644 --- a/src/runtime/cgo/gcc_util.c +++ b/src/runtime/cgo/gcc_util.c @@ -4,25 +4,6 @@ #include "libcgo.h" -/* Stub for creating a new thread */ -void -x_cgo_thread_start(ThreadStart *arg) -{ - ThreadStart *ts; - - /* Make our own copy that can persist after we return. */ - _cgo_tsan_acquire(); - ts = malloc(sizeof *ts); - _cgo_tsan_release(); - if(ts == nil) { - fprintf(stderr, "runtime/cgo: out of memory in thread_start\n"); - abort(); - } - *ts = *arg; - - _cgo_sys_thread_start(ts); /* OS-dependent half */ -} - #ifndef CGO_TSAN void(* const _cgo_yield)() = NULL; #else diff --git a/src/runtime/cgo/gcc_windows_386.c b/src/runtime/cgo/gcc_windows_386.c deleted file mode 100644 index b93eab03b0..0000000000 --- a/src/runtime/cgo/gcc_windows_386.c +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#define WIN32_LEAN_AND_MEAN -#include <windows.h> -#include <process.h> -#include <stdlib.h> -#include <stdio.h> -#include <errno.h> -#include "libcgo.h" -#include "libcgo_windows.h" - -static unsigned long __stdcall threadentry(void*); -static void (*setg_gcc)(void*); -static DWORD *tls_g; - -void -x_cgo_init(G *g, void (*setg)(void*), void **tlsg, void **tlsbase) -{ - setg_gcc = setg; - tls_g = (DWORD *)tlsg; -} - -void -_cgo_sys_thread_start(ThreadStart *ts) -{ - _cgo_beginthread(threadentry, ts); -} - -static unsigned long -__stdcall -threadentry(void *v) -{ - ThreadStart ts; - - ts = *(ThreadStart*)v; - free(v); - - // minit queries stack bounds from the OS. - - /* - * Set specific keys in thread local storage. - */ - asm volatile ( - "movl %0, %%fs:0(%1)\n" // MOVL tls0, 0(tls_g)(FS) - "movl %%fs:0(%1), %%eax\n" // MOVL 0(tls_g)(FS), tmp - "movl %2, 0(%%eax)\n" // MOVL g, 0(AX) - :: "r"(ts.tls), "r"(*tls_g), "r"(ts.g) : "%eax" - ); - - crosscall1(ts.fn, setg_gcc, ts.g); - return 0; -} diff --git a/src/runtime/cgo/gcc_windows_amd64.c b/src/runtime/cgo/gcc_windows_amd64.c deleted file mode 100644 index 2fcb416b74..0000000000 --- a/src/runtime/cgo/gcc_windows_amd64.c +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#define WIN32_LEAN_AND_MEAN -#include <windows.h> -#include <process.h> -#include <stdlib.h> -#include <stdio.h> -#include <errno.h> -#include "libcgo.h" -#include "libcgo_windows.h" - -static unsigned long __stdcall threadentry(void*); -static void (*setg_gcc)(void*); -static DWORD *tls_g; - -void -x_cgo_init(G *g, void (*setg)(void*), void **tlsg, void **tlsbase) -{ - setg_gcc = setg; - tls_g = (DWORD *)tlsg; -} - - -void -_cgo_sys_thread_start(ThreadStart *ts) -{ - _cgo_beginthread(threadentry, ts); -} - -static unsigned long -__stdcall -threadentry(void *v) -{ - ThreadStart ts; - - ts = *(ThreadStart*)v; - free(v); - - // minit queries stack bounds from the OS. - - /* - * Set specific keys in thread local storage. - */ - asm volatile ( - "movq %0, %%gs:0(%1)\n" // MOVL tls0, 0(tls_g)(GS) - :: "r"(ts.tls), "r"(*tls_g) - ); - - crosscall1(ts.fn, setg_gcc, (void*)ts.g); - return 0; -} diff --git a/src/runtime/cgo/gcc_windows_arm64.c b/src/runtime/cgo/gcc_windows_arm64.c deleted file mode 100644 index afde94d4a7..0000000000 --- a/src/runtime/cgo/gcc_windows_arm64.c +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#define WIN32_LEAN_AND_MEAN -#include <windows.h> -#include <process.h> -#include <stdlib.h> -#include <stdio.h> -#include <errno.h> -#include "libcgo.h" -#include "libcgo_windows.h" - -static unsigned long __stdcall threadentry(void*); -static void (*setg_gcc)(void*); - -void -x_cgo_init(G *g, void (*setg)(void*)) -{ - setg_gcc = setg; -} - -void -_cgo_sys_thread_start(ThreadStart *ts) -{ - _cgo_beginthread(threadentry, ts); -} - - -static unsigned long -__stdcall -threadentry(void *v) -{ - ThreadStart ts; - - ts = *(ThreadStart*)v; - free(v); - - crosscall1(ts.fn, setg_gcc, (void *)ts.g); - return 0; -} diff --git a/src/runtime/cgo/libcgo_windows.h b/src/runtime/cgo/libcgo_windows.h deleted file mode 100644 index 682b7bdbbf..0000000000 --- a/src/runtime/cgo/libcgo_windows.h +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Call _beginthread, aborting on failure. -void _cgo_beginthread(unsigned long (__stdcall *func)(void*), void* arg); diff --git a/src/runtime/cgo/pthread_unix.c b/src/runtime/cgo/pthread_unix.c index d38045fdaa..438c599f1c 100644 --- a/src/runtime/cgo/pthread_unix.c +++ b/src/runtime/cgo/pthread_unix.c @@ -73,6 +73,8 @@ x_cgo_sys_thread_create(void* (*func)(void*)) { } } +void (* _cgo_sys_thread_create)(void* (*func)(void*)) = x_cgo_sys_thread_create; + void x_cgo_getstackbound(uintptr bounds[2]) { @@ -115,6 +117,8 @@ x_cgo_getstackbound(uintptr bounds[2]) _cgo_tsan_release(); } +void (* _cgo_getstackbound)(uintptr[2]) = x_cgo_getstackbound; + // _cgo_try_pthread_create retries pthread_create if it fails with EAGAIN. int _cgo_try_pthread_create(pthread_t* thread, const pthread_attr_t* attr, void* (*pfn)(void*), void* arg) { diff --git a/src/runtime/os_windows.go b/src/runtime/os_windows.go index ca1f9837fa..5af1b03788 100644 --- a/src/runtime/os_windows.go +++ b/src/runtime/os_windows.go @@ -750,18 +750,11 @@ func semacreate(mp *m) { } } -// May run with m.p==nil, so write barriers are not allowed. This -// function is called by newosproc0, so it is also required to -// operate without stack guards. +// May run with m.p==nil, so write barriers are not allowed. // //go:nowritebarrierrec -//go:nosplit func newosproc(mp *m) { - // We pass 0 for the stack size to use the default for this binary. - thandle := stdcall(_CreateThread, 0, 0, - abi.FuncPCABI0(tstart_stdcall), uintptr(unsafe.Pointer(mp)), - 0, 0) - + thandle, err := createThread(0, unsafe.Pointer(abi.FuncPCABI0(tstart_stdcall)), unsafe.Pointer(mp)) if thandle == 0 { if atomic.Load(&exiting) != 0 { // CreateThread may fail if called @@ -771,7 +764,7 @@ func newosproc(mp *m) { lock(&deadlock) lock(&deadlock) } - print("runtime: failed to create new OS thread (have ", mcount(), " already; errno=", getlasterror(), ")\n") + print("runtime: failed to create new OS thread (have ", mcount(), " already; errno=", err, ")\n") throw("runtime.newosproc") } @@ -786,7 +779,33 @@ func newosproc(mp *m) { //go:nowritebarrierrec //go:nosplit func newosproc0(stacksize uintptr, fn unsafe.Pointer) { - throw("bad newosproc0") + thandle, err := createThread(stacksize, fn, nil) + if thandle == 0 { + print("runtime: failed to create new OS thread (errno=", err, ")\n") + throw("runtime: failed to create new OS thread\n") + } + stdcall_no_g(_CloseHandle, thandle) +} + +// createThread calls CreateThread to create a new thread. +// +//go:nowritebarrierrec +//go:nosplit +func createThread(stackSize uintptr, fn unsafe.Pointer, arg unsafe.Pointer) (handle uintptr, err uint32) { + for tries := range 20 { + // We pass 0 for the stack size to use the default for this binary. + handle = stdcall_no_g(_CreateThread, 0, stackSize, uintptr(fn), uintptr(arg), 0, 0) + if handle == 0 { + err = getlasterror() + } + if handle == 0 && err == windows.ERROR_ACCESS_DENIED { + // "Insufficient resources". Yield, then back off a bit before retrying. + usleep_no_g(uint32(tries) * 1000) + continue + } + break + } + return handle, err } //go:nosplit diff --git a/src/runtime/proc.go b/src/runtime/proc.go index 71e80e9cb7..7e177c72cd 100644 --- a/src/runtime/proc.go +++ b/src/runtime/proc.go @@ -222,10 +222,10 @@ func main() { throw("_cgo_pthread_key_created missing") } - if _cgo_thread_start == nil { - throw("_cgo_thread_start missing") - } if GOOS != "windows" { + if _cgo_thread_start == nil { + throw("_cgo_thread_start missing") + } if _cgo_setenv == nil { throw("_cgo_setenv missing") } @@ -2913,11 +2913,8 @@ func newm(fn func(), pp *p, id int64) { } func newm1(mp *m) { - if iscgo { + if iscgo && _cgo_thread_start != nil { var ts cgothreadstart - if _cgo_thread_start == nil { - throw("_cgo_thread_start missing") - } ts.g.set(mp.g0) ts.tls = (*uint64)(unsafe.Pointer(&mp.tls[0])) ts.fn = unsafe.Pointer(abi.FuncPCABI0(mstart)) |
