summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTim Schumacher <timschumi@gmx.de>2022-06-12 14:48:28 +0200
committerBrian Gianforcaro <b.gianfo@gmail.com>2022-07-22 10:07:15 -0700
commit899fd74f8ecc7c0b57a555babbad445f87ac36b4 (patch)
treebb941253403d99995c38c6aa174317fca4c87521
parent1269ce0c35fdeeb27d797f878c82d12877add874 (diff)
downloadserenity-899fd74f8ecc7c0b57a555babbad445f87ac36b4.zip
LibC: Implement `pthread_cancel`
-rw-r--r--Kernel/Thread.cpp1
-rw-r--r--Userland/Libraries/LibC/bits/pthread_cancel.h23
-rw-r--r--Userland/Libraries/LibC/pthread.cpp46
-rw-r--r--Userland/Libraries/LibC/pthread.h2
-rw-r--r--Userland/Libraries/LibC/signal_numbers.h3
5 files changed, 71 insertions, 4 deletions
diff --git a/Kernel/Thread.cpp b/Kernel/Thread.cpp
index e0cd1073e3..9f7aa6e70d 100644
--- a/Kernel/Thread.cpp
+++ b/Kernel/Thread.cpp
@@ -871,6 +871,7 @@ static DefaultSignalAction default_signal_action(u8 signal)
case SIGIO:
case SIGPROF:
case SIGTERM:
+ case SIGCANCEL:
return DefaultSignalAction::Terminate;
case SIGCHLD:
case SIGURG:
diff --git a/Userland/Libraries/LibC/bits/pthread_cancel.h b/Userland/Libraries/LibC/bits/pthread_cancel.h
new file mode 100644
index 0000000000..174c1bd9a6
--- /dev/null
+++ b/Userland/Libraries/LibC/bits/pthread_cancel.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2022, the SerenityOS developers.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+__BEGIN_DECLS
+
+// This is our hook for cancellation points.
+#ifdef _DYNAMIC_LOADER
+inline void __pthread_maybe_cancel(void)
+{
+}
+#else
+void __pthread_maybe_cancel(void);
+#endif
+
+__END_DECLS
diff --git a/Userland/Libraries/LibC/pthread.cpp b/Userland/Libraries/LibC/pthread.cpp
index 6aa9e2e5cf..29aecba78e 100644
--- a/Userland/Libraries/LibC/pthread.cpp
+++ b/Userland/Libraries/LibC/pthread.cpp
@@ -12,6 +12,7 @@
#include <AK/StdLibExtras.h>
#include <Kernel/API/Syscall.h>
#include <LibSystem/syscall.h>
+#include <bits/pthread_cancel.h>
#include <bits/pthread_integration.h>
#include <errno.h>
#include <limits.h>
@@ -51,6 +52,8 @@ struct CleanupHandler {
static thread_local SinglyLinkedList<CleanupHandler> cleanup_handlers;
+static __thread bool pending_cancellation = false;
+
extern "C" {
[[noreturn]] static void exit_thread(void* code, void* stack_location, size_t stack_size)
@@ -154,6 +157,21 @@ void pthread_exit(void* value_ptr)
pthread_exit_without_cleanup_handlers(value_ptr);
}
+void __pthread_maybe_cancel()
+{
+ // Check if we have cancellations enabled.
+ if (s_thread_cancel_state != PTHREAD_CANCEL_ENABLE)
+ return;
+
+ // Check if a cancellation request is pending.
+ if (!pending_cancellation)
+ return;
+
+ // Exit the thread via `pthread_exit`. This handles passing the
+ // return value and calling the cleanup handlers for us.
+ pthread_exit(PTHREAD_CANCELED);
+}
+
// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_cleanup_push.html
void pthread_cleanup_push(void (*routine)(void*), void* arg)
{
@@ -481,11 +499,35 @@ int pthread_setschedparam([[maybe_unused]] pthread_t thread, [[maybe_unused]] in
return 0;
}
+static void pthread_cancel_signal_handler(int signal)
+{
+ // SIGCANCEL is a custom signal that is beyond the usual range of signal numbers.
+ // Let's make sure we know about it in case we still end up in here, but the signal
+ // number is being mangled.
+ VERIFY(signal == SIGCANCEL);
+
+ // Note: We don't handle PTHREAD_CANCEL_ASYNCHRONOUS any different from PTHREAD_CANCEL_DEFERRED,
+ // since ASYNCHRONOUS just means that the thread can be cancelled at any time (instead of just
+ // at the next cancellation point) and it seems to be generally discouraged to use it at all.
+ pending_cancellation = true;
+}
+
// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_cancel.html
// NOTE: libgcc expects this function to exist in libpthread, even if it is not implemented.
-int pthread_cancel(pthread_t)
+int pthread_cancel(pthread_t thread)
{
- TODO();
+ // Set up our signal handler, which listens on SIGCANCEL and flips the cancellation indicator.
+ // Note that signal handlers are shared across the whole process, so we can just set that up at any time.
+ static bool set_up_cancel_handler = false;
+
+ if (!set_up_cancel_handler) {
+ struct sigaction act = {};
+ act.sa_handler = pthread_cancel_signal_handler;
+ sigaction(SIGCANCEL, &act, nullptr);
+ set_up_cancel_handler = true;
+ }
+
+ return pthread_kill(thread, SIGCANCEL);
}
int pthread_setname_np(pthread_t thread, char const* name)
diff --git a/Userland/Libraries/LibC/pthread.h b/Userland/Libraries/LibC/pthread.h
index 6260cd5454..2f7ff03c23 100644
--- a/Userland/Libraries/LibC/pthread.h
+++ b/Userland/Libraries/LibC/pthread.h
@@ -33,7 +33,7 @@ int pthread_attr_destroy(pthread_attr_t*);
#define PTHREAD_CREATE_JOINABLE 0
#define PTHREAD_CREATE_DETACHED 1
-#define PTHREAD_CANCELED (-1)
+#define PTHREAD_CANCELED ((void*)-1)
int pthread_attr_getdetachstate(pthread_attr_t const*, int*);
int pthread_attr_setdetachstate(pthread_attr_t*, int);
diff --git a/Userland/Libraries/LibC/signal_numbers.h b/Userland/Libraries/LibC/signal_numbers.h
index ef91bca29a..17ee5bd9d4 100644
--- a/Userland/Libraries/LibC/signal_numbers.h
+++ b/Userland/Libraries/LibC/signal_numbers.h
@@ -38,4 +38,5 @@
#define SIGIO 29
#define SIGINFO 30
#define SIGSYS 31
-#define NSIG 32
+#define SIGCANCEL 32
+#define NSIG 33