File 2022-Implement-TCP_USER_TIMEOUT-for-socket.patch of Package erlang

From 94b71711959b2ca07efa0d9b1b84461c75ef2433 Mon Sep 17 00:00:00 2001
From: Raimo Niskanen <raimo@erlang.org>
Date: Mon, 17 Nov 2025 15:36:28 +0100
Subject: [PATCH 2/4] Implement TCP_USER_TIMEOUT for `socket`

Update the documentation to mention this option
and some other that were overlooked.
---
 erts/emulator/nifs/common/prim_socket_int.h |  5 ++
 erts/emulator/nifs/common/prim_socket_nif.c | 85 ++++++++++++++++++++-
 lib/kernel/doc/guides/socket_usage.md       | 23 +++---
 lib/kernel/src/socket.erl                   | 10 +++
 4 files changed, 112 insertions(+), 11 deletions(-)

diff --git a/erts/emulator/nifs/common/prim_socket_int.h b/erts/emulator/nifs/common/prim_socket_int.h
index 919cdaff5e..af8e5caab8 100644
--- a/erts/emulator/nifs/common/prim_socket_int.h
+++ b/erts/emulator/nifs/common/prim_socket_int.h
@@ -567,6 +567,11 @@ extern BOOLEAN_T esock_getopt_int(SOCKET sock,
                                   int    opt,
                                   int*   valP);
 
