Use CFSocket instead of CFFileDescriptor in wxCFEventLoopSource.

Use OS X socket APIs for monitoring file descriptors. They are more flexible
than CFFileDescriptor functions and can be used with any descriptors, not
necessarily the socket ones.

See #10258.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@74342 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Vadim Zeitlin
2013-07-03 00:29:05 +00:00
parent 71e9885be0
commit a25b76f5f9
3 changed files with 90 additions and 48 deletions

View File

@@ -11,7 +11,7 @@
#ifndef _WX_OSX_EVTLOOPSRC_H_ #ifndef _WX_OSX_EVTLOOPSRC_H_
#define _WX_OSX_EVTLOOPSRC_H_ #define _WX_OSX_EVTLOOPSRC_H_
typedef struct __CFFileDescriptor *CFFileDescriptorRef; typedef struct __CFSocket* CFSocketRef;
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// wxCFEventLoopSource: CoreFoundation-based wxEventLoopSource for OS X // wxCFEventLoopSource: CoreFoundation-based wxEventLoopSource for OS X
@@ -20,19 +20,23 @@ typedef struct __CFFileDescriptor *CFFileDescriptorRef;
class WXDLLIMPEXP_BASE wxCFEventLoopSource : public wxEventLoopSource class WXDLLIMPEXP_BASE wxCFEventLoopSource : public wxEventLoopSource
{ {
public: public:
// Create a new source in uninitialized state, call InitSocketRef() later
// to associate it with the socket it is going to use.
wxCFEventLoopSource(wxEventLoopSourceHandler *handler, int flags) wxCFEventLoopSource(wxEventLoopSourceHandler *handler, int flags)
: wxEventLoopSource(handler, flags) : wxEventLoopSource(handler, flags)
{ {
m_cffd = NULL; m_cfSocket = NULL;
} }
// we take ownership of this CFFileDescriptorRef // Finish initialization of the event loop source by providing the
void SetFileDescriptor(CFFileDescriptorRef cffd); // associated socket. This object takes ownership of it and will release it.
void InitSourceSocket(CFSocketRef cfSocket);
// Destructor deletes the associated socket.
virtual ~wxCFEventLoopSource(); virtual ~wxCFEventLoopSource();
private: private:
CFFileDescriptorRef m_cffd; CFSocketRef m_cfSocket;
wxDECLARE_NO_COPY_CLASS(wxCFEventLoopSource); wxDECLARE_NO_COPY_CLASS(wxCFEventLoopSource);
}; };

View File

@@ -42,23 +42,28 @@
#include "wx/nonownedwnd.h" #include "wx/nonownedwnd.h"
#endif #endif
#include <CoreFoundation/CFSocket.h>
// ============================================================================ // ============================================================================
// wxCFEventLoopSource and wxCFEventLoop implementation // wxCFEventLoopSource and wxCFEventLoop implementation
// ============================================================================ // ============================================================================
#if wxUSE_EVENTLOOP_SOURCE #if wxUSE_EVENTLOOP_SOURCE
void wxCFEventLoopSource::SetFileDescriptor(CFFileDescriptorRef cffd) void wxCFEventLoopSource::InitSourceSocket(CFSocketRef cfSocket)
{ {
wxASSERT_MSG( !m_cffd, "shouldn't be called more than once" ); wxASSERT_MSG( !m_cfSocket, "shouldn't be called more than once" );
m_cffd = cffd; m_cfSocket = cfSocket;
} }
wxCFEventLoopSource::~wxCFEventLoopSource() wxCFEventLoopSource::~wxCFEventLoopSource()
{ {
if ( m_cffd ) if ( m_cfSocket )
CFRelease(m_cffd); {
CFSocketInvalidate(m_cfSocket);
CFRelease(m_cfSocket);
}
} }
#endif // wxUSE_EVENTLOOP_SOURCE #endif // wxUSE_EVENTLOOP_SOURCE

View File

