]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Add isc_uv_export()/isc_uv_import() functions to libuv compatibility layer.
authorWitold Kręcicki <wpk@isc.org>
Tue, 7 Jan 2020 10:00:15 +0000 (11:00 +0100)
committerEvan Hunt <each@isc.org>
Mon, 13 Jan 2020 18:52:07 +0000 (10:52 -0800)
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.

lib/isc/Makefile.in
lib/isc/netmgr/Makefile.in
lib/isc/netmgr/uv-compat.c [new file with mode: 0644]
lib/isc/netmgr/uv-compat.h
lib/isc/win32/libisc.def.in
lib/isc/win32/libisc.vcxproj.in
util/copyrights
win32utils/libuv.diff [new file with mode: 0644]

index 906ebd3e69eb0f619586a17fbb705a1d8ded368c..a8986c5a45257e8ce8ab05d79c3cbee06ade381b 100644 (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@ \
index 74f04395211b0776eb9b3b8f0d3d4dc35c2cc420..f58caf507103b27c9ac91552b10094a6fd2f33a6 100644 (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}
 
diff --git a/lib/isc/netmgr/uv-compat.c b/lib/isc/netmgr/uv-compat.c
new file mode 100644 (file)
index 0000000..101c3c7
--- /dev/null
@@ -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
index 6ec2f3237b7bf25a05132fec14511159f39a7b05..092c87976caf02f71e97bd7cafbee814caf6aec2 100644 (file)
 #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_thandle) {
-    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.
+ */
index fa20fe059d7fc336efbc80e0667d559bdecacac8..618737983a7f2a8514604d6a0566a7c0ece9bae1 100644 (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
index e41b625e3a47e9bfdfd60a2acbf41a432714ae46..9fd2d149fe3613ac9e7fb692a20e82ef9264255f 100644 (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" />
index a675fce20b64dfc994c14f02b5a554ae740f043f..d15bd6424f3d887b29b845770c0f6354d81ab7d8 100644 (file)
 ./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
 ./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
diff --git a/win32utils/libuv.diff b/win32utils/libuv.diff
new file mode 100644 (file)
index 0000000..41f3796
--- /dev/null
@@ -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);