]> git.saurik.com Git - wxWidgets.git/commitdiff
Rewrite wxExecute() implementation under Unix.
authorVadim Zeitlin <vadim@wxwidgets.org>
Wed, 3 Jul 2013 00:32:16 +0000 (00:32 +0000)
committerVadim Zeitlin <vadim@wxwidgets.org>
Wed, 3 Jul 2013 00:32:16 +0000 (00:32 +0000)
This commit changes wxExecute() to handle SIGCHLD to be notified about the
child process termination instead of detecting when the file descriptor
corresponding to the other end of a pipe opened in the parent process was
closed in the child as this was not reliable and could (and did) result in not
detecting the termination of the child processes that closed all their file
descriptors before exiting.

This commit also removes a lot of platform-specific code duplicating the
generic event loop sources support and reuses it for wxExecute() purposes too.

Final big change is that wxEndProcessData was merged into wxExecuteData and we
don't have two similar but quite different classes any more but just one,
which is used both to pass the information from wxExecute() to wxAppTraits
methods and to store this information until the child termination.

Closes #10258.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@74350 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775

22 files changed:
docs/changes.txt
include/wx/unix/app.h
include/wx/unix/apptbase.h
include/wx/unix/apptrait.h
include/wx/unix/execute.h
include/wx/unix/private/executeiohandler.h [new file with mode: 0644]
src/cocoa/evtloop.mm
src/cocoa/utilsexc.mm
src/common/evtloopcmn.cpp
src/dfb/utils.cpp
src/gtk/evtloop.cpp
src/gtk/utilsgtk.cpp
src/gtk1/utilsgtk.cpp
src/motif/utils.cpp
src/os2/utils.cpp
src/osx/core/evtloop_cf.cpp
src/osx/core/utilsexc_cf.cpp
src/unix/apptraits.cpp
src/unix/evtloopunix.cpp
src/unix/utilsunx.cpp
src/x11/utils.cpp
tests/exec/exec.cpp

index 2cfac6cc8f75a122885390ff9829711d057e9939..ca2dc13101d4eb5255a5334c53f777fb59464961 100644 (file)
@@ -574,6 +574,7 @@ All:
   event type constants to make using Bind() less verbose. E.g.
   wxEVT_COMMAND_MENU_SELECTED is now wxEVT_MENU (but the old name remains
   available for compatibility) (Catalin Raceanu).
+- Fix wxExecute() implementation under Unix (Rob Bresalier).
 - Add wxEvtHandler::CallAfter() method for asynchronous method calls.
 - Add support for symlinks to wxFileName (David Hart).
 - Add wxDIR_NO_FOLLOW flag for wxDir traversal (David Hart).
index 812783bcef3d038d668be74c5efc5bbfd6802b19..222d2662e8a27e066571875cd87536e92b14543f 100644 (file)
@@ -47,7 +47,7 @@ public:
 
     // Register the signal wake up pipe with the given dispatcher.
     //
-    // This is not used anywhere yet but will be soon.
+    // This is used by wxExecute(wxEXEC_NOEVENTS) implementation only.
     //
     // The pointer to the handler used for processing events on this descriptor
     // is returned so that it can be deleted when we no longer needed it.
index fd624aeed4f10624eb69fb61d177a472b8e063d3..1d5085125830ad9cac7d1bdd5e21e659501cda7c 100644 (file)
 #ifndef _WX_UNIX_APPTBASE_H_
 #define _WX_UNIX_APPTBASE_H_
 
-struct wxEndProcessData;
-struct wxExecuteData;
+#include "wx/evtloop.h"
+#include "wx/evtloopsrc.h"
+
+class wxExecuteData;
 class wxFDIOManager;
 class wxEventLoopSourcesManagerBase;
 
@@ -27,23 +29,13 @@ public:
     // wxExecute() support methods
     // ---------------------------
 
-    // wait for the process termination, return whatever wxExecute() must
-    // return
+    // Wait for the process termination and return its exit code or -1 on error.
     //
-    // base class implementation handles all cases except wxEXEC_SYNC without
-    // wxEXEC_NOEVENTS one which is implemented at the GUI level
+    // Notice that this is only used when execData.flags contains wxEXEC_SYNC
+    // and does not contain wxEXEC_NOEVENTS, i.e. when we need to really wait
+    // until the child process exit and dispatch the events while doing it.
     virtual int WaitForChild(wxExecuteData& execData);
 
