diff --git a/Kernel/Thread.cpp b/Kernel/Thread.cpp index e0cd1073e3bcfd..9f7aa6e70d69d7 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 00000000000000..174c1bd9a62c6f --- /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 +#include + +__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 6aa9e2e5cf571b..29aecba78ef54a 100644 --- a/Userland/Libraries/LibC/pthread.cpp +++ b/Userland/Libraries/LibC/pthread.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -51,6 +52,8 @@ struct CleanupHandler { static thread_local SinglyLinkedList 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 6260cd54544707..2f7ff03c2314ba 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 ef91bca29a38b8..17ee5bd9d44710 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