X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/1d8dd65e6225836946dea72ce46cedb325ef3ff0..ffd678371dc2232b9afd08c25360800377165e8c:/src/unix/utilsunx.cpp diff --git a/src/unix/utilsunx.cpp b/src/unix/utilsunx.cpp index 1f09b3d448..6fdbc765eb 100644 --- a/src/unix/utilsunx.cpp +++ b/src/unix/utilsunx.cpp @@ -29,9 +29,20 @@ #include "wx/stream.h" #ifdef HAVE_STATFS - #include +# ifdef __BSD__ +# include +# include +# else +# include +# endif #endif // HAVE_STATFS +#ifdef HAVE_STATVFS + #include + + #define statfs statvfs +#endif // HAVE_STATVFS + #if wxUSE_GUI #include "wx/unix/execute.h" #endif @@ -283,67 +294,21 @@ bool wxShell(const wxString& command, wxArrayString& output) void wxHandleProcessTermination(wxEndProcessData *proc_data) { - int pid = (proc_data->pid > 0) ? proc_data->pid : -(proc_data->pid); - - // waitpid is POSIX so should be available everywhere, however on older - // systems wait() might be used instead in a loop (until the right pid - // terminates) - int status = 0; - int rc; - - // wait for child termination and if waitpid() was interrupted, try again - do + // notify user about termination if required + if ( proc_data->process ) { - rc = waitpid(pid, &status, 0); + proc_data->process->OnTerminate(proc_data->pid, proc_data->exitcode); } - while ( rc == -1 && errno == EINTR ); - if (rc == -1) - { - // JACS: this could happen if the process was terminated and waitpid called, - // so commenting out for now. - //wxLogSysError(_("Waiting for subprocess termination failed (return code = -1)")); - } - else if (! (WIFEXITED(status))) - { - wxLogSysError(_("Waiting for subprocess termination failed (WIFEXITED returned zero)")); - - /* AFAIK, this can only happen if something went wrong within - wxGTK, i.e. due to a race condition or some serious bug. - After having fixed the order of statements in - GTK_EndProcessDetector(). (KB) - */ - } - else if (WIFSIGNALED(status)) + // clean up + if ( proc_data->pid > 0 ) { - wxLogSysError(_("Waiting for subprocess termination failed (signal not caught)")); - - /* AFAIK, this can only happen if something went wrong within - wxGTK, i.e. due to a race condition or some serious bug. - After having fixed the order of statements in - GTK_EndProcessDetector(). (KB) - */ + delete proc_data; } - // else + else { - // notify user about termination if required - if (proc_data->process) - { - proc_data->process->OnTerminate(proc_data->pid, - WEXITSTATUS(status)); - } - // clean up - if ( proc_data->pid > 0 ) - { - delete proc_data; - } - else - { - // wxExecute() will know about it - proc_data->exitcode = status; - - proc_data->pid = 0; - } + // let wxExecute() know that the process has terminated + proc_data->pid = 0; } } @@ -451,6 +416,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 +684,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 +725,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; @@ -931,8 +1013,7 @@ long wxGetFreeMemory() bool wxGetDiskSpace(const wxString& path, wxLongLong *pTotal, wxLongLong *pFree) { -#ifdef HAVE_STATFS - +#if defined(HAVE_STATFS) || defined(HAVE_STATVFS) struct statfs fs; if ( statfs(path, &fs) != 0 ) { @@ -941,6 +1022,9 @@ bool wxGetDiskSpace(const wxString& path, wxLongLong *pTotal, wxLongLong *pFree) return FALSE; } + // under Solaris we might have to use fs.f_frsize instead as I think it + // may be a multiple of the block size in general (TODO) + if ( pTotal ) { *pTotal = wxLongLong(fs.f_blocks) * fs.f_bsize; @@ -979,8 +1063,10 @@ bool wxGetEnv(const wxString& var, wxString *value) bool wxSetEnv(const wxString& variable, const wxChar *value) { #if defined(HAVE_SETENV) - return setenv(variable.mb_str(), value ? wxString(value).mb_str().data() - : NULL, 1 /* overwrite */) == 0; + return setenv(variable.mb_str(), + value ? (const char *)wxString(value).mb_str() + : NULL, + 1 /* overwrite */) == 0; #elif defined(HAVE_PUTENV) wxString s = variable; if ( value ) @@ -1007,7 +1093,7 @@ bool wxSetEnv(const wxString& variable, const wxChar *value) #include -static void wxFatalSignalHandler(wxTYPE_SA_HANDLER) +extern "C" void wxFatalSignalHandler(wxTYPE_SA_HANDLER) { if ( wxTheApp ) {