-    // integrate the monitoring of the given fd with the port-specific event
-    // loop: when this fd, which corresponds to a dummy pipe opened between the
-    // parent and child processes, is closed by the child, the parent is
-    // notified about this via a call to wxHandleProcessTermination() function
-    //
-    // the default implementation uses wxFDIODispatcher and so is suitable for
-    // the console applications or ports which don't have any specific event
-    // loop
-    virtual int AddProcessCallback(wxEndProcessData *data, int fd);
-
 #if wxUSE_SOCKETS
     // return a pointer to the object which should be used to integrate
     // monitoring of the file descriptors to the event loop (currently this is
@@ -62,10 +54,12 @@ public:
     virtual wxEventLoopSourcesManagerBase* GetEventLoopSourcesManager();
 
 protected:
-    // a helper for the implementation of WaitForChild() in wxGUIAppTraits:
-    // checks the streams used for redirected IO in execData and returns true
-    // if there is any activity in them
-    bool CheckForRedirectedIO(wxExecuteData& execData);
+    // Wait for the process termination by running the given event loop until
+    // this happens.
+    //
+    // This is used by the public WaitForChild() after creating the event loop
+    // of the appropriate kind.
+    int RunLoopUntilChildExit(wxExecuteData& execData, wxEventLoopBase& loop);
 };
 
 #endif // _WX_UNIX_APPTBASE_H_
index 5f56733d2896e8a61590a56d0086f6a36bae1bd5..f5e5aebbf0385f1d566e6f4bdc08c8a2111a1ed3 100644 (file)
@@ -51,9 +51,6 @@ class WXDLLIMPEXP_CORE wxGUIAppTraits : public wxGUIAppTraitsBase
 public:
     virtual wxEventLoopBase *CreateEventLoop();
     virtual int WaitForChild(wxExecuteData& execData);
-#ifdef wxHAS_GUI_PROCESS_CALLBACKS
-    virtual int AddProcessCallback(wxEndProcessData *data, int fd);
-#endif
 #if wxUSE_TIMER
     virtual wxTimerImpl *CreateTimerImpl(wxTimer *timer);
 #endif
index 418fa246d502fecc3b48212f759cf9ee4aae0f0a..37dea4f7ef273fcf1f7aabd0ac64460b57b21b24 100644 (file)
@@ -4,63 +4,53 @@
 // Author:      Vadim Zeitlin
 // Id:          $Id$
 // Copyright:   (c) 1998 Robert Roebling, Julian Smart, Vadim Zeitlin
+//              (c) 2013 Vadim Zeitlin
 // Licence:     wxWindows licence
 /////////////////////////////////////////////////////////////////////////////
 
 #ifndef _WX_UNIX_EXECUTE_H
 #define _WX_UNIX_EXECUTE_H
 
-#include "wx/unix/pipe.h"
-
-class WXDLLIMPEXP_FWD_BASE wxProcess;
-class wxStreamTempInputBuffer;
+#include "wx/app.h"
+#include "wx/hashmap.h"
+#include "wx/process.h"
 
-struct wxEndProcessData
-{
-    wxEndProcessData()
-    {
-        pid =
-        tag =
-        exitcode = -1;
-        process = NULL;
-        async = false;
-    }
+#include "wx/unix/pipe.h"
+#include "wx/private/streamtempinput.h"
 
-    int pid;                // pid of the process
-    int tag;                // port dependent value
-    wxProcess *process;     // if !NULL: notified on process termination
-    int exitcode;           // the exit code
-    bool async;             // if true, delete us on process termination
-};
+class wxEventLoopBase;
 
