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:
@@ -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.
|
||||
|
@@ -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_
|
||||
|
@@ -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
|
||||
|
@@ -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/app.h"
|
||||
#include "wx/hashmap.h"
|
||||
#include "wx/process.h"
|
||||
|
||||
#include "wx/unix/pipe.h"
|
||||
#include "wx/private/streamtempinput.h"
|
||||
|
||||
class WXDLLIMPEXP_FWD_BASE wxProcess;
|
||||
class wxStreamTempInputBuffer;
|
||||
class wxEventLoopBase;
|
||||
|
||||
struct wxEndProcessData
|
||||
{
|
||||
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
|
||||
// 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
|
||||
|
||||
|
||||
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
|
||||
|
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_
|
Reference in New Issue
Block a user