Skip to content

Commit dfc1d98

Browse files
committed
Move pthread_cancel to native code
1 parent fbc25fe commit dfc1d98

File tree

8 files changed

+54
-51
lines changed

8 files changed

+54
-51
lines changed

src/library_pthread.js

Lines changed: 9 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,7 @@ var LibraryPThread = {
446446
},
447447

448448
__emscripten_thread_cleanup: function(thread) {
449+
// Called when a thread needs to be cleaned up so it can be reused.
449450
if (!ENVIRONMENT_IS_PTHREAD) cleanupThread(thread);
450451
else postMessage({ 'cmd': 'cleanupThread', 'thread': thread });
451452
},
@@ -788,12 +789,12 @@ var LibraryPThread = {
788789
#endif
789790
},
790791

791-
pthread_kill__deps: ['$killThread', 'emscripten_main_browser_thread_id'],
792+
pthread_kill__deps: ['emscripten_main_browser_thread_id'],
792793
pthread_kill: function(thread, signal) {
793794
if (signal < 0 || signal >= 65/*_NSIG*/) return {{{ cDefine('EINVAL') }}};
794795
if (thread === _emscripten_main_browser_thread_id()) {
795796
if (signal == 0) return 0; // signal == 0 is a no-op.
796-
err('Main thread (id=' + thread + ') cannot be killed with pthread_kill!');
797+
err('Main thread (id=0x' + thread.toString(16) + ') cannot be killed with pthread_kill!');
797798
return {{{ cDefine('ESRCH') }}};
798799
}
799800
if (!thread) {
@@ -802,38 +803,19 @@ var LibraryPThread = {
802803
}
803804
var self = {{{ makeGetValue('thread', C_STRUCTS.pthread.self, 'i32') }}};
804805
if (self !== thread) {
805-
err('pthread_kill attempted on thread ' + thread + ', which does not point to a valid thread, or does not exist anymore!');
806+
err('pthread_kill attempted on thread 0x' + thread.toString(16) + ', which does not point to a valid thread, or does not exist anymore!');
806807
return {{{ cDefine('ESRCH') }}};
807808
}
808-
if (signal != 0) {
809+
if (signal === {{{ cDefine('SIGCANCEL') }}}) { // Used by pthread_cancel in musl
810+
if (!ENVIRONMENT_IS_PTHREAD) cancelThread(thread);
811+
else postMessage({ 'cmd': 'cancelThread', 'thread': thread });
812+
} else if (signal != 0) {
809813
if (!ENVIRONMENT_IS_PTHREAD) killThread(thread);
810-
else postMessage({ 'cmd': 'killThread', 'thread': thread});
814+
else postMessage({ 'cmd': 'killThread', 'thread': thread });
811815
}
812816
return 0;
813817
},
814818

815-
pthread_cancel__deps: ['$cancelThread', 'emscripten_main_browser_thread_id'],
816-
pthread_cancel: function(thread) {
817-
if (thread === _emscripten_main_browser_thread_id()) {
818-
err('Main thread (id=' + thread + ') cannot be canceled!');
819-
return {{{ cDefine('ESRCH') }}};
820-
}
821-
if (!thread) {
822-
err('pthread_cancel attempted on a null thread pointer!');
823-
return {{{ cDefine('ESRCH') }}};
824-
}
825-
var self = {{{ makeGetValue('thread', C_STRUCTS.pthread.self, 'i32') }}};
826-
if (self !== thread) {
827-
err('pthread_cancel attempted on thread ' + thread + ', which does not point to a valid thread, or does not exist anymore!');
828-
return {{{ cDefine('ESRCH') }}};
829-
}
830-
// Signal the thread that it needs to cancel itself.
831-
Atomics.store(HEAPU32, (thread + {{{ C_STRUCTS.pthread.cancel }}}) >> 2, 1);
832-
if (!ENVIRONMENT_IS_PTHREAD) cancelThread(thread);
833-
else postMessage({ 'cmd': 'cancelThread', 'thread': thread});
834-
return 0;
835-
},
836-
837819
__pthread_detached_exit: function() {
838820
// Called at the end of pthread_exit (which occurs also when leaving the
839821
// thread main function) if an only if the thread is in a detached state.

src/struct_info_internal.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@
3131
]
3232
},
3333
"defines": [
34-
"EM_THREAD_NAME_MAX"
34+
"EM_THREAD_NAME_MAX",
35+
"SIGCANCEL"
3536
]
3637
},
3738
{

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

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,9 @@ int __timedwait_cp(volatile int *addr, int val,
4242
int r;
4343
struct timespec to, *top=0;
4444

45+
#ifndef __EMSCRIPTEN__
4546
if (priv) priv = FUTEX_PRIVATE;
47+
#endif
4648

4749
if (at) {
4850
if (at->tv_nsec >= 1000000000UL) return EINVAL;
@@ -56,17 +58,20 @@ int __timedwait_cp(volatile int *addr, int val,
5658
top = &to;
5759
}
5860
#ifdef __EMSCRIPTEN__
61+
pthread_t self = __pthread_self();
5962
double msecsToSleep = top ? (top->tv_sec * 1000 + top->tv_nsec / 1000000.0) : INFINITY;
6063
int is_runtime_thread = emscripten_is_main_runtime_thread();
64+
65+
// Main runtime thread may need to run proxied calls, so sleep in very small slices to be responsive.
66+
const double maxMsecsSliceToSleep = is_runtime_thread ? 1 : 100;
67+
6168
// cp suffix in the function name means "cancellation point", so this wait can be cancelled
62-
// by the users unless current threads cancelability is set to PTHREAD_CANCEL_DISABLE
69+
// by the users unless current threads cancellability is set to PTHREAD_CANCEL_DISABLE
6370
// which may be either done by the user of __timedwait() function.
64-
if (is_runtime_thread ||
65-
pthread_self()->canceldisable != PTHREAD_CANCEL_DISABLE ||
66-
pthread_self()->cancelasync == PTHREAD_CANCEL_ASYNCHRONOUS) {
71+
if (is_runtime_thread || self->canceldisable != PTHREAD_CANCEL_DISABLE || self->cancelasync) {
6772
double sleepUntilTime = emscripten_get_now() + msecsToSleep;
6873
do {
69-
if (pthread_self()->cancel) {
74+
if (self->cancel) {
7075
// Emscripten-specific return value: The wait was canceled by user calling
7176
// pthread_cancel() for this thread, and the caller needs to cooperatively
7277
// cancel execution.
@@ -80,8 +85,8 @@ int __timedwait_cp(volatile int *addr, int val,
8085
r = ETIMEDOUT;
8186
break;
8287
}
83-
if (waitMsecs > 100) waitMsecs = 100; // non-main threads can sleep in longer slices.
84-
if (is_runtime_thread && waitMsecs > 1) waitMsecs = 1; // the runtime thread may need to run proxied calls, so sleep in very small slices to be responsive.
88+
if (waitMsecs > maxMsecsSliceToSleep)
89+
waitMsecs = maxMsecsSliceToSleep;
8590
r = -emscripten_futex_wait((void*)addr, val, waitMsecs);
8691
} while(r == ETIMEDOUT);
8792
} else {
@@ -92,7 +97,7 @@ int __timedwait_cp(volatile int *addr, int val,
9297
r = -__futex4_cp(addr, FUTEX_WAIT|priv, val, top);
9398
#endif
9499
if (r != EINTR && r != ETIMEDOUT && r != ECANCELED) r = 0;
95-
#ifndef __EMSCRIPTEN__
100+
#ifndef __EMSCRIPTEN__ // XXX Emscripten revert musl commit a63c0104e496f7ba78b64be3cd299b41e8cd427f
96101
/* Mitigate bug in old kernels wrongly reporting EINTR for non-
97102
* interrupting (SA_RESTART) signal handlers. This is only practical
98103
* when NO interrupting signal handlers have been installed, and

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

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,28 +8,33 @@
88
void __wait(volatile int *addr, volatile int *waiters, int val, int priv)
99
{
1010
int spins=100;
11+
#ifndef __EMSCRIPTEN__
1112
if (priv) priv = FUTEX_PRIVATE;
13+
#endif
1214
while (spins-- && (!waiters || !*waiters)) {
1315
if (*addr==val) a_spin();
1416
else return;
1517
}
1618
if (waiters) a_inc(waiters);
1719
#ifdef __EMSCRIPTEN__
20+
pthread_t self = __pthread_self();
1821
int is_runtime_thread = emscripten_is_main_runtime_thread();
22+
23+
// Main runtime thread may need to run proxied calls, so sleep in very small slices to be responsive.
24+
const double maxMsecsSliceToSleep = is_runtime_thread ? 1 : 100;
25+
1926
while (*addr==val) {
20-
if (is_runtime_thread || pthread_self()->cancelasync == PTHREAD_CANCEL_ASYNCHRONOUS) {
27+
if (is_runtime_thread || self->cancelasync) {
2128
// Must wait in slices in case this thread is cancelled in between.
2229
int e;
2330
do {
24-
if (pthread_self()->cancel) {
31+
if (self->cancel) {
2532
if (waiters) a_dec(waiters);
2633
return;
2734
}
2835
// Assist other threads by executing proxied operations that are effectively singlethreaded.
2936
if (is_runtime_thread) emscripten_main_thread_process_queued_calls();
30-
// Main thread waits in _very_ small slices so that it stays responsive to assist proxied
31-
// pthread calls.
32-
e = emscripten_futex_wait((void*)addr, val, is_runtime_thread ? 1 : 100);
37+
e = emscripten_futex_wait((void*)addr, val, maxMsecsSliceToSleep);
3338
} while(e == -ETIMEDOUT);
3439
} else {
3540
// Can wait in one go.

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@
33
#include "pthread_impl.h"
44
#include "syscall.h"
55

6+
#ifdef __EMSCRIPTEN__
7+
hidden long __cancel();
8+
#else
69
hidden long __cancel(), __syscall_cp_asm(), __syscall_cp_c();
10+
#endif
711

812
long __cancel()
913
{
@@ -14,6 +18,7 @@ long __cancel()
1418
return -ECANCELED;
1519
}
1620

21+
#ifndef __EMSCRIPTEN__
1722
long __syscall_cp_asm(volatile void *, syscall_arg_t,
1823
syscall_arg_t, syscall_arg_t, syscall_arg_t,
1924
syscall_arg_t, syscall_arg_t, syscall_arg_t);
@@ -66,6 +71,7 @@ static void cancel_handler(int sig, siginfo_t *si, void *ctx)
6671

6772
__syscall(SYS_tkill, self->tid, SIGCANCEL);
6873
}
74+
#endif
6975

7076
void __testcancel()
7177
{
@@ -74,6 +80,7 @@ void __testcancel()
7480
__cancel();
7581
}
7682

83+
#ifndef __EMSCRIPTEN__
7784
static void init_cancellation()
7885
{
7986
struct sigaction sa = {
@@ -83,14 +90,17 @@ static void init_cancellation()
8390
memset(&sa.sa_mask, -1, _NSIG/8);
8491
__libc_sigaction(SIGCANCEL, &sa, 0);
8592
}
93+
#endif
8694

8795
int pthread_cancel(pthread_t t)
8896
{
97+
#ifndef __EMSCRIPTEN__
8998
static int init;
9099
if (!init) {
91100
init_cancellation();
92101
init = 1;
93102
}
103+
#endif
94104
a_store(&t->cancel, 1);
95105
if (t == pthread_self()) {
96106
if (t->canceldisable == PTHREAD_CANCEL_ENABLE && t->cancelasync)

system/lib/pthread/pthread_create.c

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222

2323
// See musl's pthread_create.c
2424

25-
extern int __pthread_create_js(struct pthread *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
25+
extern int __pthread_create_js(pthread_t thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
2626
extern void _emscripten_thread_init(int, int, int);
2727
extern int _emscripten_default_pthread_stack_size();
2828
extern void __pthread_detached_exit();
@@ -76,7 +76,7 @@ int __pthread_create(pthread_t* restrict res,
7676
void* (*entry)(void*),
7777
void* restrict arg) {
7878
// Note on LSAN: lsan intercepts/wraps calls to pthread_create so any
79-
// allocation we we do here should be considered leaks.
79+
// allocation we do here should be considered as leak.
8080
// See: lsan_interceptors.cpp.
8181
if (!res) {
8282
return EINVAL;
@@ -188,11 +188,11 @@ static void free_tls_data() {
188188
}
189189

190190
void _emscripten_thread_exit(void* result) {
191-
struct pthread *self = __pthread_self();
191+
pthread_t self = __pthread_self();
192192
assert(self);
193193

194-
self->canceldisable = PTHREAD_CANCEL_DISABLE;
195-
self->cancelasync = PTHREAD_CANCEL_DEFERRED;
194+
self->canceldisable = 1;
195+
self->cancelasync = 0;
196196
self->result = result;
197197

198198
// Run any handlers registered with pthread_cleanup_push
@@ -219,16 +219,16 @@ void _emscripten_thread_exit(void* result) {
219219
return;
220220
}
221221

222-
// We have the call the buildin free here since lsan handling for this thread
222+
// We have the call the builtin free here since lsan handling for this thread
223223
// gets shut down during __pthread_tsd_run_dtors.
224224
emscripten_builtin_free(self->tsd);
225225
self->tsd = NULL;
226226

227227
// Not hosting a pthread anymore in this worker set __pthread_self to NULL
228228
_emscripten_thread_init(0, 0, 0);
229229

230-
/* This atomic potentially competes with a concurrent pthread_detach
231-
* call; the loser is responsible for freeing thread resources. */
230+
// This atomic potentially competes with a concurrent pthread_detach
231+
// call; the loser is responsible for freeing thread resources.
232232
int state = a_cas(&self->detach_state, DT_JOINABLE, DT_EXITING);
233233

234234
// Mark the thread as no longer running.
@@ -244,7 +244,7 @@ void _emscripten_thread_exit(void* result) {
244244
}
245245
}
246246

247-
// Mark as `no_sanitize("address"` since emscripten_pthread_exit destroys
247+
// Mark as `no_sanitize("address")` since emscripten_pthread_exit destroys
248248
// the current thread and runs its exit handlers. Without this asan injects
249249
// a call to __asan_handle_no_return before emscripten_unwind_to_js_event_loop
250250
// which seem to cause a crash later down the line.

tests/reference_struct_info.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,7 @@
320320
"SEEK_END": 2,
321321
"SEEK_SET": 0,
322322
"SIGALRM": 14,
323+
"SIGCANCEL": 33,
323324
"SOCK_CLOEXEC": 524288,
324325
"SOCK_DGRAM": 2,
325326
"SOCK_NONBLOCK": 2048,

tools/system_libs.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -815,7 +815,6 @@ def get_files(self):
815815
# TODO: Support this. See #12216.
816816
'pthread_setname_np.c',
817817
# TODO: These could be moved away from JS in the upcoming musl upgrade.
818-
'pthread_cancel.c',
819818
'pthread_join.c', 'pthread_testcancel.c',
820819
]
821820
libc_files += files_in_path(

0 commit comments

Comments
 (0)