-// struct in which information is passed from wxExecute() to wxAppTraits
-// methods
-struct wxExecuteData
+// Information associated with a running child process.
+class wxExecuteData
 {
+public:
     wxExecuteData()
     {
         flags =
         pid = 0;
+        exitcode = -1;
 
         process = NULL;
 
-#if wxUSE_STREAMS
-        bufOut =
-        bufErr = NULL;
+        syncEventLoop = NULL;
 
+#if wxUSE_STREAMS
         fdOut =
         fdErr = wxPipe::INVALID_FD;
 #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;
-    }
+    // This must be called in the parent process as soon as fork() returns to
+    // update us with the effective child PID. It also ensures that we handle
+    // SIGCHLD to be able to detect when this PID exits, so wxTheApp must be
+    // available.
+    void OnStart(int pid);
+
+    // Called when the child process exits.
+    void OnExit(int exitcode);
+
+    // Return true if we should (or already did) redirect the child IO.
+    bool IsRedirected() const { return process && process->IsRedirected(); }
 
 
     // wxExecute() flags
@@ -69,26 +59,43 @@ struct wxExecuteData
     // the pid of the child process
     int pid;
 
+    // The exit code of the process, set once the child terminates.
+    int exitcode;
+
     // the associated process object or NULL
     wxProcess *process;
 
-    // pipe used for end process detection
-    wxPipe pipeEndProcDetect;
+    // Local event loop used to wait for the child process termination in
+    // synchronous execution case. We can't create it ourselves as its exact
+    // type depends on the application kind (console/GUI), so we rely on
+    // wxAppTraits setting up this pointer to point to the appropriate object.
+    wxEventLoopBase *syncEventLoop;
 
 #if wxUSE_STREAMS
     // the input buffer bufOut is connected to stdout, this is why it is
     // called bufOut and not bufIn
-    wxStreamTempInputBuffer *bufOut,
-                            *bufErr;
+    wxStreamTempInputBuffer bufOut,
+                            bufErr;
 
     // the corresponding FDs, -1 if not redirected
     int fdOut,
         fdErr;
 #endif // wxUSE_STREAMS
-};
 
-// this function is called when the process terminates from port specific
-// callback function and is common to all ports (src/unix/utilsunx.cpp)
-extern WXDLLIMPEXP_BASE void wxHandleProcessTermination(wxEndProcessData *proc_data);
+
+private:
+    // SIGCHLD signal handler that checks whether any of the currently running
+    // children have exited.
+    static void OnSomeChildExited(int sig);
+
+    // All currently running child processes indexed by their PID.
+    //
+    // Notice that the container doesn't own its elements.
+    WX_DECLARE_HASH_MAP(int, wxExecuteData*, wxIntegerHash, wxIntegerEqual,
+                        ChildProcessesData);
+    static ChildProcessesData ms_childProcesses;
+
+    wxDECLARE_NO_COPY_CLASS(wxExecuteData);
+};
 
 #endif // _WX_UNIX_EXECUTE_H
