From 821d856a610b21f2946e3283db7f79443227776e Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Wed, 3 Jul 2013 00:32:16 +0000 Subject: [PATCH] Rewrite wxExecute() implementation under Unix. 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 --- docs/changes.txt | 1 + include/wx/unix/app.h | 2 +- include/wx/unix/apptbase.h | 34 +- include/wx/unix/apptrait.h | 3 - include/wx/unix/execute.h | 91 ++--- include/wx/unix/private/executeiohandler.h | 137 ++++++++ src/cocoa/evtloop.mm | 2 +- src/cocoa/utilsexc.mm | 2 - src/common/evtloopcmn.cpp | 4 +- src/dfb/utils.cpp | 1 - src/gtk/evtloop.cpp | 2 +- src/gtk/utilsgtk.cpp | 36 -- src/gtk1/utilsgtk.cpp | 30 -- src/motif/utils.cpp | 30 -- src/os2/utils.cpp | 9 - src/osx/core/evtloop_cf.cpp | 1 + src/osx/core/utilsexc_cf.cpp | 82 +---- src/unix/apptraits.cpp | 45 +-- src/unix/evtloopunix.cpp | 2 + src/unix/utilsunx.cpp | 525 +++++++++++++++-------------- src/x11/utils.cpp | 2 - tests/exec/exec.cpp | 2 + 22 files changed, 493 insertions(+), 550 deletions(-) create mode 100644 include/wx/unix/private/executeiohandler.h diff --git a/docs/changes.txt b/docs/changes.txt index 2cfac6c..ca2dc13 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -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). diff --git a/include/wx/unix/app.h b/include/wx/unix/app.h index 812783b..222d266 100644 --- a/include/wx/unix/app.h +++ b/include/wx/unix/app.h @@ -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. diff --git a/include/wx/unix/apptbase.h b/include/wx/unix/apptbase.h index fd624ae..1d50851 100644 --- a/include/wx/unix/apptbase.h +++ b/include/wx/unix/apptbase.h @@ -12,8 +12,10 @@ #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_ diff --git a/include/wx/unix/apptrait.h b/include/wx/unix/apptrait.h index 5f56733..f5e5aeb 100644 --- a/include/wx/unix/apptrait.h +++ b/include/wx/unix/apptrait.h @@ -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 diff --git a/include/wx/unix/execute.h b/include/wx/unix/execute.h index 418fa24..37dea4f 100644 --- a/include/wx/unix/execute.h +++ b/include/wx/unix/execute.h @@ -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 index 0000000..ffee30b --- /dev/null +++ b/include/wx/unix/private/executeiohandler.h @@ -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 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 +{ +public: + wxExecuteFDIOHandler(wxFDIODispatcher& dispatcher, + int fd, + wxStreamTempInputBuffer& buf) + : wxExecuteIOHandlerBase(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 +{ +public: + wxExecuteEventLoopSourceHandler(int fd, wxStreamTempInputBuffer& buf) + : wxExecuteIOHandlerBase(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_ diff --git a/src/cocoa/evtloop.mm b/src/cocoa/evtloop.mm index 4ec9e52..562681a 100644 --- a/src/cocoa/evtloop.mm +++ b/src/cocoa/evtloop.mm @@ -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 +// (c) 2013 Rob Bresalier // Licence: wxWindows licence /////////////////////////////////////////////////////////////////////////////// diff --git a/src/cocoa/utilsexc.mm b/src/cocoa/utilsexc.mm index 7df71bf..a72a3da 100644 --- a/src/cocoa/utilsexc.mm +++ b/src/cocoa/utilsexc.mm @@ -17,8 +17,6 @@ #include "wx/utils.h" #endif -#include "wx/unix/execute.h" - #if 0 #ifndef WX_PRECOMP diff --git a/src/common/evtloopcmn.cpp b/src/common/evtloopcmn.cpp index 2399911..bcc3472 100644 --- a/src/common/evtloopcmn.cpp +++ b/src/common/evtloopcmn.cpp @@ -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 +// Copyright: (c) 2006, 2013 Vadim Zeitlin +// (c) 2013 Rob Bresalier // Licence: wxWindows licence /////////////////////////////////////////////////////////////////////////////// diff --git a/src/dfb/utils.cpp b/src/dfb/utils.cpp index da9e2ba..3e02d9a 100644 --- a/src/dfb/utils.cpp +++ b/src/dfb/utils.cpp @@ -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 diff --git a/src/gtk/evtloop.cpp b/src/gtk/evtloop.cpp index 3bfdfdc..2b7475b 100644 --- a/src/gtk/evtloop.cpp +++ b/src/gtk/evtloop.cpp @@ -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 +// (c) 2013 Rob Bresalier, Vadim Zeitlin // Licence: wxWindows licence /////////////////////////////////////////////////////////////////////////////// diff --git a/src/gtk/utilsgtk.cpp b/src/gtk/utilsgtk.cpp index fcd4738..55c4015 100644 --- a/src/gtk/utilsgtk.cpp +++ b/src/gtk/utilsgtk.cpp @@ -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" @@ -187,39 +184,6 @@ const gchar *wx_pango_version_check (int major, int minor, int micro) } // ---------------------------------------------------------------------------- -// subprocess routines -// ---------------------------------------------------------------------------- - -#ifdef __UNIX__ - -extern "C" { -static gboolean EndProcessDetector(GIOChannel* source, GIOCondition, void* data) -{ - wxEndProcessData * const - proc_data = static_cast(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 // ---------------------------------------------------------------------------- diff --git a/src/gtk1/utilsgtk.cpp b/src/gtk1/utilsgtk.cpp index ca0fb9f..9a18cfa 100644 --- a/src/gtk1/utilsgtk.cpp +++ b/src/gtk1/utilsgtk.cpp @@ -23,8 +23,6 @@ #include "wx/evtloop.h" #include "wx/process.h" -#include "wx/unix/execute.h" - #include #include #include @@ -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(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) diff --git a/src/motif/utils.cpp b/src/motif/utils.cpp index ec1095d..fc82697 100644 --- a/src/motif/utils.cpp +++ b/src/motif/utils.cpp @@ -42,8 +42,6 @@ #pragma message disable nosimpint #endif -#include "wx/unix/execute.h" - #include #include @@ -80,34 +78,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 // ---------------------------------------------------------------------------- diff --git a/src/os2/utils.cpp b/src/os2/utils.cpp index 66af86b..40339d1 100644 --- a/src/os2/utils.cpp +++ b/src/os2/utils.cpp @@ -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. -} diff --git a/src/osx/core/evtloop_cf.cpp b/src/osx/core/evtloop_cf.cpp index 3ae62e9..ef70c23 100644 --- a/src/osx/core/evtloop_cf.cpp +++ b/src/osx/core/evtloop_cf.cpp @@ -5,6 +5,7 @@ // Created: 2009-10-18 // RCS-ID: $Id$ // Copyright: (c) 2009 Vadim Zeitlin +// (c) 2013 Rob Bresalier // Licence: wxWindows licence /////////////////////////////////////////////////////////////////////////////// diff --git a/src/osx/core/utilsexc_cf.cpp b/src/osx/core/utilsexc_cf.cpp index e5881f4..f00cb5e 100644 --- a/src/osx/core/utilsexc_cf.cpp +++ b/src/osx/core/utilsexc_cf.cpp @@ -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" @@ -30,84 +30,6 @@ #include -/*! - 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(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(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(callbackType)); wxCFEventLoopSource * const source = static_cast(ctxData); diff --git a/src/unix/apptraits.cpp b/src/unix/apptraits.cpp index a2698ee..91e821e 100644 --- a/src/unix/apptraits.cpp +++ b/src/unix/apptraits.cpp @@ -30,6 +30,7 @@ #endif // WX_PRECOMP #include "wx/unix/execute.h" +#include "wx/evtloop.h" // ============================================================================ // implementation @@ -37,47 +38,15 @@ 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); } - diff --git a/src/unix/evtloopunix.cpp b/src/unix/evtloopunix.cpp index 70279d9..4b7d8fd 100644 --- a/src/unix/evtloopunix.cpp +++ b/src/unix/evtloopunix.cpp @@ -5,6 +5,8 @@ // Created: 2007-05-07 // RCS-ID: $Id$ // Copyright: (c) 2006 Zork Lukasz Michalski +// (c) 2009, 2013 Vadim Zeitlin +// (c) 2013 Rob Bresalier // Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// diff --git a/src/unix/utilsunx.cpp b/src/unix/utilsunx.cpp index 381ce76..d757498 100644 --- a/src/unix/utilsunx.cpp +++ b/src/unix/utilsunx.cpp @@ -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 #include // 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 + signalHandler(wxTheApp->RegisterSignalWakeUpPipe(dispatcher)); + +#if wxUSE_STREAMS + // And then the two for the child output and error streams if necessary. + wxScopedPtr + 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 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 + 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(); } } - diff --git a/src/x11/utils.cpp b/src/x11/utils.cpp index 688756e..47c6945 100644 --- a/src/x11/utils.cpp +++ b/src/x11/utils.cpp @@ -58,8 +58,6 @@ #pragma message disable nosimpint #endif -#include "wx/unix/execute.h" - #include "wx/x11/private.h" #include "X11/Xutil.h" diff --git a/tests/exec/exec.cpp b/tests/exec/exec.cpp index 4355808..d9e61fd 100644 --- a/tests/exec/exec.cpp +++ b/tests/exec/exec.cpp @@ -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 /////////////////////////////////////////////////////////////////////////////// // ---------------------------------------------------------------------------- -- 2.7.4