+// Conditionally make the socket non-blocking for the lifetime of this object.
+class wxSocketUnblocker
+{
+public:
+ wxSocketUnblocker(GSocket *socket, bool unblock = true)
+ : m_socket(socket),
+ m_unblock(unblock)
+ {
+ if ( m_unblock )
+ m_socket->SetNonBlocking(true);
+ }
+
+ ~wxSocketUnblocker()
+ {
+ if ( m_unblock )
+ m_socket->SetNonBlocking(false);
+ }
+
+private:
+ GSocket * const m_socket;
+ bool m_unblock;
+
+ DECLARE_NO_COPY_CLASS(wxSocketUnblocker)
+};
+
+// ============================================================================
+// GSocketManager
+// ============================================================================
+
+GSocketManager *GSocketManager::ms_manager = NULL;
+
+/* static */
+void GSocketManager::Set(GSocketManager *manager)
+{
+ wxASSERT_MSG( !ms_manager, "too late to set manager now" );
+
+ ms_manager = manager;
+}
+
+/* static */
+void GSocketManager::Init()
+{
+ wxASSERT_MSG( !ms_manager, "shouldn't be initialized twice" );
+
+ /*
+ Details: Initialize() creates a hidden window as a sink for socket
+ events, such as 'read completed'. wxMSW has only one message loop
+ for the main thread. If Initialize is called in a secondary thread,
+ the socket window will be created for the secondary thread, but
+ since there is no message loop on this thread, it will never
+ receive events and all socket operations will time out.
+ BTW, the main thread must not be stopped using sleep or block
+ on a semaphore (a bad idea in any case) or socket operations
+ will time out.
+
+ On the Mac side, Initialize() stores a pointer to the CFRunLoop for
+ the main thread. Because secondary threads do not have run loops,
+ adding event notifications to the "Current" loop would have no
+ effect at all, events would never fire.
+ */
+ wxASSERT_MSG( wxIsMainThread(),
+ "sockets must be initialized from the main thread" );
+
+ wxAppConsole * const app = wxAppConsole::GetInstance();
+ wxCHECK_RET( app, "sockets can't be initialized without wxApp" );
+
+ ms_manager = app->GetTraits()->GetSocketManager();
+}
+
+// ==========================================================================
+// GSocketBase
+// ==========================================================================
+
+/* static */
+GSocket *GSocketBase::Create(wxSocketBase& wxsocket)
+{
+ GSocket * const newsocket = new GSocket(wxsocket);
+ if ( !GSocketManager::Get()->Init_Socket(newsocket) )
+ {
+ delete newsocket;
+ return NULL;
+ }
+
+ return newsocket;
+}
+
+GSocketBase::GSocketBase(wxSocketBase& wxsocket)
+ : m_wxsocket(&wxsocket)
+{
+ m_fd = INVALID_SOCKET;
+ m_detected = 0;
+ m_local = NULL;
+ m_peer = NULL;
+ m_error = GSOCK_NOERROR;
+ m_server = false;
+ m_stream = true;
+ m_non_blocking = false;
+
+ SetTimeout(wxsocket.GetTimeout() * 1000);
+
+ m_establishing = false;
+ m_reusable = false;
+ m_broadcast = false;
+ m_dobind = true;
+ m_initialRecvBufferSize = -1;
+ m_initialSendBufferSize = -1;
+}
+
+GSocketBase::~GSocketBase()
+{
+ if (m_fd != INVALID_SOCKET)
+ Shutdown();
+
+ if (m_local)
+ GAddress_destroy(m_local);
+
+ if (m_peer)
+ GAddress_destroy(m_peer);
+
+ // cast is ok as all GSocketBase objects we have in our code are really
+ // GSockets
+ GSocketManager::Get()->Destroy_Socket(static_cast<GSocket *>(this));
+}
+
+/* GSocket_Shutdown:
+ * Disallow further read/write operations on this socket, close
+ * the fd and disable all callbacks.
+ */
+void GSocketBase::Shutdown()
+{
+ if ( m_fd != INVALID_SOCKET )
+ {
+ shutdown(m_fd, 1 /* SD_SEND */);
+ Close();
+ }
+
+ m_detected = GSOCK_LOST_FLAG;
+}
+
+/* GSocket_SetTimeout:
+ * Sets the timeout for blocking calls. Time is expressed in
+ * milliseconds.
+ */
+void GSocketBase::SetTimeout(unsigned long millis)
+{
+#ifdef __WXMSW__
+ m_timeout.tv_sec = (millis / 1000);
+ m_timeout.tv_usec = (millis % 1000) * 1000;
+#else
+ m_timeout = millis;
+#endif
+}
+
+void GSocketBase::NotifyOnStateChange(GSocketEvent event)
+{
+ // GSocketEvent and wxSocketNotify enums have the same elements with the
+ // same values
+ m_wxsocket->OnRequest(static_cast<wxSocketNotify>(event));
+}