Commit 34c67eb2 authored by Dmitriy Vyukov's avatar Dmitriy Vyukov

runtime: detect deadlocks in programs using cgo

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
parent a3393e17
......@@ -12,3 +12,4 @@
#include "../runtime.h"
bool runtime·iscgo = 1;
uint32 runtime·needextram = 1; // create an extra M on first cgo call
......@@ -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++;
/*
......
......@@ -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
......
......@@ -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
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment