BUG: Fix hang in Process_Kill on OS X caused by an OS bug in which a pipe read end cannot be closed if the pipe write end is open, the pipe is full, and another process is blocking waiting to write. Work around the problem by killing the children before closing the pipes.

This commit is contained in:
Brad King 2008-01-10 18:32:38 -05:00
parent f61305e88c
commit 1c2ffbb5bc
1 changed files with 54 additions and 39 deletions

View File

@ -141,6 +141,7 @@ typedef struct kwsysProcessCreateInformation_s
static int kwsysProcessInitialize(kwsysProcess* cp);
static void kwsysProcessCleanup(kwsysProcess* cp, int error);
static void kwsysProcessCleanupDescriptor(int* pfd);
static void kwsysProcessClosePipes(kwsysProcess* cp);
static int kwsysProcessSetNonBlocking(int fd);
static int kwsysProcessCreate(kwsysProcess* cp, int prIndex,
kwsysProcessCreateInformation* si, int* readEnd);
@ -893,8 +894,6 @@ void kwsysProcess_Execute(kwsysProcess* cp)
/*--------------------------------------------------------------------------*/
kwsysEXPORT void kwsysProcess_Disown(kwsysProcess* cp)
{
int i;
/* Make sure a detached child process is running. */
if(!cp || !cp->Detached || cp->State != kwsysProcess_State_Executing ||
cp->TimeoutExpired || cp->Killed)
@ -902,33 +901,8 @@ kwsysEXPORT void kwsysProcess_Disown(kwsysProcess* cp)
return;
}
/* Close any pipes that are still open. */
for(i=0; i < KWSYSPE_PIPE_COUNT; ++i)
{
if(cp->PipeReadEnds[i] >= 0)
{
#if KWSYSPE_USE_SELECT
/* If the pipe was reported by the last call to select, we must
read from it. This is needed to satisfy the suggestions from
"man select_tut" and is not needed for the polling
implementation. Ignore the data. */
if(FD_ISSET(cp->PipeReadEnds[i], &cp->PipeSet))
{
/* We are handling this pipe now. Remove it from the set. */
FD_CLR(cp->PipeReadEnds[i], &cp->PipeSet);
/* The pipe is ready to read without blocking. Keep trying to
read until the operation is not interrupted. */
while((read(cp->PipeReadEnds[i], cp->PipeBuffer,
KWSYSPE_PIPE_BUFFER_SIZE) < 0) && (errno == EINTR));
}
#endif
/* We are done reading from this pipe. */
kwsysProcessCleanupDescriptor(&cp->PipeReadEnds[i]);
--cp->PipesLeft;
}
}
/* Close all the pipes safely. */
kwsysProcessClosePipes(cp);
/* We will not wait for exit, so cleanup now. */
kwsysProcessCleanup(cp, 0);
@ -1346,18 +1320,17 @@ void kwsysProcess_Kill(kwsysProcess* cp)
return;
}
/* First close the child exit report pipe write end to avoid causing a
SIGPIPE when the child terminates and our signal handler tries to
report it after we have already closed the read end. */
kwsysProcessCleanupDescriptor(&cp->SignalPipe);
#if !defined(__APPLE__)
/* Close all the pipe read ends. Do this before killing the
children because Cygwin has problems killing processes that are
blocking to wait for writing to their output pipes. First close
the child exit report pipe write end to avoid causing a SIGPIPE
when the child terminates and our signal handler tries to report
it. */
kwsysProcessCleanupDescriptor(&cp->SignalPipe);
for(i=0; i < KWSYSPE_PIPE_COUNT; ++i)
{
kwsysProcessCleanupDescriptor(&cp->PipeReadEnds[i]);
}
cp->PipesLeft = 0;
blocking to wait for writing to their output pipes. */
kwsysProcessClosePipes(cp);
#endif
/* Kill the children. */
cp->Killed = 1;
@ -1374,6 +1347,14 @@ void kwsysProcess_Kill(kwsysProcess* cp)
while((waitpid(cp->ForkPIDs[i], &status, 0) < 0) && (errno == EINTR));
}
}
#if defined(__APPLE__)
/* Close all the pipe read ends. Do this after killing the
children because OS X has problems closing pipe read ends whose
pipes are full and still have an open write end. */
kwsysProcessClosePipes(cp);
#endif
cp->CommandsLeft = 0;
}
@ -1533,6 +1514,40 @@ static void kwsysProcessCleanupDescriptor(int* pfd)
}
}
/*--------------------------------------------------------------------------*/
static void kwsysProcessClosePipes(kwsysProcess* cp)
{
int i;
/* Close any pipes that are still open. */
for(i=0; i < KWSYSPE_PIPE_COUNT; ++i)
{
if(cp->PipeReadEnds[i] >= 0)
{
#if KWSYSPE_USE_SELECT
/* If the pipe was reported by the last call to select, we must
read from it. This is needed to satisfy the suggestions from
"man select_tut" and is not needed for the polling
implementation. Ignore the data. */
if(FD_ISSET(cp->PipeReadEnds[i], &cp->PipeSet))
{
/* We are handling this pipe now. Remove it from the set. */
FD_CLR(cp->PipeReadEnds[i], &cp->PipeSet);
/* The pipe is ready to read without blocking. Keep trying to
read until the operation is not interrupted. */
while((read(cp->PipeReadEnds[i], cp->PipeBuffer,
KWSYSPE_PIPE_BUFFER_SIZE) < 0) && (errno == EINTR));
}
#endif
/* We are done reading from this pipe. */
kwsysProcessCleanupDescriptor(&cp->PipeReadEnds[i]);
--cp->PipesLeft;
}
}
}
/*--------------------------------------------------------------------------*/
static int kwsysProcessSetNonBlocking(int fd)
{