diff options
Diffstat (limited to 'src/pkg/runtime/cgocall.c')
-rw-r--r-- | src/pkg/runtime/cgocall.c | 102 |
1 files changed, 69 insertions, 33 deletions
diff --git a/src/pkg/runtime/cgocall.c b/src/pkg/runtime/cgocall.c index b82966546..2a04453fd 100644 --- a/src/pkg/runtime/cgocall.c +++ b/src/pkg/runtime/cgocall.c @@ -7,6 +7,7 @@ #include "stack.h" #include "cgocall.h" #include "race.h" +#include "../../cmd/ld/textflag.h" // Cgo call and callback support. // @@ -86,11 +87,6 @@ void *_cgo_init; /* filled in by dynamic linker when Cgo is available */ static int64 cgosync; /* represents possible synchronization in C code */ -// These two are only used by the architecture where TLS based storage isn't -// the default for g and m (e.g., ARM) -void *_cgo_load_gm; /* filled in by dynamic linker when Cgo is available */ -void *_cgo_save_gm; /* filled in by dynamic linker when Cgo is available */ - static void unwindm(void); // Call from Go to C. @@ -98,15 +94,6 @@ static void unwindm(void); static void endcgo(void); static FuncVal endcgoV = { endcgo }; -// Gives a hint that the next syscall -// executed by the current goroutine will block. -// Currently used only on windows. -void -net·runtime_blockingSyscallHint(void) -{ - g->blockingsyscall = true; -} - void runtime·cgocall(void (*fn)(void*), void *arg) { @@ -126,6 +113,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++; /* @@ -154,11 +145,7 @@ runtime·cgocall(void (*fn)(void*), void *arg) * so it is safe to call while "in a system call", outside * the $GOMAXPROCS accounting. */ - if(g->blockingsyscall) { - g->blockingsyscall = false; - runtime·entersyscallblock(); - } else - runtime·entersyscall(); + runtime·entersyscall(); runtime·asmcgocall(fn, arg); runtime·exitsyscall(); @@ -211,6 +198,8 @@ runtime·cmalloc(uintptr n) a.n = n; a.ret = nil; runtime·cgocall(_cgo_malloc, &a); + if(a.ret == nil) + runtime·throw("runtime: C malloc failed"); return a.ret; } @@ -224,20 +213,66 @@ runtime·cfree(void *p) static FuncVal unwindmf = {unwindm}; +typedef struct CallbackArgs CallbackArgs; +struct CallbackArgs +{ + FuncVal *fn; + void *arg; + uintptr argsize; +}; + +// Location of callback arguments depends on stack frame layout +// and size of stack frame of cgocallback_gofunc. + +// On arm, stack frame is two words and there's a saved LR between +// SP and the stack frame and between the stack frame and the arguments. +#ifdef GOARCH_arm +#define CBARGS (CallbackArgs*)((byte*)m->g0->sched.sp+4*sizeof(void*)) +#endif + +// On amd64, stack frame is one word, plus caller PC. +#ifdef GOARCH_amd64 +#define CBARGS (CallbackArgs*)((byte*)m->g0->sched.sp+2*sizeof(void*)) +#endif + +// On 386, stack frame is three words, plus caller PC. +#ifdef GOARCH_386 +#define CBARGS (CallbackArgs*)((byte*)m->g0->sched.sp+4*sizeof(void*)) +#endif + +void runtime·cgocallbackg1(void); + +#pragma textflag NOSPLIT void -runtime·cgocallbackg(FuncVal *fn, void *arg, uintptr argsize) +runtime·cgocallbackg(void) { - Defer d; + if(g != m->curg) { + runtime·prints("runtime: bad g in cgocallback"); + runtime·exit(2); + } if(m->racecall) { - reflect·call(fn, arg, argsize); - return; + // We were not in syscall, so no need to call runtime·exitsyscall. + // However we must set m->locks for the following reason. + // Race detector runtime makes __tsan_symbolize cgo callback + // holding internal mutexes. The mutexes are not cooperative with Go scheduler. + // So if we deschedule a goroutine that holds race detector internal mutex + // (e.g. preempt it), another goroutine will deadlock trying to acquire the same mutex. + m->locks++; + runtime·cgocallbackg1(); + m->locks--; + } else { + runtime·exitsyscall(); // coming out of cgo call + runtime·cgocallbackg1(); + runtime·entersyscall(); // going back to cgo call } +} - if(g != m->curg) - runtime·throw("runtime: bad g in cgocallback"); - - runtime·exitsyscall(); // coming out of cgo call +void +runtime·cgocallbackg1(void) +{ + CallbackArgs *cb; + Defer d; if(m->needextram) { m->needextram = 0; @@ -253,13 +288,14 @@ runtime·cgocallbackg(FuncVal *fn, void *arg, uintptr argsize) d.free = false; g->defer = &d; - if(raceenabled) + if(raceenabled && !m->racecall) runtime·raceacquire(&cgosync); // Invoke callback. - reflect·call(fn, arg, argsize); + cb = CBARGS; + runtime·newstackcall(cb->fn, cb->arg, cb->argsize); - if(raceenabled) + if(raceenabled && !m->racecall) runtime·racereleasemerge(&cgosync); // Pop defer. @@ -268,8 +304,6 @@ runtime·cgocallbackg(FuncVal *fn, void *arg, uintptr argsize) if(g->defer != &d || d.fn != &unwindmf) runtime·throw("runtime: bad defer entry in cgocallback"); g->defer = d.link; - - runtime·entersyscall(); // going back to cgo call } static void @@ -282,9 +316,11 @@ unwindm(void) runtime·throw("runtime: unwindm not implemented"); case '8': case '6': - case '5': m->g0->sched.sp = *(uintptr*)m->g0->sched.sp; break; + case '5': + m->g0->sched.sp = *(uintptr*)((byte*)m->g0->sched.sp + 4); + break; } } |