summaryrefslogtreecommitdiff
path: root/libgo/runtime/go-cgo.c
blob: 88b3f1e83e1f413bd6ba01058284002ac92b10d1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
/* 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 "go-panic.h"
#include "go-type.h"

extern void chanrecv1 (ChanType *, Hchan *, void *)
  __asm__ (GOSYM_PREFIX "runtime.chanrecv1");

/* 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;

  if (runtime_needextram && runtime_cas (&runtime_needextram, 1, 0))
    runtime_newextram ();

  runtime_lockOSThread();

  m = runtime_m ();
  ++m->ncgocall;
  ++m->ncgo;
  runtime_entersyscall (0);
}

/* Prepare to return to Go code from C/C++ code.  */

void
syscall_cgocalldone ()
{
  G* g;

  g = runtime_g ();
  __go_assert (g != NULL);
  --g->m->ncgo;
  if (g->m->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->m->cgomal = NULL;
    }

  /* If we are invoked because the C function called _cgo_panic, then
     _cgo_panic will already have exited syscall mode.  */
  if (g->atomicstatus == _Gsyscall)
    runtime_exitsyscall (0);

  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 (0);

  if (runtime_m ()->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.  */
      chanrecv1 (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 (0);
  mp = runtime_m ();
  if (mp->dropextram && mp->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;
  M *m;
  CgoMal *c;

  ret = __go_alloc (n);

  m = runtime_m ();
  c = (CgoMal *) __go_alloc (sizeof (CgoMal));
  c->next = m->cgomal;
  c->alloc = ret;
  m->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 (0);
  ret = alloc_saved (n);
  runtime_entersyscall (0);
  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;
  Eface e;
  const struct __go_type_descriptor *td;

  runtime_exitsyscall (0);
  len = __builtin_strlen (p);
  data = alloc_saved (len);
  __builtin_memcpy (data, p, len);
  ps = alloc_saved (sizeof *ps);
  ps->str = data;
  ps->len = len;
  td = &string_type_descriptor;
  memcpy(&e._type, &td, sizeof td); /* This is a const_cast.  */
  e.data = 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;