class WXDLLIMPEXP_FWD_BASE wxProcess;
class wxStreamTempInputBuffer;
+#if defined(__WXDFB__) || defined(__WXX11__)
+ #define wxHAS_GENERIC_PROCESS_CALLBACK 1
+#endif
+
+#ifdef wxHAS_GENERIC_PROCESS_CALLBACK
+struct wxEndProcessFDIOHandler;
+#endif
+
// if pid > 0, the execution is async and the data is freed in the callback
// executed when the process terminates, if pid < 0, the execution is
// synchronous and the caller (wxExecute) frees the data
struct wxEndProcessData
{
- int pid, // pid of the process
- tag; // port dependent value
+ int pid; // pid of the process
+ int tag; // port dependent value
wxProcess *process; // if !NULL: notified on process termination
int exitcode; // the exit code
+
+#ifdef wxHAS_GENERIC_PROCESS_CALLBACK
+ wxEndProcessFDIOHandler *fdioHandler;
+#endif
};
// struct in which information is passed from wxExecute() to wxAppTraits
// callback function and is common to all ports (src/unix/utilsunx.cpp)
extern WXDLLIMPEXP_BASE void wxHandleProcessTermination(wxEndProcessData *proc_data);
-// this function is called to associate the port-specific callback with the
+// This function is called to associate the port-specific callback with the
// child process. The return valus is port-specific.
+//
+// The file descriptor 'fd' is descriptor of a dummy pipe opened between the
+// parent and the child. No data are written to or read from this pipe, its
+// sole purpose is that the child process will close it when it terminates and
+// the parent will be notified about it if it looks at 'fd' (e.g. using
+// select()).
+//
+// wxAddProcessCallback() does whatever is necessary to ensure that 'fd' is
+// periodically (typically every event loop iteration) checked for its status
+// and that wxHandleProcessTermination() is called once 'fd' indicates the
+// child terminated.
extern WXDLLIMPEXP_CORE int wxAddProcessCallback(wxEndProcessData *proc_data, int fd);
#if defined(__WXMAC__) || defined(__WXCOCOA__)
bool wxSetWMDecorations(Window w, long style);
bool wxMWMIsRunning(Window w);
-// Checks if any of our children are finished.
-// implemented in src/x11/utils.cpp
-void wxCheckForFinishedChildren();
-
#endif
// _WX_PRIVATE_H_
void wxBell()
{
}
-
-int wxAddProcessCallback(wxEndProcessData *proc_data, int fd)
-{
- wxFAIL_MSG( "wxAddProcessCallback not implemented" );
- return 0;
-}
#include "wx/unix/execute.h"
#include "wx/unix/private.h"
+#ifdef wxHAS_GENERIC_PROCESS_CALLBACK
+#include "wx/private/fdiodispatcher.h"
+#endif
+
#include <pwd.h>
#include <sys/wait.h> // waitpid()
}
}
+#if wxHAS_GENERIC_PROCESS_CALLBACK
+struct wxEndProcessFDIOHandler : public wxFDIOHandler
+{
+ wxEndProcessFDIOHandler(wxEndProcessData *data, int fd)
+ : m_data(data), m_fd(fd)
+ {}
+
+ virtual void OnReadWaiting()
+ { wxFAIL_MSG("this isn't supposed to happen"); }
+ virtual void OnWriteWaiting()
+ { wxFAIL_MSG("this isn't supposed to happen"); }
+
+ virtual void OnExceptionWaiting()
+ {
+ int pid = (m_data->pid > 0) ? m_data->pid : -(m_data->pid);
+ int status = 0;
+
+ // has the process really terminated?
+ int rc = waitpid(pid, &status, WNOHANG);
+ 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
+ // (OnExceptionWaiting() won't be called again, the descriptor
+ // is closed now).
+ wxLogDebug("Child process (PID %i) still alive, even though notification was received that it terminated.", pid);
+ }
+ else if ( rc == -1 )
+ {
+ // As above, if waitpid() fails, the best we can do is to log the
+ // error and pretend the child terminated:
+ wxLogSysError(_("Failed to check child process' status"));
+ }
+
+ // set exit code to -1 if something bad happened
+ m_data->exitcode = (rc > 0 && WIFEXITED(status))
+ ? WEXITSTATUS(status)
+ : -1;
+
+ wxLogTrace("exec",
+ "Child process (PID %i) terminated with exit code %i",
+ pid, m_data->exitcode);
+
+ // child exited, end waiting
+ wxFDIODispatcher::Get()->UnregisterFD(m_fd);
+ close(m_fd);
+
+ m_data->fdioHandler = NULL;
+ wxHandleProcessTermination(m_data);
+
+ delete this;
+ }
+
+ wxEndProcessData *m_data;
+ int m_fd;
+};
+
+int wxAddProcessCallback(wxEndProcessData *proc_data, int fd)
+{
+ proc_data->fdioHandler = new wxEndProcessFDIOHandler(proc_data, fd);
+ wxFDIODispatcher::Get()->RegisterFD
+ (
+ fd,
+ proc_data->fdioHandler,
+ wxFDIO_EXCEPTION
+ );
+ return fd; // unused, but return something unique for the tag
+}
+#endif // wxHAS_GENERIC_PROCESS_CALLBACK
+
#endif // wxUSE_GUI
#if wxUSE_BASE
{
XEvent event;
- // Start off by checking if any of our child processes have finished.
- wxCheckForFinishedChildren();
-
// TODO allowing for threads, as per e.g. wxMSW
// This now waits until either an X event is received,
return false;
}
-// ----------------------------------------------------------------------------
-// wxExecute stuff
-// ----------------------------------------------------------------------------
-
-WX_DECLARE_HASH_MAP( int, wxEndProcessData*, wxIntegerHash, wxIntegerEqual, wxProcMap );
-
-static wxProcMap *gs_procmap;
-
-int wxAddProcessCallback(wxEndProcessData *proc_data, int fd)
-{
- if (!gs_procmap) gs_procmap = new wxProcMap();
- (*gs_procmap)[fd] = proc_data;
- return 1;
-}
-
-void wxCheckForFinishedChildren()
-{
- wxProcMap::iterator it;
- if (!gs_procmap) return;
- if (gs_procmap->size() == 0) {
- // Map empty, delete it.
- delete gs_procmap;
- gs_procmap = NULL;
- return;
- }
- for (it = gs_procmap->begin();it != gs_procmap->end(); ++it)
- {
- wxEndProcessData *proc_data = it->second;
- int pid = (proc_data->pid > 0) ? proc_data->pid : -(proc_data->pid);
- int status = 0;
- // has the process really terminated?
- int rc = waitpid(pid, &status, WNOHANG);
- if (rc == 0)
- continue; // no, it didn't exit yet, continue waiting
-
- // set exit code to -1 if something bad happened
- proc_data->exitcode = rc != -1 && WIFEXITED(status) ?
- WEXITSTATUS(status) : -1;
-
- // child exited, end waiting
- close(it->first);
-
- // don't call us again!
- gs_procmap->erase(it->first);
-
- wxHandleProcessTermination(proc_data);
-
- // Iterator is invalid. Handle any further children in subsequent
- // calls.
- break;
- }
-}
-
// ----------------------------------------------------------------------------
// misc
// ----------------------------------------------------------------------------