Author: bonefish Date: 2011-06-06 22:37:05 +0200 (Mon, 06 Jun 2011) New Revision: 41984 Changeset: https://dev.haiku-os.org/changeset/41984 Modified: haiku/branches/developer/bonefish/signals/headers/private/kernel/ksignal.h haiku/branches/developer/bonefish/signals/headers/private/kernel/thread.h haiku/branches/developer/bonefish/signals/headers/private/kernel/thread_types.h haiku/branches/developer/bonefish/signals/headers/private/system/syscalls.h haiku/branches/developer/bonefish/signals/src/system/kernel/signal.cpp haiku/branches/developer/bonefish/signals/src/system/kernel/thread.cpp haiku/branches/developer/bonefish/signals/src/system/libroot/posix/pthread/pthread_cancel.cpp Log: * Added syscall _kern_cancel_thread() for asynchronous thread cancellation. * Added Thread::cancel_function, which temporarily stores the function to be called upon thread cancellation. * Added kernel-internal signal SIGNAL_CANCEL_THREAD which is used to deliver the message. It is handled by invoking Thread::cancel_function as a signal handler. * Finished implementation of pthread_cancel(). Asynchronous thread cancellation should work now. Modified: haiku/branches/developer/bonefish/signals/headers/private/kernel/ksignal.h =================================================================== --- haiku/branches/developer/bonefish/signals/headers/private/kernel/ksignal.h 2011-06-06 20:34:52 UTC (rev 41983) +++ haiku/branches/developer/bonefish/signals/headers/private/kernel/ksignal.h 2011-06-06 20:37:05 UTC (rev 41984) @@ -35,9 +35,12 @@ #define SYSCALL_RESTART_PARAMETER_SIZE 32 -// Kernel internal signal to continue a thread. Used by resume_thread(). -// Non-blockable, prevents syscall restart. +// kernel-internal signals +#define SIGNAL_CANCEL_THREAD 63 + // Cancel a thread. Non-blockable. #define SIGNAL_CONTINUE_THREAD 64 + // Continue a thread. Used by resume_thread(). Non-blockable, prevents + // syscall restart. struct signal_frame_data { Modified: haiku/branches/developer/bonefish/signals/headers/private/kernel/thread.h =================================================================== --- haiku/branches/developer/bonefish/signals/headers/private/kernel/thread.h 2011-06-06 20:34:52 UTC (rev 41983) +++ haiku/branches/developer/bonefish/signals/headers/private/kernel/thread.h 2011-06-06 20:37:05 UTC (rev 41984) @@ -149,6 +149,7 @@ status_t _user_snooze_etc(bigtime_t timeout, int timebase, uint32 flags, bigtime_t* _remainingTime); status_t _user_kill_thread(thread_id thread); +status_t _user_cancel_thread(thread_id threadID, void (*cancelFunction)(int)); void _user_thread_yield(void); void _user_exit_thread(status_t return_value); bool _user_has_data(thread_id thread); Modified: haiku/branches/developer/bonefish/signals/headers/private/kernel/thread_types.h =================================================================== --- haiku/branches/developer/bonefish/signals/headers/private/kernel/thread_types.h 2011-06-06 20:34:52 UTC (rev 41983) +++ haiku/branches/developer/bonefish/signals/headers/private/kernel/thread_types.h 2011-06-06 20:37:05 UTC (rev 41984) @@ -450,6 +450,8 @@ // modified by the thread itself and // thus freely readable by it + void (*cancel_function)(int); + struct { uint8 parameters[SYSCALL_RESTART_PARAMETER_SIZE]; } syscall_restart; Modified: haiku/branches/developer/bonefish/signals/headers/private/system/syscalls.h =================================================================== --- haiku/branches/developer/bonefish/signals/headers/private/system/syscalls.h 2011-06-06 20:34:52 UTC (rev 41983) +++ haiku/branches/developer/bonefish/signals/headers/private/system/syscalls.h 2011-06-06 20:37:05 UTC (rev 41984) @@ -157,6 +157,8 @@ int32 newPriority); extern status_t _kern_kill_thread(thread_id thread); extern void _kern_exit_thread(status_t returnValue); +extern status_t _kern_cancel_thread(thread_id threadID, + void (*cancelFunction)(int)); extern void _kern_thread_yield(void); extern status_t _kern_wait_for_thread(thread_id thread, status_t *_returnCode); Modified: haiku/branches/developer/bonefish/signals/src/system/kernel/signal.cpp =================================================================== --- haiku/branches/developer/bonefish/signals/src/system/kernel/signal.cpp 2011-06-06 20:34:52 UTC (rev 41983) +++ haiku/branches/developer/bonefish/signals/src/system/kernel/signal.cpp 2011-06-06 20:37:05 UTC (rev 41984) @@ -42,8 +42,10 @@ #endif -#define BLOCKABLE_SIGNALS (~(KILL_SIGNALS | SIGNAL_TO_MASK(SIGSTOP) \ - | SIGNAL_TO_MASK(SIGNAL_CONTINUE_THREAD))) +#define BLOCKABLE_SIGNALS \ + (~(KILL_SIGNALS | SIGNAL_TO_MASK(SIGSTOP) \ + | SIGNAL_TO_MASK(SIGNAL_CONTINUE_THREAD) \ + | SIGNAL_TO_MASK(SIGNAL_CANCEL_THREAD))) #define STOP_SIGNALS \ (SIGNAL_TO_MASK(SIGSTOP) | SIGNAL_TO_MASK(SIGTSTP) \ | SIGNAL_TO_MASK(SIGTTIN) | SIGNAL_TO_MASK(SIGTTOU)) @@ -127,7 +129,7 @@ {"invalid 60", 0}, {"invalid 61", 0}, {"invalid 62", 0}, - {"invalid 63", 0}, + {"CANCEL_THREAD", 0}, {"CONTINUE_THREAD", 0} // priority must be <= that of SIGSTOP }; @@ -1031,6 +1033,14 @@ notify_debugger(thread, signal, handler, false); continue; + case SIGNAL_CANCEL_THREAD: + // set up the signal handler + handler.sa_handler = thread->cancel_function; + handler.sa_flags = 0; + handler.sa_mask = 0; + handler.sa_userdata = NULL; + break; + case SIGNAL_CONTINUE_THREAD: // prevent syscall restart, but otherwise ignore restart = false; Modified: haiku/branches/developer/bonefish/signals/src/system/kernel/thread.cpp =================================================================== --- haiku/branches/developer/bonefish/signals/src/system/kernel/thread.cpp 2011-06-06 20:34:52 UTC (rev 41983) +++ haiku/branches/developer/bonefish/signals/src/system/kernel/thread.cpp 2011-06-06 20:37:05 UTC (rev 41984) @@ -3326,6 +3326,33 @@ status_t +_user_cancel_thread(thread_id threadID, void (*cancelFunction)(int)) +{ + // check the cancel function + if (cancelFunction == NULL || !IS_USER_ADDRESS(cancelFunction)) + return B_BAD_VALUE; + + // get and lock the thread + Thread* thread = Thread::GetAndLock(threadID); + if (thread == NULL) + return B_BAD_THREAD_ID; + BReference<Thread> threadReference(thread, true); + ThreadLocker threadLocker(thread, true); + + // only threads of the same team can be canceled + if (thread->team != thread_get_current_thread()->team) + return B_NOT_ALLOWED; + + // set the cancel function + thread->cancel_function = cancelFunction; + + // send the cancellation signal to the thread + InterruptsSpinLocker schedulerLocker(gSchedulerLock); + return send_signal_to_thread_locked(thread, SIGNAL_CANCEL_THREAD, NULL, 0); +} + + +status_t _user_resume_thread(thread_id thread) { // TODO: Don't allow kernel threads to be resumed! Modified: haiku/branches/developer/bonefish/signals/src/system/libroot/posix/pthread/pthread_cancel.cpp =================================================================== --- haiku/branches/developer/bonefish/signals/src/system/libroot/posix/pthread/pthread_cancel.cpp 2011-06-06 20:34:52 UTC (rev 41983) +++ haiku/branches/developer/bonefish/signals/src/system/libroot/posix/pthread/pthread_cancel.cpp 2011-06-06 20:37:05 UTC (rev 41984) @@ -7,7 +7,9 @@ #include "pthread_private.h" +#include <syscalls.h> + static inline void test_asynchronous_cancel(int32 flags) { @@ -19,6 +21,17 @@ } +/*! Invoked when the thread is canceled asynchronously. + Has the simple signal handler signature, since it is invoked just like a + signal handler. +*/ +static void +asynchronous_cancel_thread(int) +{ + pthread_exit(PTHREAD_CANCELED); +} + + // #pragma mark - public API @@ -36,9 +49,8 @@ static const int32 kFlags = THREAD_CANCEL_ENABLED | THREAD_CANCEL_ASYNCHRONOUS; - if ((~oldFlags & kFlags) == 0) { - // TODO: Cancel asynchronously! - } + if ((~oldFlags & kFlags) == 0) + return _kern_cancel_thread(thread->id, &asynchronous_cancel_thread); return 0; }