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:
@@ -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
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -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
|
||||||
|
@@ -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)
|
||||||
{
|
{
|
||||||
|
Reference in New Issue
Block a user