Add wxAddProcessCallback for both wxMac and wxCocoa and use it for wxCocoa.

It can be turned on for wxMac if desired.  See utilsunx.cpp comment.
NOTE: I can't add the symbol to version-script.in because other platforms
have had this symbol even though wxMac/wxCocoa have not.


git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/branches/WX_2_8_BRANCH@49239 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
David Elliott
2007-10-19 03:10:31 +00:00
parent 0e3d1f4af5
commit 551896d9da
2 changed files with 143 additions and 0 deletions

View File

@@ -231,6 +231,123 @@ int wxAddProcessCallbackForPid(wxEndProcessData *proc_data, int pid)
#endif // USE_POLLING/!USE_POLLING
/////////////////////////////////////////////////////////////////////////////
// New implementation avoiding mach ports entirely.
#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 callbackType, CFDataRef address, void const *data, void *info)
{
wxEndProcessData * const proc_data = static_cast<wxEndProcessData*>(info);
/// This code could reasonably be shared between wxMac/wxCocoa and wxGTK ///
// PID is always positive on UNIX but wx uses the sign bit as a flag.
int pid = (proc_data->pid > 0) ? proc_data->pid : -proc_data->pid;
int status = 0;
int rc = waitpid(pid, &status, WNOHANG);
if(rc == 0)
{
// Keep waiting in case we got a spurious notification
// NOTE: In my limited testing, this doesn't happen.
return;
}
if(rc == -1)
{ // Error.. really shouldn't happen but try to gracefully handle it
wxLogLastError(_T("waitpid"));
proc_data->exitcode = -1;
}
else
{ // Process ended for some reason
wxASSERT_MSG(rc == pid, _T("unexpected waitpid() return value"));
if(WIFEXITED(status))
proc_data->exitcode = WEXITSTATUS(status);
else if(WIFSIGNALED(status))
// wxGTK doesn't do this but why not?
proc_data->exitcode = -WTERMSIG(status);
else
{ // Should NEVER happen according to waitpid docs
wxLogError(wxT("waitpid indicates process exited but not due to exiting or signalling"));
proc_data->exitcode = -1;
}
}
/// The above code could reasonably be shared between wxMac/wxCocoa and wxGTK ///
/*
Either waitpid failed 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(proc_data);
}
/*!
Implements the GUI-specific wxAddProcessCallback 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 wxAddProcessCallback(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("Failed to create socket for end process detection");
return 0;
}
CFRunLoopSourceRef runLoopSource = CFSocketCreateRunLoopSource(kCFAllocatorDefault, cfSocket, /*highest priority:*/0);
if(runLoopSource == NULL)
{
wxLogError("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;
}
/////////////////////////////////////////////////////////////////////////////
// NOTE: This doesn't really belong here but this was a handy file to
// put it in because it's already compiled for wxCocoa and wxMac GUI lib.
#if wxUSE_GUI

View File

@@ -1177,7 +1177,33 @@ bool wxHandleFatalExceptions(bool doit)
// wxExecute support
// ----------------------------------------------------------------------------
/*
NOTE: The original code shipped in 2.8 used __DARWIN__ && __WXMAC__ to wrap
the wxGUIAppTraits differences but __DARWIN__ && (__WXMAC__ || __WXCOCOA__)
to decide whether to call wxAddProcessCallbackForPid instead of
wxAddProcessCallback. This define normalizes things so the two match.
Since wxCocoa was already creating the pipes in its wxGUIAppTraits I
decided to leave that as is and implement wxAddProcessCallback in the
utilsexec_cf.cpp file. I didn't see a reason to wrap that in a __WXCOCOA__
check since it's valid for both wxMac and wxCocoa.
Since the existing code is working for wxMac I've left it as is although
do note that the old task_for_pid method still used on PPC machines is
expected to fail in Leopard PPC and theoretically already fails if you run
your PPC app under Rosetta.
You thus have two choices if you find end process detect broken:
1) Change the define below such that the new code is used for wxMac.
This is theoretically ABI compatible since the old code still remains
in utilsexec_cf.cpp it's just no longer used by this code.
2) Change the USE_POLLING define in utilsexc_cf.cpp to 1 unconditionally
This is theoretically not compatible since it removes the
wxMAC_MachPortEndProcessDetect helper function. Though in practice
this shouldn't be a problem since it wasn't prototyped anywhere.
*/
#define USE_OLD_DARWIN_END_PROCESS_DETECT (defined(__DARWIN__) && defined(__WXMAC__))
// #define USE_OLD_DARWIN_END_PROCESS_DETECT 0
// wxMac doesn't use the same process end detection mechanisms so we don't
// need wxExecute-related helpers for it.