diff options
| author | Alessandro Gatti <a.gatti@frob.it> | 2024-11-18 05:35:48 +0100 |
|---|---|---|
| committer | Damien George <damien@micropython.org> | 2025-04-22 10:19:20 +1000 |
| commit | 38a3873310c571c32e52edc2a7d4de7c80ff35d5 (patch) | |
| tree | 869f3b4063ef5ca1fcd90d782c5de2a21e12a218 | |
| parent | 6025b78d01a883ed24c2f983c1d7b6a4a05d3774 (diff) | |
unix/mpthreadport: Work around lack of thread cancellation on Android.
This commit fixes thread-related compilation issues under Android using
Termux as its runtime environment.
On Android's libc (Bionic) thread cancellation is not implemented, but
the Unix port uses that mechanism to provide asynchronous thread
termination. In this commit there is a workaround for that, by adding a
new signal handler to each newly created thread, whose callback simply
exits the thread. Threads are then sent the new signal rather than
being explicitly cancelled, which in turn trigger the signal handler to
stop the thread execution at the next possible occasion.
This makes the cancellation behaviour differ slightly on Android, as
threads are probably going to linger a little bit more since the method
introduced in this commit is equivalent to setting
PTHREAD_CANCEL_DEFERRED as the thread cancellation type. On the other
hand there are no guarantees of immediate cancellation using
PTHREAD_CANCEL_ASYNCHRONOUS either.
This fixes the pthread-related issues reported in #16259.
Signed-off-by: Alessandro Gatti <a.gatti@frob.it>
| -rw-r--r-- | ports/unix/mpthreadport.c | 32 |
1 files changed, 32 insertions, 0 deletions
diff --git a/ports/unix/mpthreadport.c b/ports/unix/mpthreadport.c index 5172645bc..ded3bd14a 100644 --- a/ports/unix/mpthreadport.c +++ b/ports/unix/mpthreadport.c @@ -45,8 +45,14 @@ // potential conflict with other uses of the more commonly used SIGUSR1. #ifdef SIGRTMIN #define MP_THREAD_GC_SIGNAL (SIGRTMIN + 5) +#ifdef __ANDROID__ +#define MP_THREAD_TERMINATE_SIGNAL (SIGRTMIN + 6) +#endif #else #define MP_THREAD_GC_SIGNAL (SIGUSR1) +#ifdef __ANDROID__ +#define MP_THREAD_TERMINATE_SIGNAL (SIGUSR2) +#endif #endif // This value seems to be about right for both 32-bit and 64-bit builds. @@ -107,6 +113,18 @@ static void mp_thread_gc(int signo, siginfo_t *info, void *context) { } } +// On Android, pthread_cancel and pthread_setcanceltype are not implemented. +// To achieve that result a new signal handler responding on either +// (SIGRTMIN + 6) or SIGUSR2 is installed on every child thread. The sole +// purpose of this new signal handler is to terminate the thread in a safe +// asynchronous manner. + +#ifdef __ANDROID__ +static void mp_thread_terminate(int signo, siginfo_t *info, void *context) { + pthread_exit(NULL); +} +#endif + void mp_thread_init(void) { pthread_key_create(&tls_key, NULL); pthread_setspecific(tls_key, &mp_state_ctx.thread); @@ -135,6 +153,14 @@ void mp_thread_init(void) { sa.sa_sigaction = mp_thread_gc; sigemptyset(&sa.sa_mask); sigaction(MP_THREAD_GC_SIGNAL, &sa, NULL); + + // Install a signal handler for asynchronous termination if needed. + #if defined(__ANDROID__) + sa.sa_flags = SA_SIGINFO; + sa.sa_sigaction = mp_thread_terminate; + sigemptyset(&sa.sa_mask); + sigaction(MP_THREAD_TERMINATE_SIGNAL, &sa, NULL); + #endif } void mp_thread_deinit(void) { @@ -142,7 +168,11 @@ void mp_thread_deinit(void) { while (thread->next != NULL) { mp_thread_t *th = thread; thread = thread->next; + #if defined(__ANDROID__) + pthread_kill(th->id, MP_THREAD_TERMINATE_SIGNAL); + #else pthread_cancel(th->id); + #endif free(th); } mp_thread_unix_end_atomic_section(); @@ -200,7 +230,9 @@ void mp_thread_start(void) { } #endif + #if !defined(__ANDROID__) pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); + #endif mp_thread_unix_begin_atomic_section(); for (mp_thread_t *th = thread; th != NULL; th = th->next) { if (th->id == pthread_self()) { |
