]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: make cgocallback wait on package init
authorDavid Crawshaw <crawshaw@golang.org>
Mon, 13 Apr 2015 23:31:39 +0000 (19:31 -0400)
committerDavid Crawshaw <crawshaw@golang.org>
Tue, 14 Apr 2015 13:39:02 +0000 (13:39 +0000)
With the new buildmodes c-archive and c-shared, it is possible for a
cgo call to come in early in the lifecycle of a Go program. Calls
before the runtime has been initialized are caught by
_cgo_wait_runtime_init_done. However a call can come in after the
runtime has initialized, but before the program's package init
functions have finished running.

To avoid this cgocallback checks m.ncgo to see if we are on a thread
running Go. If not, we may be a foreign thread and it blocks until
main_init is complete.

Change-Id: I7a9f137fa2a40c322a0b93764261f9aa17fcf5b8
Reviewed-on: https://go-review.googlesource.com/8897
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Run-TryBot: David Crawshaw <crawshaw@golang.org>

misc/cgo/testcarchive/main.c
misc/cgo/testcarchive/src/libgo/libgo.go
src/runtime/cgocall.go
src/runtime/proc.go

index 3ce12682b83a213a101f3515083fb40201cd237a..f4d59f7636f9e99c88e26cd196ec62e935d8a24c 100644 (file)
@@ -12,13 +12,13 @@ extern int32_t FromPkg();
 int main(void) {
        int32_t res;
 
-       if (DidMainRun()) {
-               fprintf(stderr, "ERROR: buildmode=c-archive should not run main\n");
+       if (!DidInitRun()) {
+               fprintf(stderr, "ERROR: buildmode=c-archive init should run\n");
                return 2;
        }
 
-       if (!DidInitRun()) {
-               fprintf(stderr, "ERROR: buildmode=c-archive init should run\n");
+       if (DidMainRun()) {
+               fprintf(stderr, "ERROR: buildmode=c-archive should not run main\n");
                return 2;
        }
 
index 25ddda3f762e7a66d4598c2afe86dbf36af37205..87cb79cabe61cb354afd12dfc989e484844e34ca 100644 (file)
@@ -4,21 +4,39 @@
 
 package main
 
-import _ "p"
+import (
+       _ "p"
+       "syscall"
+       "time"
+)
 
 import "C"
 
-var (
-       ranInit bool
-       ranMain bool
-)
+var initCh = make(chan int, 1)
+var ranMain bool
 
-func init() { ranInit = true }
+func init() {
+       // emulate an exceedingly slow package initialization function
+       time.Sleep(100 * time.Millisecond)
+       initCh <- 42
+}
 
 func main() { ranMain = true }
 
 //export DidInitRun
-func DidInitRun() bool { return ranInit }
+func DidInitRun() bool {
+       select {
+       case x := <-initCh:
+               if x != 42 {
+                       // Just in case initCh was not correctly made.
+                       println("want init value of 42, got: ", x)
+                       syscall.Exit(2)
+               }
+               return true
+       default:
+               return false
+       }
+}
 
 //export DidMainRun
 func DidMainRun() bool { return ranMain }
index 5b24304c1d515224d0237804d1d3eca00cf7d76f..d4d0cf47c38cae8e661c6d750d0f880bc12be472 100644 (file)
@@ -193,6 +193,14 @@ func cgocallbackg1() {
                systemstack(newextram)
        }
 
+       if gp.m.ncgo == 0 {
+               // 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
+       }
+
        // Add entry to defer stack in case of panic.
        restore := true
        defer unwindm(&restore)
index 7b6183d905d88493633aac987dd82de240578401..50f9dd7f5219bc093794b928a6d84a6e1f753a2a 100644 (file)
@@ -12,6 +12,12 @@ func runtime_init()
 //go:linkname main_init main.init
 func main_init()
 
+// 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
+
 //go:linkname main_main main.main
 func main_main()
 
@@ -70,6 +76,7 @@ func main() {
                // Allocate new M as main_main() is expected to block forever.
                systemstack(newextram)
        }
+       main_init_done = make(chan bool)
        if iscgo {
                if _cgo_thread_start == nil {
                        throw("_cgo_thread_start missing")
@@ -95,6 +102,7 @@ func main() {
        }
 
        main_init()
+       close(main_init_done)
 
        needUnlock = false
        unlockOSThread()