]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: detect deadlocks in programs using cgo
authorDmitriy Vyukov <dvyukov@google.com>
Wed, 22 May 2013 18:57:47 +0000 (22:57 +0400)
committerDmitriy Vyukov <dvyukov@google.com>
Wed, 22 May 2013 18:57:47 +0000 (22:57 +0400)
When cgo is used, runtime creates an additional M to handle callbacks on threads not created by Go.
This effectively disabled deadlock detection, which is a right thing, because Go program can be blocked
and only serve callbacks on external threads.
This also disables deadlock detection under race detector, because it happens to use cgo.
With this change the additional M is created lazily on first cgo call. So deadlock detector
works for programs that import "C", "net" or "net/http/pprof" but do not use them in fact.
Also fixes deadlock detector under race detector.
It should be fine to create the M later, because C code can not call into Go before first cgo call,
because C code does not know when Go initialization has completed. So a Go program need to call into C
first either to create an external thread, or notify a thread created in global ctor that Go
initialization has completed.
Fixes #4973.
Fixes #5475.

R=golang-dev, minux.ma, iant
CC=golang-dev
https://golang.org/cl/9303046

src/pkg/runtime/cgo/iscgo.c
src/pkg/runtime/cgocall.c
src/pkg/runtime/proc.c
src/pkg/runtime/runtime.h

index eb6f5c09d769ba744d9dcf9e551bd0b460ddcb8d..0907a19581006d33358775484641c3674334deca 100644 (file)
@@ -12,3 +12,4 @@
 #include "../runtime.h"
 
 bool runtime·iscgo = 1;
+uint32 runtime·needextram = 1;  // create an extra M on first cgo call
index b829665462399ecdcf4a5c1dc37edfbd4fb5b6df..16bc76554911a7932fd22b848b7b6741702e6f92 100644 (file)
@@ -126,6 +126,10 @@ runtime·cgocall(void (*fn)(void*), void *arg)
        if(raceenabled)
                runtime·racereleasemerge(&cgosync);
 
+       // Create an extra M for callbacks on threads not created by Go on first cgo call.
+       if(runtime·needextram && runtime·cas(&runtime·needextram, 1, 0))
+               runtime·newextram();
+
        m->ncgocall++;
 
        /*
index 10170d874e181e0f6411ce9bc961151df78c4327..d6d308e524526a75cd66664c9e8799f787f0f9f9 100644 (file)
@@ -60,6 +60,7 @@ enum { MaxGomaxprocs = 1<<8 };
 
 Sched  runtime·sched;
 int32  runtime·gomaxprocs;
+uint32 runtime·needextram;
 bool   runtime·singleproc;
 bool   runtime·iscgo;
 uint32 runtime·gcwaiting;
@@ -475,11 +476,8 @@ runtime·mstart(void)
 
        // Install signal handlers; after minit so that minit can
        // prepare the thread to be able to handle the signals.
-       if(m == &runtime·m0) {
+       if(m == &runtime·m0)
                runtime·initsig();
-               if(runtime·iscgo)
-                       runtime·newextram();
-       }
        
        if(m->mstartfn)
                m->mstartfn();
@@ -587,6 +585,14 @@ runtime·needm(byte x)
 {
        M *mp;
 
+       if(runtime·needextram) {
+               // Can happen if C/C++ code calls Go from a global ctor.
+               // Can not throw, because scheduler is not initialized yet.
+               runtime·write(2, "fatal error: cgo callback before cgo call\n",
+                       sizeof("fatal error: cgo callback before cgo call\n")-1);
+               runtime·exit(1);
+       }
+
        // Lock extra list, take head, unlock popped list.
        // nilokay=false is safe here because of the invariant above,
        // that the extra list always contains or will soon contain
index ef162e9bbbb34339f41af9491f2fe60b3e385248..17f8c9a94a5d0ff5a2d807e027b81af4c32199f3 100644 (file)
@@ -657,6 +657,7 @@ extern      G*      runtime·lastg;
 extern M*      runtime·allm;
 extern P**     runtime·allp;
 extern int32   runtime·gomaxprocs;
+extern uint32  runtime·needextram;
 extern bool    runtime·singleproc;
 extern uint32  runtime·panicking;
 extern uint32  runtime·gcwaiting;             // gc is waiting to run