fix for wxExecute(subprocess which produces a lot of output) bug

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@11712 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Vadim Zeitlin
2001-09-27 14:12:23 +00:00
parent 74b1c2658a
commit 0e300ddd7d
2 changed files with 121 additions and 3 deletions

View File

@@ -19,6 +19,7 @@ All (GUI):
- wxFindReplaceDialog added (based on work of Markus Greither)
- wxTextCtrl::SetMaxLength() added (wxMSW/wxGTK)
- polygon support in wxRegion (Klaas Holwerda)
- fixed bug with using wxExecute() to capture huge amounts of output
wxHTML:

View File

@@ -451,6 +451,106 @@ size_t wxProcessFileOutputStream::OnSysWrite(const void *buffer, size_t bufsize)
return ret;
}
// ----------------------------------------------------------------------------
// wxStreamTempBuffer
// ----------------------------------------------------------------------------
/*
Extract of a mail to wx-users to give the context of the problem we are
trying to solve here:
MC> If I run the command:
MC> find . -name "*.h" -exec grep linux {} \;
MC> in the exec sample synchronously from the 'Capture command output'
MC> menu, wxExecute never returns. I have to xkill it. Has anyone
MC> else encountered this?
Yes, I can reproduce it too.
I even think I understand why it happens: before launching the external
command we set up a pipe with a valid file descriptor on the reading side
when the output is redirected. So the subprocess happily writes to it ...
until the pipe buffer (which is usually quite big on Unix, I think the
default is 4Mb) is full. Then the writing process stops and waits until we
read some data from the pipe to be able to continue writing to it but we
never do it because we wait until it terminates to start reading and so we
have a classical deadlock.
Here is the fix: we now read the output as soon as it appears into a temp
buffer (wxStreamTempBuffer object) and later just stuff it back into the
stream when the process terminates. See supporting code in wxExecute()
itself as well.
*/
class wxStreamTempBuffer
{
public:
wxStreamTempBuffer();
// call to associate a stream with this buffer, otherwise nothing happens
// at all
void Init(wxInputStream *stream);
// check for input on our stream and cache it in our buffer if any
void Update();
~wxStreamTempBuffer();
private:
// the stream we're buffering, if NULL we don't do anything at all
wxInputStream *m_stream;
// the buffer of size m_size (NULL if m_size == 0)
void *m_buffer;
// the size of the buffer
size_t m_size;
};
wxStreamTempBuffer::wxStreamTempBuffer()
{
m_stream = NULL;
m_buffer = NULL;
m_size = 0;
}
void wxStreamTempBuffer::Init(wxInputStream *stream)
{
m_stream = stream;
}
void wxStreamTempBuffer::Update()
{
if ( m_stream && !m_stream->Eof() )
{
// realloc in blocks of 1Kb - surely not the best strategy but which
// one is?
static const size_t incSize = 1024;
void *buf = realloc(m_buffer, m_size + incSize);
if ( !buf )
{
// don't read any more, we don't have enough memory to do it
m_stream = NULL;
}
else // got memory for the buffer
{
m_buffer = buf;
m_stream->Read((char *)m_buffer + m_size, incSize);
m_size += incSize;
}
}
}
wxStreamTempBuffer::~wxStreamTempBuffer()
{
if ( m_buffer )
{
m_stream->Ungetch(m_buffer, m_size);
free(m_buffer);
}
}
#endif // wxUSE_STREAMS
long wxExecute(wxChar **argv,
@@ -619,15 +719,22 @@ long wxExecute(wxChar **argv,
ARGS_CLEANUP;
// pipe initialization: construction of the wxStreams
#if wxUSE_STREAMS
wxStreamTempBuffer bufIn, bufErr;
#endif // wxUSE_STREAMS
if ( process && process->IsRedirected() )
{
#if wxUSE_STREAMS
// These two streams are relative to this process.
// in/out for subprocess correspond to our out/in
wxOutputStream *outStream = new wxProcessFileOutputStream(pipeIn[1]);
wxInputStream *inStream = new wxProcessFileInputStream(pipeOut[0]);
wxInputStream *errStream = new wxProcessFileInputStream(pipeErr[0]);
process->SetPipeStreams(inStream, outStream, errStream);
bufIn.Init(inStream);
bufErr.Init(inStream);
#endif // wxUSE_STREAMS
close(pipeIn[0]); // close reading side
@@ -653,9 +760,19 @@ long wxExecute(wxChar **argv,
wxBusyCursor bc;
wxWindowDisabler wd;
// it will be set to 0 from GTK_EndProcessDetector
while (data->pid != 0)
// data->pid will be set to 0 from GTK_EndProcessDetector when the
// process terminates
while ( data->pid != 0 )
{
#if wxUSE_STREAMS
bufIn.Update();
bufErr.Update();
#endif // wxUSE_STREAMS
// give GTK+ a chance to call GTK_EndProcessDetector here and
// also repaint the GUI
wxYield();
}
int exitcode = data->exitcode;