aboutsummaryrefslogtreecommitdiff
path: root/src/runtime
diff options
context:
space:
mode:
authorqmuntal <quimmuntal@gmail.com>2026-02-17 18:23:43 +0100
committerQuim Muntal <quimmuntal@gmail.com>2026-03-18 22:58:11 -0700
commitbf1c1b3bde50f33593cd0db8f19812ea957964f5 (patch)
tree8e9b7ddccc889900dec98b2ebff7f03ef52afcc2 /src/runtime
parentc1c0af1e16c4c932d4c05442d9717dac0dc08a79 (diff)
downloadgo-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.go32
-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.c42
-rw-r--r--src/runtime/cgo/gcc_unix.c2
-rw-r--r--src/runtime/cgo/gcc_util.c19
-rw-r--r--src/runtime/cgo/gcc_windows_386.c54
-rw-r--r--src/runtime/cgo/gcc_windows_amd64.c53
-rw-r--r--src/runtime/cgo/gcc_windows_arm64.c41
-rw-r--r--src/runtime/cgo/libcgo_windows.h6
-rw-r--r--src/runtime/cgo/pthread_unix.c4
-rw-r--r--src/runtime/os_windows.go41
-rw-r--r--src/runtime/proc.go11
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))