socket: use SOCK_NONBLOCK to eliminate extra system call

Every time function `cf_socket_open()` is called to create a socket,
`curlx_nonblock()` is called to make that socket non-blocking. And
`curlx_nonblock()` will cost us 1 or 2 system calls (2 for `fcntl()`, 1
for `ioctl()`, etc.), meanwhile, tucking `SOCK_NONBLOCK` and
`SOCK_CLOEXEC` into the `type` argument for `socket()` is widely
supported across UNIX-like OS: Linux, *BSD, Solaris, etc. With that
ability, we can save 1 or 2 system calls on each socket.

Another change in this PR is to eliminate the redundant
`curlx_nonblock()` call on the socket in `cf_udp_setup_quic()` as that
socket created by `cf_socket_open()` is already non-blocking.

Ref:
https://man7.org/linux/man-pages/man2/socket.2.html
https://man.freebsd.org/cgi/man.cgi?socket(2)
https://man.dragonflybsd.org/?command=socket&section=2
https://man.netbsd.org/socket.2
https://man.openbsd.org/socket
https://docs.oracle.com/cd/E88353_01/html/E37843/socket-3c.html
https://illumos.org/man/3SOCKET/socket
...

Closes #13855
This commit is contained in:
Andy Pan 2024-06-02 14:40:42 +08:00 committed by Daniel Stenberg
parent 97e5e37cc8
commit 3392f0f97e
No known key found for this signature in database
GPG Key ID: 5CC908FDB71E12C2

View File

@ -1016,7 +1016,20 @@ static CURLcode cf_socket_open(struct Curl_cfilter *cf,
(void)data;
DEBUGASSERT(ctx->sock == CURL_SOCKET_BAD);
ctx->started_at = Curl_now();
#ifdef SOCK_NONBLOCK
/* Don't tuck SOCK_NONBLOCK into socktype when opensocket callback is set
* because we wouldn't know how socketype is about to be used in the
* callback, SOCK_NONBLOCK might get factored out before calling socket().
*/
if(!data->set.fopensocket)
ctx->addr.socktype |= SOCK_NONBLOCK;
#endif
result = socket_open(data, &ctx->addr, &ctx->sock);
#ifdef SOCK_NONBLOCK
/* Restore the socktype after the socket is created. */
if(!data->set.fopensocket)
ctx->addr.socktype &= ~SOCK_NONBLOCK;
#endif
if(result)
goto out;
@ -1087,8 +1100,14 @@ static CURLcode cf_socket_open(struct Curl_cfilter *cf,
}
#endif
#ifndef SOCK_NONBLOCK
/* set socket non-blocking */
(void)curlx_nonblock(ctx->sock, TRUE);
#else
if(data->set.fopensocket)
/* set socket non-blocking */
(void)curlx_nonblock(ctx->sock, TRUE);
#endif
ctx->sock_connected = (ctx->addr.socktype != SOCK_DGRAM);
out:
if(result) {
@ -1681,7 +1700,7 @@ out:
}
static CURLcode cf_udp_setup_quic(struct Curl_cfilter *cf,
struct Curl_easy *data)
struct Curl_easy *data)
{
struct cf_socket_ctx *ctx = cf->ctx;
int rc;
@ -1707,7 +1726,11 @@ static CURLcode cf_udp_setup_quic(struct Curl_cfilter *cf,
ctx->sock, ctx->ip.local_ip, ctx->ip.local_port,
ctx->ip.remote_ip, ctx->ip.remote_port);
(void)curlx_nonblock(ctx->sock, TRUE);
/* Currently, cf->ctx->sock is always non-blocking because the only
* caller to cf_udp_setup_quic() is cf_udp_connect() that passes the
* non-blocking socket created by cf_socket_open() to it. Thus, we
* don't need to call curlx_nonblock() in cf_udp_setup_quic() anymore.
*/
switch(ctx->addr.family) {
#if defined(__linux__) && defined(IP_MTU_DISCOVER)
case AF_INET: {