Skip to content

Commit b104a0e

Browse files
cuonglmgopherbot
authored andcommitted
runtime/cgo: reduce runtime init done check using atomic
Every call from C to Go does acquire a mutex to check whether Go runtime has been fully initialized. This often does not matter, because the lock is held only briefly. However, with code that does a lot of parallel calls from C to Go could cause heavy contention on the mutex. Since this is an initialization guard, we can double check with atomic operation to provide a fast path in case the initialization is done. With this CL, program in #60961 reduces from ~2.7s to ~1.8s. Fixes #60961 Change-Id: Iba4cabbee3c9bc646e70ef7eb074212ba63fdc04 Reviewed-on: https://go-review.googlesource.com/c/go/+/505455 Reviewed-by: Heschi Kreinick <[email protected]> Auto-Submit: Cuong Manh Le <[email protected]> Run-TryBot: Ian Lance Taylor <[email protected]> Auto-Submit: Ian Lance Taylor <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]> Run-TryBot: Cuong Manh Le <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]> TryBot-Result: Gopher Robot <[email protected]>
1 parent d161207 commit b104a0e

1 file changed

Lines changed: 30 additions & 30 deletions

File tree

src/runtime/cgo/gcc_libinit.c

Lines changed: 30 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -41,30 +41,37 @@ x_cgo_sys_thread_create(void* (*func)(void*), void* arg) {
4141
uintptr_t
4242
_cgo_wait_runtime_init_done(void) {
4343
void (*pfn)(struct context_arg*);
44+
pfn = __atomic_load_n(&cgo_context_function, __ATOMIC_CONSUME);
4445

45-
pthread_mutex_lock(&runtime_init_mu);
46-
while (runtime_init_done == 0) {
47-
pthread_cond_wait(&runtime_init_cond, &runtime_init_mu);
48-
}
46+
int done = 2;
47+
if (__atomic_load_n(&runtime_init_done, __ATOMIC_CONSUME) != done) {
48+
pthread_mutex_lock(&runtime_init_mu);
49+
while (__atomic_load_n(&runtime_init_done, __ATOMIC_CONSUME) == 0) {
50+
pthread_cond_wait(&runtime_init_cond, &runtime_init_mu);
51+
}
4952

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-
}
53+
// The key and x_cgo_pthread_key_created are for the whole program,
54+
// whereas the specific and destructor is per thread.
55+
if (x_cgo_pthread_key_created == 0 && pthread_key_create(&pthread_g, pthread_key_destructor) == 0) {
56+
x_cgo_pthread_key_created = 1;
57+
}
5558

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;
6659

67-
pthread_mutex_unlock(&runtime_init_mu);
60+
// TODO(iant): For the case of a new C thread calling into Go, such
61+
// as when using -buildmode=c-archive, we know that Go runtime
62+
// initialization is complete but we do not know that all Go init
63+
// functions have been run. We should not fetch cgo_context_function
64+
// until they have been, because that is where a call to
65+
// SetCgoTraceback is likely to occur. We are going to wait for Go
66+
// initialization to be complete anyhow, later, by waiting for
67+
// main_init_done to be closed in cgocallbackg1. We should wait here
68+
// instead. See also issue #15943.
69+
pfn = __atomic_load_n(&cgo_context_function, __ATOMIC_CONSUME);
70+
71+
__atomic_store_n(&runtime_init_done, done, __ATOMIC_RELEASE);
72+
pthread_mutex_unlock(&runtime_init_mu);
73+
}
74+
6875
if (pfn != nil) {
6976
struct context_arg arg;
7077

@@ -88,27 +95,20 @@ void x_cgo_bindm(void* g) {
8895
void
8996
x_cgo_notify_runtime_init_done(void* dummy __attribute__ ((unused))) {
9097
pthread_mutex_lock(&runtime_init_mu);
91-
runtime_init_done = 1;
98+
__atomic_store_n(&runtime_init_done, 1, __ATOMIC_RELEASE);
9299
pthread_cond_broadcast(&runtime_init_cond);
93100
pthread_mutex_unlock(&runtime_init_mu);
94101
}
95102

96103
// Sets the context function to call to record the traceback context
97104
// when calling a Go function from C code. Called from runtime.SetCgoTraceback.
98105
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);
106+
__atomic_store_n(&cgo_context_function, context, __ATOMIC_RELEASE);
102107
}
103108

104109
// Gets the context function.
105110
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;
111+
return __atomic_load_n(&cgo_context_function, __ATOMIC_CONSUME);
112112
}
113113

114114
// _cgo_try_pthread_create retries pthread_create if it fails with

0 commit comments

Comments
 (0)