* Resolved TODO: waitpid() clears pending SIGCHLD, if the signal is
blocked and no other child status is available. * Respect SA_NOCLDWAIT and ignored SIGCHLD in waitpid(): Unless a child status is available immediately, the thread shall block until all children are gone. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@22161 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -12,6 +12,8 @@
|
|||||||
|
|
||||||
#define KILL_SIGNALS ((1L << (SIGKILL - 1)) | (1L << (SIGKILLTHR - 1)))
|
#define KILL_SIGNALS ((1L << (SIGKILL - 1)) | (1L << (SIGKILLTHR - 1)))
|
||||||
|
|
||||||
|
#define SIGNAL_TO_MASK(signal) (1LL << (signal - 1))
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|||||||
@@ -33,7 +33,6 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#define SIGNAL_TO_MASK(signal) (1LL << (signal - 1))
|
|
||||||
#define BLOCKABLE_SIGNALS (~(KILL_SIGNALS | SIGNAL_TO_MASK(SIGSTOP)))
|
#define BLOCKABLE_SIGNALS (~(KILL_SIGNALS | SIGNAL_TO_MASK(SIGSTOP)))
|
||||||
#define STOP_SIGNALS \
|
#define STOP_SIGNALS \
|
||||||
(SIGNAL_TO_MASK(SIGSTOP) | SIGNAL_TO_MASK(SIGTSTP) \
|
(SIGNAL_TO_MASK(SIGSTOP) | SIGNAL_TO_MASK(SIGTSTP) \
|
||||||
|
|||||||
+53
-15
@@ -23,6 +23,7 @@
|
|||||||
#include <kernel.h>
|
#include <kernel.h>
|
||||||
#include <kimage.h>
|
#include <kimage.h>
|
||||||
#include <kscheduler.h>
|
#include <kscheduler.h>
|
||||||
|
#include <ksignal.h>
|
||||||
#include <port.h>
|
#include <port.h>
|
||||||
#include <sem.h>
|
#include <sem.h>
|
||||||
#include <syscall_process_info.h>
|
#include <syscall_process_info.h>
|
||||||
@@ -1320,6 +1321,21 @@ get_job_control_entry(team_job_control_children* children, pid_t id)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static job_control_entry*
|
||||||
|
get_job_control_entry(struct team* team, pid_t id, uint32 flags)
|
||||||
|
{
|
||||||
|
job_control_entry* entry = get_job_control_entry(team->dead_children, id);
|
||||||
|
|
||||||
|
if (entry == NULL && (flags & WCONTINUED) != 0)
|
||||||
|
entry = get_job_control_entry(team->continued_children, id);
|
||||||
|
|
||||||
|
if (entry == NULL && (flags & WUNTRACED) != 0)
|
||||||
|
entry = get_job_control_entry(team->stopped_children, id);
|
||||||
|
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*! This is the kernel backend for waitpid(). It is a bit more powerful when it
|
/*! This is the kernel backend for waitpid(). It is a bit more powerful when it
|
||||||
comes to the reason why a thread has died than waitpid() can be.
|
comes to the reason why a thread has died than waitpid() can be.
|
||||||
*/
|
*/
|
||||||
@@ -1327,7 +1343,8 @@ static thread_id
|
|||||||
wait_for_child(pid_t child, uint32 flags, int32 *_reason,
|
wait_for_child(pid_t child, uint32 flags, int32 *_reason,
|
||||||
status_t *_returnCode)
|
status_t *_returnCode)
|
||||||
{
|
{
|
||||||
struct team *team = thread_get_current_thread()->team;
|
struct thread* thread = thread_get_current_thread();
|
||||||
|
struct team* team = thread->team;
|
||||||
struct job_control_entry foundEntry;
|
struct job_control_entry foundEntry;
|
||||||
struct job_control_entry* freeDeathEntry = NULL;
|
struct job_control_entry* freeDeathEntry = NULL;
|
||||||
status_t status = B_OK;
|
status_t status = B_OK;
|
||||||
@@ -1339,18 +1356,14 @@ wait_for_child(pid_t child, uint32 flags, int32 *_reason,
|
|||||||
child = -team->group_id;
|
child = -team->group_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ignoreFoundEntries = false;
|
||||||
|
bool ignoreFoundEntriesChecked = false;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
InterruptsSpinLocker locker(team_spinlock);
|
InterruptsSpinLocker locker(team_spinlock);
|
||||||
|
|
||||||
// check whether any condition holds
|
// check whether any condition holds
|
||||||
job_control_entry* entry = get_job_control_entry(team->dead_children,
|
job_control_entry* entry = get_job_control_entry(team, child, flags);
|
||||||
child);
|
|
||||||
|
|
||||||
if (entry == NULL && (flags & WCONTINUED) != 0)
|
|
||||||
entry = get_job_control_entry(team->continued_children, child);
|
|
||||||
|
|
||||||
if (entry == NULL && (flags & WUNTRACED) != 0)
|
|
||||||
entry = get_job_control_entry(team->stopped_children, child);
|
|
||||||
|
|
||||||
// If we don't have an entry yet, check whether there are any children
|
// If we don't have an entry yet, check whether there are any children
|
||||||
// complying to the process group specification at all.
|
// complying to the process group specification at all.
|
||||||
@@ -1410,14 +1423,36 @@ wait_for_child(pid_t child, uint32 flags, int32 *_reason,
|
|||||||
locker.Unlock();
|
locker.Unlock();
|
||||||
|
|
||||||
// we got our entry and can return to our caller
|
// we got our entry and can return to our caller
|
||||||
if (status == B_OK)
|
if (status == B_OK) {
|
||||||
|
if (ignoreFoundEntries) {
|
||||||
|
// ... unless we shall ignore found entries
|
||||||
|
delete freeDeathEntry;
|
||||||
|
freeDeathEntry = NULL;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (status != B_WOULD_BLOCK || (flags & WNOHANG) != 0)
|
if (status != B_WOULD_BLOCK || (flags & WNOHANG) != 0)
|
||||||
return status;
|
return status;
|
||||||
|
|
||||||
status = deadWaitEntry.Wait(B_CAN_INTERRUPT);
|
status = deadWaitEntry.Wait(B_CAN_INTERRUPT);
|
||||||
if (status == B_INTERRUPTED)
|
if (status == B_INTERRUPTED)
|
||||||
return status;
|
return status;
|
||||||
|
|
||||||
|
// If SA_NOCLDWAIT is set or SIGCHLD is ignored, we shall wait until
|
||||||
|
// all our children are dead and fail with ECHILD. We check the
|
||||||
|
// condition at this point.
|
||||||
|
if (!ignoreFoundEntriesChecked) {
|
||||||
|
struct sigaction& handler = thread->sig_action[SIGCHLD - 1];
|
||||||
|
if ((handler.sa_flags & SA_NOCLDWAIT) != 0
|
||||||
|
|| handler.sa_handler == SIG_IGN) {
|
||||||
|
ignoreFoundEntries = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ignoreFoundEntriesChecked = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
delete freeDeathEntry;
|
delete freeDeathEntry;
|
||||||
@@ -1443,11 +1478,14 @@ wait_for_child(pid_t child, uint32 flags, int32 *_reason,
|
|||||||
*_returnCode = foundEntry.status;
|
*_returnCode = foundEntry.status;
|
||||||
*_reason = (foundEntry.signal << 16) | reason;
|
*_reason = (foundEntry.signal << 16) | reason;
|
||||||
|
|
||||||
// TODO: From the Open Group Base Specs Issue 6:
|
// If SIGCHLD is blocked, we shall clear pending SIGCHLDs, if no other child
|
||||||
// "... if SIGCHLD is blocked, if wait() or waitpid() return because
|
// status is available.
|
||||||
// the status of a child process is available, any pending SIGCHLD signal
|
if ((atomic_get(&thread->sig_block_mask) & SIGNAL_TO_MASK(SIGCHLD)) != 0) {
|
||||||
// shall be cleared unless the status of another child process is
|
InterruptsSpinLocker locker(team_spinlock);
|
||||||
// available."
|
|
||||||
|
if (get_job_control_entry(team, child, flags) == NULL)
|
||||||
|
atomic_and(&thread->sig_pending, ~SIGNAL_TO_MASK(SIGCHLD));
|
||||||
|
}
|
||||||
|
|
||||||
return foundEntry.thread;
|
return foundEntry.thread;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user