Skip to content

Commit c08ab58

Browse files
committed
Simplify thread clean / exit / terminate logic. NFC
Mostly inspired by musl.
1 parent b9a8d92 commit c08ab58

File tree

5 files changed

+36
-39
lines changed

5 files changed

+36
-39
lines changed

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

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,30 +4,37 @@
44
static int __pthread_detach(pthread_t t)
55
{
66
#ifdef __EMSCRIPTEN__
7-
// XXX EMSCRIPTEN: Add check for invalid (already joined) thread. Again
7+
// XXX Emscripten: Add check for invalid (already joined) thread. Again
88
// for the benefit of the conformance tests.
9-
if (!_emscripten_thread_is_valid(t))
10-
return ESRCH;
11-
#endif
12-
/* If the cas fails, detach state is either already-detached
13-
* or exiting/exited, and pthread_join will trap or cleanup. */
14-
#ifdef __EMSCRIPTEN__
15-
int old_state = a_cas(&t->detach_state, DT_JOINABLE, DT_DETACHED);
16-
if (old_state != DT_JOINABLE) {
9+
if (!_emscripten_thread_is_valid(t)) return ESRCH;
10+
11+
// Note that we don't use pthread_join here, to avoid
12+
// returning EDEADLK when attempting to detach itself.
13+
switch (a_cas(&t->detach_state, DT_JOINABLE, DT_DETACHED)) {
14+
case DT_EXITED:
15+
case DT_EXITING:
16+
_emscripten_thread_cleanup(t);
17+
return 0;
18+
case DT_JOINABLE:
19+
return 0;
20+
case DT_DETACHED: // already-detached
21+
default: // >= DT_DETACHED
1722
// Even though the man page says this is undefined behaviour to attempt to
1823
// detach an already-detached thread we have several tests in the posixtest
1924
// suite that depend on this (pthread_join.c)
20-
if (old_state == DT_DETACHED)
21-
return EINVAL;
25+
return EINVAL;
26+
}
2227
#else
28+
/* If the cas fails, detach state is either already-detached
29+
* or exiting/exited, and pthread_join will trap or cleanup. */
2330
if (a_cas(&t->detach_state, DT_JOINABLE, DT_DETACHED) != DT_JOINABLE) {
24-
#endif
2531
int cs;
2632
__pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
2733
__pthread_join(t, 0);
2834
__pthread_setcancelstate(cs, 0);
2935
}
3036
return 0;
37+
#endif
3138
}
3239

3340
weak_alias(__pthread_detach, pthread_detach);

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

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,8 @@ static int __pthread_timedjoin_np(pthread_t t, void **res, const struct timespec
1313
// Attempt to join a thread which does not point to a valid thread, or
1414
// does not exist anymore.
1515
if (!_emscripten_thread_is_valid(t)) return ESRCH;
16-
// Thread is attempting to join to itself. Already detached threads are
17-
// handled below by returning EINVAL instead.
18-
// TODO: The detached check here is just to satisfy the
19-
// `other.test_{proxy,main}_pthread_join_detach` tests.
20-
if (t->detach_state != DT_DETACHED && __pthread_self() == t) return EDEADLK;
16+
// Thread is attempting to join to itself.
17+
if (__pthread_self() == t) return EDEADLK;
2118
#endif
2219
int state, cs, r = 0;
2320
__pthread_testcancel();

system/lib/pthread/library_pthread.c

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ void emscripten_main_thread_process_queued_calls() {
117117
}
118118

119119
int _emscripten_thread_is_valid(pthread_t thread) {
120-
return thread->self == thread;
120+
return thread->tid;
121121
}
122122

123123
static void *dummy_tsd[1] = { 0 };
@@ -126,8 +126,7 @@ weak_alias(dummy_tsd, __pthread_tsd_main);
126126
// See system/lib/README.md for static constructor ordering.
127127
__attribute__((constructor(48)))
128128
void _emscripten_init_main_thread(void) {
129-
// The pthread struct has a field that points to itself - this is used as
130-
// a magic ID to detect whether the pthread_t structure is 'alive'.
129+
// The pthread struct has a field that points to itself.
131130
__main_pthread.self = &__main_pthread;
132131
__main_pthread.detach_state = DT_JOINABLE;
133132
// Main thread ID is always 1. It can't be 0 because musl assumes

system/lib/pthread/pthread_create.c

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -173,9 +173,10 @@ int __pthread_create(pthread_t* restrict res,
173173
new->map_base = block;
174174
new->map_size = size;
175175

176-
// The pthread struct has a field that points to itself - this is used as a
177-
// magic ID to detect whether the pthread_t structure is 'alive'.
176+
// The pthread struct has a field that points to itself.
178177
new->self = new;
178+
179+
// Thread ID, this becomes zero when the thread is no longer available.
179180
new->tid = next_tid++;
180181

181182
// pthread struct robust_list head should point to itself.
@@ -280,18 +281,21 @@ void _emscripten_thread_free_data(pthread_t t) {
280281
// A thread can never free its own thread data.
281282
assert(t != pthread_self());
282283
#ifndef NDEBUG
283-
if (t->profilerBlock) {
284-
emscripten_builtin_free(t->profilerBlock);
284+
intptr_t old_block = __c11_atomic_exchange((_Atomic intptr_t*)&t->profilerBlock, 0, __ATOMIC_SEQ_CST);
285+
if (old_block) {
286+
emscripten_builtin_free((void*)old_block);
285287
}
286288
#endif
289+
// The tid may be reused. Clear it to prevent inadvertent use
290+
// and inform functions that would use it that it's no longer
291+
// available.
292+
t->tid = 0;
287293

288-
// Free all the enture thread block (called map_base because
294+
// Free the entire thread block (called map_base because
289295
// musl normally allocates this using mmap). This region
290296
// includes the pthread structure itself.
291297
unsigned char* block = t->map_base;
292298
dbg("_emscripten_thread_free_data thread=%p map_base=%p", t, block);
293-
// To aid in debugging, set the entire region to zero.
294-
memset(block, 0, sizeof(struct pthread));
295299
emscripten_builtin_free(block);
296300
}
297301

test/other/test_pthread_self_join_detach.c

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,11 @@ int main() {
2121
pthread_t self = pthread_self();
2222

2323
/*
24-
* Attempts to join the current thread will either generate
25-
* EDEADLK or EINVAL depending on whether has already been
26-
* detached
24+
* Attempts to join the current thread will generate EDEADLK.
2725
*/
2826
int ret = pthread_join(self, NULL);
2927
printf("pthread_join -> %s\n", strerror(ret));
30-
if (is_detached) {
31-
assert(ret == EINVAL);
32-
} else {
33-
assert(ret == EDEADLK);
34-
}
28+
assert(ret == EDEADLK);
3529

3630
/*
3731
* Attempts to detach the main thread will either succeed
@@ -45,10 +39,6 @@ int main() {
4539
assert(ret == 0);
4640
}
4741

48-
ret = pthread_join(self, NULL);
49-
printf("pthread_join -> %s\n", strerror(ret));
50-
assert(ret == EINVAL);
51-
5242
puts("passed");
5343

5444
return 0;

0 commit comments

Comments
 (0)