2
0
mirror of https://gitlab.isc.org/isc-projects/bind9 synced 2025-08-30 22:15:20 +00:00

Add isc_uv_export()/isc_uv_import() functions to libuv compatibility layer.

These functions can be used to pass a uv handle between threads in a
safe manner. The other option is to use uv_pipe and pass the uv_handle
via IPC, which is way more complex.  uv_export() and uv_import() functions
existed in libuv at some point but were removed later. This code is
based on the original removed code.

The Windows version of the code uses two functions internal to libuv;
a patch for libuv is attached for exporting these functions.
This commit is contained in:
Witold Kręcicki
2020-01-07 11:00:15 +01:00
committed by Evan Hunt
parent ab1adcca98
commit c6c0a9fdba
8 changed files with 246 additions and 9 deletions

View File

@@ -55,7 +55,7 @@ OBJS = pk11.@O@ pk11_result.@O@ \
lex.@O@ lfsr.@O@ lib.@O@ log.@O@ \
md.@O@ mem.@O@ mutexblock.@O@ \
netmgr/netmgr.@O@ netmgr/tcp.@O@ netmgr/udp.@O@ \
netmgr/tcpdns.@O@ netmgr/uverr2result.@O@ \
netmgr/tcpdns.@O@ netmgr/uverr2result.@O@ netmgr/uv-compat.@O@ \
netaddr.@O@ netscope.@O@ nonce.@O@ openssl_shim.@O@ pool.@O@ \
parseint.@O@ portset.@O@ queue.@O@ quota.@O@ \
radix.@O@ random.@O@ ratelimiter.@O@ \

View File

@@ -25,10 +25,10 @@ CDEFINES =
CWARNINGS =
# Alphabetically
OBJS = netmgr.@O@ tcp.@O@ udp.@O@ tcpdns.@O@ uverr2result.@O@
OBJS = netmgr.@O@ tcp.@O@ udp.@O@ tcpdns.@O@ uverr2result.@O@ uv-compat.@O@
# Alphabetically
SRCS = netmgr.c tcp.c udp.c tcpdns.c uverr2result.c
SRCS = netmgr.c tcp.c udp.c tcpdns.c uverr2result.c uv-compat.c
TARGETS = ${OBJS}

168
lib/isc/netmgr/uv-compat.c Normal file
View File

