]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
socket: use SOCK_NONBLOCK to eliminate extra system call
authorAndy Pan <i@andypan.me>
Sun, 2 Jun 2024 06:40:42 +0000 (14:40 +0800)
committerDaniel Stenberg <daniel@haxx.se>
Tue, 4 Jun 2024 15:51:28 +0000 (17:51 +0200)
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

lib/cf-socket.c

index 9ec496874355399e20376d7bf00576605cd6315e..22827d37538569b8e3b0049a23b99e4e8c51bf53 100644 (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: {