1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/osx/core/gsockosx.cpp
3 // Purpose:     wxSocketImpl implementation for OS X
4 // Authors:     Brian Victor, Vadim Zeitlin
5 // Created:     February 2002
6 // Copyright:   (c) 2002 Brian Victor
7 //              (c) 2008 Vadim Zeitlin
8 // Licence:     wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10 
11 #include "wx/wxprec.h"
12 
13 #if wxUSE_SOCKETS
14 
15 #include "wx/private/socket.h"
16 #include "wx/unix/private/sockunix.h"
17 #include "wx/apptrait.h"
18 #include "wx/link.h"
19 
20 #include "wx/osx/core/cfstring.h"           // for wxMacWakeUp() only
21 
22 #include <CoreFoundation/CoreFoundation.h>
23 
24 namespace
25 {
26 
27 // ----------------------------------------------------------------------------
28 // global variables
29 // ----------------------------------------------------------------------------
30 
31 // Sockets must use the event loop to monitor the events so we store a
32 // reference to the main thread event loop here
33 static CFRunLoopRef gs_mainRunLoop = NULL;
34 
35 // ----------------------------------------------------------------------------
36 // Mac-specific socket implementation
37 // ----------------------------------------------------------------------------
38 
39 class wxSocketImplMac : public wxSocketImplUnix
40 {
41 public:
wxSocketImplMac(wxSocketBase & wxsocket)42     wxSocketImplMac(wxSocketBase& wxsocket)
43         : wxSocketImplUnix(wxsocket)
44     {
45         m_socket = NULL;
46         m_source = NULL;
47     }
48 
~wxSocketImplMac()49     virtual ~wxSocketImplMac()
50     {
51         wxASSERT_MSG( !m_source && !m_socket, "forgot to call Close()?" );
52     }
53 
54     // get the underlying socket: creates it on demand
GetSocket()55     CFSocketRef GetSocket() /* const */
56     {
57         if ( !m_socket )
58             Initialize();
59 
60         return m_socket;
61     }
62 
63 private:
DoClose()64     virtual void DoClose()
65     {
66         wxSocketManager * const manager = wxSocketManager::Get();
67         if ( manager )
68         {
69             manager->Uninstall_Callback(this, wxSOCKET_INPUT);
70             manager->Uninstall_Callback(this, wxSOCKET_OUTPUT);
71         }
72 
73         // VZ: CFRunLoopRemoveSource() is probably unnecessary as
74         //     CFSocketInvalidate() seems to do it internally from reading the
75         //     docs, please remove it (and this comment) after testing
76         CFRunLoopRemoveSource(gs_mainRunLoop, m_source, kCFRunLoopCommonModes);
77         CFSocketInvalidate(m_socket);
78 
79         CFRelease(m_source);
80         m_source = NULL;
81 
82         CFRelease(m_socket);
83         m_socket = NULL;
84     }
85 
86     // initialize the data associated with the given socket
Initialize()87     bool Initialize()
88     {
89         // we need a valid Unix socket to create a CFSocket
90         if ( m_fd < 0 )
91             return false;
92 
93         CFSocketContext cont;
94         cont.version = 0;               // this currently must be 0
95         cont.info = this;               // pointer passed to our callback
96         cont.retain = NULL;             // no need to retain/release/copy the
97         cont.release = NULL;            //  socket pointer, so all callbacks
98         cont.copyDescription = NULL;    //  can be left NULL
99 
100         m_socket = CFSocketCreateWithNative
101                    (
102                         NULL,                   // default allocator
103                         m_fd,
104                         kCFSocketReadCallBack |
105                         kCFSocketWriteCallBack |
106                         kCFSocketConnectCallBack,
107                         SocketCallback,
108                         &cont
109                    );
110         if ( !m_socket )
111             return false;
112 
113         m_source = CFSocketCreateRunLoopSource(NULL, m_socket, 0);
114 
115         if ( !m_source )
116         {
117             CFRelease(m_socket);
118             m_socket = NULL;
119 
120             return false;
121         }
122 
123         CFRunLoopAddSource(gs_mainRunLoop, m_source, kCFRunLoopCommonModes);
124 
125         return true;
126     }
127 
SocketCallback(CFSocketRef WXUNUSED (s),CFSocketCallBackType callbackType,CFDataRef WXUNUSED (address),const void * data,void * info)128     static void SocketCallback(CFSocketRef WXUNUSED(s),
129                                CFSocketCallBackType callbackType,
130                                CFDataRef WXUNUSED(address),
131                                const void* data,
132                                void* info)
133     {
134         wxSocketImplMac * const socket = static_cast<wxSocketImplMac *>(info);
135 
136         switch (callbackType)
137         {
138             case kCFSocketConnectCallBack:
139                 wxASSERT(!socket->IsServer());
140                 // KH: If data is non-NULL, the connect failed, do not call Detected_Write,
141                 // which will only end up creating a spurious connect event because the
142                 // call to getsocketopt SO_ERROR inexplicably returns no error.
143                 // The change in behaviour cannot be traced to any particular commit or
144                 // timeframe so I'm not sure what to think, but after so many hours,
145                 // this seems to address the issue and it's time to move on.
146                 if (data == NULL)
147                     socket->OnWriteWaiting();
148                 break;
149 
150             case kCFSocketReadCallBack:
151                 socket->OnReadWaiting();
152                 break;
153 
154             case kCFSocketWriteCallBack:
155                 socket->OnWriteWaiting();
156                 break;
157 
158             default:
159                 wxFAIL_MSG( "unexpected socket callback" );
160         }
161 
162         // receiving a socket event does _not_ make ReceiveNextEvent() (or the
163         // equivalent NSApp:nextEventMatchingMask:untilDate:inMode:dequeue)
164         // return control, i.e. apparently it doesn't count as a real event, so
165         // we need to generate a wake up to return control to the code waiting
166         // for something to happen and process this socket event
167         wxMacWakeUp();
168     }
169 
170     CFSocketRef m_socket;
171     CFRunLoopSourceRef m_source;
172 
173     wxDECLARE_NO_COPY_CLASS(wxSocketImplMac);
174 };
175 
176 } // anonymous namespace
177 
178 
179 // ----------------------------------------------------------------------------
180 // CoreFoundation implementation of wxSocketManager
181 // ----------------------------------------------------------------------------
182 
183 class wxSocketManagerMac : public wxSocketManager
184 {
185 public:
186     virtual bool OnInit();
187     virtual void OnExit();
188 
CreateSocket(wxSocketBase & wxsocket)189     virtual wxSocketImpl *CreateSocket(wxSocketBase& wxsocket)
190     {
191         return new wxSocketImplMac(wxsocket);
192     }
193 
194     virtual void Install_Callback(wxSocketImpl *socket, wxSocketNotify event);
195     virtual void Uninstall_Callback(wxSocketImpl *socket, wxSocketNotify event);
196 
197 private:
198     // return CFSocket callback mask corresponding to the given event (the
199     // socket parameter is needed because some events are interpreted
200     // differently depending on whether they happen on a server or on a client
201     // socket)
202     static int GetCFCallback(wxSocketImpl *socket, wxSocketNotify event);
203 };
204 
OnInit()205 bool wxSocketManagerMac::OnInit()
206 {
207     // No need to store the main loop again
208     if (gs_mainRunLoop != NULL)
209         return true;
210 
211     // Get the loop for the main thread so our events will actually fire.
212     // The common socket.cpp code will assert if initialize is called from a
213     // secondary thread, otherwise Mac would have the same problems as MSW
214     gs_mainRunLoop = CFRunLoopGetCurrent();
215     if ( !gs_mainRunLoop )
216         return false;
217 
218     CFRetain(gs_mainRunLoop);
219 
220     return true;
221 }
222 
OnExit()223 void wxSocketManagerMac::OnExit()
224 {
225     // Release the reference count, and set the reference back to NULL
226     CFRelease(gs_mainRunLoop);
227     gs_mainRunLoop = NULL;
228 }
229 
230 /* static */
GetCFCallback(wxSocketImpl * socket,wxSocketNotify event)231 int wxSocketManagerMac::GetCFCallback(wxSocketImpl *socket, wxSocketNotify event)
232 {
233     switch ( event )
234     {
235         case wxSOCKET_CONNECTION:
236             return socket->IsServer() ? kCFSocketReadCallBack
237                                       : kCFSocketConnectCallBack;
238 
239         case wxSOCKET_INPUT:
240             return kCFSocketReadCallBack;
241 
242         case wxSOCKET_OUTPUT:
243             return kCFSocketWriteCallBack;
244 
245         case wxSOCKET_LOST:
246             wxFAIL_MSG( "unexpected wxSocketNotify" );
247             return 0;
248 
249         default:
250             wxFAIL_MSG( "unknown wxSocketNotify" );
251             return 0;
252     }
253 }
254 
Install_Callback(wxSocketImpl * socket_,wxSocketNotify event)255 void wxSocketManagerMac::Install_Callback(wxSocketImpl *socket_,
256                                           wxSocketNotify event)
257 {
258     wxSocketImplMac * const socket = static_cast<wxSocketImplMac *>(socket_);
259 
260     CFSocketEnableCallBacks(socket->GetSocket(), GetCFCallback(socket, event));
261 }
262 
Uninstall_Callback(wxSocketImpl * socket_,wxSocketNotify event)263 void wxSocketManagerMac::Uninstall_Callback(wxSocketImpl *socket_,
264                                             wxSocketNotify event)
265 {
266     wxSocketImplMac * const socket = static_cast<wxSocketImplMac *>(socket_);
267 
268     CFSocketDisableCallBacks(socket->GetSocket(), GetCFCallback(socket, event));
269 }
270 
271 // set the wxBase variable to point to CF wxSocketManager implementation so
272 // that the GUI code in utilsexc_cf.cpp could return it from its traits method
273 //
274 // this is very roundabout but necessary to allow us to have different
275 // behaviours in console and GUI applications while avoiding dependencies of
276 // GUI library on the network one
277 extern WXDLLIMPEXP_BASE wxSocketManager *wxOSXSocketManagerCF;
278 
279 static struct OSXManagerSetter
280 {
OSXManagerSetterOSXManagerSetter281     OSXManagerSetter()
282     {
283         static wxSocketManagerMac s_manager;
284         wxOSXSocketManagerCF = &s_manager;
285     }
286 } gs_OSXManagerSetter;
287 
288 // see the relative linker macro in socket.cpp
289 wxFORCE_LINK_THIS_MODULE(osxsocket)
290 
291 #endif // wxUSE_SOCKETS
292