Patchwork libgo patch committed: Better SWIG interface for allocating memory

login
register
mail settings
Submitter Ian Taylor
Date June 4, 2012, 5:35 a.m.
Message ID <mcrlik33ai7.fsf@dhcp-172-18-216-180.mtv.corp.google.com>
Download mbox | patch
Permalink /patch/162628/
State New
Headers show

Comments

Ian Taylor - June 4, 2012, 5:35 a.m.
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

Patch

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