Commit 7bea4023 by Ian Lance Taylor

runtime: Better SWIG interface for allocating Go memory from C/C++.

From-SVN: r188164
parent 7b4cf266
...@@ -10,3 +10,9 @@ func Entersyscall() ...@@ -10,3 +10,9 @@ func Entersyscall()
func Exitsyscall() func Exitsyscall()
func GetErrno() Errno func GetErrno() Errno
func SetErrno(Errno) func SetErrno(Errno)
// These functions are used by CGO and SWIG.
func Cgocall()
func CgocallDone()
func CgocallBack()
func CgocallBackDone()
...@@ -4,11 +4,116 @@ ...@@ -4,11 +4,116 @@
Use of this source code is governed by a BSD-style Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file. */ license that can be found in the LICENSE file. */
#include "runtime.h"
#include "go-alloc.h" #include "go-alloc.h"
#include "interface.h" #include "interface.h"
#include "go-panic.h" #include "go-panic.h"
#include "go-string.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 /* These are routines used by SWIG. The gc runtime library provides
the same routines under the same name, though in that case the code the same routines under the same name, though in that case the code
is required to import runtime/cgo. */ is required to import runtime/cgo. */
...@@ -16,7 +121,12 @@ ...@@ -16,7 +121,12 @@
void * void *
_cgo_allocate (size_t n) _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 extern const struct __go_type_descriptor string_type_descriptor
...@@ -30,13 +140,39 @@ _cgo_panic (const char *p) ...@@ -30,13 +140,39 @@ _cgo_panic (const char *p)
struct __go_string *ps; struct __go_string *ps;
struct __go_empty_interface e; struct __go_empty_interface e;
runtime_exitsyscall ();
len = __builtin_strlen (p); len = __builtin_strlen (p);
data = __go_alloc (len); data = alloc_saved (len);
__builtin_memcpy (data, p, len); __builtin_memcpy (data, p, len);
ps = __go_alloc (sizeof *ps); ps = alloc_saved (sizeof *ps);
ps->__data = data; ps->__data = data;
ps->__length = len; ps->__length = len;
e.__type_descriptor = &string_type_descriptor; e.__type_descriptor = &string_type_descriptor;
e.__object = ps; 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); __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;
}
...@@ -153,6 +153,9 @@ struct G ...@@ -153,6 +153,9 @@ struct G
// uintptr sigpc; // uintptr sigpc;
uintptr gopc; // pc of go statement that created this goroutine uintptr gopc; // pc of go statement that created this goroutine
int32 ncgo;
struct cgoalloc *cgoalloc;
Traceback* traceback; Traceback* traceback;
ucontext_t context; ucontext_t context;
...@@ -174,6 +177,7 @@ struct M ...@@ -174,6 +177,7 @@ struct M
int32 profilehz; int32 profilehz;
int32 helpgc; int32 helpgc;
uint32 fastrand; uint32 fastrand;
uint64 ncgocall;
Note havenextg; Note havenextg;
G* nextg; G* nextg;
M* alllink; // on allm M* alllink; // on allm
......
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