Skip to content

Commit 2558ff4

Browse files
panjf2000aduh95
authored andcommitted
deps: libuv: reapply 3a9a6e3e6b
Original commit message: tcp: support customizing TCP_KEEPINTVL and TCP_KEEPCNT Implement `uv_tcp_keepalive_ex` function that extends `uv_tcp_keepalive` to support `TCP_KEEPINTVL` and `TCP_KEEPCN` socket options in addition to TCP_KEEPIDLE. Refs: libuv/libuv@3a9a6e3
1 parent 86282b5 commit 2558ff4

File tree

8 files changed

+199
-59
lines changed

8 files changed

+199
-59
lines changed

deps/uv/docs/src/tcp.rst

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,35 @@ API
9191
9292
.. versionchanged:: 1.49.0 If `delay` is less than 1 then ``UV_EINVAL``` is returned.
9393
94+
.. c:function:: int uv_tcp_keepalive_ex(uv_tcp_t* handle, int on, unsigned int idle, unsigned int intvl, unsigned int cnt)
95+
96+
Enable / disable TCP keep-alive with all socket options: `TCP_KEEPIDLE`, `TCP_KEEPINTVL` and `TCP_KEEPCNT`.
97+
`idle` is the value for `TCP_KEEPIDLE`, `intvl` is the value for `TCP_KEEPINTVL`,
98+
`cnt` is the value for `TCP_KEEPCNT`, ignored when `on` is zero.
99+
100+
With TCP keep-alive enabled, `idle` is the time (in seconds) the connection needs to remain idle before
101+
TCP starts sending keep-alive probes. `intvl` is the time (in seconds) between individual keep-alive probes.
102+
TCP will drop the connection after sending `cnt` probes without getting any replies from the peer, then the
103+
handle is destroyed with a ``UV_ETIMEDOUT`` error passed to the corresponding callback.
104+
105+
If one of `idle`, `intvl`, or `cnt` is less than 1, ``UV_EINVAL`` is returned.
106+
107+
.. versionchanged:: 1.52.0 added support of setting `TCP_KEEPINTVL` and `TCP_KEEPCNT` socket options.
108+
109+
.. note::
110+
Ensure that the socket options are supported by the underlying operating system.
111+
Currently supported platforms:
112+
- AIX
113+
- DragonFlyBSD
114+
- FreeBSD
115+
- HP-UX
116+
- illumos
117+
- Linux
118+
- macOS
119+
- NetBSD
120+
- Solaris
121+
- Windows
122+
94123
.. c:function:: int uv_tcp_simultaneous_accepts(uv_tcp_t* handle, int enable)
95124
96125
Enable / disable simultaneous asynchronous accept requests that are

deps/uv/include/uv.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -602,6 +602,11 @@ UV_EXTERN int uv_tcp_nodelay(uv_tcp_t* handle, int enable);
602602
UV_EXTERN int uv_tcp_keepalive(uv_tcp_t* handle,
603603
int enable,
604604
unsigned int delay);
605+
UV_EXTERN int uv_tcp_keepalive_ex(uv_tcp_t* handle,
606+
int on,
607+
unsigned int idle,
608+
unsigned int intvl,
609+
unsigned int cnt);
605610
UV_EXTERN int uv_tcp_simultaneous_accepts(uv_tcp_t* handle, int enable);
606611

607612
enum uv_tcp_flags {

deps/uv/src/unix/internal.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -359,7 +359,11 @@ int uv__slurp(const char* filename, char* buf, size_t len);
359359
/* tcp */
360360
int uv__tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb);
361361
int uv__tcp_nodelay(int fd, int on);
362-
int uv__tcp_keepalive(int fd, int on, unsigned int delay);
362+
int uv__tcp_keepalive(int fd,
363+
int on,
364+
unsigned int idle,
365+
unsigned int intvl,
366+
unsigned int cnt);
363367

364368
/* tty */
365369
void uv__tty_close(uv_tty_t* handle);

deps/uv/src/unix/stream.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -416,7 +416,7 @@ int uv__stream_open(uv_stream_t* stream, int fd, int flags) {
416416

417417
/* TODO Use delay the user passed in. */
418418
if ((stream->flags & UV_HANDLE_TCP_KEEPALIVE) &&
419-
uv__tcp_keepalive(fd, 1, 60)) {
419+
uv__tcp_keepalive(fd, 1, 60, 1, 10)) {
420420
return UV__ERR(errno);
421421
}
422422
}

deps/uv/src/unix/tcp.c

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -465,22 +465,18 @@ int uv__tcp_nodelay(int fd, int on) {
465465
#else
466466
#define UV_KEEPALIVE_FACTOR(x)
467467
#endif
468-
int uv__tcp_keepalive(int fd, int on, unsigned int delay) {
469-
int idle;
470-
int intvl;
471-
int cnt;
472-
473-
(void) &idle;
474-
(void) &intvl;
475-
(void) &cnt;
476-
468+
int uv__tcp_keepalive(int fd,
469+
int on,
470+
unsigned int idle,
471+
unsigned int intvl,
472+
unsigned int cnt) {
477473
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)))
478474
return UV__ERR(errno);
479475

480476
if (!on)
481477
return 0;
482478

483-
if (delay < 1)
479+
if (idle < 1 || intvl < 1 || cnt < 1)
484480
return UV_EINVAL;
485481

486482
#ifdef __sun
@@ -506,13 +502,16 @@ int uv__tcp_keepalive(int fd, int on, unsigned int delay) {
506502
* The TCP connection will be aborted after certain amount of probes, which is set by TCP_KEEPCNT, without receiving response.
507503
*/
508504

509-
idle = delay;
510-
/* Kernel expects at least 10 seconds. */
505+
/* Kernel expects at least 10 seconds for TCP_KEEPIDLE and TCP_KEEPINTVL. */
511506
if (idle < 10)
512507
idle = 10;
513-
/* Kernel expects at most 10 days. */
508+
if (intvl < 10)
509+
intvl = 10;
510+
/* Kernel expects at most 10 days for TCP_KEEPIDLE and TCP_KEEPINTVL. */
514511
if (idle > 10*24*60*60)
515512
idle = 10*24*60*60;
513+
if (intvl > 10*24*60*60)
514+
intvl = 10*24*60*60;
516515

517516
UV_KEEPALIVE_FACTOR(idle);
518517

@@ -522,12 +521,10 @@ int uv__tcp_keepalive(int fd, int on, unsigned int delay) {
522521
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &idle, sizeof(idle)))
523522
return UV__ERR(errno);
524523

525-
intvl = 10; /* required at least 10 seconds */
526524
UV_KEEPALIVE_FACTOR(intvl);
527525
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &intvl, sizeof(intvl)))
528526
return UV__ERR(errno);
529527

530-
cnt = 1; /* 1 retry, ensure (TCP_KEEPINTVL * TCP_KEEPCNT) is 10 seconds */
531528
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &cnt, sizeof(cnt)))
532529
return UV__ERR(errno);
533530
#else
@@ -539,15 +536,14 @@ int uv__tcp_keepalive(int fd, int on, unsigned int delay) {
539536

540537
/* Note that the consequent probes will not be sent at equal intervals on Solaris,
541538
* but will be sent using the exponential backoff algorithm. */
542-
int time_to_abort = 10; /* 10 seconds */
539+
unsigned int time_to_abort = intvl * cnt;
543540
UV_KEEPALIVE_FACTOR(time_to_abort);
544541
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE_ABORT_THRESHOLD, &time_to_abort, sizeof(time_to_abort)))
545542
return UV__ERR(errno);
546543
#endif
547544

548545
#else /* !defined(__sun) */
549546

550-
idle = delay;
551547
UV_KEEPALIVE_FACTOR(idle);
552548
#ifdef TCP_KEEPIDLE
553549
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &idle, sizeof(idle)))
@@ -559,14 +555,12 @@ int uv__tcp_keepalive(int fd, int on, unsigned int delay) {
559555
#endif
560556

561557
#ifdef TCP_KEEPINTVL
562-
intvl = 1; /* 1 second; same as default on Win32 */
563558
UV_KEEPALIVE_FACTOR(intvl);
564559
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &intvl, sizeof(intvl)))
565560
return UV__ERR(errno);
566561
#endif
567562

568563
#ifdef TCP_KEEPCNT
569-
cnt = 10; /* 10 retries; same as hardcoded on Win32 */
570564
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &cnt, sizeof(cnt)))
571565
return UV__ERR(errno);
572566
#endif
@@ -594,11 +588,20 @@ int uv_tcp_nodelay(uv_tcp_t* handle, int on) {
594588
}
595589

596590

597-
int uv_tcp_keepalive(uv_tcp_t* handle, int on, unsigned int delay) {
591+
int uv_tcp_keepalive(uv_tcp_t* handle, int on, unsigned int idle) {
592+
return uv_tcp_keepalive_ex(handle, on, idle, 1, 10);
593+
}
594+
595+
596+
int uv_tcp_keepalive_ex(uv_tcp_t* handle,
597+
int on,
598+
unsigned int idle,
599+
unsigned int intvl,
600+
unsigned int cnt) {
598601
int err;
599602

600603
if (uv__stream_fd(handle) != -1) {
601-
err =uv__tcp_keepalive(uv__stream_fd(handle), on, delay);
604+
err = uv__tcp_keepalive(uv__stream_fd(handle), on, idle, intvl, cnt);
602605
if (err)
603606
return err;
604607
}
@@ -608,7 +611,7 @@ int uv_tcp_keepalive(uv_tcp_t* handle, int on, unsigned int delay) {
608611
else
609612
handle->flags &= ~UV_HANDLE_TCP_KEEPALIVE;
610613

611-
/* TODO Store delay if uv__stream_fd(handle) == -1 but don't want to enlarge
614+
/* TODO Store idle if uv__stream_fd(handle) == -1 but don't want to enlarge
612615
* uv_tcp_t with an int that's almost never used...
613616
*/
614617

deps/uv/src/win/tcp.c

Lines changed: 95 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -49,29 +49,99 @@ static int uv__tcp_nodelay(uv_tcp_t* handle, SOCKET socket, int enable) {
4949
}
5050

5151

52-
static int uv__tcp_keepalive(uv_tcp_t* handle, SOCKET socket, int enable, unsigned int delay) {
52+
/*
53+
* Check if Windows version is 10.0.16299 (Windows 10, version 1709) or later.
54+
*/
55+
static int minimal_windows10_version1709(void) {
56+
OSVERSIONINFOW os_info;
57+
if (!pRtlGetVersion)
58+
return 0;
59+
pRtlGetVersion(&os_info);
60+
if (os_info.dwMajorVersion < 10)
61+
return 0;
62+
if (os_info.dwMajorVersion > 10)
63+
return 1;
64+
if (os_info.dwMinorVersion > 0)
65+
return 1;
66+
return os_info.dwBuildNumber >= 16299;
67+
}
68+
69+
70+
static int uv__tcp_keepalive(uv_tcp_t* handle,
71+
SOCKET socket,
72+
int on,
73+
unsigned int idle,
74+
unsigned int intvl,
75+
unsigned int cnt) {
5376
if (setsockopt(socket,
5477
SOL_SOCKET,
5578
SO_KEEPALIVE,
56-
(const char*)&enable,
57-
sizeof enable) == -1) {
79+
(const char*)&on,
80+
sizeof on) == -1) {
5881
return WSAGetLastError();
5982
}
6083

61-
if (!enable)
84+
if (!on)
6285
return 0;
6386

64-
if (delay < 1)
87+
if (idle < 1 || intvl < 1 || cnt < 1)
6588
return UV_EINVAL;
6689

67-
if (setsockopt(socket,
68-
IPPROTO_TCP,
69-
TCP_KEEPALIVE,
70-
(const char*)&delay,
71-
sizeof delay) == -1) {
72-
return WSAGetLastError();
90+
/* Windows 10, version 1709 (build 10.0.16299) and later require second units
91+
* for TCP keepalive options. */
92+
if (minimal_windows10_version1709()) {
93+
if (setsockopt(socket,
94+
IPPROTO_TCP,
95+
TCP_KEEPIDLE,
96+
(const char*)&idle,
97+
sizeof idle) == -1) {
98+
return WSAGetLastError();
99+
}
100+
101+
if (setsockopt(socket,
102+
IPPROTO_TCP,
103+
TCP_KEEPINTVL,
104+
(const char*)&intvl,
105+
sizeof intvl) == -1) {
106+
return WSAGetLastError();
107+
}
108+
109+
if (setsockopt(socket,
110+
IPPROTO_TCP,
111+
TCP_KEEPCNT,
112+
(const char*)&cnt,
113+
sizeof cnt) == -1) {
114+
return WSAGetLastError();
115+
}
116+
117+
return 0;
73118
}
74119

120+
/* For those versions prior to Windows 10 version 1709,
121+
* we fall back to SIO_KEEPALIVE_VALS that expects millisecond units.
122+
* The SIO_KEEPALIVE_VALS IOCTL is supported on Windows 2000
123+
* and later versions of the operating system. */
124+
struct tcp_keepalive keepalive;
125+
keepalive.onoff = on;
126+
keepalive.keepalivetime = idle * 1000;
127+
keepalive.keepaliveinterval = intvl * 1000;
128+
/* On Windows Vista and later, the number of keep-alive probes
129+
* (data retransmissions) is set to 10 and cannot be changed.
130+
* On Windows Server 2003, Windows XP, and Windows 2000, the default setting
131+
* for number of keep-alive probes is 5 and cannot be changed programmatically.
132+
*/
133+
DWORD dummy;
134+
if (WSAIoctl(socket,
135+
SIO_KEEPALIVE_VALS,
136+
(LPVOID) &keepalive,
137+
sizeof keepalive,
138+
NULL,
139+
0,
140+
&dummy,
141+
NULL,
142+
NULL) == -1)
143+
return WSAGetLastError();
144+
75145
return 0;
76146
}
77147

@@ -132,7 +202,7 @@ static int uv__tcp_set_socket(uv_loop_t* loop,
132202

133203
/* TODO: Use stored delay. */
134204
if (handle->flags & UV_HANDLE_TCP_KEEPALIVE) {
135-
err = uv__tcp_keepalive(handle, socket, 1, 60);
205+
err = uv__tcp_keepalive(handle, socket, 1, 60, 1, 10);
136206
if (err)
137207
return err;
138208
}
@@ -749,20 +819,6 @@ static int uv__is_loopback(const struct sockaddr_storage* storage) {
749819
return 0;
750820
}
751821

752-
// Check if Windows version is 10.0.16299 or later
753-
static int uv__is_fast_loopback_fail_supported(void) {
754-
OSVERSIONINFOW os_info;
755-
if (!pRtlGetVersion)
756-
return 0;
757-
pRtlGetVersion(&os_info);
758-
if (os_info.dwMajorVersion < 10)
759-
return 0;
760-
if (os_info.dwMajorVersion > 10)
761-
return 1;
762-
if (os_info.dwMinorVersion > 0)
763-
return 1;
764-
return os_info.dwBuildNumber >= 16299;
765-
}
766822

767823
static int uv__tcp_try_connect(uv_connect_t* req,
768824
uv_tcp_t* handle,
@@ -809,7 +865,7 @@ static int uv__tcp_try_connect(uv_connect_t* req,
809865
* is not reachable, instead of waiting for 2s. We do not care if this fails.
810866
* This only works on Windows version 10.0.16299 and later.
811867
*/
812-
if (uv__is_fast_loopback_fail_supported() && uv__is_loopback(&converted)) {
868+
if (minimal_windows10_version1709() && uv__is_loopback(&converted)) {
813869
memset(&retransmit_ioctl, 0, sizeof(retransmit_ioctl));
814870
retransmit_ioctl.Rtt = TCP_INITIAL_RTO_NO_SYN_RETRANSMISSIONS;
815871
retransmit_ioctl.MaxSynRetransmissions = TCP_INITIAL_RTO_NO_SYN_RETRANSMISSIONS;
@@ -1335,22 +1391,30 @@ int uv_tcp_nodelay(uv_tcp_t* handle, int enable) {
13351391
}
13361392

13371393

1338-
int uv_tcp_keepalive(uv_tcp_t* handle, int enable, unsigned int delay) {
1394+
int uv_tcp_keepalive(uv_tcp_t* handle, int on, unsigned int idle) {
1395+
return uv_tcp_keepalive_ex(handle, on, idle, 1, 10);
1396+
}
1397+
1398+
int uv_tcp_keepalive_ex(uv_tcp_t* handle,
1399+
int on,
1400+
unsigned int idle,
1401+
unsigned int intvl,
1402+
unsigned int cnt) {
13391403
int err;
13401404

13411405
if (handle->socket != INVALID_SOCKET) {
1342-
err = uv__tcp_keepalive(handle, handle->socket, enable, delay);
1406+
err = uv__tcp_keepalive(handle, handle->socket, on, idle, intvl, cnt);
13431407
if (err)
13441408
return uv_translate_sys_error(err);
13451409
}
13461410

1347-
if (enable) {
1411+
if (on) {
13481412
handle->flags |= UV_HANDLE_TCP_KEEPALIVE;
13491413
} else {
13501414
handle->flags &= ~UV_HANDLE_TCP_KEEPALIVE;
13511415
}
13521416

1353-
/* TODO: Store delay if handle->socket isn't created yet. */
1417+
/* TODO: Store idle if handle->socket isn't created yet. */
13541418

13551419
return 0;
13561420
}

0 commit comments

Comments
 (0)