]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: put a bool in front of initialization-done channel
authorIan Lance Taylor <iant@golang.org>
Tue, 10 Feb 2026 18:17:58 +0000 (10:17 -0800)
committerGopher Robot <gobot@golang.org>
Wed, 11 Feb 2026 02:43:34 +0000 (18:43 -0800)
Calls to Go functions from threads not started by Go have to wait
for Go initialization to be complete. Before this CL they did this
by receiving from a channel that is closed when initialization is done.
That works well but introduces a channel operation into cgo calls.
This is particularly frustrating because the channel is approximately
always closed.

This CL adds an atomic bool before the channel, so that in the normal
case we are just adding a single locked memory load. We still use
the channel as a fallback.

For #77522

Change-Id: I8f609bf349bb0f836cefa5f6ad6d0c3c7bbfe5e0
Reviewed-on: https://go-review.googlesource.com/c/go/+/743940
Reviewed-by: Keith Randall <khr@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
Auto-Submit: Ian Lance Taylor <iant@golang.org>

src/runtime/cgocall.go
src/runtime/proc.go

index b36da2f12b7264baf48f5efca70d79236e8eda75..7f05c13aee428de61d52ce029c4e331a9e1c4726 100644 (file)
@@ -424,8 +424,13 @@ func cgocallbackg1(fn, frame unsafe.Pointer, ctxt uintptr) {
                // The C call to Go came from a thread not currently running
                // any Go. In the case of -buildmode=c-archive or c-shared,
                // this call may be coming in before package initialization
-               // is complete. Wait until it is.
-               <-main_init_done
+               // is complete. Don't proceed until it is.
+               //
+               // We check a bool first for speed, and wait on a channel
+               // if it's not ready.
+               if !mainInitDone.Load() {
+                       <-mainInitDoneChan
+               }
        }
 
        // Check whether the profiler needs to be turned on or off; this route to
index 005c875cbf180198a4aa9123288b6daca27acbdd..8027ac5bee39bd09c34ebdf5694d3fcd5591e87a 100644 (file)
@@ -127,11 +127,14 @@ var (
 // done to start up the runtime. It is built by the linker.
 var runtime_inittasks []*initTask
 
-// main_init_done is a signal used by cgocallbackg that initialization
-// has been completed. It is made before _cgo_notify_runtime_init_done,
-// so all cgo calls can rely on it existing. When main_init is complete,
-// it is closed, meaning cgocallbackg can reliably receive from it.
-var main_init_done chan bool
+// mainInitDone is a signal used by cgocallbackg that initialization
+// has been completed. If this is false, wait on mainInitDoneChan.
+var mainInitDone atomic.Bool
+
+// mainInitDoneChan is closed after initialization has been completed.
+// It is made before _cgo_notify_runtime_init_done, so all cgo
+// calls can rely on it existing.
+var mainInitDoneChan chan bool
 
 //go:linkname main_main main.main
 func main_main()
@@ -213,7 +216,7 @@ func main() {
        gcenable()
        defaultGOMAXPROCSUpdateEnable() // don't STW before runtime initialized.
 
-       main_init_done = make(chan bool)
+       mainInitDoneChan = make(chan bool)
        if iscgo {
                if _cgo_pthread_key_created == nil {
                        throw("_cgo_pthread_key_created missing")
@@ -265,7 +268,8 @@ func main() {
        // of collecting statistics in malloc and newproc
        inittrace.active = false
 
-       close(main_init_done)
+       mainInitDone.Store(true)
+       close(mainInitDoneChan)
 
        needUnlock = false
        unlockOSThread()