github.com/lovishpuri/go-40569/src@v0.0.0-20230519171745-f8623e7c56cf/runtime/cgo/gcc_libinit.c (about)

     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  //go:build unix
     6  
     7  #include <pthread.h>
     8  #include <errno.h>
     9  #include <stdio.h>
    10  #include <stdlib.h>
    11  #include <string.h> // strerror
    12  #include <time.h>
    13  #include "libcgo.h"
    14  #include "libcgo_unix.h"
    15  
    16  static pthread_cond_t runtime_init_cond = PTHREAD_COND_INITIALIZER;
    17  static pthread_mutex_t runtime_init_mu = PTHREAD_MUTEX_INITIALIZER;
    18  static int runtime_init_done;
    19  
    20  // pthread_g is a pthread specific key, for storing the g that binded to the C thread.
    21  // The registered pthread_key_destructor will dropm, when the pthread-specified value g is not NULL,
    22  // while a C thread is exiting.
    23  static pthread_key_t pthread_g;
    24  static void pthread_key_destructor(void* g);
    25  uintptr_t x_cgo_pthread_key_created;
    26  void (*x_crosscall2_ptr)(void (*fn)(void *), void *, int, size_t);
    27  
    28  // The context function, used when tracing back C calls into Go.
    29  static void (*cgo_context_function)(struct context_arg*);
    30  
    31  void
    32  x_cgo_sys_thread_create(void* (*func)(void*), void* arg) {
    33  	pthread_t p;
    34  	int err = _cgo_try_pthread_create(&p, NULL, func, arg);
    35  	if (err != 0) {
    36  		fprintf(stderr, "pthread_create failed: %s", strerror(err));
    37  		abort();
    38  	}
    39  }
    40  
    41  uintptr_t
    42  _cgo_wait_runtime_init_done(void) {
    43  	void (*pfn)(struct context_arg*);
    44  
    45  	pthread_mutex_lock(&runtime_init_mu);
    46  	while (runtime_init_done == 0) {
    47  		pthread_cond_wait(&runtime_init_cond, &runtime_init_mu);
    48  	}
    49  
    50  	// The key and x_cgo_pthread_key_created are for the whole program,
    51  	// whereas the specific and destructor is per thread.
    52  	if (x_cgo_pthread_key_created == 0 && pthread_key_create(&pthread_g, pthread_key_destructor) == 0) {
    53  		x_cgo_pthread_key_created = 1;
    54  	}
    55  
    56  	// TODO(iant): For the case of a new C thread calling into Go, such
    57  	// as when using -buildmode=c-archive, we know that Go runtime
    58  	// initialization is complete but we do not know that all Go init
    59  	// functions have been run. We should not fetch cgo_context_function
    60  	// until they have been, because that is where a call to
    61  	// SetCgoTraceback is likely to occur. We are going to wait for Go
    62  	// initialization to be complete anyhow, later, by waiting for
    63  	// main_init_done to be closed in cgocallbackg1. We should wait here
    64  	// instead. See also issue #15943.
    65  	pfn = cgo_context_function;
    66  
    67  	pthread_mutex_unlock(&runtime_init_mu);
    68  	if (pfn != nil) {
    69  		struct context_arg arg;
    70  
    71  		arg.Context = 0;
    72  		(*pfn)(&arg);
    73  		return arg.Context;
    74  	}
    75  	return 0;
    76  }
    77  
    78  // Store the g into a thread-specific value associated with the pthread key pthread_g.
    79  // And pthread_key_destructor will dropm when the thread is exiting.
    80  void x_cgo_bindm(void* g) {
    81  	// We assume this will always succeed, otherwise, there might be extra M leaking,
    82  	// when a C thread exits after a cgo call.
    83  	// We only invoke this function once per thread in runtime.needAndBindM,
    84  	// and the next calls just reuse the bound m.
    85  	pthread_setspecific(pthread_g, g);
    86  }
    87  
    88  void
    89  x_cgo_notify_runtime_init_done(void* dummy __attribute__ ((unused))) {
    90  	pthread_mutex_lock(&runtime_init_mu);
    91  	runtime_init_done = 1;
    92  	pthread_cond_broadcast(&runtime_init_cond);
    93  	pthread_mutex_unlock(&runtime_init_mu);
    94  }
    95  
    96  // Sets the context function to call to record the traceback context
    97  // when calling a Go function from C code. Called from runtime.SetCgoTraceback.
    98  void x_cgo_set_context_function(void (*context)(struct context_arg*)) {
    99  	pthread_mutex_lock(&runtime_init_mu);
   100  	cgo_context_function = context;
   101  	pthread_mutex_unlock(&runtime_init_mu);
   102  }
   103  
   104  // Gets the context function.
   105  void (*(_cgo_get_context_function(void)))(struct context_arg*) {
   106  	void (*ret)(struct context_arg*);
   107  
   108  	pthread_mutex_lock(&runtime_init_mu);
   109  	ret = cgo_context_function;
   110  	pthread_mutex_unlock(&runtime_init_mu);
   111  	return ret;
   112  }
   113  
   114  // _cgo_try_pthread_create retries pthread_create if it fails with
   115  // EAGAIN.
   116  int
   117  _cgo_try_pthread_create(pthread_t* thread, const pthread_attr_t* attr, void* (*pfn)(void*), void* arg) {
   118  	int tries;
   119  	int err;
   120  	struct timespec ts;
   121  
   122  	for (tries = 0; tries < 20; tries++) {
   123  		err = pthread_create(thread, attr, pfn, arg);
   124  		if (err == 0) {
   125  			pthread_detach(*thread);
   126  			return 0;
   127  		}
   128  		if (err != EAGAIN) {
   129  			return err;
   130  		}
   131  		ts.tv_sec = 0;
   132  		ts.tv_nsec = (tries + 1) * 1000 * 1000; // Milliseconds.
   133  		nanosleep(&ts, nil);
   134  	}
   135  	return EAGAIN;
   136  }
   137  
   138  static void
   139  pthread_key_destructor(void* g) {
   140  	if (x_crosscall2_ptr != NULL) {
   141  		// fn == NULL means dropm.
   142  		// We restore g by using the stored g, before dropm in runtime.cgocallback,
   143  		// since the g stored in the TLS by Go might be cleared in some platforms,
   144  		// before this destructor invoked.
   145  		x_crosscall2_ptr(NULL, g, 0, 0);
   146  	}
   147  }