diff --git a/include/wx/unix/private/executeiohandler.h b/include/wx/unix/private/executeiohandler.h
new file mode 100644 (file)
index 0000000..ffee30b
--- /dev/null
@@ -0,0 +1,137 @@
+///////////////////////////////////////////////////////////////////////////////
+// Name:        wx/unix/private/executeiohandler.h
+// Purpose:     IO handler class for the FD used by wxExecute() under Unix
+// Author:      Rob Bresalier, Vadim Zeitlin
+// Created:     2013-01-06
+// RCS-ID:      $Id$
+// Copyright:   (c) 2013 Rob Bresalier, Vadim Zeitlin
+// Licence:     wxWindows licence
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef _WX_UNIX_PRIVATE_EXECUTEIOHANDLER_H_
+#define _WX_UNIX_PRIVATE_EXECUTEIOHANDLER_H_
+
+#include "wx/private/streamtempinput.h"
+
+// This class handles IO events on the pipe FD connected to the child process
+// stdout/stderr and is used by wxExecute().
+//
+// Currently it can derive from either wxEventLoopSourceHandler or
+// wxFDIOHandler depending on the kind of dispatcher/event loop it is used
+// with. In the future, when we get rid of wxFDIOHandler entirely, it will
+// derive from wxEventLoopSourceHandler only.
+template <class T>
+class wxExecuteIOHandlerBase : public T
+{
+public:
+    wxExecuteIOHandlerBase(int fd, wxStreamTempInputBuffer& buf)
+        : m_fd(fd),
+          m_buf(buf)
+    {
+        m_callbackDisabled = false;
+    }
+
+    // Called when the associated descriptor is available for reading.
+    virtual void OnReadWaiting()
+    {
+        // Sync process, process all data coming at us from the pipe so that
+        // the pipe does not get full and cause a deadlock situation.
+        m_buf.Update();
+
+        if ( m_buf.Eof() )
+            DisableCallback();
+    }
+
+    // These methods are never called as we only monitor the associated FD for
+    // reading, but we still must implement them as they're pure virtual in the
+    // base class.
+    virtual void OnWriteWaiting() { }
+    virtual void OnExceptionWaiting() { }
+
+    // Disable any future calls to our OnReadWaiting(), can be called when
+    // we're sure that no more input is forthcoming.
+    void DisableCallback()
+    {
+        if ( !m_callbackDisabled )
+        {
+            m_callbackDisabled = true;
+
+            DoDisable();
+        }
+    }
+
+protected:
+    const int m_fd;
+
+private:
+    virtual void DoDisable() = 0;
+
+    wxStreamTempInputBuffer& m_buf;
+
+    // If true, DisableCallback() had been already called.
+    bool m_callbackDisabled;
+
+    wxDECLARE_NO_COPY_CLASS(wxExecuteIOHandlerBase);
+};
+
+// This is the version used with wxFDIODispatcher, which must be passed to the
+// ctor in order to register this handler with it.
+class wxExecuteFDIOHandler : public wxExecuteIOHandlerBase<wxFDIOHandler>
+{
+public:
+    wxExecuteFDIOHandler(wxFDIODispatcher& dispatcher,
+                         int fd,
+                         wxStreamTempInputBuffer& buf)
+        : wxExecuteIOHandlerBase<wxFDIOHandler>(fd, buf),
+          m_dispatcher(dispatcher)
+    {
+        dispatcher.RegisterFD(fd, this, wxFDIO_INPUT);
+    }
+
+    virtual ~wxExecuteFDIOHandler()
+    {
+        DisableCallback();
+    }
+
+private:
+    virtual void DoDisable()
+    {
+        m_dispatcher.UnregisterFD(m_fd);
+    }
+
+    wxFDIODispatcher& m_dispatcher;
+
+    wxDECLARE_NO_COPY_CLASS(wxExecuteFDIOHandler);
+};
+
+// And this is the version used with an event loop. As AddSourceForFD() is
+// static, we don't require passing the event loop to the ctor but an event
+// loop must be running to handle our events.
+class wxExecuteEventLoopSourceHandler
+    : public wxExecuteIOHandlerBase<wxEventLoopSourceHandler>
+{
+public:
+    wxExecuteEventLoopSourceHandler(int fd, wxStreamTempInputBuffer& buf)
+        : wxExecuteIOHandlerBase<wxEventLoopSourceHandler>(fd, buf)
+    {
+        m_source = wxEventLoop::AddSourceForFD(fd, this, wxEVENT_SOURCE_INPUT);
+    }
+
+    virtual ~wxExecuteEventLoopSourceHandler()
+    {
+        DisableCallback();
+    }
+
+private:
+    virtual void DoDisable()
+    {
+        delete m_source;
+        m_source = NULL;
+    }
+
+    wxEventLoopSource* m_source;
+
+    wxDECLARE_NO_COPY_CLASS(wxExecuteEventLoopSourceHandler);
+};
+
+#endif // _WX_UNIX_PRIVATE_EXECUTEIOHANDLER_H_
index 4ec9e52acef314bc82e2bb85cee30decc0c44290..562681a89d496e36e9b15934aa70937002b3d367 100644 (file)
@@ -2,10 +2,10 @@
 // Name:        src/cocoa/evtloop.mm
 // Purpose:     implements wxEventLoop for Cocoa
 // Author:      David Elliott
-// Modified by:
 // Created:     2003/10/02
 // RCS-ID:      $Id$
 // Copyright:   (c) 2003 David Elliott <dfe@cox.net>
+//              (c) 2013 Rob Bresalier
 // Licence:     wxWindows licence
 ///////////////////////////////////////////////////////////////////////////////
 
index 7df71bf3213d29827a551352e37fd5fd2efb5170..a72a3daddf97b8cc7178b25485fae725300c5050 100644 (file)
@@ -17,8 +17,6 @@
     #include "wx/utils.h"
 #endif
 
-#include "wx/unix/execute.h"
-
 #if 0
 
 #ifndef   WX_PRECOMP
index 2399911f685549f84a18487d85c3ea87f4fefbb4..bcc3472e8fe2c2aba41a6692bdbd2603ba5b4d57 100644 (file)
@@ -2,10 +2,10 @@
 // Name:        src/common/evtloopcmn.cpp
 // Purpose:     common wxEventLoop-related stuff
 // Author:      Vadim Zeitlin
-// Modified by:
 // Created:     2006-01-12
 // RCS-ID:      $Id$
-// Copyright:   (c) 2006 Vadim Zeitlin <vadim@wxwindows.org>
+// Copyright:   (c) 2006, 2013 Vadim Zeitlin <vadim@wxwindows.org>
+//              (c) 2013 Rob Bresalier
 // Licence:     wxWindows licence
 ///////////////////////////////////////////////////////////////////////////////
 
