fixed IO redirection to work in wxExecute() in console applications (including the case when the child process outputs more than pipe buffer size) by using wxSelectDispatcher for multiplexing

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@52692 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Vadim Zeitlin
2008-03-22 14:10:54 +00:00
parent a83ca5fbf5
commit 33343395da
3 changed files with 177 additions and 73 deletions

View File

@@ -40,9 +40,22 @@ struct wxExecuteData
#if wxUSE_STREAMS #if wxUSE_STREAMS
bufOut = bufOut =
bufErr = NULL; bufErr = NULL;
fdOut =
fdErr = wxPipe::INVALID_FD;
#endif // wxUSE_STREAMS #endif // wxUSE_STREAMS
} }
// get the FD corresponding to the read end of the process end detection
// pipe and close the write one
int GetEndProcReadFD()
{
const int fd = pipeEndProcDetect.Detach(wxPipe::Read);
pipeEndProcDetect.Close();
return fd;
}
// wxExecute() flags // wxExecute() flags
int flags; int flags;
@@ -60,6 +73,10 @@ struct wxExecuteData
// called bufOut and not bufIn // called bufOut and not bufIn
wxStreamTempInputBuffer *bufOut, wxStreamTempInputBuffer *bufOut,
*bufErr; *bufErr;
// the corresponding FDs, -1 if not redirected
int fdOut,
fdErr;
#endif // wxUSE_STREAMS #endif // wxUSE_STREAMS
}; };

View File

@@ -58,11 +58,9 @@ int wxGUIAppTraits::WaitForChild(wxExecuteData& execData)
endProcData.tag = AddProcessCallback endProcData.tag = AddProcessCallback
( (
&endProcData, &endProcData,
execData.pipeEndProcDetect.Detach(wxPipe::Read) execData.GetEndProcReadFD()
); );
execData.pipeEndProcDetect.Close();
// prepare to wait for the child termination: show to the user that we're // prepare to wait for the child termination: show to the user that we're
// busy and refuse all input unless explicitly told otherwise // busy and refuse all input unless explicitly told otherwise

View File

@@ -41,6 +41,7 @@
#include "wx/wfstream.h" #include "wx/wfstream.h"
#include "wx/private/selectdispatcher.h"
#include "wx/private/fdiodispatcher.h" #include "wx/private/fdiodispatcher.h"
#include "wx/unix/execute.h" #include "wx/unix/execute.h"
#include "wx/unix/private.h" #include "wx/unix/private.h"
@@ -650,19 +651,17 @@ long wxExecute(wxChar **argv, int flags, wxProcess *process)
// called bufOut and not bufIn // called bufOut and not bufIn
wxStreamTempInputBuffer bufOut, wxStreamTempInputBuffer bufOut,
bufErr; bufErr;
#endif // HAS_PIPE_INPUT_STREAM
if ( process && process->IsRedirected() ) if ( process && process->IsRedirected() )
{ {
#if HAS_PIPE_INPUT_STREAM
wxOutputStream *inStream = wxOutputStream *inStream =
new wxFileOutputStream(pipeIn.Detach(wxPipe::Write)); new wxFileOutputStream(pipeIn.Detach(wxPipe::Write));
wxPipeInputStream *outStream = const int fdOut = pipeOut.Detach(wxPipe::Read);
new wxPipeInputStream(pipeOut.Detach(wxPipe::Read)); wxPipeInputStream *outStream = new wxPipeInputStream(fdOut);
wxPipeInputStream *errStream = const int fdErr = pipeErr.Detach(wxPipe::Read);
new wxPipeInputStream(pipeErr.Detach(wxPipe::Read)); wxPipeInputStream *errStream = new wxPipeInputStream(fdErr);
process->SetPipeStreams(outStream, inStream, errStream); process->SetPipeStreams(outStream, inStream, errStream);
@@ -671,8 +670,11 @@ long wxExecute(wxChar **argv, int flags, wxProcess *process)
execData.bufOut = &bufOut; execData.bufOut = &bufOut;
execData.bufErr = &bufErr; execData.bufErr = &bufErr;
#endif // HAS_PIPE_INPUT_STREAM
execData.fdOut = fdOut;
execData.fdErr = fdErr;
} }
#endif // HAS_PIPE_INPUT_STREAM
if ( pipeIn.IsOk() ) if ( pipeIn.IsOk() )
{ {
@@ -1325,11 +1327,114 @@ bool wxAppTraits::CheckForRedirectedIO(wxExecuteData& execData)
#endif // HAS_PIPE_INPUT_STREAM/!HAS_PIPE_INPUT_STREAM #endif // HAS_PIPE_INPUT_STREAM/!HAS_PIPE_INPUT_STREAM
} }
class wxReadFDIOHandler : public wxFDIOHandler
{
public:
wxReadFDIOHandler(wxFDIODispatcher& disp, int fd) : m_fd(fd)
{
if ( fd )
disp.RegisterFD(fd, this, wxFDIO_INPUT);
}
int GetFD() const { return m_fd; }
virtual void OnWriteWaiting() { wxFAIL_MSG("unreachable"); }
virtual void OnExceptionWaiting() { wxFAIL_MSG("unreachable"); }
private:
const int m_fd;
DECLARE_NO_COPY_CLASS(wxReadFDIOHandler)
};
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;
DECLARE_NO_COPY_CLASS(wxEndHandler)
};
#if wxUSE_STREAMS
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;
DECLARE_NO_COPY_CLASS(wxRedirectedIOHandler)
};
#endif // wxUSE_STREAMS
int wxAppTraits::WaitForChild(wxExecuteData& execData) int wxAppTraits::WaitForChild(wxExecuteData& execData)
{ {
if ( execData.flags & wxEXEC_SYNC ) if ( !(execData.flags & wxEXEC_SYNC) )
{ {
// just block waiting for the child to exit // asynchronous execution: just launch the process and return
wxEndProcessData *endProcData = new wxEndProcessData;
endProcData->process = execData.process;
endProcData->pid = execData.pid;
endProcData->tag = AddProcessCallback
(
endProcData,
execData.GetEndProcReadFD()
);
return execData.pid;
}
#if wxUSE_STREAMS
wxProcess * const process = execData.process;
if ( process && process->IsRedirected() )
{
// 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 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();
}
}
//else: no IO redirection, just block waiting for the child to exit
#endif // wxUSE_STREAMS
int status = 0; int status = 0;
int result = waitpid(execData.pid, &status, 0); int result = waitpid(execData.pid, &status, 0);
@@ -1386,22 +1491,6 @@ int wxAppTraits::WaitForChild(wxExecuteData& execData)
return -1; return -1;
} }
else // asynchronous execution
{
wxEndProcessData *endProcData = new wxEndProcessData;
endProcData->process = execData.process;
endProcData->pid = execData.pid;
endProcData->tag = AddProcessCallback
(
endProcData,
execData.pipeEndProcDetect.Detach(wxPipe::Read)
);
execData.pipeEndProcDetect.Close();
return execData.pid;
}
}
void wxHandleProcessTermination(wxEndProcessData *proc_data) void wxHandleProcessTermination(wxEndProcessData *proc_data)
{ {