Home | History | Annotate | Download | only in cgo
      1 // Copyright 2015 The Go Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 // +build cgo
      6 // +build darwin dragonfly freebsd linux netbsd openbsd solaris
      7 
      8 #include <pthread.h>
      9 #include <errno.h>
     10 #include <stdio.h>
     11 #include <stdlib.h>
     12 #include <string.h> // strerror
     13 #include <time.h>
     14 #include "libcgo.h"
     15 #include "libcgo_unix.h"
     16 
     17 static pthread_cond_t runtime_init_cond = PTHREAD_COND_INITIALIZER;
     18 static pthread_mutex_t runtime_init_mu = PTHREAD_MUTEX_INITIALIZER;
     19 static int runtime_init_done;
     20 
     21 // The context function, used when tracing back C calls into Go.
     22 static void (*cgo_context_function)(struct context_arg*);
     23 
     24 void
     25 x_cgo_sys_thread_create(void* (*func)(void*), void* arg) {
     26 	pthread_t p;
     27 	int err = _cgo_try_pthread_create(&p, NULL, func, arg);
     28 	if (err != 0) {
     29 		fprintf(stderr, "pthread_create failed: %s", strerror(err));
     30 		abort();
     31 	}
     32 }
     33 
     34 uintptr_t
     35 _cgo_wait_runtime_init_done() {
     36 	void (*pfn)(struct context_arg*);
     37 
     38 	pthread_mutex_lock(&runtime_init_mu);
     39 	while (runtime_init_done == 0) {
     40 		pthread_cond_wait(&runtime_init_cond, &runtime_init_mu);
     41 	}
     42 
     43 	// TODO(iant): For the case of a new C thread calling into Go, such
     44 	// as when using -buildmode=c-archive, we know that Go runtime
     45 	// initialization is complete but we do not know that all Go init
     46 	// functions have been run. We should not fetch cgo_context_function
     47 	// until they have been, because that is where a call to
     48 	// SetCgoTraceback is likely to occur. We are going to wait for Go
     49 	// initialization to be complete anyhow, later, by waiting for
     50 	// main_init_done to be closed in cgocallbackg1. We should wait here
     51 	// instead. See also issue #15943.
     52 	pfn = cgo_context_function;
     53 
     54 	pthread_mutex_unlock(&runtime_init_mu);
     55 	if (pfn != nil) {
     56 		struct context_arg arg;
     57 
     58 		arg.Context = 0;
     59 		(*pfn)(&arg);
     60 		return arg.Context;
     61 	}
     62 	return 0;
     63 }
     64 
     65 void
     66 x_cgo_notify_runtime_init_done(void* dummy) {
     67 	pthread_mutex_lock(&runtime_init_mu);
     68 	runtime_init_done = 1;
     69 	pthread_cond_broadcast(&runtime_init_cond);
     70 	pthread_mutex_unlock(&runtime_init_mu);
     71 }
     72 
     73 // Sets the context function to call to record the traceback context
     74 // when calling a Go function from C code. Called from runtime.SetCgoTraceback.
     75 void x_cgo_set_context_function(void (*context)(struct context_arg*)) {
     76 	pthread_mutex_lock(&runtime_init_mu);
     77 	cgo_context_function = context;
     78 	pthread_mutex_unlock(&runtime_init_mu);
     79 }
     80 
     81 // Gets the context function.
     82 void (*(_cgo_get_context_function(void)))(struct context_arg*) {
     83 	void (*ret)(struct context_arg*);
     84 
     85 	pthread_mutex_lock(&runtime_init_mu);
     86 	ret = cgo_context_function;
     87 	pthread_mutex_unlock(&runtime_init_mu);
     88 	return ret;
     89 }
     90 
     91 // _cgo_try_pthread_create retries pthread_create if it fails with
     92 // EAGAIN.
     93 int
     94 _cgo_try_pthread_create(pthread_t* thread, const pthread_attr_t* attr, void* (*pfn)(void*), void* arg) {
     95 	int tries;
     96 	int err;
     97 	struct timespec ts;
     98 
     99 	for (tries = 0; tries < 20; tries++) {
    100 		err = pthread_create(thread, attr, pfn, arg);
    101 		if (err == 0) {
    102 			pthread_detach(*thread);
    103 			return 0;
    104 		}
    105 		if (err != EAGAIN) {
    106 			return err;
    107 		}
    108 		ts.tv_sec = 0;
    109 		ts.tv_nsec = (tries + 1) * 1000 * 1000; // Milliseconds.
    110 		nanosleep(&ts, nil);
    111 	}
    112 	return EAGAIN;
    113 }
    114