Author: bonefish Date: 2011-05-18 03:27:28 +0200 (Wed, 18 May 2011) New Revision: 41560 Changeset: https://dev.haiku-os.org/changeset/41560 Ticket: https://dev.haiku-os.org/ticket/5679 Modified: haiku/branches/developer/bonefish/signals/headers/private/kernel/ksignal.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/team.cpp haiku/branches/developer/bonefish/signals/src/system/libroot/posix/signal/kill.c haiku/branches/developer/bonefish/signals/src/system/libroot/posix/signal/raise.c haiku/branches/developer/bonefish/signals/src/system/libroot/posix/signal/send_signal.c Log: * PendingSignals: - Renamed HighestSignalPriority() and DequeueSignal() parameter blocked to nonBlocked and inverted semantics. - _GetHighestPrioritySignal(): Fixed return value in case we found an unqueued signal. * Thread::AllPendingSignals(): Now returns the signals pending for this thread or for the team. * handle_signals(): Also handle team signals. * is_team_signal_blocked(): Added Team* team parameter. The function now checks whether all of the team's threads block the signal. * send_signal_to_team_locked(): Actually send the signal to the team. * sigwait_internal(): Also handle team signals. * Added "bool toThread" parameter to _kern_send_signal(). If the given ID is >= 0, toThread determines whether the target is a thread or a team. * kill(): Changed semantics to be more POSIX compliant: An ID >= 0 targets a team now, not a thread. This breaks BeOS compatibility. send_signal() works as before, though. * raise(): Use send_signal() instead of kill() to get POSIX semantics. This aligns the handling of signals sent to processes with required POSIX semantics: A signal sent to a process is handled by any thread in the process that does't block the signal (addresses #5679). Modified: haiku/branches/developer/bonefish/signals/headers/private/kernel/ksignal.h =================================================================== --- haiku/branches/developer/bonefish/signals/headers/private/kernel/ksignal.h 2011-05-17 23:02:44 UTC (rev 41559) +++ haiku/branches/developer/bonefish/signals/headers/private/kernel/ksignal.h 2011-05-18 01:27:28 UTC (rev 41560) @@ -1,5 +1,7 @@ /* - * Copyright 2003-2008, Axel Dörfler, axeld@xxxxxxxxxxxxxxxxx All rights reserved. + * Copyright 2011, Ingo Weinhold, ingo_weinhold@xxxxxxx + * Copyright 2003-2008, Axel Dörfler, axeld@xxxxxxxxxxxxxxxxx + * All rights reserved. * Distributed under the terms of the MIT License. */ #ifndef _KERNEL_SIGNAL_H @@ -40,19 +42,20 @@ struct Signal : BReferenceable, DeferredDeletable, DoublyLinkedListLinkImpl<Signal> { public: - Signal(int32 number) : fNumber(number) {} + Signal() {} + Signal(uint32 number) : fNumber(number) {} - void SetTo(int32 number) + void SetTo(uint32 number) { fNumber = number; } - int32 Number() const { return fNumber; } + uint32 Number() const { return fNumber; } int32 Priority() const; protected: virtual void LastReferenceReleased(); private: - int fNumber; + uint32 fNumber; }; @@ -64,7 +67,8 @@ { return fQueuedSignalsMask | fUnqueuedSignalsMask; } - int32 HighestSignalPriority(sigset_t blocked) const; + int32 HighestSignalPriority(sigset_t nonBlocked) + const; void Clear(); void AddSignal(int32 signal) @@ -75,13 +79,14 @@ { RemoveSignals(SIGNAL_TO_MASK(signal)); } void RemoveSignals(sigset_t mask); - Signal* DequeueSignal(sigset_t blocked, Signal& buffer); + Signal* DequeueSignal(sigset_t nonBlocked, + Signal& buffer); private: typedef DoublyLinkedList<Signal> SignalList; private: - int32 _GetHighestPrioritySignal(sigset_t blocked, + int32 _GetHighestPrioritySignal(sigset_t nonBlocked, Signal*& _queuedSignal, int32& _unqueuedSignal) const; void _UpdateQueuedSignalMask(); @@ -105,7 +110,7 @@ #endif bool handle_signals(Thread *thread); -bool is_team_signal_blocked(int signal); +bool is_team_signal_blocked(Team* team, int signal); status_t send_signal_to_thread(thread_id threadID, uint32 signal, uint32 flags); status_t send_signal_to_team_locked(Team* team, uint32 signal, uint32 flags); @@ -117,7 +122,7 @@ void update_current_thread_signals_flag(); -status_t _user_send_signal(pid_t tid, uint sig); +status_t _user_send_signal(int32 id, uint32 signal, bool toThread); status_t _user_sigprocmask(int how, const sigset_t *set, sigset_t *oldSet); status_t _user_sigaction(int sig, const struct sigaction *newAction, struct sigaction *oldAction); Modified: haiku/branches/developer/bonefish/signals/headers/private/kernel/thread_types.h =================================================================== --- haiku/branches/developer/bonefish/signals/headers/private/kernel/thread_types.h 2011-05-17 23:02:44 UTC (rev 41559) +++ haiku/branches/developer/bonefish/signals/headers/private/kernel/thread_types.h 2011-05-18 01:27:28 UTC (rev 41560) @@ -310,12 +310,22 @@ const char* const* otherArgs, int otherArgCount); + sigset_t PendingSignals() const + { return fPendingSignals.AllSignals(); } + + void AddPendingSignal(int signal) + { fPendingSignals.AddSignal(signal); } void RemovePendingSignal(int signal) { fPendingSignals.RemoveSignal(signal); } void RemovePendingSignals(sigset_t mask) { fPendingSignals.RemoveSignals(mask); } void ResetSignals(); + inline int32 HighestPendingSignalPriority( + sigset_t nonBlocked) const; + inline Signal* DequeuePendingSignal(sigset_t nonBlocked, + Signal& buffer); + struct sigaction& SignalActionFor(int32 signal) { return fSignalActions[signal - 1]; } void InheritSignalActions(Team* parent); @@ -329,7 +339,8 @@ char fArgs[64]; // contents for the team_info::args field - PendingSignals fPendingSignals; // protected by scheduler lock + BKernel::PendingSignals fPendingSignals; + // protected by scheduler lock struct sigaction fSignalActions[MAX_SIGNO]; // protected by fLock }; @@ -358,7 +369,7 @@ sigset_t sig_block_mask; // protected by scheduler lock, // only modified by the thread itself - sigset_t sig_temp_enabled; // protected by scheduler lock + sigset_t sig_temp_enabled; // only accessed by this thread addr_t signal_stack_base; // only accessed by this thread size_t signal_stack_size; // only accessed by this thread bool signal_stack_enabled; // only accessed by this thread @@ -471,8 +482,7 @@ sigset_t ThreadPendingSignals() const { return fPendingSignals.AllSignals(); } - sigset_t AllPendingSignals() const - { return fPendingSignals.AllSignals(); } + inline sigset_t AllPendingSignals() const; void AddPendingSignal(int signal) { fPendingSignals.AddSignal(signal); } void RemovePendingSignal(int signal) @@ -481,6 +491,11 @@ { fPendingSignals.RemoveSignals(mask); } void ResetSignals(); + inline int32 HighestPendingSignalPriority( + sigset_t nonBlocked) const; + inline Signal* DequeuePendingSignal(sigset_t nonBlocked, + Signal& buffer); + bool Lock() { mutex_lock(&fLock); return true; } bool TryLock() @@ -496,7 +511,8 @@ private: mutex fLock; - PendingSignals fPendingSignals; // protected by scheduler lock + BKernel::PendingSignals fPendingSignals; + // protected by scheduler lock }; @@ -592,6 +608,41 @@ }; +inline int32 +Team::HighestPendingSignalPriority(sigset_t nonBlocked) const +{ + return fPendingSignals.HighestSignalPriority(nonBlocked); +} + + +inline Signal* +Team::DequeuePendingSignal(sigset_t nonBlocked, Signal& buffer) +{ + return fPendingSignals.DequeueSignal(nonBlocked, buffer); +} + + +inline sigset_t +Thread::AllPendingSignals() const +{ + return fPendingSignals.AllSignals() | team->PendingSignals(); +} + + +inline int32 +Thread::HighestPendingSignalPriority(sigset_t nonBlocked) const +{ + return fPendingSignals.HighestSignalPriority(nonBlocked); +} + + +inline Signal* +Thread::DequeuePendingSignal(sigset_t nonBlocked, Signal& buffer) +{ + return fPendingSignals.DequeueSignal(nonBlocked, buffer); +} + + } // namespace BKernel using BKernel::Team; Modified: haiku/branches/developer/bonefish/signals/headers/private/system/syscalls.h =================================================================== --- haiku/branches/developer/bonefish/signals/headers/private/system/syscalls.h 2011-05-17 23:02:44 UTC (rev 41559) +++ haiku/branches/developer/bonefish/signals/headers/private/system/syscalls.h 2011-05-18 01:27:28 UTC (rev 41560) @@ -1,5 +1,5 @@ /* - * Copyright 2004-2010, Haiku Inc. All rights reserved. + * Copyright 2004-2011, Haiku, Inc. All rights reserved. * Distributed under the terms of the MIT License. */ #ifndef _SYSTEM_SYSCALLS_H @@ -198,7 +198,7 @@ extern status_t _kern_setgroups(int groupCount, const gid_t* groupList); // signal functions -extern status_t _kern_send_signal(pid_t tid, uint sig); +extern status_t _kern_send_signal(int32 id, uint32 signal, bool toThread); extern status_t _kern_sigprocmask(int how, const sigset_t *set, sigset_t *oldSet); extern status_t _kern_sigaction(int sig, const struct sigaction *action, Modified: haiku/branches/developer/bonefish/signals/src/system/kernel/signal.cpp =================================================================== --- haiku/branches/developer/bonefish/signals/src/system/kernel/signal.cpp 2011-05-17 23:02:44 UTC (rev 41559) +++ haiku/branches/developer/bonefish/signals/src/system/kernel/signal.cpp 2011-05-18 01:27:28 UTC (rev 41560) @@ -93,9 +93,6 @@ }; -static status_t deliver_signal(Thread *thread, uint signal, uint32 flags); - - // #pragma mark - Signal @@ -133,18 +130,18 @@ } -/*! Of the signals not it \a blocked returns the priority of that with the +/*! Of the signals in \a nonBlocked returns the priority of that with the highest priority. - \param blocked The mask with the blocked signals. + \param nonBlocked The mask with the non-blocked signals. \return The priority of the highest priority non-blocked signal, or, if all signals are blocked, \c -1. */ int32 -PendingSignals::HighestSignalPriority(sigset_t blocked) const +PendingSignals::HighestSignalPriority(sigset_t nonBlocked) const { Signal* queuedSignal; int32 unqueuedSignal; - return _GetHighestPrioritySignal(blocked, queuedSignal, unqueuedSignal); + return _GetHighestPrioritySignal(nonBlocked, queuedSignal, unqueuedSignal); } @@ -203,22 +200,21 @@ } -/*! Removes and returns a signal not in \a blocked that has the highest - priority. +/*! Removes and returns a signal in \a nonBlocked that has the highest priority. The caller gets a reference to the returned signal, if any. - \param blocked The mask of blocked signals. + \param nonBlocked The mask of non-blocked signals. \param buffer If the signal is not queued this buffer is returned. In this case the method acquires a reference to \a buffer, so that the caller gets a reference also in this case. \return The removed signal or \c NULL, if all signals are blocked. */ Signal* -PendingSignals::DequeueSignal(sigset_t blocked, Signal& buffer) +PendingSignals::DequeueSignal(sigset_t nonBlocked, Signal& buffer) { // find the signal with the highest priority Signal* queuedSignal; int32 unqueuedSignal; - if (_GetHighestPrioritySignal(blocked, queuedSignal, unqueuedSignal) < 0) + if (_GetHighestPrioritySignal(nonBlocked, queuedSignal, unqueuedSignal) < 0) return NULL; // if it is a queued signal, dequeue it @@ -240,7 +236,7 @@ /*! Of the signals not it \a blocked returns the priority of that with the highest priority. - \param blocked The mask with the blocked signals. + \param blocked The mask with the non-blocked signals. \param _queuedSignal If the found signal is a queued signal, the variable will be set to that signal, otherwise to \c NULL. \param _unqueuedSignal If the found signal is an unqueued signal, the @@ -249,11 +245,9 @@ signals are blocked, \c -1. */ int32 -PendingSignals::_GetHighestPrioritySignal(sigset_t blocked, +PendingSignals::_GetHighestPrioritySignal(sigset_t nonBlocked, Signal*& _queuedSignal, int32& _unqueuedSignal) const { - sigset_t nonBlocked = ~blocked; - // check queued signals Signal* queuedSignal = NULL; int32 queuedPriority = -1; @@ -301,7 +295,7 @@ _queuedSignal = NULL; _unqueuedSignal = unqueuedSignal; - return queuedPriority; + return unqueuedPriority; } @@ -326,28 +320,29 @@ namespace SignalTracing { -class HandleSignals : public AbstractTraceEntry { +class HandleSignal : public AbstractTraceEntry { public: - HandleSignals(uint32 signals) + HandleSignal(uint32 signal) : - fSignals(signals) + fSignal(signal) { Initialized(); } virtual void AddDump(TraceOutput& out) { - out.Print("signal handle: 0x%lx", fSignals); + out.Print("signal handle: %" B_PRIu32 " (%s)" , fSignal, + fSignal < NSIG ? kSignalInfos[fSignal].name : "invalid"); } private: - uint32 fSignals; + uint32 fSignal; }; class ExecuteSignalHandler : public AbstractTraceEntry { public: - ExecuteSignalHandler(int signal, struct sigaction* handler) + ExecuteSignalHandler(uint32 signal, struct sigaction* handler) : fSignal(signal), fHandler((void*)handler->sa_handler) @@ -357,12 +352,14 @@ virtual void AddDump(TraceOutput& out) { - out.Print("signal exec handler: signal: %d, handler: %p", - fSignal, fHandler); + out.Print("signal exec handler: signal: %" B_PRIu32 " (%s), " + "handler: %p", fSignal, + fSignal < NSIG ? kSignalInfos[fSignal].name : "invalid", + fHandler); } private: - int fSignal; + uint32 fSignal; void* fHandler; }; @@ -513,7 +510,7 @@ static void update_thread_signals_flag(Thread* thread) { - sigset_t mask = ~thread->sig_block_mask | thread->sig_temp_enabled; + sigset_t mask = ~thread->sig_block_mask; if ((thread->AllPendingSignals() & mask) != 0) atomic_or(&thread->flags, THREAD_FLAGS_SIGNALS_PENDING); else @@ -532,6 +529,20 @@ } +/*! Updates all of the given team's threads' Thread::flags fields according to + what signals are pending. + The caller must hold the scheduler lock. +*/ +static void +update_team_threads_signal_flag(Team* team) +{ + for (Thread* thread = team->thread_list; thread != NULL; + thread = thread->team_next) { + update_thread_signals_flag(thread); + } +} + + /*! Notifies the user debugger about a signal to be handled. The caller must not hold any locks. @@ -544,10 +555,10 @@ ignored. */ static bool -notify_debugger(Thread* thread, int signal, struct sigaction& handler, +notify_debugger(Thread* thread, Signal* signal, struct sigaction& handler, bool deadly) { - uint64 signalMask = SIGNAL_TO_MASK(signal); + uint64 signalMask = SIGNAL_TO_MASK(signal->Number()); // first check the ignore signal masks the debugger specified for the thread InterruptsSpinLocker threadDebugInfoLocker(thread->debug_info.lock); @@ -563,10 +574,42 @@ threadDebugInfoLocker.Unlock(); // deliver the event - return user_debug_handle_signal(signal, &handler, deadly); + return user_debug_handle_signal(signal->Number(), &handler, deadly); } +/*! Removes and returns a signal with the highest priority in \a nonBlocked that + is pending in the given thread or its team. + After dequeuing the signal the Thread::flags field of the affected threads + are updated. + The caller gets a reference to the returned signal, if any. + The caller must hold the scheduler lock. + \param thread The thread. + \param nonBlocked The mask of non-blocked signals. + \param buffer If the signal is not queued this buffer is returned. In this + case the method acquires a reference to \a buffer, so that the caller + gets a reference also in this case. + \return The removed signal or \c NULL, if all signals are blocked. +*/ +static Signal* +dequeue_thread_or_team_signal(Thread* thread, sigset_t nonBlocked, + Signal& buffer) +{ + Team* team = thread->team; + Signal* signal; + if (team->HighestPendingSignalPriority(nonBlocked) + > thread->HighestPendingSignalPriority(nonBlocked)) { + signal = team->DequeuePendingSignal(nonBlocked, buffer); + update_team_threads_signal_flag(team); + } else { + signal = thread->DequeuePendingSignal(nonBlocked, buffer); + update_thread_signals_flag(thread); + } + + return signal; +} + + /*! Actually handles the signal -- i.e. the thread will exit, a custom signal handler is prepared, or whatever the signal demands. Interrupts must be enabled. @@ -581,35 +624,25 @@ { Team* team = thread->team; - // get the pending signals that need to be handled + TeamLocker teamLocker(team); InterruptsSpinLocker schedulerLocker(gSchedulerLock); - uint32 signalMask = thread->AllPendingSignals() - & (~thread->sig_block_mask | thread->sig_temp_enabled); - thread->sig_temp_enabled = 0; + // If userland requested to defer signals, we check now, if this is + // possible. + sigset_t nonBlockedMask = thread->sig_temp_enabled != 0 + ? thread->sig_temp_enabled : ~thread->sig_block_mask; + sigset_t signalMask = thread->AllPendingSignals() & nonBlockedMask; - schedulerLocker.Unlock(); - - // If SIGKILL[THR] are pending, we ignore other signals. - // Otherwise check, if the thread shall stop for debugging. - if ((signalMask & KILL_SIGNALS) != 0) { - signalMask &= KILL_SIGNALS; - } else if ((atomic_get(&thread->debug_info.flags) & B_THREAD_DEBUG_STOP) - != 0) { - user_debug_stop_thread(); - } - - if (signalMask == 0) - return false; - if (thread->user_thread->defer_signals > 0 - && (signalMask & NON_DEFERRABLE_SIGNALS) == 0) { + && (signalMask & NON_DEFERRABLE_SIGNALS) == 0 + && thread->sig_temp_enabled == 0) { thread->user_thread->pending_signals = signalMask; return false; } thread->user_thread->pending_signals = 0; + // determine syscall restart behavior uint32 restartFlags = atomic_and(&thread->flags, ~THREAD_FLAGS_DONT_RESTART_SYSCALL); bool alwaysRestart @@ -617,40 +650,65 @@ bool restart = alwaysRestart || (restartFlags & THREAD_FLAGS_DONT_RESTART_SYSCALL) == 0; - T(HandleSignals(signalMask)); + // Loop until we've handled all signals. + bool initialIteration = true; + while (true) { + if (initialIteration) { + initialIteration = false; + } else { + teamLocker.Lock(); + schedulerLocker.Lock(); - for (int32 i = 0; i < NSIG; i++) { - bool debugSignal; - int32 signal = i + 1; + signalMask = thread->AllPendingSignals() & nonBlockedMask; + } -// TODO: Move below and also check the team's pending signals. - if ((signalMask & SIGNAL_TO_MASK(signal)) == 0) + // Unless SIGKILL[THR] are pending, check, if the thread shall stop for + // debugging. + if ((signalMask & KILL_SIGNALS) == 0 + && (atomic_get(&thread->debug_info.flags) & B_THREAD_DEBUG_STOP) + != 0) { + schedulerLocker.Unlock(); + teamLocker.Unlock(); + + user_debug_stop_thread(); continue; + } - // clear the signal that we will handle - TeamLocker teamLocker(team); - schedulerLocker.Lock(); + // We're done, if there aren't any pending signals anymore. + if ((signalMask & nonBlockedMask) == 0) + break; -// TODO: Might be a pending team signal. - thread->RemovePendingSignal(signal); - update_current_thread_signals_flag(); + // get pending non-blocked thread or team signal with the highest + // priority + Signal stackSignal; + Signal* signal = dequeue_thread_or_team_signal(thread, nonBlockedMask, + stackSignal); + ASSERT(signal != NULL); + BReference<Signal> signalReference(signal, true); schedulerLocker.Unlock(); - struct sigaction handler = team->SignalActionFor(signal); + // get the action for the signal + struct sigaction handler = team->SignalActionFor(signal->Number()); if ((handler.sa_flags & SA_ONESHOT) != 0 && handler.sa_handler != SIG_IGN && handler.sa_handler != SIG_DFL) { - team->SignalActionFor(signal).sa_handler = SIG_DFL; + team->SignalActionFor(signal->Number()).sa_handler = SIG_DFL; } + T(HandleSignal(signal->Number())); + teamLocker.Unlock(); - debugSignal = !(~atomic_get(&team->debug_info.flags) - & (B_TEAM_DEBUG_SIGNALS | B_TEAM_DEBUG_DEBUGGER_INSTALLED)); + // debug the signal, if a debugger is installed and the signal debugging + // flag is set + bool debugSignal = (~atomic_get(&team->debug_info.flags) + & (B_TEAM_DEBUG_DEBUGGER_INSTALLED | B_TEAM_DEBUG_SIGNALS)) + != 0; - TRACE(("Thread 0x%lx received signal %s\n", thread->id, - kSignalInfos[signal].name)); + // handle the signal + TRACE(("Thread %" B_PRId32 " received signal %s\n", thread->id, + kSignalInfos[signal->Number()].name)); if (handler.sa_handler == SIG_IGN) { // signal is to be ignored @@ -662,7 +720,7 @@ continue; } else if (handler.sa_handler == SIG_DFL) { // default signal behaviour - switch (signal) { + switch (signal->Number()) { case SIGCHLD: case SIGWINCH: case SIGURG: @@ -682,7 +740,8 @@ team->LockTeamAndParent(false); team_set_job_control_state(team, - JOB_CONTROL_STATE_CONTINUED, signal, false); + JOB_CONTROL_STATE_CONTINUED, signal->Number(), + false); team->UnlockTeamAndParent(); @@ -706,12 +765,10 @@ team->LockTeamAndParent(false); team_set_job_control_state(team, - JOB_CONTROL_STATE_STOPPED, signal, false); + JOB_CONTROL_STATE_STOPPED, signal->Number(), false); // send a SIGCHLD to the parent (if it does have // SA_NOCLDSTOP defined) - // TODO: We should send the signal to the team, not the - // thread. Team* parentTeam = team->parent; struct sigaction& parentHandler @@ -734,7 +791,7 @@ // the main thread first, since the signal will kill this // thread only. if (thread != team->main_thread) - send_signal_to_team(team->id, SIGKILL, 0); + send_signal_to_thread(team->id, SIGKILL, 0); case SIGQUIT: case SIGPOLL: case SIGPROF: @@ -742,19 +799,20 @@ case SIGVTALRM: case SIGXCPU: case SIGXFSZ: - TRACE(("Shutting down thread 0x%lx due to signal #%ld\n", - thread->id, signal)); + TRACE(("Shutting down thread %" B_PRId32 " due to signal %" + B_PRIu32 "\n", thread->id, signal->Number())); case SIGKILL: case SIGKILLTHR: default: // if the thread exited normally, the exit reason is already set if (thread->exit.reason != THREAD_RETURN_EXIT) { thread->exit.reason = THREAD_RETURN_INTERRUPTED; - thread->exit.signal = (uint16)signal; + thread->exit.signal = (uint16)signal->Number(); } // notify the debugger - if (debugSignal && signal != SIGKILL && signal != SIGKILLTHR + if (debugSignal && signal->Number() != SIGKILL + && signal->Number() != SIGKILLTHR && !notify_debugger(thread, signal, handler, true)) continue; @@ -774,7 +832,7 @@ atomic_and(&thread->flags, ~THREAD_FLAGS_RESTART_SYSCALL); } - T(ExecuteSignalHandler(signal, &handler)); + T(ExecuteSignalHandler(signal->Number(), &handler)); TRACE(("### Setting up custom signal handler frame...\n")); @@ -786,37 +844,58 @@ if ((handler.sa_flags & SA_NOMASK) == 0) { // Update the block mask while the signal handler is running - it // will be automatically restored when the signal frame is left. - thread->sig_block_mask |= (handler.sa_mask | SIGNAL_TO_MASK(signal)) - & BLOCKABLE_SIGNALS; + thread->sig_block_mask + |= (handler.sa_mask | SIGNAL_TO_MASK(signal->Number())) + & BLOCKABLE_SIGNALS; } update_current_thread_signals_flag(); schedulerLocker.Unlock(); - arch_setup_signal_frame(thread, &handler, signal, blockMask); + arch_setup_signal_frame(thread, &handler, signal->Number(), blockMask); + // Reset sig_temp_enabled. It would have been set by + // sigsuspend_internal(). + thread->sig_temp_enabled = 0; + return false; } - // clear syscall restart thread flag, if we're not supposed to restart the - // syscall - if (!restart) + // We have not handled any signal (respectively only ignored ones). + + // If sig_temp_enabled is non-null, we came from a sigsuspend(). Not having + // handled any signal, we should restart the syscall. + if (thread->sig_temp_enabled != 0) { + thread->sig_temp_enabled = 0; + restart = true; + atomic_or(&thread->flags, THREAD_FLAGS_RESTART_SYSCALL); + } else if (!restart) { + // clear syscall restart thread flag, if we're not supposed to restart + // the syscall atomic_and(&thread->flags, ~THREAD_FLAGS_RESTART_SYSCALL); + } return false; } -/*! Checks whether the given signal is blocked for the process. - The caller must hold the scheduler lock. +/*! Checks whether the given signal is blocked for the given team (i.e. all of + its threads). + The caller must hold the team's lock and the scheduler lock. */ bool -is_team_signal_blocked(int signal) +is_team_signal_blocked(Team* team, int signal) { -// TODO: Check the team, not the current thread! - return (thread_get_current_thread()->sig_block_mask - & SIGNAL_TO_MASK(signal)) != 0; + sigset_t mask = SIGNAL_TO_MASK(signal); + + for (Thread* thread = team->thread_list; thread != NULL; + thread = thread->team_next) { + if ((thread->sig_block_mask & mask) == 0) + return false; + } + + return true; } @@ -1017,16 +1096,96 @@ { T(SendSignal(team->id, signal, flags)); - // TODO: Actually send the signal to the team! - if (team->main_thread == NULL) - return B_BAD_TEAM_ID; + if ((flags & B_CHECK_PERMISSION) != 0) { + // TODO: check permission + } - status_t error = deliver_signal(team->main_thread, signal, flags); - if (error != B_OK) - return error; + if (signal == 0) + return B_OK; + if (team == team_get_kernel_team()) { + // signals to the kernel team are ignored + return B_NOT_ALLOWED; + } + + InterruptsSpinLocker schedulerLocker(gSchedulerLock); + + team->AddPendingSignal(signal); + + switch (signal) { + case SIGKILL: + case SIGKILLTHR: + { + // Also add a SIGKILLTHR to the main thread's signals and wake it + // up/interrupt it, so we get this over with as soon as possible + // (only the main thread shuts down the team). + Thread* mainThread = team->main_thread; + if (mainThread != NULL) { + mainThread->AddPendingSignal(SIGKILLTHR); + + // wake up main thread + if (mainThread->state == B_THREAD_SUSPENDED) + scheduler_enqueue_in_run_queue(mainThread); + else + thread_interrupt(mainThread, true); + } + break; + } + + case SIGCONT: + // wake up any suspended threads + for (Thread* thread = team->thread_list; thread != NULL; + thread = thread->team_next) { + if (thread->state == B_THREAD_SUSPENDED) { + scheduler_enqueue_in_run_queue(thread); + + // don't restart syscall, if requested + if ((flags & SIGNAL_FLAG_DONT_RESTART_SYSCALL) != 0) { + atomic_or(&thread->flags, + THREAD_FLAGS_DONT_RESTART_SYSCALL); + } + } + + // remove any pending stop signals + thread->RemovePendingSignals(STOP_SIGNALS); + } + + // remove any pending team stop signals + team->RemovePendingSignals(STOP_SIGNALS); + break; + + case SIGSTOP: + case SIGTSTP: + case SIGTTIN: + case SIGTTOU: + // send the stop signal to all threads + // TODO: Is that correct or should we only target the main thread? + for (Thread* thread = team->thread_list; thread != NULL; + thread = thread->team_next) { + thread->AddPendingSignal(signal); + } + + // remove the stop signal from the team again + team->RemovePendingSignal(signal); + + // fall through to interrupt threads + default: + // Interrupt all interruptibly waiting threads, if the signal is + // not masked. + for (Thread* thread = team->thread_list; thread != NULL; + thread = thread->team_next) { + sigset_t nonBlocked = ~thread->sig_block_mask + | SIGNAL_TO_MASK(SIGCHLD); + if ((thread->AllPendingSignals() & nonBlocked) != 0) + thread_interrupt(thread, false); + } + break; + } + + update_team_threads_signal_flag(team); + if ((flags & B_DO_NOT_RESCHEDULE) == 0) - scheduler_reschedule_if_necessary(); + scheduler_reschedule_if_necessary_locked(); return B_OK; } @@ -1134,25 +1293,31 @@ int send_signal_etc(pid_t id, uint signal, uint32 flags) { - if (signal < 0 || signal > MAX_SIGNO) + if (signal > MAX_SIGNO) return B_BAD_VALUE; // If id is > 0, send the signal to the respective thread. if (id > 0) return send_signal_to_thread(id, signal, flags); - // send a signal to the specified process group (the absolute value of the - // id) + // If id == 0, send the signal to the current thread. + if (id == 0) { + return send_signal_to_thread(thread_get_current_thread_id(), signal, + flags); + } - // TODO: Handle -1 correctly: The signal shall be sent to all teams the - // calling team has permission to send signals to. - if (id == 0 || id == -1) { - // send a signal to the current team - id = thread_get_current_thread()->team->id; - } else - id = -id; + // If id == -1, send the signal to all teams the calling team has permission + // to send signals to. + if (id == -1) { + // TODO: Implement correctly! + // currently only send to the current team + return send_signal_to_team(thread_get_current_thread()->team->id, + signal, flags); + } - return send_signal_to_process_group(id, signal, flags); + // Send a signal to the specified process group (the absolute value of the + // id). + return send_signal_to_process_group(-id, signal, flags); } @@ -1208,8 +1373,7 @@ } -/*! \brief sigaction() for the specified thread. - A \a threadID is < 0 specifies the current thread. +/*! \brief Like sigaction(), but returning the error instead of setting errno. */ static status_t sigaction_internal(int signal, const struct sigaction* act, @@ -1292,7 +1456,7 @@ Thread* thread = thread_get_current_thread(); bigtime_t remainingTime = 0; - ASSERT(B_ONE_SHOT_RELATIVE_ALARM == B_ONE_SHOT_RELATIVE_TIMER); + STATIC_ASSERT(B_ONE_SHOT_RELATIVE_ALARM == B_ONE_SHOT_RELATIVE_TIMER); // just to be sure no one changes the headers some day TRACE(("set_alarm: thread = %p\n", thread)); @@ -1334,20 +1498,20 @@ InterruptsSpinLocker schedulerLocker(gSchedulerLock); while (true) { - sigset_t blockedSignals = thread->sig_block_mask; sigset_t pendingSignals = thread->AllPendingSignals(); if ((pendingSignals & requestedSignals) != 0) { - // select the lowest pending signal to return in _signal - for (int signal = 1; signal < NSIG; signal++) { - if ((SIGNAL_TO_MASK(signal) & pendingSignals) != 0) { - thread->RemovePendingSignal(signal); - update_current_thread_signals_flag(); - *_signal = signal; - return B_OK; - } - } + // get signal with the highest priority + Signal stackSignal; + Signal* signal = dequeue_thread_or_team_signal(thread, + requestedSignals, stackSignal); + ASSERT(signal != NULL); + BReference<Signal> signalReference(signal, true); + + *_signal = signal->Number(); + return B_OK; } + sigset_t blockedSignals = thread->sig_block_mask; if ((pendingSignals & ~blockedSignals) != 0) { // Non-blocked signals are pending -- return to let them be handled. return B_INTERRUPTED; @@ -1383,17 +1547,19 @@ Before returning, the original signal block mask is reinstantiated. */ static status_t -sigsuspend_internal(const sigset_t* mask) +sigsuspend_internal(const sigset_t* _mask) { - T(SigSuspend(*mask)); + sigset_t mask = *_mask & BLOCKABLE_SIGNALS; + T(SigSuspend(mask)); + Thread* thread = thread_get_current_thread(); InterruptsSpinLocker schedulerLocker(gSchedulerLock); // Set the new block mask and block until interrupted. sigset_t oldMask = thread->sig_block_mask; - thread->sig_block_mask = *mask & BLOCKABLE_SIGNALS; + thread->sig_block_mask = mask & BLOCKABLE_SIGNALS; while (!has_signals_pending(thread)) { thread_prepare_to_block(thread, B_CAN_INTERRUPT, @@ -1404,7 +1570,9 @@ // restore the original block mask thread->sig_block_mask = oldMask; - thread->sig_temp_enabled = ~*mask; + thread->sig_temp_enabled = ~mask; + // guaranteed to be non-0 (due to BLOCKABLE_SIGNALS), will be used and + // reset by handle_signals() update_current_thread_signals_flag(); @@ -1457,10 +1625,39 @@ } +/*! Sends a signal to a thread, process, or process group. + \param id Specifies the ID of the target: + - \code id > 0 \endcode: If \a toThread is \c true, the target is the + thread with ID \a id, otherwise the team with the ID \a id. + - \code id == 0 \endcode: If toThread is \c true, the target is the + current thread, otherwise the current team. + - \code id == -1 \endcode: The target are all teams the current team has + permission to send signals to. Currently not implemented correctly. + - \code id < -1 \endcode: The target are is the process group with ID + \c -id. + \param signal The signal number. \c 0 to just perform checks, but not + actually send any signal. + \param toThread If \c true, implies BeOS's \c send_signal() semantics, i.e. + if \code id >= 0 \endcode the target is a thread, not a team. If + \c false, the function has POSIX \c kill() semantics, i.e. if + \code id >= 0 \endcode the target is a team. +*/ status_t -_user_send_signal(pid_t team, uint signal) +_user_send_signal(int32 id, uint32 signal, bool toThread) { - return send_signal_etc(team, signal, B_CHECK_PERMISSION); + // If toThread == true, delegate to send_signal_etc(). Also do that when + // id < 0, since in this case the semantics is the same as well. + uint32 flags = B_CHECK_PERMISSION; + if (toThread || id < 0) + return send_signal_etc(id, signal, flags); [... truncated: 107 lines follow ...]