This patch to libgo makes a better interface available to SWIG when C/C++ code wants to allocate memory that is garbage collected by Go. Previously if the only pointer to the memory were held in the C/C++ code, it was possible for the garbage collector to discard the memory before the C/C++ function returned. This patch fixes this in a straightforward way: it keeps a list of allocated memory, and drops the list when returning to Go. Bootstrapped and ran Go testsuite, and also SWIG testsuite, on x86_64-unknown-linux-gnu. Committed to mainline and 4.7 branch.
Ian
diff -r 9b3e0b116fe4 libgo/go/syscall/libcall_support.go --- a/libgo/go/syscall/libcall_support.go Wed May 30 16:04:09 2012 -0700 +++ b/libgo/go/syscall/libcall_support.go Sun Jun 03 22:30:03 2012 -0700 @@ -10,3 +10,9 @@ func Exitsyscall() func GetErrno() Errno func SetErrno(Errno) + +// These functions are used by CGO and SWIG. +func Cgocall() +func CgocallDone() +func CgocallBack() +func CgocallBackDone() diff -r 9b3e0b116fe4 libgo/runtime/go-cgo.c --- a/libgo/runtime/go-cgo.c Wed May 30 16:04:09 2012 -0700 +++ b/libgo/runtime/go-cgo.c Sun Jun 03 22:30:03 2012 -0700 @@ -4,11 +4,116 @@ Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. */ +#include "runtime.h" #include "go-alloc.h" #include "interface.h" #include "go-panic.h" #include "go-string.h" +/* Go memory allocated by code not written in Go. We keep a linked + list of these allocations so that the garbage collector can see + them. */ + +struct cgoalloc +{ + struct cgoalloc *next; + void *alloc; +}; + +/* Prepare to call from code written in Go to code written in C or + C++. This takes the current goroutine out of the Go scheduler, as + though it were making a system call. Otherwise the program can + lock up if the C code goes to sleep on a mutex or for some other + reason. This idea is to call this function, then immediately call + the C/C++ function. After the C/C++ function returns, call + syscall_cgocalldone. The usual Go code would look like + + syscall.Cgocall() + defer syscall.Cgocalldone() + cfunction() + + */ + +/* We let Go code call these via the syscall package. */ +void syscall_cgocall(void) __asm__ ("syscall.Cgocall"); +void syscall_cgocalldone(void) __asm__ ("syscall.CgocallDone"); +void syscall_cgocallback(void) __asm__ ("syscall.CgocallBack"); +void syscall_cgocallbackdone(void) __asm__ ("syscall.CgocallBackDone"); + +void +syscall_cgocall () +{ + M* m; + G* g; + + m = runtime_m (); + ++m->ncgocall; + g = runtime_g (); + ++g->ncgo; + runtime_entersyscall (); +} + +/* Prepare to return to Go code from C/C++ code. */ + +void +syscall_cgocalldone () +{ + G* g; + + g = runtime_g (); + __go_assert (g != NULL); + --g->ncgo; + if (g->ncgo == 0) + { + /* We are going back to Go, and we are not in a recursive call. + Let the garbage collector clean up any unreferenced + memory. */ + g->cgoalloc = NULL; + } + + /* If we are invoked because the C function called _cgo_panic, then + _cgo_panic will already have exited syscall mode. */ + if (g->status == Gsyscall) + runtime_exitsyscall (); +} + +/* Call back from C/C++ code to Go code. */ + +void +syscall_cgocallback () +{ + runtime_exitsyscall (); +} + +/* Prepare to return to C/C++ code from a callback to Go code. */ + +void +syscall_cgocallbackdone () +{ + runtime_entersyscall (); +} + +/* Allocate memory and save it in a list visible to the Go garbage + collector. */ + +void * +alloc_saved (size_t n) +{ + void *ret; + G *g; + struct cgoalloc *c; + + ret = __go_alloc (n); + + g = runtime_g (); + c = (struct cgoalloc *) __go_alloc (sizeof (struct cgoalloc)); + c->next = g->cgoalloc; + c->alloc = ret; + g->cgoalloc = c; + + return ret; +} + /* These are routines used by SWIG. The gc runtime library provides the same routines under the same name, though in that case the code is required to import runtime/cgo. */ @@ -16,7 +121,12 @@ void * _cgo_allocate (size_t n) { - return __go_alloc (n); + void *ret; + + runtime_exitsyscall (); + ret = alloc_saved (n); + runtime_entersyscall (); + return ret; } extern const struct __go_type_descriptor string_type_descriptor @@ -30,13 +140,39 @@ struct __go_string *ps; struct __go_empty_interface e; + runtime_exitsyscall (); len = __builtin_strlen (p); - data = __go_alloc (len); + data = alloc_saved (len); __builtin_memcpy (data, p, len); - ps = __go_alloc (sizeof *ps); + ps = alloc_saved (sizeof *ps); ps->__data = data; ps->__length = len; e.__type_descriptor = &string_type_descriptor; e.__object = ps; + + /* We don't call runtime_entersyscall here, because normally what + will happen is that we will walk up the stack to a Go deferred + function that calls recover. However, this will do the wrong + thing if this panic is recovered and the stack unwinding is + caught by a C++ exception handler. It might be possible to + handle this by calling runtime_entersyscall in the personality + function in go-unwind.c. FIXME. */ + __go_panic (e); } + +/* Return the number of CGO calls. */ + +int64 runtime_NumCgoCall (void) __asm__ ("runtime.NumCgoCall"); + +int64 +runtime_NumCgoCall (void) +{ + int64 ret; + M* m; + + ret = 0; + for (m = runtime_atomicloadp (&runtime_allm); m != NULL; m = m->alllink) + ret += m->ncgocall; + return ret; +} diff -r 9b3e0b116fe4 libgo/runtime/runtime.h --- a/libgo/runtime/runtime.h Wed May 30 16:04:09 2012 -0700 +++ b/libgo/runtime/runtime.h Sun Jun 03 22:30:03 2012 -0700 @@ -153,6 +153,9 @@ // uintptr sigpc; uintptr gopc; // pc of go statement that created this goroutine + int32 ncgo; + struct cgoalloc *cgoalloc; + Traceback* traceback; ucontext_t context; @@ -174,6 +177,7 @@ int32 profilehz; int32 helpgc; uint32 fastrand; + uint64 ncgocall; Note havenextg; G* nextg; M* alllink; // on allm