+extern BOOLEAN_T esock_getopt_uint(SOCKET        sock,
+                                   int           level,
+                                   int           opt,
+                                   unsigned int *valP);
+
 
 /* ** Socket Registry functions *** */
 extern void esock_send_reg_add_msg(ErlNifEnv*   env,
diff --git a/erts/emulator/nifs/common/prim_socket_nif.c b/erts/emulator/nifs/common/prim_socket_nif.c
index 7a7ad9c272..4719e43e28 100644
--- a/erts/emulator/nifs/common/prim_socket_nif.c
+++ b/erts/emulator/nifs/common/prim_socket_nif.c
@@ -1310,6 +1310,10 @@ static ERL_NIF_TERM esock_getopt_int_opt(ErlNifEnv*       env,
                                          ESockDescriptor* descP,
                                          int              level,
                                          int              opt);
+static ERL_NIF_TERM esock_getopt_uint_opt(ErlNifEnv*       env,
+                                          ESockDescriptor* descP,
+                                          int              level,
+                                          int              opt);
 static ERL_NIF_TERM esock_getopt_size_opt(ErlNifEnv*       env,
                                           ESockDescriptor* descP,
                                           int              level,
@@ -1719,6 +1723,11 @@ static ERL_NIF_TERM esock_setopt_int_opt(ErlNifEnv*       env,
                                          int              level,
                                          int              opt,
                                          ERL_NIF_TERM     eVal);
+static ERL_NIF_TERM esock_setopt_uint_opt(ErlNifEnv*       env,
+                                          ESockDescriptor* descP,
+                                          int              level,
+                                          int              opt,
+                                          ERL_NIF_TERM     eVal);
 #if (defined(SO_RCVTIMEO) || defined(SO_SNDTIMEO))      \
     && defined(ESOCK_USE_RCVSNDTIMEO)
 static ERL_NIF_TERM esock_setopt_timeval_opt(ErlNifEnv*       env,
@@ -3762,7 +3771,14 @@ static struct ESockOpt optLevelTCP[] =
 #endif
         &esock_atom_nopush},
         {0, NULL, NULL, &esock_atom_syncnt},
-        {0, NULL, NULL, &esock_atom_user_timeout}
+        {
+#ifdef TCP_USER_TIMEOUT
+            TCP_USER_TIMEOUT,
+            esock_setopt_uint_opt, esock_getopt_uint_opt,
+#else
+            0, NULL, NULL,
+#endif
+            &esock_atom_user_timeout}
 
     };
 
@@ -8286,6 +8302,31 @@ ERL_NIF_TERM esock_setopt_int_opt(ErlNifEnv*       env,
 
 
 
+/* esock_setopt_uint_opt - set an option that has an unsigned integer value
+ */
+
+static
+ERL_NIF_TERM esock_setopt_uint_opt(ErlNifEnv*       env,
+                                   ESockDescriptor* descP,
+                                   int              level,
+                                   int              opt,
+                                   ERL_NIF_TERM     eVal)
+{
+    ERL_NIF_TERM result;
+    unsigned int val;
+
+    if (GET_UINT(env, eVal, &val)) {
+        result =
+            esock_setopt_level_opt(env, descP, level, opt,
+                                   &val, sizeof(val));
+    } else {
+        result = esock_make_invalid(env, esock_atom_value);
+    }
+    return result;
+}
+
+
+
 /* esock_setopt_str_opt - set an option that has an string value
  */
 
@@ -9892,6 +9933,24 @@ ERL_NIF_TERM esock_getopt_int_opt(ErlNifEnv*       env,
 
 
 
+/* esock_getopt_uint_opt - get an unsigned integer option
+ */
+static
+ERL_NIF_TERM esock_getopt_uint_opt(ErlNifEnv*       env,
+                                   ESockDescriptor* descP,
+                                   int              level,
+                                   int              opt)
+{
+    unsigned int val;
+
+    if (! esock_getopt_uint(descP->sock, level, opt, &val))
+        return esock_make_error_errno(env, sock_errno());
+
+    return esock_make_ok2(env, MKUI(env, val));
+}
+
+
+
 /* esock_getopt_int - get an integer option
  */
 extern
@@ -9916,6 +9975,30 @@ BOOLEAN_T esock_getopt_int(SOCKET           sock,
 
 
 
+/* esock_getopt_uint - get an unsigned integer option
+ */
+extern
+BOOLEAN_T esock_getopt_uint(SOCKET           sock,
+                            int              level,
+                            int              opt,
+                            unsigned int    *valP)
+{
+    unsigned int val = 0;
+    SOCKOPTLEN_T valSz = sizeof(val);
+
+#ifdef __WIN32__
+    if (sock_getopt(sock, level, opt, (char*) &val, &valSz) != 0)
+#else
+    if (sock_getopt(sock, level, opt, &val, &valSz) != 0)
+#endif
+        return FALSE;
+
+    *valP = val;
+    return TRUE;
+}
+
+
+
 static
 ERL_NIF_TERM esock_getopt_size_opt(ErlNifEnv*       env,
                                    ESockDescriptor* descP,
diff --git a/lib/kernel/doc/guides/socket_usage.md b/lib/kernel/doc/guides/socket_usage.md
index 06184c9633..5efdb63712 100644
--- a/lib/kernel/doc/guides/socket_usage.md
+++ b/lib/kernel/doc/guides/socket_usage.md
@@ -626,16 +626,19 @@ _Table: ipv6 options_
 [](){: #socket_options_tcp }
 Options for level `tcp`:
 
-| Option Name | Value Type | Set | Get | Other Requirements and comments                                                                          |
-| ----------- | ---------- | --- | --- | -------------------------------------------------------------------------------------------------------- |
-| congestion  | string()   | yes | yes | none                                                                                                     |
-| cork        | boolean()  | yes | yes | 'nopush' one some platforms (FreeBSD)                                                                    |
-| keepcnt     | integer()  | yes | yes | On Windows (at least), it is illegal to set to a value greater than 255.                                 |
-| keepidle    | integer()  | yes | yes | none                                                                                                     |
-| keepintvl   | integer()  | yes | yes | none                                                                                                     |
-| maxseg      | integer()  | yes | yes | Set not allowed on all platforms.                                                                        |
-| nodelay     | boolean()  | yes | yes | none                                                                                                     |
-| nopush      | boolean()  | yes | yes | 'cork' on some platforms (Linux). On Darwin this has a different meaning than on, for instance, FreeBSD. |
+| Option Name  | Value Type     | Set | Get | Other Requirements and comments                      |
+| ------------ | -------------- | --- | --- | ---------------------------------------------------- |
+| congestion   | string()       | yes | yes | none                                                 |
+| cork         | boolean()      | yes | yes | 'nopush' one some platforms (FreeBSD)                |
+| keepcnt      | integer()      | yes | yes | On Windows (at least), it is illegal to set to       |
+|              |                |     |     | a value greater than 255.                            |
+| keepidle     | integer()      | yes | yes | none                                                 |
+| keepintvl    | integer()      | yes | yes | none                                                 |
+| maxseg       | integer()      | yes | yes | Set not allowed on all platforms.                    |
+| nodelay      | boolean()      | yes | yes | none                                                 |
+| nopush       | boolean()      | yes | yes | 'cork' on some platforms (Linux). On Darwin this has |
+|              |                |     |     | a different meaning than on, for instance, FreeBSD.  |
+| user_timeout | integer() >= 0 | yes | yes | none                                                 |
 
 _Table: tcp options_
 
diff --git a/lib/kernel/src/socket.erl b/lib/kernel/src/socket.erl
index 4b2b688787..aeb650f8ad 100644
--- a/lib/kernel/src/socket.erl
+++ b/lib/kernel/src/socket.erl
@@ -1266,10 +1266,20 @@ _Options for protocol level_ [_`tcp`:_](`t:level/0`)
 
 - **`{tcp, cork}`** - `Value = boolean()`
 
+- **`{tcp, keepcnt}`** - `Value = integer()`
+
+- **`{tcp, keepidle}`** - `Value = integer()`
+
+- **`{tcp, keepintvl}`** - `Value = integer()`
+
 - **`{tcp, maxseg}`** - `Value = integer()`
 
 - **`{tcp, nodelay}`** - `Value = boolean()`
 
+- **`{tcp, nopush}`** - `Value = boolean()`
+
+- **`{tcp, user_timeout}`** - `Value = non_neg_integer()`
+
 _Options for protocol level_ [_`udp`:_](`t:level/0`)
 
 - **`{udp, cork}`** - `Value = boolean()`
-- 
2.51.0

openSUSE Build Service is sponsored by