summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlessandro Gatti <a.gatti@frob.it>2024-11-18 05:35:48 +0100
committerDamien George <damien@micropython.org>2025-04-22 10:19:20 +1000
commit38a3873310c571c32e52edc2a7d4de7c80ff35d5 (patch)
tree869f3b4063ef5ca1fcd90d782c5de2a21e12a218
parent6025b78d01a883ed24c2f983c1d7b6a4a05d3774 (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.c32
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()) {