4501d9ad44
cgo should lock the M. See also https://golang.org/cl/18882 . Reviewed-on: https://go-review.googlesource.com/18883 From-SVN: r233670
258 lines
6.3 KiB
C
258 lines
6.3 KiB
C
/* go-cgo.c -- SWIG support routines for libgo.
|
|
|
|
Copyright 2011 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 "go-alloc.h"
|
|
#include "interface.h"
|
|
#include "go-panic.h"
|
|
#include "go-type.h"
|
|
|
|
extern void __go_receive (ChanType *, Hchan *, byte *);
|
|
|
|
/* 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__ (GOSYM_PREFIX "syscall.Cgocall");
|
|
void syscall_cgocalldone(void) __asm__ (GOSYM_PREFIX "syscall.CgocallDone");
|
|
void syscall_cgocallback(void) __asm__ (GOSYM_PREFIX "syscall.CgocallBack");
|
|
void syscall_cgocallbackdone(void) __asm__ (GOSYM_PREFIX "syscall.CgocallBackDone");
|
|
|
|
void
|
|
syscall_cgocall ()
|
|
{
|
|
M* m;
|
|
G* g;
|
|
|
|
if (runtime_needextram && runtime_cas (&runtime_needextram, 1, 0))
|
|
runtime_newextram ();
|
|
|
|
runtime_lockOSThread();
|
|
|
|
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->cgomal = 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 ();
|
|
|
|
runtime_unlockOSThread();
|
|
}
|
|
|
|
/* Call back from C/C++ code to Go code. */
|
|
|
|
void
|
|
syscall_cgocallback ()
|
|
{
|
|
M *mp;
|
|
|
|
mp = runtime_m ();
|
|
if (mp == NULL)
|
|
{
|
|
runtime_needm ();
|
|
mp = runtime_m ();
|
|
mp->dropextram = true;
|
|
}
|
|
|
|
runtime_exitsyscall ();
|
|
|
|
if (runtime_g ()->ncgo == 0)
|
|
{
|
|
/* The C call to Go came from a thread not currently running any
|
|
Go. In the case of -buildmode=c-archive or c-shared, this
|
|
call may be coming in before package initialization is
|
|
complete. Wait until it is. */
|
|
__go_receive (NULL, runtime_main_init_done, NULL);
|
|
}
|
|
|
|
mp = runtime_m ();
|
|
if (mp->needextram)
|
|
{
|
|
mp->needextram = 0;
|
|
runtime_newextram ();
|
|
}
|
|
}
|
|
|
|
/* Prepare to return to C/C++ code from a callback to Go code. */
|
|
|
|
void
|
|
syscall_cgocallbackdone ()
|
|
{
|
|
M *mp;
|
|
|
|
runtime_entersyscall ();
|
|
mp = runtime_m ();
|
|
if (mp->dropextram && runtime_g ()->ncgo == 0)
|
|
{
|
|
mp->dropextram = false;
|
|
runtime_dropm ();
|
|
}
|
|
}
|
|
|
|
/* Allocate memory and save it in a list visible to the Go garbage
|
|
collector. */
|
|
|
|
void *
|
|
alloc_saved (size_t n)
|
|
{
|
|
void *ret;
|
|
G *g;
|
|
CgoMal *c;
|
|
|
|
ret = __go_alloc (n);
|
|
|
|
g = runtime_g ();
|
|
c = (CgoMal *) __go_alloc (sizeof (CgoMal));
|
|
c->next = g->cgomal;
|
|
c->alloc = ret;
|
|
g->cgomal = 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. */
|
|
|
|
void *
|
|
_cgo_allocate (size_t n)
|
|
{
|
|
void *ret;
|
|
|
|
runtime_exitsyscall ();
|
|
ret = alloc_saved (n);
|
|
runtime_entersyscall ();
|
|
return ret;
|
|
}
|
|
|
|
extern const struct __go_type_descriptor string_type_descriptor
|
|
__asm__ (GOSYM_PREFIX "__go_tdn_string");
|
|
|
|
void
|
|
_cgo_panic (const char *p)
|
|
{
|
|
intgo len;
|
|
unsigned char *data;
|
|
String *ps;
|
|
struct __go_empty_interface e;
|
|
|
|
runtime_exitsyscall ();
|
|
len = __builtin_strlen (p);
|
|
data = alloc_saved (len);
|
|
__builtin_memcpy (data, p, len);
|
|
ps = alloc_saved (sizeof *ps);
|
|
ps->str = data;
|
|
ps->len = 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);
|
|
}
|
|
|
|
/* Used for _cgo_wait_runtime_init_done. This is based on code in
|
|
runtime/cgo/gcc_libinit.c in the master library. */
|
|
|
|
static pthread_cond_t runtime_init_cond = PTHREAD_COND_INITIALIZER;
|
|
static pthread_mutex_t runtime_init_mu = PTHREAD_MUTEX_INITIALIZER;
|
|
static _Bool runtime_init_done;
|
|
|
|
/* This is called by exported cgo functions to ensure that the runtime
|
|
has been initialized before we enter the function. This is needed
|
|
when building with -buildmode=c-archive or similar. */
|
|
|
|
void
|
|
_cgo_wait_runtime_init_done (void)
|
|
{
|
|
int err;
|
|
|
|
if (__atomic_load_n (&runtime_init_done, __ATOMIC_ACQUIRE))
|
|
return;
|
|
|
|
err = pthread_mutex_lock (&runtime_init_mu);
|
|
if (err != 0)
|
|
abort ();
|
|
while (!__atomic_load_n (&runtime_init_done, __ATOMIC_ACQUIRE))
|
|
{
|
|
err = pthread_cond_wait (&runtime_init_cond, &runtime_init_mu);
|
|
if (err != 0)
|
|
abort ();
|
|
}
|
|
err = pthread_mutex_unlock (&runtime_init_mu);
|
|
if (err != 0)
|
|
abort ();
|
|
}
|
|
|
|
/* This is called by runtime_main after the Go runtime is
|
|
initialized. */
|
|
|
|
void
|
|
_cgo_notify_runtime_init_done (void)
|
|
{
|
|
int err;
|
|
|
|
err = pthread_mutex_lock (&runtime_init_mu);
|
|
if (err != 0)
|
|
abort ();
|
|
__atomic_store_n (&runtime_init_done, 1, __ATOMIC_RELEASE);
|
|
err = pthread_cond_broadcast (&runtime_init_cond);
|
|
if (err != 0)
|
|
abort ();
|
|
err = pthread_mutex_unlock (&runtime_init_mu);
|
|
if (err != 0)
|
|
abort ();
|
|
}
|
|
|
|
// runtime_iscgo is set to true if some cgo code is linked in.
|
|
// This is done by a constructor in the cgo generated code.
|
|
_Bool runtime_iscgo;
|
|
|
|
// runtime_cgoHasExtraM is set on startup when an extra M is created
|
|
// for cgo. The extra M must be created before any C/C++ code calls
|
|
// cgocallback.
|
|
_Bool runtime_cgoHasExtraM;
|