disable the events when we get a notification about socket being ready for IO and reenable them later after performing the IO in the Unix version to avoid continuous flood of ready notifications

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@57796 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Vadim Zeitlin
2009-01-02 21:53:30 +00:00
parent 41285bc82f
commit df21920b80
5 changed files with 137 additions and 85 deletions

View File

@@ -104,21 +104,36 @@ wxSocketError wxSocketImplUnix::GetLastError() const
}
}
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)
{
@@ -130,82 +145,91 @@ void wxSocketImplUnix::OnStateChange(wxSocketNotify event)
void wxSocketImplUnix::OnReadWaiting()
{
char c;
wxASSERT_MSG( m_fd != INVALID_SOCKET, "invalid socket ready for reading?" );
if (m_fd == INVALID_SOCKET)
{
return;
}
// 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);
int num = recv(m_fd, &c, 1, MSG_PEEK);
if (num > 0)
{
OnStateChange(wxSOCKET_INPUT);
}
else
{
if (m_server && m_stream)
// 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 )
{
OnStateChange(wxSOCKET_CONNECTION);
notify = wxSOCKET_CONNECTION;
}
else if (num == 0)
else // check if there is really any input available
{
if (m_stream)
{
/* graceful shutdown */
OnStateChange(wxSOCKET_LOST);
}
else
{
/* Empty datagram received */
OnStateChange(wxSOCKET_INPUT);
}
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;
}
}
else
{
/* 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);
}
}
}
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);
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_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);
}
}
else
{
OnStateChange(wxSOCKET_OUTPUT);
}
}
void wxSocketImplUnix::OnExceptionWaiting()