@@ -0,0 +1,168 @@
/*
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* See the COPYRIGHT file distributed with this work for additional
* information regarding copyright ownership.
*/
#include <unistd.h>
#include "uv-compat.h"
/*
* XXXWPK: This code goes into libuv internals and it's platform dependent.
* It's ugly, we shouldn't do it, but the alternative with passing sockets
* over IPC sockets is even worse, and causes all kind of different
* problems. We should try to push these things upstream.
*/
#ifdef WIN32
/* This code is adapted from libuv/src/win/internal.h */
typedef enum {
UV__IPC_SOCKET_XFER_NONE = 0,
UV__IPC_SOCKET_XFER_TCP_CONNECTION,
UV__IPC_SOCKET_XFER_TCP_SERVER
} uv__ipc_socket_xfer_type_t;
typedef struct {
WSAPROTOCOL_INFOW socket_info;
uint32_t delayed_error;
} uv__ipc_socket_xfer_info_t;
int
uv__tcp_xfer_import(uv_tcp_t *tcp, uv__ipc_socket_xfer_type_t xfer_type,
uv__ipc_socket_xfer_info_t *xfer_info);
int
uv__tcp_xfer_export(uv_tcp_t *handle, int pid,
uv__ipc_socket_xfer_info_t *xfer_info);
int
isc_uv_export(uv_stream_t *stream, isc_uv_stream_info_t *info) {
if (stream->type != UV_TCP) {
return (-1);
}
if (uv__tcp_xfer_export((uv_tcp_t *) stream, GetCurrentProcessId(),
&info->socket_info) == -1) {
return (-1);
}
info->type = UV_TCP;
}
int
isc_uv_import(uv_stream_t *stream, isc_uv_stream_info_t *info) {
uv__ipc_socket_xfer_info_t xfer_info;
if (stream->type != UV_TCP || info->type != UV_TCP) {
return (-1);
}
xfer_info.socket_info = info->socket_info;
return (uv__tcp_xfer_import((uv_tcp_t *) stream,
UV__IPC_SOCKET_XFER_TCP_SERVER,
&xfer_info));
}
#else /* WIN32 */
/* Adapted from libuv/src/unix/internal.h */
#include <sys/ioctl.h>
#include <fcntl.h>
static int
isc_uv__cloexec(int fd, int set) {
int r;
/*
* This #ifdef is taken directly from the libuv sources.
* We use FIOCLEX and FIONCLEX ioctl() calls when possible,
* but on some platforms are not implemented, or defined but
* not implemented correctly. On those, we use the FD_CLOEXEC
* fcntl() call, which adds extra system call overhead, but
* works.
*/
#if defined(_AIX) || \
defined(__APPLE__) || \
defined(__DragonFly__) || \
defined(__FreeBSD__) || \
defined(__FreeBSD_kernel__) || \
defined(__linux__) || \
defined(__OpenBSD__) || \
defined(__NetBSD__)
do {
r = ioctl(fd, set ? FIOCLEX : FIONCLEX);
} while (r == -1 && errno == EINTR);
#else /* FIOCLEX/FIONCLEX unsupported */
int flags;
do {
r = fcntl(fd, F_GETFD);
} while (r == -1 && errno == EINTR);
if (r == -1) {
return (-1);
}
if (!!(r & FD_CLOEXEC) == !!set) {
return (0);
}
if (set) {
flags = r | FD_CLOEXEC;
} else {
flags = r & ~FD_CLOEXEC;
}
do {
r = fcntl(fd, F_SETFD, flags);
} while (r == -1 && errno == EINTR);
#endif /* FIOCLEX/FIONCLEX unsupported */
if (r != 0) {
return (-1);
}
return (0);
}
int
isc_uv_export(uv_stream_t *stream, isc_uv_stream_info_t *info) {
int oldfd, fd;
int err;
if (stream->type != UV_TCP) {
return (-1);
}
err = uv_fileno((uv_handle_t *) stream, (uv_os_fd_t *) &oldfd);
if (err != 0) {
return (err);
}
fd = dup(oldfd);
if (fd == -1) {
return (-1);
}
err = isc_uv__cloexec(fd, 1);
if (err != 0) {
close(fd);
return (err);
}
info->type = stream->type;
info->fd = fd;
return (0);
}
int
isc_uv_import(uv_stream_t *stream, isc_uv_stream_info_t *info) {
if (info->type != UV_TCP) {
return (-1);
}
uv_tcp_t *tcp = (uv_tcp_t *) stream;
return (uv_tcp_open(tcp, info->fd));
}
#endif

View File

