[issue20924] openssl init 100% CPU utilization

Roman O. Vlasov report at bugs.python.org
Fri Mar 28 19:39:28 CET 2014


Roman O. Vlasov added the comment:

To reproduce the 100% CPU load problem, we used a simple python TLS client on separate linux PC with Traffic control utility (tc):
	tc qdisc change dev eth0 root netem delay 5000ms

After in-depth analyzing, we realized that _ssl.c behaves differently depening on socket.timeout:

1) sock.timeout set to None, that is equivalent to s->socket_timeout==-1 in _ssl.c produces 100% CPU load if client works on bad long delay channel.
The problem is this case is that the do{} loop runs in PySSL_SSLdo_handshake() calling check_socket_and_wait_for_timeout() which immediatly returns SOCKET_IS_BLOCKING because s->sock_timeout==-1. 

2) sock.timeout set to 0 (non-blocking) makes _ssl.c immediatly return with error:
	_ssl.c: The operation did not complete

3) socket.timeout set to any positive value makes _ssl.c wait socket on select (producing no CPU load).

By default, accept() returns blocking socket with timeout set to None (1st case)

Below are some code details:

Our server class is inherited from asyncore.dispatcher.
In __init__ it executes the following statements (ripped), 
creating listening socket, accepting client connection and then doing wrap_socket:

-----------------------------------------------------------
asyncore.py: 
	sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
	sock.setblocking(0)
	sock.setsockopt(
		socket.SOL_SOCKET, 
		socket.SO_REUSEADDR,
		socket.getsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR) | 1
	)
	sock.bind(self.server_address)
	sock.listen(5)
	asyncore.loop(...)
	select.select(...)  # wait for client connection

# Here client connects

	conn, addr = sock.accept()
# conn.gettimeout() returns None which means blocking socket
	ssl_sock = ssl.wrap_socket(
            conn
            , server_side = server_side
            , certfile = certfile
            , cert_reqs = cert_reqs
            , ssl_version=ssl.PROTOCOL_SSLv3
        )

	File "C:\Python27\Lib\ssl.py", line 399, in wrap_socket
	File "C:\Python27\Lib\ssl.py", line 152, in __init__
		self.do_handshake()
	File "C:\Python27\Lib\ssl.py", line 315, in do_handshake
		self._sslobj.do_handshake()
_ssl.c:
	PySSL_SSLdo_handshake():
		// wants to read more data from socket
		if (err == SSL_ERROR_WANT_READ) {
			sockstate = check_socket_and_wait_for_timeout(self->Socket, 0);
		...
	check_socket_and_wait_for_timeout():
		/* Nothing to do unless we're in timeout mode (not non-blocking) */
		if (s->sock_timeout < 0.0)
			return SOCKET_IS_BLOCKING;		// <-- this is 1st case producing 100% CPU load
		else if (s->sock_timeout == 0.0)
			return SOCKET_IS_NONBLOCKING;	// <-- this is 2nd case returning error immediatly

We think that anyone who follows standard Python documentation (http://docs.python.org/2/library/ssl.html#server-side-operation) will get the same result if using client with delay>1000ms

----------
nosy: +rv

_______________________________________
Python tracker <report at bugs.python.org>
<http://bugs.python.org/issue20924>
_______________________________________


More information about the Python-bugs-list mailing list