index da9e2baa9d582ed0e07a67f6630fc636373d8d98..3e02d9a6ba841bfc91716bb8c8c7e29931fb982f 100644 (file)
@@ -18,7 +18,6 @@
 #include "wx/utils.h"
 #include "wx/evtloop.h"
 #include "wx/apptrait.h"
-#include "wx/unix/execute.h"
 #include "wx/unix/private/timer.h"
 
 #ifndef WX_PRECOMP
index 3bfdfdc7999137f8688fec9ea9b1e7a46b8f730e..2b7475b0dce06e81ccc87ea587b36a33bcdf94a0 100644 (file)
@@ -2,10 +2,10 @@
 // Name:        src/gtk/evtloop.cpp
 // Purpose:     implements wxEventLoop for GTK+
 // Author:      Vadim Zeitlin
-// Modified by:
 // Created:     10.07.01
 // RCS-ID:      $Id$
 // Copyright:   (c) 2001 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
+//              (c) 2013 Rob Bresalier, Vadim Zeitlin
 // Licence:     wxWindows licence
 ///////////////////////////////////////////////////////////////////////////////
 
index fcd473847964047ee179e506f48219138faa4eb4..55c40158a5d214182b83f5c88bee27a720d31a80 100644 (file)
@@ -21,9 +21,6 @@
 #include "wx/apptrait.h"
 #include "wx/process.h"
 #include "wx/sysopt.h"
-#ifdef __UNIX__
-#include "wx/unix/execute.h"
-#endif
 
 #include "wx/gtk/private/timer.h"
 #include "wx/evtloop.h"
@@ -186,39 +183,6 @@ const gchar *wx_pango_version_check (int major, int minor, int micro)
 #endif
 }
 
-// ----------------------------------------------------------------------------
-// subprocess routines
-// ----------------------------------------------------------------------------
-
-#ifdef __UNIX__
-
-extern "C" {
-static gboolean EndProcessDetector(GIOChannel* source, GIOCondition, void* data)
-{
-    wxEndProcessData * const
-        proc_data = static_cast<wxEndProcessData *>(data);
-
-    // child exited, end waiting
-    close(g_io_channel_unix_get_fd(source));
-
-    wxHandleProcessTermination(proc_data);
-
-    // don't call us again!
-    return false;
-}
-}
-
-int wxGUIAppTraits::AddProcessCallback(wxEndProcessData *proc_data, int fd)
-{
-    GIOChannel* channel = g_io_channel_unix_new(fd);
-    GIOCondition cond = GIOCondition(G_IO_IN | G_IO_HUP | G_IO_ERR);
-    unsigned id = g_io_add_watch(channel, cond, EndProcessDetector, proc_data);
-    g_io_channel_unref(channel);
-    return int(id);
-}
-
-#endif // __UNIX__
-
 // ----------------------------------------------------------------------------
 // wxPlatformInfo-related
 // ----------------------------------------------------------------------------
index ca0fb9f05f442c1d98f321fd6324500be62acbbc..9a18cfae57f9db3cedc08b01cc83421d0c96f17e 100644 (file)
@@ -23,8 +23,6 @@
 #include "wx/evtloop.h"
 #include "wx/process.h"
 
-#include "wx/unix/execute.h"
-
 #include <stdarg.h>
 #include <string.h>
 #include <sys/stat.h>
@@ -126,34 +124,6 @@ wxWindow* wxFindWindowAtPoint(const wxPoint& pt)
 // subprocess routines
 // ----------------------------------------------------------------------------
 
-extern "C" {
-static
-void GTK_EndProcessDetector(gpointer data, gint source,
-                            GdkInputCondition WXUNUSED(condition) )
-{
-    wxEndProcessData * const
-        proc_data = static_cast<wxEndProcessData *>(data);
-
-    // child exited, end waiting
-    close(source);
-
-    // don't call us again!
-    gdk_input_remove(proc_data->tag);
-
-    wxHandleProcessTermination(proc_data);
-}
-}
-
-int wxGUIAppTraits::AddProcessCallback(wxEndProcessData *proc_data, int fd)
-{
-    int tag = gdk_input_add(fd,
-                            GDK_INPUT_READ,
-                            GTK_EndProcessDetector,
-                            (gpointer)proc_data);
-
-    return tag;
-}
-
 #if wxUSE_TIMER
 
 wxTimerImpl* wxGUIAppTraits::CreateTimerImpl(wxTimer *timer)
