]> git.saurik.com Git - wxWidgets.git/blobdiff - src/unix/utilsunx.cpp
Disable wxUSE_ENH_METAFILE for wxGTK builds.
[wxWidgets.git] / src / unix / utilsunx.cpp
index a09fb424df8fca1bcc043df3dba4d49dff65dad3..d757498cc3242cfdce0d3f7408403bb83fe4a5e3 100644 (file)
@@ -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()
@@ -63,9 +65,9 @@
 
 #if HAS_PIPE_STREAMS
 
-// define this to let wxexec.cpp know that we know what we're doing
-#define _WX_USED_BY_WXEXECUTE_
-#include "../common/execcmn.cpp"
+#include "wx/private/pipestream.h"
+#include "wx/private/streamtempinput.h"
+#include "wx/unix/private/executeiohandler.h"
 
 #endif // HAS_PIPE_STREAMS
 
 #include <errno.h>
 #include <netdb.h>
 #include <signal.h>
-#include <fcntl.h>          // for O_WRONLY and friends
 #include <time.h>           // nanosleep() and/or usleep()
 #include <ctype.h>          // isspace()
 #include <sys/time.h>       // needed for FD_SETSIZE
     #include <sys/sysinfo.h>   // for SAGET and MINFO structures
 #endif
 
+#ifdef HAVE_SETPRIORITY
+    #include <sys/resource.h>   // for setpriority()
+#endif
+
 // ----------------------------------------------------------------------------
 // conditional compilation
 // ----------------------------------------------------------------------------
@@ -482,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)
@@ -516,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
@@ -545,6 +613,21 @@ long wxExecute(char **argv, int flags, wxProcess *process,
         }
     }
 
+    // priority: we need to map wxWidgets priority which is in the range 0..100
+    // to Unix nice value which is in the range -20..19. As there is an odd
+    // number of elements in our range and an even number in the Unix one, we
+    // have to do it in this rather ugly way to guarantee that:
+    //  1. wxPRIORITY_{MIN,DEFAULT,MAX} map to -20, 0 and 19 respectively.
+    //  2. The mapping is monotonously increasing.
+    //  3. The mapping is onto the target range.
+    int prio = process ? process->GetPriority() : 0;
+    if ( prio <= 50 )
+        prio = (2*prio)/5 - 20;
+    else if ( prio < 55 )
+        prio = 1;
+    else
+        prio = (2*prio)/5 - 21;
+
     // fork the process
     //
     // NB: do *not* use vfork() here, it completely breaks this code for some
@@ -578,6 +661,13 @@ long wxExecute(char **argv, int flags, wxProcess *process,
         }
 #endif // !__VMS
 
+#if defined(HAVE_SETPRIORITY)
+        if ( prio && setpriority(PRIO_PROCESS, 0, prio) != 0 )
+        {
+            wxLogSysError(_("Failed to set process priority"));
+        }
+#endif // HAVE_SETPRIORITY
+
         // redirect stdin, stdout and stderr
         if ( pipeIn.IsOk() )
         {
@@ -600,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
@@ -618,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);
             }
