Skip to content

Conversation

@clue
Copy link
Member

@clueclue commented Aug 23, 2020

Multiple socket servers can listen on the same socket address (using
SO_REUSEPORT or shared file descriptors). Accordingly, when a new
connection is incoming, multiple event-loops could report the socket to
be readable and try to a accept() an incoming connection from the same
socket.

This wouldn't be an issue with the underlying accept() system call.
The first call would succeed and subsequent calls would report EAGAIN
or EWOULDBLOCK for the other servers given the socket resource is in
non-blocking mode.

However, PHP's stream_socket_accept() implementation first runs a
poll() on the socket resource before performing an accept(). This
means multiple instances listening on the same address could end up in a
race condition where some may be "stuck" in the pending poll().
See https://github.com/php/php-src/blob/91fbd12d5736b3cc9fc6bc2545e877dd65be1f6c/main/network.c#L707

We work around this by specifiying a 0 timeout to ensure the poll()
doesn't block in this case. This allows all servers to either complete
successfully or report an error and continue processing right away.

Additionally, this also fixes two test failures for PHP 8. Instead of using an invalid (closed) socket server to test error reporting, we now use a socket that doesn't have a pending connection (refs #243). I'll file another PR address the last test failures on PHP 8, but this is currently blocked by #243.

Among others, this is a prerequisite for #164

Multiple socket servers can listen on the same socket address (using `SO_REUSEPORT` or shared file descriptors). Accordingly, when a new connection is incoming, multiple event-loops could report the socket to be readable and try to a `accept()` an incoming connection from the same socket. This wouldn't be an issue with the underlying `accept()` system call. The first call would succeed and subsequent calls would report `EAGAIN` or `EWOULDBLOCK` for the other servers given the socket resource is in non-blocking mode. However, PHP's `stream_socket_accept()` implementation first runs a `poll()` on the socket resource before performing an `accept()`. This means multiple instances listening on the same address could end up in a race condition where some may be "stuck" in the pending `poll()`. We work around this by specifiying a `0` timeout to ensure the `poll()` doesn't block in this case. This allows all servers to either complete successfully or report an error and continue processing right away.
@clueclue added the bug label Aug 23, 2020
@clueclue added this to the v1.6.0 milestone Aug 23, 2020
@clueclue requested a review from jsorAugust 23, 2020 15:54
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@clue@jsor@WyriHaximus