Skip to content

Commit e338f1f

Browse files
committed
Move __cxa_thread_atexit to native code
Rather than using a separate JS array use pthread TLS key which get cleaned during `__pthread_tsd_run_dtors`. Followup to #14484 and #14464 which both move more of the cleanup handling for threads and processes onto the native side.
1 parent f46fbc5 commit e338f1f

12 files changed

+82
-28
lines changed

src/library_pthread.js

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,6 @@ var LibraryPThread = {
9696
},
9797
// Maps pthread_t to pthread info objects
9898
pthreads: {},
99-
threadExitHandlers: [], // An array of C functions to run when this thread exits.
10099

101100
#if PTHREADS_PROFILING
102101
createProfilerBlock: function(pthreadPtr) {
@@ -165,12 +164,8 @@ var LibraryPThread = {
165164
#endif
166165

167166
runExitHandlers: function() {
168-
while (PThread.threadExitHandlers.length > 0) {
169-
PThread.threadExitHandlers.pop()();
170-
}
171-
172167
// Call into the musl function that runs destructors of all thread-specific data.
173-
if (ENVIRONMENT_IS_PTHREAD && _pthread_self()) ___pthread_tsd_run_dtors();
168+
if (_pthread_self()) ___pthread_tsd_run_dtors();
174169
},
175170

176171
runExitHandlersAndDeinitThread: function(tb, exitCode) {
@@ -1058,13 +1053,6 @@ var LibraryPThread = {
10581053
throw 'unwind';
10591054
},
10601055

1061-
__cxa_thread_atexit__sig: 'vii',
1062-
__cxa_thread_atexit: function(routine, arg) {
1063-
PThread.threadExitHandlers.push(function() { {{{ makeDynCall('vi', 'routine') }}}(arg) });
1064-
},
1065-
__cxa_thread_atexit_impl: '__cxa_thread_atexit',
1066-
1067-
10681056
// Returns 0 on success, or one of the values -ETIMEDOUT, -EWOULDBLOCK or -EINVAL on error.
10691057
emscripten_futex_wait__deps: ['emscripten_main_thread_process_queued_calls'],
10701058
emscripten_futex_wait: function(addr, val, timeout) {

src/postamble_minimal.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,12 @@ function run() {
2323
#else
2424
var ret = _main();
2525

26-
#if EXIT_RUNTIME
27-
callRuntimeCallbacks(__ATEXIT__);
28-
<<< ATEXITS >>>
2926
#if USE_PTHREADS
3027
PThread.runExitHandlers();
3128
#endif
29+
#if EXIT_RUNTIME
30+
callRuntimeCallbacks(__ATEXIT__);
31+
<<< ATEXITS >>>
3232
#endif
3333

3434
#if IN_TEST_HARNESS

src/preamble.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -426,12 +426,12 @@ function exitRuntime() {
426426
#if USE_PTHREADS
427427
if (ENVIRONMENT_IS_PTHREAD) return; // PThreads reuse the runtime from the main thread.
428428
#endif
429-
#if EXIT_RUNTIME
430-
callRuntimeCallbacks(__ATEXIT__);
431-
<<< ATEXITS >>>
432429
#if USE_PTHREADS
433430
PThread.runExitHandlers();
434431
#endif
432+
#if EXIT_RUNTIME
433+
callRuntimeCallbacks(__ATEXIT__);
434+
<<< ATEXITS >>>
435435
#endif
436436
runtimeExited = true;
437437
}

system/lib/libc/musl/src/thread/pthread_key_create.c

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,9 @@ int __pthread_key_delete(pthread_key_t k)
4040
}
4141

4242
#ifdef __EMSCRIPTEN__
43-
void EMSCRIPTEN_KEEPALIVE __pthread_tsd_run_dtors()
44-
#else
45-
void __pthread_tsd_run_dtors()
43+
EMSCRIPTEN_KEEPALIVE
4644
#endif
45+
void __pthread_tsd_run_dtors()
4746
{
4847
pthread_t self = __pthread_self();
4948
int i, j, not_finished = self->tsd_used;
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* Copyright 2019 The Emscripten Authors. All rights reserved.
3+
* Emscripten is available under two separate licenses, the MIT license and the
4+
* University of Illinois/NCSA Open Source License. Both these licenses can be
5+
* found in the LICENSE file.
6+
* Inspired by libcxxabi/src/cxa_thread_atexit.cpp.
7+
* The main reasons we don't use that version direclty are:
8+
* 1. We want to be able to use __cxa_thread_atexit in pure C programs
9+
* where libcxxabi is not linked in at all.
10+
* 2. The libcxxabi relies on TLS variables and we use to free our TLS data
11+
* block on thread exit. This would cause a chicken and egg issue
12+
* where the TLS variables in libcxxabi/src/cxa_thread_atexit.cpp would
13+
* be freed while porcessing the list of __cxa_thread_atexit handlers
14+
*/
15+
#include <assert.h>
16+
#include <stdatomic.h>
17+
#include <stdbool.h>
18+
#include <pthread.h>
19+
#include <stdlib.h>
20+
21+
#include "libc.h"
22+
23+
typedef void(*Dtor)(void*);
24+
25+
typedef struct DtorList {
26+
Dtor dtor;
27+
void* obj;
28+
struct DtorList* next;
29+
} DtorList;
30+
31+
void run_dtors(void* arg) {
32+
DtorList* dtors = (DtorList*)arg;
33+
DtorList* head;
34+
while ((head = dtors)) {
35+
dtors = head->next;
36+
head->dtor(head->obj);
37+
free(head);
38+
}
39+
}
40+
41+
static pthread_key_t key;
42+
atomic_bool key_created = false;
43+
44+
// Note: emscripten_tls_init is 49, and must come before that since
45+
// it calls __cxa_thread_atexit
46+
__attribute__((constructor(48)))
47+
static void make_key() {
48+
pthread_key_create(&key, run_dtors);
49+
key_created = true;
50+
}
51+
52+
int __cxa_thread_atexit_impl(Dtor dtor, void* obj, void* dso_symbol) {
53+
assert(key_created);
54+
DtorList* old_head = pthread_getspecific(key);
55+
DtorList* head = (DtorList*)(malloc(sizeof(DtorList)));
56+
assert(head);
57+
head->dtor = dtor;
58+
head->obj = obj;
59+
head->next = old_head;
60+
pthread_setspecific(key, head);
61+
return 0;
62+
}
63+
64+
weak_alias(__cxa_thread_atexit_impl, __cxa_thread_atexit);

system/lib/pthread/library_pthread.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@
66
*/
77

88
#define _GNU_SOURCE
9-
#include "../internal/libc.h"
10-
#include "../internal/pthread_impl.h"
9+
1110
#include <assert.h>
1211
#include <dirent.h>
1312
#include <errno.h>
@@ -35,6 +34,9 @@
3534
#include <emscripten/threading.h>
3635
#include <emscripten/stack.h>
3736

37+
#include "libc.h"
38+
#include "pthread_impl.h"
39+
3840
// With LLVM 3.6, C11 is the default compilation mode.
3941
// gets() is deprecated under that standard, but emcc
4042
// still provides it, so always include it in the build.

tests/other/metadce/minimal_Oz_USE_PTHREADS_PROXY_TO_PTHREAD.exports

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ J
1111
K
1212
L
1313
M
14-
N
14+
r
1515
s
1616
t
1717
u

tests/other/metadce/minimal_Oz_USE_PTHREADS_PROXY_TO_PTHREAD.funcs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ $emscripten_sync_run_in_main_thread
3434
$emscripten_tls_init
3535
$free_tls
3636
$init_mparams
37+
$nodtor
38+
$run_dtors
3739
$sbrk
3840
$stackAlloc
3941
$stackRestore

tests/other/metadce/minimal_Oz_USE_PTHREADS_PROXY_TO_PTHREAD.imports

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,3 @@ a.n
1515
a.o
1616
a.p
1717
a.q
18-
a.r

tests/other/metadce/minimal_Oz_USE_PTHREADS_PROXY_TO_PTHREAD.sent

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,3 @@ n
1515
o
1616
p
1717
q
18-
r
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
15872
1+
16310

tools/system_libs.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -774,6 +774,7 @@ def get_files(self):
774774
filenames=[
775775
'library_pthread.c',
776776
'emscripten_thread_state.s',
777+
'cxa_thread_atexit.c',
777778
])
778779
else:
779780
ignore += ['thread']

0 commit comments

Comments
 (0)