Rewrite wxExecute() implementation under Unix.
This commit changes wxExecute() to handle SIGCHLD to be notified about the child process termination instead of detecting when the file descriptor corresponding to the other end of a pipe opened in the parent process was closed in the child as this was not reliable and could (and did) result in not detecting the termination of the child processes that closed all their file descriptors before exiting. This commit also removes a lot of platform-specific code duplicating the generic event loop sources support and reuses it for wxExecute() purposes too. Final big change is that wxEndProcessData was merged into wxExecuteData and we don't have two similar but quite different classes any more but just one, which is used both to pass the information from wxExecute() to wxAppTraits methods and to store this information until the child termination. Closes #10258. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@74350 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
// Author: Vadim Zeitlin
|
||||
// Id: $Id$
|
||||
// Copyright: (c) 1998 Robert Roebling, Vadim Zeitlin
|
||||
// (c) 2013 Rob Bresalier, Vadim Zeitlin
|
||||
// Licence: wxWindows licence
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -37,6 +38,7 @@
|
||||
#include "wx/apptrait.h"
|
||||
|
||||
#include "wx/process.h"
|
||||
#include "wx/scopedptr.h"
|
||||
#include "wx/thread.h"
|
||||
|
||||
#include "wx/cmdline.h"
|
||||
@@ -48,9 +50,9 @@
|
||||
#include "wx/unix/execute.h"
|
||||
#include "wx/unix/private.h"
|
||||
|
||||
#ifdef wxHAS_GENERIC_PROCESS_CALLBACK
|
||||
#include "wx/private/fdiodispatcher.h"
|
||||
#endif
|
||||
#include "wx/evtloop.h"
|
||||
#include "wx/mstream.h"
|
||||
#include "wx/private/fdioeventloopsourcehandler.h"
|
||||
|
||||
#include <pwd.h>
|
||||
#include <sys/wait.h> // waitpid()
|
||||
@@ -65,6 +67,7 @@
|
||||
|
||||
#include "wx/private/pipestream.h"
|
||||
#include "wx/private/streamtempinput.h"
|
||||
#include "wx/unix/private/executeiohandler.h"
|
||||
|
||||
#endif // HAS_PIPE_STREAMS
|
||||
|
||||
@@ -484,6 +487,76 @@ long wxExecute(wchar_t **wargv, int flags, wxProcess *process,
|
||||
|
||||
#endif // wxUSE_UNICODE
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
// Helper function of wxExecute(): wait for the process termination without
|
||||
// dispatching any events.
|
||||
//
|
||||
// This is used in wxEXEC_NOEVENTS case.
|
||||
int BlockUntilChildExit(wxExecuteData& execData)
|
||||
{
|
||||
wxCHECK_MSG( wxTheApp, -1,
|
||||
wxS("Can't block until child exit without wxTheApp") );
|
||||
|
||||
// Even if we don't want to dispatch events, we still need to handle
|
||||
// child IO notifications and process termination concurrently, i.e.
|
||||
// we can't simply block waiting for the child to terminate as we would
|
||||
// dead lock if it writes more than the pipe buffer size (typically
|
||||
// 4KB) bytes of output -- it would then block waiting for us to read
|
||||
// the data while we'd block waiting for it to terminate.
|
||||
//
|
||||
// So while we don't use the full blown event loop, we still do use a
|
||||
// dispatcher with which we register just the 3 FDs we're interested
|
||||
// in: the child stdout and stderr and the pipe written to by the
|
||||
// signal handler so that we could react to the child process
|
||||
// termination too.
|
||||
|
||||
// Notice that we must create a new dispatcher object here instead of
|
||||
// reusing the global wxFDIODispatcher::Get() because we want to
|
||||
// monitor only the events on the FDs explicitly registered with this
|
||||
// one and not all the other ones that could be registered with the
|
||||
// global dispatcher (think about the case of nested wxExecute() calls).
|
||||
wxSelectDispatcher dispatcher;
|
||||
|
||||
// Do register all the FDs we want to monitor here: first, the one used to
|
||||
// handle the signals asynchronously.
|
||||
wxScopedPtr<wxFDIOHandler>
|
||||
signalHandler(wxTheApp->RegisterSignalWakeUpPipe(dispatcher));
|
||||
|
||||
#if wxUSE_STREAMS
|
||||
// And then the two for the child output and error streams if necessary.
|
||||
wxScopedPtr<wxFDIOHandler>
|
||||
stdoutHandler,
|
||||
stderrHandler;
|
||||
if ( execData.IsRedirected() )
|
||||
{
|
||||
stdoutHandler.reset(new wxExecuteFDIOHandler
|
||||
(
|
||||
dispatcher,
|
||||
execData.fdOut,
|
||||
execData.bufOut
|
||||
));
|
||||
stderrHandler.reset(new wxExecuteFDIOHandler
|
||||
(
|
||||
dispatcher,
|
||||
execData.fdErr,
|
||||
execData.bufErr
|
||||
));
|
||||
}
|
||||
#endif // wxUSE_STREAMS
|
||||
|
||||
// And dispatch until the PID is reset from wxExecuteData::OnExit().
|
||||
while ( execData.pid )
|
||||
{
|
||||
dispatcher.Dispatch();
|
||||
}
|
||||
|
||||
return execData.exitcode;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
// wxExecute: the real worker function
|
||||
long wxExecute(char **argv, int flags, wxProcess *process,
|
||||
const wxExecuteEnv *env)
|
||||
@@ -518,21 +591,14 @@ long wxExecute(char **argv, int flags, wxProcess *process,
|
||||
}
|
||||
#endif // __DARWIN__
|
||||
|
||||
|
||||
// this struct contains all information which we use for housekeeping
|
||||
wxExecuteData execData;
|
||||
wxScopedPtr<wxExecuteData> execDataPtr(new wxExecuteData);
|
||||
wxExecuteData& execData = *execDataPtr;
|
||||
|
||||
execData.flags = flags;
|
||||
execData.process = process;
|
||||
|
||||
// create pipes
|
||||
if ( !execData.pipeEndProcDetect.Create() )
|
||||
{
|
||||
wxLogError( _("Failed to execute '%s'\n"), *argv );
|
||||
|
||||
return ERROR_RETURN_CODE;
|
||||
}
|
||||
|
||||
// pipes for inter process communication
|
||||
// create pipes for inter process communication
|
||||
wxPipe pipeIn, // stdin
|
||||
pipeOut, // stdout
|
||||
pipeErr; // stderr
|
||||
@@ -624,13 +690,6 @@ long wxExecute(char **argv, int flags, wxProcess *process,
|
||||
// the descriptors do not need to be closed but for now this is better
|
||||
// than never closing them at all as wx code never used FD_CLOEXEC.
|
||||
|
||||
// Note that while the reading side of the end process detection pipe
|
||||
// can be safely closed, we should keep the write one opened, it will
|
||||
// be only closed when the process terminates resulting in a read
|
||||
// notification to the parent
|
||||
const int fdEndProc = execData.pipeEndProcDetect.Detach(wxPipe::Write);
|
||||
execData.pipeEndProcDetect.Close();
|
||||
|
||||
// TODO: Iterating up to FD_SETSIZE is both inefficient (because it may
|
||||
// be quite big) and incorrect (because in principle we could
|
||||
// have more opened descriptions than this number). Unfortunately
|
||||
@@ -642,8 +701,7 @@ long wxExecute(char **argv, int flags, wxProcess *process,
|
||||
{
|
||||
if ( fd != STDIN_FILENO &&
|
||||
fd != STDOUT_FILENO &&
|
||||
fd != STDERR_FILENO &&
|
||||
fd != fdEndProc )
|
||||
fd != STDERR_FILENO )
|
||||
{
|
||||
close(fd);
|
||||
}
|
||||
@@ -706,18 +764,11 @@ long wxExecute(char **argv, int flags, wxProcess *process,
|
||||
}
|
||||
else // we're in parent
|
||||
{
|
||||
// save it for WaitForChild() use
|
||||
execData.pid = pid;
|
||||
if (execData.process)
|
||||
execData.process->SetPid(pid); // and also in the wxProcess
|
||||
execData.OnStart(pid);
|
||||
|
||||
// prepare for IO redirection
|
||||
|
||||
#if HAS_PIPE_STREAMS
|
||||
// the input buffer bufOut is connected to stdout, this is why it is
|
||||
// called bufOut and not bufIn
|
||||
wxStreamTempInputBuffer bufOut,
|
||||
bufErr;
|
||||
|
||||
if ( process && process->IsRedirected() )
|
||||
{
|
||||
@@ -748,14 +799,14 @@ long wxExecute(char **argv, int flags, wxProcess *process,
|
||||
|
||||
process->SetPipeStreams(outStream, inStream, errStream);
|
||||
|
||||
bufOut.Init(outStream);
|
||||
bufErr.Init(errStream);
|
||||
if ( flags & wxEXEC_SYNC )
|
||||
{
|
||||
execData.bufOut.Init(outStream);
|
||||
execData.bufErr.Init(errStream);
|
||||
|
||||
execData.bufOut = &bufOut;
|
||||
execData.bufErr = &bufErr;
|
||||
|
||||
execData.fdOut = fdOut;
|
||||
execData.fdErr = fdErr;
|
||||
execData.fdOut = fdOut;
|
||||
execData.fdErr = fdErr;
|
||||
}
|
||||
}
|
||||
#endif // HAS_PIPE_STREAMS
|
||||
|
||||
@@ -766,14 +817,32 @@ long wxExecute(char **argv, int flags, wxProcess *process,
|
||||
pipeErr.Close();
|
||||
}
|
||||
|
||||
// we want this function to work even if there is no wxApp so ensure
|
||||
// that we have a valid traits pointer
|
||||
wxConsoleAppTraits traitsConsole;
|
||||
wxAppTraits *traits = wxTheApp ? wxTheApp->GetTraits() : NULL;
|
||||
if ( !traits )
|
||||
traits = &traitsConsole;
|
||||
// For the asynchronous case we don't have to do anything else, just
|
||||
// let the process run.
|
||||
if ( !(flags & wxEXEC_SYNC) )
|
||||
{
|
||||
// Ensure that the housekeeping data is kept alive, it will be
|
||||
// destroyed only when the child terminates.
|
||||
execDataPtr.release();
|
||||
|
||||
return traits->WaitForChild(execData);
|
||||
return execData.pid;
|
||||
}
|
||||
|
||||
|
||||
// If we don't need to dispatch any events, things are relatively
|
||||
// simple and we don't need to delegate to wxAppTraits.
|
||||
if ( flags & wxEXEC_NOEVENTS )
|
||||
{
|
||||
return BlockUntilChildExit(execData);
|
||||
}
|
||||
|
||||
|
||||
// If we do need to dispatch events, enter a local event loop waiting
|
||||
// until the child exits. As the exact kind of event loop depends on
|
||||
// the sort of application we're in (console or GUI), we delegate this
|
||||
// to wxAppTraits which virtualizes all the differences between the
|
||||
// console and the GUI programs.
|
||||
return wxApp::GetValidTraits().WaitForChild(execData);
|
||||
}
|
||||
|
||||
#if !defined(__VMS) && !defined(__INTEL_COMPILER)
|
||||
@@ -1336,141 +1405,67 @@ bool wxHandleFatalExceptions(bool doit)
|
||||
// wxExecute support
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
int wxAppTraits::AddProcessCallback(wxEndProcessData *data, int fd)
|
||||
int wxAppTraits::WaitForChild(wxExecuteData& execData)
|
||||
{
|
||||
// define a custom handler processing only the closure of the descriptor
|
||||
struct wxEndProcessFDIOHandler : public wxFDIOHandler
|
||||
wxConsoleEventLoop loop;
|
||||
return RunLoopUntilChildExit(execData, loop);
|
||||
}
|
||||
|
||||
// This function is common code for both console and GUI applications and used
|
||||
// by wxExecute() to wait for the child exit while dispatching events.
|
||||
//
|
||||
// Notice that it should not be used for all the other cases, e.g. when we
|
||||
// don't need to wait for the child (wxEXEC_ASYNC) or when the events must not
|
||||
// dispatched (wxEXEC_NOEVENTS).
|
||||
int
|
||||
wxAppTraits::RunLoopUntilChildExit(wxExecuteData& execData,
|
||||
wxEventLoopBase& loop)
|
||||
{
|
||||
// It is possible that wxExecuteData::OnExit() had already been called
|
||||
// and reset the PID to 0, in which case we don't need to do anything
|
||||
// at all.
|
||||
if ( !execData.pid )
|
||||
return execData.exitcode;
|
||||
|
||||
#if wxUSE_STREAMS
|
||||
// Monitor the child streams if necessary.
|
||||
wxScopedPtr<wxEventLoopSourceHandler>
|
||||
stdoutHandler,
|
||||
stderrHandler;
|
||||
if ( execData.IsRedirected() )
|
||||
{
|
||||
wxEndProcessFDIOHandler(wxEndProcessData *data, int fd)
|
||||
: m_data(data), m_fd(fd)
|
||||
{
|
||||
}
|
||||
stdoutHandler.reset(new wxExecuteEventLoopSourceHandler
|
||||
(
|
||||
execData.fdOut, execData.bufOut
|
||||
));
|
||||
stderrHandler.reset(new wxExecuteEventLoopSourceHandler
|
||||
(
|
||||
execData.fdErr, execData.bufErr
|
||||
));
|
||||
}
|
||||
#endif // wxUSE_STREAMS
|
||||
|
||||
virtual void OnReadWaiting()
|
||||
{
|
||||
wxFDIODispatcher::Get()->UnregisterFD(m_fd);
|
||||
close(m_fd);
|
||||
// Store the event loop in the data associated with the child
|
||||
// process so that it could exit the loop when the child exits.
|
||||
execData.syncEventLoop = &loop;
|
||||
|
||||
wxHandleProcessTermination(m_data);
|
||||
// And run it.
|
||||
loop.Run();
|
||||
|
||||
delete this;
|
||||
}
|
||||
|
||||
virtual void OnWriteWaiting() { wxFAIL_MSG("unreachable"); }
|
||||
virtual void OnExceptionWaiting() { wxFAIL_MSG("unreachable"); }
|
||||
|
||||
wxEndProcessData * const m_data;
|
||||
const int m_fd;
|
||||
};
|
||||
|
||||
wxFDIODispatcher::Get()->RegisterFD
|
||||
(
|
||||
fd,
|
||||
new wxEndProcessFDIOHandler(data, fd),
|
||||
wxFDIO_INPUT
|
||||
);
|
||||
return fd; // unused, but return something unique for the tag
|
||||
// The exit code will have been set when the child termination was detected.
|
||||
return execData.exitcode;
|
||||
}
|
||||
|
||||
bool wxAppTraits::CheckForRedirectedIO(wxExecuteData& execData)
|
||||
{
|
||||
#if HAS_PIPE_STREAMS
|
||||
bool hasIO = false;
|
||||
// ----------------------------------------------------------------------------
|
||||
// wxExecuteData
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
if ( execData.bufOut && execData.bufOut->Update() )
|
||||
hasIO = true;
|
||||
|
||||
if ( execData.bufErr && execData.bufErr->Update() )
|
||||
hasIO = true;
|
||||
|
||||
return hasIO;
|
||||
#else // !HAS_PIPE_STREAMS
|
||||
wxUnusedVar(execData);
|
||||
|
||||
return false;
|
||||
#endif // HAS_PIPE_STREAMS/!HAS_PIPE_STREAMS
|
||||
}
|
||||
|
||||
// helper classes/functions used by WaitForChild()
|
||||
namespace
|
||||
{
|
||||
|
||||
// convenient base class for IO handlers which are registered for read
|
||||
// notifications only and which also stores the FD we're reading from
|
||||
//
|
||||
// the derived classes still have to implement OnReadWaiting()
|
||||
class wxReadFDIOHandler : public wxFDIOHandler
|
||||
{
|
||||
public:
|
||||
wxReadFDIOHandler(wxFDIODispatcher& disp, int fd) : m_fd(fd)
|
||||
{
|
||||
if ( fd )
|
||||
disp.RegisterFD(fd, this, wxFDIO_INPUT);
|
||||
}
|
||||
|
||||
virtual void OnWriteWaiting() { wxFAIL_MSG("unreachable"); }
|
||||
virtual void OnExceptionWaiting() { wxFAIL_MSG("unreachable"); }
|
||||
|
||||
protected:
|
||||
const int m_fd;
|
||||
|
||||
wxDECLARE_NO_COPY_CLASS(wxReadFDIOHandler);
|
||||
};
|
||||
|
||||
// class for monitoring our end of the process detection pipe, simply sets a
|
||||
// flag when input on the pipe (which must be due to EOF) is detected
|
||||
class wxEndHandler : public wxReadFDIOHandler
|
||||
{
|
||||
public:
|
||||
wxEndHandler(wxFDIODispatcher& disp, int fd)
|
||||
: wxReadFDIOHandler(disp, fd)
|
||||
{
|
||||
m_terminated = false;
|
||||
}
|
||||
|
||||
bool Terminated() const { return m_terminated; }
|
||||
|
||||
virtual void OnReadWaiting() { m_terminated = true; }
|
||||
|
||||
private:
|
||||
bool m_terminated;
|
||||
|
||||
wxDECLARE_NO_COPY_CLASS(wxEndHandler);
|
||||
};
|
||||
|
||||
#if HAS_PIPE_STREAMS
|
||||
|
||||
// class for monitoring our ends of child stdout/err, should be constructed
|
||||
// with the FD and stream from wxExecuteData and will do nothing if they're
|
||||
// invalid
|
||||
//
|
||||
// unlike wxEndHandler this class registers itself with the provided dispatcher
|
||||
class wxRedirectedIOHandler : public wxReadFDIOHandler
|
||||
{
|
||||
public:
|
||||
wxRedirectedIOHandler(wxFDIODispatcher& disp,
|
||||
int fd,
|
||||
wxStreamTempInputBuffer *buf)
|
||||
: wxReadFDIOHandler(disp, fd),
|
||||
m_buf(buf)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void OnReadWaiting()
|
||||
{
|
||||
m_buf->Update();
|
||||
}
|
||||
|
||||
private:
|
||||
wxStreamTempInputBuffer * const m_buf;
|
||||
|
||||
wxDECLARE_NO_COPY_CLASS(wxRedirectedIOHandler);
|
||||
};
|
||||
|
||||
#endif // HAS_PIPE_STREAMS
|
||||
|
||||
// helper function which calls waitpid() and analyzes the result
|
||||
int DoWaitForChild(int pid, int flags = 0)
|
||||
// Helper function that checks whether the child with the given PID has exited
|
||||
// and fills the provided parameter with its return code if it did.
|
||||
bool CheckForChildExit(int pid, int* exitcodeOut)
|
||||
{
|
||||
wxASSERT_MSG( pid > 0, "invalid PID" );
|
||||
|
||||
@@ -1479,124 +1474,150 @@ int DoWaitForChild(int pid, int flags = 0)
|
||||
// loop while we're getting EINTR
|
||||
for ( ;; )
|
||||
{
|
||||
rc = waitpid(pid, &status, flags);
|
||||
rc = waitpid(pid, &status, WNOHANG);
|
||||
|
||||
if ( rc != -1 || errno != EINTR )
|
||||
break;
|
||||
}
|
||||
|
||||
if ( rc == 0 )
|
||||
switch ( rc )
|
||||
{
|
||||
// This can only happen if the child application closes our dummy pipe
|
||||
// that is used to monitor its lifetime; in that case, our best bet is
|
||||
// to pretend the process did terminate, because otherwise wxExecute()
|
||||
// would hang indefinitely (OnReadWaiting() won't be called again, the
|
||||
// descriptor is closed now).
|
||||
wxLogDebug("Child process (PID %d) still alive but pipe closed so "
|
||||
"generating a close notification", pid);
|
||||
}
|
||||
else if ( rc == -1 )
|
||||
{
|
||||
wxLogLastError(wxString::Format("waitpid(%d)", pid));
|
||||
}
|
||||
else // child did terminate
|
||||
{
|
||||
wxASSERT_MSG( rc == pid, "unexpected waitpid() return value" );
|
||||
case 0:
|
||||
// No error but the child is still running.
|
||||
return false;
|
||||
|
||||
// notice that the caller expects the exit code to be signed, e.g. -1
|
||||
// instead of 255 so don't assign WEXITSTATUS() to an int
|
||||
signed char exitcode;
|
||||
if ( WIFEXITED(status) )
|
||||
exitcode = WEXITSTATUS(status);
|
||||
else if ( WIFSIGNALED(status) )
|
||||
exitcode = -WTERMSIG(status);
|
||||
else
|
||||
{
|
||||
wxLogError("Child process (PID %d) exited for unknown reason, "
|
||||
"status = %d", pid, status);
|
||||
exitcode = -1;
|
||||
}
|
||||
case -1:
|
||||
// Checking child status failed. Invalid PID?
|
||||
wxLogLastError(wxString::Format("waitpid(%d)", pid));
|
||||
return false;
|
||||
|
||||
return exitcode;
|
||||
default:
|
||||
// Child did terminate.
|
||||
wxASSERT_MSG( rc == pid, "unexpected waitpid() return value" );
|
||||
|
||||
// notice that the caller expects the exit code to be signed, e.g. -1
|
||||
// instead of 255 so don't assign WEXITSTATUS() to an int
|
||||
signed char exitcode;
|
||||
if ( WIFEXITED(status) )
|
||||
exitcode = WEXITSTATUS(status);
|
||||
else if ( WIFSIGNALED(status) )
|
||||
exitcode = -WTERMSIG(status);
|
||||
else
|
||||
{
|
||||
wxLogError("Child process (PID %d) exited for unknown reason, "
|
||||
"status = %d", pid, status);
|
||||
exitcode = -1;
|
||||
}
|
||||
|
||||
if ( exitcodeOut )
|
||||
*exitcodeOut = exitcode;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
int wxAppTraits::WaitForChild(wxExecuteData& execData)
|
||||
wxExecuteData::ChildProcessesData wxExecuteData::ms_childProcesses;
|
||||
|
||||
/* static */
|
||||
void wxExecuteData::OnSomeChildExited(int WXUNUSED(sig))
|
||||
{
|
||||
if ( !(execData.flags & wxEXEC_SYNC) )
|
||||
{
|
||||
// asynchronous execution: just launch the process and return,
|
||||
// endProcData will be destroyed when it terminates (currently we leak
|
||||
// it if the process doesn't terminate before we do and this should be
|
||||
// fixed but it's not a real leak so it's not really very high
|
||||
// priority)
|
||||
wxEndProcessData *endProcData = new wxEndProcessData;
|
||||
endProcData->process = execData.process;
|
||||
endProcData->pid = execData.pid;
|
||||
endProcData->tag = AddProcessCallback
|
||||
(
|
||||
endProcData,
|
||||
execData.GetEndProcReadFD()
|
||||
);
|
||||
endProcData->async = true;
|
||||
// We know that some child process has terminated, but we don't know which
|
||||
// one, so check all of them (notice that more than one could have exited).
|
||||
//
|
||||
// An alternative approach would be to call waitpid(-1, &status, WNOHANG)
|
||||
// (in a loop to take care of the multiple children exiting case) and
|
||||
// perhaps this would be more efficient. But for now this seems to work.
|
||||
|
||||
return execData.pid;
|
||||
}
|
||||
//else: synchronous execution case
|
||||
|
||||
#if HAS_PIPE_STREAMS && wxUSE_SOCKETS
|
||||
wxProcess * const process = execData.process;
|
||||
if ( process && process->IsRedirected() )
|
||||
// Make a copy of the list before iterating over it to avoid problems due
|
||||
// to deleting entries from it in the process.
|
||||
const ChildProcessesData allChildProcesses = ms_childProcesses;
|
||||
for ( ChildProcessesData::const_iterator it = allChildProcesses.begin();
|
||||
it != allChildProcesses.end();
|
||||
++it )
|
||||
{
|
||||
// we can't simply block waiting for the child to terminate as we would
|
||||
// dead lock if it writes more than the pipe buffer size (typically
|
||||
// 4KB) bytes of output -- it would then block waiting for us to read
|
||||
// the data while we'd block waiting for it to terminate
|
||||
const int pid = it->first;
|
||||
|
||||
// Check whether this child exited.
|
||||
int exitcode;
|
||||
if ( !CheckForChildExit(pid, &exitcode) )
|
||||
continue;
|
||||
|
||||
// And handle its termination if it did.
|
||||
//
|
||||
// so multiplex here waiting for any input from the child or closure of
|
||||
// the pipe used to indicate its termination
|
||||
wxSelectDispatcher disp;
|
||||
|
||||
wxEndHandler endHandler(disp, execData.GetEndProcReadFD());
|
||||
|
||||
wxRedirectedIOHandler outHandler(disp, execData.fdOut, execData.bufOut),
|
||||
errHandler(disp, execData.fdErr, execData.bufErr);
|
||||
|
||||
while ( !endHandler.Terminated() )
|
||||
{
|
||||
disp.Dispatch();
|
||||
}
|
||||
// Notice that this will implicitly remove it from ms_childProcesses.
|
||||
it->second->OnExit(exitcode);
|
||||
}
|
||||
//else: no IO redirection, just block waiting for the child to exit
|
||||
#endif // HAS_PIPE_STREAMS
|
||||
|
||||
return DoWaitForChild(execData.pid);
|
||||
}
|
||||
|
||||
void wxHandleProcessTermination(wxEndProcessData *data)
|
||||
void wxExecuteData::OnStart(int pid_)
|
||||
{
|
||||
data->exitcode = DoWaitForChild(data->pid, WNOHANG);
|
||||
wxCHECK_RET( wxTheApp,
|
||||
wxS("Ensure wxTheApp is set before calling wxExecute()") );
|
||||
|
||||
// notify user about termination if required
|
||||
if ( data->process )
|
||||
// Setup the signal handler for SIGCHLD to be able to detect the child
|
||||
// termination.
|
||||
//
|
||||
// Notice that SetSignalHandler() is idempotent, so it's fine to call
|
||||
// it more than once with the same handler.
|
||||
wxTheApp->SetSignalHandler(SIGCHLD, OnSomeChildExited);
|
||||
|
||||
|
||||
// Remember the child PID to be able to wait for it later.
|
||||
pid = pid_;
|
||||
|
||||
// Also save it in wxProcess where it will be accessible to the user code.
|
||||
if ( process )
|
||||
process->SetPid(pid);
|
||||
|
||||
// Finally, add this object itself to the list of child processes so that
|
||||
// we can check for its termination the next time we get SIGCHLD.
|
||||
ms_childProcesses[pid] = this;
|
||||
}
|
||||
|
||||
void wxExecuteData::OnExit(int exitcode_)
|
||||
{
|
||||
// Remove this process from the hash list of child processes that are
|
||||
// still open as soon as possible to ensure we don't process it again even
|
||||
// if another SIGCHLD happens.
|
||||
if ( !ms_childProcesses.erase(pid) )
|
||||
{
|
||||
data->process->OnTerminate(data->pid, data->exitcode);
|
||||
wxFAIL_MSG(wxString::Format(wxS("Data for PID %d not in the list?"), pid));
|
||||
}
|
||||
|
||||
if ( data->async )
|
||||
|
||||
exitcode = exitcode_;
|
||||
|
||||
#if wxUSE_STREAMS
|
||||
if ( IsRedirected() )
|
||||
{
|
||||
// in case of asynchronous execution we don't need this data any more
|
||||
// Read the remaining data in a blocking way: this is fine because the
|
||||
// child has already exited and hence all the data must be already
|
||||
// available in the streams buffers.
|
||||
bufOut.ReadAll();
|
||||
bufErr.ReadAll();
|
||||
}
|
||||
#endif // wxUSE_STREAMS
|
||||
|
||||
// Notify user about termination if required
|
||||
if ( !(flags & wxEXEC_SYNC) )
|
||||
{
|
||||
if ( process )
|
||||
process->OnTerminate(pid, exitcode);
|
||||
|
||||
// in case of asynchronous execution we don't need this object any more
|
||||
// after the child terminates
|
||||
delete data;
|
||||
delete this;
|
||||
}
|
||||
else // sync execution
|
||||
{
|
||||
// let wxExecute() know that the process has terminated
|
||||
data->pid = 0;
|
||||
pid = 0;
|
||||
|
||||
// Stop the event loop for synchronous wxExecute() if we're running one.
|
||||
if ( syncEventLoop )
|
||||
syncEventLoop->ScheduleExit();
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user