@@ -682,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() )
         {
@@ -724,14 +799,14 @@ long wxExecute(char **argv, int flags, wxProcess *process,
 
             process->SetPipeStreams(outStream, inStream, errStream);
 
-            bufOut.Init(outStream);
-            bufErr.Init(errStream);
-
-            execData.bufOut = &bufOut;
-            execData.bufErr = &bufErr;
+            if ( flags & wxEXEC_SYNC )
+            {
+                execData.bufOut.Init(outStream);
+                execData.bufErr.Init(errStream);
 
-            execData.fdOut = fdOut;
-            execData.fdErr = fdErr;
+                execData.fdOut = fdOut;
+                execData.fdErr = fdErr;
+            }
         }
 #endif // HAS_PIPE_STREAMS
 
@@ -742,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)
@@ -818,11 +911,16 @@ wxString wxGetUserHome( const wxString &user )
 // the trailing newline
 static wxString wxGetCommandOutput(const wxString &cmd)
 {
-    FILE *f = popen(cmd.ToAscii(), "r");
+    // Suppress stderr from the shell to avoid outputting errors if the command
+    // doesn't exist.
+    FILE *f = popen((cmd + " 2>/dev/null").ToAscii(), "r");
     if ( !f )
     {
-        wxLogSysError(wxT("Executing \"%s\" failed"), cmd.c_str());
-        return wxEmptyString;
+        // Notice that this doesn't happen simply if the command doesn't exist,
+        // but only in case of some really catastrophic failure inside popen()
+        // so we should really notify the user about this as this is not normal.
+        wxLogSysError(wxT("Executing \"%s\" failed"), cmd);
+        return wxString();
     }
 
     wxString s;
@@ -1307,141 +1405,67 @@ bool wxHandleFatalExceptions(bool doit)
 // wxExecute support
 // ----------------------------------------------------------------------------
 
-int wxAppTraits::AddProcessCallback(wxEndProcessData *data, int fd)
-{
-    // define a custom handler processing only the closure of the descriptor
-    struct wxEndProcessFDIOHandler : public wxFDIOHandler
-    {
-        wxEndProcessFDIOHandler(wxEndProcessData *data, int fd)
-            : m_data(data), m_fd(fd)
-        {
-        }
-
-        virtual void OnReadWaiting()
-        {
-            wxFDIODispatcher::Get()->UnregisterFD(m_fd);
-            close(m_fd);
-
-            wxHandleProcessTermination(m_data);
-
-            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
-}
-
-bool wxAppTraits::CheckForRedirectedIO(wxExecuteData& execData)
+int wxAppTraits::WaitForChild(wxExecuteData& execData)
 {
-#if HAS_PIPE_STREAMS
-    bool hasIO = false;
-
-    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
+    wxConsoleEventLoop loop;
+    return RunLoopUntilChildExit(execData, loop);
 }
 
-// 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
+// This function is common code for both console and GUI applications and used
+// by wxExecute() to wait for the child exit while dispatching events.
 //
-// the derived classes still have to implement OnReadWaiting()
-class wxReadFDIOHandler : public wxFDIOHandler
+// 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)
 {
-public:
-    wxReadFDIOHandler(wxFDIODispatcher& disp, int fd) : m_fd(fd)
+    // 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() )
     {
-        if ( fd )
-            disp.RegisterFD(fd, this, wxFDIO_INPUT);
+        stdoutHandler.reset(new wxExecuteEventLoopSourceHandler
+                                (
+                                    execData.fdOut, execData.bufOut
+                                ));
+        stderrHandler.reset(new wxExecuteEventLoopSourceHandler
+                                (
+                                    execData.fdErr, execData.bufErr
+                                ));
     }
+#endif // wxUSE_STREAMS
 
-    virtual void OnWriteWaiting() { wxFAIL_MSG("unreachable"); }
-    virtual void OnExceptionWaiting() { wxFAIL_MSG("unreachable"); }
-
-protected:
-    const int 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;
 
-    wxDECLARE_NO_COPY_CLASS(wxReadFDIOHandler);
-};
+    // And run it.
+    loop.Run();
 
-// 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);
-};
+    // The exit code will have been set when the child termination was detected.
+    return execData.exitcode;
+}
 
-#if HAS_PIPE_STREAMS
+// ----------------------------------------------------------------------------
+// wxExecuteData
+// ----------------------------------------------------------------------------
 
-// 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
+namespace
 {
-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" );
 
@@ -1450,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" );
-
-        // 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 0:
+            // No error but the child is still running.
+            return false;
 
-        return exitcode;
-    }
+        case -1:
+            // Checking child status failed. Invalid PID?
+            wxLogLastError(wxString::Format("waitpid(%d)", pid));
+            return false;
 
-    return -1;
+        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;
+    }
 }
 
 } // 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;
-
-        return execData.pid;
-    }
-    //else: synchronous execution case
+    // 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.
 
-#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;
+        // Notice that this will implicitly remove it from ms_childProcesses.
+        it->second->OnExit(exitcode);
+    }
+}
 
-        wxEndHandler endHandler(disp, execData.GetEndProcReadFD());
+void wxExecuteData::OnStart(int pid_)
+{
+    wxCHECK_RET( wxTheApp,
+                 wxS("Ensure wxTheApp is set before calling wxExecute()") );
 
-        wxRedirectedIOHandler outHandler(disp, execData.fdOut, execData.bufOut),
-                              errHandler(disp, execData.fdErr, execData.bufErr);
+    // 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);
 
-        while ( !endHandler.Terminated() )
-        {
-            disp.Dispatch();
-        }
-    }
-    //else: no IO redirection, just block waiting for the child to exit
-#endif // HAS_PIPE_STREAMS
 
-    return DoWaitForChild(execData.pid);
+    // 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 wxHandleProcessTermination(wxEndProcessData *data)
+void wxExecuteData::OnExit(int exitcode_)
 {
-    data->exitcode = DoWaitForChild(data->pid, WNOHANG);
+    // 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) )
+    {
+        wxFAIL_MSG(wxString::Format(wxS("Data for PID %d not in the list?"), pid));
+    }
+
 
-    // notify user about termination if required
-    if ( data->process )
+    exitcode = exitcode_;
+
+#if wxUSE_STREAMS
+    if ( IsRedirected() )
     {
-        data->process->OnTerminate(data->pid, data->exitcode);
+        // 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
 
-    if ( data->async )
+    // Notify user about termination if required
+    if ( !(flags & wxEXEC_SYNC) )
     {
-        // in case of asynchronous execution we don't need this data any more
+        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();
     }
 }
-