| | |
| | |
| | |
| |
|
| | |
| |
|
| | |
| | |
| | |
| | #pragma GCC diagnostic ignored "-Wpragmas" |
| | #pragma GCC diagnostic ignored "-Wunknown-warning-option" |
| | #pragma GCC diagnostic ignored "-Watomic-alignment" |
| |
|
| | #include <pthread.h> |
| | #include <errno.h> |
| | #include <stdio.h> |
| | #include <stdlib.h> |
| | #include <string.h> |
| | #include <time.h> |
| | #include "libcgo.h" |
| | #include "libcgo_unix.h" |
| |
|
| | static pthread_cond_t runtime_init_cond = PTHREAD_COND_INITIALIZER; |
| | static pthread_mutex_t runtime_init_mu = PTHREAD_MUTEX_INITIALIZER; |
| | static int runtime_init_done; |
| |
|
| | |
| | |
| | |
| | static pthread_key_t pthread_g; |
| | static void pthread_key_destructor(void* g); |
| | uintptr_t x_cgo_pthread_key_created; |
| | void (*x_crosscall2_ptr)(void (*fn)(void *), void *, int, size_t); |
| |
|
| | |
| | static void (*cgo_traceback_function)(struct cgoTracebackArg*); |
| |
|
| | |
| | static void (*cgo_context_function)(struct cgoContextArg*); |
| |
|
| | |
| | static void (*cgo_symbolizer_function)(struct cgoSymbolizerArg*); |
| |
|
| | void |
| | x_cgo_sys_thread_create(void* (*func)(void*), void* arg) { |
| | pthread_attr_t attr; |
| | pthread_t p; |
| | int err; |
| |
|
| | pthread_attr_init(&attr); |
| | pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); |
| | err = _cgo_try_pthread_create(&p, &attr, func, arg); |
| | if (err != 0) { |
| | fprintf(stderr, "pthread_create failed: %s", strerror(err)); |
| | abort(); |
| | } |
| | } |
| |
|
| | uintptr_t |
| | _cgo_wait_runtime_init_done(void) { |
| | void (*pfn)(struct cgoContextArg*); |
| | int done; |
| |
|
| | pfn = __atomic_load_n(&cgo_context_function, __ATOMIC_CONSUME); |
| |
|
| | done = 2; |
| | if (__atomic_load_n(&runtime_init_done, __ATOMIC_CONSUME) != done) { |
| | pthread_mutex_lock(&runtime_init_mu); |
| | while (__atomic_load_n(&runtime_init_done, __ATOMIC_CONSUME) == 0) { |
| | pthread_cond_wait(&runtime_init_cond, &runtime_init_mu); |
| | } |
| |
|
| | |
| | |
| | if (x_cgo_pthread_key_created == 0 && pthread_key_create(&pthread_g, pthread_key_destructor) == 0) { |
| | x_cgo_pthread_key_created = 1; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | pfn = __atomic_load_n(&cgo_context_function, __ATOMIC_CONSUME); |
| |
|
| | __atomic_store_n(&runtime_init_done, done, __ATOMIC_RELEASE); |
| | pthread_mutex_unlock(&runtime_init_mu); |
| | } |
| |
|
| | if (pfn != nil) { |
| | struct cgoContextArg arg; |
| |
|
| | arg.Context = 0; |
| | (*pfn)(&arg); |
| | return arg.Context; |
| | } |
| | return 0; |
| | } |
| |
|
| | |
| | |
| | |
| | void _cgo_set_stacklo(G *g, uintptr *pbounds) |
| | { |
| | uintptr bounds[2]; |
| |
|
| | |
| | if (pbounds == NULL) { |
| | pbounds = &bounds[0]; |
| | } |
| |
|
| | x_cgo_getstackbound(pbounds); |
| |
|
| | g->stacklo = *pbounds; |
| |
|
| | |
| | |
| | if (g->stacklo >= g->stackhi) { |
| | fprintf(stderr, "runtime/cgo: bad stack bounds: lo=%p hi=%p\n", (void*)(g->stacklo), (void*)(g->stackhi)); |
| | abort(); |
| | } |
| | } |
| |
|
| | |
| | |
| | void x_cgo_bindm(void* g) { |
| | |
| | |
| | |
| | |
| | pthread_setspecific(pthread_g, g); |
| | } |
| |
|
| | void |
| | x_cgo_notify_runtime_init_done(void* dummy __attribute__ ((unused))) { |
| | pthread_mutex_lock(&runtime_init_mu); |
| | __atomic_store_n(&runtime_init_done, 1, __ATOMIC_RELEASE); |
| | pthread_cond_broadcast(&runtime_init_cond); |
| | pthread_mutex_unlock(&runtime_init_mu); |
| | } |
| |
|
| | |
| | |
| | void x_cgo_set_traceback_functions(struct cgoSetTracebackFunctionsArg* arg) { |
| | __atomic_store_n(&cgo_traceback_function, arg->Traceback, __ATOMIC_RELEASE); |
| | __atomic_store_n(&cgo_context_function, arg->Context, __ATOMIC_RELEASE); |
| | __atomic_store_n(&cgo_symbolizer_function, arg->Symbolizer, __ATOMIC_RELEASE); |
| | } |
| |
|
| | |
| | void (*(_cgo_get_traceback_function(void)))(struct cgoTracebackArg*) { |
| | return __atomic_load_n(&cgo_traceback_function, __ATOMIC_CONSUME); |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | void x_cgo_call_traceback_function(struct cgoTracebackArg* arg) { |
| | void (*pfn)(struct cgoTracebackArg*); |
| |
|
| | pfn = _cgo_get_traceback_function(); |
| | if (pfn == nil) { |
| | return; |
| | } |
| |
|
| | _cgo_tsan_acquire(); |
| | (*pfn)(arg); |
| | _cgo_tsan_release(); |
| | } |
| |
|
| | |
| | |
| | void (*(_cgo_get_context_function(void)))(struct cgoContextArg*) { |
| | return __atomic_load_n(&cgo_context_function, __ATOMIC_CONSUME); |
| | } |
| |
|
| | |
| | void (*(_cgo_get_symbolizer_function(void)))(struct cgoSymbolizerArg*) { |
| | return __atomic_load_n(&cgo_symbolizer_function, __ATOMIC_CONSUME); |
| | } |
| |
|
| | |
| | |
| | |
| | void x_cgo_call_symbolizer_function(struct cgoSymbolizerArg* arg) { |
| | void (*pfn)(struct cgoSymbolizerArg*); |
| |
|
| | pfn = _cgo_get_symbolizer_function(); |
| | if (pfn == nil) { |
| | return; |
| | } |
| |
|
| | _cgo_tsan_acquire(); |
| | (*pfn)(arg); |
| | _cgo_tsan_release(); |
| | } |
| |
|
| | |
| | |
| | int |
| | _cgo_try_pthread_create(pthread_t* thread, const pthread_attr_t* attr, void* (*pfn)(void*), void* arg) { |
| | int tries; |
| | int err; |
| | struct timespec ts; |
| |
|
| | for (tries = 0; tries < 20; tries++) { |
| | err = pthread_create(thread, attr, pfn, arg); |
| | if (err == 0) { |
| | return 0; |
| | } |
| | if (err != EAGAIN) { |
| | return err; |
| | } |
| | ts.tv_sec = 0; |
| | ts.tv_nsec = (tries + 1) * 1000 * 1000; |
| | nanosleep(&ts, nil); |
| | } |
| | return EAGAIN; |
| | } |
| |
|
| | static void |
| | pthread_key_destructor(void* g) { |
| | if (x_crosscall2_ptr != NULL) { |
| | |
| | |
| | |
| | |
| | x_crosscall2_ptr(NULL, g, 0, 0); |
| | } |
| | } |
| |
|