@@ -13,22 +13,59 @@
#include <uv.h>
/*
* Those functions were introduced in newer libuv, we still
* These functions were introduced in newer libuv, but we still
* want BIND9 compile on older ones so we emulate them.
* They're inline to avoid conflicts when running with a newer
* library version.
*/
#ifndef HAVE_UV_HANDLE_GET_DATA
static inline void*
uv_handle_get_data(const uv_handle_t* handle) {
return (handle->data);
static inline void *
uv_handle_get_data(const uv_handle_t *handle) {
return (handle->data);
}
#endif
#ifndef HAVE_UV_HANDLE_SET_DATA
static inline void
uv_handle_set_data(uv_handle_t* handle, void* data) {
handle->data = data;
uv_handle_set_data(uv_handle_t *handle, void *data) {
handle->data = data;
};
#endif
/*
* These functions are not available in libuv, but they're very internal
* to libuv. We should try to get them merged upstream.
*/
/*
* A sane way to pass listening TCP socket to child threads, without using
* IPC (as the libuv example shows) but a version of the uv_export() and
* uv_import() functions that were unfortunately removed from libuv.
* This is based on the original libuv code.
*/
typedef struct isc_uv_stream_info_s isc_uv_stream_info_t;
struct isc_uv_stream_info_s {
uv_handle_type type;
#ifdef WIN32
WSAPROTOCOL_INFOW socket_info;
#else
int fd;
#endif
};
int
isc_uv_export(uv_stream_t *stream, isc_uv_stream_info_t *info);
/*%<
* Exports uv_stream_t as isc_uv_stream_info_t value, which could
* be used to initialize shared streams within the same process.
*/
int
isc_uv_import(uv_stream_t *stream, isc_uv_stream_info_t *info);
/*%<
* Imports uv_stream_info_t value into uv_stream_t to initialize a
* shared stream.
*/

View File

@@ -683,6 +683,8 @@ isc_timermgr_destroy
isc_timermgr_poke
isc_tm_timegm
isc_tm_strptime
isc_uv_export
isc_uv_import
isc_win32os_versioncheck
openlog
@IF PKCS11

View File

@@ -446,6 +446,7 @@ copy InstallFiles ..\Build\Release\
<ClCompile Include="..\netmgr\tcp.c" />
<ClCompile Include="..\netmgr\udp.c" />
<ClCompile Include="..\netmgr\uverr2result.c" />
<ClCompile Include="..\netmgr\uv-compat.c" />
<ClCompile Include="..\netmgr\tcpdns.c" />
<ClCompile Include="..\netscope.c" />
<ClCompile Include="..\nonce.c" />

View File

@@ -2257,6 +2257,7 @@
./lib/isc/netmgr/tcp.c C 2019,2020
./lib/isc/netmgr/tcpdns.c C 2019,2020
./lib/isc/netmgr/udp.c C 2019,2020
./lib/isc/netmgr/uv-compat.c C 2020
./lib/isc/netmgr/uv-compat.h C 2019,2020
./lib/isc/netmgr/uverr2result.c C 2019,2020
./lib/isc/netscope.c C 2002,2004,2005,2006,2007,2016,2018,2019,2020
@@ -2604,3 +2605,4 @@
./win32utils/GeoIP.diff X 2013,2018,2019,2020
./win32utils/bind9.sln.in X 2013,2014,2015,2016,2017,2018,2019,2020
./win32utils/index.html HTML 2006,2007,2008,2012,2013,2014,2015,2016,2018,2019,2020
./win32utils/libuv.diff X 2020

27
win32utils/libuv.diff Normal file
View File

@@ -0,0 +1,27 @@
To make TCP listening properly multithreaded, we need to have the
uv_export() and uv_import() functions that were removed from libuv.
The alternative is passing sockets over IPC, which is complicated and
error prone.
To make it simple, we export two internal functions from libuv; they will
be used in lib/isc/netmgr/uv-compat.c by our versions of the uv_export()
and uv_import() functions.
diff --git a/src/win/internal.h b/src/win/internal.h
index 058ddb8e..a9dc4168 100644
--- a/src/win/internal.h
+++ b/src/win/internal.h
@@ -92,11 +92,11 @@ void uv_process_tcp_connect_req(uv_loop_t* loop, uv_tcp_t* handle,
void uv_tcp_close(uv_loop_t* loop, uv_tcp_t* tcp);
void uv_tcp_endgame(uv_loop_t* loop, uv_tcp_t* handle);
-int uv__tcp_xfer_export(uv_tcp_t* handle,
+UV_EXTERN int uv__tcp_xfer_export(uv_tcp_t* handle,
int pid,
uv__ipc_socket_xfer_type_t* xfer_type,
uv__ipc_socket_xfer_info_t* xfer_info);
-int uv__tcp_xfer_import(uv_tcp_t* tcp,
+UV_EXTERN int uv__tcp_xfer_import(uv_tcp_t* tcp,
uv__ipc_socket_xfer_type_t xfer_type,
uv__ipc_socket_xfer_info_t* xfer_info);