If an exception is raised inside a ThreadedEchoServer context manager (like when a test fails due to self.AssertEqual(...) failing), the whole thing would hang due to a deadlock. This fixes that.
Example to reproduce:
def test_config_file_trust_store(self):
server, client_config = limbo_server("webpki::san::exact-localhost-ip-san")
with server:
client_context = stdlib.STDLIB_BACKEND.client_context(client_config)
client_sock = client_context.connect(server.socket.getsockname())
self.assertEqual(True, False) # This is where the test hangs indefinitely
client_sock.close()
Explanation:
The socket server thread spawns a connection handler thread on a new connection, and then calls .join() on it to wait for it to finish. The handler thread finishes either when there is an error, or its socket is closed.
The problem happens when an exception occurs inside the ThreadedEchoServer context manager. This calls the __exit__() method, which signals the server thread to stop (by setting ThreadedEchoServer.active = False). It then tries to join the server thread, waiting for it to finish. However, if the server thread is trying to join the handler thread when this happens, it will be stuck there unless the handler's connection is closed by the client.
Since an exception can be raised inside a test, this means that it can happen before the connection is closed, which leaves the handler thread running indefinitely, which leaves the server thread waiting indefinitely, which leaves the test thread waiting indefinitely (via it trying to join the server thread during __exit__()).
This fixes that by checking during the message processing loop in the handler thread if the server thread has been deactivated, and stopping if it has.
Description
If an exception is raised inside a
ThreadedEchoServer
context manager (like when a test fails due toself.AssertEqual(...)
failing), the whole thing would hang due to a deadlock. This fixes that. Example to reproduce:Explanation:
The socket server thread spawns a connection handler thread on a new connection, and then calls
.join()
on it to wait for it to finish. The handler thread finishes either when there is an error, or its socket is closed.The problem happens when an exception occurs inside the
ThreadedEchoServer
context manager. This calls the__exit__()
method, which signals the server thread to stop (by settingThreadedEchoServer.active = False
). It then tries to join the server thread, waiting for it to finish. However, if the server thread is trying to join the handler thread when this happens, it will be stuck there unless the handler's connection is closed by the client.Since an exception can be raised inside a test, this means that it can happen before the connection is closed, which leaves the handler thread running indefinitely, which leaves the server thread waiting indefinitely, which leaves the test thread waiting indefinitely (via it trying to join the server thread during
__exit__()
).This fixes that by checking during the message processing loop in the handler thread if the server thread has been deactivated, and stopping if it has.