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
This commit is contained in:
@@ -574,6 +574,7 @@ All:
|
|||||||
event type constants to make using Bind() less verbose. E.g.
|
event type constants to make using Bind() less verbose. E.g.
|
||||||
wxEVT_COMMAND_MENU_SELECTED is now wxEVT_MENU (but the old name remains
|
wxEVT_COMMAND_MENU_SELECTED is now wxEVT_MENU (but the old name remains
|
||||||
available for compatibility) (Catalin Raceanu).
|
available for compatibility) (Catalin Raceanu).
|
||||||
|
- Fix wxExecute() implementation under Unix (Rob Bresalier).
|
||||||
- Add wxEvtHandler::CallAfter() method for asynchronous method calls.
|
- Add wxEvtHandler::CallAfter() method for asynchronous method calls.
|
||||||
- Add support for symlinks to wxFileName (David Hart).
|
- Add support for symlinks to wxFileName (David Hart).
|
||||||
- Add wxDIR_NO_FOLLOW flag for wxDir traversal (David Hart).
|
- Add wxDIR_NO_FOLLOW flag for wxDir traversal (David Hart).
|
||||||
|
@@ -47,7 +47,7 @@ public:
|
|||||||
|
|
||||||
// Register the signal wake up pipe with the given dispatcher.
|
// 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
|
// 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.
|
// is returned so that it can be deleted when we no longer needed it.
|
||||||
|
@@ -12,8 +12,10 @@
|
|||||||
#ifndef _WX_UNIX_APPTBASE_H_
|
#ifndef _WX_UNIX_APPTBASE_H_
|
||||||
#define _WX_UNIX_APPTBASE_H_
|
#define _WX_UNIX_APPTBASE_H_
|
||||||
|
|
||||||
struct wxEndProcessData;
|
#include "wx/evtloop.h"
|
||||||
struct wxExecuteData;
|
#include "wx/evtloopsrc.h"
|
||||||
|
|
||||||
|
class wxExecuteData;
|
||||||
class wxFDIOManager;
|
class wxFDIOManager;
|
||||||
class wxEventLoopSourcesManagerBase;
|
class wxEventLoopSourcesManagerBase;
|
||||||
|
|
||||||
@@ -27,23 +29,13 @@ public:
|
|||||||
// wxExecute() support methods
|
// wxExecute() support methods
|
||||||
// ---------------------------
|
// ---------------------------
|
||||||
|
|
||||||
// wait for the process termination, return whatever wxExecute() must
|
// Wait for the process termination and return its exit code or -1 on error.
|
||||||
// return
|
|
||||||
//
|
//
|
||||||
// base class implementation handles all cases except wxEXEC_SYNC without
|
// Notice that this is only used when execData.flags contains wxEXEC_SYNC
|
||||||
// wxEXEC_NOEVENTS one which is implemented at the GUI level
|
// 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);
|
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
|
#if wxUSE_SOCKETS
|
||||||
// return a pointer to the object which should be used to integrate
|
// return a pointer to the object which should be used to integrate
|
||||||
// monitoring of the file descriptors to the event loop (currently this is
|
// monitoring of the file descriptors to the event loop (currently this is
|
||||||
@@ -62,10 +54,12 @@ public:
|
|||||||
virtual wxEventLoopSourcesManagerBase* GetEventLoopSourcesManager();
|
virtual wxEventLoopSourcesManagerBase* GetEventLoopSourcesManager();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// a helper for the implementation of WaitForChild() in wxGUIAppTraits:
|
// Wait for the process termination by running the given event loop until
|
||||||
// checks the streams used for redirected IO in execData and returns true
|
// this happens.
|
||||||
// if there is any activity in them
|
//
|
||||||
bool CheckForRedirectedIO(wxExecuteData& execData);
|
// 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_
|
#endif // _WX_UNIX_APPTBASE_H_
|
||||||
|
@@ -51,9 +51,6 @@ class WXDLLIMPEXP_CORE wxGUIAppTraits : public wxGUIAppTraitsBase
|
|||||||
public:
|
public:
|
||||||
virtual wxEventLoopBase *CreateEventLoop();
|
virtual wxEventLoopBase *CreateEventLoop();
|
||||||
virtual int WaitForChild(wxExecuteData& execData);
|
virtual int WaitForChild(wxExecuteData& execData);
|
||||||
#ifdef wxHAS_GUI_PROCESS_CALLBACKS
|
|
||||||
virtual int AddProcessCallback(wxEndProcessData *data, int fd);
|
|
||||||
#endif
|
|
||||||
#if wxUSE_TIMER
|
#if wxUSE_TIMER
|
||||||
virtual wxTimerImpl *CreateTimerImpl(wxTimer *timer);
|
virtual wxTimerImpl *CreateTimerImpl(wxTimer *timer);
|
||||||
#endif
|
#endif
|
||||||
|
@@ -4,63 +4,53 @@
|
|||||||
// Author: Vadim Zeitlin
|
// Author: Vadim Zeitlin
|
||||||
// Id: $Id$
|
// Id: $Id$
|
||||||
// Copyright: (c) 1998 Robert Roebling, Julian Smart, Vadim Zeitlin
|
// Copyright: (c) 1998 Robert Roebling, Julian Smart, Vadim Zeitlin
|
||||||
|
// (c) 2013 Vadim Zeitlin
|
||||||
// Licence: wxWindows licence
|
// Licence: wxWindows licence
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#ifndef _WX_UNIX_EXECUTE_H
|
#ifndef _WX_UNIX_EXECUTE_H
|
||||||
#define _WX_UNIX_EXECUTE_H
|
#define _WX_UNIX_EXECUTE_H
|
||||||
|
|
||||||
|
#include "wx/app.h"
|
||||||
|
#include "wx/hashmap.h"
|
||||||
|
#include "wx/process.h"
|
||||||
|
|
||||||
#include "wx/unix/pipe.h"
|
#include "wx/unix/pipe.h"
|
||||||
|
#include "wx/private/streamtempinput.h"
|
||||||
|
|
||||||
class WXDLLIMPEXP_FWD_BASE wxProcess;
|
class wxEventLoopBase;
|
||||||
class wxStreamTempInputBuffer;
|
|
||||||
|
|
||||||
struct wxEndProcessData
|
// Information associated with a running child process.
|
||||||
{
|
class wxExecuteData
|
||||||
wxEndProcessData()
|
|
||||||
{
|
|
||||||
pid =
|
|
||||||
tag =
|
|
||||||
exitcode = -1;
|
|
||||||
process = NULL;
|
|
||||||
async = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
};
|
|
||||||
|
|
||||||
// struct in which information is passed from wxExecute() to wxAppTraits
|
|
||||||
// methods
|
|
||||||
struct wxExecuteData
|
|
||||||
{
|
{
|
||||||
|
public:
|
||||||
wxExecuteData()
|
wxExecuteData()
|
||||||
{
|
{
|
||||||
flags =
|
flags =
|
||||||
pid = 0;
|
pid = 0;
|
||||||
|
exitcode = -1;
|
||||||
|
|
||||||
process = NULL;
|
process = NULL;
|
||||||
|
|
||||||
#if wxUSE_STREAMS
|
syncEventLoop = NULL;
|
||||||
bufOut =
|
|
||||||
bufErr = NULL;
|
|
||||||
|
|
||||||
|
#if wxUSE_STREAMS
|
||||||
fdOut =
|
fdOut =
|
||||||
fdErr = wxPipe::INVALID_FD;
|
fdErr = wxPipe::INVALID_FD;
|
||||||
#endif // wxUSE_STREAMS
|
#endif // wxUSE_STREAMS
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the FD corresponding to the read end of the process end detection
|
// This must be called in the parent process as soon as fork() returns to
|
||||||
// pipe and close the write one
|
// update us with the effective child PID. It also ensures that we handle
|
||||||
int GetEndProcReadFD()
|
// SIGCHLD to be able to detect when this PID exits, so wxTheApp must be
|
||||||
{
|
// available.
|
||||||
const int fd = pipeEndProcDetect.Detach(wxPipe::Read);
|
void OnStart(int pid);
|
||||||
pipeEndProcDetect.Close();
|
|
||||||
return fd;
|
// 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
|
// wxExecute() flags
|
||||||
@@ -69,26 +59,43 @@ struct wxExecuteData
|
|||||||
// the pid of the child process
|
// the pid of the child process
|
||||||
int pid;
|
int pid;
|
||||||
|
|
||||||
|
// The exit code of the process, set once the child terminates.
|
||||||
|
int exitcode;
|
||||||
|
|
||||||
// the associated process object or NULL
|
// the associated process object or NULL
|
||||||
wxProcess *process;
|
wxProcess *process;
|
||||||
|
|
||||||
// pipe used for end process detection
|
// Local event loop used to wait for the child process termination in
|
||||||
wxPipe pipeEndProcDetect;
|
// 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
|
#if wxUSE_STREAMS
|
||||||
// the input buffer bufOut is connected to stdout, this is why it is
|
// the input buffer bufOut is connected to stdout, this is why it is
|
||||||
// called bufOut and not bufIn
|
// called bufOut and not bufIn
|
||||||
wxStreamTempInputBuffer *bufOut,
|
wxStreamTempInputBuffer bufOut,
|
||||||
*bufErr;
|
bufErr;
|
||||||
|
|
||||||
// the corresponding FDs, -1 if not redirected
|
// the corresponding FDs, -1 if not redirected
|
||||||
int fdOut,
|
int fdOut,
|
||||||
fdErr;
|
fdErr;
|
||||||
#endif // wxUSE_STREAMS
|
#endif // wxUSE_STREAMS
|
||||||
|
|
||||||
|
|
||||||
|
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);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 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);
|
|
||||||
|
|
||||||
#endif // _WX_UNIX_EXECUTE_H
|
#endif // _WX_UNIX_EXECUTE_H
|
||||||
|
137
include/wx/unix/private/executeiohandler.h
Normal file
137
include/wx/unix/private/executeiohandler.h
Normal file
@@ -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_
|
@@ -2,10 +2,10 @@
|
|||||||
// Name: src/cocoa/evtloop.mm
|
// Name: src/cocoa/evtloop.mm
|
||||||
// Purpose: implements wxEventLoop for Cocoa
|
// Purpose: implements wxEventLoop for Cocoa
|
||||||
// Author: David Elliott
|
// Author: David Elliott
|
||||||
// Modified by:
|
|
||||||
// Created: 2003/10/02
|
// Created: 2003/10/02
|
||||||
// RCS-ID: $Id$
|
// RCS-ID: $Id$
|
||||||
// Copyright: (c) 2003 David Elliott <dfe@cox.net>
|
// Copyright: (c) 2003 David Elliott <dfe@cox.net>
|
||||||
|
// (c) 2013 Rob Bresalier
|
||||||
// Licence: wxWindows licence
|
// Licence: wxWindows licence
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@@ -17,8 +17,6 @@
|
|||||||
#include "wx/utils.h"
|
#include "wx/utils.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "wx/unix/execute.h"
|
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
|
|
||||||
#ifndef WX_PRECOMP
|
#ifndef WX_PRECOMP
|
||||||
|
@@ -2,10 +2,10 @@
|
|||||||
// Name: src/common/evtloopcmn.cpp
|
// Name: src/common/evtloopcmn.cpp
|
||||||
// Purpose: common wxEventLoop-related stuff
|
// Purpose: common wxEventLoop-related stuff
|
||||||
// Author: Vadim Zeitlin
|
// Author: Vadim Zeitlin
|
||||||
// Modified by:
|
|
||||||
// Created: 2006-01-12
|
// Created: 2006-01-12
|
||||||
// RCS-ID: $Id$
|
// 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
|
// Licence: wxWindows licence
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@@ -18,7 +18,6 @@
|
|||||||
#include "wx/utils.h"
|
#include "wx/utils.h"
|
||||||
#include "wx/evtloop.h"
|
#include "wx/evtloop.h"
|
||||||
#include "wx/apptrait.h"
|
#include "wx/apptrait.h"
|
||||||
#include "wx/unix/execute.h"
|
|
||||||
#include "wx/unix/private/timer.h"
|
#include "wx/unix/private/timer.h"
|
||||||
|
|
||||||
#ifndef WX_PRECOMP
|
#ifndef WX_PRECOMP
|
||||||
|
@@ -2,10 +2,10 @@
|
|||||||
// Name: src/gtk/evtloop.cpp
|
// Name: src/gtk/evtloop.cpp
|
||||||
// Purpose: implements wxEventLoop for GTK+
|
// Purpose: implements wxEventLoop for GTK+
|
||||||
// Author: Vadim Zeitlin
|
// Author: Vadim Zeitlin
|
||||||
// Modified by:
|
|
||||||
// Created: 10.07.01
|
// Created: 10.07.01
|
||||||
// RCS-ID: $Id$
|
// RCS-ID: $Id$
|
||||||
// Copyright: (c) 2001 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
|
// Copyright: (c) 2001 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
|
||||||
|
// (c) 2013 Rob Bresalier, Vadim Zeitlin
|
||||||
// Licence: wxWindows licence
|
// Licence: wxWindows licence
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@@ -21,9 +21,6 @@
|
|||||||
#include "wx/apptrait.h"
|
#include "wx/apptrait.h"
|
||||||
#include "wx/process.h"
|
#include "wx/process.h"
|
||||||
#include "wx/sysopt.h"
|
#include "wx/sysopt.h"
|
||||||
#ifdef __UNIX__
|
|
||||||
#include "wx/unix/execute.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "wx/gtk/private/timer.h"
|
#include "wx/gtk/private/timer.h"
|
||||||
#include "wx/evtloop.h"
|
#include "wx/evtloop.h"
|
||||||
@@ -186,39 +183,6 @@ const gchar *wx_pango_version_check (int major, int minor, int micro)
|
|||||||
#endif
|
#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
|
// wxPlatformInfo-related
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
@@ -23,8 +23,6 @@
|
|||||||
#include "wx/evtloop.h"
|
#include "wx/evtloop.h"
|
||||||
#include "wx/process.h"
|
#include "wx/process.h"
|
||||||
|
|
||||||
#include "wx/unix/execute.h"
|
|
||||||
|
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
@@ -126,34 +124,6 @@ wxWindow* wxFindWindowAtPoint(const wxPoint& pt)
|
|||||||
// subprocess routines
|
// 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
|
#if wxUSE_TIMER
|
||||||
|
|
||||||
wxTimerImpl* wxGUIAppTraits::CreateTimerImpl(wxTimer *timer)
|
wxTimerImpl* wxGUIAppTraits::CreateTimerImpl(wxTimer *timer)
|
||||||
|
@@ -42,8 +42,6 @@
|
|||||||
#pragma message disable nosimpint
|
#pragma message disable nosimpint
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "wx/unix/execute.h"
|
|
||||||
|
|
||||||
#include <Xm/Xm.h>
|
#include <Xm/Xm.h>
|
||||||
#include <Xm/Frame.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
|
// misc
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
@@ -636,12 +636,3 @@ wxString wxPMErrorToStr(ERRORID vError)
|
|||||||
}
|
}
|
||||||
return sError;
|
return sError;
|
||||||
} // end of wxPMErrorToStr
|
} // 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.
|
|
||||||
}
|
|
||||||
|
@@ -5,6 +5,7 @@
|
|||||||
// Created: 2009-10-18
|
// Created: 2009-10-18
|
||||||
// RCS-ID: $Id$
|
// RCS-ID: $Id$
|
||||||
// Copyright: (c) 2009 Vadim Zeitlin <vadim@wxwidgets.org>
|
// Copyright: (c) 2009 Vadim Zeitlin <vadim@wxwidgets.org>
|
||||||
|
// (c) 2013 Rob Bresalier
|
||||||
// Licence: wxWindows licence
|
// Licence: wxWindows licence
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@@ -6,6 +6,7 @@
|
|||||||
// Created: 2004-11-04
|
// Created: 2004-11-04
|
||||||
// RCS-ID: $Id$
|
// RCS-ID: $Id$
|
||||||
// Copyright: (c) David Elliott, Ryan Norton
|
// Copyright: (c) David Elliott, Ryan Norton
|
||||||
|
// (c) 2013 Rob Bresalier
|
||||||
// Licence: wxWindows licence
|
// Licence: wxWindows licence
|
||||||
// Notes: This code comes from src/osx/carbon/utilsexc.cpp,1.11
|
// Notes: This code comes from src/osx/carbon/utilsexc.cpp,1.11
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -15,7 +16,6 @@
|
|||||||
#include "wx/log.h"
|
#include "wx/log.h"
|
||||||
#include "wx/utils.h"
|
#include "wx/utils.h"
|
||||||
#endif //ndef WX_PRECOMP
|
#endif //ndef WX_PRECOMP
|
||||||
#include "wx/unix/execute.h"
|
|
||||||
#include "wx/stdpaths.h"
|
#include "wx/stdpaths.h"
|
||||||
#include "wx/app.h"
|
#include "wx/app.h"
|
||||||
#include "wx/apptrait.h"
|
#include "wx/apptrait.h"
|
||||||
@@ -30,84 +30,6 @@
|
|||||||
|
|
||||||
#include <CoreFoundation/CFSocket.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
|
#if wxUSE_EVENTLOOP_SOURCE
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
@@ -122,7 +44,7 @@ wx_socket_callback(CFSocketRef WXUNUSED(s),
|
|||||||
void *ctxData)
|
void *ctxData)
|
||||||
{
|
{
|
||||||
wxLogTrace(wxTRACE_EVT_SOURCE,
|
wxLogTrace(wxTRACE_EVT_SOURCE,
|
||||||
"CFSocket callback, type=%d", callbackType);
|
"CFSocket callback, type=%d", static_cast<int>(callbackType));
|
||||||
|
|
||||||
wxCFEventLoopSource * const
|
wxCFEventLoopSource * const
|
||||||
source = static_cast<wxCFEventLoopSource *>(ctxData);
|
source = static_cast<wxCFEventLoopSource *>(ctxData);
|
||||||
|
@@ -30,6 +30,7 @@
|
|||||||
#endif // WX_PRECOMP
|
#endif // WX_PRECOMP
|
||||||
|
|
||||||
#include "wx/unix/execute.h"
|
#include "wx/unix/execute.h"
|
||||||
|
#include "wx/evtloop.h"
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// implementation
|
// implementation
|
||||||
@@ -37,47 +38,15 @@
|
|||||||
|
|
||||||
int wxGUIAppTraits::WaitForChild(wxExecuteData& execData)
|
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
|
// prepare to wait for the child termination: show to the user that we're
|
||||||
// busy and refuse all input unless explicitly told otherwise
|
// busy and refuse all input unless explicitly told otherwise
|
||||||
wxBusyCursor bc;
|
wxBusyCursor bc;
|
||||||
wxWindowDisabler wd(!(flags & wxEXEC_NODISABLE));
|
wxWindowDisabler wd(!(execData.flags & wxEXEC_NODISABLE));
|
||||||
|
|
||||||
// endProcData.pid will be set to 0 from wxHandleProcessTermination() when
|
// Allocate an event loop that will be used to wait for the process
|
||||||
// the process terminates
|
// to terminate, will handle stdout, stderr, and any other events and pass
|
||||||
while ( endProcData.pid != 0 )
|
// it to the common (to console and GUI) code which will run it.
|
||||||
{
|
wxGUIEventLoop loop;
|
||||||
// don't consume 100% of the CPU while we're sitting in this
|
return RunLoopUntilChildExit(execData, loop);
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -5,6 +5,8 @@
|
|||||||
// Created: 2007-05-07
|
// Created: 2007-05-07
|
||||||
// RCS-ID: $Id$
|
// RCS-ID: $Id$
|
||||||
// Copyright: (c) 2006 Zork Lukasz Michalski
|
// Copyright: (c) 2006 Zork Lukasz Michalski
|
||||||
|
// (c) 2009, 2013 Vadim Zeitlin <vadim@wxwidgets.org>
|
||||||
|
// (c) 2013 Rob Bresalier
|
||||||
// Licence: wxWindows licence
|
// Licence: wxWindows licence
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@@ -4,6 +4,7 @@
|
|||||||
// Author: Vadim Zeitlin
|
// Author: Vadim Zeitlin
|
||||||
// Id: $Id$
|
// Id: $Id$
|
||||||
// Copyright: (c) 1998 Robert Roebling, Vadim Zeitlin
|
// Copyright: (c) 1998 Robert Roebling, Vadim Zeitlin
|
||||||
|
// (c) 2013 Rob Bresalier, Vadim Zeitlin
|
||||||
// Licence: wxWindows licence
|
// Licence: wxWindows licence
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
@@ -37,6 +38,7 @@
|
|||||||
#include "wx/apptrait.h"
|
#include "wx/apptrait.h"
|
||||||
|
|
||||||
#include "wx/process.h"
|
#include "wx/process.h"
|
||||||
|
#include "wx/scopedptr.h"
|
||||||
#include "wx/thread.h"
|
#include "wx/thread.h"
|
||||||
|
|
||||||
#include "wx/cmdline.h"
|
#include "wx/cmdline.h"
|
||||||
@@ -48,9 +50,9 @@
|
|||||||
#include "wx/unix/execute.h"
|
#include "wx/unix/execute.h"
|
||||||
#include "wx/unix/private.h"
|
#include "wx/unix/private.h"
|
||||||
|
|
||||||
#ifdef wxHAS_GENERIC_PROCESS_CALLBACK
|
#include "wx/evtloop.h"
|
||||||
#include "wx/private/fdiodispatcher.h"
|
#include "wx/mstream.h"
|
||||||
#endif
|
#include "wx/private/fdioeventloopsourcehandler.h"
|
||||||
|
|
||||||
#include <pwd.h>
|
#include <pwd.h>
|
||||||
#include <sys/wait.h> // waitpid()
|
#include <sys/wait.h> // waitpid()
|
||||||
@@ -65,6 +67,7 @@
|
|||||||
|
|
||||||
#include "wx/private/pipestream.h"
|
#include "wx/private/pipestream.h"
|
||||||
#include "wx/private/streamtempinput.h"
|
#include "wx/private/streamtempinput.h"
|
||||||
|
#include "wx/unix/private/executeiohandler.h"
|
||||||
|
|
||||||
#endif // HAS_PIPE_STREAMS
|
#endif // HAS_PIPE_STREAMS
|
||||||
|
|
||||||
@@ -484,6 +487,76 @@ long wxExecute(wchar_t **wargv, int flags, wxProcess *process,
|
|||||||
|
|
||||||
#endif // wxUSE_UNICODE
|
#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
|
// wxExecute: the real worker function
|
||||||
long wxExecute(char **argv, int flags, wxProcess *process,
|
long wxExecute(char **argv, int flags, wxProcess *process,
|
||||||
const wxExecuteEnv *env)
|
const wxExecuteEnv *env)
|
||||||
@@ -518,21 +591,14 @@ long wxExecute(char **argv, int flags, wxProcess *process,
|
|||||||
}
|
}
|
||||||
#endif // __DARWIN__
|
#endif // __DARWIN__
|
||||||
|
|
||||||
|
|
||||||
// this struct contains all information which we use for housekeeping
|
// this struct contains all information which we use for housekeeping
|
||||||
wxExecuteData execData;
|
wxScopedPtr<wxExecuteData> execDataPtr(new wxExecuteData);
|
||||||
|
wxExecuteData& execData = *execDataPtr;
|
||||||
|
|
||||||
execData.flags = flags;
|
execData.flags = flags;
|
||||||
execData.process = process;
|
execData.process = process;
|
||||||
|
|
||||||
// create pipes
|
// create pipes for inter process communication
|
||||||
if ( !execData.pipeEndProcDetect.Create() )
|
|
||||||
{
|
|
||||||
wxLogError( _("Failed to execute '%s'\n"), *argv );
|
|
||||||
|
|
||||||
return ERROR_RETURN_CODE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// pipes for inter process communication
|
|
||||||
wxPipe pipeIn, // stdin
|
wxPipe pipeIn, // stdin
|
||||||
pipeOut, // stdout
|
pipeOut, // stdout
|
||||||
pipeErr; // stderr
|
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
|
// 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.
|
// 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
|
// TODO: Iterating up to FD_SETSIZE is both inefficient (because it may
|
||||||
// be quite big) and incorrect (because in principle we could
|
// be quite big) and incorrect (because in principle we could
|
||||||
// have more opened descriptions than this number). Unfortunately
|
// have more opened descriptions than this number). Unfortunately
|
||||||
@@ -642,8 +701,7 @@ long wxExecute(char **argv, int flags, wxProcess *process,
|
|||||||
{
|
{
|
||||||
if ( fd != STDIN_FILENO &&
|
if ( fd != STDIN_FILENO &&
|
||||||
fd != STDOUT_FILENO &&
|
fd != STDOUT_FILENO &&
|
||||||
fd != STDERR_FILENO &&
|
fd != STDERR_FILENO )
|
||||||
fd != fdEndProc )
|
|
||||||
{
|
{
|
||||||
close(fd);
|
close(fd);
|
||||||
}
|
}
|
||||||
@@ -706,18 +764,11 @@ long wxExecute(char **argv, int flags, wxProcess *process,
|
|||||||
}
|
}
|
||||||
else // we're in parent
|
else // we're in parent
|
||||||
{
|
{
|
||||||
// save it for WaitForChild() use
|
execData.OnStart(pid);
|
||||||
execData.pid = pid;
|
|
||||||
if (execData.process)
|
|
||||||
execData.process->SetPid(pid); // and also in the wxProcess
|
|
||||||
|
|
||||||
// prepare for IO redirection
|
// prepare for IO redirection
|
||||||
|
|
||||||
#if HAS_PIPE_STREAMS
|
#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() )
|
if ( process && process->IsRedirected() )
|
||||||
{
|
{
|
||||||
@@ -748,15 +799,15 @@ long wxExecute(char **argv, int flags, wxProcess *process,
|
|||||||
|
|
||||||
process->SetPipeStreams(outStream, inStream, errStream);
|
process->SetPipeStreams(outStream, inStream, errStream);
|
||||||
|
|
||||||
bufOut.Init(outStream);
|
if ( flags & wxEXEC_SYNC )
|
||||||
bufErr.Init(errStream);
|
{
|
||||||
|
execData.bufOut.Init(outStream);
|
||||||
execData.bufOut = &bufOut;
|
execData.bufErr.Init(errStream);
|
||||||
execData.bufErr = &bufErr;
|
|
||||||
|
|
||||||
execData.fdOut = fdOut;
|
execData.fdOut = fdOut;
|
||||||
execData.fdErr = fdErr;
|
execData.fdErr = fdErr;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
#endif // HAS_PIPE_STREAMS
|
#endif // HAS_PIPE_STREAMS
|
||||||
|
|
||||||
if ( pipeIn.IsOk() )
|
if ( pipeIn.IsOk() )
|
||||||
@@ -766,14 +817,32 @@ long wxExecute(char **argv, int flags, wxProcess *process,
|
|||||||
pipeErr.Close();
|
pipeErr.Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
// we want this function to work even if there is no wxApp so ensure
|
// For the asynchronous case we don't have to do anything else, just
|
||||||
// that we have a valid traits pointer
|
// let the process run.
|
||||||
wxConsoleAppTraits traitsConsole;
|
if ( !(flags & wxEXEC_SYNC) )
|
||||||
wxAppTraits *traits = wxTheApp ? wxTheApp->GetTraits() : NULL;
|
{
|
||||||
if ( !traits )
|
// Ensure that the housekeeping data is kept alive, it will be
|
||||||
traits = &traitsConsole;
|
// destroyed only when the child terminates.
|
||||||
|
execDataPtr.release();
|
||||||
|
|
||||||
return traits->WaitForChild(execData);
|
return execData.pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// If we don't need to dispatch any events, things are relatively
|
||||||
|
// simple and we don't need to delegate to wxAppTraits.
|
||||||
|
if ( flags & wxEXEC_NOEVENTS )
|
||||||
|
{
|
||||||
|
return BlockUntilChildExit(execData);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// If we do need to dispatch events, enter a local event loop waiting
|
||||||
|
// until the child exits. As the exact kind of event loop depends on
|
||||||
|
// the sort of application we're in (console or GUI), we delegate this
|
||||||
|
// to wxAppTraits which virtualizes all the differences between the
|
||||||
|
// console and the GUI programs.
|
||||||
|
return wxApp::GetValidTraits().WaitForChild(execData);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !defined(__VMS) && !defined(__INTEL_COMPILER)
|
#if !defined(__VMS) && !defined(__INTEL_COMPILER)
|
||||||
@@ -1336,141 +1405,67 @@ bool wxHandleFatalExceptions(bool doit)
|
|||||||
// wxExecute support
|
// wxExecute support
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
int wxAppTraits::AddProcessCallback(wxEndProcessData *data, int fd)
|
int wxAppTraits::WaitForChild(wxExecuteData& execData)
|
||||||
{
|
{
|
||||||
// define a custom handler processing only the closure of the descriptor
|
wxConsoleEventLoop loop;
|
||||||
struct wxEndProcessFDIOHandler : public wxFDIOHandler
|
return RunLoopUntilChildExit(execData, loop);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function is common code for both console and GUI applications and used
|
||||||
|
// by wxExecute() to wait for the child exit while dispatching events.
|
||||||
|
//
|
||||||
|
// 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)
|
||||||
|
{
|
||||||
|
// 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() )
|
||||||
{
|
{
|
||||||
wxEndProcessFDIOHandler(wxEndProcessData *data, int fd)
|
stdoutHandler.reset(new wxExecuteEventLoopSourceHandler
|
||||||
: 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,
|
execData.fdOut, execData.bufOut
|
||||||
new wxEndProcessFDIOHandler(data, fd),
|
));
|
||||||
wxFDIO_INPUT
|
stderrHandler.reset(new wxExecuteEventLoopSourceHandler
|
||||||
);
|
(
|
||||||
return fd; // unused, but return something unique for the tag
|
execData.fdErr, execData.bufErr
|
||||||
|
));
|
||||||
|
}
|
||||||
|
#endif // wxUSE_STREAMS
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
// And run it.
|
||||||
|
loop.Run();
|
||||||
|
|
||||||
|
// The exit code will have been set when the child termination was detected.
|
||||||
|
return execData.exitcode;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool wxAppTraits::CheckForRedirectedIO(wxExecuteData& execData)
|
// ----------------------------------------------------------------------------
|
||||||
{
|
// wxExecuteData
|
||||||
#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
|
|
||||||
}
|
|
||||||
|
|
||||||
// helper classes/functions used by WaitForChild()
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
|
||||||
// convenient base class for IO handlers which are registered for read
|
// Helper function that checks whether the child with the given PID has exited
|
||||||
// notifications only and which also stores the FD we're reading from
|
// and fills the provided parameter with its return code if it did.
|
||||||
//
|
bool CheckForChildExit(int pid, int* exitcodeOut)
|
||||||
// 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
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
wxEndHandler(wxFDIODispatcher& disp, int fd)
|
|
||||||
: wxReadFDIOHandler(disp, fd)
|
|
||||||
{
|
|
||||||
m_terminated = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Terminated() const { return m_terminated; }
|
|
||||||
|
|
||||||
virtual void OnReadWaiting() { m_terminated = true; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool m_terminated;
|
|
||||||
|
|
||||||
wxDECLARE_NO_COPY_CLASS(wxEndHandler);
|
|
||||||
};
|
|
||||||
|
|
||||||
#if HAS_PIPE_STREAMS
|
|
||||||
|
|
||||||
// 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
|
|
||||||
{
|
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
wxASSERT_MSG( pid > 0, "invalid PID" );
|
wxASSERT_MSG( pid > 0, "invalid PID" );
|
||||||
|
|
||||||
@@ -1479,28 +1474,25 @@ int DoWaitForChild(int pid, int flags = 0)
|
|||||||
// loop while we're getting EINTR
|
// loop while we're getting EINTR
|
||||||
for ( ;; )
|
for ( ;; )
|
||||||
{
|
{
|
||||||
rc = waitpid(pid, &status, flags);
|
rc = waitpid(pid, &status, WNOHANG);
|
||||||
|
|
||||||
if ( rc != -1 || errno != EINTR )
|
if ( rc != -1 || errno != EINTR )
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( rc == 0 )
|
switch ( rc )
|
||||||
{
|
|
||||||
// This can only happen if the child application closes our dummy pipe
|
|
||||||
// that is used to monitor its lifetime; in that case, our best bet is
|
|
||||||
// to pretend the process did terminate, because otherwise wxExecute()
|
|
||||||
// would hang indefinitely (OnReadWaiting() won't be called again, the
|
|
||||||
// descriptor is closed now).
|
|
||||||
wxLogDebug("Child process (PID %d) still alive but pipe closed so "
|
|
||||||
"generating a close notification", pid);
|
|
||||||
}
|
|
||||||
else if ( rc == -1 )
|
|
||||||
{
|
{
|
||||||
|
case 0:
|
||||||
|
// No error but the child is still running.
|
||||||
|
return false;
|
||||||
|
|
||||||
|
case -1:
|
||||||
|
// Checking child status failed. Invalid PID?
|
||||||
wxLogLastError(wxString::Format("waitpid(%d)", pid));
|
wxLogLastError(wxString::Format("waitpid(%d)", pid));
|
||||||
}
|
return false;
|
||||||
else // child did terminate
|
|
||||||
{
|
default:
|
||||||
|
// Child did terminate.
|
||||||
wxASSERT_MSG( rc == pid, "unexpected waitpid() return value" );
|
wxASSERT_MSG( rc == pid, "unexpected waitpid() return value" );
|
||||||
|
|
||||||
// notice that the caller expects the exit code to be signed, e.g. -1
|
// notice that the caller expects the exit code to be signed, e.g. -1
|
||||||
@@ -1517,86 +1509,115 @@ int DoWaitForChild(int pid, int flags = 0)
|
|||||||
exitcode = -1;
|
exitcode = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return exitcode;
|
if ( exitcodeOut )
|
||||||
}
|
*exitcodeOut = exitcode;
|
||||||
|
|
||||||
return -1;
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
|
|
||||||
int wxAppTraits::WaitForChild(wxExecuteData& execData)
|
wxExecuteData::ChildProcessesData wxExecuteData::ms_childProcesses;
|
||||||
|
|
||||||
|
/* static */
|
||||||
|
void wxExecuteData::OnSomeChildExited(int WXUNUSED(sig))
|
||||||
{
|
{
|
||||||
if ( !(execData.flags & wxEXEC_SYNC) )
|
// 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).
|
||||||
// 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
|
|
||||||
|
|
||||||
#if HAS_PIPE_STREAMS && wxUSE_SOCKETS
|
|
||||||
wxProcess * const process = execData.process;
|
|
||||||
if ( process && process->IsRedirected() )
|
|
||||||
{
|
|
||||||
// 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 multiplex here waiting for any input from the child or closure of
|
// An alternative approach would be to call waitpid(-1, &status, WNOHANG)
|
||||||
// the pipe used to indicate its termination
|
// (in a loop to take care of the multiple children exiting case) and
|
||||||
wxSelectDispatcher disp;
|
// perhaps this would be more efficient. But for now this seems to work.
|
||||||
|
|
||||||
wxEndHandler endHandler(disp, execData.GetEndProcReadFD());
|
|
||||||
|
|
||||||
wxRedirectedIOHandler outHandler(disp, execData.fdOut, execData.bufOut),
|
// Make a copy of the list before iterating over it to avoid problems due
|
||||||
errHandler(disp, execData.fdErr, execData.bufErr);
|
// to deleting entries from it in the process.
|
||||||
|
const ChildProcessesData allChildProcesses = ms_childProcesses;
|
||||||
while ( !endHandler.Terminated() )
|
for ( ChildProcessesData::const_iterator it = allChildProcesses.begin();
|
||||||
|
it != allChildProcesses.end();
|
||||||
|
++it )
|
||||||
{
|
{
|
||||||
disp.Dispatch();
|
const int pid = it->first;
|
||||||
}
|
|
||||||
}
|
|
||||||
//else: no IO redirection, just block waiting for the child to exit
|
|
||||||
#endif // HAS_PIPE_STREAMS
|
|
||||||
|
|
||||||
return DoWaitForChild(execData.pid);
|
// Check whether this child exited.
|
||||||
|
int exitcode;
|
||||||
|
if ( !CheckForChildExit(pid, &exitcode) )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// And handle its termination if it did.
|
||||||
|
//
|
||||||
|
// Notice that this will implicitly remove it from ms_childProcesses.
|
||||||
|
it->second->OnExit(exitcode);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void wxHandleProcessTermination(wxEndProcessData *data)
|
void wxExecuteData::OnStart(int pid_)
|
||||||
{
|
{
|
||||||
data->exitcode = DoWaitForChild(data->pid, WNOHANG);
|
wxCHECK_RET( wxTheApp,
|
||||||
|
wxS("Ensure wxTheApp is set before calling wxExecute()") );
|
||||||
|
|
||||||
// notify user about termination if required
|
// Setup the signal handler for SIGCHLD to be able to detect the child
|
||||||
if ( data->process )
|
// termination.
|
||||||
|
//
|
||||||
|
// Notice that SetSignalHandler() is idempotent, so it's fine to call
|
||||||
|
// it more than once with the same handler.
|
||||||
|
wxTheApp->SetSignalHandler(SIGCHLD, OnSomeChildExited);
|
||||||
|
|
||||||
|
|
||||||
|
// 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 wxExecuteData::OnExit(int exitcode_)
|
||||||
|
{
|
||||||
|
// 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) )
|
||||||
{
|
{
|
||||||
data->process->OnTerminate(data->pid, data->exitcode);
|
wxFAIL_MSG(wxString::Format(wxS("Data for PID %d not in the list?"), pid));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( data->async )
|
|
||||||
|
exitcode = exitcode_;
|
||||||
|
|
||||||
|
#if wxUSE_STREAMS
|
||||||
|
if ( IsRedirected() )
|
||||||
{
|
{
|
||||||
// in case of asynchronous execution we don't need this data any more
|
// 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
|
||||||
|
|
||||||
|
// Notify user about termination if required
|
||||||
|
if ( !(flags & wxEXEC_SYNC) )
|
||||||
|
{
|
||||||
|
if ( process )
|
||||||
|
process->OnTerminate(pid, exitcode);
|
||||||
|
|
||||||
|
// in case of asynchronous execution we don't need this object any more
|
||||||
// after the child terminates
|
// after the child terminates
|
||||||
delete data;
|
delete this;
|
||||||
}
|
}
|
||||||
else // sync execution
|
else // sync execution
|
||||||
{
|
{
|
||||||
// let wxExecute() know that the process has terminated
|
// 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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -58,8 +58,6 @@
|
|||||||
#pragma message disable nosimpint
|
#pragma message disable nosimpint
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "wx/unix/execute.h"
|
|
||||||
|
|
||||||
#include "wx/x11/private.h"
|
#include "wx/x11/private.h"
|
||||||
|
|
||||||
#include "X11/Xutil.h"
|
#include "X11/Xutil.h"
|
||||||
|
@@ -6,6 +6,8 @@
|
|||||||
// Created: 2009-01-10
|
// Created: 2009-01-10
|
||||||
// RCS-ID: $Id$
|
// RCS-ID: $Id$
|
||||||
// Copyright: (c) 2009 Francesco Montorsi
|
// Copyright: (c) 2009 Francesco Montorsi
|
||||||
|
// (c) 2013 Rob Bresalier, Vadim Zeitlin
|
||||||
|
// Licence: wxWindows licence
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
Reference in New Issue
Block a user