Author: bonefish Date: 2010-04-06 22:23:18 +0200 (Tue, 06 Apr 2010) New Revision: 36054 Changeset: http://dev.haiku-os.org/changeset/36054/haiku Modified: haiku/trunk/headers/private/kernel/thread_types.h haiku/trunk/src/system/kernel/signal.cpp Log: * Added some comments to the thread flags. * Added new thread flag THREAD_FLAGS_ALWAYS_RESTART_SYSCALL. If set, it forces syscall restart even when a signal handler without SA_RESTART was invoked. * Fixed sigwait(): If one of requested signals wasn't already pending it would never wake up. Also, the syscall always needs to be restarted, if interrupted by another signal. * Renamed a bunch of the POSIX signal function implementations which did return an error code directly (instead via errno). Added correct POSIX functions where needed. Modified: haiku/trunk/headers/private/kernel/thread_types.h =================================================================== --- haiku/trunk/headers/private/kernel/thread_types.h 2010-04-06 19:43:27 UTC (rev 36053) +++ haiku/trunk/headers/private/kernel/thread_types.h 2010-04-06 20:23:18 UTC (rev 36054) @@ -348,16 +348,34 @@ // bits for the thread::flags field #define THREAD_FLAGS_SIGNALS_PENDING 0x0001 + // unblocked signals are pending (computed flag for optimization purposes) #define THREAD_FLAGS_DEBUG_THREAD 0x0002 + // forces the thread into the debugger as soon as possible (set by + // debug_thread()) #define THREAD_FLAGS_DEBUGGER_INSTALLED 0x0004 + // a debugger is installed for the current team (computed flag for + // optimization purposes) #define THREAD_FLAGS_BREAKPOINTS_DEFINED 0x0008 + // hardware breakpoints are defined for the current team (computed flag for + // optimization purposes) #define THREAD_FLAGS_BREAKPOINTS_INSTALLED 0x0010 + // breakpoints are currently installed for the thread (i.e. the hardware is + // actually set up to trigger debug events for them) #define THREAD_FLAGS_64_BIT_SYSCALL_RETURN 0x0020 + // set by 64 bit return value syscalls #define THREAD_FLAGS_RESTART_SYSCALL 0x0040 + // set by handle_signals(), if the current syscall shall be restarted #define THREAD_FLAGS_DONT_RESTART_SYSCALL 0x0080 -#define THREAD_FLAGS_SYSCALL_RESTARTED 0x0100 -#define THREAD_FLAGS_SYSCALL 0x0200 - // Note: Set only for certain syscalls. + // explicitly disables automatic syscall restarts (e.g. resume_thread()) +#define THREAD_FLAGS_ALWAYS_RESTART_SYSCALL 0x0100 + // force syscall restart, even if a signal handler without SA_RESTART was + // invoked (e.g. sigwait()) +#define THREAD_FLAGS_SYSCALL_RESTARTED 0x0200 + // the current syscall has been restarted +#define THREAD_FLAGS_SYSCALL 0x0400 + // the thread is currently in a syscall; set/reset only for certain + // functions (e.g. ioctl()) to allow inner functions to discriminate + // whether e.g. parameters where passed from userland or kernel #endif /* _KERNEL_THREAD_TYPES_H */ Modified: haiku/trunk/src/system/kernel/signal.cpp =================================================================== --- haiku/trunk/src/system/kernel/signal.cpp 2010-04-06 19:43:27 UTC (rev 36053) +++ haiku/trunk/src/system/kernel/signal.cpp 2010-04-06 20:23:18 UTC (rev 36054) @@ -11,6 +11,7 @@ #include <ksignal.h> +#include <errno.h> #include <stddef.h> #include <string.h> @@ -23,6 +24,7 @@ #include <kscheduler.h> #include <sem.h> #include <syscall_restart.h> +#include <syscall_utils.h> #include <team.h> #include <thread.h> #include <tracing.h> @@ -331,9 +333,12 @@ thread->user_thread->pending_signals = 0; - bool restart = (atomic_and(&thread->flags, - ~THREAD_FLAGS_DONT_RESTART_SYSCALL) - & THREAD_FLAGS_DONT_RESTART_SYSCALL) == 0; + uint32 restartFlags = atomic_and(&thread->flags, + ~THREAD_FLAGS_DONT_RESTART_SYSCALL); + bool alwaysRestart + = (restartFlags & THREAD_FLAGS_ALWAYS_RESTART_SYSCALL) != 0; + bool restart = alwaysRestart + || (restartFlags & THREAD_FLAGS_DONT_RESTART_SYSCALL) == 0; T(HandleSignals(signalMask)); @@ -475,8 +480,10 @@ if (debugSignal && !notify_debugger(thread, signal, handler, false)) continue; - if (!restart || (handler->sa_flags & SA_RESTART) == 0) + if (!restart + || ((!alwaysRestart && handler->sa_flags & SA_RESTART) == 0)) { atomic_and(&thread->flags, ~THREAD_FLAGS_RESTART_SYSCALL); + } T(ExecuteSignalHandler(signal, handler)); @@ -702,8 +709,8 @@ } -int -sigprocmask(int how, const sigset_t *set, sigset_t *oldSet) +static int +sigprocmask_internal(int how, const sigset_t *set, sigset_t *oldSet) { struct thread *thread = thread_get_current_thread(); sigset_t oldMask = atomic_get(&thread->sig_block_mask); @@ -735,11 +742,18 @@ } +int +sigprocmask(int how, const sigset_t *set, sigset_t *oldSet) +{ + RETURN_AND_SET_ERRNO(sigprocmask_internal(how, set, oldSet)); +} + + /*! \brief sigaction() for the specified thread. A \a threadID is < 0 specifies the current thread. */ -int -sigaction_etc(thread_id threadID, int signal, const struct sigaction *act, +static status_t +sigaction_etc_internal(thread_id threadID, int signal, const struct sigaction *act, struct sigaction *oldAction) { struct thread *thread; @@ -793,6 +807,15 @@ int +sigaction_etc(thread_id threadID, int signal, const struct sigaction *act, + struct sigaction *oldAction) +{ + RETURN_AND_SET_ERRNO(sigaction_etc_internal(threadID, signal, act, + oldAction)); +} + + +int sigaction(int signal, const struct sigaction *act, struct sigaction *oldAction) { return sigaction_etc(-1, signal, act, oldAction); @@ -854,40 +877,57 @@ /*! Wait for the specified signals, and return the signal retrieved in \a _signal. */ -int -sigwait(const sigset_t *set, int *_signal) +static status_t +sigwait_internal(const sigset_t *set, int *_signal) { - struct thread *thread = thread_get_current_thread(); + sigset_t requestedSignals = *set & BLOCKABLE_SIGNALS; - while (!has_signals_pending(thread)) { - thread_prepare_to_block(thread, B_CAN_INTERRUPT, - THREAD_BLOCK_TYPE_SIGNAL, NULL); - thread_block(); - } + struct thread* thread = thread_get_current_thread(); - int signalsPending = atomic_get(&thread->sig_pending) & *set; + while (true) { + sigset_t pendingSignals = atomic_get(&thread->sig_pending); + sigset_t blockedSignals = atomic_get(&thread->sig_block_mask); + sigset_t pendingRequestedSignals = pendingSignals & requestedSignals; + if ((pendingRequestedSignals) != 0) { + // select the lowest pending signal to return in _signal + for (int signal = 1; signal < NSIG; signal++) { + if ((SIGNAL_TO_MASK(signal) & pendingSignals) != 0) { + atomic_and(&thread->sig_pending, ~SIGNAL_TO_MASK(signal)); + *_signal = signal; + return B_OK; + } + } + } - update_current_thread_signals_flag(); + if ((pendingSignals & ~blockedSignals) != 0) { + // Non-blocked signals are pending -- return to let them be handled. + return B_INTERRUPTED; + } - if (signalsPending) { - // select the lowest pending signal to return in _signal - for (int signal = 1; signal < NSIG; signal++) { - if ((SIGNAL_TO_MASK(signal) & signalsPending) != 0) { - *_signal = signal; - return B_OK; - } + // No signals yet. Set the signal block mask to not include the + // requested mask and wait until we're interrupted. + atomic_set(&thread->sig_block_mask, + blockedSignals & ~(requestedSignals & BLOCKABLE_SIGNALS)); + + while (!has_signals_pending(thread)) { + thread_prepare_to_block(thread, B_CAN_INTERRUPT, + THREAD_BLOCK_TYPE_SIGNAL, NULL); + thread_block(); } + + // restore the original block mask + atomic_set(&thread->sig_block_mask, blockedSignals); + + update_current_thread_signals_flag(); } - - return B_INTERRUPTED; } /*! Replace the current signal block mask and wait for any event to happen. Before returning, the original signal block mask is reinstantiated. */ -int -sigsuspend(const sigset_t *mask) +static status_t +sigsuspend_internal(const sigset_t *mask) { T(SigSuspend(*mask)); @@ -918,8 +958,8 @@ } -int -sigpending(sigset_t *set) +static status_t +sigpending_internal(sigset_t *set) { struct thread *thread = thread_get_current_thread(); @@ -961,7 +1001,7 @@ sizeof(sigset_t)) < B_OK)) return B_BAD_ADDRESS; - status = sigprocmask(how, userSet ? &set : NULL, + status = sigprocmask_internal(how, userSet ? &set : NULL, userOldSet ? &oldSet : NULL); // copy old set if asked for @@ -1009,9 +1049,14 @@ return B_BAD_ADDRESS; int signal; - status_t status = sigwait(&set, &signal); - if (status < B_OK) - return syscall_restart_handle_post(status); + status_t status = sigwait_internal(&set, &signal); + if (status == B_INTERRUPTED) { + // make sure we'll be restarted + struct thread* thread = thread_get_current_thread(); + atomic_or(&thread->flags, + THREAD_FLAGS_ALWAYS_RESTART_SYSCALL | THREAD_FLAGS_RESTART_SYSCALL); + return status; + } return user_memcpy(_userSignal, &signal, sizeof(int)); } @@ -1027,7 +1072,7 @@ if (user_memcpy(&mask, userMask, sizeof(sigset_t)) < B_OK) return B_BAD_ADDRESS; - return sigsuspend(&mask); + return sigsuspend_internal(&mask); } @@ -1042,7 +1087,7 @@ if (!IS_USER_ADDRESS(userSet)) return B_BAD_ADDRESS; - status = sigpending(&set); + status = sigpending_internal(&set); if (status == B_OK && user_memcpy(userSet, &set, sizeof(sigset_t)) < B_OK) return B_BAD_ADDRESS;