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 int kwsysProcessInitialize(kwsysProcess* cp);
static void kwsysProcessCleanup(kwsysProcess* cp, int error); static void kwsysProcessCleanup(kwsysProcess* cp, int error);
static void kwsysProcessCleanupDescriptor(int* pfd); static void kwsysProcessCleanupDescriptor(int* pfd);
static void kwsysProcessClosePipes(kwsysProcess* cp);
static int kwsysProcessSetNonBlocking(int fd); static int kwsysProcessSetNonBlocking(int fd);
static int kwsysProcessCreate(kwsysProcess* cp, int prIndex, static int kwsysProcessCreate(kwsysProcess* cp, int prIndex,
kwsysProcessCreateInformation* si, int* readEnd); kwsysProcessCreateInformation* si, int* readEnd);
@ -893,8 +894,6 @@ void kwsysProcess_Execute(kwsysProcess* cp)
/*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/
kwsysEXPORT void kwsysProcess_Disown(kwsysProcess* cp) kwsysEXPORT void kwsysProcess_Disown(kwsysProcess* cp)
{ {
int i;
/* Make sure a detached child process is running. */ /* Make sure a detached child process is running. */
if(!cp || !cp->Detached || cp->State != kwsysProcess_State_Executing || if(!cp || !cp->Detached || cp->State != kwsysProcess_State_Executing ||
cp->TimeoutExpired || cp->Killed) cp->TimeoutExpired || cp->Killed)
@ -902,33 +901,8 @@ kwsysEXPORT void kwsysProcess_Disown(kwsysProcess* cp)
return; return;
} }
/* Close any pipes that are still open. */ /* Close all the pipes safely. */
for(i=0; i < KWSYSPE_PIPE_COUNT; ++i) kwsysProcessClosePipes(cp);
{
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;
}
}
/* We will not wait for exit, so cleanup now. */ /* We will not wait for exit, so cleanup now. */
kwsysProcessCleanup(cp, 0); kwsysProcessCleanup(cp, 0);
@ -1346,18 +1320,17 @@ void kwsysProcess_Kill(kwsysProcess* cp)
return; 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 /* Close all the pipe read ends. Do this before killing the
children because Cygwin has problems killing processes that are children because Cygwin has problems killing processes that are
blocking to wait for writing to their output pipes. First close blocking to wait for writing to their output pipes. */
the child exit report pipe write end to avoid causing a SIGPIPE kwsysProcessClosePipes(cp);
when the child terminates and our signal handler tries to report #endif
it. */
kwsysProcessCleanupDescriptor(&cp->SignalPipe);
for(i=0; i < KWSYSPE_PIPE_COUNT; ++i)
{
kwsysProcessCleanupDescriptor(&cp->PipeReadEnds[i]);
}
cp->PipesLeft = 0;
/* Kill the children. */ /* Kill the children. */
cp->Killed = 1; cp->Killed = 1;
@ -1374,6 +1347,14 @@ void kwsysProcess_Kill(kwsysProcess* cp)
while((waitpid(cp->ForkPIDs[i], &status, 0) < 0) && (errno == EINTR)); 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; 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) static int kwsysProcessSetNonBlocking(int fd)
{ {