diff options
Diffstat (limited to 'src/pkg/runtime/cgocall.c')
-rw-r--r-- | src/pkg/runtime/cgocall.c | 249 |
1 files changed, 249 insertions, 0 deletions
diff --git a/src/pkg/runtime/cgocall.c b/src/pkg/runtime/cgocall.c new file mode 100644 index 000000000..829448b02 --- /dev/null +++ b/src/pkg/runtime/cgocall.c @@ -0,0 +1,249 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "runtime.h" +#include "arch.h" +#include "stack.h" +#include "cgocall.h" + +// Cgo call and callback support. +// +// To call into the C function f from Go, the cgo-generated code calls +// runtime.cgocall(_cgo_Cfunc_f, frame), where _cgo_Cfunc_f is a +// gcc-compiled function written by cgo. +// +// runtime.cgocall (below) locks g to m, calls entersyscall +// so as not to block other goroutines or the garbage collector, +// and then calls runtime.asmcgocall(_cgo_Cfunc_f, frame). +// +// runtime.asmcgocall (in $GOARCH/asm.s) switches to the m->g0 stack +// (assumed to be an operating system-allocated stack, so safe to run +// gcc-compiled code on) and calls _cgo_Cfunc_f(frame). +// +// _cgo_Cfunc_f invokes the actual C function f with arguments +// taken from the frame structure, records the results in the frame, +// and returns to runtime.asmcgocall. +// +// After it regains control, runtime.asmcgocall switches back to the +// original g (m->curg)'s stack and returns to runtime.cgocall. +// +// After it regains control, runtime.cgocall calls exitsyscall, which blocks +// until this m can run Go code without violating the $GOMAXPROCS limit, +// and then unlocks g from m. +// +// The above description skipped over the possibility of the gcc-compiled +// function f calling back into Go. If that happens, we continue down +// the rabbit hole during the execution of f. +// +// To make it possible for gcc-compiled C code to call a Go function p.GoF, +// cgo writes a gcc-compiled function named GoF (not p.GoF, since gcc doesn't +// know about packages). The gcc-compiled C function f calls GoF. +// +// GoF calls crosscall2(_cgoexp_GoF, frame, framesize). Crosscall2 +// (in cgo/$GOOS.S, a gcc-compiled assembly file) is a two-argument +// adapter from the gcc function call ABI to the 6c function call ABI. +// It is called from gcc to call 6c functions. In this case it calls +// _cgoexp_GoF(frame, framesize), still running on m->g0's stack +// and outside the $GOMAXPROCS limit. Thus, this code cannot yet +// call arbitrary Go code directly and must be careful not to allocate +// memory or use up m->g0's stack. +// +// _cgoexp_GoF calls runtime.cgocallback(p.GoF, frame, framesize). +// (The reason for having _cgoexp_GoF instead of writing a crosscall3 +// to make this call directly is that _cgoexp_GoF, because it is compiled +// with 6c instead of gcc, can refer to dotted names like +// runtime.cgocallback and p.GoF.) +// +// runtime.cgocallback (in $GOOS/asm.s) switches from m->g0's +// stack to the original g (m->curg)'s stack, on which it calls +// runtime.cgocallbackg(p.GoF, frame, framesize). +// As part of the stack switch, runtime.cgocallback saves the current +// SP as m->g0->sched.sp, so that any use of m->g0's stack during the +// execution of the callback will be done below the existing stack frames. +// Before overwriting m->g0->sched.sp, it pushes the old value on the +// m->g0 stack, so that it can be restored later. +// +// runtime.cgocallbackg (below) is now running on a real goroutine +// stack (not an m->g0 stack). First it calls runtime.exitsyscall, which will +// block until the $GOMAXPROCS limit allows running this goroutine. +// Once exitsyscall has returned, it is safe to do things like call the memory +// allocator or invoke the Go callback function p.GoF. runtime.cgocallback +// first defers a function to unwind m->g0.sched.sp, so that if p.GoF +// panics, m->g0.sched.sp will be restored to its old value: the m->g0 stack +// and the m->curg stack will be unwound in lock step. +// Then it calls p.GoF. Finally it pops but does not execute the deferred +// function, calls runtime.entersyscall, and returns to runtime.cgocallback. +// +// After it regains control, runtime.cgocallback switches back to +// m->g0's stack (the pointer is still in m->g0.sched.sp), restores the old +// m->g0.sched.sp value from the stack, and returns to _cgoexp_GoF. +// +// _cgoexp_GoF immediately returns to crosscall2, which restores the +// callee-save registers for gcc and returns to GoF, which returns to f. + +void *initcgo; /* filled in by dynamic linker when Cgo is available */ + +static void unlockm(void); +static void unwindm(void); + +// Call from Go to C. + +void +runtime·cgocall(void (*fn)(void*), void *arg) +{ + Defer *d; + + if(!runtime·iscgo) + runtime·throw("cgocall unavailable"); + + if(fn == 0) + runtime·throw("cgocall nil"); + + m->ncgocall++; + + /* + * Lock g to m to ensure we stay on the same stack if we do a + * cgo callback. + */ + d = nil; + if(m->lockedg == nil) { + m->lockedg = g; + g->lockedm = m; + + // Add entry to defer stack in case of panic. + d = runtime·malloc(sizeof(*d)); + d->fn = (byte*)unlockm; + d->siz = 0; + d->link = g->defer; + d->argp = (void*)-1; // unused because unwindm never recovers + g->defer = d; + } + + /* + * Announce we are entering a system call + * so that the scheduler knows to create another + * M to run goroutines while we are in the + * foreign code. + * + * The call to asmcgocall is guaranteed not to + * split the stack and does not allocate memory, + * so it is safe to call while "in a system call", outside + * the $GOMAXPROCS accounting. + */ + runtime·entersyscall(); + runtime·asmcgocall(fn, arg); + runtime·exitsyscall(); + + if(d != nil) { + if(g->defer != d || d->fn != (byte*)unlockm) + runtime·throw("runtime: bad defer entry in cgocallback"); + g->defer = d->link; + runtime·free(d); + unlockm(); + } +} + +static void +unlockm(void) +{ + m->lockedg = nil; + g->lockedm = nil; +} + +void +runtime·Cgocalls(int64 ret) +{ + M *m; + + ret = 0; + for(m=runtime·atomicloadp(&runtime·allm); m; m=m->alllink) + ret += m->ncgocall; + FLUSH(&ret); +} + +// Helper functions for cgo code. + +void (*_cgo_malloc)(void*); +void (*_cgo_free)(void*); + +void* +runtime·cmalloc(uintptr n) +{ + struct { + uint64 n; + void *ret; + } a; + + a.n = n; + a.ret = nil; + runtime·cgocall(_cgo_malloc, &a); + return a.ret; +} + +void +runtime·cfree(void *p) +{ + runtime·cgocall(_cgo_free, p); +} + +// Call from C back to Go. + +void +runtime·cgocallbackg(void (*fn)(void), void *arg, uintptr argsize) +{ + Defer *d; + + if(g != m->curg) + runtime·throw("runtime: bad g in cgocallback"); + + runtime·exitsyscall(); // coming out of cgo call + + // Add entry to defer stack in case of panic. + d = runtime·malloc(sizeof(*d)); + d->fn = (byte*)unwindm; + d->siz = 0; + d->link = g->defer; + d->argp = (void*)-1; // unused because unwindm never recovers + g->defer = d; + + // Invoke callback. + reflect·call((byte*)fn, arg, argsize); + + // Pop defer. + // Do not unwind m->g0->sched.sp. + // Our caller, cgocallback, will do that. + if(g->defer != d || d->fn != (byte*)unwindm) + runtime·throw("runtime: bad defer entry in cgocallback"); + g->defer = d->link; + runtime·free(d); + + runtime·entersyscall(); // going back to cgo call +} + +static void +unwindm(void) +{ + // Restore sp saved by cgocallback during + // unwind of g's stack (see comment at top of file). + switch(thechar){ + default: + runtime·throw("runtime: unwindm not implemented"); + case '8': + case '6': + m->g0->sched.sp = *(void**)m->g0->sched.sp; + break; + } +} + +void +runtime·badcgocallback(void) // called from assembly +{ + runtime·throw("runtime: misaligned stack in cgocallback"); +} + +void +runtime·cgounimpl(void) // called from (incomplete) assembly +{ + runtime·throw("runtime: cgo not implemented"); +} |