From bf1c1b3bde50f33593cd0db8f19812ea957964f5 Mon Sep 17 00:00:00 2001 From: qmuntal Date: Tue, 17 Feb 2026 18:23:43 +0100 Subject: 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 Reviewed-by: Michael Pratt LUCI-TryBot-Result: Go LUCI --- src/runtime/cgo/callbacks.go | 32 ++---- src/runtime/cgo/gcc_libinit.c | 178 ------------------------------ src/runtime/cgo/gcc_libinit_unix.c | 198 ++++++++++++++++++++++++++++++++++ src/runtime/cgo/gcc_libinit_windows.c | 42 +------- src/runtime/cgo/gcc_unix.c | 2 + src/runtime/cgo/gcc_util.c | 19 ---- src/runtime/cgo/gcc_windows_386.c | 54 ---------- src/runtime/cgo/gcc_windows_amd64.c | 53 --------- src/runtime/cgo/gcc_windows_arm64.c | 41 ------- src/runtime/cgo/libcgo_windows.h | 6 -- src/runtime/cgo/pthread_unix.c | 4 + src/runtime/os_windows.go | 41 +++++-- src/runtime/proc.go | 11 +- 13 files changed, 254 insertions(+), 427 deletions(-) delete mode 100644 src/runtime/cgo/gcc_libinit.c create mode 100644 src/runtime/cgo/gcc_libinit_unix.c delete mode 100644 src/runtime/cgo/gcc_windows_386.c delete mode 100644 src/runtime/cgo/gcc_windows_amd64.c delete mode 100644 src/runtime/cgo/gcc_windows_arm64.c delete mode 100644 src/runtime/cgo/libcgo_windows.h (limited to 'src/runtime') 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.c deleted file mode 100644 index 9d2402636e..0000000000 --- a/src/runtime/cgo/gcc_libinit.c +++ /dev/null @@ -1,178 +0,0 @@ -// Copyright 2015 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. - -//go:build unix - -// When cross-compiling with clang to linux/armv5, atomics are emulated -// and cause a compiler warning. This results in a build failure since -// cgo uses -Werror. See #65290. -#pragma GCC diagnostic ignored "-Wpragmas" -#pragma GCC diagnostic ignored "-Wunknown-warning-option" -#pragma GCC diagnostic ignored "-Watomic-alignment" - -#include -#include -#include -#include "libcgo.h" -#include "libcgo_unix.h" - -static pthread_cond_t runtime_init_cond = PTHREAD_COND_INITIALIZER; -static pthread_mutex_t runtime_init_mu = PTHREAD_MUTEX_INITIALIZER; -static int runtime_init_done; - -// pthread_g is a pthread specific key, for storing the g that binded to the C thread. -// The registered pthread_key_destructor will dropm, when the pthread-specified value g is not NULL, -// while a C thread is exiting. -static pthread_key_t pthread_g; -static void pthread_key_destructor(void* g); -uintptr_t x_cgo_pthread_key_created; -void (*x_crosscall2_ptr)(void (*fn)(void *), void *, int, size_t); - -// The traceback function, used when tracing C calls. -static void (*cgo_traceback_function)(struct cgoTracebackArg*); - -// The context function, used when tracing back C calls into Go. -static void (*cgo_context_function)(struct cgoContextArg*); - -// The symbolizer function, used when symbolizing C frames. -static void (*cgo_symbolizer_function)(struct cgoSymbolizerArg*); - -uintptr_t -_cgo_wait_runtime_init_done(void) { - void (*pfn)(struct cgoContextArg*); - int done; - - pfn = __atomic_load_n(&cgo_context_function, __ATOMIC_CONSUME); - - done = 2; - if (__atomic_load_n(&runtime_init_done, __ATOMIC_CONSUME) != done) { - pthread_mutex_lock(&runtime_init_mu); - while (__atomic_load_n(&runtime_init_done, __ATOMIC_CONSUME) == 0) { - pthread_cond_wait(&runtime_init_cond, &runtime_init_mu); - } - - // The key and x_cgo_pthread_key_created are for the whole program, - // whereas the specific and destructor is per thread. - if (x_cgo_pthread_key_created == 0 && pthread_key_create(&pthread_g, pthread_key_destructor) == 0) { - x_cgo_pthread_key_created = 1; - } - - // TODO(iant): For the case of a new C thread calling into Go, such - // as when using -buildmode=c-archive, we know that Go runtime - // initialization is complete but we do not know that all Go init - // functions have been run. We should not fetch cgo_context_function - // until they have been, because that is where a call to - // SetCgoTraceback is likely to occur. We are going to wait for Go - // initialization to be complete anyhow, later, by waiting for - // main_init_done to be closed in cgocallbackg1. We should wait here - // instead. See also issue #15943. - pfn = __atomic_load_n(&cgo_context_function, __ATOMIC_CONSUME); - - __atomic_store_n(&runtime_init_done, done, __ATOMIC_RELEASE); - pthread_mutex_unlock(&runtime_init_mu); - } - - if (pfn != nil) { - struct cgoContextArg arg; - - arg.Context = 0; - (*pfn)(&arg); - return arg.Context; - } - return 0; -} - -// Store the g into a thread-specific value associated with the pthread key pthread_g. -// And pthread_key_destructor will dropm when the thread is exiting. -void x_cgo_bindm(void* g) { - // We assume this will always succeed, otherwise, there might be extra M leaking, - // when a C thread exits after a cgo call. - // We only invoke this function once per thread in runtime.needAndBindM, - // and the next calls just reuse the bound m. - pthread_setspecific(pthread_g, g); -} - -void -x_cgo_notify_runtime_init_done(void* dummy __attribute__ ((unused))) { - pthread_mutex_lock(&runtime_init_mu); - __atomic_store_n(&runtime_init_done, 1, __ATOMIC_RELEASE); - pthread_cond_broadcast(&runtime_init_cond); - pthread_mutex_unlock(&runtime_init_mu); -} - -// Sets the traceback, context, and symbolizer functions. Called from -// runtime.SetCgoTraceback. -void x_cgo_set_traceback_functions(struct cgoSetTracebackFunctionsArg* arg) { - __atomic_store_n(&cgo_traceback_function, arg->Traceback, __ATOMIC_RELEASE); - __atomic_store_n(&cgo_context_function, arg->Context, __ATOMIC_RELEASE); - __atomic_store_n(&cgo_symbolizer_function, arg->Symbolizer, __ATOMIC_RELEASE); -} - -// Gets the traceback function to call to trace C calls. -void (*(_cgo_get_traceback_function(void)))(struct cgoTracebackArg*) { - return __atomic_load_n(&cgo_traceback_function, __ATOMIC_CONSUME); -} - -// Call the traceback function registered with x_cgo_set_traceback_functions. -// -// The traceback function is an arbitrary user C function which may be built -// with TSAN, and thus must be wrapped with TSAN acquire/release calls. For -// normal cgo calls, cmd/cgo automatically inserts TSAN acquire/release calls. -// Since the traceback, context, and symbolizer functions are registered at -// startup and called via the runtime, they do not get automatic TSAN -// acquire/release calls. -// -// The only purpose of this wrapper is to perform TSAN acquire/release. -// Alternatively, if the runtime arranged to safely call TSAN acquire/release, -// it could perform the call directly. -void x_cgo_call_traceback_function(struct cgoTracebackArg* arg) { - void (*pfn)(struct cgoTracebackArg*); - - pfn = _cgo_get_traceback_function(); - if (pfn == nil) { - return; - } - - _cgo_tsan_acquire(); - (*pfn)(arg); - _cgo_tsan_release(); -} - -// Gets the context function to call to record the traceback context -// when calling a Go function from C code. -void (*(_cgo_get_context_function(void)))(struct cgoContextArg*) { - return __atomic_load_n(&cgo_context_function, __ATOMIC_CONSUME); -} - -// Gets the symbolizer function to call to symbolize C frames. -void (*(_cgo_get_symbolizer_function(void)))(struct cgoSymbolizerArg*) { - return __atomic_load_n(&cgo_symbolizer_function, __ATOMIC_CONSUME); -} - -// Call the symbolizer function registered with x_cgo_set_traceback_functions. -// -// See comment on x_cgo_call_traceback_function. -void x_cgo_call_symbolizer_function(struct cgoSymbolizerArg* arg) { - void (*pfn)(struct cgoSymbolizerArg*); - - pfn = _cgo_get_symbolizer_function(); - if (pfn == nil) { - return; - } - - _cgo_tsan_acquire(); - (*pfn)(arg); - _cgo_tsan_release(); -} - -static void -pthread_key_destructor(void* g) { - if (x_crosscall2_ptr != NULL) { - // fn == NULL means dropm. - // We restore g by using the stored g, before dropm in runtime.cgocallback, - // since the g stored in the TLS by Go might be cleared in some platforms, - // before this destructor invoked. - x_crosscall2_ptr(NULL, g, 0, 0); - } -} diff --git a/src/runtime/cgo/gcc_libinit_unix.c b/src/runtime/cgo/gcc_libinit_unix.c new file mode 100644 index 0000000000..0ff0454931 --- /dev/null +++ b/src/runtime/cgo/gcc_libinit_unix.c @@ -0,0 +1,198 @@ +// Copyright 2015 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. + +//go:build unix + +// When cross-compiling with clang to linux/armv5, atomics are emulated +// and cause a compiler warning. This results in a build failure since +// cgo uses -Werror. See #65290. +#pragma GCC diagnostic ignored "-Wpragmas" +#pragma GCC diagnostic ignored "-Wunknown-warning-option" +#pragma GCC diagnostic ignored "-Watomic-alignment" + +#include +#include +#include +#include "libcgo.h" +#include "libcgo_unix.h" + +static pthread_cond_t runtime_init_cond = PTHREAD_COND_INITIALIZER; +static pthread_mutex_t runtime_init_mu = PTHREAD_MUTEX_INITIALIZER; +static int runtime_init_done; + +// pthread_g is a pthread specific key, for storing the g that binded to the C thread. +// The registered pthread_key_destructor will dropm, when the pthread-specified value g is not NULL, +// while a C thread is exiting. +static pthread_key_t pthread_g; +static void pthread_key_destructor(void* g); +uintptr_t x_cgo_pthread_key_created; +void (*x_crosscall2_ptr)(void (*fn)(void *), void *, int, size_t); + +// The traceback function, used when tracing C calls. +static void (*cgo_traceback_function)(struct cgoTracebackArg*); + +// The context function, used when tracing back C calls into Go. +static void (*cgo_context_function)(struct cgoContextArg*); + +// The symbolizer function, used when symbolizing C frames. +static void (*cgo_symbolizer_function)(struct cgoSymbolizerArg*); + +uintptr_t +_cgo_wait_runtime_init_done(void) { + void (*pfn)(struct cgoContextArg*); + int done; + + pfn = __atomic_load_n(&cgo_context_function, __ATOMIC_CONSUME); + + done = 2; + if (__atomic_load_n(&runtime_init_done, __ATOMIC_CONSUME) != done) { + pthread_mutex_lock(&runtime_init_mu); + while (__atomic_load_n(&runtime_init_done, __ATOMIC_CONSUME) == 0) { + pthread_cond_wait(&runtime_init_cond, &runtime_init_mu); + } + + // The key and x_cgo_pthread_key_created are for the whole program, + // whereas the specific and destructor is per thread. + if (x_cgo_pthread_key_created == 0 && pthread_key_create(&pthread_g, pthread_key_destructor) == 0) { + x_cgo_pthread_key_created = 1; + } + + // TODO(iant): For the case of a new C thread calling into Go, such + // as when using -buildmode=c-archive, we know that Go runtime + // initialization is complete but we do not know that all Go init + // functions have been run. We should not fetch cgo_context_function + // until they have been, because that is where a call to + // SetCgoTraceback is likely to occur. We are going to wait for Go + // initialization to be complete anyhow, later, by waiting for + // main_init_done to be closed in cgocallbackg1. We should wait here + // instead. See also issue #15943. + pfn = __atomic_load_n(&cgo_context_function, __ATOMIC_CONSUME); + + __atomic_store_n(&runtime_init_done, done, __ATOMIC_RELEASE); + pthread_mutex_unlock(&runtime_init_mu); + } + + if (pfn != nil) { + struct cgoContextArg arg; + + arg.Context = 0; + (*pfn)(&arg); + return arg.Context; + } + return 0; +} + +// Store the g into a thread-specific value associated with the pthread key pthread_g. +// And pthread_key_destructor will dropm when the thread is exiting. +void x_cgo_bindm(void* g) { + // We assume this will always succeed, otherwise, there might be extra M leaking, + // when a C thread exits after a cgo call. + // We only invoke this function once per thread in runtime.needAndBindM, + // and the next calls just reuse the bound m. + 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); + __atomic_store_n(&runtime_init_done, 1, __ATOMIC_RELEASE); + pthread_cond_broadcast(&runtime_init_cond); + pthread_mutex_unlock(&runtime_init_mu); +} + +// Sets the traceback, context, and symbolizer functions. Called from +// runtime.SetCgoTraceback. +void x_cgo_set_traceback_functions(struct cgoSetTracebackFunctionsArg* arg) { + __atomic_store_n(&cgo_traceback_function, arg->Traceback, __ATOMIC_RELEASE); + __atomic_store_n(&cgo_context_function, arg->Context, __ATOMIC_RELEASE); + __atomic_store_n(&cgo_symbolizer_function, arg->Symbolizer, __ATOMIC_RELEASE); +} + +// Gets the traceback function to call to trace C calls. +void (*(_cgo_get_traceback_function(void)))(struct cgoTracebackArg*) { + return __atomic_load_n(&cgo_traceback_function, __ATOMIC_CONSUME); +} + +// Call the traceback function registered with x_cgo_set_traceback_functions. +// +// The traceback function is an arbitrary user C function which may be built +// with TSAN, and thus must be wrapped with TSAN acquire/release calls. For +// normal cgo calls, cmd/cgo automatically inserts TSAN acquire/release calls. +// Since the traceback, context, and symbolizer functions are registered at +// startup and called via the runtime, they do not get automatic TSAN +// acquire/release calls. +// +// The only purpose of this wrapper is to perform TSAN acquire/release. +// Alternatively, if the runtime arranged to safely call TSAN acquire/release, +// it could perform the call directly. +void x_cgo_call_traceback_function(struct cgoTracebackArg* arg) { + void (*pfn)(struct cgoTracebackArg*); + + pfn = _cgo_get_traceback_function(); + if (pfn == nil) { + return; + } + + _cgo_tsan_acquire(); + (*pfn)(arg); + _cgo_tsan_release(); +} + +// Gets the context function to call to record the traceback context +// when calling a Go function from C code. +void (*(_cgo_get_context_function(void)))(struct cgoContextArg*) { + return __atomic_load_n(&cgo_context_function, __ATOMIC_CONSUME); +} + +// Gets the symbolizer function to call to symbolize C frames. +void (*(_cgo_get_symbolizer_function(void)))(struct cgoSymbolizerArg*) { + return __atomic_load_n(&cgo_symbolizer_function, __ATOMIC_CONSUME); +} + +// Call the symbolizer function registered with x_cgo_set_traceback_functions. +// +// See comment on x_cgo_call_traceback_function. +void x_cgo_call_symbolizer_function(struct cgoSymbolizerArg* arg) { + void (*pfn)(struct cgoSymbolizerArg*); + + pfn = _cgo_get_symbolizer_function(); + if (pfn == nil) { + return; + } + + _cgo_tsan_acquire(); + (*pfn)(arg); + _cgo_tsan_release(); +} + +static void +pthread_key_destructor(void* g) { + if (x_crosscall2_ptr != NULL) { + // fn == NULL means dropm. + // We restore g by using the stored g, before dropm in runtime.cgocallback, + // since the g stored in the TLS by Go might be cleared in some platforms, + // before this destructor invoked. + 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 #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 -#include -#include -#include -#include -#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 -#include -#include -#include -#include -#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 -#include -#include -#include -#include -#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)) -- cgit v1.3-5-g9baa