| /* 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 (); |
| |
| 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 (); |
| } |
| |
| /* 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; |