1 /* go-cgo.c -- SWIG support routines for libgo.
2 
3    Copyright 2011 The Go Authors. All rights reserved.
4    Use of this source code is governed by a BSD-style
5    license that can be found in the LICENSE file.  */
6 
7 #include "runtime.h"
8 #include "go-alloc.h"
9 #include "interface.h"
10 #include "go-panic.h"
11 #include "go-type.h"
12 
13 extern void __go_receive (ChanType *, Hchan *, byte *);
14 
15 /* Prepare to call from code written in Go to code written in C or
16    C++.  This takes the current goroutine out of the Go scheduler, as
17    though it were making a system call.  Otherwise the program can
18    lock up if the C code goes to sleep on a mutex or for some other
19    reason.  This idea is to call this function, then immediately call
20    the C/C++ function.  After the C/C++ function returns, call
21    syscall_cgocalldone.  The usual Go code would look like
22 
23        syscall.Cgocall()
24        defer syscall.Cgocalldone()
25        cfunction()
26 
27    */
28 
29 /* We let Go code call these via the syscall package.  */
30 void syscall_cgocall(void) __asm__ (GOSYM_PREFIX "syscall.Cgocall");
31 void syscall_cgocalldone(void) __asm__ (GOSYM_PREFIX "syscall.CgocallDone");
32 void syscall_cgocallback(void) __asm__ (GOSYM_PREFIX "syscall.CgocallBack");
33 void syscall_cgocallbackdone(void) __asm__ (GOSYM_PREFIX "syscall.CgocallBackDone");
34 
35 void
syscall_cgocall()36 syscall_cgocall ()
37 {
38   M* m;
39   G* g;
40 
41   if (runtime_needextram && runtime_cas (&runtime_needextram, 1, 0))
42     runtime_newextram ();
43 
44   runtime_lockOSThread();
45 
46   m = runtime_m ();
47   ++m->ncgocall;
48   g = runtime_g ();
49   ++g->ncgo;
50   runtime_entersyscall ();
51 }
52 
53 /* Prepare to return to Go code from C/C++ code.  */
54 
55 void
syscall_cgocalldone()56 syscall_cgocalldone ()
57 {
58   G* g;
59 
60   g = runtime_g ();
61   __go_assert (g != NULL);
62   --g->ncgo;
63   if (g->ncgo == 0)
64     {
65       /* We are going back to Go, and we are not in a recursive call.
66 	 Let the garbage collector clean up any unreferenced
67 	 memory.  */
68       g->cgomal = NULL;
69     }
70 
71   /* If we are invoked because the C function called _cgo_panic, then
72      _cgo_panic will already have exited syscall mode.  */
73   if (g->status == Gsyscall)
74     runtime_exitsyscall ();
75 
76   runtime_unlockOSThread();
77 }
78 
79 /* Call back from C/C++ code to Go code.  */
80 
81 void
syscall_cgocallback()82 syscall_cgocallback ()
83 {
84   M *mp;
85 
86   mp = runtime_m ();
87   if (mp == NULL)
88     {
89       runtime_needm ();
90       mp = runtime_m ();
91       mp->dropextram = true;
92     }
93 
94   runtime_exitsyscall ();
95 
96   if (runtime_g ()->ncgo == 0)
97     {
98       /* The C call to Go came from a thread not currently running any
99 	 Go.  In the case of -buildmode=c-archive or c-shared, this
100 	 call may be coming in before package initialization is
101 	 complete.  Wait until it is.  */
102       __go_receive (NULL, runtime_main_init_done, NULL);
103     }
104 
105   mp = runtime_m ();
106   if (mp->needextram)
107     {
108       mp->needextram = 0;
109       runtime_newextram ();
110     }
111 }
112 
113 /* Prepare to return to C/C++ code from a callback to Go code.  */
114 
115 void
syscall_cgocallbackdone()116 syscall_cgocallbackdone ()
117 {
118   M *mp;
119 
120   runtime_entersyscall ();
121   mp = runtime_m ();
122   if (mp->dropextram && runtime_g ()->ncgo == 0)
123     {
124       mp->dropextram = false;
125       runtime_dropm ();
126     }
127 }
128 
129 /* Allocate memory and save it in a list visible to the Go garbage
130    collector.  */
131 
132 void *
alloc_saved(size_t n)133 alloc_saved (size_t n)
134 {
135   void *ret;
136   G *g;
137   CgoMal *c;
138 
139   ret = __go_alloc (n);
140 
141   g = runtime_g ();
142   c = (CgoMal *) __go_alloc (sizeof (CgoMal));
143   c->next = g->cgomal;
144   c->alloc = ret;
145   g->cgomal = c;
146 
147   return ret;
148 }
149 
150 /* These are routines used by SWIG.  The gc runtime library provides
151    the same routines under the same name, though in that case the code
152    is required to import runtime/cgo.  */
153 
154 void *
_cgo_allocate(size_t n)155 _cgo_allocate (size_t n)
156 {
157   void *ret;
158 
159   runtime_exitsyscall ();
160   ret = alloc_saved (n);
161   runtime_entersyscall ();
162   return ret;
163 }
164 
165 extern const struct __go_type_descriptor string_type_descriptor
166   __asm__ (GOSYM_PREFIX "__go_tdn_string");
167 
168 void
_cgo_panic(const char * p)169 _cgo_panic (const char *p)
170 {
171   intgo len;
172   unsigned char *data;
173   String *ps;
174   struct __go_empty_interface e;
175 
176   runtime_exitsyscall ();
177   len = __builtin_strlen (p);
178   data = alloc_saved (len);
179   __builtin_memcpy (data, p, len);
180   ps = alloc_saved (sizeof *ps);
181   ps->str = data;
182   ps->len = len;
183   e.__type_descriptor = &string_type_descriptor;
184   e.__object = ps;
185 
186   /* We don't call runtime_entersyscall here, because normally what
187      will happen is that we will walk up the stack to a Go deferred
188      function that calls recover.  However, this will do the wrong
189      thing if this panic is recovered and the stack unwinding is
190      caught by a C++ exception handler.  It might be possible to
191      handle this by calling runtime_entersyscall in the personality
192      function in go-unwind.c.  FIXME.  */
193 
194   __go_panic (e);
195 }
196 
197 /* Used for _cgo_wait_runtime_init_done.  This is based on code in
198    runtime/cgo/gcc_libinit.c in the master library.  */
199 
200 static pthread_cond_t runtime_init_cond = PTHREAD_COND_INITIALIZER;
201 static pthread_mutex_t runtime_init_mu = PTHREAD_MUTEX_INITIALIZER;
202 static _Bool runtime_init_done;
203 
204 /* This is called by exported cgo functions to ensure that the runtime
205    has been initialized before we enter the function.  This is needed
206    when building with -buildmode=c-archive or similar.  */
207 
208 void
_cgo_wait_runtime_init_done(void)209 _cgo_wait_runtime_init_done (void)
210 {
211   int err;
212 
213   if (__atomic_load_n (&runtime_init_done, __ATOMIC_ACQUIRE))
214     return;
215 
216   err = pthread_mutex_lock (&runtime_init_mu);
217   if (err != 0)
218     abort ();
219   while (!__atomic_load_n (&runtime_init_done, __ATOMIC_ACQUIRE))
220     {
221       err = pthread_cond_wait (&runtime_init_cond, &runtime_init_mu);
222       if (err != 0)
223 	abort ();
224     }
225   err = pthread_mutex_unlock (&runtime_init_mu);
226   if (err != 0)
227     abort ();
228 }
229 
230 /* This is called by runtime_main after the Go runtime is
231    initialized.  */
232 
233 void
_cgo_notify_runtime_init_done(void)234 _cgo_notify_runtime_init_done (void)
235 {
236   int err;
237 
238   err = pthread_mutex_lock (&runtime_init_mu);
239   if (err != 0)
240     abort ();
241   __atomic_store_n (&runtime_init_done, 1, __ATOMIC_RELEASE);
242   err = pthread_cond_broadcast (&runtime_init_cond);
243   if (err != 0)
244     abort ();
245   err = pthread_mutex_unlock (&runtime_init_mu);
246   if (err != 0)
247     abort ();
248 }
249 
250 // runtime_iscgo is set to true if some cgo code is linked in.
251 // This is done by a constructor in the cgo generated code.
252 _Bool runtime_iscgo;
253 
254 // runtime_cgoHasExtraM is set on startup when an extra M is created
255 // for cgo.  The extra M must be created before any C/C++ code calls
256 // cgocallback.
257 _Bool runtime_cgoHasExtraM;
258