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:
committed by
Evan Hunt
parent
ab1adcca98
commit
c6c0a9fdba
@@ -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@ \
|
||||
|
@@ -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
168
lib/isc/netmgr/uv-compat.c
Normal 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
|
@@ -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.
|
||||
*/
|
||||
|
@@ -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
|
||||
|
@@ -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" />
|
||||
|
@@ -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
27
win32utils/libuv.diff
Normal 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);
|
||||
|
Reference in New Issue
Block a user