index ec1095d283926617aafafd72b5000c8ae1276649..fc826976f85199a275eb08ded624e1f4640b3fa9 100644 (file)
@@ -42,8 +42,6 @@
 #pragma message disable nosimpint
 #endif
 
-#include "wx/unix/execute.h"
-
 #include <Xm/Xm.h>
 #include <Xm/Frame.h>
 
@@ -79,34 +77,6 @@ void wxFlushEvents(WXDisplay* wxdisplay)
     }
 }
 
-// ----------------------------------------------------------------------------
-// wxExecute stuff
-// ----------------------------------------------------------------------------
-
-static void xt_notify_end_process(XtPointer data, int *WXUNUSED(fid),
-                                  XtInputId *id)
-{
-    wxEndProcessData *proc_data = (wxEndProcessData *)data;
-
-    wxHandleProcessTermination(proc_data);
-
-    // VZ: I think they should be the same...
-    wxASSERT( (int)*id == proc_data->tag );
-
-    XtRemoveInput(*id);
-}
-
-int wxGUIAppTraits::AddProcessCallback(wxEndProcessData *proc_data, int fd)
-{
-    XtInputId id = XtAppAddInput((XtAppContext) wxTheApp->GetAppContext(),
-                                 fd,
-                                 (XtPointer *) XtInputReadMask,
-                                 (XtInputCallbackProc) xt_notify_end_process,
-                                 (XtPointer) proc_data);
-
-    return (int)id;
-}
-
 // ----------------------------------------------------------------------------
 // misc
 // ----------------------------------------------------------------------------
index 66af86b3e842b6b9d29694c48d02aef8063472b0..40339d1593d6b9f0144c1a648f0f07ea067c2bcf 100644 (file)
@@ -636,12 +636,3 @@ wxString wxPMErrorToStr(ERRORID vError)
     }
     return sError;
 } // end of wxPMErrorToStr
-
-// replacement for implementation in unix/utilsunx.cpp,
-// to be used by all X11 based ports.
-struct wxEndProcessData;
-
-void wxHandleProcessTermination(wxEndProcessData *WXUNUSED(proc_data))
-{
-    // For now, just do nothing. To be filled in as needed.
-}
index 3ae62e9ba1eaa25910fe9c6616371d5b4f51aadc..ef70c2368edd43f508d986c17f67d122c768dc20 100644 (file)
@@ -5,6 +5,7 @@
 // Created:     2009-10-18
 // RCS-ID:      $Id$
 // Copyright:   (c) 2009 Vadim Zeitlin <vadim@wxwidgets.org>
+//              (c) 2013 Rob Bresalier
 // Licence:     wxWindows licence
 ///////////////////////////////////////////////////////////////////////////////
 
index e5881f4b6d9b77eb430b396486c6edad22aa3779..f00cb5e38fff670c8819ce364a0e599803fbebbe 100644 (file)
@@ -6,6 +6,7 @@
 // Created:     2004-11-04
 // RCS-ID:      $Id$
 // Copyright:   (c) David Elliott, Ryan Norton
+//              (c) 2013 Rob Bresalier
 // Licence:     wxWindows licence
 // Notes:       This code comes from src/osx/carbon/utilsexc.cpp,1.11
 /////////////////////////////////////////////////////////////////////////////
@@ -15,7 +16,6 @@
     #include "wx/log.h"
     #include "wx/utils.h"
 #endif //ndef WX_PRECOMP
-#include "wx/unix/execute.h"
 #include "wx/stdpaths.h"
 #include "wx/app.h"
 #include "wx/apptrait.h"
 
 #include <CoreFoundation/CFSocket.h>
 
