http: prevent race condition between worker thread and I/O thread

This prevents a losing race condition that could prevent the server
from reading requests from an HTTP client.

A connected socket can either be written to or read from based on the
result of GenerateWaitSockets(). That method checks the HTTPRemoteClient
flag m_send_ready. If it's `true` the implication is that there is
data in the client's send buffer ready to go. Once that data is sent
and the buffer is empty, MaybeSendBytesFromBuffer() sets it `false` again.

The sad case was when a worker thread calling WriteReply() adds
data to the send buffer, but before it sets m_send_ready to `true`,
the I/O thread sends that data and empties the buffer. With the
buffer unexpectedly empty, WriteReply() sets m_send_ready to `true`.

The effect of this is that the socket will stay in "write" mode
with nothing to write. With nothing to write, MaybeSendBytesFromBuffer()
never sets it back to `false` and the socket is stuck forever.
This commit is contained in:
Matthew Zipkin
2026-06-26 16:33:14 -04:00
parent 57b3bf8496
commit 73da2a8a52
2 changed files with 24 additions and 10 deletions

View File

@@ -483,11 +483,14 @@ public:
/// @}
/**
* Set true by worker threads after writing a response to m_send_buffer.
* Set false by the HTTPServer I/O thread after flushing m_send_buffer.
* Checked in the HTTPServer I/O loop to avoid locking m_send_mutex if there's nothing to send.
*/
std::atomic_bool m_send_ready{false};
* Set true by worker threads after writing a response to m_send_buffer.
* Set false by the HTTPServer I/O thread after flushing m_send_buffer.
* Checked in the HTTPServer I/O loop to decide whether to poll the socket for
* writeability or readability.
* Guarded by m_send_mutex so it stays consistent with m_send_buffer's emptiness:
* the two must always be updated together under the same lock.
*/
bool m_send_ready GUARDED_BY(m_send_mutex){false};
/**
* Mutex that serializes the Send() and Recv() calls on `m_sock`. Reading