I was working on StacklessIO for Stackless Python. When running the test_socket unittest, I came across a single failure: The testInsideTimeout would fail, with the server receiving a ECONNRESET when trying to write its “done!” string.
Normally this shouldn’t happen. Because even though the client has closed the connection, a RST packet is only sent in response to the send() call, so at the time of calling send all appears well and the call should succeed.
Investigating this further, it turned out to be due to the way I’m implementing timeout.
StacklessIO uses Winsock overlapped IO. A request is issued and then when it is finished a thread waiting on a IOCompletion port will wake up and cause the waiting tasklet to be resumed. To time out, for example, a recv() call, I schedule a Windows timer as well. If it fires before the request is done, the tasklet is woken up with a timeout error. There appears to be no way in the API to cancel a pending IO request, so at this point, the IO is still pending.
Anyway, this all is well and good, but where does the RST come from then? Well, when the timeout occurs, the tasklet wakes up and the socket is closed. And calling closesocket() on a connection with pending IO has at least two effects, only one of which is documented:
- All pending IO is canceled with the WSA_OPERATION_ABORTED error.
- A RST is sent to the remote party
I’ve never seen the latter behaviour documented. But apparently then, calling closesocket() when IO is pending is equivalent to an abortive close().
I’m not sure if this is significant. If a socket call times out, the usual recommendation is to close the connection anyway since the connection may be in an undefined state due to race conditions. But it is a bit annoying all the same.