-/*!
-    Called due to source signal detected by the CFRunLoop.
-    This is nearly identical to the wxGTK equivalent.
- */
-extern "C" void WXCF_EndProcessDetector(CFSocketRef s,
-                                        CFSocketCallBackType WXUNUSED(callbackType),
-                                        CFDataRef WXUNUSED(address),
-                                        void const *WXUNUSED(data),
-                                        void *info)
-{
-    /*
-        Either our pipe was closed or the process ended successfully.  Either way,
-        we're done.  It's not if waitpid is going to magically succeed when
-        we get fired again.  CFSocketInvalidate closes the fd for us and also
-        invalidates the run loop source for us which should cause it to
-        release the CFSocket (thus causing it to be deallocated) and remove
-        itself from the runloop which should release it and cause it to also
-        be deallocated.  Of course, it's possible the RunLoop hangs onto
-        one or both of them by retaining/releasing them within its stack
-        frame.  However, that shouldn't be depended on.  Assume that s is
-        deallocated due to the following call.
-     */
-    CFSocketInvalidate(s);
-
-    // Now tell wx that the process has ended.
-    wxHandleProcessTermination(static_cast<wxEndProcessData *>(info));
-}
-
-/*!
-    Implements the GUI-specific AddProcessCallback() for both wxMac and
-    wxCocoa using the CFSocket/CFRunLoop API which is available to both.
-    Takes advantage of the fact that sockets on UNIX are just regular
-    file descriptors and thus even a non-socket file descriptor can
-    apparently be used with CFSocket so long as you only tell CFSocket
-    to do things with it that would be valid for a non-socket fd.
- */
-int wxGUIAppTraits::AddProcessCallback(wxEndProcessData *proc_data, int fd)
-{
-    static int s_last_tag = 0;
-    CFSocketContext context =
-    {   0
-    ,   static_cast<void*>(proc_data)
-    ,   NULL
-    ,   NULL
-    ,   NULL
-    };
-    CFSocketRef cfSocket = CFSocketCreateWithNative(kCFAllocatorDefault,fd,kCFSocketReadCallBack,&WXCF_EndProcessDetector,&context);
-    if(cfSocket == NULL)
-    {
-        wxLogError(wxT("Failed to create socket for end process detection"));
-        return 0;
-    }
-    CFRunLoopSourceRef runLoopSource = CFSocketCreateRunLoopSource(kCFAllocatorDefault, cfSocket, /*highest priority:*/0);
-    if(runLoopSource == NULL)
-    {
-        wxLogError(wxT("Failed to create CFRunLoopSource from CFSocket for end process detection"));
-        // closes the fd.. we can't really stop it, nor do we necessarily want to.
-        CFSocketInvalidate(cfSocket);
-        CFRelease(cfSocket);
-        return 0;
-    }
-    // Now that the run loop source has the socket retained and we no longer
-    // need to refer to it within this method, we can release it.
-    CFRelease(cfSocket);
-
-    CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopCommonModes);
-    // Now that the run loop has the source retained we can release it.
-    CFRelease(runLoopSource);
-
-    /*
-        Feed wx some bullshit.. we don't use it since CFSocket helpfully passes
-        itself into our callback and that's enough to be able to
-        CFSocketInvalidate it which is all we need to do to get everything we
-        just created to be deallocated.
-     */
-    return ++s_last_tag;
-}
-
 #if wxUSE_EVENTLOOP_SOURCE
 
 namespace
@@ -122,7 +44,7 @@ wx_socket_callback(CFSocketRef WXUNUSED(s),
                    void *ctxData)
 {
     wxLogTrace(wxTRACE_EVT_SOURCE,
-               "CFSocket callback, type=%d", callbackType);
+               "CFSocket callback, type=%d", static_cast<int>(callbackType));
 
     wxCFEventLoopSource * const
         source = static_cast<wxCFEventLoopSource *>(ctxData);
index a2698eef66a928c5e54d6f45a2de30cafb2f5e0e..91e821e327c3523b4abd6faa6e5c18edd9f9a11e 100644 (file)
@@ -30,6 +30,7 @@
 #endif // WX_PRECOMP
 
 #include "wx/unix/execute.h"
+#include "wx/evtloop.h"
 
 // ============================================================================
 // implementation
 
 int wxGUIAppTraits::WaitForChild(wxExecuteData& execData)
 {
-    const int flags = execData.flags;
-    if ( !(flags & wxEXEC_SYNC) || (flags & wxEXEC_NOEVENTS) )
-    {
-        // async or blocking sync cases are already handled by the base class
-        // just fine, no need to duplicate its code here
-        return wxAppTraits::WaitForChild(execData);
-    }
-
-    // here we're dealing with the case of synchronous execution when we want
-    // to process the GUI events while waiting for the child termination
-
-    wxEndProcessData endProcData;
-    endProcData.pid = execData.pid;
-    endProcData.tag = AddProcessCallback
-                      (
-                         &endProcData,
-                         execData.GetEndProcReadFD()
-                      );
-    endProcData.async = false;
-
-
     // prepare to wait for the child termination: show to the user that we're
     // busy and refuse all input unless explicitly told otherwise
     wxBusyCursor bc;
-    wxWindowDisabler wd(!(flags & wxEXEC_NODISABLE));
+    wxWindowDisabler wd(!(execData.flags & wxEXEC_NODISABLE));
 
-    // endProcData.pid will be set to 0 from wxHandleProcessTermination() when
-    // the process terminates
-    while ( endProcData.pid != 0 )
-    {
-        // don't consume 100% of the CPU while we're sitting in this
-        // loop
-        if ( !CheckForRedirectedIO(execData) )
-            wxMilliSleep(1);
-
-        // give the toolkit a chance to call wxHandleProcessTermination() here
-        // and also repaint the GUI and handle other accumulated events
-        wxYield();
-    }
-
-    return endProcData.exitcode;
+    // Allocate an event loop that will be used to wait for the process
+    // to terminate, will handle stdout, stderr, and any other events and pass
+    // it to the common (to console and GUI) code which will run it.
+    wxGUIEventLoop loop;
+    return RunLoopUntilChildExit(execData, loop);
 }
 
