Skip to content

Commit

Permalink
streams/xp_socket: eliminate poll() when MSG_DONTWAIT is available
Browse files Browse the repository at this point in the history
If there is a zero timeout and MSG_DONTWAIT is available (or the
socket is non-blocking), the poll() call is not necessary, and we can
just call recv() right away.

Before this change:

 poll([{fd=4, events=POLLIN|POLLPRI|POLLERR|POLLHUP}], 1, 0) = 0 (Timeout)
 poll([{fd=4, events=POLLIN|POLLERR|POLLHUP}], 1, 60000) = 1 ([{fd=4, revents=POLLIN}])
 recvfrom(4, "HTTP/1.1 301 Moved Permanently\r\n"..., 8192, MSG_DONTWAIT, NULL, NULL) = 348
 poll([{fd=4, events=POLLIN|POLLPRI|POLLERR|POLLHUP}], 1, 0) = 1 ([{fd=4, revents=POLLIN}])
 recvfrom(4, "", 1, MSG_PEEK, NULL, NULL) = 0

After this change:

 recvfrom(4, 0x7ffe0cc719a0, 1, MSG_PEEK|MSG_DONTWAIT, NULL, NULL) = -1 EAGAIN (Resource temporarily unavailable)
 poll([{fd=4, events=POLLIN|POLLERR|POLLHUP}], 1, 60000) = 1 ([{fd=4, revents=POLLIN}])
 recvfrom(4, "HTTP/1.1 301 Moved Permanently\r\n"..., 8192, MSG_DONTWAIT, NULL, NULL) = 348
 recvfrom(4, "", 1, MSG_PEEK|MSG_DONTWAIT, NULL, NULL) = 0

The first poll() is replaced by recvfrom(), and the third poll() is
omitted completely.

ext/openssl/xp_ssl: eliminate poll() when MSG_DONTWAIT is available

If there is a zero timeout and MSG_DONTWAIT is available (or the
socket is non-blocking), the poll() call is not necessary, and we can
just call recv() right away.

Closes GH-8092.
  • Loading branch information
MaxKellermann authored and devnexen committed Jun 18, 2022
1 parent 7d6821a commit 2d98631
Show file tree
Hide file tree
Showing 3 changed files with 18 additions and 4 deletions.
6 changes: 6 additions & 0 deletions NEWS
Expand Up @@ -28,6 +28,9 @@ PHP NEWS
- ODBC:
. Fixed handling of single-key connection strings. (Calvin Buckley)

- OpenSSL:
. Discard poll calls on socket when no timeout/non blocking/MSG_DONTWAIT. (Max Kellermann)

- PCRE:
. Implemented FR #77726 (Allow null character in regex patterns). (cmb)

Expand All @@ -37,6 +40,9 @@ PHP NEWS
- Standard:
. Deprecated utf8_encode() and utf8_decode(). (Rowan Tommins)

- Streams:
. Discard poll calls on socket when no timeout/non blocking/MSG_DONTWAIT. (Max Kellermann)

09 Jun 2022, PHP 8.2.0alpha1

- CLI:
Expand Down
11 changes: 9 additions & 2 deletions ext/openssl/xp_ssl.c
Expand Up @@ -46,6 +46,10 @@
#undef X509_EXTENSIONS
#endif

#ifndef MSG_DONTWAIT
# define MSG_DONTWAIT 0
#endif

/* Flags for determining allowed stream crypto methods */
#define STREAM_CRYPTO_IS_CLIENT (1<<0)
#define STREAM_CRYPTO_METHOD_SSLv2 (1<<1)
Expand Down Expand Up @@ -2395,7 +2399,10 @@ static int php_openssl_sockop_set_option(php_stream *stream, int option, int val

if (sslsock->s.socket == -1) {
alive = 0;
} else if (php_pollfd_for(sslsock->s.socket, PHP_POLLREADABLE|POLLPRI, &tv) > 0) {
} else if ((!sslsock->ssl_active && value == 0 && (MSG_DONTWAIT || !sslsock->s.is_blocked)) ||
php_pollfd_for(sslsock->s.socket, PHP_POLLREADABLE|POLLPRI, &tv) > 0) {
/* the poll() call was skipped if the socket is non-blocking (or MSG_DONTWAIT is available) and if the timeout is zero */
/* additionally, we don't use this optimization if SSL is active because in that case, we're not using MSG_DONTWAIT */
if (sslsock->ssl_active) {
int n = SSL_peek(sslsock->ssl_handle, &buf, sizeof(buf));
if (n <= 0) {
Expand All @@ -2413,7 +2420,7 @@ static int php_openssl_sockop_set_option(php_stream *stream, int option, int val
alive = 0;
}
}
} else if (0 == recv(sslsock->s.socket, &buf, sizeof(buf), MSG_PEEK) && php_socket_errno() != EAGAIN) {
} else if (0 == recv(sslsock->s.socket, &buf, sizeof(buf), MSG_PEEK|MSG_DONTWAIT) && php_socket_errno() != EAGAIN) {
alive = 0;
}
}
Expand Down
5 changes: 3 additions & 2 deletions main/streams/xp_socket.c
Expand Up @@ -337,15 +337,16 @@ static int php_sockop_set_option(php_stream *stream, int option, int value, void

if (sock->socket == -1) {
alive = 0;
} else if (php_pollfd_for(sock->socket, PHP_POLLREADABLE|POLLPRI, &tv) > 0) {
} else if ((value == 0 && (MSG_DONTWAIT || !sock->is_blocked)) || php_pollfd_for(sock->socket, PHP_POLLREADABLE|POLLPRI, &tv) > 0) {
/* the poll() call was skipped if the socket is non-blocking (or MSG_DONTWAIT is available) and if the timeout is zero */
#ifdef PHP_WIN32
int ret;
#else
ssize_t ret;
#endif
int err;

ret = recv(sock->socket, &buf, sizeof(buf), MSG_PEEK);
ret = recv(sock->socket, &buf, sizeof(buf), MSG_PEEK|MSG_DONTWAIT);
err = php_socket_errno();
if (0 == ret || /* the counterpart did properly shutdown*/
(0 > ret && err != EWOULDBLOCK && err != EAGAIN && err != EMSGSIZE)) { /* there was an unrecoverable error */
Expand Down

0 comments on commit 2d98631

Please sign in to comment.