[Common-cvs] netio/platform/posix sockimp.cpp,1.103,1.104
atin at helixcommunity.org atin at helixcommunity.orgUpdate of /cvsroot/common/netio/platform/posix
In directory cvs01.internal.helixcommunity.org:/tmp/cvs-serv7233/platform/posix
Modified Files:
sockimp.cpp
Log Message:
Synopsis
========
if a player sent a TEARDOWN during the course of a session where the server is
stuck in a re-transmission (sendmsg() returns an EWOULDBLOCK/EAGAIN) loop due
to some reason, the streamer process with the connection would spin out and
consume close to 100% cpu.
Branches: SERVER_12_1, SERVER_CURRENT
Reviewer: ckarusala
Description
===========
when the client closes the socket it generally means that the server's socket
read/recvfrom method will return zero bytes. when this happens the socket
state is set to HX_SOCK_STATE_CLOSED inside CHXSocket::DoRead().
the problem is that there wasn't a check inside the CHXSocket::Close() method
to see if the socket is in the HX_SOCK_STATE_CLOSED state and if there is some
pending data to be written out to the socket the socket is set to linger until
the data can be written out.
so essentially, the select() returns that the socket is ready to be written
to, so the server writes to the socket, only to return back with a
EWOULDBLOCK. so it resets the flags in the select() to notify if the socket is
ready for writing again. the next select() it returns back stating once again
that the socket is ready for writing. this loop of write notification and
write failure keeps on indefinitely for every mainloop() iteration and because
the socket is forever ready to be written to, the select() returns immediately
thus causing high mainloop iterations and consequently high cpu usage.
fix:
====
add the check for socket state != HX_SOCK_STATE_CLOSED in Close() to make it
linger only if the socket is not closed.
Files Affected
==============
common/netio/platform/posix/sockimp.cpp
Testing Performed
=================
Unit Tests:
N/A
Integration Tests:
(1) solaris/linux/windows: as described in the repro steps in the pr using
the NEGiES packet filter tool.
(1) run unmodified server.
request clip from client.
start filter.
wait for a few seconds.
stop client.
run a system tracing tool on the server.
u will see the socket options being set to SO_LINGER
all subsequent select() calls return with 1 socket being ready for
writing and all sendmsg() calls return EAGAIN/EWOUDBLOCK.
(i haven't figured out why the select() calls return that the
socket is ready for writing even tho it is blocked.)
observe the cpu usage for the responsible streams is close to 100%.
stop the filter after 15 seconds.
observe that the cpu usage drops back to zero.
(2) repeat test (1) with the modified server.
as expected after the client is stopped the server closes the socket
and so there aren't any blocked sendmsg() calls.
(3) run test (1) but don't stop the client.
it can be observed that there is no spin out with the original or
modified server, because the socket isn't closed and so even though
the sendmsg() returns with a EWOULDBLOCK, the next select() returns
with the socket not being ready for writing (which is how it is
normally).
Leak Tests:
N/A
Performance Tests:
N/A
Platforms Tested: sunos-5.10-sparc-server, linux-rhel4-i686, win32-i386-vc7
Build verified: sunos-5.10-sparc-server, linux-rhel4-i686, win32-i386-vc7
QA Hints
========
None
Index: sockimp.cpp
===================================================================
RCS file: /cvsroot/common/netio/platform/posix/sockimp.cpp,v
retrieving revision 1.103
retrieving revision 1.104
diff -u -d -r1.103 -r1.104
--- sockimp.cpp 28 Jul 2008 22:56:01 -0000 1.103
+++ sockimp.cpp 3 Apr 2009 18:19:15 -0000 1.104
@@ -1022,7 +1022,12 @@
m_pSock4->Close();
}
#endif
- if (HX_SOCK_VALID(m_sock) && !m_pWriteQueue->IsEmpty())
+ // if a socket is ready for reading and a read returns zero bytes
+ // then it generally means that the socket has been closed from
+ // the peer's side, so its ok to close the socket from our side too.
+ if (HX_SOCK_VALID(m_sock) &&
+ m_sock.state != HX_SOCK_STATE_CLOSED &&
+ !m_pWriteQueue->IsEmpty())
{
UINT32 uLinger = LINGER_OFF;
if (hx_getsockopt(&m_sock, HX_SOCKOPT_LINGER, &uLinger) != 0)