-
index 70279d9368451f68a87367343f3148e9d5863c05..4b7d8fded14b85c17a2d3a04ee6958a8aa624032 100644 (file)
@@ -5,6 +5,8 @@
 // Created:     2007-05-07
 // RCS-ID:      $Id$
 // Copyright:   (c) 2006 Zork Lukasz Michalski
+//              (c) 2009, 2013 Vadim Zeitlin <vadim@wxwidgets.org>
+//              (c) 2013 Rob Bresalier
 // Licence:     wxWindows licence
 /////////////////////////////////////////////////////////////////////////////
 
index 381ce7636727ddf85c261368ba44e65ba939f540..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()
@@ -65,6 +67,7 @@
 
 #include "wx/private/pipestream.h"
 #include "wx/private/streamtempinput.h"
+#include "wx/unix/private/executeiohandler.h"
 
 #endif // HAS_PIPE_STREAMS
 
@@ -484,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)
@@ -518,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
@@ -624,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
@@ -642,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);
             }
@@ -706,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() )
         {
@@ -748,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
 
@@ -766,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 execData.pid;
+        }
+
 
-        return traits->WaitForChild(execData);
+        // 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)
@@ -1336,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
-{
-public:
-    wxReadFDIOHandler(wxFDIODispatcher& disp, int fd) : m_fd(fd)
-    {
-        if ( fd )
-            disp.RegisterFD(fd, this, wxFDIO_INPUT);
-    }
-
-    virtual void OnWriteWaiting() { wxFAIL_MSG("unreachable"); }
-    virtual void OnExceptionWaiting() { wxFAIL_MSG("unreachable"); }
-
-protected:
-    const int m_fd;
-
-    wxDECLARE_NO_COPY_CLASS(wxReadFDIOHandler);
-};
-
-// 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
+// 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:
-    wxEndHandler(wxFDIODispatcher& disp, int fd)
-        : wxReadFDIOHandler(disp, 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() )
     {
-        m_terminated = false;
+        stdoutHandler.reset(new wxExecuteEventLoopSourceHandler
+                                (
+                                    execData.fdOut, execData.bufOut
+                                ));
+        stderrHandler.reset(new wxExecuteEventLoopSourceHandler
+                                (
+                                    execData.fdErr, execData.bufErr
+                                ));
     }
+#endif // wxUSE_STREAMS
 
-    bool Terminated() const { return m_terminated; }
+    // 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;
 
-    virtual void OnReadWaiting() { m_terminated = true; }
-
-private:
-    bool m_terminated;
+    // And run it.
+    loop.Run();
 
-    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" );
 
@@ -1479,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 )
-    {
-        // 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 )
+    switch ( rc )
     {
-        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();
     }
 }
-
index 688756efef46181d9ad55ab5302bb201c51c1ac5..47c694584e0612b4b03e7d6f09458369f11e8881 100644 (file)
@@ -58,8 +58,6 @@
 #pragma message disable nosimpint
 #endif
 
-#include "wx/unix/execute.h"
-
 #include "wx/x11/private.h"
 
 #include "X11/Xutil.h"
index 4355808d3f138118f0443aefbf3cb6a5411c954d..d9e61fdfff7a1dc8148a7cfc302a4c2fb9763347 100644 (file)
@@ -6,6 +6,8 @@
 // Created:     2009-01-10
 // RCS-ID:      $Id$
 // Copyright:   (c) 2009 Francesco Montorsi
+//              (c) 2013 Rob Bresalier, Vadim Zeitlin
+// Licence:     wxWindows licence
 ///////////////////////////////////////////////////////////////////////////////
 
 // ----------------------------------------------------------------------------