From 7a187cd6291b5d547a9aac1d94bb61f44bbedbe7 Mon Sep 17 00:00:00 2001 From: Ingo Weinhold Date: Sat, 9 Apr 2016 19:34:46 +0200 Subject: [PATCH] _user_debug_thread(): Use new signal SIGNAL_DEBUG_THREAD This resolves a TODO: We used thread_interrupt() to wake up the thread from an interruptable wait. However, if the thread was already in the kernel and about to start waiting, that would have no effect and the thread would wait anyway. Now there's the new non-blockable signal SIGNAL_DEBUG_THREAD, which is sent to the thread instead, making sure that thread doesn't start waiting. --- headers/private/kernel/ksignal.h | 4 +++ src/system/kernel/debug/user_debugger.cpp | 33 +++++++---------------- src/system/kernel/signal.cpp | 22 ++++++++++++++- 3 files changed, 34 insertions(+), 25 deletions(-) diff --git a/headers/private/kernel/ksignal.h b/headers/private/kernel/ksignal.h index 4727053c8d..6b096dcf78 100644 --- a/headers/private/kernel/ksignal.h +++ b/headers/private/kernel/ksignal.h @@ -36,6 +36,10 @@ using BKernel::Thread; #define SYSCALL_RESTART_PARAMETER_SIZE 32 // kernel-internal signals +#define SIGNAL_DEBUG_THREAD 62 + // Debug a thread. Used together with the B_THREAD_DEBUG_STOP thread debug + // flag. Continues the thread, if suspended, but has no effect otherwise. + // Non-blockable. #define SIGNAL_CANCEL_THREAD 63 // Cancel a thread. Non-blockable. #define SIGNAL_CONTINUE_THREAD 64 diff --git a/src/system/kernel/debug/user_debugger.cpp b/src/system/kernel/debug/user_debugger.cpp index fabfecd62d..0d74fcd881 100644 --- a/src/system/kernel/debug/user_debugger.cpp +++ b/src/system/kernel/debug/user_debugger.cpp @@ -2903,39 +2903,24 @@ _user_debug_thread(thread_id threadID) if ((thread->debug_info.flags & B_THREAD_DEBUG_NUB_THREAD) != 0) return B_NOT_ALLOWED; - // already marked stopped? - if ((thread->debug_info.flags & B_THREAD_DEBUG_STOPPED) != 0) + // already marked stopped or being told to stop? + if ((thread->debug_info.flags + & (B_THREAD_DEBUG_STOPPED | B_THREAD_DEBUG_STOP)) != 0) { return B_OK; + } // set the flag that tells the thread to stop as soon as possible atomic_or(&thread->debug_info.flags, B_THREAD_DEBUG_STOP); update_thread_user_debug_flag(thread); - // resume/interrupt the thread, if necessary + // send the thread a SIGNAL_DEBUG_THREAD, so it is interrupted (or + // continued) threadDebugInfoLocker.Unlock(); - SpinLocker schedulerLocker(thread->scheduler_lock); + ReadSpinLocker teamLocker(thread->team_lock); + SpinLocker locker(thread->team->signal_lock); - switch (thread->state) { - case B_THREAD_SUSPENDED: - // thread suspended: wake it up - scheduler_enqueue_in_run_queue(thread); - break; - - default: - // thread may be waiting: interrupt it - thread_interrupt(thread, false); - // TODO: If the thread is already in the kernel and e.g. - // about to acquire a semaphore (before - // thread_prepare_to_block()), we won't interrupt it. - // Maybe we should rather send a signal (SIGTRAP). - schedulerLocker.Unlock(); - - schedulerLocker.SetTo(thread_get_current_thread()->scheduler_lock, - false); - scheduler_reschedule_if_necessary_locked(); - break; - } + send_signal_to_thread_locked(thread, SIGNAL_DEBUG_THREAD, NULL, 0); return B_OK; } diff --git a/src/system/kernel/signal.cpp b/src/system/kernel/signal.cpp index 84a51658d1..8305e0bf95 100644 --- a/src/system/kernel/signal.cpp +++ b/src/system/kernel/signal.cpp @@ -45,13 +45,15 @@ #define BLOCKABLE_SIGNALS \ (~(KILL_SIGNALS | SIGNAL_TO_MASK(SIGSTOP) \ + | SIGNAL_TO_MASK(SIGNAL_DEBUG_THREAD) \ | 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)) #define CONTINUE_SIGNALS \ - (SIGNAL_TO_MASK(SIGCONT) | SIGNAL_TO_MASK(SIGNAL_CONTINUE_THREAD)) + (SIGNAL_TO_MASK(SIGCONT) | SIGNAL_TO_MASK(SIGNAL_CONTINUE_THREAD) \ + | SIGNAL_TO_MASK(SIGNAL_DEBUG_THREAD)) #define DEFAULT_IGNORE_SIGNALS \ (SIGNAL_TO_MASK(SIGCHLD) | SIGNAL_TO_MASK(SIGWINCH) \ | SIGNAL_TO_MASK(SIGCONT) \ @@ -1038,6 +1040,11 @@ handle_signals(Thread* thread) notify_debugger(thread, signal, handler, false); continue; + case SIGNAL_DEBUG_THREAD: + // ignore -- used together with B_THREAD_DEBUG_STOP, which + // is handled above + continue; + case SIGNAL_CANCEL_THREAD: // set up the signal handler handler.sa_handler = thread->cancel_function; @@ -1425,6 +1432,19 @@ send_signal_to_thread_locked(Thread* thread, uint32 signalNumber, break; } + case SIGNAL_DEBUG_THREAD: + { + // Wake up thread if it was suspended, otherwise interrupt it. + thread->going_to_suspend = false; + + SpinLocker locker(thread->scheduler_lock); + if (thread->state == B_THREAD_SUSPENDED) + scheduler_enqueue_in_run_queue(thread); + else + thread_interrupt(thread, false); + + break; + } case SIGNAL_CONTINUE_THREAD: { // wake up thread, and interrupt its current syscall