virtual wxSocketError GetLastError() const;
+ virtual void ReenableEvents(wxSocketEventFlags flags)
+ {
+ // enable the notifications about input/output being available again in
+ // case they were disabled by OnRead/WriteWaiting()
+ //
+ // notice that we'd like to enable the events here only if there is
+ // nothing more left on the socket right now as otherwise we're going
+ // to get a "ready for whatever" notification immediately (well, during
+ // the next event loop iteration) and disable the event back again
+ // which is rather inefficient but unfortunately doing it like this
+ // doesn't work because the existing code (e.g. src/common/sckipc.cpp)
+ // expects to keep getting notifications about the data available from
+ // the socket even if it didn't read all the data the last time, so we
+ // absolutely have to continue generating them
+ EnableEvents(flags);
+ }
+
// wxFDIOHandler methods
virtual void OnReadWaiting();
virtual void OnWriteWaiting();
}
// enable or disable notifications for socket input/output events
- void EnableEvents() { DoEnableEvents(true); }
- void DisableEvents() { DoEnableEvents(false); }
+ void EnableEvents(int flags = wxSOCKET_INPUT_FLAG | wxSOCKET_OUTPUT_FLAG)
+ { DoEnableEvents(flags, true); }
+ void DisableEvents(int flags = wxSOCKET_INPUT_FLAG | wxSOCKET_OUTPUT_FLAG)
+ { DoEnableEvents(flags, false); }
// really enable or disable socket input/output events
- void DoEnableEvents(bool enable);
+ void DoEnableEvents(int flags, bool enable);
protected:
// descriptors for input and output event notification channels associated
// down the socket if the event is wxSOCKET_LOST
void OnStateChange(wxSocketNotify event);
+ // check if there is any input available, return 1 if yes, 0 if no or -1 on
+ // error
+ int CheckForInput();
+
+
// give it access to our m_fds
friend class wxSocketFDBasedManager;
};
~wxSocketReadGuard()
{
m_socket->m_reading = false;
+
+ m_socket->m_impl->ReenableEvents(wxSOCKET_INPUT_FLAG);
}
private:
wxASSERT_MSG( !m_socket->m_writing, "write reentrancy?" );
m_socket->m_writing = true;
+
+ m_socket->m_impl->ReenableEvents(wxSOCKET_OUTPUT_FLAG);
}
~wxSocketWriteGuard()
WX_SOCKLEN_T fromlen = sizeof(from);
const SOCKET fd = accept(m_fd, &from.addr, &fromlen);
+ // accepting is similar to reading in the sense that it resets "ready for
+ // read" flag on the socket
+ ReenableEvents(wxSOCKET_INPUT_FLAG);
+
if ( fd == INVALID_SOCKET )
return NULL;
}
}
-/*
- * Disallow further read/write operations on this socket, close
- * the fd and disable all callbacks.
- */
void wxSocketImpl::Shutdown()
{
if ( m_fd != INVALID_SOCKET )
// send the wx event if enabled and we're interested in it
if ( m_notify && (m_eventmask & flag) && m_handler )
{
- // If we are in the middle of a R/W operation, do not propagate events
- // to users. Also, filter 'late' events which are no longer valid.
- if ( notification == wxSOCKET_INPUT )
- {
- if ( m_reading || !m_impl->Select(wxSOCKET_INPUT_FLAG) )
- return;
- }
- else if ( notification == wxSOCKET_OUTPUT )
- {
- if ( m_writing || !m_impl->Select(wxSOCKET_OUTPUT_FLAG) )
- return;
- }
-
wxSocketEvent event(m_id);
event.m_event = notification;
event.m_clientData = m_clientData;
}
}
-void wxSocketImplUnix::DoEnableEvents(bool flag)
+void wxSocketImplUnix::DoEnableEvents(int flags, bool enable)
{
wxSocketManager * const manager = wxSocketManager::Get();
- if ( flag )
+ if ( enable )
{
- manager->Install_Callback(this, wxSOCKET_INPUT);
- manager->Install_Callback(this, wxSOCKET_OUTPUT);
+ if ( flags & wxSOCKET_INPUT_FLAG )
+ manager->Install_Callback(this, wxSOCKET_INPUT);
+ if ( flags & wxSOCKET_OUTPUT_FLAG )
+ manager->Install_Callback(this, wxSOCKET_OUTPUT);
}
else // off
{
- manager->Uninstall_Callback(this, wxSOCKET_INPUT);
- manager->Uninstall_Callback(this, wxSOCKET_OUTPUT);
+ if ( flags & wxSOCKET_INPUT_FLAG )
+ manager->Uninstall_Callback(this, wxSOCKET_INPUT);
+ if ( flags & wxSOCKET_OUTPUT_FLAG )
+ manager->Uninstall_Callback(this, wxSOCKET_OUTPUT);
}
}
+int wxSocketImplUnix::CheckForInput()
+{
+ char c;
+ int rc;
+ do
+ {
+ rc = recv(m_fd, &c, 1, MSG_PEEK);
+ } while ( rc == -1 && errno == EINTR );
+
+ return rc;
+}
void wxSocketImplUnix::OnStateChange(wxSocketNotify event)
{
void wxSocketImplUnix::OnReadWaiting()
{
- char c;
-
- if (m_fd == INVALID_SOCKET)
- {
- return;
- }
-
- int num = recv(m_fd, &c, 1, MSG_PEEK);
-
- if (num > 0)
- {
- OnStateChange(wxSOCKET_INPUT);
- }
- else
- {
- if (m_server && m_stream)
- {
- OnStateChange(wxSOCKET_CONNECTION);
- }
- else if (num == 0)
+ wxASSERT_MSG( m_fd != INVALID_SOCKET, "invalid socket ready for reading?" );
+
+ // we need to disable the read notifications until we read all the data
+ // already available for the socket, otherwise we're going to keep getting
+ // them continuously which is worse than inefficient: as IO notifications
+ // have higher priority than idle events in e.g. GTK+, our pending events
+ // whose handlers typically call Read() which would consume the data and so
+ // stop the notifications flood would never be dispatched at all if the
+ // notifications were not disabled
+ DisableEvents(wxSOCKET_INPUT_FLAG);
+
+
+ // find out what are we going to notify about exactly
+ wxSocketNotify notify;
+
+ // TCP listening sockets become ready for reading when there is a pending
+ // connection
+ if ( m_server && m_stream )
{
- if (m_stream)
- {
- /* graceful shutdown */
- OnStateChange(wxSOCKET_LOST);
- }
- else
- {
- /* Empty datagram received */
- OnStateChange(wxSOCKET_INPUT);
- }
+ notify = wxSOCKET_CONNECTION;
}
- else
+ else // check if there is really any input available
{
- /* Do not throw a lost event in cases where the socket isn't really lost */
- if ((errno == EWOULDBLOCK) || (errno == EAGAIN) || (errno == EINTR))
- {
- OnStateChange(wxSOCKET_INPUT);
- }
- else
- {
- OnStateChange(wxSOCKET_LOST);
- }
+ switch ( CheckForInput() )
+ {
+ case 1:
+ notify = wxSOCKET_INPUT;
+ break;
+
+ case 0:
+ // reading 0 bytes for a TCP socket means that the connection
+ // was closed by peer but for UDP it just means that we got an
+ // empty datagram
+ notify = m_stream ? wxSOCKET_LOST : wxSOCKET_INPUT;
+ break;
+
+ default:
+ wxFAIL_MSG( "unexpected CheckForInput() return value" );
+ // fall through
+
+ case -1:
+ if ( GetLastError() == wxSOCKET_WOULDBLOCK )
+ {
+ // just a spurious wake up
+ EnableEvents(wxSOCKET_INPUT_FLAG);
+ return;
+ }
+
+ notify = wxSOCKET_LOST;
+ }
}
- }
+
+ OnStateChange(notify);
}
void wxSocketImplUnix::OnWriteWaiting()
{
- if (m_establishing && !m_server)
- {
- int error;
- SOCKOPTLEN_T len = sizeof(error);
+ wxASSERT_MSG( m_fd != INVALID_SOCKET, "invalid socket ready for writing?" );
- m_establishing = false;
+ // see comment in the beginning of OnReadWaiting() above
+ DisableEvents(wxSOCKET_OUTPUT_FLAG);
- getsockopt(m_fd, SOL_SOCKET, SO_ERROR, (char*)&error, &len);
- if (error)
+ // check whether this is a notification for the completion of a
+ // non-blocking connect()
+ if ( m_establishing && !m_server )
{
- OnStateChange(wxSOCKET_LOST);
- }
- else
- {
- OnStateChange(wxSOCKET_CONNECTION);
- /* We have to fire this event by hand because CONNECTION (for clients)
- * and OUTPUT are internally the same and we just disabled CONNECTION
- * events with the above macro.
- */
- OnStateChange(wxSOCKET_OUTPUT);
+ m_establishing = false;
+
+ // check whether we connected successfully
+ int error;
+ SOCKOPTLEN_T len = sizeof(error);
+
+ getsockopt(m_fd, SOL_SOCKET, SO_ERROR, (char*)&error, &len);
+
+ if ( error )
+ {
+ OnStateChange(wxSOCKET_LOST);
+ return;
+ }
+
+ OnStateChange(wxSOCKET_CONNECTION);
}
- }
- else
- {
+
OnStateChange(wxSOCKET_OUTPUT);
- }
}
void wxSocketImplUnix::OnExceptionWaiting()