@@ -28,7 +28,6 @@
#include <sys/wait.h> #include <sys/wait.h>
#include <CoreFoundation/CFFileDescriptor.h>
#include <CoreFoundation/CFSocket.h> #include <CoreFoundation/CFSocket.h>
/*! /*!
@@ -114,34 +113,36 @@ int wxGUIAppTraits::AddProcessCallback(wxEndProcessData *proc_data, int fd)
namespace namespace
{ {
void EnableDescriptorCallBacks(CFFileDescriptorRef cffd, int flags) extern "C"
{
if ( flags & wxEVENT_SOURCE_INPUT )
CFFileDescriptorEnableCallBacks(cffd, kCFFileDescriptorReadCallBack);
if ( flags & wxEVENT_SOURCE_OUTPUT )
CFFileDescriptorEnableCallBacks(cffd, kCFFileDescriptorWriteCallBack);
}
void void
wx_cffiledescriptor_callback(CFFileDescriptorRef cffd, wx_socket_callback(CFSocketRef WXUNUSED(s),
CFOptionFlags flags, CFSocketCallBackType callbackType,
CFDataRef WXUNUSED(address),
void const *WXUNUSED(data),
void *ctxData) void *ctxData)
{ {
wxLogTrace(wxTRACE_EVT_SOURCE, wxLogTrace(wxTRACE_EVT_SOURCE,
"CFFileDescriptor callback, flags=%d", flags); "CFSocket callback, type=%d", callbackType);
wxCFEventLoopSource * const wxCFEventLoopSource * const
source = static_cast<wxCFEventLoopSource *>(ctxData); source = static_cast<wxCFEventLoopSource *>(ctxData);
wxEventLoopSourceHandler * const wxEventLoopSourceHandler * const
handler = source->GetHandler(); handler = source->GetHandler();
if ( flags & kCFFileDescriptorReadCallBack )
handler->OnReadWaiting();
if ( flags & kCFFileDescriptorWriteCallBack )
handler->OnWriteWaiting();
// we need to re-enable callbacks to be called again switch ( callbackType )
EnableDescriptorCallBacks(cffd, source->GetFlags()); {
case kCFSocketReadCallBack:
handler->OnReadWaiting();
break;
case kCFSocketWriteCallBack:
handler->OnWriteWaiting();
break;
default:
wxFAIL_MSG( "Unexpected callback type." );
}
} }
} // anonymous namespace } // anonymous namespace
@@ -157,31 +158,63 @@ public:
wxScopedPtr<wxCFEventLoopSource> wxScopedPtr<wxCFEventLoopSource>
source(new wxCFEventLoopSource(handler, flags)); source(new wxCFEventLoopSource(handler, flags));
CFFileDescriptorContext ctx = { 0, source.get(), NULL, NULL, NULL }; CFSocketContext context = { 0, source.get(), NULL, NULL, NULL };
wxCFRef<CFFileDescriptorRef>
cffd(CFFileDescriptorCreate int callbackTypes = 0;
if ( flags & wxEVENT_SOURCE_INPUT )
callbackTypes |= kCFSocketReadCallBack;
if ( flags & wxEVENT_SOURCE_OUTPUT )
callbackTypes |= kCFSocketWriteCallBack;
wxCFRef<CFSocketRef>
cfSocket(CFSocketCreateWithNative
( (
kCFAllocatorDefault, kCFAllocatorDefault,
fd, fd,
true, // close on invalidate callbackTypes,
wx_cffiledescriptor_callback, &wx_socket_callback,
&ctx &context
)); ));
if ( !cffd )
if ( !cfSocket )
{
wxLogError(wxS("Failed to create event loop source socket."));
return NULL; return NULL;
}
// Adjust the socket options to suit our needs:
CFOptionFlags sockopt = CFSocketGetSocketFlags(cfSocket);
// First, by default, write callback is not called repeatedly when data
// can be written to the socket but we need this behaviour so request
// it explicitly.
if ( flags & wxEVENT_SOURCE_OUTPUT )
sockopt |= kCFSocketAutomaticallyReenableWriteCallBack;
// Second, we use the socket to monitor the FD but it doesn't own it,
// so prevent the FD from being closed when the socket is invalidated.
sockopt &= ~kCFSocketCloseOnInvalidate;
CFSocketSetSocketFlags(cfSocket, sockopt);
wxCFRef<CFRunLoopSourceRef> wxCFRef<CFRunLoopSourceRef>
cfsrc(CFFileDescriptorCreateRunLoopSource(kCFAllocatorDefault, cffd, 0)); runLoopSource(CFSocketCreateRunLoopSource
if ( !cfsrc ) (
kCFAllocatorDefault,
cfSocket,
0 // Lowest index means highest priority
));
if ( !runLoopSource )
{
wxLogError(wxS("Failed to create low level event loop source."));
CFSocketInvalidate(cfSocket);
return NULL; return NULL;
}
CFRunLoopRef cfloop = CFRunLoopGetCurrent(); // Save the socket so that we can remove it later if asked to.
CFRunLoopAddSource(cfloop, cfsrc, kCFRunLoopDefaultMode); source->InitSourceSocket(cfSocket.release());
// Enable the callbacks initially. CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopCommonModes);
EnableDescriptorCallBacks(cffd, source->GetFlags());
source->SetFileDescriptor(cffd.release());
return source.release(); return source.release();
} }