From a011d4221175fa6255654b64dd8bf89ddc3ac2ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Sur=C3=BD?= Date: Tue, 27 Apr 2021 00:07:43 +0200 Subject: [PATCH 01/13] Add new isc_managers API to simplify <*>mgr create/destroy Previously, netmgr, taskmgr, timermgr and socketmgr all had their own isc_<*>mgr_create() and isc_<*>mgr_destroy() functions. The new isc_managers_create() and isc_managers_destroy() fold all four into a single function and makes sure the objects are created and destroy in correct order. Especially now, when taskmgr runs on top of netmgr, the correct order is important and when the code was duplicated at many places it's easy to make mistake. The former isc_<*>mgr_create() and isc_<*>mgr_destroy() functions were made private and a single call to isc_managers_create() and isc_managers_destroy() is required at the program startup / shutdown. --- bin/delv/delv.c | 20 +---- bin/dig/dighost.c | 22 +---- bin/dnssec/dnssec-signzone.c | 12 +-- bin/named/controlconf.c | 8 +- bin/named/include/named/globals.h | 2 +- bin/named/main.c | 62 +++---------- bin/named/server.c | 13 +-- bin/named/statschannel.c | 2 +- bin/nsupdate/nsupdate.c | 26 ++---- bin/rndc/rndc.c | 13 +-- bin/tests/system/pipelined/pipequeries.c | 27 ++---- bin/tests/system/resolve.c | 40 ++------- bin/tests/system/tkey/keycreate.c | 39 ++++---- bin/tests/system/tkey/keydelete.c | 42 ++++----- bin/tests/test_client.c | 5 +- bin/tests/test_server.c | 5 +- bin/tools/mdig.c | 26 ++---- lib/dns/tests/dnstest.c | 20 +---- lib/isc/Makefile.am | 6 ++ lib/isc/hp.c | 3 + lib/isc/include/isc/hp.h | 6 +- lib/isc/include/isc/managers.h | 30 +++++++ lib/isc/include/isc/netmgr.h | 9 -- lib/isc/include/isc/socket.h | 66 +------------- lib/isc/include/isc/task.h | 75 +--------------- lib/isc/include/isc/timer.h | 54 +---------- lib/isc/managers.c | 109 +++++++++++++++++++++++ lib/isc/netmgr/netmgr.c | 16 ++-- lib/isc/netmgr_p.h | 29 ++++++ lib/isc/socket_p.h | 81 +++++++++++++++++ lib/isc/task.c | 10 +-- lib/isc/task_p.h | 86 ++++++++++++++++++ lib/isc/tests/Makefile.am | 2 +- lib/isc/tests/doh_test.c | 5 +- lib/isc/tests/isctest.c | 23 +---- lib/isc/tests/netmgr_test.c | 9 +- lib/isc/tests/socket_test.c | 2 +- lib/isc/tests/task_test.c | 15 ++-- lib/isc/timer.c | 12 +-- lib/isc/timer_p.h | 65 ++++++++++++++ lib/isc/unix/socket.c | 10 +-- lib/isc/unix/socket_p.h | 25 ------ lib/isc/win32/libisc.def.in | 11 +-- lib/isc/win32/libisc.vcxproj.filters.in | 6 ++ lib/isc/win32/libisc.vcxproj.in | 2 + lib/isc/win32/socket.c | 10 +-- lib/ns/tests/nstest.c | 39 ++------ util/copyrights | 7 +- 48 files changed, 591 insertions(+), 616 deletions(-) create mode 100644 lib/isc/include/isc/managers.h create mode 100644 lib/isc/managers.c create mode 100644 lib/isc/netmgr_p.h create mode 100644 lib/isc/socket_p.h create mode 100644 lib/isc/task_p.h create mode 100644 lib/isc/timer_p.h delete mode 100644 lib/isc/unix/socket_p.h diff --git a/bin/delv/delv.c b/bin/delv/delv.c index fc974853c4..c7eb8d6839 100644 --- a/bin/delv/delv.c +++ b/bin/delv/delv.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -1761,10 +1762,8 @@ main(int argc, char *argv[]) { isc_mem_create(&mctx); CHECK(isc_appctx_create(mctx, &actx)); - netmgr = isc_nm_start(mctx, 1); - CHECK(isc_taskmgr_create(mctx, 0, netmgr, &taskmgr)); - CHECK(isc_socketmgr_create(mctx, &socketmgr)); - CHECK(isc_timermgr_create(mctx, &timermgr)); + isc_managers_create(mctx, 1, 0, 0, &netmgr, &taskmgr, &timermgr, + &socketmgr); parse_args(argc, argv); @@ -1867,18 +1866,7 @@ cleanup: if (client != NULL) { dns_client_destroy(&client); } - if (taskmgr != NULL) { - isc_taskmgr_destroy(&taskmgr); - } - if (netmgr != NULL) { - isc_nm_destroy(&netmgr); - } - if (timermgr != NULL) { - isc_timermgr_destroy(&timermgr); - } - if (socketmgr != NULL) { - isc_socketmgr_destroy(&socketmgr); - } + isc_managers_destroy(&netmgr, &taskmgr, &timermgr, &socketmgr); if (actx != NULL) { isc_appctx_destroy(&actx); } diff --git a/bin/dig/dighost.c b/bin/dig/dighost.c index a4a2ebdaf1..09926547b5 100644 --- a/bin/dig/dighost.c +++ b/bin/dig/dighost.c @@ -41,6 +41,7 @@ #include #include #include +#include #include #include #include @@ -106,9 +107,9 @@ unsigned int timeout = 0; unsigned int extrabytes; isc_mem_t *mctx = NULL; isc_log_t *lctx = NULL; +isc_nm_t *netmgr = NULL; isc_taskmgr_t *taskmgr = NULL; isc_task_t *global_task = NULL; -isc_nm_t *netmgr = NULL; isc_sockaddr_t localaddr; isc_refcount_t sendcount = ATOMIC_VAR_INIT(0); isc_refcount_t recvcount = ATOMIC_VAR_INIT(0); @@ -1360,10 +1361,7 @@ setup_libs(void) { isc_log_setdebuglevel(lctx, 0); - netmgr = isc_nm_start(mctx, 1); - - result = isc_taskmgr_create(mctx, 0, netmgr, &taskmgr); - check_result(result, "isc_taskmgr_create"); + isc_managers_create(mctx, 1, 0, 0, &netmgr, &taskmgr, NULL, NULL); result = isc_task_create(taskmgr, 0, &global_task); check_result(result, "isc_task_create"); @@ -4226,20 +4224,8 @@ destroy_libs(void) { debug("freeing task"); isc_task_detach(&global_task); } - /* - * The taskmgr_destroy() and isc_nm_destroy() calls block until - * all events are cleared. - */ - if (taskmgr != NULL) { - debug("freeing taskmgr"); - isc_taskmgr_destroy(&taskmgr); - } - debug("closing down netmgr"); - isc_nm_closedown(netmgr); - - debug("destroy netmgr"); - isc_nm_destroy(&netmgr); + isc_managers_destroy(&netmgr, &taskmgr, NULL, NULL); LOCK_LOOKUP; isc_refcount_destroy(&recvcount); diff --git a/bin/dnssec/dnssec-signzone.c b/bin/dnssec/dnssec-signzone.c index 902dfce8b4..f19b692612 100644 --- a/bin/dnssec/dnssec-signzone.c +++ b/bin/dnssec/dnssec-signzone.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -3963,13 +3964,7 @@ main(int argc, char *argv[]) { print_time(outfp); print_version(outfp); - netmgr = isc_nm_start(mctx, ntasks); - - result = isc_taskmgr_create(mctx, 0, netmgr, &taskmgr); - if (result != ISC_R_SUCCESS) { - fatal("failed to create task manager: %s", - isc_result_totext(result)); - } + isc_managers_create(mctx, ntasks, 0, 0, &netmgr, &taskmgr, NULL, NULL); master = NULL; result = isc_task_create(taskmgr, 0, &master); @@ -4020,8 +4015,7 @@ main(int argc, char *argv[]) { for (i = 0; i < (int)ntasks; i++) { isc_task_detach(&tasks[i]); } - isc_taskmgr_destroy(&taskmgr); - isc_nm_destroy(&netmgr); + isc_managers_destroy(&netmgr, &taskmgr, NULL, NULL); isc_mem_put(mctx, tasks, ntasks * sizeof(isc_task_t *)); postsign(); TIME_NOW(&sign_finish); diff --git a/bin/named/controlconf.c b/bin/named/controlconf.c index 01918009a3..d3084b3ea1 100644 --- a/bin/named/controlconf.c +++ b/bin/named/controlconf.c @@ -1164,10 +1164,10 @@ add_listener(named_controls_t *cp, controllistener_t **listenerp, } #endif - CHECK(isc_nm_listentcp(named_g_nm, (isc_nmiface_t *)&listener->address, - control_newconn, listener, - sizeof(controlconnection_t), 5, NULL, - &listener->sock)); + CHECK(isc_nm_listentcp( + named_g_netmgr, (isc_nmiface_t *)&listener->address, + control_newconn, listener, sizeof(controlconnection_t), 5, NULL, + &listener->sock)); #if 0 /* XXX: no unix socket support yet */ if (type == isc_socktype_unix) { diff --git a/bin/named/include/named/globals.h b/bin/named/include/named/globals.h index b8ea5946a4..8663eaf012 100644 --- a/bin/named/include/named/globals.h +++ b/bin/named/include/named/globals.h @@ -62,7 +62,7 @@ EXTERN bool named_g_run_done INIT(false); */ EXTERN isc_timermgr_t *named_g_timermgr INIT(NULL); EXTERN isc_socketmgr_t *named_g_socketmgr INIT(NULL); -EXTERN isc_nm_t *named_g_nm INIT(NULL); +EXTERN isc_nm_t *named_g_netmgr INIT(NULL); EXTERN cfg_parser_t *named_g_parser INIT(NULL); EXTERN cfg_parser_t *named_g_addparser INIT(NULL); EXTERN const char *named_g_version INIT(PACKAGE_VERSION); diff --git a/bin/named/main.c b/bin/named/main.c index f033607609..28bfdd57f6 100644 --- a/bin/named/main.c +++ b/bin/named/main.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -937,45 +938,17 @@ create_managers(void) { "using %u UDP listener%s per interface", named_g_udpdisp, named_g_udpdisp == 1 ? "" : "s"); - /* - * We have ncpus network threads, ncpus worker threads, ncpus - * old network threads - make it 4x just to be safe. The memory - * impact is negligible. - */ - isc_hp_init(4 * named_g_cpus); - named_g_nm = isc_nm_start(named_g_mctx, named_g_cpus); - if (named_g_nm == NULL) { - UNEXPECTED_ERROR(__FILE__, __LINE__, "isc_nm_start() failed"); - return (ISC_R_UNEXPECTED); + result = isc_managers_create(named_g_mctx, named_g_cpus, + 0 /* quantum */, maxsocks, &named_g_netmgr, + &named_g_taskmgr, &named_g_timermgr, + &named_g_socketmgr); + if (result != ISC_R_SUCCESS) { + return (result); } - result = isc_taskmgr_create(named_g_mctx, 0, named_g_nm, - &named_g_taskmgr); - if (result != ISC_R_SUCCESS) { - UNEXPECTED_ERROR(__FILE__, __LINE__, - "isc_taskmgr_create() failed: %s", - isc_result_totext(result)); - return (ISC_R_UNEXPECTED); - } - - result = isc_timermgr_create(named_g_mctx, &named_g_timermgr); - if (result != ISC_R_SUCCESS) { - UNEXPECTED_ERROR(__FILE__, __LINE__, - "isc_timermgr_create() failed: %s", - isc_result_totext(result)); - return (ISC_R_UNEXPECTED); - } - - result = isc_socketmgr_create2(named_g_mctx, &named_g_socketmgr, - maxsocks, named_g_cpus); - if (result != ISC_R_SUCCESS) { - UNEXPECTED_ERROR(__FILE__, __LINE__, - "isc_socketmgr_create() failed: %s", - isc_result_totext(result)); - return (ISC_R_UNEXPECTED); - } isc_socketmgr_maxudp(named_g_socketmgr, maxudp); - isc_nm_maxudp(named_g_nm, maxudp); + isc_nm_maxudp(named_g_netmgr, maxudp); + result = isc_socketmgr_getmaxsockets(named_g_socketmgr, &socks); if (result == ISC_R_SUCCESS) { isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, @@ -988,21 +961,8 @@ create_managers(void) { static void destroy_managers(void) { - /* - * isc_nm_closedown() closes all active connections, freeing - * attached clients and other resources and preventing new - * connections from being established, but it not does not - * stop all processing or destroy the netmgr yet. - */ - isc_nm_closedown(named_g_nm); - - /* - * isc_taskmgr_destroy() will block until all tasks have exited. - */ - isc_taskmgr_destroy(&named_g_taskmgr); - isc_nm_destroy(&named_g_nm); - isc_timermgr_destroy(&named_g_timermgr); - isc_socketmgr_destroy(&named_g_socketmgr); + isc_managers_destroy(&named_g_netmgr, &named_g_taskmgr, + &named_g_timermgr, &named_g_socketmgr); } static void diff --git a/bin/named/server.c b/bin/named/server.c index e306fd63b5..3edad71616 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -8840,7 +8840,8 @@ load_configuration(const char *filename, named_server_t *server, advertised = MAX_ADVERTISED_TIMEOUT; } - isc_nm_settimeouts(named_g_nm, initial, idle, keepalive, advertised); + isc_nm_settimeouts(named_g_netmgr, initial, idle, keepalive, + advertised); /* * Configure sets of UDP query source ports. @@ -9950,7 +9951,7 @@ run_server(isc_task_t *task, isc_event_t *event) { CHECKFATAL(ns_interfacemgr_create( named_g_mctx, server->sctx, named_g_taskmgr, - named_g_timermgr, named_g_socketmgr, named_g_nm, + named_g_timermgr, named_g_socketmgr, named_g_netmgr, named_g_dispatchmgr, server->task, named_g_udpdisp, geoip, named_g_cpus, &server->interfacemgr), "creating interface manager"); @@ -10220,7 +10221,7 @@ named_server_create(isc_mem_t *mctx, named_server_t **serverp) { CHECKFATAL(dns_zonemgr_create(named_g_mctx, named_g_taskmgr, named_g_timermgr, named_g_socketmgr, - named_g_nm, &server->zonemgr), + named_g_netmgr, &server->zonemgr), "dns_zonemgr_create"); CHECKFATAL(dns_zonemgr_setsize(server->zonemgr, 1000), "dns_zonemgr_" "setsize"); @@ -10260,7 +10261,7 @@ named_server_create(isc_mem_t *mctx, named_server_t **serverp) { isc_sockstatscounter_max), "isc_stats_create"); isc_socketmgr_setstats(named_g_socketmgr, server->sockstats); - isc_nm_setstats(named_g_nm, server->sockstats); + isc_nm_setstats(named_g_netmgr, server->sockstats); CHECKFATAL(isc_stats_create(named_g_mctx, &server->zonestats, dns_zonestatscounter_max), @@ -16342,7 +16343,7 @@ named_server_tcptimeouts(isc_lex_t *lex, isc_buffer_t **text) { return (ISC_R_UNEXPECTEDEND); } - isc_nm_gettimeouts(named_g_nm, &initial, &idle, &keepalive, + isc_nm_gettimeouts(named_g_netmgr, &initial, &idle, &keepalive, &advertised); /* Look for optional arguments. */ @@ -16396,7 +16397,7 @@ named_server_tcptimeouts(isc_lex_t *lex, isc_buffer_t **text) { result = isc_task_beginexclusive(named_g_server->task); RUNTIME_CHECK(result == ISC_R_SUCCESS); - isc_nm_settimeouts(named_g_nm, initial, idle, keepalive, + isc_nm_settimeouts(named_g_netmgr, initial, idle, keepalive, advertised); isc_task_endexclusive(named_g_server->task); diff --git a/bin/named/statschannel.c b/bin/named/statschannel.c index d2cfc12bf5..7e1e982646 100644 --- a/bin/named/statschannel.c +++ b/bin/named/statschannel.c @@ -3612,7 +3612,7 @@ add_listener(named_server_t *server, named_statschannel_t **listenerp, CHECK(ISC_R_FAMILYNOSUPPORT); } - CHECK(isc_httpdmgr_create(named_g_nm, server->mctx, addr, client_ok, + CHECK(isc_httpdmgr_create(named_g_netmgr, server->mctx, addr, client_ok, destroy_listener, listener, &listener->httpdmgr)); diff --git a/bin/nsupdate/nsupdate.c b/bin/nsupdate/nsupdate.c index 4965a274f5..b5ab597899 100644 --- a/bin/nsupdate/nsupdate.c +++ b/bin/nsupdate/nsupdate.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -923,16 +924,8 @@ setup_system(void) { result = dns_dispatchmgr_create(gmctx, &dispatchmgr); check_result(result, "dns_dispatchmgr_create"); - result = isc_socketmgr_create(gmctx, &socketmgr); - check_result(result, "dns_socketmgr_create"); - - result = isc_timermgr_create(gmctx, &timermgr); - check_result(result, "dns_timermgr_create"); - - netmgr = isc_nm_start(gmctx, 1); - - result = isc_taskmgr_create(gmctx, 0, netmgr, &taskmgr); - check_result(result, "isc_taskmgr_create"); + isc_managers_create(gmctx, 1, 0, 0, &netmgr, &taskmgr, &timermgr, + &socketmgr); result = isc_task_create(taskmgr, 0, &global_task); check_result(result, "isc_task_create"); @@ -3325,21 +3318,12 @@ cleanup(void) { dst_key_free(&sig0key); } - ddebug("Shutting down task manager"); - isc_taskmgr_destroy(&taskmgr); - - ddebug("Shutting down network manager"); - isc_nm_destroy(&netmgr); + ddebug("Shutting down managers"); + isc_managers_destroy(&netmgr, &taskmgr, &timermgr, &socketmgr); ddebug("Destroying event"); isc_event_free(&global_event); - ddebug("Shutting down socket manager"); - isc_socketmgr_destroy(&socketmgr); - - ddebug("Shutting down timer manager"); - isc_timermgr_destroy(&timermgr); - #ifdef HAVE_GSSAPI /* * Cleanup GSSAPI resources after taskmgr has been destroyed. diff --git a/bin/rndc/rndc.c b/bin/rndc/rndc.c index 624910a02d..fe30895f99 100644 --- a/bin/rndc/rndc.c +++ b/bin/rndc/rndc.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -59,6 +60,7 @@ const char *progname = NULL; bool verbose; +static isc_nm_t *netmgr = NULL; static isc_taskmgr_t *taskmgr = NULL; static isc_task_t *rndc_task = NULL; @@ -72,7 +74,6 @@ static bool local4set = false, local6set = false; static int nserveraddrs; static int currentaddr = 0; static unsigned int remoteport = 0; -static isc_nm_t *netmgr = NULL; static isc_buffer_t *databuf = NULL; static isccc_ccmsg_t rndc_ccmsg; static uint32_t algorithm; @@ -1030,9 +1031,7 @@ main(int argc, char **argv) { serial = isc_random32(); isc_mem_create(&rndc_mctx); - netmgr = isc_nm_start(rndc_mctx, 1); - DO("create task manager", - isc_taskmgr_create(rndc_mctx, 0, netmgr, &taskmgr)); + isc_managers_create(rndc_mctx, 1, 0, 0, &netmgr, &taskmgr, NULL, NULL); DO("create task", isc_task_create(taskmgr, 0, &rndc_task)); isc_log_create(rndc_mctx, &log, &logconfig); isc_log_setcontext(log); @@ -1089,9 +1088,7 @@ main(int argc, char **argv) { } isc_task_detach(&rndc_task); - isc_taskmgr_destroy(&taskmgr); - - isc_nm_closedown(netmgr); + isc_managers_destroy(&netmgr, &taskmgr, NULL, NULL); /* * Note: when TCP connections are shut down, there will be a final @@ -1101,8 +1098,6 @@ main(int argc, char **argv) { */ isccc_ccmsg_invalidate(&rndc_ccmsg); - isc_nm_destroy(&netmgr); - isc_log_destroy(&log); isc_log_setcontext(NULL); diff --git a/bin/tests/system/pipelined/pipequeries.c b/bin/tests/system/pipelined/pipequeries.c index 02f312ee2f..aa2007d7d2 100644 --- a/bin/tests/system/pipelined/pipequeries.c +++ b/bin/tests/system/pipelined/pipequeries.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -208,10 +209,10 @@ main(int argc, char *argv[]) { isc_logconfig_t *lcfg; isc_nm_t *netmgr = NULL; isc_taskmgr_t *taskmgr = NULL; - isc_task_t *task; - isc_timermgr_t *timermgr; - isc_socketmgr_t *socketmgr; - dns_dispatchmgr_t *dispatchmgr; + isc_task_t *task = NULL; + isc_timermgr_t *timermgr = NULL; + isc_socketmgr_t *socketmgr = NULL; + dns_dispatchmgr_t *dispatchmgr = NULL; unsigned int attrs, attrmask; dns_dispatch_t *dispatchv4; dns_view_t *view; @@ -277,17 +278,10 @@ main(int argc, char *argv[]) { RUNCHECK(dst_lib_init(mctx, NULL)); - netmgr = isc_nm_start(mctx, 1); + isc_managers_create(mctx, 1, 0, 0, &netmgr, &taskmgr, &timermgr, + &socketmgr); - RUNCHECK(isc_taskmgr_create(mctx, 0, netmgr, &taskmgr)); - task = NULL; RUNCHECK(isc_task_create(taskmgr, 0, &task)); - timermgr = NULL; - - RUNCHECK(isc_timermgr_create(mctx, &timermgr)); - socketmgr = NULL; - RUNCHECK(isc_socketmgr_create(mctx, &socketmgr)); - dispatchmgr = NULL; RUNCHECK(dns_dispatchmgr_create(mctx, &dispatchmgr)); attrs = DNS_DISPATCHATTR_UDP | DNS_DISPATCHATTR_MAKEQUERY | @@ -318,13 +312,10 @@ main(int argc, char *argv[]) { dns_dispatch_detach(&dispatchv4); dns_dispatchmgr_destroy(&dispatchmgr); - isc_socketmgr_destroy(&socketmgr); - isc_timermgr_destroy(&timermgr); - isc_task_shutdown(task); isc_task_detach(&task); - isc_taskmgr_destroy(&taskmgr); - isc_nm_destroy(&netmgr); + + isc_managers_destroy(&netmgr, &taskmgr, &timermgr, &socketmgr); dst_lib_destroy(); diff --git a/bin/tests/system/resolve.c b/bin/tests/system/resolve.c index b2fc5225a8..c8f7a18621 100644 --- a/bin/tests/system/resolve.c +++ b/bin/tests/system/resolve.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -66,25 +67,8 @@ isc_timermgr_t *ctxs_timermgr = NULL; static void ctxs_destroy(void) { - if (ctxs_netmgr != NULL) { - isc_nm_closedown(ctxs_netmgr); - } - - if (ctxs_taskmgr != NULL) { - isc_taskmgr_destroy(&ctxs_taskmgr); - } - - if (ctxs_netmgr != NULL) { - isc_nm_destroy(&ctxs_netmgr); - } - - if (ctxs_timermgr != NULL) { - isc_timermgr_destroy(&ctxs_timermgr); - } - - if (ctxs_socketmgr != NULL) { - isc_socketmgr_destroy(&ctxs_socketmgr); - } + isc_managers_destroy(&ctxs_netmgr, &ctxs_taskmgr, &ctxs_timermgr, + &ctxs_socketmgr); if (ctxs_actx != NULL) { isc_appctx_destroy(&ctxs_actx); @@ -106,22 +90,8 @@ ctxs_init(void) { goto fail; } - ctxs_netmgr = isc_nm_start(ctxs_mctx, 1); - - result = isc_taskmgr_create(ctxs_mctx, 0, ctxs_netmgr, &ctxs_taskmgr); - if (result != ISC_R_SUCCESS) { - goto fail; - } - - result = isc_socketmgr_create(ctxs_mctx, &ctxs_socketmgr); - if (result != ISC_R_SUCCESS) { - goto fail; - } - - result = isc_timermgr_create(ctxs_mctx, &ctxs_timermgr); - if (result != ISC_R_SUCCESS) { - goto fail; - } + isc_managers_create(ctxs_mctx, 1, 0, 0, &ctxs_netmgr, &ctxs_taskmgr, + &ctxs_timermgr, &ctxs_socketmgr); return (ISC_R_SUCCESS); diff --git a/bin/tests/system/tkey/keycreate.c b/bin/tests/system/tkey/keycreate.c index 51ed1212dd..b72b38056a 100644 --- a/bin/tests/system/tkey/keycreate.c +++ b/bin/tests/system/tkey/keycreate.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -191,21 +192,21 @@ sendquery(isc_task_t *task, isc_event_t *event) { int main(int argc, char *argv[]) { - char *ourkeyname; + char *ourkeyname = NULL; isc_nm_t *netmgr = NULL; isc_taskmgr_t *taskmgr = NULL; - isc_timermgr_t *timermgr; - isc_socketmgr_t *socketmgr; - isc_socket_t *sock; + isc_timermgr_t *timermgr = NULL; + isc_socketmgr_t *socketmgr = NULL; + isc_socket_t *sock = NULL; unsigned int attrs, attrmask; isc_sockaddr_t bind_any; - dns_dispatchmgr_t *dispatchmgr; - dns_dispatch_t *dispatchv4; - dns_view_t *view; - dns_tkeyctx_t *tctx; - isc_log_t *log; - isc_logconfig_t *logconfig; - isc_task_t *task; + dns_dispatchmgr_t *dispatchmgr = NULL; + dns_dispatch_t *dispatchv4 = NULL; + dns_view_t *view = NULL; + dns_tkeyctx_t *tctx = NULL; + isc_log_t *log = NULL; + isc_logconfig_t *logconfig = NULL; + isc_task_t *task = NULL; isc_result_t result; int type; @@ -235,17 +236,12 @@ main(int argc, char *argv[]) { RUNCHECK(dst_lib_init(mctx, NULL)); - netmgr = isc_nm_start(mctx, 1); + isc_managers_create(mctx, 1, 0, 0, &netmgr, &taskmgr, &timermgr, + &socketmgr); - RUNCHECK(isc_taskmgr_create(mctx, 0, netmgr, &taskmgr)); - task = NULL; RUNCHECK(isc_task_create(taskmgr, 0, &task)); - timermgr = NULL; - RUNCHECK(isc_timermgr_create(mctx, &timermgr)); - socketmgr = NULL; - RUNCHECK(isc_socketmgr_create(mctx, &socketmgr)); - dispatchmgr = NULL; RUNCHECK(dns_dispatchmgr_create(mctx, &dispatchmgr)); + isc_sockaddr_any(&bind_any); attrs = DNS_DISPATCHATTR_UDP | DNS_DISPATCHATTR_MAKEQUERY | DNS_DISPATCHATTR_IPV4; @@ -293,11 +289,8 @@ main(int argc, char *argv[]) { dns_dispatchmgr_destroy(&dispatchmgr); isc_task_shutdown(task); isc_task_detach(&task); - isc_taskmgr_destroy(&taskmgr); - isc_nm_destroy(&netmgr); isc_socket_detach(&sock); - isc_socketmgr_destroy(&socketmgr); - isc_timermgr_destroy(&timermgr); + isc_managers_destroy(&netmgr, &taskmgr, &timermgr, &socketmgr); dst_key_free(&ourkey); dns_tsigkey_detach(&initialkey); diff --git a/bin/tests/system/tkey/keydelete.c b/bin/tests/system/tkey/keydelete.c index cc92df049c..891b9cd04f 100644 --- a/bin/tests/system/tkey/keydelete.c +++ b/bin/tests/system/tkey/keydelete.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -135,22 +136,22 @@ sendquery(isc_task_t *task, isc_event_t *event) { int main(int argc, char **argv) { - char *keyname; - isc_nm_t *netmgr; + char *keyname = NULL; + isc_nm_t *netmgr = NULL; isc_taskmgr_t *taskmgr = NULL; - isc_timermgr_t *timermgr; - isc_socketmgr_t *socketmgr; - isc_socket_t *sock; + isc_timermgr_t *timermgr = NULL; + isc_socketmgr_t *socketmgr = NULL; + isc_socket_t *sock = NULL; unsigned int attrs, attrmask; isc_sockaddr_t bind_any; - dns_dispatchmgr_t *dispatchmgr; - dns_dispatch_t *dispatchv4; - dns_view_t *view; - dns_tkeyctx_t *tctx; - dst_key_t *dstkey; - isc_log_t *log; - isc_logconfig_t *logconfig; - isc_task_t *task; + dns_dispatchmgr_t *dispatchmgr = NULL; + dns_dispatch_t *dispatchv4 = NULL; + dns_view_t *view = NULL; + dns_tkeyctx_t *tctx = NULL; + dst_key_t *dstkey = NULL; + isc_log_t *log = NULL; + isc_logconfig_t *logconfig = NULL; + isc_task_t *task = NULL; isc_result_t result; int type; @@ -179,16 +180,10 @@ main(int argc, char **argv) { RUNCHECK(dst_lib_init(mctx, NULL)); - netmgr = isc_nm_start(mctx, 1); + isc_managers_create(mctx, 1, 0, 0, &netmgr, &taskmgr, &timermgr, + &socketmgr); - RUNCHECK(isc_taskmgr_create(mctx, 0, netmgr, &taskmgr)); - task = NULL; RUNCHECK(isc_task_create(taskmgr, 0, &task)); - timermgr = NULL; - RUNCHECK(isc_timermgr_create(mctx, &timermgr)); - socketmgr = NULL; - RUNCHECK(isc_socketmgr_create(mctx, &socketmgr)); - dispatchmgr = NULL; RUNCHECK(dns_dispatchmgr_create(mctx, &dispatchmgr)); isc_sockaddr_any(&bind_any); attrs = DNS_DISPATCHATTR_UDP | DNS_DISPATCHATTR_MAKEQUERY | @@ -237,11 +232,8 @@ main(int argc, char **argv) { dns_dispatchmgr_destroy(&dispatchmgr); isc_task_shutdown(task); isc_task_detach(&task); - isc_taskmgr_destroy(&taskmgr); - isc_nm_destroy(&netmgr); isc_socket_detach(&sock); - isc_socketmgr_destroy(&socketmgr); - isc_timermgr_destroy(&timermgr); + isc_managers_destroy(&netmgr, &taskmgr, &timermgr, &socketmgr); dns_tsigkeyring_detach(&ring); diff --git a/bin/tests/test_client.c b/bin/tests/test_client.c index c145115af8..7c9f10e2c6 100644 --- a/bin/tests/test_client.c +++ b/bin/tests/test_client.c @@ -23,6 +23,7 @@ #include #include +#include #include #include #include @@ -307,7 +308,7 @@ setup(void) { isc_mem_create(&mctx); - netmgr = isc_nm_start(mctx, workers); + isc_managers_create(mctx, workers, 0, 0, &netmgr, NULL, NULL, NULL); } static void @@ -316,7 +317,7 @@ teardown(void) { close(out); } - isc_nm_destroy(&netmgr); + isc_managers_destroy(&netmgr, NULL, NULL, NULL); isc_mem_destroy(&mctx); if (tls_ctx) { isc_tlsctx_free(&tls_ctx); diff --git a/bin/tests/test_server.c b/bin/tests/test_server.c index 9276b62022..a53577e201 100644 --- a/bin/tests/test_server.c +++ b/bin/tests/test_server.c @@ -17,6 +17,7 @@ #include #include +#include #include #include #include @@ -188,12 +189,12 @@ setup(void) { isc_mem_create(&mctx); - netmgr = isc_nm_start(mctx, workers); + isc_managers_create(mctx, workers, 0, 0, &netmgr, NULL, NULL, NULL); } static void teardown(void) { - isc_nm_destroy(&netmgr); + isc_managers_destroy(&netmgr, NULL, NULL, NULL); isc_mem_destroy(&mctx); if (tls_ctx) { isc_tlsctx_free(&tls_ctx); diff --git a/bin/tools/mdig.c b/bin/tools/mdig.c index a8c47b6197..1b0f04c4ce 100644 --- a/bin/tools/mdig.c +++ b/bin/tools/mdig.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -2070,10 +2071,10 @@ main(int argc, char *argv[]) { isc_logconfig_t *lcfg; isc_nm_t *netmgr = NULL; isc_taskmgr_t *taskmgr = NULL; - isc_task_t *task; - isc_timermgr_t *timermgr; - isc_socketmgr_t *socketmgr; - dns_dispatchmgr_t *dispatchmgr; + isc_task_t *task = NULL; + isc_timermgr_t *timermgr = NULL; + isc_socketmgr_t *socketmgr = NULL; + dns_dispatchmgr_t *dispatchmgr = NULL; unsigned int attrs, attrmask; dns_dispatch_t *dispatchvx; dns_view_t *view; @@ -2130,17 +2131,11 @@ main(int argc, char *argv[]) { fatal("can't choose between IPv4 and IPv6"); } - netmgr = isc_nm_start(mctx, 1); + isc_managers_create(mctx, 1, 0, 0, &netmgr, &taskmgr, &timermgr, + &socketmgr); - RUNCHECK(isc_taskmgr_create(mctx, 0, netmgr, &taskmgr)); - task = NULL; RUNCHECK(isc_task_create(taskmgr, 0, &task)); - timermgr = NULL; - RUNCHECK(isc_timermgr_create(mctx, &timermgr)); - socketmgr = NULL; - RUNCHECK(isc_socketmgr_create(mctx, &socketmgr)); - dispatchmgr = NULL; RUNCHECK(dns_dispatchmgr_create(mctx, &dispatchmgr)); attrs = DNS_DISPATCHATTR_UDP | DNS_DISPATCHATTR_MAKEQUERY; @@ -2206,13 +2201,10 @@ main(int argc, char *argv[]) { dns_dispatch_detach(&dispatchvx); dns_dispatchmgr_destroy(&dispatchmgr); - isc_socketmgr_destroy(&socketmgr); - isc_timermgr_destroy(&timermgr); - isc_task_shutdown(task); isc_task_detach(&task); - isc_taskmgr_destroy(&taskmgr); - isc_nm_destroy(&netmgr); + + isc_managers_destroy(&netmgr, &taskmgr, &timermgr, &socketmgr); dst_lib_destroy(); diff --git a/lib/dns/tests/dnstest.c b/lib/dns/tests/dnstest.c index 71f0e0c743..6a247b6b05 100644 --- a/lib/dns/tests/dnstest.c +++ b/lib/dns/tests/dnstest.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -95,18 +96,7 @@ cleanup_managers(void) { isc_task_shutdown(maintask); isc_task_destroy(&maintask); } - if (socketmgr != NULL) { - isc_socketmgr_destroy(&socketmgr); - } - if (taskmgr != NULL) { - isc_taskmgr_destroy(&taskmgr); - } - if (netmgr != NULL) { - isc_nm_destroy(&netmgr); - } - if (timermgr != NULL) { - isc_timermgr_destroy(&timermgr); - } + isc_managers_destroy(&netmgr, &taskmgr, &timermgr, &socketmgr); if (app_running) { isc_app_finish(); } @@ -117,10 +107,8 @@ create_managers(void) { isc_result_t result; ncpus = isc_os_ncpus(); - netmgr = isc_nm_start(dt_mctx, ncpus); - CHECK(isc_taskmgr_create(dt_mctx, 0, netmgr, &taskmgr)); - CHECK(isc_timermgr_create(dt_mctx, &timermgr)); - CHECK(isc_socketmgr_create(dt_mctx, &socketmgr)); + isc_managers_create(dt_mctx, ncpus, 0, 0, &netmgr, &taskmgr, &timermgr, + &socketmgr); CHECK(isc_task_create(taskmgr, 0, &maintask)); return (ISC_R_SUCCESS); diff --git a/lib/isc/Makefile.am b/lib/isc/Makefile.am index 22212d9e38..96124a2d89 100644 --- a/lib/isc/Makefile.am +++ b/lib/isc/Makefile.am @@ -46,6 +46,7 @@ libisc_la_HEADERS = \ include/isc/list.h \ include/isc/log.h \ include/isc/magic.h \ + include/isc/managers.h \ include/isc/md.h \ include/isc/mem.h \ include/isc/meminfo.h \ @@ -182,6 +183,7 @@ libisc_la_SOURCES = \ lex.c \ lib.c \ log.c \ + managers.c \ md.c \ mem.c \ mutexblock.c \ @@ -222,6 +224,10 @@ libisc_la_SOURCES = \ pthreads/thread.c \ entropy_private.h \ fsaccess_common_p.h \ + task_p.h \ + timer_p.h \ + socket_p.h \ + netmgr_p.h \ lib_p.h \ mem_p.h \ tls_p.h diff --git a/lib/isc/hp.c b/lib/isc/hp.c index 92d160b3eb..20646a8a74 100644 --- a/lib/isc/hp.c +++ b/lib/isc/hp.c @@ -82,6 +82,9 @@ tid(void) { void isc_hp_init(int max_threads) { + if (isc__hp_max_threads > max_threads) { + return; + } isc__hp_max_threads = max_threads; isc__hp_max_retired = max_threads * HP_MAX_HPS; } diff --git a/lib/isc/include/isc/hp.h b/lib/isc/include/isc/hp.h index 44155e625a..948eaa894a 100644 --- a/lib/isc/include/isc/hp.h +++ b/lib/isc/include/isc/hp.h @@ -66,8 +66,10 @@ typedef void(isc_hp_deletefunc_t)(void *); void isc_hp_init(int max_threads); /*%< - * Initialize hazard pointer constants - isc__hp_max_threads. If more threads - * will try to access hp it will assert. + * Initialize hazard pointer constants, isc__hp_max_threads and + * isc__hp_max_retired. If more threads try to access hp, it + * will assert. Calling this function repeatedly can be used + * to increase the limits, but cannot reduce them. */ isc_hp_t * diff --git a/lib/isc/include/isc/managers.h b/lib/isc/include/isc/managers.h new file mode 100644 index 0000000000..3b3227b09e --- /dev/null +++ b/lib/isc/include/isc/managers.h @@ -0,0 +1,30 @@ +/* + * 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 https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#pragma once + +#include +#include +#include +#include +#include + +typedef struct isc_managers isc_managers_t; + +isc_result_t +isc_managers_create(isc_mem_t *mctx, size_t workers, size_t quantum, + size_t sockets, isc_nm_t **netmgrp, + isc_taskmgr_t **taskmgrp, isc_timermgr_t **timermgrp, + isc_socketmgr_t **socketmgrp); + +void +isc_managers_destroy(isc_nm_t **netmgrp, isc_taskmgr_t **taskmgrp, + isc_timermgr_t **timermgrp, isc_socketmgr_t **socketmgrp); diff --git a/lib/isc/include/isc/netmgr.h b/lib/isc/include/isc/netmgr.h index 5ecb92d022..e0a1719026 100644 --- a/lib/isc/include/isc/netmgr.h +++ b/lib/isc/include/isc/netmgr.h @@ -68,19 +68,10 @@ typedef void (*isc_nm_opaquecb_t)(void *arg); * callbacks. */ -isc_nm_t * -isc_nm_start(isc_mem_t *mctx, uint32_t workers); -/*%< - * Creates a new network manager with 'workers' worker threads, - * and starts it running. - */ - void isc_nm_attach(isc_nm_t *mgr, isc_nm_t **dst); void isc_nm_detach(isc_nm_t **mgr0); -void -isc_nm_destroy(isc_nm_t **mgr0); /*%< * Attach/detach a network manager. When all references have been * released, the network manager is shut down, freeing all resources. diff --git a/lib/isc/include/isc/socket.h b/lib/isc/include/isc/socket.h index 61b853c990..ad6cf29603 100644 --- a/lib/isc/include/isc/socket.h +++ b/lib/isc/include/isc/socket.h @@ -9,8 +9,7 @@ * information regarding copyright ownership. */ -#ifndef ISC_SOCKET_H -#define ISC_SOCKET_H 1 +#pragma once /***** ***** Module Info @@ -676,42 +675,6 @@ isc_socket_sendto2(isc_socket_t *sock, isc_region_t *region, isc_task_t *task, */ /*@}*/ -isc_result_t -isc_socketmgr_create(isc_mem_t *mctx, isc_socketmgr_t **managerp); - -isc_result_t -isc_socketmgr_create2(isc_mem_t *mctx, isc_socketmgr_t **managerp, - unsigned int maxsocks, int nthreads); -/*%< - * Create a socket manager. If "maxsocks" is non-zero, it specifies the - * maximum number of sockets that the created manager should handle. - * isc_socketmgr_create() is equivalent of isc_socketmgr_create2() with - * "maxsocks" being zero. - * - * Notes: - * - *\li All memory will be allocated in memory context 'mctx'. - * - * Requires: - * - *\li 'mctx' is a valid memory context. - * - *\li 'managerp' points to a NULL isc_socketmgr_t. - * - *\li 'actx' is a valid application context (for createinctx()). - * - * Ensures: - * - *\li '*managerp' is a valid isc_socketmgr_t. - * - * Returns: - * - *\li #ISC_R_SUCCESS - *\li #ISC_R_NOMEMORY - *\li #ISC_R_UNEXPECTED - *\li #ISC_R_NOTIMPLEMENTED - */ - isc_result_t isc_socketmgr_getmaxsockets(isc_socketmgr_t *manager, unsigned int *nsockp); /*%< @@ -741,31 +704,6 @@ isc_socketmgr_setstats(isc_socketmgr_t *manager, isc_stats_t *stats); * (see above). */ -void -isc_socketmgr_destroy(isc_socketmgr_t **managerp); -/*%< - * Destroy a socket manager. - * - * Notes: - * - *\li This routine blocks until there are no sockets left in the manager, - * so if the caller holds any socket references using the manager, it - * must detach them before calling isc_socketmgr_destroy() or it will - * block forever. - * - * Requires: - * - *\li '*managerp' is a valid isc_socketmgr_t. - * - *\li All sockets managed by this manager are fully detached. - * - * Ensures: - * - *\li *managerp == NULL - * - *\li All resources used by the manager have been freed. - */ - isc_sockettype_t isc_socket_gettype(isc_socket_t *sock); /*%< @@ -908,5 +846,3 @@ typedef isc_result_t (*isc_socketmgrcreatefunc_t)(isc_mem_t * mctx, isc_socketmgr_t **managerp); ISC_LANG_ENDDECLS - -#endif /* ISC_SOCKET_H */ diff --git a/lib/isc/include/isc/task.h b/lib/isc/include/isc/task.h index 29e7c99544..f8a790403e 100644 --- a/lib/isc/include/isc/task.h +++ b/lib/isc/include/isc/task.h @@ -9,8 +9,7 @@ * information regarding copyright ownership. */ -#ifndef ISC_TASK_H -#define ISC_TASK_H 1 +#pragma once /***** ***** Module Info @@ -626,81 +625,11 @@ isc_task_privilege(isc_task_t *task); ***** Task Manager. *****/ -isc_result_t -isc_taskmgr_create(isc_mem_t *mctx, unsigned int default_quantum, isc_nm_t *nm, - isc_taskmgr_t **managerp); -/*%< - * Create a new task manager. - * - * Notes: - * - *\li If 'default_quantum' is non-zero, then it will be used as the default - * quantum value when tasks are created. If zero, then an implementation - * defined default quantum will be used. - * - *\li If 'nm' is set then netmgr is paused when an exclusive task mode - * is requested. - * - * Requires: - * - *\li 'mctx' is a valid memory context. - * - *\li managerp != NULL && *managerp == NULL - * - * Ensures: - * - *\li On success, '*managerp' will be attached to the newly created task - * manager. - * - * Returns: - * - *\li #ISC_R_SUCCESS - *\li #ISC_R_NOMEMORY - *\li #ISC_R_NOTHREADS No threads could be created. - *\li #ISC_R_UNEXPECTED An unexpected error occurred. - *\li #ISC_R_SHUTTINGDOWN The non-threaded, shared, task - * manager shutting down. - */ - void isc_taskmgr_attach(isc_taskmgr_t *, isc_taskmgr_t **); void isc_taskmgr_detach(isc_taskmgr_t *); -void -isc_taskmgr_destroy(isc_taskmgr_t **managerp); -/*%< - * Destroy '*managerp'. - * - * Notes: - * - *\li Calling isc_taskmgr_destroy() will shutdown all tasks managed by - * *managerp that haven't already been shutdown. The call will block - * until all tasks have entered the done state. - * - *\li isc_taskmgr_destroy() must not be called by a task event action, - * because it would block forever waiting for the event action to - * complete. An event action that wants to cause task manager shutdown - * should request some non-event action thread of execution to do the - * shutdown, e.g. by signaling a condition variable or using - * isc_app_shutdown(). - * - *\li Task manager references are not reference counted, so the caller - * must ensure that no attempt will be made to use the manager after - * isc_taskmgr_destroy() returns. - * - * Requires: - * - *\li '*managerp' is a valid task manager. - * - *\li isc_taskmgr_destroy() has not be called previously on '*managerp'. - * - * Ensures: - * - *\li All resources used by the task manager, and any tasks it managed, - * have been freed. - */ - void isc_taskmgr_setexcltask(isc_taskmgr_t *mgr, isc_task_t *task); /*%< @@ -736,5 +665,3 @@ isc_taskmgr_renderjson(isc_taskmgr_t *mgr, void *tasksobj0); #endif /* HAVE_JSON_C */ ISC_LANG_ENDDECLS - -#endif /* ISC_TASK_H */ diff --git a/lib/isc/include/isc/timer.h b/lib/isc/include/isc/timer.h index 88601b8f85..96376e5cc8 100644 --- a/lib/isc/include/isc/timer.h +++ b/lib/isc/include/isc/timer.h @@ -9,8 +9,7 @@ * information regarding copyright ownership. */ -#ifndef ISC_TIMER_H -#define ISC_TIMER_H 1 +#pragma once /***** ***** Module Info @@ -276,58 +275,7 @@ isc_timer_gettype(isc_timer_t *timer); *\li 'timer' to be a valid timer. */ -isc_result_t -isc_timermgr_create(isc_mem_t *mctx, isc_timermgr_t **managerp); -/*%< - * Create a timer manager. - * - * Notes: - * - *\li All memory will be allocated in memory context 'mctx'. - * - * Requires: - * - *\li 'mctx' is a valid memory context. - * - *\li 'managerp' points to a NULL isc_timermgr_t. - * - * Ensures: - * - *\li '*managerp' is a valid isc_timermgr_t. - * - * Returns: - * - *\li Success - *\li No memory - *\li Unexpected error - */ - -void -isc_timermgr_destroy(isc_timermgr_t **managerp); -/*%< - * Destroy a timer manager. - * - * Notes: - * - *\li This routine blocks until there are no timers left in the manager, - * so if the caller holds any timer references using the manager, it - * must detach them before calling isc_timermgr_destroy() or it will - * block forever. - * - * Requires: - * - *\li '*managerp' is a valid isc_timermgr_t. - * - * Ensures: - * - *\li *managerp == NULL - * - *\li All resources used by the manager have been freed. - */ - void isc_timermgr_poke(isc_timermgr_t *m); ISC_LANG_ENDDECLS - -#endif /* ISC_TIMER_H */ diff --git a/lib/isc/managers.c b/lib/isc/managers.c new file mode 100644 index 0000000000..8a17fd1e05 --- /dev/null +++ b/lib/isc/managers.c @@ -0,0 +1,109 @@ +/* + * 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 https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#include +#include +#include + +#include "netmgr_p.h" +#include "socket_p.h" +#include "task_p.h" +#include "timer_p.h" + +isc_result_t +isc_managers_create(isc_mem_t *mctx, size_t workers, size_t quantum, + size_t sockets, isc_nm_t **netmgrp, + isc_taskmgr_t **taskmgrp, isc_timermgr_t **timermgrp, + isc_socketmgr_t **socketmgrp) { + isc_result_t result; + isc_nm_t *netmgr = NULL; + isc_socketmgr_t *socketmgr = NULL; + isc_taskmgr_t *taskmgr = NULL; + isc_timermgr_t *timermgr = NULL; + + /* + * We have ncpus network threads, ncpus old network threads - make + * it 4x just to be on the safe side. + */ + isc_hp_init(4 * workers); + + REQUIRE(netmgrp != NULL && *netmgrp == NULL); + isc__netmgr_create(mctx, workers, &netmgr); + *netmgrp = netmgr; + INSIST(netmgr != NULL); + + REQUIRE(taskmgrp == NULL || *taskmgrp == NULL); + if (taskmgrp != NULL) { + result = isc__taskmgr_create(mctx, quantum, netmgr, &taskmgr); + if (result != ISC_R_SUCCESS) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_taskmgr_create() failed: %s", + isc_result_totext(result)); + goto fail; + } + *taskmgrp = taskmgr; + } + + REQUIRE(timermgrp == NULL || *timermgrp == NULL); + if (timermgrp != NULL) { + result = isc__timermgr_create(mctx, &timermgr); + if (result != ISC_R_SUCCESS) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_timermgr_create() failed: %s", + isc_result_totext(result)); + goto fail; + } + *timermgrp = timermgr; + } + + REQUIRE(socketmgrp == NULL || *socketmgrp == NULL); + if (socketmgrp != NULL) { + result = isc__socketmgr_create(mctx, &socketmgr, sockets, + workers); + if (result != ISC_R_SUCCESS) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_socketmgr_create() failed: %s", + isc_result_totext(result)); + goto fail; + } + *socketmgrp = socketmgr; + } + + return (ISC_R_SUCCESS); +fail: + isc_managers_destroy(netmgrp, taskmgrp, timermgrp, socketmgrp); + + return (result); +} + +void +isc_managers_destroy(isc_nm_t **netmgrp, isc_taskmgr_t **taskmgrp, + isc_timermgr_t **timermgrp, isc_socketmgr_t **socketmgrp) { + if (netmgrp != NULL && *netmgrp != NULL) { + isc_nm_closedown(*netmgrp); + } + + if (taskmgrp != NULL && *taskmgrp != NULL) { + isc__taskmgr_destroy(taskmgrp); + } + + if (netmgrp != NULL && *netmgrp != NULL) { + isc__netmgr_destroy(netmgrp); + } + + if (timermgrp != NULL && *timermgrp != NULL) { + isc__timermgr_destroy(timermgrp); + } + + if (socketmgrp != NULL && *socketmgrp != NULL) { + isc__socketmgr_destroy(socketmgrp); + } +} diff --git a/lib/isc/netmgr/netmgr.c b/lib/isc/netmgr/netmgr.c index 0699fe807e..7acaca8791 100644 --- a/lib/isc/netmgr/netmgr.c +++ b/lib/isc/netmgr/netmgr.c @@ -37,6 +37,7 @@ #include #include "netmgr-int.h" +#include "netmgr_p.h" #include "openssl_shim.h" #include "uv-compat.h" @@ -206,8 +207,8 @@ isc__nm_winsock_destroy(void) { } #endif /* WIN32 */ -isc_nm_t * -isc_nm_start(isc_mem_t *mctx, uint32_t workers) { +void +isc__netmgr_create(isc_mem_t *mctx, uint32_t workers, isc_nm_t **netmgrp) { isc_nm_t *mgr = NULL; char name[32]; @@ -296,7 +297,7 @@ isc_nm_start(isc_mem_t *mctx, uint32_t workers) { } mgr->magic = NM_MAGIC; - return (mgr); + *netmgrp = mgr; } /* @@ -485,14 +486,13 @@ isc_nm_closedown(isc_nm_t *mgr) { } void -isc_nm_destroy(isc_nm_t **mgr0) { +isc__netmgr_destroy(isc_nm_t **netmgrp) { isc_nm_t *mgr = NULL; int counter = 0; - REQUIRE(mgr0 != NULL); - REQUIRE(VALID_NM(*mgr0)); + REQUIRE(VALID_NM(*netmgrp)); - mgr = *mgr0; + mgr = *netmgrp; /* * Close active connections. @@ -524,7 +524,7 @@ isc_nm_destroy(isc_nm_t **mgr0) { /* * Detach final reference. */ - isc_nm_detach(mgr0); + isc_nm_detach(netmgrp); } void diff --git a/lib/isc/netmgr_p.h b/lib/isc/netmgr_p.h new file mode 100644 index 0000000000..ce934d51c9 --- /dev/null +++ b/lib/isc/netmgr_p.h @@ -0,0 +1,29 @@ +/* + * 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 https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#pragma once + +#include +#include + +void +isc__netmgr_create(isc_mem_t *mctx, uint32_t workers, isc_nm_t **netgmrp); +/*%< + * Creates a new network manager with 'workers' worker threads, + * and starts it running. + */ + +void +isc__netmgr_destroy(isc_nm_t **netmgrp); +/*%< + * Destroy is working the same way as isc_nm_detach, but it actively waits + * for all other references to be gone. + */ diff --git a/lib/isc/socket_p.h b/lib/isc/socket_p.h new file mode 100644 index 0000000000..2ac133fd13 --- /dev/null +++ b/lib/isc/socket_p.h @@ -0,0 +1,81 @@ +/* + * 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 https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#include +#include +#include + +isc_result_t +isc__socketmgr_create(isc_mem_t *mctx, isc_socketmgr_t **managerp, + unsigned int maxsocks, int nthreads); +/*%< + * Create a socket manager. If "maxsocks" is non-zero, it specifies the + * maximum number of sockets that the created manager should handle. + * + * Notes: + * + *\li All memory will be allocated in memory context 'mctx'. + * + * Requires: + * + *\li 'mctx' is a valid memory context. + * + *\li 'managerp' points to a NULL isc_socketmgr_t. + * + * Ensures: + * + *\li '*managerp' is a valid isc_socketmgr_t. + * + * Returns: + * + *\li #ISC_R_SUCCESS + *\li #ISC_R_NOMEMORY + *\li #ISC_R_UNEXPECTED + *\li #ISC_R_NOTIMPLEMENTED + */ + +void +isc__socketmgr_destroy(isc_socketmgr_t **managerp); +/*%< + * Destroy a socket manager. + * + * Notes: + * + *\li This routine blocks until there are no sockets left in the manager, + * so if the caller holds any socket references using the manager, it + * must detach them before calling isc_socketmgr_destroy() or it will + * block forever. + * + * Requires: + * + *\li '*managerp' is a valid isc_socketmgr_t. + * + *\li All sockets managed by this manager are fully detached. + * + * Ensures: + * + *\li *managerp == NULL + * + *\li All resources used by the manager have been freed. + */ + +#if !WIN32 + +#include + +typedef struct isc_socketwait isc_socketwait_t; +int +isc__socketmgr_waitevents(isc_socketmgr_t *, struct timeval *, + isc_socketwait_t **); +isc_result_t +isc__socketmgr_dispatch(isc_socketmgr_t *, isc_socketwait_t *); + +#endif diff --git a/lib/isc/task.c b/lib/isc/task.c index b8996a7f0e..2c987875b3 100644 --- a/lib/isc/task.c +++ b/lib/isc/task.c @@ -44,9 +44,7 @@ #include #endif /* HAVE_JSON_C */ -#ifdef OPENSSL_LEAKS -#include -#endif /* ifdef OPENSSL_LEAKS */ +#include "task_p.h" /* * Task manager is built around 'as little locking as possible' concept. @@ -967,8 +965,8 @@ isc_taskmgr_detach(isc_taskmgr_t *manager) { } isc_result_t -isc_taskmgr_create(isc_mem_t *mctx, unsigned int default_quantum, isc_nm_t *nm, - isc_taskmgr_t **managerp) { +isc__taskmgr_create(isc_mem_t *mctx, unsigned int default_quantum, isc_nm_t *nm, + isc_taskmgr_t **managerp) { isc_taskmgr_t *manager; /* @@ -1010,7 +1008,7 @@ isc_taskmgr_create(isc_mem_t *mctx, unsigned int default_quantum, isc_nm_t *nm, } void -isc_taskmgr_destroy(isc_taskmgr_t **managerp) { +isc__taskmgr_destroy(isc_taskmgr_t **managerp) { isc_taskmgr_t *manager; isc_task_t *task; diff --git a/lib/isc/task_p.h b/lib/isc/task_p.h new file mode 100644 index 0000000000..fbf3eb1c56 --- /dev/null +++ b/lib/isc/task_p.h @@ -0,0 +1,86 @@ +/* + * 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 https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#pragma once + +#include +#include +#include + +isc_result_t +isc__taskmgr_create(isc_mem_t *mctx, unsigned int default_quantum, isc_nm_t *nm, + isc_taskmgr_t **managerp); +/*%< + * Create a new task manager. + * + * Notes: + * + *\li If 'default_quantum' is non-zero, then it will be used as the default + * quantum value when tasks are created. If zero, then an implementation + * defined default quantum will be used. + * + *\li If 'nm' is set then netmgr is paused when an exclusive task mode + * is requested. + * + * Requires: + * + *\li 'mctx' is a valid memory context. + * + *\li managerp != NULL && *managerp == NULL + * + * Ensures: + * + *\li On success, '*managerp' will be attached to the newly created task + * manager. + * + * Returns: + * + *\li #ISC_R_SUCCESS + *\li #ISC_R_NOMEMORY + *\li #ISC_R_NOTHREADS No threads could be created. + *\li #ISC_R_UNEXPECTED An unexpected error occurred. + *\li #ISC_R_SHUTTINGDOWN The non-threaded, shared, task + * manager shutting down. + */ + +void +isc__taskmgr_destroy(isc_taskmgr_t **managerp); +/*%< + * Destroy '*managerp'. + * + * Notes: + * + *\li Calling isc_taskmgr_destroy() will shutdown all tasks managed by + * *managerp that haven't already been shutdown. The call will block + * until all tasks have entered the done state. + * + *\li isc_taskmgr_destroy() must not be called by a task event action, + * because it would block forever waiting for the event action to + * complete. An event action that wants to cause task manager shutdown + * should request some non-event action thread of execution to do the + * shutdown, e.g. by signaling a condition variable or using + * isc_app_shutdown(). + * + *\li Task manager references are not reference counted, so the caller + * must ensure that no attempt will be made to use the manager after + * isc_taskmgr_destroy() returns. + * + * Requires: + * + *\li '*managerp' is a valid task manager. + * + *\li isc_taskmgr_destroy() has not be called previously on '*managerp'. + * + * Ensures: + * + *\li All resources used by the task manager, and any tasks it managed, + * have been freed. + */ diff --git a/lib/isc/tests/Makefile.am b/lib/isc/tests/Makefile.am index 1cee32d754..c8da51d3ee 100644 --- a/lib/isc/tests/Makefile.am +++ b/lib/isc/tests/Makefile.am @@ -10,7 +10,7 @@ LDADD += \ check_LTLIBRARIES = libisctest.la libisctest_la_SOURCES = \ - ../unix/socket_p.h \ + ../socket_p.h \ isctest.c \ isctest.h \ uv_wrap.h diff --git a/lib/isc/tests/doh_test.c b/lib/isc/tests/doh_test.c index a0a93584d2..a04a000a68 100644 --- a/lib/isc/tests/doh_test.c +++ b/lib/isc/tests/doh_test.c @@ -43,6 +43,7 @@ #include "../netmgr/netmgr-int.h" #include "../netmgr/uv-compat.c" #include "../netmgr/uv-compat.h" +#include "../netmgr_p.h" #include "isctest.h" #define MAX_NM 2 @@ -319,7 +320,7 @@ nm_setup(void **state) { nm = isc_mem_get(test_mctx, MAX_NM * sizeof(nm[0])); for (size_t i = 0; i < MAX_NM; i++) { - nm[i] = isc_nm_start(test_mctx, nworkers); + isc__netmgr_create(test_mctx, nworkers, &nm[i]); assert_non_null(nm[i]); } @@ -339,7 +340,7 @@ nm_teardown(void **state) { isc_nm_t **nm = (isc_nm_t **)*state; for (size_t i = 0; i < MAX_NM; i++) { - isc_nm_destroy(&nm[i]); + isc__netmgr_destroy(&nm[i]); assert_null(nm[i]); } isc_mem_put(test_mctx, nm, MAX_NM * sizeof(nm[0])); diff --git a/lib/isc/tests/isctest.c b/lib/isc/tests/isctest.c index c9b6b6929f..bf35210251 100644 --- a/lib/isc/tests/isctest.c +++ b/lib/isc/tests/isctest.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -58,18 +59,7 @@ cleanup_managers(void) { isc_task_shutdown(maintask); isc_task_destroy(&maintask); } - if (socketmgr != NULL) { - isc_socketmgr_destroy(&socketmgr); - } - if (taskmgr != NULL) { - isc_taskmgr_destroy(&taskmgr); - } - if (netmgr != NULL) { - isc_nm_destroy(&netmgr); - } - if (timermgr != NULL) { - isc_timermgr_destroy(&timermgr); - } + isc_managers_destroy(&netmgr, &taskmgr, &timermgr, &socketmgr); } static isc_result_t @@ -87,14 +77,12 @@ create_managers(unsigned int workers) { INSIST(workers != 0); isc_hp_init(6 * workers); + isc_managers_create(test_mctx, workers, 0, 0, &netmgr, &taskmgr, + &timermgr, &socketmgr); - netmgr = isc_nm_start(test_mctx, workers); - CHECK(isc_taskmgr_create(test_mctx, 0, netmgr, &taskmgr)); CHECK(isc_task_create(taskmgr, 0, &maintask)); isc_taskmgr_setexcltask(taskmgr, maintask); - CHECK(isc_timermgr_create(test_mctx, &timermgr)); - CHECK(isc_socketmgr_create(test_mctx, &socketmgr)); return (ISC_R_SUCCESS); cleanup: @@ -150,9 +138,6 @@ isc_test_end(void) { if (maintask != NULL) { isc_task_detach(&maintask); } - if (taskmgr != NULL) { - isc_taskmgr_destroy(&taskmgr); - } cleanup_managers(); diff --git a/lib/isc/tests/netmgr_test.c b/lib/isc/tests/netmgr_test.c index d062dbb799..661133a92d 100644 --- a/lib/isc/tests/netmgr_test.c +++ b/lib/isc/tests/netmgr_test.c @@ -37,6 +37,7 @@ #include "../netmgr/udp.c" #include "../netmgr/uv-compat.c" #include "../netmgr/uv-compat.h" +#include "../netmgr_p.h" #include "isctest.h" typedef void (*stream_connect_function)(isc_nm_t *nm); @@ -335,12 +336,12 @@ nm_setup(void **state __attribute__((unused))) { return (-1); } - listen_nm = isc_nm_start(test_mctx, workers); + isc__netmgr_create(test_mctx, workers, &listen_nm); assert_non_null(listen_nm); isc_nm_settimeouts(listen_nm, T_INIT, T_IDLE, T_KEEPALIVE, T_ADVERTISED); - connect_nm = isc_nm_start(test_mctx, workers); + isc__netmgr_create(test_mctx, workers, &connect_nm); assert_non_null(connect_nm); isc_nm_settimeouts(connect_nm, T_INIT, T_IDLE, T_KEEPALIVE, T_ADVERTISED); @@ -358,10 +359,10 @@ static int nm_teardown(void **state __attribute__((unused))) { UNUSED(state); - isc_nm_destroy(&connect_nm); + isc__netmgr_destroy(&connect_nm); assert_null(connect_nm); - isc_nm_destroy(&listen_nm); + isc__netmgr_destroy(&listen_nm); assert_null(listen_nm); WAIT_FOR_EQ(active_cconnects, 0); diff --git a/lib/isc/tests/socket_test.c b/lib/isc/tests/socket_test.c index 47e98a7b46..003571848e 100644 --- a/lib/isc/tests/socket_test.c +++ b/lib/isc/tests/socket_test.c @@ -30,7 +30,7 @@ #include #include -#include "../unix/socket_p.h" +#include "../socket_p.h" #include "isctest.h" static bool recv_dscp; diff --git a/lib/isc/tests/task_test.c b/lib/isc/tests/task_test.c index f6b40f203d..be649393ec 100644 --- a/lib/isc/tests/task_test.c +++ b/lib/isc/tests/task_test.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -784,7 +785,6 @@ maxtask_cb(isc_task_t *task, isc_event_t *event) { static void manytasks(void **state) { isc_mem_t *mctx = NULL; - isc_result_t result; isc_event_t *event = NULL; uintptr_t ntasks = 10000; @@ -801,9 +801,7 @@ manytasks(void **state) { isc_mem_debugging = ISC_MEM_DEBUGRECORD; isc_mem_create(&mctx); - netmgr = isc_nm_start(mctx, 4); - result = isc_taskmgr_create(mctx, 0, netmgr, &taskmgr); - assert_int_equal(result, ISC_R_SUCCESS); + isc_managers_create(mctx, 4, 0, 0, &netmgr, &taskmgr, NULL, NULL); atomic_init(&done, false); @@ -818,8 +816,8 @@ manytasks(void **state) { } UNLOCK(&lock); - isc_taskmgr_destroy(&taskmgr); - isc_nm_destroy(&netmgr); + isc_managers_destroy(&netmgr, &taskmgr, NULL, NULL); + isc_mem_destroy(&mctx); isc_condition_destroy(&cv); isc_mutex_destroy(&lock); @@ -899,7 +897,7 @@ sd_event2(isc_task_t *task, isc_event_t *event) { } static void -shutdown(void **state) { +task_shutdown(void **state) { isc_result_t result; isc_eventtype_t event_type; isc_event_t *event = NULL; @@ -1545,7 +1543,8 @@ main(int argc, char **argv) { cmocka_unit_test_setup_teardown(purgeevent_notpurge, _setup, _teardown), cmocka_unit_test_setup_teardown(purgerange, _setup, _teardown), - cmocka_unit_test_setup_teardown(shutdown, _setup4, _teardown), + cmocka_unit_test_setup_teardown(task_shutdown, _setup4, + _teardown), cmocka_unit_test_setup_teardown(task_exclusive, _setup4, _teardown), }; diff --git a/lib/isc/timer.c b/lib/isc/timer.c index af49801660..a29d966726 100644 --- a/lib/isc/timer.c +++ b/lib/isc/timer.c @@ -29,9 +29,7 @@ #include #include -#ifdef OPENSSL_LEAKS -#include -#endif /* ifdef OPENSSL_LEAKS */ +#include "timer_p.h" #ifdef ISC_TIMER_TRACE #define XTRACE(s) fprintf(stderr, "%s\n", (s)) @@ -630,10 +628,6 @@ static isc_threadresult_t } UNLOCK(&manager->lock); -#ifdef OPENSSL_LEAKS - ERR_remove_state(0); -#endif /* ifdef OPENSSL_LEAKS */ - return ((isc_threadresult_t)0); } @@ -663,7 +657,7 @@ set_index(void *what, unsigned int index) { } isc_result_t -isc_timermgr_create(isc_mem_t *mctx, isc_timermgr_t **managerp) { +isc__timermgr_create(isc_mem_t *mctx, isc_timermgr_t **managerp) { isc_timermgr_t *manager; isc_result_t result; @@ -707,7 +701,7 @@ isc_timermgr_poke(isc_timermgr_t *manager) { } void -isc_timermgr_destroy(isc_timermgr_t **managerp) { +isc__timermgr_destroy(isc_timermgr_t **managerp) { isc_timermgr_t *manager; /* diff --git a/lib/isc/timer_p.h b/lib/isc/timer_p.h new file mode 100644 index 0000000000..dca9d72466 --- /dev/null +++ b/lib/isc/timer_p.h @@ -0,0 +1,65 @@ +/* + * 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 https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#pragma once + +#include +#include +#include + +isc_result_t +isc__timermgr_create(isc_mem_t *mctx, isc_timermgr_t **managerp); +/*%< + * Create a timer manager. + * + * Notes: + * + *\li All memory will be allocated in memory context 'mctx'. + * + * Requires: + * + *\li 'mctx' is a valid memory context. + * + *\li 'managerp' points to a NULL isc_timermgr_t. + * + * Ensures: + * + *\li '*managerp' is a valid isc_timermgr_t. + * + * Returns: + * + *\li Success + *\li No memory + *\li Unexpected error + */ + +void +isc__timermgr_destroy(isc_timermgr_t **managerp); +/*%< + * Destroy a timer manager. + * + * Notes: + * + *\li This routine blocks until there are no timers left in the manager, + * so if the caller holds any timer references using the manager, it + * must detach them before calling isc_timermgr_destroy() or it will + * block forever. + * + * Requires: + * + *\li '*managerp' is a valid isc_timermgr_t. + * + * Ensures: + * + *\li *managerp == NULL + * + *\li All resources used by the manager have been freed. + */ diff --git a/lib/isc/unix/socket.c b/lib/isc/unix/socket.c index 03923b79c7..c196874483 100644 --- a/lib/isc/unix/socket.c +++ b/lib/isc/unix/socket.c @@ -74,6 +74,7 @@ #include #include "errno2result.h" +#include "socket_p.h" #ifdef ENABLE_TCP_FASTOPEN #include @@ -3752,12 +3753,7 @@ cleanup_thread(isc_mem_t *mctx, isc__socketthread_t *thread) { } isc_result_t -isc_socketmgr_create(isc_mem_t *mctx, isc_socketmgr_t **managerp) { - return (isc_socketmgr_create2(mctx, managerp, 0, 1)); -} - -isc_result_t -isc_socketmgr_create2(isc_mem_t *mctx, isc_socketmgr_t **managerp, +isc__socketmgr_create(isc_mem_t *mctx, isc_socketmgr_t **managerp, unsigned int maxsocks, int nthreads) { int i; isc_socketmgr_t *manager; @@ -3828,7 +3824,7 @@ isc_socketmgr_setstats(isc_socketmgr_t *manager, isc_stats_t *stats) { } void -isc_socketmgr_destroy(isc_socketmgr_t **managerp) { +isc__socketmgr_destroy(isc_socketmgr_t **managerp) { isc_socketmgr_t *manager; /* diff --git a/lib/isc/unix/socket_p.h b/lib/isc/unix/socket_p.h deleted file mode 100644 index e6dd818d3c..0000000000 --- a/lib/isc/unix/socket_p.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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 https://mozilla.org/MPL/2.0/. - * - * See the COPYRIGHT file distributed with this work for additional - * information regarding copyright ownership. - */ - -#ifndef ISC_SOCKET_P_H -#define ISC_SOCKET_P_H - -/*! \file */ - -#include - -typedef struct isc_socketwait isc_socketwait_t; -int -isc__socketmgr_waitevents(isc_socketmgr_t *, struct timeval *, - isc_socketwait_t **); -isc_result_t -isc__socketmgr_dispatch(isc_socketmgr_t *, isc_socketwait_t *); -#endif /* ISC_SOCKET_P_H */ diff --git a/lib/isc/win32/libisc.def.in b/lib/isc/win32/libisc.def.in index 9160199e6d..d5ae24177c 100644 --- a/lib/isc/win32/libisc.def.in +++ b/lib/isc/win32/libisc.def.in @@ -96,9 +96,6 @@ isc_socket_send isc_socket_sendto isc_socket_sendto2 isc_socket_setname -isc_socketmgr_create -isc_socketmgr_create2 -isc_socketmgr_destroy isc_socketmgr_getmaxsockets isc_socketmgr_setreserved isc_socketmgr_setstats @@ -352,6 +349,8 @@ isc_logconfig_create isc_logconfig_destroy isc_logconfig_use isc_logfile_roll +isc_managers_create +isc_managers_destroy isc_md_new isc_md_init isc_md_reset @@ -453,7 +452,6 @@ isc_nmhandle_setdata isc_nmhandle_settimeout isc_nm_cancelread isc_nm_closedown -isc_nm_destroy isc_nm_detach isc_nm_http_endpoint isc_nm_httpconnect @@ -469,7 +467,6 @@ isc_nm_read isc_nm_resumeread isc_nm_send isc_nm_setstats -isc_nm_start isc_nm_stoplistening isc_nm_tcpconnect isc_nm_tcpdnsconnect @@ -653,8 +650,6 @@ isc_task_setprivilege isc_task_shutdown isc_task_unpause isc_task_unsend -isc_taskmgr_create -isc_taskmgr_destroy isc_taskmgr_excltask @IF NOTYET isc_taskmgr_renderjson @@ -706,8 +701,6 @@ isc_timer_detach isc_timer_gettype isc_timer_reset isc_timer_touch -isc_timermgr_create -isc_timermgr_destroy isc_timermgr_poke isc_tls_get_http2_alpn isc_tls_create diff --git a/lib/isc/win32/libisc.vcxproj.filters.in b/lib/isc/win32/libisc.vcxproj.filters.in index c859f3e46d..6c10081745 100644 --- a/lib/isc/win32/libisc.vcxproj.filters.in +++ b/lib/isc/win32/libisc.vcxproj.filters.in @@ -140,6 +140,9 @@ Library Header Files + + Library Header Files + Library Header Files @@ -530,6 +533,9 @@ Library Source Files + + Library Source Files + Library Source Files diff --git a/lib/isc/win32/libisc.vcxproj.in b/lib/isc/win32/libisc.vcxproj.in index 19439387ed..7689222868 100644 --- a/lib/isc/win32/libisc.vcxproj.in +++ b/lib/isc/win32/libisc.vcxproj.in @@ -302,6 +302,7 @@ copy InstallFiles ..\Build\Release\ + @@ -412,6 +413,7 @@ copy InstallFiles ..\Build\Release\ + diff --git a/lib/isc/win32/socket.c b/lib/isc/win32/socket.c index cf101189f8..3fecf3ee40 100644 --- a/lib/isc/win32/socket.c +++ b/lib/isc/win32/socket.c @@ -82,6 +82,7 @@ #endif /* HAVE_LIBXML2 */ #include "errno2result.h" +#include "socket_p.h" /* * Set by the -T dscp option on the command line. If set to a value @@ -2548,12 +2549,7 @@ SocketIoThread(LPVOID ThreadContext) { * Create a new socket manager. */ isc_result_t -isc_socketmgr_create(isc_mem_t *mctx, isc_socketmgr_t **managerp) { - return (isc_socketmgr_create2(mctx, managerp, 0, 1)); -} - -isc_result_t -isc_socketmgr_create2(isc_mem_t *mctx, isc_socketmgr_t **managerp, +isc__socketmgr_create(isc_mem_t *mctx, isc_socketmgr_t **managerp, unsigned int maxsocks, int nthreads) { isc_socketmgr_t *manager; @@ -2611,7 +2607,7 @@ isc_socketmgr_setstats(isc_socketmgr_t *manager, isc_stats_t *stats) { } void -isc_socketmgr_destroy(isc_socketmgr_t **managerp) { +isc__socketmgr_destroy(isc_socketmgr_t **managerp) { isc_socketmgr_t *manager; /* diff --git a/lib/ns/tests/nstest.c b/lib/ns/tests/nstest.c index 173a5c80e7..67f0792bdc 100644 --- a/lib/ns/tests/nstest.c +++ b/lib/ns/tests/nstest.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -56,7 +57,6 @@ isc_taskmgr_t *taskmgr = NULL; isc_task_t *maintask = NULL; isc_timermgr_t *timermgr = NULL; isc_socketmgr_t *socketmgr = NULL; -isc_nm_t *nm = NULL; dns_zonemgr_t *zonemgr = NULL; dns_dispatchmgr_t *dispatchmgr = NULL; ns_clientmgr_t *clientmgr = NULL; @@ -198,28 +198,9 @@ cleanup_managers(void) { if (interfacemgr != NULL) { ns_interfacemgr_detach(&interfacemgr); } - if (socketmgr != NULL) { - isc_socketmgr_destroy(&socketmgr); - } - ns_test_nap(500000); - if (nm != NULL) { - /* - * Force something in the workqueue as a workaround - * for libuv bug - not sending uv_close callback. - */ - isc_nm_pause(nm); - isc_nm_resume(nm); - isc_nm_detach(&nm); - } - if (taskmgr != NULL) { - isc_taskmgr_destroy(&taskmgr); - } - if (netmgr != NULL) { - isc_nm_destroy(&netmgr); - } - if (timermgr != NULL) { - isc_timermgr_destroy(&timermgr); - } + + isc_managers_destroy(&netmgr, &taskmgr, &timermgr, &socketmgr); + if (app_running) { isc_app_finish(); } @@ -241,24 +222,18 @@ create_managers(void) { isc_event_t *event = NULL; ncpus = isc_os_ncpus(); - netmgr = isc_nm_start(mctx, ncpus); - CHECK(isc_taskmgr_create(mctx, 0, netmgr, &taskmgr)); + isc_managers_create(mctx, ncpus, 0, 0, &netmgr, &taskmgr, &timermgr, + &socketmgr); CHECK(isc_task_create(taskmgr, 0, &maintask)); isc_taskmgr_setexcltask(taskmgr, maintask); CHECK(isc_task_onshutdown(maintask, shutdown_managers, NULL)); - CHECK(isc_timermgr_create(mctx, &timermgr)); - - CHECK(isc_socketmgr_create(mctx, &socketmgr)); - - nm = isc_nm_start(mctx, ncpus); - CHECK(ns_server_create(mctx, matchview, &sctx)); CHECK(dns_dispatchmgr_create(mctx, &dispatchmgr)); CHECK(ns_interfacemgr_create(mctx, sctx, taskmgr, timermgr, socketmgr, - nm, dispatchmgr, maintask, ncpus, NULL, + netmgr, dispatchmgr, maintask, ncpus, NULL, ncpus, &interfacemgr)); CHECK(ns_listenlist_default(mctx, port, -1, true, &listenon)); diff --git a/util/copyrights b/util/copyrights index 1e37f6a4c7..ba92b26e62 100644 --- a/util/copyrights +++ b/util/copyrights @@ -1887,6 +1887,7 @@ ./lib/isc/include/isc/list.h C 1997,1998,1999,2000,2001,2002,2004,2006,2007,2011,2012,2013,2016,2018,2019,2020,2021 ./lib/isc/include/isc/log.h C 1999,2000,2001,2002,2004,2005,2006,2007,2009,2014,2016,2017,2018,2019,2020,2021 ./lib/isc/include/isc/magic.h C 1999,2000,2001,2004,2005,2006,2007,2016,2017,2018,2019,2020,2021 +./lib/isc/include/isc/managers.h C 2021 ./lib/isc/include/isc/md.h C 2018,2019,2020,2021 ./lib/isc/include/isc/mem.h C 1997,1998,1999,2000,2001,2004,2005,2006,2007,2008,2009,2010,2011,2012,2013,2015,2016,2017,2018,2019,2020,2021 ./lib/isc/include/isc/meminfo.h C 2015,2016,2018,2019,2020,2021 @@ -1944,6 +1945,7 @@ ./lib/isc/lib.c C 1999,2000,2001,2004,2005,2007,2009,2013,2014,2015,2016,2018,2019,2020,2021 ./lib/isc/lib_p.h C 2021 ./lib/isc/log.c C 1999,2000,2001,2002,2003,2004,2005,2006,2007,2009,2011,2012,2013,2014,2016,2017,2018,2019,2020,2021 +./lib/isc/managers.c C 2021 ./lib/isc/md.c C 2018,2019,2020,2021 ./lib/isc/mem.c C 1997,1998,1999,2000,2001,2002,2003,2004,2005,2006,2007,2008,2009,2010,2012,2013,2014,2015,2016,2017,2018,2019,2020,2021 ./lib/isc/mem_p.h C 2018,2019,2020,2021 @@ -1960,6 +1962,7 @@ ./lib/isc/netmgr/uv-compat.c C 2020,2021 ./lib/isc/netmgr/uv-compat.h C 2019,2020,2021 ./lib/isc/netmgr/uverr2result.c C 2019,2020,2021 +./lib/isc/netmgr_p.h C 2021 ./lib/isc/netscope.c C 2002,2004,2005,2006,2007,2016,2018,2019,2020,2021 ./lib/isc/nonce.c C 2018,2019,2020,2021 ./lib/isc/openssl_shim.c C 2018,2019,2020,2021 @@ -1989,10 +1992,12 @@ ./lib/isc/serial.c C 1999,2000,2001,2004,2005,2007,2016,2018,2019,2020,2021 ./lib/isc/siphash.c C 2019,2020,2021 ./lib/isc/sockaddr.c C 1999,2000,2001,2002,2003,2004,2005,2006,2007,2010,2011,2012,2014,2015,2016,2017,2018,2019,2020,2021 +./lib/isc/socket_p.h C 2021 ./lib/isc/stats.c C 2009,2012,2013,2014,2015,2016,2017,2018,2019,2020,2021 ./lib/isc/string.c C 1999,2000,2001,2003,2004,2005,2006,2007,2011,2012,2014,2015,2016,2018,2019,2020,2021 ./lib/isc/symtab.c C 1996,1997,1998,1999,2000,2001,2004,2005,2007,2011,2012,2013,2016,2018,2019,2020,2021 ./lib/isc/task.c C 1998,1999,2000,2001,2002,2003,2004,2005,2006,2007,2008,2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019,2020,2021 +./lib/isc/task_p.h C 2021 ./lib/isc/taskpool.c C 1999,2000,2001,2004,2005,2007,2011,2012,2013,2016,2018,2019,2020,2021 ./lib/isc/tests/aes_test.c C 2014,2016,2018,2019,2020,2021 ./lib/isc/tests/buffer_test.c C 2014,2015,2016,2017,2018,2019,2020,2021 @@ -2031,6 +2036,7 @@ ./lib/isc/tests/timer_test.c C 2018,2019,2020,2021 ./lib/isc/tests/uv_wrap.h C 2020,2021 ./lib/isc/timer.c C 1998,1999,2000,2001,2002,2004,2005,2007,2008,2009,2011,2012,2013,2014,2015,2016,2017,2018,2019,2020,2021 +./lib/isc/timer_p.h C 2021 ./lib/isc/tls.c C 2021 ./lib/isc/tls_p.h C 2021 ./lib/isc/tm.c C 2014,2016,2018,2019,2020,2021 @@ -2059,7 +2065,6 @@ ./lib/isc/unix/pk11_api.c C 2014,2016,2018,2019,2020,2021 ./lib/isc/unix/resource.c C 2000,2001,2004,2007,2008,2009,2016,2018,2019,2020,2021 ./lib/isc/unix/socket.c C 1998,1999,2000,2001,2002,2003,2004,2005,2006,2007,2008,2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019,2020,2021 -./lib/isc/unix/socket_p.h C 2000,2001,2004,2005,2007,2008,2009,2016,2018,2019,2020,2021 ./lib/isc/unix/stdio.c C 2000,2001,2004,2007,2011,2012,2013,2014,2016,2018,2019,2020,2021 ./lib/isc/unix/stdtime.c C 1999,2000,2001,2004,2005,2007,2016,2018,2019,2020,2021 ./lib/isc/unix/syslog.c C 2001,2004,2005,2007,2016,2018,2019,2020,2021 From b5bf58b41955238450362426d5d42617e39ced51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Sur=C3=BD?= Date: Tue, 27 Apr 2021 10:28:40 +0200 Subject: [PATCH 02/13] Destroy netmgr before destroying taskmgr With taskmgr running on top of netmgr, the ordering of how the tasks and netmgr shutdown interacts was wrong as previously isc_taskmgr_destroy() was waiting until all tasks were properly shutdown and detached. This responsibility was moved to netmgr, so we now need to do the following: 1. shutdown all the tasks - this schedules all shutdown events onto the netmgr queue 2. shutdown the netmgr - this also makes sure all the tasks and events are properly executed 3. Shutdown the taskmgr - this now waits for all the tasks to finish running before returning 4. Shutdown the netmgr - this call waits for all the netmgr netievents to finish before returning This solves the race when the taskmgr object would be destroyed before all the tasks were finished running in the netmgr loops. --- bin/tests/test_client.c | 2 - lib/dns/tests/dnstest.c | 5 ++- lib/isc/include/isc/netmgr.h | 9 ---- lib/isc/managers.c | 49 ++++++++++++++++++--- lib/isc/netmgr/netmgr.c | 4 +- lib/isc/netmgr_p.h | 11 ++++- lib/isc/task.c | 22 +++++----- lib/isc/task_p.h | 18 ++++++++ lib/isc/tests/doh_test.c | 18 ++++---- lib/isc/tests/isctest.c | 5 ++- lib/isc/tests/netmgr_test.c | 82 ++++++++++++++++++------------------ lib/isc/win32/libisc.def.in | 1 - lib/ns/client.c | 27 +++++------- lib/ns/tests/nstest.c | 5 ++- 14 files changed, 155 insertions(+), 103 deletions(-) diff --git a/bin/tests/test_client.c b/bin/tests/test_client.c index 7c9f10e2c6..6337406298 100644 --- a/bin/tests/test_client.c +++ b/bin/tests/test_client.c @@ -465,8 +465,6 @@ run(void) { } waitforsignal(); - - isc_nm_closedown(netmgr); } int diff --git a/lib/dns/tests/dnstest.c b/lib/dns/tests/dnstest.c index 6a247b6b05..c14eed36e8 100644 --- a/lib/dns/tests/dnstest.c +++ b/lib/dns/tests/dnstest.c @@ -96,7 +96,10 @@ cleanup_managers(void) { isc_task_shutdown(maintask); isc_task_destroy(&maintask); } - isc_managers_destroy(&netmgr, &taskmgr, &timermgr, &socketmgr); + isc_managers_destroy(netmgr == NULL ? NULL : &netmgr, + taskmgr == NULL ? NULL : &taskmgr, + timermgr == NULL ? NULL : &timermgr, + socketmgr == NULL ? NULL : &socketmgr); if (app_running) { isc_app_finish(); } diff --git a/lib/isc/include/isc/netmgr.h b/lib/isc/include/isc/netmgr.h index e0a1719026..7ce897786b 100644 --- a/lib/isc/include/isc/netmgr.h +++ b/lib/isc/include/isc/netmgr.h @@ -79,15 +79,6 @@ isc_nm_detach(isc_nm_t **mgr0); * for all other references to be gone. */ -void -isc_nm_closedown(isc_nm_t *mgr); -/*%< - * Close down all active connections, freeing associated resources; - * prevent new connections from being established. This can optionally - * be called prior to shutting down the netmgr, to stop all processing - * before shutting down the task manager. - */ - /* Return thread ID of current thread, or ISC_NETMGR_TID_UNKNOWN */ int isc_nm_tid(void); diff --git a/lib/isc/managers.c b/lib/isc/managers.c index 8a17fd1e05..f86d4c8485 100644 --- a/lib/isc/managers.c +++ b/lib/isc/managers.c @@ -42,6 +42,7 @@ isc_managers_create(isc_mem_t *mctx, size_t workers, size_t quantum, REQUIRE(taskmgrp == NULL || *taskmgrp == NULL); if (taskmgrp != NULL) { + INSIST(netmgr != NULL); result = isc__taskmgr_create(mctx, quantum, netmgr, &taskmgr); if (result != ISC_R_SUCCESS) { UNEXPECTED_ERROR(__FILE__, __LINE__, @@ -87,23 +88,57 @@ fail: void isc_managers_destroy(isc_nm_t **netmgrp, isc_taskmgr_t **taskmgrp, isc_timermgr_t **timermgrp, isc_socketmgr_t **socketmgrp) { - if (netmgrp != NULL && *netmgrp != NULL) { - isc_nm_closedown(*netmgrp); + /* + * If we have a taskmgr to clean up, then we must also have a netmgr. + */ + REQUIRE(taskmgrp != NULL || netmgrp == NULL); + + /* + * The sequence of operations here is important: + * + * 1. Initiate shutdown of the taskmgr, sending shutdown events to + * all tasks that are not already shutting down. + */ + if (taskmgrp != NULL) { + INSIST(*taskmgrp != NULL); + isc__taskmgr_shutdown(*taskmgrp); } - if (taskmgrp != NULL && *taskmgrp != NULL) { + /* + * 2. Initiate shutdown of the network manager, freeing clients + * and other resources and preventing new connections, but do + * not stop processing of existing events. + */ + if (netmgrp != NULL) { + INSIST(*netmgrp != NULL); + isc__netmgr_shutdown(*netmgrp); + } + + /* + * 3. Finish destruction of the task manager when all tasks + * have completed. + */ + if (taskmgrp != NULL) { isc__taskmgr_destroy(taskmgrp); } - if (netmgrp != NULL && *netmgrp != NULL) { + /* + * 4. Finish destruction of the netmgr, and wait until all + * references have been released. + */ + if (netmgrp != NULL) { isc__netmgr_destroy(netmgrp); } - if (timermgrp != NULL && *timermgrp != NULL) { + /* + * 5. Clean up the remaining managers. + */ + if (timermgrp != NULL) { + INSIST(*timermgrp != NULL); isc__timermgr_destroy(timermgrp); } - - if (socketmgrp != NULL && *socketmgrp != NULL) { + if (socketmgrp != NULL) { + INSIST(*socketmgrp != NULL); isc__socketmgr_destroy(socketmgrp); } } diff --git a/lib/isc/netmgr/netmgr.c b/lib/isc/netmgr/netmgr.c index 7acaca8791..85a1f1714a 100644 --- a/lib/isc/netmgr/netmgr.c +++ b/lib/isc/netmgr/netmgr.c @@ -474,7 +474,7 @@ isc_nm_detach(isc_nm_t **mgr0) { } void -isc_nm_closedown(isc_nm_t *mgr) { +isc__netmgr_shutdown(isc_nm_t *mgr) { REQUIRE(VALID_NM(mgr)); atomic_store(&mgr->closing, true); @@ -497,7 +497,7 @@ isc__netmgr_destroy(isc_nm_t **netmgrp) { /* * Close active connections. */ - isc_nm_closedown(mgr); + isc__netmgr_shutdown(mgr); /* * Wait for the manager to be dereferenced elsewhere. diff --git a/lib/isc/netmgr_p.h b/lib/isc/netmgr_p.h index ce934d51c9..8f33c84160 100644 --- a/lib/isc/netmgr_p.h +++ b/lib/isc/netmgr_p.h @@ -24,6 +24,13 @@ isc__netmgr_create(isc_mem_t *mctx, uint32_t workers, isc_nm_t **netgmrp); void isc__netmgr_destroy(isc_nm_t **netmgrp); /*%< - * Destroy is working the same way as isc_nm_detach, but it actively waits - * for all other references to be gone. + * Similar to isc_nm_detach(), but actively waits for all other references + * to be gone before returning. + */ + +void +isc__netmgr_shutdown(isc_nm_t *mgr); +/*%< + * Shut down all active connections, freeing associated resources; + * prevent new connections from being established. */ diff --git a/lib/isc/task.c b/lib/isc/task.c index 2c987875b3..49112f22e7 100644 --- a/lib/isc/task.c +++ b/lib/isc/task.c @@ -1008,19 +1008,12 @@ isc__taskmgr_create(isc_mem_t *mctx, unsigned int default_quantum, isc_nm_t *nm, } void -isc__taskmgr_destroy(isc_taskmgr_t **managerp) { - isc_taskmgr_t *manager; +isc__taskmgr_shutdown(isc_taskmgr_t *manager) { isc_task_t *task; - /* - * Destroy '*managerp'. - */ - - REQUIRE(managerp != NULL); - manager = *managerp; REQUIRE(VALID_MANAGER(manager)); - XTHREADTRACE("isc_taskmgr_destroy"); + XTHREADTRACE(e "isc_taskmgr_shutdown"); /* * Only one non-worker thread may ever call this routine. * If a worker thread wants to initiate shutdown of the @@ -1070,10 +1063,19 @@ isc__taskmgr_destroy(isc_taskmgr_t **managerp) { } UNLOCK(&manager->lock); +} - isc_taskmgr_detach(manager); +void +isc__taskmgr_destroy(isc_taskmgr_t **managerp) { + REQUIRE(managerp != NULL && VALID_MANAGER(*managerp)); + isc_taskmgr_t *manager = *managerp; *managerp = NULL; + + XTHREADTRACE("isc_taskmgr_destroy"); + + REQUIRE(isc_refcount_decrement(&manager->references) == 1); + manager_free(manager); } void diff --git a/lib/isc/task_p.h b/lib/isc/task_p.h index fbf3eb1c56..4dda7d63ba 100644 --- a/lib/isc/task_p.h +++ b/lib/isc/task_p.h @@ -77,6 +77,24 @@ isc__taskmgr_destroy(isc_taskmgr_t **managerp); * *\li '*managerp' is a valid task manager. * + *\li 'isc__taskmgr_shutdown()' and isc__netmgr_shutdown() have been + * called. + */ + +void +isc__taskmgr_shutdown(isc_taskmgr_t *manager); +/*%> + * Shutdown 'manager'. + * + * Notes: + * + *\li Calling isc__taskmgr_shutdown() will shut down all tasks managed by + * *managerp that haven't already been shut down. + * + * Requires: + * + *\li 'manager' is a valid task manager. + * *\li isc_taskmgr_destroy() has not be called previously on '*managerp'. * * Ensures: diff --git a/lib/isc/tests/doh_test.c b/lib/isc/tests/doh_test.c index a04a000a68..1fa9d51fd3 100644 --- a/lib/isc/tests/doh_test.c +++ b/lib/isc/tests/doh_test.c @@ -499,7 +499,7 @@ doh_noop(void **state) { .length = send_msg.len }, noop_read_cb, NULL, atomic_load(&use_TLS), 30000); - isc_nm_closedown(connect_nm); + isc__netmgr_shutdown(connect_nm); assert_int_equal(0, atomic_load(&csends)); assert_int_equal(0, atomic_load(&creads)); @@ -546,7 +546,7 @@ doh_noresponse(void **state) { isc_nm_stoplistening(listen_sock); isc_nmsocket_close(&listen_sock); assert_null(listen_sock); - isc_nm_closedown(connect_nm); + isc__netmgr_shutdown(connect_nm); } static void @@ -667,7 +667,7 @@ doh_timeout_recovery(void **state) { isc_nm_stoplistening(listen_sock); isc_nmsocket_close(&listen_sock); assert_null(listen_sock); - isc_nm_closedown(connect_nm); + isc__netmgr_shutdown(connect_nm); } static void @@ -793,7 +793,7 @@ doh_recv_one(void **state) { isc_nm_stoplistening(listen_sock); isc_nmsocket_close(&listen_sock); assert_null(listen_sock); - isc_nm_closedown(connect_nm); + isc__netmgr_shutdown(connect_nm); X(csends); X(creads); @@ -914,7 +914,7 @@ doh_recv_two(void **state) { isc_nm_stoplistening(listen_sock); isc_nmsocket_close(&listen_sock); assert_null(listen_sock); - isc_nm_closedown(connect_nm); + isc__netmgr_shutdown(connect_nm); X(csends); X(creads); @@ -980,7 +980,7 @@ doh_recv_send(void **state) { isc_thread_join(threads[i], NULL); } - isc_nm_closedown(connect_nm); + isc__netmgr_shutdown(connect_nm); isc_nm_stoplistening(listen_sock); isc_nmsocket_close(&listen_sock); assert_null(listen_sock); @@ -1051,7 +1051,7 @@ doh_recv_half_send(void **state) { isc_thread_yield(); } - isc_nm_closedown(connect_nm); + isc__netmgr_shutdown(connect_nm); for (size_t i = 0; i < nthreads; i++) { isc_thread_join(threads[i], NULL); @@ -1135,7 +1135,7 @@ doh_half_recv_send(void **state) { isc_thread_join(threads[i], NULL); } - isc_nm_closedown(connect_nm); + isc__netmgr_shutdown(connect_nm); X(csends); X(creads); @@ -1203,7 +1203,7 @@ doh_half_recv_half_send(void **state) { isc_thread_yield(); } - isc_nm_closedown(connect_nm); + isc__netmgr_shutdown(connect_nm); isc_nm_stoplistening(listen_sock); isc_nmsocket_close(&listen_sock); assert_null(listen_sock); diff --git a/lib/isc/tests/isctest.c b/lib/isc/tests/isctest.c index bf35210251..89ef18c5c7 100644 --- a/lib/isc/tests/isctest.c +++ b/lib/isc/tests/isctest.c @@ -59,7 +59,10 @@ cleanup_managers(void) { isc_task_shutdown(maintask); isc_task_destroy(&maintask); } - isc_managers_destroy(&netmgr, &taskmgr, &timermgr, &socketmgr); + isc_managers_destroy(netmgr == NULL ? NULL : &netmgr, + taskmgr == NULL ? NULL : &taskmgr, + timermgr == NULL ? NULL : &timermgr, + socketmgr == NULL ? NULL : &socketmgr); } static isc_result_t diff --git a/lib/isc/tests/netmgr_test.c b/lib/isc/tests/netmgr_test.c index 661133a92d..2a1516bffc 100644 --- a/lib/isc/tests/netmgr_test.c +++ b/lib/isc/tests/netmgr_test.c @@ -678,7 +678,7 @@ mock_udpconnect_uv_udp_open(void **state __attribute__((unused))) { isc_nm_udpconnect(connect_nm, (isc_nmiface_t *)&udp_connect_addr, (isc_nmiface_t *)&udp_listen_addr, connect_connect_cb, NULL, T_CONNECT, 0); - isc_nm_closedown(connect_nm); + isc__netmgr_shutdown(connect_nm); RESET_RETURN; } @@ -692,7 +692,7 @@ mock_udpconnect_uv_udp_bind(void **state __attribute__((unused))) { isc_nm_udpconnect(connect_nm, (isc_nmiface_t *)&udp_connect_addr, (isc_nmiface_t *)&udp_listen_addr, connect_connect_cb, NULL, T_CONNECT, 0); - isc_nm_closedown(connect_nm); + isc__netmgr_shutdown(connect_nm); RESET_RETURN; } @@ -707,7 +707,7 @@ mock_udpconnect_uv_udp_connect(void **state __attribute__((unused))) { isc_nm_udpconnect(connect_nm, (isc_nmiface_t *)&udp_connect_addr, (isc_nmiface_t *)&udp_listen_addr, connect_connect_cb, NULL, T_CONNECT, 0); - isc_nm_closedown(connect_nm); + isc__netmgr_shutdown(connect_nm); RESET_RETURN; } @@ -722,7 +722,7 @@ mock_udpconnect_uv_recv_buffer_size(void **state __attribute__((unused))) { isc_nm_udpconnect(connect_nm, (isc_nmiface_t *)&udp_connect_addr, (isc_nmiface_t *)&udp_listen_addr, connect_connect_cb, NULL, T_CONNECT, 0); - isc_nm_closedown(connect_nm); + isc__netmgr_shutdown(connect_nm); RESET_RETURN; } @@ -736,7 +736,7 @@ mock_udpconnect_uv_send_buffer_size(void **state __attribute__((unused))) { isc_nm_udpconnect(connect_nm, (isc_nmiface_t *)&udp_connect_addr, (isc_nmiface_t *)&udp_listen_addr, connect_connect_cb, NULL, T_CONNECT, 0); - isc_nm_closedown(connect_nm); + isc__netmgr_shutdown(connect_nm); RESET_RETURN; } @@ -759,7 +759,7 @@ udp_noop(void **state __attribute__((unused))) { isc_nm_udpconnect(connect_nm, (isc_nmiface_t *)&udp_connect_addr, (isc_nmiface_t *)&udp_listen_addr, connect_connect_cb, NULL, T_CONNECT, 0); - isc_nm_closedown(connect_nm); + isc__netmgr_shutdown(connect_nm); atomic_assert_int_eq(cconnects, 0); atomic_assert_int_eq(csends, 0); @@ -788,7 +788,7 @@ udp_noresponse(void **state __attribute__((unused))) { isc_nm_stoplistening(listen_sock); isc_nmsocket_close(&listen_sock); assert_null(listen_sock); - isc_nm_closedown(connect_nm); + isc__netmgr_shutdown(connect_nm); X(cconnects); X(csends); @@ -861,7 +861,7 @@ udp_timeout_recovery(void **state __attribute__((unused))) { isc_nm_stoplistening(listen_sock); isc_nmsocket_close(&listen_sock); assert_null(listen_sock); - isc_nm_closedown(connect_nm); + isc__netmgr_shutdown(connect_nm); } static void @@ -890,7 +890,7 @@ udp_recv_one(void **state __attribute__((unused))) { isc_nm_stoplistening(listen_sock); isc_nmsocket_close(&listen_sock); assert_null(listen_sock); - isc_nm_closedown(connect_nm); + isc__netmgr_shutdown(connect_nm); X(cconnects); X(csends); @@ -938,7 +938,7 @@ udp_recv_two(void **state __attribute__((unused))) { isc_nm_stoplistening(listen_sock); isc_nmsocket_close(&listen_sock); assert_null(listen_sock); - isc_nm_closedown(connect_nm); + isc__netmgr_shutdown(connect_nm); X(cconnects); X(csends); @@ -981,7 +981,7 @@ udp_recv_send(void **state __attribute__((unused))) { isc_thread_join(threads[i], NULL); } - isc_nm_closedown(connect_nm); + isc__netmgr_shutdown(connect_nm); isc_nm_stoplistening(listen_sock); isc_nmsocket_close(&listen_sock); assert_null(listen_sock); @@ -1021,7 +1021,7 @@ udp_recv_half_send(void **state __attribute__((unused))) { WAIT_FOR_GE(ssends, esends / 2); WAIT_FOR_GE(creads, esends / 2); - isc_nm_closedown(connect_nm); + isc__netmgr_shutdown(connect_nm); DONE(); for (size_t i = 0; i < workers; i++) { @@ -1074,7 +1074,7 @@ udp_half_recv_send(void **state __attribute__((unused))) { /* Try to send a little while longer */ isc_test_nap((esends / 2) * 10000); - isc_nm_closedown(connect_nm); + isc__netmgr_shutdown(connect_nm); DONE(); for (size_t i = 0; i < workers; i++) { @@ -1116,7 +1116,7 @@ udp_half_recv_half_send(void **state __attribute__((unused))) { WAIT_FOR_GE(ssends, esends / 2); WAIT_FOR_GE(creads, esends / 2); - isc_nm_closedown(connect_nm); + isc__netmgr_shutdown(connect_nm); isc_nm_stoplistening(listen_sock); isc_nmsocket_close(&listen_sock); assert_null(listen_sock); @@ -1219,7 +1219,7 @@ stream_noop(void **state __attribute__((unused))) { connect_readcb = NULL; isc_refcount_increment0(&active_cconnects); stream_connect(connect_connect_cb, NULL, T_CONNECT, 0); - isc_nm_closedown(connect_nm); + isc__netmgr_shutdown(connect_nm); atomic_assert_int_eq(cconnects, 0); atomic_assert_int_eq(csends, 0); @@ -1245,7 +1245,7 @@ stream_noresponse(void **state __attribute__((unused))) { isc_nm_stoplistening(listen_sock); isc_nmsocket_close(&listen_sock); assert_null(listen_sock); - isc_nm_closedown(connect_nm); + isc__netmgr_shutdown(connect_nm); X(cconnects); X(csends); @@ -1295,7 +1295,7 @@ stream_timeout_recovery(void **state __attribute__((unused))) { isc_nm_stoplistening(listen_sock); isc_nmsocket_close(&listen_sock); assert_null(listen_sock); - isc_nm_closedown(connect_nm); + isc__netmgr_shutdown(connect_nm); } static void @@ -1323,7 +1323,7 @@ stream_recv_one(void **state __attribute__((unused))) { isc_nm_stoplistening(listen_sock); isc_nmsocket_close(&listen_sock); assert_null(listen_sock); - isc_nm_closedown(connect_nm); + isc__netmgr_shutdown(connect_nm); X(cconnects); X(csends); @@ -1368,7 +1368,7 @@ stream_recv_two(void **state __attribute__((unused))) { isc_nm_stoplistening(listen_sock); isc_nmsocket_close(&listen_sock); assert_null(listen_sock); - isc_nm_closedown(connect_nm); + isc__netmgr_shutdown(connect_nm); X(cconnects); X(csends); @@ -1417,7 +1417,7 @@ stream_recv_send(void **state __attribute__((unused))) { isc_thread_join(threads[i], NULL); } - isc_nm_closedown(connect_nm); + isc__netmgr_shutdown(connect_nm); isc_nm_stoplistening(listen_sock); isc_nmsocket_close(&listen_sock); assert_null(listen_sock); @@ -1463,7 +1463,7 @@ stream_recv_half_send(void **state __attribute__((unused))) { WAIT_FOR_GE(ssends, esends / 2); WAIT_FOR_GE(creads, esends / 2); - isc_nm_closedown(connect_nm); + isc__netmgr_shutdown(connect_nm); DONE(); for (size_t i = 0; i < workers; i++) { @@ -1522,7 +1522,7 @@ stream_half_recv_send(void **state __attribute__((unused))) { /* Try to send a little while longer */ isc_test_nap((esends / 2) * 10000); - isc_nm_closedown(connect_nm); + isc__netmgr_shutdown(connect_nm); DONE(); for (size_t i = 0; i < workers; i++) { @@ -1570,7 +1570,7 @@ stream_half_recv_half_send(void **state __attribute__((unused))) { WAIT_FOR_GE(ssends, esends / 2); WAIT_FOR_GE(creads, esends / 2); - isc_nm_closedown(connect_nm); + isc__netmgr_shutdown(connect_nm); isc_nm_stoplistening(listen_sock); isc_nmsocket_close(&listen_sock); assert_null(listen_sock); @@ -1768,7 +1768,7 @@ tcpdns_noop(void **state __attribute__((unused))) { isc_nm_tcpdnsconnect(connect_nm, (isc_nmiface_t *)&tcp_connect_addr, (isc_nmiface_t *)&tcp_listen_addr, connect_connect_cb, NULL, T_CONNECT, 0); - isc_nm_closedown(connect_nm); + isc__netmgr_shutdown(connect_nm); atomic_assert_int_eq(cconnects, 0); atomic_assert_int_eq(csends, 0); @@ -1802,7 +1802,7 @@ tcpdns_noresponse(void **state __attribute__((unused))) { isc_nm_stoplistening(listen_sock); isc_nmsocket_close(&listen_sock); assert_null(listen_sock); - isc_nm_closedown(connect_nm); + isc__netmgr_shutdown(connect_nm); X(cconnects); X(csends); @@ -1857,7 +1857,7 @@ tcpdns_timeout_recovery(void **state __attribute__((unused))) { isc_nm_stoplistening(listen_sock); isc_nmsocket_close(&listen_sock); assert_null(listen_sock); - isc_nm_closedown(connect_nm); + isc__netmgr_shutdown(connect_nm); } static void @@ -1887,7 +1887,7 @@ tcpdns_recv_one(void **state __attribute__((unused))) { isc_nm_stoplistening(listen_sock); isc_nmsocket_close(&listen_sock); assert_null(listen_sock); - isc_nm_closedown(connect_nm); + isc__netmgr_shutdown(connect_nm); X(cconnects); X(csends); @@ -1937,7 +1937,7 @@ tcpdns_recv_two(void **state __attribute__((unused))) { isc_nm_stoplistening(listen_sock); isc_nmsocket_close(&listen_sock); assert_null(listen_sock); - isc_nm_closedown(connect_nm); + isc__netmgr_shutdown(connect_nm); X(cconnects); X(csends); @@ -1981,7 +1981,7 @@ tcpdns_recv_send(void **state __attribute__((unused))) { isc_thread_join(threads[i], NULL); } - isc_nm_closedown(connect_nm); + isc__netmgr_shutdown(connect_nm); isc_nm_stoplistening(listen_sock); isc_nmsocket_close(&listen_sock); assert_null(listen_sock); @@ -2022,7 +2022,7 @@ tcpdns_recv_half_send(void **state __attribute__((unused))) { WAIT_FOR_GE(ssends, esends / 2); WAIT_FOR_GE(creads, esends / 2); - isc_nm_closedown(connect_nm); + isc__netmgr_shutdown(connect_nm); DONE(); for (size_t i = 0; i < workers; i++) { @@ -2076,7 +2076,7 @@ tcpdns_half_recv_send(void **state __attribute__((unused))) { /* Try to send a little while longer */ isc_test_nap((esends / 2) * 10000); - isc_nm_closedown(connect_nm); + isc__netmgr_shutdown(connect_nm); DONE(); for (size_t i = 0; i < workers; i++) { @@ -2119,7 +2119,7 @@ tcpdns_half_recv_half_send(void **state __attribute__((unused))) { WAIT_FOR_GE(ssends, esends / 2); WAIT_FOR_GE(creads, esends / 2); - isc_nm_closedown(connect_nm); + isc__netmgr_shutdown(connect_nm); isc_nm_stoplistening(listen_sock); isc_nmsocket_close(&listen_sock); assert_null(listen_sock); @@ -2356,7 +2356,7 @@ tlsdns_noop(void **state __attribute__((unused))) { connect_connect_cb, NULL, T_CONNECT, 0, tcp_connect_tlsctx); - isc_nm_closedown(connect_nm); + isc__netmgr_shutdown(connect_nm); atomic_assert_int_eq(cconnects, 0); atomic_assert_int_eq(csends, 0); @@ -2392,7 +2392,7 @@ tlsdns_noresponse(void **state __attribute__((unused))) { isc_nm_stoplistening(listen_sock); isc_nmsocket_close(&listen_sock); assert_null(listen_sock); - isc_nm_closedown(connect_nm); + isc__netmgr_shutdown(connect_nm); X(cconnects); X(csends); @@ -2453,7 +2453,7 @@ tlsdns_timeout_recovery(void **state __attribute__((unused))) { isc_nm_stoplistening(listen_sock); isc_nmsocket_close(&listen_sock); assert_null(listen_sock); - isc_nm_closedown(connect_nm); + isc__netmgr_shutdown(connect_nm); } static void @@ -2485,7 +2485,7 @@ tlsdns_recv_one(void **state __attribute__((unused))) { isc_nm_stoplistening(listen_sock); isc_nmsocket_close(&listen_sock); assert_null(listen_sock); - isc_nm_closedown(connect_nm); + isc__netmgr_shutdown(connect_nm); X(cconnects); X(csends); @@ -2538,7 +2538,7 @@ tlsdns_recv_two(void **state __attribute__((unused))) { isc_nm_stoplistening(listen_sock); isc_nmsocket_close(&listen_sock); assert_null(listen_sock); - isc_nm_closedown(connect_nm); + isc__netmgr_shutdown(connect_nm); X(cconnects); X(csends); @@ -2583,7 +2583,7 @@ tlsdns_recv_send(void **state __attribute__((unused))) { isc_thread_join(threads[i], NULL); } - isc_nm_closedown(connect_nm); + isc__netmgr_shutdown(connect_nm); isc_nm_stoplistening(listen_sock); isc_nmsocket_close(&listen_sock); assert_null(listen_sock); @@ -2625,7 +2625,7 @@ tlsdns_recv_half_send(void **state __attribute__((unused))) { WAIT_FOR_GE(ssends, esends / 2); WAIT_FOR_GE(creads, esends / 2); - isc_nm_closedown(connect_nm); + isc__netmgr_shutdown(connect_nm); DONE(); for (size_t i = 0; i < workers; i++) { @@ -2680,7 +2680,7 @@ tlsdns_half_recv_send(void **state __attribute__((unused))) { /* Try to send a little while longer */ isc_test_nap((esends / 2) * 10000); - isc_nm_closedown(connect_nm); + isc__netmgr_shutdown(connect_nm); DONE(); for (size_t i = 0; i < workers; i++) { @@ -2724,7 +2724,7 @@ tlsdns_half_recv_half_send(void **state __attribute__((unused))) { WAIT_FOR_GE(ssends, esends / 2); WAIT_FOR_GE(creads, esends / 2); - isc_nm_closedown(connect_nm); + isc__netmgr_shutdown(connect_nm); isc_nm_stoplistening(listen_sock); isc_nmsocket_close(&listen_sock); assert_null(listen_sock); diff --git a/lib/isc/win32/libisc.def.in b/lib/isc/win32/libisc.def.in index d5ae24177c..3c4dd02f71 100644 --- a/lib/isc/win32/libisc.def.in +++ b/lib/isc/win32/libisc.def.in @@ -451,7 +451,6 @@ isc_nmhandle_peeraddr isc_nmhandle_setdata isc_nmhandle_settimeout isc_nm_cancelread -isc_nm_closedown isc_nm_detach isc_nm_http_endpoint isc_nm_httpconnect diff --git a/lib/ns/client.c b/lib/ns/client.c index 16e14be4c9..bc0afe1a19 100644 --- a/lib/ns/client.c +++ b/lib/ns/client.c @@ -1629,15 +1629,13 @@ ns__client_put_cb(void *client0) { void ns__client_request(isc_nmhandle_t *handle, isc_result_t eresult, isc_region_t *region, void *arg) { - ns_client_t *client; - ns_clientmgr_t *mgr; - ns_interface_t *ifp; + ns_client_t *client = NULL; isc_result_t result; isc_result_t sigresult = ISC_R_SUCCESS; - isc_buffer_t *buffer; + isc_buffer_t *buffer = NULL; isc_buffer_t tbuffer; - dns_rdataset_t *opt; - const dns_name_t *signame; + dns_rdataset_t *opt = NULL; + const dns_name_t *signame = NULL; bool ra; /* Recursion available. */ isc_netaddr_t netaddr; int match; @@ -1645,29 +1643,24 @@ ns__client_request(isc_nmhandle_t *handle, isc_result_t eresult, unsigned int flags; bool notimp; size_t reqsize; - dns_aclenv_t *env; + dns_aclenv_t *env = NULL; #ifdef HAVE_DNSTAP dns_dtmsgtype_t dtmsgtype; #endif /* ifdef HAVE_DNSTAP */ - ifp = (ns_interface_t *)arg; if (eresult != ISC_R_SUCCESS) { return; } - mgr = ifp->clientmgr; - if (mgr == NULL) { - /* The interface was shut down in the meantime, just bail */ - return; - } - - REQUIRE(VALID_MANAGER(mgr)); - client = isc_nmhandle_getdata(handle); if (client == NULL) { + ns_interface_t *ifp = (ns_interface_t *)arg; + + INSIST(VALID_MANAGER(ifp->clientmgr)); + client = isc_nmhandle_getextra(handle); - result = ns__client_setup(client, mgr, true); + result = ns__client_setup(client, ifp->clientmgr, true); if (result != ISC_R_SUCCESS) { return; } diff --git a/lib/ns/tests/nstest.c b/lib/ns/tests/nstest.c index 67f0792bdc..b291335afa 100644 --- a/lib/ns/tests/nstest.c +++ b/lib/ns/tests/nstest.c @@ -199,7 +199,10 @@ cleanup_managers(void) { ns_interfacemgr_detach(&interfacemgr); } - isc_managers_destroy(&netmgr, &taskmgr, &timermgr, &socketmgr); + isc_managers_destroy(netmgr == NULL ? NULL : &netmgr, + taskmgr == NULL ? NULL : &taskmgr, + timermgr == NULL ? NULL : &timermgr, + socketmgr == NULL ? NULL : &socketmgr); if (app_running) { isc_app_finish(); From dacf586e186e464adddd781cf4b96464400ae0a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Sur=C3=BD?= Date: Tue, 27 Apr 2021 12:03:34 +0200 Subject: [PATCH 03/13] Make the netmgr queue processing quantized There was a theoretical possibility of clogging up the queue processing with an endless loop where currently processing netievent would schedule new netievent that would get processed immediately. This wasn't such a problem when only netmgr netievents were processed, but with the addition of the tasks, there are at least two situation where this could happen: 1. In lib/dns/zone.c:setnsec3param() the task would get re-enqueued when the zone was not yet fully loaded. 2. Tasks have internal quantum for maximum number of isc_events to be processed, when the task quantum is reached, the task would get rescheduled and then immediately processed by the netmgr queue processing. As the isc_queue doesn't have a mechanism to atomically move the queue, this commit adds a mechanism to quantize the queue, so enqueueing new netievents will never stop processing other uv_loop_t events. The default quantum size is 128. Since the queue used in the network manager allows items to be enqueued more than once, tasks are now reference-counted around task_ready() and task_run(). task_ready() now has a public API wrapper, isc_task_ready(), that the netmgr can use to reschedule processing of a task if the quantum has been reached. Incidental changes: Cleaned up some unused fields left in isc_task_t and isc_taskmgr_t after the last refactoring, and changed atomic flags to atomic_bools for easier manipulation. --- lib/isc/include/isc/task.h | 6 ++ lib/isc/netmgr/netmgr-int.h | 3 + lib/isc/netmgr/netmgr.c | 121 ++++++++++++++++++++---------------- lib/isc/task.c | 120 ++++++++++++++--------------------- lib/isc/win32/libisc.def.in | 1 + 5 files changed, 125 insertions(+), 126 deletions(-) diff --git a/lib/isc/include/isc/task.h b/lib/isc/include/isc/task.h index f8a790403e..45d062ee32 100644 --- a/lib/isc/include/isc/task.h +++ b/lib/isc/include/isc/task.h @@ -136,6 +136,12 @@ isc_task_create_bound(isc_taskmgr_t *manager, unsigned int quantum, *\li #ISC_R_SHUTTINGDOWN */ +void +isc_task_ready(isc_task_t *task); +/*%< + * Enqueue the task onto netmgr queue. + */ + isc_result_t isc_task_run(isc_task_t *task); /*%< diff --git a/lib/isc/netmgr/netmgr-int.h b/lib/isc/netmgr/netmgr-int.h index 4384932dca..6db3f93d05 100644 --- a/lib/isc/netmgr/netmgr-int.h +++ b/lib/isc/netmgr/netmgr-int.h @@ -39,6 +39,8 @@ #include "uv-compat.h" +#define ISC_NETMGR_QUANTUM_DEFAULT 128 + #define ISC_NETMGR_TID_UNKNOWN -1 /* Must be different from ISC_NETMGR_TID_UNKNOWN */ @@ -188,6 +190,7 @@ typedef struct isc__networker { char *recvbuf; char *sendbuf; bool recvbuf_inuse; + unsigned int quantum; } isc__networker_t; /* diff --git a/lib/isc/netmgr/netmgr.c b/lib/isc/netmgr/netmgr.c index 85a1f1714a..f080e8f52c 100644 --- a/lib/isc/netmgr/netmgr.c +++ b/lib/isc/netmgr/netmgr.c @@ -135,15 +135,16 @@ nm_thread(isc_threadarg_t worker0); static void async_cb(uv_async_t *handle); static bool -process_queue(isc__networker_t *worker, isc_queue_t *queue); +process_queue(isc__networker_t *worker, isc_queue_t *queue, + unsigned int *quantump); static bool -process_priority_queue(isc__networker_t *worker); -static void -process_privilege_queue(isc__networker_t *worker); -static void -process_tasks_queue(isc__networker_t *worker); -static void -process_normal_queue(isc__networker_t *worker); +process_priority_queue(isc__networker_t *worker, unsigned int *quantump); +static bool +process_privilege_queue(isc__networker_t *worker, unsigned int *quantump); +static bool +process_task_queue(isc__networker_t *worker, unsigned int *quantump); +static bool +process_normal_queue(isc__networker_t *worker, unsigned int *quantump); static void isc__nm_async_stop(isc__networker_t *worker, isc__netievent_t *ev0); @@ -264,6 +265,7 @@ isc__netmgr_create(isc_mem_t *mctx, uint32_t workers, isc_nm_t **netmgrp) { *worker = (isc__networker_t){ .mgr = mgr, .id = i, + .quantum = ISC_NETMGR_QUANTUM_DEFAULT, }; r = uv_loop_init(&worker->loop); @@ -278,8 +280,8 @@ isc__netmgr_create(isc_mem_t *mctx, uint32_t workers, isc_nm_t **netmgrp) { isc_condition_init(&worker->cond); worker->ievents = isc_queue_new(mgr->mctx, 128); - worker->ievents_priv = isc_queue_new(mgr->mctx, 128); worker->ievents_task = isc_queue_new(mgr->mctx, 128); + worker->ievents_priv = isc_queue_new(mgr->mctx, 128); worker->ievents_prio = isc_queue_new(mgr->mctx, 128); worker->recvbuf = isc_mem_get(mctx, ISC_NETMGR_RECVBUF_SIZE); worker->sendbuf = isc_mem_get(mctx, ISC_NETMGR_SENDBUF_SIZE); @@ -631,7 +633,8 @@ nm_thread(isc_threadarg_t worker0) { while (worker->paused) { WAIT(&worker->cond, &worker->lock); UNLOCK(&worker->lock); - (void)process_priority_queue(worker); + (void)process_priority_queue( + worker, &(unsigned int){ UINT_MAX }); LOCK(&worker->lock); } @@ -642,10 +645,11 @@ nm_thread(isc_threadarg_t worker0) { UNLOCK(&worker->lock); /* - * All workers must run the privileged event + * All workers must drain the privileged event * queue before we resume from pause. */ - process_privilege_queue(worker); + (void)process_privilege_queue( + worker, &(unsigned int){ UINT_MAX }); LOCK(&mgr->lock); while (atomic_load(&mgr->paused)) { @@ -660,16 +664,6 @@ nm_thread(isc_threadarg_t worker0) { } INSIST(!worker->finished); - - /* - * We've fully resumed from pause. Drain the normal - * asynchronous event queues before resuming the uv_run() - * loop. (This is not strictly necessary, it just ensures - * that all pending events are processed before another - * pause can slip in.) - */ - process_tasks_queue(worker); - process_normal_queue(worker); } /* @@ -677,8 +671,8 @@ nm_thread(isc_threadarg_t worker0) { * (they may include shutdown events) but do not process * the netmgr event queue. */ - process_privilege_queue(worker); - process_tasks_queue(worker); + (void)process_privilege_queue(worker, &(unsigned int){ UINT_MAX }); + (void)process_task_queue(worker, &(unsigned int){ UINT_MAX }); LOCK(&mgr->lock); mgr->workers_running--; @@ -688,6 +682,23 @@ nm_thread(isc_threadarg_t worker0) { return ((isc_threadresult_t)0); } +static bool +process_all_queues(isc__networker_t *worker, unsigned int quantum) { + /* + * The queue processing functions will return false when the + * system is pausing or stopping, or if we have completed + * 'quantum' events. + * + * We don't want to proceed to a new queue until the previous one + * has been fully drained, so whenever one queue is interrupted, + * we skip all the later ones. + */ + return (process_priority_queue(worker, &quantum) && + process_privilege_queue(worker, &quantum) && + process_task_queue(worker, &quantum) && + process_normal_queue(worker, &quantum)); +} + /* * async_cb() is a universal callback for 'async' events sent to event loop. * It's the only way to safely pass data to the libuv event loop. We use a @@ -697,18 +708,14 @@ nm_thread(isc_threadarg_t worker0) { static void async_cb(uv_async_t *handle) { isc__networker_t *worker = (isc__networker_t *)handle->loop->data; + unsigned int quantum = worker->quantum; - /* - * process_priority_queue() returns false when pausing or stopping, - * so we don't want to process the other queues in that case. - */ - if (!process_priority_queue(worker)) { - return; + if (!process_all_queues(worker, quantum)) { + /* If we didn't process all the events, we need to enqueue + * async_cb to be run in the next iteration of the uv_loop + */ + uv_async_send(handle); } - - process_privilege_queue(worker); - process_tasks_queue(worker); - process_normal_queue(worker); } static void @@ -775,8 +782,7 @@ isc__nm_async_task(isc__networker_t *worker, isc__netievent_t *ev0) { switch (result) { case ISC_R_QUOTA: - isc_nm_task_enqueue(worker->mgr, (isc_task_t *)ievent->task, - isc_nm_tid()); + isc_task_ready(ievent->task); return; case ISC_R_SUCCESS: return; @@ -787,23 +793,23 @@ isc__nm_async_task(isc__networker_t *worker, isc__netievent_t *ev0) { } static bool -process_priority_queue(isc__networker_t *worker) { - return (process_queue(worker, worker->ievents_prio)); +process_priority_queue(isc__networker_t *worker, unsigned int *quantump) { + return (process_queue(worker, worker->ievents_prio, quantump)); } -static void -process_privilege_queue(isc__networker_t *worker) { - (void)process_queue(worker, worker->ievents_priv); +static bool +process_privilege_queue(isc__networker_t *worker, unsigned int *quantump) { + return (process_queue(worker, worker->ievents_priv, quantump)); } -static void -process_tasks_queue(isc__networker_t *worker) { - (void)process_queue(worker, worker->ievents_task); +static bool +process_task_queue(isc__networker_t *worker, unsigned int *quantump) { + return (process_queue(worker, worker->ievents_task, quantump)); } -static void -process_normal_queue(isc__networker_t *worker) { - (void)process_queue(worker, worker->ievents); +static bool +process_normal_queue(isc__networker_t *worker, unsigned int *quantump) { + return (process_queue(worker, worker->ievents, quantump)); } /* @@ -905,16 +911,27 @@ process_netievent(isc__networker_t *worker, isc__netievent_t *ievent) { } static bool -process_queue(isc__networker_t *worker, isc_queue_t *queue) { - isc__netievent_t *ievent = NULL; +process_queue(isc__networker_t *worker, isc_queue_t *queue, + unsigned int *quantump) { + while (*quantump > 0) { + isc__netievent_t *ievent = + (isc__netievent_t *)isc_queue_dequeue(queue); + + (*quantump)--; + + if (ievent == NULL) { + /* We fully drained this queue */ + return (true); + } - while ((ievent = (isc__netievent_t *)isc_queue_dequeue(queue)) != NULL) - { if (!process_netievent(worker, ievent)) { + /* Netievent told us to stop */ return (false); } } - return (true); + + /* No more quantum */ + return (false); } void * diff --git a/lib/isc/task.c b/lib/isc/task.c index 49112f22e7..27073afdb1 100644 --- a/lib/isc/task.c +++ b/lib/isc/task.c @@ -17,6 +17,7 @@ */ #include +#include #include #include @@ -102,6 +103,7 @@ struct isc_task { task_state_t state; int pause_cnt; isc_refcount_t references; + isc_refcount_t running; isc_eventlist_t events; isc_eventlist_t on_shutdown; unsigned int nevents; @@ -112,21 +114,14 @@ struct isc_task { void *tag; bool bound; /* Protected by atomics */ - atomic_uint_fast32_t flags; + atomic_bool shuttingdown; + atomic_bool privileged; /* Locked by task manager lock. */ LINK(isc_task_t) link; }; -#define TASK_F_SHUTTINGDOWN 0x01 -#define TASK_F_PRIVILEGED 0x02 - -#define TASK_SHUTTINGDOWN(t) \ - ((atomic_load_acquire(&(t)->flags) & TASK_F_SHUTTINGDOWN) != 0) -#define TASK_PRIVILEGED(t) \ - ((atomic_load_acquire(&(t)->flags) & TASK_F_PRIVILEGED) != 0) - -#define TASK_FLAG_SET(t, f) atomic_fetch_or_release(&(t)->flags, (f)) -#define TASK_FLAG_CLR(t, f) atomic_fetch_and_release(&(t)->flags, ~(f)) +#define TASK_SHUTTINGDOWN(t) (atomic_load_acquire(&(t)->shuttingdown)) +#define TASK_PRIVILEGED(t) (atomic_load_acquire(&(t)->privileged)) #define TASK_MANAGER_MAGIC ISC_MAGIC('T', 'S', 'K', 'M') #define VALID_MANAGER(m) ISC_MAGIC_VALID(m, TASK_MANAGER_MAGIC) @@ -137,10 +132,8 @@ struct isc_taskmgr { isc_refcount_t references; isc_mem_t *mctx; isc_mutex_t lock; - atomic_uint_fast32_t tasks_running; - atomic_uint_fast32_t tasks_ready; atomic_uint_fast32_t tasks_count; - isc_nm_t *nm; + isc_nm_t *netmgr; /* Locked by task manager lock. */ unsigned int default_quantum; @@ -148,9 +141,6 @@ struct isc_taskmgr { atomic_bool exclusive_req; atomic_bool exiting; - /* Locked by halt_lock */ - unsigned int halted; - /* * Multiple threads can read/write 'excl' at the same time, so we need * to protect the access. We can't use 'lock' since isc_task_detach() @@ -161,9 +151,6 @@ struct isc_taskmgr { }; #define DEFAULT_DEFAULT_QUANTUM 25 -#define FINISHED(m) \ - (atomic_load_relaxed(&((m)->exiting)) && \ - atomic_load(&(m)->tasks_count) == 0) /*% * The following are intended for internal use (indicated by "isc__" @@ -193,6 +180,7 @@ task_finished(isc_task_t *task) { XTRACE("task_finished"); + isc_refcount_destroy(&task->running); isc_refcount_destroy(&task->references); LOCK(&manager->lock); @@ -251,11 +239,13 @@ isc_task_create_bound(isc_taskmgr_t *manager, unsigned int quantum, task->pause_cnt = 0; isc_refcount_init(&task->references, 1); + isc_refcount_init(&task->running, 0); INIT_LIST(task->events); INIT_LIST(task->on_shutdown); task->nevents = 0; task->quantum = (quantum > 0) ? quantum : manager->default_quantum; - atomic_init(&task->flags, 0); + atomic_init(&task->shuttingdown, false); + atomic_init(&task->privileged, false); task->now = 0; isc_time_settoepoch(&task->tnow); memset(task->name, 0, sizeof(task->name)); @@ -311,9 +301,9 @@ task_shutdown(isc_task_t *task) { XTRACE("task_shutdown"); - if (!TASK_SHUTTINGDOWN(task)) { + if (atomic_compare_exchange_strong(&task->shuttingdown, + &(bool){ false }, true)) { XTRACE("shutting down"); - TASK_FLAG_SET(task, TASK_F_SHUTTINGDOWN); if (task->state == task_state_idle) { INSIST(EMPTY(task->events)); task->state = task_state_ready; @@ -350,7 +340,14 @@ task_ready(isc_task_t *task) { REQUIRE(VALID_MANAGER(manager)); XTRACE("task_ready"); - isc_nm_task_enqueue(manager->nm, task, task->threadid); + + isc_refcount_increment0(&task->running); + isc_nm_task_enqueue(manager->netmgr, task, task->threadid); +} + +void +isc_task_ready(isc_task_t *task) { + task_ready(task); } static inline bool @@ -820,8 +817,7 @@ task_run(isc_task_t *task) { * and task lock to avoid deadlocks, just bail then. */ if (task->state != task_state_ready) { - UNLOCK(&task->lock); - return (ISC_R_SUCCESS); + goto done; } INSIST(task->state == task_state_ready); @@ -886,7 +882,6 @@ task_run(isc_task_t *task) { * The task is done. */ XTRACE("done"); - finished = true; task->state = task_state_done; } else { if (task->state == task_state_running) { @@ -920,6 +915,13 @@ task_run(isc_task_t *task) { break; } } + +done: + if (isc_refcount_decrement(&task->running) == 1 && + task->state == task_state_done) + { + finished = true; + } UNLOCK(&task->lock); if (finished) { @@ -937,7 +939,7 @@ isc_task_run(isc_task_t *task) { static void manager_free(isc_taskmgr_t *manager) { isc_refcount_destroy(&manager->references); - isc_nm_detach(&manager->nm); + isc_nm_detach(&manager->netmgr); isc_mutex_destroy(&manager->lock); isc_mutex_destroy(&manager->excl_lock); @@ -988,13 +990,10 @@ isc__taskmgr_create(isc_mem_t *mctx, unsigned int default_quantum, isc_nm_t *nm, manager->default_quantum = default_quantum; if (nm != NULL) { - isc_nm_attach(nm, &manager->nm); + isc_nm_attach(nm, &manager->netmgr); } INIT_LIST(manager->tasks); - atomic_init(&manager->tasks_count, 0); - atomic_init(&manager->tasks_running, 0); - atomic_init(&manager->tasks_ready, 0); atomic_init(&manager->exiting, false); atomic_store_relaxed(&manager->exclusive_req, false); @@ -1074,6 +1073,18 @@ isc__taskmgr_destroy(isc_taskmgr_t **managerp) { XTHREADTRACE("isc_taskmgr_destroy"); +#ifdef ISC_TASK_TRACE + int counter = 0; + while (isc_refcount_current(&manager->references) > 1 && + counter++ < 1000) { + usleep(10 * 1000); + } +#else + while (isc_refcount_current(&manager->references) > 1) { + usleep(10 * 1000); + } +#endif + REQUIRE(isc_refcount_decrement(&manager->references) == 1); manager_free(manager); } @@ -1130,7 +1141,7 @@ isc_task_beginexclusive(isc_task_t *task) { return (ISC_R_LOCKBUSY); } - isc_nm_pause(manager->nm); + isc_nm_pause(manager->netmgr); return (ISC_R_SUCCESS); } @@ -1143,7 +1154,7 @@ isc_task_endexclusive(isc_task_t *task) { REQUIRE(task->state == task_state_running); manager = task->manager; - isc_nm_resume(manager->nm); + isc_nm_resume(manager->netmgr); REQUIRE(atomic_compare_exchange_strong(&manager->exclusive_req, &(bool){ true }, false)); } @@ -1210,20 +1221,8 @@ isc_task_unpause(isc_task_t *task) { void isc_task_setprivilege(isc_task_t *task, bool priv) { REQUIRE(VALID_TASK(task)); - uint_fast32_t oldflags, newflags; - oldflags = atomic_load_acquire(&task->flags); - do { - if (priv) { - newflags = oldflags | TASK_F_PRIVILEGED; - } else { - newflags = oldflags & ~TASK_F_PRIVILEGED; - } - if (newflags == oldflags) { - return; - } - } while (!atomic_compare_exchange_weak_acq_rel(&task->flags, &oldflags, - newflags)); + atomic_store_release(&task->privileged, priv); } bool @@ -1269,21 +1268,6 @@ isc_taskmgr_renderxml(isc_taskmgr_t *mgr, void *writer0) { mgr->default_quantum)); TRY0(xmlTextWriterEndElement(writer)); /* default-quantum */ - TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "tasks-count")); - TRY0(xmlTextWriterWriteFormatString( - writer, "%d", (int)atomic_load_relaxed(&mgr->tasks_count))); - TRY0(xmlTextWriterEndElement(writer)); /* tasks-count */ - - TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "tasks-running")); - TRY0(xmlTextWriterWriteFormatString( - writer, "%d", (int)atomic_load_relaxed(&mgr->tasks_running))); - TRY0(xmlTextWriterEndElement(writer)); /* tasks-running */ - - TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "tasks-ready")); - TRY0(xmlTextWriterWriteFormatString( - writer, "%d", (int)atomic_load_relaxed(&mgr->tasks_ready))); - TRY0(xmlTextWriterEndElement(writer)); /* tasks-ready */ - TRY0(xmlTextWriterEndElement(writer)); /* thread-model */ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "tasks")); @@ -1373,18 +1357,6 @@ isc_taskmgr_renderjson(isc_taskmgr_t *mgr, void *tasks0) { CHECKMEM(obj); json_object_object_add(tasks, "default-quantum", obj); - obj = json_object_new_int(atomic_load_relaxed(&mgr->tasks_count)); - CHECKMEM(obj); - json_object_object_add(tasks, "tasks-count", obj); - - obj = json_object_new_int(atomic_load_relaxed(&mgr->tasks_running)); - CHECKMEM(obj); - json_object_object_add(tasks, "tasks-running", obj); - - obj = json_object_new_int(atomic_load_relaxed(&mgr->tasks_ready)); - CHECKMEM(obj); - json_object_object_add(tasks, "tasks-ready", obj); - array = json_object_new_array(); CHECKMEM(array); diff --git a/lib/isc/win32/libisc.def.in b/lib/isc/win32/libisc.def.in index 3c4dd02f71..e6f7787291 100644 --- a/lib/isc/win32/libisc.def.in +++ b/lib/isc/win32/libisc.def.in @@ -101,6 +101,7 @@ isc_socketmgr_setreserved isc_socketmgr_setstats isc_task_getname isc_task_gettag +isc_task_ready isc_task_run isc_task_unsendrange isc_taskmgr_attach From 2836bc185409742651290ebe34e27e8cda3404ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Sur=C3=BD?= Date: Tue, 27 Apr 2021 12:03:20 +0200 Subject: [PATCH 04/13] Fix wrong query accounting in the connect function in dighost.c The start_udp() function didn't properly attach to the query and thus a callback with ISC_R_CANCELED would end with wrong accounting on the query object. Usually, this doesn't happen because underlying libuv API uv_udp_connect() is synchronous, but isc_nm_udpconnect() could return ISC_R_CANCELED in case it's called while the netmgr is shutting down. --- bin/dig/dighost.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/bin/dig/dighost.c b/bin/dig/dighost.c index 09926547b5..a941bcf87d 100644 --- a/bin/dig/dighost.c +++ b/bin/dig/dighost.c @@ -227,8 +227,9 @@ void (*dighost_shutdown)(void); /* forward declarations */ +#define cancel_lookup(l) _cancel_lookup(l, __FILE__, __LINE__) static void -cancel_lookup(dig_lookup_t *lookup); +_cancel_lookup(dig_lookup_t *lookup, const char *file, unsigned int line); static void recv_done(isc_nmhandle_t *handle, isc_result_t eresult, isc_region_t *region, @@ -1694,6 +1695,9 @@ _query_detach(dig_query_t **queryp, const char *file, unsigned int line) { isc_refcount_current(&query->references) - 1); if (isc_refcount_decrement(&query->references) == 1) { + INSIST(query->readhandle == NULL); + INSIST(query->sendhandle == NULL); + if (ISC_LINK_LINKED(query, link)) { ISC_LIST_UNLINK(lookup->q, query, link); } @@ -2669,11 +2673,12 @@ send_done(isc_nmhandle_t *handle, isc_result_t eresult, void *arg) { /*% * Cancel a lookup, sending canceling reads on all existing sockets. */ + static void -cancel_lookup(dig_lookup_t *lookup) { +_cancel_lookup(dig_lookup_t *lookup, const char *file, unsigned int line) { dig_query_t *query, *next; - debug("cancel_lookup()"); + debug("%s:%u:%s()", file, line, __func__); query = ISC_LIST_HEAD(lookup->q); while (query != NULL) { REQUIRE(DIG_VALID_QUERY(query)); @@ -2939,6 +2944,7 @@ static void start_udp(dig_query_t *query) { isc_result_t result; dig_query_t *next = NULL; + dig_query_t *connectquery = NULL; REQUIRE(DIG_VALID_QUERY(query)); @@ -2990,8 +2996,10 @@ start_udp(dig_query_t *query) { } } + query_attach(query, &connectquery); isc_nm_udpconnect(netmgr, (isc_nmiface_t *)&localaddr, - (isc_nmiface_t *)&query->sockaddr, udp_ready, query, + (isc_nmiface_t *)&query->sockaddr, udp_ready, + connectquery, (timeout ? timeout : UDP_TIMEOUT) * 1000, 0); } @@ -4186,15 +4194,13 @@ cancel_all(void) { return; } atomic_store(&cancel_now, true); - if (current_lookup != NULL) { + while (current_lookup != NULL) { for (q = ISC_LIST_HEAD(current_lookup->q); q != NULL; q = nq) { nq = ISC_LIST_NEXT(q, link); debug("canceling pending query %p, belonging to %p", q, current_lookup); if (q->readhandle != NULL) { - isc_refcount_decrement0(&recvcount); - debug("recvcount=%" PRIuFAST32, - isc_refcount_current(&recvcount)); + isc_nm_cancelread(q->readhandle); } query_detach(&q); } From 0b491913df7512adc604fe5c620558f6775a6215 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Sur=C3=BD?= Date: Tue, 4 May 2021 14:25:55 +0200 Subject: [PATCH 05/13] Don't clear dig lookup if it was already cleared This workarounds couple of races where the current_lookup would be already detached during shutting down the dig, but still processing the pending reads. --- bin/dig/dighost.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/bin/dig/dighost.c b/bin/dig/dighost.c index a941bcf87d..7ae651e517 100644 --- a/bin/dig/dighost.c +++ b/bin/dig/dighost.c @@ -1754,13 +1754,18 @@ start_lookup(void) { * decremented, current_lookup will not be set to NULL.) */ static void -clear_current_lookup() { +clear_current_lookup(void) { dig_lookup_t *lookup = current_lookup; INSIST(!free_now); debug("clear_current_lookup()"); + if (lookup == NULL) { + debug("current_lookup is already detached"); + return; + } + if (ISC_LIST_HEAD(lookup->q) != NULL) { debug("still have a worker"); return; @@ -2907,6 +2912,7 @@ udp_ready(isc_nmhandle_t *handle, isc_result_t eresult, void *arg) { isc_result_totext(eresult)); } + cancel_lookup(l); lookup_detach(&l); query_detach(&query); return; @@ -3574,16 +3580,19 @@ recv_done(isc_nmhandle_t *handle, isc_result_t eresult, isc_region_t *region, region, arg); LOCK_LOOKUP; - lookup_attach(query->lookup, &l); isc_refcount_decrement0(&recvcount); debug("recvcount=%" PRIuFAST32, isc_refcount_current(&recvcount)); if (eresult == ISC_R_CANCELED) { debug("recv_done: cancel"); - goto detach_query; + isc_nmhandle_detach(&query->readhandle); + query_detach(&query); + return; } + lookup_attach(query->lookup, &l); + if (query->lookup->use_usec) { TIME_NOW_HIRES(&query->time_recv); } else { @@ -4204,7 +4213,13 @@ cancel_all(void) { } query_detach(&q); } - lookup_detach(¤t_lookup); + + /* + * current_lookup could have been detached via query_detach(). + */ + if (current_lookup != NULL) { + lookup_detach(¤t_lookup); + } } l = ISC_LIST_HEAD(lookup_list); while (l != NULL) { From 29a208aaf7998ea337ce5586c516bf9d0735d2b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Sur=C3=BD?= Date: Wed, 5 May 2021 21:21:02 +0200 Subject: [PATCH 06/13] Fix crash when allocating UDP socket fails on OpenBSD When socket() call fails, the UDP connect code would call the connectcb with empty req->handle. This has been fixed. --- lib/isc/netmgr/udp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/isc/netmgr/udp.c b/lib/isc/netmgr/udp.c index ef404d93da..221da22663 100644 --- a/lib/isc/netmgr/udp.c +++ b/lib/isc/netmgr/udp.c @@ -690,7 +690,6 @@ isc__nm_async_udpconnect(isc__networker_t *worker, isc__netievent_t *ev0) { REQUIRE(sock->parent == NULL); REQUIRE(sock->tid == isc_nm_tid()); - req->handle = isc__nmhandle_get(sock, &req->peer, &sock->iface->addr); result = udp_connect_direct(sock, req); if (result != ISC_R_SUCCESS) { atomic_store(&sock->active, false); @@ -742,6 +741,7 @@ isc_nm_udpconnect(isc_nm_t *mgr, isc_nmiface_t *local, isc_nmiface_t *peer, req->cbarg = cbarg; req->peer = peer->addr; req->local = local->addr; + req->handle = isc__nmhandle_get(sock, &req->peer, &sock->iface->addr); result = isc__nm_socket(sa_family, SOCK_DGRAM, 0, &sock->fd); if (result != ISC_R_SUCCESS) { From 5c08f977913cc41bface5c52808c96ee142e6953 Mon Sep 17 00:00:00 2001 From: Evan Hunt Date: Thu, 6 May 2021 19:41:49 -0700 Subject: [PATCH 07/13] only run tasks as privileged if taskmgr is in privileged mode all zone loading tasks have the privileged flag, but we only want them to run as privileged tasks when the server is being initialized; if we privilege them the rest of the time, the server may hang for a long time after a reload/reconfig. so now we call isc_taskmgr_setmode() to turn privileged execution mode on or off in the task manager. isc_task_privileged() returns true if the task's privilege flag is set *and* the taskmgr is in privileged execution mode. this is used to determine in which netmgr event queue the task should be run. --- bin/named/server.c | 27 +++++++++++++++--- lib/dns/include/dns/zone.h | 10 +++++-- lib/dns/win32/libdns.def.in | 5 ++-- lib/dns/zone.c | 11 ++++++-- lib/isc/include/isc/task.h | 52 +++++++++++++++++++++++++++++------ lib/isc/netmgr/netmgr.c | 2 +- lib/isc/task.c | 21 +++++++++++++- lib/isc/tests/task_test.c | 16 ++++++----- lib/isc/tests/taskpool_test.c | 6 ++-- lib/isc/win32/libisc.def.in | 5 +++- 10 files changed, 123 insertions(+), 32 deletions(-) diff --git a/bin/named/server.c b/bin/named/server.c index 3edad71616..ea343340ad 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -9845,6 +9845,12 @@ view_loaded(void *arg) { "all zones loaded"); } + /* + * Clear taskmgr privileged mode now that zones are loaded. + */ + isc_taskmgr_setmode(dns_zonemgr_gettaskmgr(server->zonemgr), + isc_taskmgrmode_normal); + CHECKFATAL(dns_zonemgr_forcemaint(server->zonemgr), "forcing zone maintenance"); @@ -9867,7 +9873,7 @@ view_loaded(void *arg) { } static isc_result_t -load_zones(named_server_t *server, bool reconfig) { +load_zones(named_server_t *server, bool init, bool reconfig) { isc_result_t result; dns_view_t *view; ns_zoneload_t *zl; @@ -9924,6 +9930,19 @@ cleanup: isc_mem_put(server->mctx, zl, sizeof(*zl)); } + /* + * If we're setting up the server for the first time, + * set the task manager into privileged mode; this ensures + * that no other tasks will begin to run until after + * zone loading is complete. + * + * We do *not* want to do this in the case of reload or + * reconfig, as loading a large zone could cause the server + * to be inactive for too long a time. + */ + isc_taskmgr_setmode(named_g_taskmgr, init ? isc_taskmgrmode_privileged + : isc_taskmgrmode_normal); + isc_task_endexclusive(server->task); return (result); } @@ -9989,7 +10008,7 @@ run_server(isc_task_t *task, isc_event_t *event) { CHECKFATAL(load_configuration(named_g_conffile, server, true), "loading configuration"); - CHECKFATAL(load_zones(server, false), "loading zones"); + CHECKFATAL(load_zones(server, true, false), "loading zones"); #ifdef ENABLE_AFL named_g_run_done = true; #endif /* ifdef ENABLE_AFL */ @@ -10502,7 +10521,7 @@ reload(named_server_t *server) { CHECK(loadconfig(server)); - result = load_zones(server, false); + result = load_zones(server, false, false); if (result == ISC_R_SUCCESS) { isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, NAMED_LOGMODULE_SERVER, ISC_LOG_INFO, @@ -10871,7 +10890,7 @@ named_server_reconfigcommand(named_server_t *server) { CHECK(loadconfig(server)); - result = load_zones(server, true); + result = load_zones(server, false, true); if (result == ISC_R_SUCCESS) { isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, NAMED_LOGMODULE_SERVER, ISC_LOG_INFO, diff --git a/lib/dns/include/dns/zone.h b/lib/dns/include/dns/zone.h index 48e3ae34b5..3acb5a1983 100644 --- a/lib/dns/include/dns/zone.h +++ b/lib/dns/include/dns/zone.h @@ -1735,6 +1735,12 @@ dns_zonemgr_releasezone(dns_zonemgr_t *zmgr, dns_zone_t *zone); *\li 'zone->zmgr' == NULL; */ +isc_taskmgr_t * +dns_zonemgr_gettaskmgr(dns_zonemgr_t *zmgr); +/*% + * Get the tasmkgr object attached to 'zmgr'. + */ + void dns_zonemgr_settransfersin(dns_zonemgr_t *zmgr, uint32_t value); /*%< @@ -1746,7 +1752,7 @@ dns_zonemgr_settransfersin(dns_zonemgr_t *zmgr, uint32_t value); */ uint32_t -dns_zonemgr_getttransfersin(dns_zonemgr_t *zmgr); +dns_zonemgr_gettransfersin(dns_zonemgr_t *zmgr); /*%< * Return the maximum number of simultaneous transfers in allowed. * @@ -1764,7 +1770,7 @@ dns_zonemgr_settransfersperns(dns_zonemgr_t *zmgr, uint32_t value); */ uint32_t -dns_zonemgr_getttransfersperns(dns_zonemgr_t *zmgr); +dns_zonemgr_gettransfersperns(dns_zonemgr_t *zmgr); /*%< * Return the number of transfers allowed per nameserver. * diff --git a/lib/dns/win32/libdns.def.in b/lib/dns/win32/libdns.def.in index bcf586d02b..145d47883e 100644 --- a/lib/dns/win32/libdns.def.in +++ b/lib/dns/win32/libdns.def.in @@ -1389,8 +1389,9 @@ dns_zonemgr_getiolimit dns_zonemgr_getnotifyrate dns_zonemgr_getserialqueryrate dns_zonemgr_getstartupnotifyrate -dns_zonemgr_getttransfersin -dns_zonemgr_getttransfersperns +dns_zonemgr_gettaskmgr +dns_zonemgr_gettransfersin +dns_zonemgr_gettransfersperns dns_zonemgr_managezone dns_zonemgr_releasezone dns_zonemgr_resumexfrs diff --git a/lib/dns/zone.c b/lib/dns/zone.c index 9553ade3e8..c0d69c76aa 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -18478,7 +18478,7 @@ dns_zonemgr_settransfersin(dns_zonemgr_t *zmgr, uint32_t value) { } uint32_t -dns_zonemgr_getttransfersin(dns_zonemgr_t *zmgr) { +dns_zonemgr_gettransfersin(dns_zonemgr_t *zmgr) { REQUIRE(DNS_ZONEMGR_VALID(zmgr)); return (zmgr->transfersin); @@ -18492,12 +18492,19 @@ dns_zonemgr_settransfersperns(dns_zonemgr_t *zmgr, uint32_t value) { } uint32_t -dns_zonemgr_getttransfersperns(dns_zonemgr_t *zmgr) { +dns_zonemgr_gettransfersperns(dns_zonemgr_t *zmgr) { REQUIRE(DNS_ZONEMGR_VALID(zmgr)); return (zmgr->transfersperns); } +isc_taskmgr_t * +dns_zonemgr_gettaskmgr(dns_zonemgr_t *zmgr) { + REQUIRE(DNS_ZONEMGR_VALID(zmgr)); + + return (zmgr->taskmgr); +} + /* * Try to start a new incoming zone transfer to fill a quota * slot that was just vacated. diff --git a/lib/isc/include/isc/task.h b/lib/isc/include/isc/task.h index 45d062ee32..4883785a22 100644 --- a/lib/isc/include/isc/task.h +++ b/lib/isc/include/isc/task.h @@ -12,8 +12,8 @@ #pragma once /***** -***** Module Info -*****/ + ***** Module Info + *****/ /*! \file isc/task.h * \brief The task system provides a lightweight execution context, which is @@ -84,8 +84,8 @@ #define ISC_TASKEVENT_LASTEVENT (ISC_EVENTCLASS_TASK + 65535) /***** -***** Tasks. -*****/ + ***** Tasks. + *****/ ISC_LANG_BEGINDECLS @@ -611,15 +611,15 @@ isc_task_setprivilege(isc_task_t *task, bool priv); * but when the task manager has been set to privileged execution mode via * isc_taskmgr_setmode(), only tasks with the flag set will be executed, * and all other tasks will wait until they're done. Once all privileged - * tasks have finished executing, the task manager will automatically - * return to normal execution mode and nonprivileged task can resume. + * tasks have finished executing, the task manager resumes running + * non-privileged tasks. * * Requires: *\li 'task' is a valid task. */ bool -isc_task_privilege(isc_task_t *task); +isc_task_getprivilege(isc_task_t *task); /*%< * Returns the current value of the task's privilege flag. * @@ -627,14 +627,48 @@ isc_task_privilege(isc_task_t *task); *\li 'task' is a valid task. */ +bool +isc_task_privileged(isc_task_t *task); +/*%< + * Returns true if the task's privilege flag is set *and* the task + * manager is currently in privileged mode. + * + * Requires: + *\li 'task' is a valid task. + */ + /***** -***** Task Manager. -*****/ + ***** Task Manager. + *****/ void isc_taskmgr_attach(isc_taskmgr_t *, isc_taskmgr_t **); void isc_taskmgr_detach(isc_taskmgr_t *); +/*%< + * Attach/detach the task manager. + */ + +void +isc_taskmgr_setmode(isc_taskmgr_t *manager, isc_taskmgrmode_t mode); +isc_taskmgrmode_t +isc_taskmgr_mode(isc_taskmgr_t *manager); +/*%< + * Set/get the operating mode of the task manager. Valid modes are: + * + *\li isc_taskmgrmode_normal + *\li isc_taskmgrmode_privileged + * + * In privileged execution mode, only tasks that have had the "privilege" + * flag set via isc_task_setprivilege() can be executed. When all such + * tasks are complete, non-privileged tasks resume running. The task calling + * this function should be in task-exclusive mode; the privileged tasks + * will be run after isc_task_endexclusive() is called. + * + * Requires: + * + *\li 'manager' is a valid task manager. + */ void isc_taskmgr_setexcltask(isc_taskmgr_t *mgr, isc_task_t *task); diff --git a/lib/isc/netmgr/netmgr.c b/lib/isc/netmgr/netmgr.c index f080e8f52c..56d4630266 100644 --- a/lib/isc/netmgr/netmgr.c +++ b/lib/isc/netmgr/netmgr.c @@ -757,7 +757,7 @@ isc_nm_task_enqueue(isc_nm_t *nm, isc_task_t *task, int threadid) { worker = &nm->workers[tid]; - if (isc_task_privilege(task)) { + if (isc_task_privileged(task)) { event = (isc__netievent_t *) isc__nm_get_netievent_privilegedtask(nm, task); } else { diff --git a/lib/isc/task.c b/lib/isc/task.c index 27073afdb1..5c217ad5b0 100644 --- a/lib/isc/task.c +++ b/lib/isc/task.c @@ -138,6 +138,7 @@ struct isc_taskmgr { /* Locked by task manager lock. */ unsigned int default_quantum; LIST(isc_task_t) tasks; + atomic_uint_fast32_t mode; atomic_bool exclusive_req; atomic_bool exiting; @@ -995,6 +996,7 @@ isc__taskmgr_create(isc_mem_t *mctx, unsigned int default_quantum, isc_nm_t *nm, INIT_LIST(manager->tasks); atomic_init(&manager->exiting, false); + atomic_init(&manager->mode, isc_taskmgrmode_normal); atomic_store_relaxed(&manager->exclusive_req, false); isc_mem_attach(mctx, &manager->mctx); @@ -1218,6 +1220,16 @@ isc_task_unpause(isc_task_t *task) { } } +void +isc_taskmgr_setmode(isc_taskmgr_t *manager, isc_taskmgrmode_t mode) { + atomic_store(&manager->mode, mode); +} + +isc_taskmgrmode_t +isc_taskmgr_mode(isc_taskmgr_t *manager) { + return (atomic_load(&manager->mode)); +} + void isc_task_setprivilege(isc_task_t *task, bool priv) { REQUIRE(VALID_TASK(task)); @@ -1226,12 +1238,19 @@ isc_task_setprivilege(isc_task_t *task, bool priv) { } bool -isc_task_privilege(isc_task_t *task) { +isc_task_getprivilege(isc_task_t *task) { REQUIRE(VALID_TASK(task)); return (TASK_PRIVILEGED(task)); } +bool +isc_task_privileged(isc_task_t *task) { + REQUIRE(VALID_TASK(task)); + + return (isc_taskmgr_mode(task->manager) && TASK_PRIVILEGED(task)); +} + bool isc_task_exiting(isc_task_t *task) { REQUIRE(VALID_TASK(task)); diff --git a/lib/isc/tests/task_test.c b/lib/isc/tests/task_test.c index be649393ec..4edcfc902f 100644 --- a/lib/isc/tests/task_test.c +++ b/lib/isc/tests/task_test.c @@ -216,18 +216,19 @@ privileged_events(void **state) { * queue without things happening while we do it. */ isc_nm_pause(netmgr); + isc_taskmgr_setmode(taskmgr, isc_taskmgrmode_privileged); result = isc_task_create(taskmgr, 0, &task1); assert_int_equal(result, ISC_R_SUCCESS); isc_task_setname(task1, "privileged", NULL); - assert_false(isc_task_privilege(task1)); + assert_false(isc_task_getprivilege(task1)); isc_task_setprivilege(task1, true); - assert_true(isc_task_privilege(task1)); + assert_true(isc_task_getprivilege(task1)); result = isc_task_create(taskmgr, 0, &task2); assert_int_equal(result, ISC_R_SUCCESS); isc_task_setname(task2, "normal", NULL); - assert_false(isc_task_privilege(task2)); + assert_false(isc_task_getprivilege(task2)); /* First event: privileged */ event = isc_event_allocate(test_mctx, task1, ISC_TASKEVENT_TEST, set, @@ -296,7 +297,7 @@ privileged_events(void **state) { assert_int_equal(atomic_load(&counter), 6); isc_task_setprivilege(task1, false); - assert_false(isc_task_privilege(task1)); + assert_false(isc_task_getprivilege(task1)); isc_task_destroy(&task1); assert_null(task1); @@ -330,18 +331,19 @@ privilege_drop(void **state) { * without things happening while we do it. */ isc_nm_pause(netmgr); + isc_taskmgr_setmode(taskmgr, isc_taskmgrmode_privileged); result = isc_task_create(taskmgr, 0, &task1); assert_int_equal(result, ISC_R_SUCCESS); isc_task_setname(task1, "privileged", NULL); - assert_false(isc_task_privilege(task1)); + assert_false(isc_task_getprivilege(task1)); isc_task_setprivilege(task1, true); - assert_true(isc_task_privilege(task1)); + assert_true(isc_task_getprivilege(task1)); result = isc_task_create(taskmgr, 0, &task2); assert_int_equal(result, ISC_R_SUCCESS); isc_task_setname(task2, "normal", NULL); - assert_false(isc_task_privilege(task2)); + assert_false(isc_task_getprivilege(task2)); /* First event: privileged */ event = isc_event_allocate(test_mctx, task1, ISC_TASKEVENT_TEST, diff --git a/lib/isc/tests/taskpool_test.c b/lib/isc/tests/taskpool_test.c index 9878599721..58e0094f72 100644 --- a/lib/isc/tests/taskpool_test.c +++ b/lib/isc/tests/taskpool_test.c @@ -164,9 +164,9 @@ set_privilege(void **state) { assert_true(VALID_TASK(task2)); assert_true(VALID_TASK(task3)); - assert_true(isc_task_privilege(task1)); - assert_true(isc_task_privilege(task2)); - assert_true(isc_task_privilege(task3)); + assert_true(isc_task_getprivilege(task1)); + assert_true(isc_task_getprivilege(task2)); + assert_true(isc_task_getprivilege(task3)); isc_task_destroy(&task1); isc_task_destroy(&task2); diff --git a/lib/isc/win32/libisc.def.in b/lib/isc/win32/libisc.def.in index e6f7787291..db582009ad 100644 --- a/lib/isc/win32/libisc.def.in +++ b/lib/isc/win32/libisc.def.in @@ -635,9 +635,10 @@ isc_task_endexclusive isc_task_exiting isc_task_getcurrenttime isc_task_getcurrenttimex +isc_task_getprivilege isc_task_onshutdown isc_task_pause -isc_task_privilege +isc_task_privileged isc_task_purge isc_task_purgeevent isc_task_purgerange @@ -651,6 +652,7 @@ isc_task_shutdown isc_task_unpause isc_task_unsend isc_taskmgr_excltask +isc_taskmgr_mode @IF NOTYET isc_taskmgr_renderjson @END NOTYET @@ -658,6 +660,7 @@ isc_taskmgr_renderjson isc_taskmgr_renderxml @END LIBXML2 isc_taskmgr_setexcltask +isc_taskmgr_setmode isc_taskpool_create isc_taskpool_destroy isc_taskpool_expand From 2eae7813b608cd174cf3b9976cbd4f651e68a194 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Sur=C3=BD?= Date: Thu, 6 May 2021 09:03:33 +0200 Subject: [PATCH 08/13] Run isc__nm_http_stoplistening() synchronously in netmgr When isc__nm_http_stoplistening() is run from inside the netmgr, we need to make sure it's run synchronously. This commit is just a band-aid though, as the desired behvaior for isc_nm_stoplistening() is not always the same: 1. When run from outside user of the interface, the call must be synchronous, e.g. the calling code expects the call to really stop listening on the interfaces. 2. But if there's a call from listen when listening fails, that needs to be scheduled to run asynchronously, because isc_nm_listen is being run in a paused (interlocked) netmgr thread and we could get stuck. The proper solution would be to make isc_nm_stoplistening() behave like uv_close(), i.e., to have a proper callback. --- lib/isc/netmgr/http.c | 15 +++++++++------ lib/isc/netmgr/tlsstream.c | 6 ++++++ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/lib/isc/netmgr/http.c b/lib/isc/netmgr/http.c index 4482e2023d..021feaa3c3 100644 --- a/lib/isc/netmgr/http.c +++ b/lib/isc/netmgr/http.c @@ -2239,8 +2239,6 @@ isc_nm_http_endpoint(isc_nmsocket_t *sock, const char *uri, isc_nm_recv_cb_t cb, void isc__nm_http_stoplistening(isc_nmsocket_t *sock) { - isc__netievent_httpstop_t *ievent = NULL; - REQUIRE(VALID_NMSOCK(sock)); REQUIRE(sock->type == isc_nm_httplistener); @@ -2250,9 +2248,15 @@ isc__nm_http_stoplistening(isc_nmsocket_t *sock) { ISC_UNREACHABLE(); } - ievent = isc__nm_get_netievent_httpstop(sock->mgr, sock); - isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid], - (isc__netievent_t *)ievent); + if (!isc__nm_in_netthread()) { + isc__netievent_httpstop_t *ievent = + isc__nm_get_netievent_httpstop(sock->mgr, sock); + isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid], + (isc__netievent_t *)ievent); + } else { + isc__netievent_httpstop_t ievent = { .sock = sock }; + isc__nm_async_httpstop(NULL, (isc__netievent_t *)&ievent); + } } static void @@ -2294,7 +2298,6 @@ isc__nm_async_httpstop(isc__networker_t *worker, isc__netievent_t *ev0) { UNUSED(worker); REQUIRE(VALID_NMSOCK(sock)); - REQUIRE(sock->tid == isc_nm_tid()); atomic_store(&sock->listening, false); atomic_store(&sock->closing, false); diff --git a/lib/isc/netmgr/tlsstream.c b/lib/isc/netmgr/tlsstream.c index 264ffcde41..fab2072f22 100644 --- a/lib/isc/netmgr/tlsstream.c +++ b/lib/isc/netmgr/tlsstream.c @@ -849,6 +849,12 @@ isc__nm_tls_stoplistening(isc_nmsocket_t *sock) { REQUIRE(VALID_NMSOCK(sock)); REQUIRE(sock->type == isc_nm_tlslistener); + if (!atomic_compare_exchange_strong(&sock->closing, &(bool){ false }, + true)) { + INSIST(0); + ISC_UNREACHABLE(); + } + atomic_store(&sock->listening, false); atomic_store(&sock->closed, true); sock->recv_cb = NULL; From 4c8f6ebeb15e10ef25954ff3aab9f5b8b8e64ecf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Sur=C3=BD?= Date: Wed, 5 May 2021 11:51:39 +0200 Subject: [PATCH 09/13] Use barriers for netmgr synchronization The netmgr listening, stoplistening, pausing and resuming functions now use barriers for synchronization, which makes the code much simpler. isc/barrier.h defines isc_barrier macros as a front-end for uv_barrier on platforms where that works, and pthread_barrier where it doesn't (including TSAN builds). --- lib/isc/Makefile.am | 1 + lib/isc/include/isc/barrier.h | 37 +++++ lib/isc/netmgr/http.c | 4 +- lib/isc/netmgr/netmgr-int.h | 14 +- lib/isc/netmgr/netmgr.c | 139 +++++++++++------ lib/isc/netmgr/tcp.c | 171 +++++++++++++-------- lib/isc/netmgr/tcpdns.c | 181 ++++++++++++++--------- lib/isc/netmgr/tlsdns.c | 189 +++++++++++++++--------- lib/isc/netmgr/tlsstream.c | 13 +- lib/isc/netmgr/udp.c | 161 ++++++++++++-------- lib/isc/task.c | 2 +- lib/isc/win32/libisc.vcxproj.filters.in | 3 + lib/isc/win32/libisc.vcxproj.in | 1 + util/copyrights | 1 + 14 files changed, 589 insertions(+), 328 deletions(-) create mode 100644 lib/isc/include/isc/barrier.h diff --git a/lib/isc/Makefile.am b/lib/isc/Makefile.am index 96124a2d89..c5368309f4 100644 --- a/lib/isc/Makefile.am +++ b/lib/isc/Makefile.am @@ -11,6 +11,7 @@ libisc_la_HEADERS = \ include/isc/atomic.h \ include/isc/attributes.h \ include/isc/backtrace.h \ + include/isc/barrier.h \ include/isc/base32.h \ include/isc/base64.h \ include/isc/bind9.h \ diff --git a/lib/isc/include/isc/barrier.h b/lib/isc/include/isc/barrier.h new file mode 100644 index 0000000000..48353c69a8 --- /dev/null +++ b/lib/isc/include/isc/barrier.h @@ -0,0 +1,37 @@ +/* + * 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 https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#pragma once + +#include + +#if __SANITIZE_THREAD__ && !defined(WIN32) + +#include + +#define isc_barrier_t pthread_barrier_t + +#define isc_barrier_init(barrier, count) \ + pthread_barrier_init(barrier, NULL, count) +#define isc_barrier_destroy(barrier) pthread_barrier_destroy(barrier) +#define isc_barrier_wait(barrier) pthread_barrier_wait(barrier) + +#else + +#include + +#define isc_barrier_t uv_barrier_t + +#define isc_barrier_init(barrier, count) uv_barrier_init(barrier, count) +#define isc_barrier_destroy(barrier) uv_barrier_destroy(barrier) +#define isc_barrier_wait(barrier) uv_barrier_wait(barrier) + +#endif /* __SANITIZE_THREAD__ */ diff --git a/lib/isc/netmgr/http.c b/lib/isc/netmgr/http.c index 021feaa3c3..e64a018e5d 100644 --- a/lib/isc/netmgr/http.c +++ b/lib/isc/netmgr/http.c @@ -1199,7 +1199,7 @@ isc_nm_httpconnect(isc_nm_t *mgr, isc_nmiface_t *local, isc_nmiface_t *peer, sock->extrahandlesize = extrahandlesize; sock->connect_timeout = timeout; - sock->result = ISC_R_DEFAULT; + sock->result = ISC_R_UNSET; sock->connect_cb = cb; sock->connect_cbarg = cbarg; atomic_init(&sock->client, true); @@ -2170,7 +2170,7 @@ isc_nm_listenhttp(isc_nm_t *mgr, isc_nmiface_t *iface, int backlog, isc__nmsocket_attach(sock, &sock->outer->h2.httpserver); sock->nchildren = sock->outer->nchildren; - sock->result = ISC_R_DEFAULT; + sock->result = ISC_R_UNSET; sock->tid = isc_random_uniform(sock->nchildren); sock->fd = (uv_os_sock_t)-1; diff --git a/lib/isc/netmgr/netmgr-int.h b/lib/isc/netmgr/netmgr-int.h index 6db3f93d05..a126a4e403 100644 --- a/lib/isc/netmgr/netmgr-int.h +++ b/lib/isc/netmgr/netmgr-int.h @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -174,7 +175,6 @@ typedef struct isc__networker { uv_async_t async; /* async channel to send * data to this networker */ isc_mutex_t lock; - isc_condition_t cond; bool paused; bool finished; isc_thread_t thread; @@ -185,6 +185,8 @@ typedef struct isc__networker { * used for listening etc. * can be processed while * worker is paused */ + isc_condition_t cond_prio; + isc_refcount_t references; atomic_int_fast64_t pktcount; char *recvbuf; @@ -671,7 +673,7 @@ struct isc_nm { isc_mutex_t evlock; uint_fast32_t workers_running; - uint_fast32_t workers_paused; + atomic_uint_fast32_t workers_paused; atomic_uint_fast32_t maxudp; atomic_bool paused; @@ -702,6 +704,9 @@ struct isc_nm { atomic_uint_fast32_t keepalive; atomic_uint_fast32_t advertised; + isc_barrier_t pausing; + isc_barrier_t resuming; + #ifdef NETMGR_TRACE ISC_LIST(isc_nmsocket_t) active_sockets; #endif @@ -836,6 +841,9 @@ struct isc_nmsocket { /*% Self socket */ isc_nmsocket_t *self; + isc_barrier_t startlistening; + isc_barrier_t stoplistening; + /*% TLS stuff */ struct tls { isc_tls_t *tls; @@ -930,7 +938,7 @@ struct isc_nmsocket { /* Atomic */ /*% Number of running (e.g. listening) child sockets */ - uint_fast32_t rchildren; + atomic_uint_fast32_t rchildren; /*% * Socket is active if it's listening, working, etc. If it's diff --git a/lib/isc/netmgr/netmgr.c b/lib/isc/netmgr/netmgr.c index 56d4630266..b795dd5aba 100644 --- a/lib/isc/netmgr/netmgr.c +++ b/lib/isc/netmgr/netmgr.c @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -135,8 +136,12 @@ nm_thread(isc_threadarg_t worker0); static void async_cb(uv_async_t *handle); static bool +process_netievent(isc__networker_t *worker, isc__netievent_t *ievent); +static bool process_queue(isc__networker_t *worker, isc_queue_t *queue, unsigned int *quantump); +static void +wait_for_priority_queue(isc__networker_t *worker); static bool process_priority_queue(isc__networker_t *worker, unsigned int *quantump); static bool @@ -146,6 +151,15 @@ process_task_queue(isc__networker_t *worker, unsigned int *quantump); static bool process_normal_queue(isc__networker_t *worker, unsigned int *quantump); +#define drain_priority_queue(worker) \ + (void)process_priority_queue(worker, &(unsigned int){ UINT_MAX }) +#define drain_privilege_queue(worker) \ + (void)process_privilege_queue(worker, &(unsigned int){ UINT_MAX }) +#define drain_task_queue(worker) \ + (void)process_task_queue(worker, &(unsigned int){ UINT_MAX }) +#define drain_normal_queue(worker) \ + (void)process_normal_queue(worker, &(unsigned int){ UINT_MAX }) + static void isc__nm_async_stop(isc__networker_t *worker, isc__netievent_t *ev0); static void @@ -229,6 +243,7 @@ isc__netmgr_create(isc_mem_t *mctx, uint32_t workers, isc_nm_t **netmgrp) { isc_refcount_init(&mgr->references, 1); atomic_init(&mgr->maxudp, 0); atomic_init(&mgr->interlocked, ISC_NETMGR_NON_INTERLOCKED); + atomic_init(&mgr->workers_paused, 0); #ifdef NETMGR_TRACE ISC_LIST_INIT(mgr->active_sockets); @@ -258,6 +273,9 @@ isc__netmgr_create(isc_mem_t *mctx, uint32_t workers, isc_nm_t **netmgrp) { isc_mempool_associatelock(mgr->evpool, &mgr->evlock); isc_mempool_setfillcount(mgr->evpool, 32); + isc_barrier_init(&mgr->pausing, workers); + isc_barrier_init(&mgr->resuming, workers); + mgr->workers = isc_mem_get(mctx, workers * sizeof(isc__networker_t)); for (size_t i = 0; i < workers; i++) { int r; @@ -277,12 +295,13 @@ isc__netmgr_create(isc_mem_t *mctx, uint32_t workers, isc_nm_t **netmgrp) { RUNTIME_CHECK(r == 0); isc_mutex_init(&worker->lock); - isc_condition_init(&worker->cond); worker->ievents = isc_queue_new(mgr->mctx, 128); worker->ievents_task = isc_queue_new(mgr->mctx, 128); worker->ievents_priv = isc_queue_new(mgr->mctx, 128); worker->ievents_prio = isc_queue_new(mgr->mctx, 128); + isc_condition_init(&worker->cond_prio); + worker->recvbuf = isc_mem_get(mctx, ISC_NETMGR_RECVBUF_SIZE); worker->sendbuf = isc_mem_get(mctx, ISC_NETMGR_SENDBUF_SIZE); @@ -351,6 +370,7 @@ nm_destroy(isc_nm_t **mgr0) { { isc_mempool_put(mgr->evpool, ievent); } + isc_condition_destroy(&worker->cond_prio); r = uv_loop_close(&worker->loop); INSIST(r == 0); @@ -360,7 +380,6 @@ nm_destroy(isc_nm_t **mgr0) { isc_queue_destroy(worker->ievents_task); isc_queue_destroy(worker->ievents_prio); isc_mutex_destroy(&worker->lock); - isc_condition_destroy(&worker->cond); isc_mem_put(mgr->mctx, worker->sendbuf, ISC_NETMGR_SENDBUF_SIZE); @@ -373,6 +392,9 @@ nm_destroy(isc_nm_t **mgr0) { isc_stats_detach(&mgr->stats); } + isc_barrier_destroy(&mgr->resuming); + isc_barrier_destroy(&mgr->pausing); + isc_condition_destroy(&mgr->wkstatecond); isc_condition_destroy(&mgr->wkpausecond); isc_mutex_destroy(&mgr->lock); @@ -413,13 +435,18 @@ isc_nm_pause(isc_nm_t *mgr) { } } + if (isc__nm_in_netthread()) { + isc_barrier_wait(&mgr->pausing); + } + LOCK(&mgr->lock); - while (mgr->workers_paused != pausing) { + while (atomic_load(&mgr->workers_paused) != pausing) { WAIT(&mgr->wkstatecond, &mgr->lock); } + UNLOCK(&mgr->lock); + REQUIRE(atomic_compare_exchange_strong(&mgr->paused, &(bool){ false }, true)); - UNLOCK(&mgr->lock); } void @@ -439,14 +466,19 @@ isc_nm_resume(isc_nm_t *mgr) { } } + if (isc__nm_in_netthread()) { + isc_barrier_wait(&mgr->resuming); + } + LOCK(&mgr->lock); - while (mgr->workers_paused != 0) { + while (atomic_load(&mgr->workers_paused) != 0) { WAIT(&mgr->wkstatecond, &mgr->lock); } + UNLOCK(&mgr->lock); + REQUIRE(atomic_compare_exchange_strong(&mgr->paused, &(bool){ true }, false)); - BROADCAST(&mgr->wkpausecond); - UNLOCK(&mgr->lock); + isc__nm_drop_interlocked(mgr); } @@ -617,45 +649,29 @@ nm_thread(isc_threadarg_t worker0) { if (worker->paused) { INSIST(atomic_load(&mgr->interlocked) != isc_nm_tid()); - /* - * We need to lock the worker first; otherwise - * isc_nm_resume() might slip in before WAIT() in - * the while loop starts, then the signal never - * gets delivered and we are stuck forever in the - * paused loop. - */ - LOCK(&worker->lock); - LOCK(&mgr->lock); - mgr->workers_paused++; - SIGNAL(&mgr->wkstatecond); - UNLOCK(&mgr->lock); - - while (worker->paused) { - WAIT(&worker->cond, &worker->lock); - UNLOCK(&worker->lock); - (void)process_priority_queue( - worker, &(unsigned int){ UINT_MAX }); - LOCK(&worker->lock); + atomic_fetch_add(&mgr->workers_paused, 1); + if (isc_barrier_wait(&mgr->pausing) != 0) { + LOCK(&mgr->lock); + SIGNAL(&mgr->wkstatecond); + UNLOCK(&mgr->lock); } - LOCK(&mgr->lock); - mgr->workers_paused--; - SIGNAL(&mgr->wkstatecond); - UNLOCK(&mgr->lock); - UNLOCK(&worker->lock); + while (worker->paused) { + wait_for_priority_queue(worker); + } /* * All workers must drain the privileged event * queue before we resume from pause. */ - (void)process_privilege_queue( - worker, &(unsigned int){ UINT_MAX }); + drain_privilege_queue(worker); - LOCK(&mgr->lock); - while (atomic_load(&mgr->paused)) { - WAIT(&mgr->wkpausecond, &mgr->lock); + atomic_fetch_sub(&mgr->workers_paused, 1); + if (isc_barrier_wait(&mgr->resuming) != 0) { + LOCK(&mgr->lock); + SIGNAL(&mgr->wkstatecond); + UNLOCK(&mgr->lock); } - UNLOCK(&mgr->lock); } if (r == 0) { @@ -671,8 +687,8 @@ nm_thread(isc_threadarg_t worker0) { * (they may include shutdown events) but do not process * the netmgr event queue. */ - (void)process_privilege_queue(worker, &(unsigned int){ UINT_MAX }); - (void)process_task_queue(worker, &(unsigned int){ UINT_MAX }); + drain_privilege_queue(worker); + drain_task_queue(worker); LOCK(&mgr->lock); mgr->workers_running--; @@ -792,6 +808,34 @@ isc__nm_async_task(isc__networker_t *worker, isc__netievent_t *ev0) { } } +static void +wait_for_priority_queue(isc__networker_t *worker) { + isc_queue_t *queue = worker->ievents_prio; + isc_condition_t *cond = &worker->cond_prio; + bool wait_for_work = true; + + while (true) { + isc__netievent_t *ievent; + LOCK(&worker->lock); + ievent = (isc__netievent_t *)isc_queue_dequeue(queue); + if (wait_for_work) { + while (ievent == NULL) { + WAIT(cond, &worker->lock); + ievent = (isc__netievent_t *)isc_queue_dequeue( + queue); + } + } + UNLOCK(&worker->lock); + wait_for_work = false; + + if (ievent == NULL) { + return; + } + + (void)process_netievent(worker, ievent); + } +} + static bool process_priority_queue(isc__networker_t *worker, unsigned int *quantump) { return (process_queue(worker, worker->ievents_prio, quantump)); @@ -917,13 +961,13 @@ process_queue(isc__networker_t *worker, isc_queue_t *queue, isc__netievent_t *ievent = (isc__netievent_t *)isc_queue_dequeue(queue); - (*quantump)--; - if (ievent == NULL) { /* We fully drained this queue */ return (true); } + (*quantump)--; + if (!process_netievent(worker, ievent)) { /* Netievent told us to stop */ return (false); @@ -1034,7 +1078,7 @@ isc__nm_enqueue_ievent(isc__networker_t *worker, isc__netievent_t *event) { */ LOCK(&worker->lock); isc_queue_enqueue(worker->ievents_prio, (uintptr_t)event); - SIGNAL(&worker->cond); + SIGNAL(&worker->cond_prio); UNLOCK(&worker->lock); } else if (event->type == netievent_privilegedtask) { isc_queue_enqueue(worker->ievents_priv, (uintptr_t)event); @@ -1122,7 +1166,14 @@ nmsocket_cleanup(isc_nmsocket_t *sock, bool dofree FLARG) { } /* - * This was a parent socket; free the children. + * This was a parent socket: destroy the listening + * barriers that synchronized the children. + */ + isc_barrier_destroy(&sock->startlistening); + isc_barrier_destroy(&sock->stoplistening); + + /* + * Now free them. */ isc_mem_put(sock->mgr->mctx, sock->children, sock->nchildren * sizeof(*sock)); @@ -1169,7 +1220,6 @@ nmsocket_cleanup(isc_nmsocket_t *sock, bool dofree FLARG) { isc_mem_free(sock->mgr->mctx, sock->ah_frees); isc_mem_free(sock->mgr->mctx, sock->ah_handles); isc_mutex_destroy(&sock->lock); - isc_condition_destroy(&sock->cond); isc_condition_destroy(&sock->scond); isc__nm_tls_cleanup_data(sock); isc__nm_http_cleanup_data(sock); @@ -1416,7 +1466,6 @@ isc___nmsocket_init(isc_nmsocket_t *sock, isc_nm_t *mgr, isc_nmsocket_type type, } isc_mutex_init(&sock->lock); - isc_condition_init(&sock->cond); isc_condition_init(&sock->scond); isc_refcount_init(&sock->references, 1); diff --git a/lib/isc/netmgr/tcp.c b/lib/isc/netmgr/tcp.c index 60cf9222c5..052030bf79 100644 --- a/lib/isc/netmgr/tcp.c +++ b/lib/isc/netmgr/tcp.c @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -116,7 +117,7 @@ failed_accept_cb(isc_nmsocket_t *sock, isc_result_t eresult) { static isc_result_t tcp_connect_direct(isc_nmsocket_t *sock, isc__nm_uvreq_t *req) { isc__networker_t *worker = NULL; - isc_result_t result = ISC_R_DEFAULT; + isc_result_t result = ISC_R_UNSET; int r; REQUIRE(VALID_NMSOCK(sock)); @@ -302,7 +303,7 @@ isc_nm_tcpconnect(isc_nm_t *mgr, isc_nmiface_t *local, isc_nmiface_t *peer, sock->extrahandlesize = extrahandlesize; sock->connect_timeout = timeout; - sock->result = ISC_R_DEFAULT; + sock->result = ISC_R_UNSET; sock->fd = (uv_os_sock_t)-1; atomic_init(&sock->client, true); @@ -344,7 +345,7 @@ isc_nm_tcpconnect(isc_nm_t *mgr, isc_nmiface_t *local, isc_nmiface_t *peer, (isc__netievent_t *)ievent); } LOCK(&sock->lock); - while (sock->result == ISC_R_DEFAULT) { + while (sock->result == ISC_R_UNSET) { WAIT(&sock->cond, &sock->lock); } atomic_store(&sock->active, true); @@ -375,6 +376,39 @@ isc__nm_tcp_lb_socket(sa_family_t sa_family) { return (sock); } +static void +start_tcp_child(isc_nm_t *mgr, isc_nmiface_t *iface, isc_nmsocket_t *sock, + uv_os_sock_t fd, int tid) { + isc__netievent_tcplisten_t *ievent = NULL; + isc_nmsocket_t *csock = &sock->children[tid]; + + isc__nmsocket_init(csock, mgr, isc_nm_tcpsocket, iface); + csock->parent = sock; + csock->accept_cb = sock->accept_cb; + csock->accept_cbarg = sock->accept_cbarg; + csock->extrahandlesize = sock->extrahandlesize; + csock->backlog = sock->backlog; + csock->tid = tid; + /* + * We don't attach to quota, just assign - to avoid + * increasing quota unnecessarily. + */ + csock->pquota = sock->pquota; + isc_quota_cb_init(&csock->quotacb, quota_accept_cb, csock); + +#if HAVE_SO_REUSEPORT_LB || defined(WIN32) + UNUSED(fd); + csock->fd = isc__nm_tcp_lb_socket(iface->addr.type.sa.sa_family); +#else + csock->fd = dup(fd); +#endif + REQUIRE(csock->fd >= 0); + + ievent = isc__nm_get_netievent_tcplisten(mgr, csock); + isc__nm_maybe_enqueue_ievent(&mgr->workers[tid], + (isc__netievent_t *)ievent); +} + isc_result_t isc_nm_listentcp(isc_nm_t *mgr, isc_nmiface_t *iface, isc_nm_accept_cb_t accept_cb, void *accept_cbarg, @@ -382,18 +416,15 @@ isc_nm_listentcp(isc_nm_t *mgr, isc_nmiface_t *iface, isc_nmsocket_t **sockp) { isc_result_t result = ISC_R_SUCCESS; isc_nmsocket_t *sock = NULL; - sa_family_t sa_family = iface->addr.type.sa.sa_family; size_t children_size = 0; -#if !HAVE_SO_REUSEPORT_LB && !defined(WIN32) uv_os_sock_t fd = -1; -#endif REQUIRE(VALID_NM(mgr)); sock = isc_mem_get(mgr->mctx, sizeof(*sock)); isc__nmsocket_init(sock, mgr, isc_nm_tcplistener, iface); - sock->rchildren = 0; + atomic_init(&sock->rchildren, 0); #if defined(WIN32) sock->nchildren = 1; #else @@ -403,42 +434,36 @@ isc_nm_listentcp(isc_nm_t *mgr, isc_nmiface_t *iface, sock->children = isc_mem_get(mgr->mctx, children_size); memset(sock->children, 0, children_size); - sock->result = ISC_R_DEFAULT; - sock->tid = isc_random_uniform(sock->nchildren); + sock->result = ISC_R_UNSET; + + sock->accept_cb = accept_cb; + sock->accept_cbarg = accept_cbarg; + sock->extrahandlesize = extrahandlesize; + sock->backlog = backlog; + sock->pquota = quota; + + if (isc__nm_in_netthread()) { + sock->tid = isc_nm_tid(); + } else { + sock->tid = isc_random_uniform(sock->nchildren); + } sock->fd = -1; #if !HAVE_SO_REUSEPORT_LB && !defined(WIN32) - fd = isc__nm_tcp_lb_socket(sa_family); + fd = isc__nm_tcp_lb_socket(iface->addr.type.sa.sa_family); #endif + isc_barrier_init(&sock->startlistening, sock->nchildren); + for (size_t i = 0; i < sock->nchildren; i++) { - isc__netievent_tcplisten_t *ievent = NULL; - isc_nmsocket_t *csock = &sock->children[i]; + if ((int)i == isc_nm_tid()) { + continue; + } + start_tcp_child(mgr, iface, sock, fd, i); + } - isc__nmsocket_init(csock, mgr, isc_nm_tcpsocket, iface); - csock->parent = sock; - csock->accept_cb = accept_cb; - csock->accept_cbarg = accept_cbarg; - csock->extrahandlesize = extrahandlesize; - csock->backlog = backlog; - csock->tid = i; - /* - * We don't attach to quota, just assign - to avoid - * increasing quota unnecessarily. - */ - csock->pquota = quota; - isc_quota_cb_init(&csock->quotacb, quota_accept_cb, csock); - -#if HAVE_SO_REUSEPORT_LB || defined(WIN32) - csock->fd = isc__nm_tcp_lb_socket(sa_family); -#else - csock->fd = dup(fd); -#endif - REQUIRE(csock->fd >= 0); - - ievent = isc__nm_get_netievent_tcplisten(mgr, csock); - isc__nm_maybe_enqueue_ievent(&mgr->workers[i], - (isc__netievent_t *)ievent); + if (isc__nm_in_netthread()) { + start_tcp_child(mgr, iface, sock, fd, isc_nm_tid()); } #if !HAVE_SO_REUSEPORT_LB && !defined(WIN32) @@ -446,21 +471,21 @@ isc_nm_listentcp(isc_nm_t *mgr, isc_nmiface_t *iface, #endif LOCK(&sock->lock); - while (sock->rchildren != sock->nchildren) { + while (atomic_load(&sock->rchildren) != sock->nchildren) { WAIT(&sock->cond, &sock->lock); } result = sock->result; atomic_store(&sock->active, true); - BROADCAST(&sock->scond); UNLOCK(&sock->lock); - INSIST(result != ISC_R_DEFAULT); + + INSIST(result != ISC_R_UNSET); if (result == ISC_R_SUCCESS) { - REQUIRE(sock->rchildren == sock->nchildren); + REQUIRE(atomic_load(&sock->rchildren) == sock->nchildren); *sockp = sock; } else { atomic_store(&sock->active, false); - isc__nm_tcp_stoplistening(sock); + isc_nm_stoplistening(sock); isc_nmsocket_close(&sock); } @@ -565,16 +590,14 @@ done: sock->pquota = NULL; } - sock->parent->rchildren += 1; - if (sock->parent->result == ISC_R_DEFAULT) { + atomic_fetch_add(&sock->parent->rchildren, 1); + if (sock->parent->result == ISC_R_UNSET) { sock->parent->result = result; } SIGNAL(&sock->parent->cond); - if (!atomic_load(&sock->parent->active)) { - WAIT(&sock->parent->scond, &sock->parent->lock); - } - INSIST(atomic_load(&sock->parent->active)); UNLOCK(&sock->parent->lock); + + isc_barrier_wait(&sock->parent->startlistening); } static void @@ -637,7 +660,15 @@ isc__nm_tcp_stoplistening(isc_nmsocket_t *sock) { INSIST(0); ISC_UNREACHABLE(); } - enqueue_stoplistening(sock); + + if (!isc__nm_in_netthread()) { + enqueue_stoplistening(sock); + } else if (!isc__nm_acquire_interlocked(sock->mgr)) { + enqueue_stoplistening(sock); + } else { + stop_tcp_parent(sock); + isc__nm_drop_interlocked(sock->mgr); + } } void @@ -655,7 +686,12 @@ isc__nm_async_tcpstop(isc__networker_t *worker, isc__netievent_t *ev0) { return; } - stop_tcp_parent(sock); + if (!isc__nm_acquire_interlocked(sock->mgr)) { + enqueue_stoplistening(sock); + } else { + stop_tcp_parent(sock); + isc__nm_drop_interlocked(sock->mgr); + } } void @@ -1194,8 +1230,6 @@ timer_close_cb(uv_handle_t *handle) { static void stop_tcp_child(isc_nmsocket_t *sock) { - bool last_child = false; - REQUIRE(sock->type == isc_nm_tcpsocket); REQUIRE(sock->tid == isc_nm_tid()); @@ -1206,33 +1240,40 @@ stop_tcp_child(isc_nmsocket_t *sock) { tcp_close_direct(sock); - LOCK(&sock->parent->lock); - sock->parent->rchildren -= 1; - last_child = (sock->parent->rchildren == 0); - UNLOCK(&sock->parent->lock); + atomic_fetch_sub(&sock->parent->rchildren, 1); - if (last_child) { - atomic_store(&sock->parent->closed, true); - isc__nmsocket_prep_destroy(sock->parent); - } + isc_barrier_wait(&sock->parent->stoplistening); } static void stop_tcp_parent(isc_nmsocket_t *sock) { + isc_nmsocket_t *csock = NULL; REQUIRE(VALID_NMSOCK(sock)); REQUIRE(sock->type == isc_nm_tcplistener); + isc_barrier_init(&sock->stoplistening, sock->nchildren); + for (size_t i = 0; i < sock->nchildren; i++) { - isc__netievent_tcpstop_t *ievent = NULL; - isc_nmsocket_t *csock = &sock->children[i]; + csock = &sock->children[i]; REQUIRE(VALID_NMSOCK(csock)); - atomic_store(&csock->active, false); + if ((int)i == isc_nm_tid()) { + /* + * We need to schedule closing the other sockets first + */ + continue; + } - ievent = isc__nm_get_netievent_tcpstop(sock->mgr, csock); - isc__nm_enqueue_ievent(&sock->mgr->workers[csock->tid], - (isc__netievent_t *)ievent); + atomic_store(&csock->active, false); + enqueue_stoplistening(csock); } + + csock = &sock->children[isc_nm_tid()]; + atomic_store(&csock->active, false); + stop_tcp_child(csock); + + atomic_store(&sock->closed, true); + isc__nmsocket_prep_destroy(sock); } static void diff --git a/lib/isc/netmgr/tcpdns.c b/lib/isc/netmgr/tcpdns.c index 9d2601b906..142d8b30fa 100644 --- a/lib/isc/netmgr/tcpdns.c +++ b/lib/isc/netmgr/tcpdns.c @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -85,7 +86,7 @@ stop_tcpdns_child(isc_nmsocket_t *sock); static isc_result_t tcpdns_connect_direct(isc_nmsocket_t *sock, isc__nm_uvreq_t *req) { isc__networker_t *worker = NULL; - isc_result_t result = ISC_R_DEFAULT; + isc_result_t result = ISC_R_UNSET; int r; REQUIRE(VALID_NMSOCK(sock)); @@ -269,7 +270,7 @@ isc_nm_tcpdnsconnect(isc_nm_t *mgr, isc_nmiface_t *local, isc_nmiface_t *peer, sock->extrahandlesize = extrahandlesize; sock->connect_timeout = timeout; - sock->result = ISC_R_DEFAULT; + sock->result = ISC_R_UNSET; atomic_init(&sock->client, true); req = isc__nm_uvreq_get(mgr, sock); @@ -311,7 +312,7 @@ isc_nm_tcpdnsconnect(isc_nm_t *mgr, isc_nmiface_t *local, isc_nmiface_t *peer, } LOCK(&sock->lock); - while (sock->result == ISC_R_DEFAULT) { + while (sock->result == ISC_R_UNSET) { WAIT(&sock->cond, &sock->lock); } atomic_store(&sock->active, true); @@ -342,6 +343,40 @@ isc__nm_tcpdns_lb_socket(sa_family_t sa_family) { return (sock); } +static void +start_tcpdns_child(isc_nm_t *mgr, isc_nmiface_t *iface, isc_nmsocket_t *sock, + uv_os_sock_t fd, int tid) { + isc__netievent_tcpdnslisten_t *ievent = NULL; + isc_nmsocket_t *csock = &sock->children[tid]; + + isc__nmsocket_init(csock, mgr, isc_nm_tcpdnssocket, iface); + csock->parent = sock; + csock->accept_cb = sock->accept_cb; + csock->accept_cbarg = sock->accept_cbarg; + csock->recv_cb = sock->recv_cb; + csock->recv_cbarg = sock->recv_cbarg; + csock->extrahandlesize = sock->extrahandlesize; + csock->backlog = sock->backlog; + csock->tid = tid; + /* + * We don't attach to quota, just assign - to avoid + * increasing quota unnecessarily. + */ + csock->pquota = sock->pquota; + isc_quota_cb_init(&csock->quotacb, quota_accept_cb, csock); + +#if HAVE_SO_REUSEPORT_LB || defined(WIN32) + UNUSED(fd); + csock->fd = isc__nm_tcpdns_lb_socket(iface->addr.type.sa.sa_family); +#else + csock->fd = dup(fd); +#endif + REQUIRE(csock->fd >= 0); + + ievent = isc__nm_get_netievent_tcpdnslisten(mgr, csock); + isc__nm_maybe_enqueue_ievent(&mgr->workers[tid], + (isc__netievent_t *)ievent); +} isc_result_t isc_nm_listentcpdns(isc_nm_t *mgr, isc_nmiface_t *iface, isc_nm_recv_cb_t recv_cb, void *recv_cbarg, @@ -350,18 +385,15 @@ isc_nm_listentcpdns(isc_nm_t *mgr, isc_nmiface_t *iface, isc_nmsocket_t **sockp) { isc_result_t result = ISC_R_SUCCESS; isc_nmsocket_t *sock = NULL; - sa_family_t sa_family = iface->addr.type.sa.sa_family; size_t children_size = 0; -#if !HAVE_SO_REUSEPORT_LB && !defined(WIN32) uv_os_sock_t fd = -1; -#endif REQUIRE(VALID_NM(mgr)); sock = isc_mem_get(mgr->mctx, sizeof(*sock)); isc__nmsocket_init(sock, mgr, isc_nm_tcpdnslistener, iface); - sock->rchildren = 0; + atomic_init(&sock->rchildren, 0); #if defined(WIN32) sock->nchildren = 1; #else @@ -371,44 +403,37 @@ isc_nm_listentcpdns(isc_nm_t *mgr, isc_nmiface_t *iface, sock->children = isc_mem_get(mgr->mctx, children_size); memset(sock->children, 0, children_size); - sock->result = ISC_R_DEFAULT; - sock->tid = isc_random_uniform(sock->nchildren); + sock->result = ISC_R_UNSET; + sock->accept_cb = accept_cb; + sock->accept_cbarg = accept_cbarg; + sock->recv_cb = recv_cb; + sock->recv_cbarg = recv_cbarg; + sock->extrahandlesize = extrahandlesize; + sock->backlog = backlog; + sock->pquota = quota; + + if (isc__nm_in_netthread()) { + sock->tid = isc_nm_tid(); + } else { + sock->tid = isc_random_uniform(sock->nchildren); + } sock->fd = -1; #if !HAVE_SO_REUSEPORT_LB && !defined(WIN32) - fd = isc__nm_tcpdns_lb_socket(sa_family); + fd = isc__nm_tcpdns_lb_socket(iface->addr.type.sa.sa_family); #endif + isc_barrier_init(&sock->startlistening, sock->nchildren); + for (size_t i = 0; i < sock->nchildren; i++) { - isc__netievent_tcpdnslisten_t *ievent = NULL; - isc_nmsocket_t *csock = &sock->children[i]; + if ((int)i == isc_nm_tid()) { + continue; + } + start_tcpdns_child(mgr, iface, sock, fd, i); + } - isc__nmsocket_init(csock, mgr, isc_nm_tcpdnssocket, iface); - csock->parent = sock; - csock->accept_cb = accept_cb; - csock->accept_cbarg = accept_cbarg; - csock->recv_cb = recv_cb; - csock->recv_cbarg = recv_cbarg; - csock->extrahandlesize = extrahandlesize; - csock->backlog = backlog; - csock->tid = i; - /* - * We don't attach to quota, just assign - to avoid - * increasing quota unnecessarily. - */ - csock->pquota = quota; - isc_quota_cb_init(&csock->quotacb, quota_accept_cb, csock); - -#if HAVE_SO_REUSEPORT_LB || defined(WIN32) - csock->fd = isc__nm_tcpdns_lb_socket(sa_family); -#else - csock->fd = dup(fd); -#endif - REQUIRE(csock->fd >= 0); - - ievent = isc__nm_get_netievent_tcpdnslisten(mgr, csock); - isc__nm_maybe_enqueue_ievent(&mgr->workers[i], - (isc__netievent_t *)ievent); + if (isc__nm_in_netthread()) { + start_tcpdns_child(mgr, iface, sock, fd, isc_nm_tid()); } #if !HAVE_SO_REUSEPORT_LB && !defined(WIN32) @@ -416,21 +441,21 @@ isc_nm_listentcpdns(isc_nm_t *mgr, isc_nmiface_t *iface, #endif LOCK(&sock->lock); - while (sock->rchildren != sock->nchildren) { + while (atomic_load(&sock->rchildren) != sock->nchildren) { WAIT(&sock->cond, &sock->lock); } result = sock->result; atomic_store(&sock->active, true); - BROADCAST(&sock->scond); UNLOCK(&sock->lock); - INSIST(result != ISC_R_DEFAULT); + + INSIST(result != ISC_R_UNSET); if (result == ISC_R_SUCCESS) { - REQUIRE(sock->rchildren == sock->nchildren); + REQUIRE(atomic_load(&sock->rchildren) == sock->nchildren); *sockp = sock; } else { atomic_store(&sock->active, false); - isc__nm_tcpdns_stoplistening(sock); + isc_nm_stoplistening(sock); isc_nmsocket_close(&sock); } @@ -446,7 +471,7 @@ isc__nm_async_tcpdnslisten(isc__networker_t *worker, isc__netievent_t *ev0) { int r; int flags = 0; isc_nmsocket_t *sock = NULL; - isc_result_t result = ISC_R_DEFAULT; + isc_result_t result = ISC_R_UNSET; REQUIRE(VALID_NMSOCK(ievent->sock)); REQUIRE(ievent->sock->tid == isc_nm_tid()); @@ -534,16 +559,14 @@ done: sock->pquota = NULL; } - sock->parent->rchildren += 1; - if (sock->parent->result == ISC_R_DEFAULT) { + atomic_fetch_add(&sock->parent->rchildren, 1); + if (sock->parent->result == ISC_R_UNSET) { sock->parent->result = result; } SIGNAL(&sock->parent->cond); - if (!atomic_load(&sock->parent->active)) { - WAIT(&sock->parent->scond, &sock->parent->lock); - } - INSIST(atomic_load(&sock->parent->active)); UNLOCK(&sock->parent->lock); + + isc_barrier_wait(&sock->parent->startlistening); } static void @@ -607,7 +630,15 @@ isc__nm_tcpdns_stoplistening(isc_nmsocket_t *sock) { INSIST(0); ISC_UNREACHABLE(); } - enqueue_stoplistening(sock); + + if (!isc__nm_in_netthread()) { + enqueue_stoplistening(sock); + } else if (!isc__nm_acquire_interlocked(sock->mgr)) { + enqueue_stoplistening(sock); + } else { + stop_tcpdns_parent(sock); + isc__nm_drop_interlocked(sock->mgr); + } } void @@ -626,7 +657,15 @@ isc__nm_async_tcpdnsstop(isc__networker_t *worker, isc__netievent_t *ev0) { return; } - stop_tcpdns_parent(sock); + /* + * If network manager is paused, re-enqueue the event for later. + */ + if (!isc__nm_acquire_interlocked(sock->mgr)) { + enqueue_stoplistening(sock); + } else { + stop_tcpdns_parent(sock); + isc__nm_drop_interlocked(sock->mgr); + } } void @@ -1224,8 +1263,6 @@ timer_close_cb(uv_handle_t *timer) { static void stop_tcpdns_child(isc_nmsocket_t *sock) { - bool last_child = false; - REQUIRE(sock->type == isc_nm_tcpdnssocket); REQUIRE(sock->tid == isc_nm_tid()); @@ -1236,33 +1273,41 @@ stop_tcpdns_child(isc_nmsocket_t *sock) { tcpdns_close_direct(sock); - LOCK(&sock->parent->lock); - sock->parent->rchildren -= 1; - last_child = (sock->parent->rchildren == 0); - UNLOCK(&sock->parent->lock); + atomic_fetch_sub(&sock->parent->rchildren, 1); - if (last_child) { - atomic_store(&sock->parent->closed, true); - isc__nmsocket_prep_destroy(sock->parent); - } + isc_barrier_wait(&sock->parent->stoplistening); } static void stop_tcpdns_parent(isc_nmsocket_t *sock) { + isc_nmsocket_t *csock = NULL; + REQUIRE(VALID_NMSOCK(sock)); REQUIRE(sock->type == isc_nm_tcpdnslistener); + isc_barrier_init(&sock->stoplistening, sock->nchildren); + for (size_t i = 0; i < sock->nchildren; i++) { - isc__netievent_tcpdnsstop_t *ievent = NULL; - isc_nmsocket_t *csock = &sock->children[i]; + csock = &sock->children[i]; REQUIRE(VALID_NMSOCK(csock)); - atomic_store(&csock->active, false); + if ((int)i == isc_nm_tid()) { + /* + * We need to schedule closing the other sockets first + */ + continue; + } - ievent = isc__nm_get_netievent_tcpdnsstop(sock->mgr, csock); - isc__nm_enqueue_ievent(&sock->mgr->workers[csock->tid], - (isc__netievent_t *)ievent); + atomic_store(&csock->active, false); + enqueue_stoplistening(csock); } + + csock = &sock->children[isc_nm_tid()]; + atomic_store(&csock->active, false); + stop_tcpdns_child(csock); + + atomic_store(&sock->closed, true); + isc__nmsocket_prep_destroy(sock); } static void diff --git a/lib/isc/netmgr/tlsdns.c b/lib/isc/netmgr/tlsdns.c index bb7ea76a3c..3f68c7f513 100644 --- a/lib/isc/netmgr/tlsdns.c +++ b/lib/isc/netmgr/tlsdns.c @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -97,7 +98,7 @@ can_log_tlsdns_quota(void) { static isc_result_t tlsdns_connect_direct(isc_nmsocket_t *sock, isc__nm_uvreq_t *req) { isc__networker_t *worker = NULL; - isc_result_t result = ISC_R_DEFAULT; + isc_result_t result = ISC_R_UNSET; int r; REQUIRE(VALID_NMSOCK(sock)); @@ -324,7 +325,7 @@ isc_nm_tlsdnsconnect(isc_nm_t *mgr, isc_nmiface_t *local, isc_nmiface_t *peer, sock->extrahandlesize = extrahandlesize; sock->connect_timeout = timeout; - sock->result = ISC_R_DEFAULT; + sock->result = ISC_R_UNSET; sock->tls.ctx = sslctx; atomic_init(&sock->client, true); atomic_init(&sock->connecting, true); @@ -364,7 +365,7 @@ isc_nm_tlsdnsconnect(isc_nm_t *mgr, isc_nmiface_t *local, isc_nmiface_t *peer, (isc__netievent_t *)ievent); } LOCK(&sock->lock); - while (sock->result == ISC_R_DEFAULT) { + while (sock->result == ISC_R_UNSET) { WAIT(&sock->cond, &sock->lock); } atomic_store(&sock->active, true); @@ -407,6 +408,43 @@ isc__nm_tlsdns_lb_socket(sa_family_t sa_family) { return (sock); } +static void +start_tlsdns_child(isc_nm_t *mgr, isc_nmiface_t *iface, isc_nmsocket_t *sock, + uv_os_sock_t fd, int tid) { + isc__netievent_tlsdnslisten_t *ievent = NULL; + isc_nmsocket_t *csock = &sock->children[tid]; + + isc__nmsocket_init(csock, mgr, isc_nm_tlsdnssocket, iface); + csock->parent = sock; + csock->accept_cb = sock->accept_cb; + csock->accept_cbarg = sock->accept_cbarg; + csock->recv_cb = sock->recv_cb; + csock->recv_cbarg = sock->recv_cbarg; + csock->extrahandlesize = sock->extrahandlesize; + csock->backlog = sock->backlog; + csock->tid = tid; + csock->tls.ctx = sock->tls.ctx; + + /* + * We don't attach to quota, just assign - to avoid + * increasing quota unnecessarily. + */ + csock->pquota = sock->pquota; + isc_quota_cb_init(&csock->quotacb, quota_accept_cb, csock); + +#if HAVE_SO_REUSEPORT_LB || defined(WIN32) + UNUSED(fd); + csock->fd = isc__nm_tlsdns_lb_socket(iface->addr.type.sa.sa_family); +#else + csock->fd = dup(fd); +#endif + REQUIRE(csock->fd >= 0); + + ievent = isc__nm_get_netievent_tlsdnslisten(mgr, csock); + isc__nm_maybe_enqueue_ievent(&mgr->workers[tid], + (isc__netievent_t *)ievent); +} + isc_result_t isc_nm_listentlsdns(isc_nm_t *mgr, isc_nmiface_t *iface, isc_nm_recv_cb_t recv_cb, void *recv_cbarg, @@ -415,18 +453,15 @@ isc_nm_listentlsdns(isc_nm_t *mgr, isc_nmiface_t *iface, isc_tlsctx_t *sslctx, isc_nmsocket_t **sockp) { isc_result_t result = ISC_R_SUCCESS; isc_nmsocket_t *sock = NULL; - sa_family_t sa_family = iface->addr.type.sa.sa_family; size_t children_size = 0; -#if !HAVE_SO_REUSEPORT_LB && !defined(WIN32) uv_os_sock_t fd = -1; -#endif REQUIRE(VALID_NM(mgr)); sock = isc_mem_get(mgr->mctx, sizeof(*sock)); isc__nmsocket_init(sock, mgr, isc_nm_tlsdnslistener, iface); - sock->rchildren = 0; + atomic_init(&sock->rchildren, 0); #if defined(WIN32) sock->nchildren = 1; #else @@ -436,47 +471,39 @@ isc_nm_listentlsdns(isc_nm_t *mgr, isc_nmiface_t *iface, sock->children = isc_mem_get(mgr->mctx, children_size); memset(sock->children, 0, children_size); - sock->result = ISC_R_DEFAULT; - sock->tid = isc_random_uniform(sock->nchildren); - sock->fd = -1; + sock->result = ISC_R_UNSET; + sock->accept_cb = accept_cb; + sock->accept_cbarg = accept_cbarg; + sock->recv_cb = recv_cb; + sock->recv_cbarg = recv_cbarg; + sock->extrahandlesize = extrahandlesize; + sock->backlog = backlog; + sock->pquota = quota; + + if (isc__nm_in_netthread()) { + sock->tid = isc_nm_tid(); + } else { + sock->tid = isc_random_uniform(sock->nchildren); + } + sock->tls.ctx = sslctx; + sock->fd = -1; #if !HAVE_SO_REUSEPORT_LB && !defined(WIN32) - fd = isc__nm_tlsdns_lb_socket(sa_family); + fd = isc__nm_tlsdns_lb_socket(iface->addr.type.sa.sa_family); #endif + isc_barrier_init(&sock->startlistening, sock->nchildren); + for (size_t i = 0; i < sock->nchildren; i++) { - isc__netievent_tlsdnslisten_t *ievent = NULL; - isc_nmsocket_t *csock = &sock->children[i]; + if ((int)i == isc_nm_tid()) { + continue; + } + start_tlsdns_child(mgr, iface, sock, fd, i); + } - isc__nmsocket_init(csock, mgr, isc_nm_tlsdnssocket, iface); - csock->parent = sock; - csock->accept_cb = accept_cb; - csock->accept_cbarg = accept_cbarg; - csock->recv_cb = recv_cb; - csock->recv_cbarg = recv_cbarg; - csock->extrahandlesize = extrahandlesize; - csock->backlog = backlog; - csock->tid = i; - csock->tls.ctx = sslctx; - - /* - * We don't attach to quota, just assign - to avoid - * increasing quota unnecessarily. - */ - csock->pquota = quota; - isc_quota_cb_init(&csock->quotacb, quota_accept_cb, csock); - -#if HAVE_SO_REUSEPORT_LB || defined(WIN32) - csock->fd = isc__nm_tlsdns_lb_socket(sa_family); -#else - csock->fd = dup(fd); -#endif - REQUIRE(csock->fd >= 0); - - ievent = isc__nm_get_netievent_tlsdnslisten(mgr, csock); - isc__nm_maybe_enqueue_ievent(&mgr->workers[i], - (isc__netievent_t *)ievent); + if (isc__nm_in_netthread()) { + start_tlsdns_child(mgr, iface, sock, fd, isc_nm_tid()); } #if !HAVE_SO_REUSEPORT_LB && !defined(WIN32) @@ -484,21 +511,21 @@ isc_nm_listentlsdns(isc_nm_t *mgr, isc_nmiface_t *iface, #endif LOCK(&sock->lock); - while (sock->rchildren != sock->nchildren) { + while (atomic_load(&sock->rchildren) != sock->nchildren) { WAIT(&sock->cond, &sock->lock); } result = sock->result; atomic_store(&sock->active, true); - BROADCAST(&sock->scond); UNLOCK(&sock->lock); - INSIST(result != ISC_R_DEFAULT); + + INSIST(result != ISC_R_UNSET); if (result == ISC_R_SUCCESS) { - REQUIRE(sock->rchildren == sock->nchildren); + REQUIRE(atomic_load(&sock->rchildren) == sock->nchildren); *sockp = sock; } else { atomic_store(&sock->active, false); - isc__nm_tlsdns_stoplistening(sock); + isc_nm_stoplistening(sock); isc_nmsocket_close(&sock); } @@ -514,7 +541,7 @@ isc__nm_async_tlsdnslisten(isc__networker_t *worker, isc__netievent_t *ev0) { int r; int flags = 0; isc_nmsocket_t *sock = NULL; - isc_result_t result = ISC_R_DEFAULT; + isc_result_t result = ISC_R_UNSET; REQUIRE(VALID_NMSOCK(ievent->sock)); REQUIRE(ievent->sock->tid == isc_nm_tid()); @@ -603,16 +630,14 @@ done: sock->pquota = NULL; } - sock->parent->rchildren += 1; - if (sock->parent->result == ISC_R_DEFAULT) { + atomic_fetch_add(&sock->parent->rchildren, 1); + if (sock->parent->result == ISC_R_UNSET) { sock->parent->result = result; } SIGNAL(&sock->parent->cond); - if (!atomic_load(&sock->parent->active)) { - WAIT(&sock->parent->scond, &sock->parent->lock); - } - INSIST(atomic_load(&sock->parent->active)); UNLOCK(&sock->parent->lock); + + isc_barrier_wait(&sock->parent->startlistening); } static void @@ -676,7 +701,15 @@ isc__nm_tlsdns_stoplistening(isc_nmsocket_t *sock) { INSIST(0); ISC_UNREACHABLE(); } - enqueue_stoplistening(sock); + + if (!isc__nm_in_netthread()) { + enqueue_stoplistening(sock); + } else if (!isc__nm_acquire_interlocked(sock->mgr)) { + enqueue_stoplistening(sock); + } else { + stop_tlsdns_parent(sock); + isc__nm_drop_interlocked(sock->mgr); + } } static void @@ -770,7 +803,15 @@ isc__nm_async_tlsdnsstop(isc__networker_t *worker, isc__netievent_t *ev0) { return; } - stop_tlsdns_parent(sock); + /* + * If network manager is paused, re-enqueue the event for later. + */ + if (!isc__nm_acquire_interlocked(sock->mgr)) { + enqueue_stoplistening(sock); + } else { + stop_tlsdns_parent(sock); + isc__nm_drop_interlocked(sock->mgr); + } } void @@ -1770,8 +1811,6 @@ timer_close_cb(uv_handle_t *handle) { static void stop_tlsdns_child(isc_nmsocket_t *sock) { - bool last_child = false; - REQUIRE(sock->type == isc_nm_tlsdnssocket); REQUIRE(sock->tid == isc_nm_tid()); @@ -1782,34 +1821,42 @@ stop_tlsdns_child(isc_nmsocket_t *sock) { tlsdns_close_direct(sock); - LOCK(&sock->parent->lock); - sock->parent->rchildren -= 1; - last_child = (sock->parent->rchildren == 0); - UNLOCK(&sock->parent->lock); + atomic_fetch_sub(&sock->parent->rchildren, 1); - if (last_child) { - atomic_store(&sock->parent->closed, true); - isc__nmsocket_prep_destroy(sock->parent); - } + isc_barrier_wait(&sock->parent->stoplistening); } static void stop_tlsdns_parent(isc_nmsocket_t *sock) { + isc_nmsocket_t *csock = NULL; + REQUIRE(VALID_NMSOCK(sock)); REQUIRE(sock->type == isc_nm_tlsdnslistener); + isc_barrier_init(&sock->stoplistening, sock->nchildren); + for (size_t i = 0; i < sock->nchildren; i++) { - isc__netievent_tlsdnsstop_t *ievent = NULL; - isc_nmsocket_t *csock = &sock->children[i]; + csock = &sock->children[i]; REQUIRE(VALID_NMSOCK(csock)); - atomic_store(&csock->active, false); + if ((int)i == isc_nm_tid()) { + /* + * We need to schedule closing the other sockets first + */ + continue; + } - ievent = isc__nm_get_netievent_tlsdnsstop(sock->mgr, csock); - isc__nm_enqueue_ievent(&sock->mgr->workers[csock->tid], - (isc__netievent_t *)ievent); + atomic_store(&csock->active, false); + enqueue_stoplistening(csock); } + + csock = &sock->children[isc_nm_tid()]; + atomic_store(&csock->active, false); + stop_tlsdns_child(csock); + + atomic_store(&sock->closed, true); + isc__nmsocket_prep_destroy(sock); } static void diff --git a/lib/isc/netmgr/tlsstream.c b/lib/isc/netmgr/tlsstream.c index fab2072f22..ec339a738a 100644 --- a/lib/isc/netmgr/tlsstream.c +++ b/lib/isc/netmgr/tlsstream.c @@ -621,7 +621,7 @@ isc_nm_listentls(isc_nm_t *mgr, isc_nmiface_t *iface, tlssock->tlsstream.server_iface = *iface; ISC_LINK_INIT(&tlssock->tlsstream.server_iface.addr, link); tlssock->iface = &tlssock->tlsstream.server_iface; - tlssock->result = ISC_R_DEFAULT; + tlssock->result = ISC_R_UNSET; tlssock->accept_cb = accept_cb; tlssock->accept_cbarg = accept_cbarg; tlssock->extrahandlesize = extrahandlesize; @@ -643,19 +643,12 @@ isc_nm_listentls(isc_nm_t *mgr, isc_nmiface_t *iface, /* wait for listen result */ isc__nmsocket_attach(tlssock->outer, &tsock); - LOCK(&tlssock->outer->lock); - while (tlssock->outer->rchildren != tlssock->outer->nchildren) { - WAIT(&tlssock->outer->cond, &tlssock->outer->lock); - } - result = tlssock->outer->result; tlssock->result = result; atomic_store(&tlssock->active, true); INSIST(tlssock->outer->tlsstream.tlslistener == NULL); isc__nmsocket_attach(tlssock, &tlssock->outer->tlsstream.tlslistener); - BROADCAST(&tlssock->outer->scond); - UNLOCK(&tlssock->outer->lock); isc__nmsocket_detach(&tsock); - INSIST(result != ISC_R_DEFAULT); + INSIST(result != ISC_R_UNSET); if (result == ISC_R_SUCCESS) { atomic_store(&tlssock->listening, true); @@ -891,7 +884,7 @@ isc_nm_tlsconnect(isc_nm_t *mgr, isc_nmiface_t *local, isc_nmiface_t *peer, ISC_LINK_INIT(&nsock->tlsstream.local_iface.addr, link); nsock->iface = &nsock->tlsstream.local_iface; nsock->extrahandlesize = extrahandlesize; - nsock->result = ISC_R_DEFAULT; + nsock->result = ISC_R_UNSET; nsock->connect_cb = cb; nsock->connect_cbarg = cbarg; nsock->connect_timeout = timeout; diff --git a/lib/isc/netmgr/udp.c b/lib/isc/netmgr/udp.c index 221da22663..ca4900c5ea 100644 --- a/lib/isc/netmgr/udp.c +++ b/lib/isc/netmgr/udp.c @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -77,18 +78,44 @@ isc__nm_udp_lb_socket(sa_family_t sa_family) { return (sock); } +static void +start_udp_child(isc_nm_t *mgr, isc_nmiface_t *iface, isc_nmsocket_t *sock, + uv_os_sock_t fd, int tid) { + isc_nmsocket_t *csock; + isc__netievent_udplisten_t *ievent = NULL; + + csock = &sock->children[tid]; + + isc__nmsocket_init(csock, mgr, isc_nm_udpsocket, iface); + csock->parent = sock; + csock->iface = sock->iface; + csock->reading = true; + csock->recv_cb = sock->recv_cb; + csock->recv_cbarg = sock->recv_cbarg; + csock->extrahandlesize = sock->extrahandlesize; + csock->tid = tid; + +#if HAVE_SO_REUSEPORT_LB || defined(WIN32) + UNUSED(fd); + csock->fd = isc__nm_udp_lb_socket(iface->addr.type.sa.sa_family); +#else + csock->fd = dup(fd); +#endif + REQUIRE(csock->fd >= 0); + + ievent = isc__nm_get_netievent_udplisten(mgr, csock); + isc__nm_maybe_enqueue_ievent(&mgr->workers[tid], + (isc__netievent_t *)ievent); +} + isc_result_t isc_nm_listenudp(isc_nm_t *mgr, isc_nmiface_t *iface, isc_nm_recv_cb_t cb, void *cbarg, size_t extrahandlesize, isc_nmsocket_t **sockp) { isc_result_t result = ISC_R_SUCCESS; isc_nmsocket_t *sock = NULL; - sa_family_t sa_family = iface->addr.type.sa.sa_family; size_t children_size = 0; -#if !HAVE_SO_REUSEPORT_LB && !defined(WIN32) - uv_os_sock_t fd = -1; -#endif - REQUIRE(VALID_NM(mgr)); + uv_os_sock_t fd = -1; /* * We are creating mgr->nworkers duplicated sockets, one @@ -97,7 +124,7 @@ isc_nm_listenudp(isc_nm_t *mgr, isc_nmiface_t *iface, isc_nm_recv_cb_t cb, sock = isc_mem_get(mgr->mctx, sizeof(isc_nmsocket_t)); isc__nmsocket_init(sock, mgr, isc_nm_udplistener, iface); - sock->rchildren = 0; + atomic_init(&sock->rchildren, 0); #if defined(WIN32) sock->nchildren = 1; #else @@ -111,37 +138,29 @@ isc_nm_listenudp(isc_nm_t *mgr, isc_nmiface_t *iface, isc_nm_recv_cb_t cb, sock->recv_cb = cb; sock->recv_cbarg = cbarg; sock->extrahandlesize = extrahandlesize; - sock->result = ISC_R_DEFAULT; - sock->tid = isc_random_uniform(sock->nchildren); + sock->result = ISC_R_UNSET; + if (isc__nm_in_netthread()) { + sock->tid = isc_nm_tid(); + } else { + sock->tid = isc_random_uniform(sock->nchildren); + } sock->fd = -1; #if !HAVE_SO_REUSEPORT_LB && !defined(WIN32) - fd = isc__nm_udp_lb_socket(sa_family); + fd = isc__nm_udp_lb_socket(iface->addr.type.sa.sa_family); #endif + isc_barrier_init(&sock->startlistening, sock->nchildren); + for (size_t i = 0; i < sock->nchildren; i++) { - isc__netievent_udplisten_t *ievent = NULL; - isc_nmsocket_t *csock = &sock->children[i]; + if ((int)i == isc_nm_tid()) { + continue; + } + start_udp_child(mgr, iface, sock, fd, i); + } - isc__nmsocket_init(csock, mgr, isc_nm_udpsocket, iface); - csock->parent = sock; - csock->iface = sock->iface; - csock->reading = true; - csock->recv_cb = cb; - csock->recv_cbarg = cbarg; - csock->extrahandlesize = sock->extrahandlesize; - csock->tid = i; - -#if HAVE_SO_REUSEPORT_LB || defined(WIN32) - csock->fd = isc__nm_udp_lb_socket(sa_family); -#else - csock->fd = dup(fd); -#endif - REQUIRE(csock->fd >= 0); - - ievent = isc__nm_get_netievent_udplisten(mgr, csock); - isc__nm_maybe_enqueue_ievent(&mgr->workers[i], - (isc__netievent_t *)ievent); + if (isc__nm_in_netthread()) { + start_udp_child(mgr, iface, sock, fd, isc_nm_tid()); } #if !HAVE_SO_REUSEPORT_LB && !defined(WIN32) @@ -149,21 +168,21 @@ isc_nm_listenudp(isc_nm_t *mgr, isc_nmiface_t *iface, isc_nm_recv_cb_t cb, #endif LOCK(&sock->lock); - while (sock->rchildren != sock->nchildren) { + while (atomic_load(&sock->rchildren) != sock->nchildren) { WAIT(&sock->cond, &sock->lock); } result = sock->result; atomic_store(&sock->active, true); - BROADCAST(&sock->scond); UNLOCK(&sock->lock); - INSIST(result != ISC_R_DEFAULT); + + INSIST(result != ISC_R_UNSET); if (result == ISC_R_SUCCESS) { - REQUIRE(sock->rchildren == sock->nchildren); + REQUIRE(atomic_load(&sock->rchildren) == sock->nchildren); *sockp = sock; } else { atomic_store(&sock->active, false); - isc__nm_udp_stoplistening(sock); + isc_nm_stoplistening(sock); isc_nmsocket_close(&sock); } @@ -181,7 +200,7 @@ isc__nm_async_udplisten(isc__networker_t *worker, isc__netievent_t *ev0) { int r, uv_bind_flags = 0; int uv_init_flags = 0; sa_family_t sa_family; - isc_result_t result = ISC_R_DEFAULT; + isc_result_t result = ISC_R_UNSET; REQUIRE(VALID_NMSOCK(ievent->sock)); REQUIRE(ievent->sock->tid == isc_nm_tid()); @@ -269,16 +288,14 @@ isc__nm_async_udplisten(isc__networker_t *worker, isc__netievent_t *ev0) { done: result = isc__nm_uverr2result(r); - sock->parent->rchildren += 1; - if (sock->parent->result == ISC_R_DEFAULT) { + atomic_fetch_add(&sock->parent->rchildren, 1); + if (sock->parent->result == ISC_R_UNSET) { sock->parent->result = result; } SIGNAL(&sock->parent->cond); - if (!atomic_load(&sock->parent->active)) { - WAIT(&sock->parent->scond, &sock->parent->lock); - } - INSIST(atomic_load(&sock->parent->active)); UNLOCK(&sock->parent->lock); + + isc_barrier_wait(&sock->parent->startlistening); } static void @@ -300,7 +317,14 @@ isc__nm_udp_stoplistening(isc_nmsocket_t *sock) { ISC_UNREACHABLE(); } - enqueue_stoplistening(sock); + if (!isc__nm_in_netthread()) { + enqueue_stoplistening(sock); + } else if (!isc__nm_acquire_interlocked(sock->mgr)) { + enqueue_stoplistening(sock); + } else { + stop_udp_parent(sock); + isc__nm_drop_interlocked(sock->mgr); + } } /* @@ -324,7 +348,12 @@ isc__nm_async_udpstop(isc__networker_t *worker, isc__netievent_t *ev0) { /* * If network manager is paused, re-enqueue the event for later. */ - stop_udp_parent(sock); + if (!isc__nm_acquire_interlocked(sock->mgr)) { + enqueue_stoplistening(sock); + } else { + stop_udp_parent(sock); + isc__nm_drop_interlocked(sock->mgr); + } } /* @@ -590,7 +619,7 @@ static isc_result_t udp_connect_direct(isc_nmsocket_t *sock, isc__nm_uvreq_t *req) { isc__networker_t *worker = NULL; int uv_bind_flags = UV_UDP_REUSEADDR; - isc_result_t result = ISC_R_DEFAULT; + isc_result_t result = ISC_R_UNSET; int tries = 3; int r; @@ -733,7 +762,7 @@ isc_nm_udpconnect(isc_nm_t *mgr, isc_nmiface_t *local, isc_nmiface_t *peer, sock->read_timeout = timeout; sock->extrahandlesize = extrahandlesize; sock->peer = peer->addr; - sock->result = ISC_R_DEFAULT; + sock->result = ISC_R_UNSET; atomic_init(&sock->client, true); req = isc__nm_uvreq_get(mgr, sock); @@ -782,7 +811,7 @@ isc_nm_udpconnect(isc_nm_t *mgr, isc_nmiface_t *local, isc_nmiface_t *peer, (isc__netievent_t *)event); } LOCK(&sock->lock); - while (sock->result == ISC_R_DEFAULT) { + while (sock->result == ISC_R_UNSET) { WAIT(&sock->cond, &sock->lock); } atomic_store(&sock->active, true); @@ -970,8 +999,6 @@ stop_udp_child(isc_nmsocket_t *sock) { REQUIRE(sock->type == isc_nm_udpsocket); REQUIRE(sock->tid == isc_nm_tid()); - bool last_child = false; - if (!atomic_compare_exchange_strong(&sock->closing, &(bool){ false }, true)) { return; @@ -979,33 +1006,41 @@ stop_udp_child(isc_nmsocket_t *sock) { udp_close_direct(sock); - LOCK(&sock->parent->lock); - sock->parent->rchildren -= 1; - last_child = (sock->parent->rchildren == 0); - UNLOCK(&sock->parent->lock); + atomic_fetch_sub(&sock->parent->rchildren, 1); - if (last_child) { - atomic_store(&sock->parent->closed, true); - isc__nmsocket_prep_destroy(sock->parent); - } + isc_barrier_wait(&sock->parent->stoplistening); } static void stop_udp_parent(isc_nmsocket_t *sock) { + isc_nmsocket_t *csock = NULL; + REQUIRE(VALID_NMSOCK(sock)); REQUIRE(sock->type == isc_nm_udplistener); + isc_barrier_init(&sock->stoplistening, sock->nchildren); + for (size_t i = 0; i < sock->nchildren; i++) { - isc__netievent_udpstop_t *ievent = NULL; - isc_nmsocket_t *csock = &sock->children[i]; + csock = &sock->children[i]; REQUIRE(VALID_NMSOCK(csock)); - atomic_store(&csock->active, false); + if ((int)i == isc_nm_tid()) { + /* + * We need to schedule closing the other sockets first + */ + continue; + } - ievent = isc__nm_get_netievent_udpstop(sock->mgr, csock); - isc__nm_enqueue_ievent(&sock->mgr->workers[i], - (isc__netievent_t *)ievent); + atomic_store(&csock->active, false); + enqueue_stoplistening(csock); } + + csock = &sock->children[isc_nm_tid()]; + atomic_store(&csock->active, false); + stop_udp_child(csock); + + atomic_store(&sock->closed, true); + isc__nmsocket_prep_destroy(sock); } static void diff --git a/lib/isc/task.c b/lib/isc/task.c index 5c217ad5b0..49e2c4fcc6 100644 --- a/lib/isc/task.c +++ b/lib/isc/task.c @@ -1014,7 +1014,7 @@ isc__taskmgr_shutdown(isc_taskmgr_t *manager) { REQUIRE(VALID_MANAGER(manager)); - XTHREADTRACE(e "isc_taskmgr_shutdown"); + XTHREADTRACE("isc_taskmgr_shutdown"); /* * Only one non-worker thread may ever call this routine. * If a worker thread wants to initiate shutdown of the diff --git a/lib/isc/win32/libisc.vcxproj.filters.in b/lib/isc/win32/libisc.vcxproj.filters.in index 6c10081745..ce7b6e7edd 100644 --- a/lib/isc/win32/libisc.vcxproj.filters.in +++ b/lib/isc/win32/libisc.vcxproj.filters.in @@ -44,6 +44,9 @@ Library Header Files + + Library Header Files + Library Header Files diff --git a/lib/isc/win32/libisc.vcxproj.in b/lib/isc/win32/libisc.vcxproj.in index 7689222868..6f73664314 100644 --- a/lib/isc/win32/libisc.vcxproj.in +++ b/lib/isc/win32/libisc.vcxproj.in @@ -269,6 +269,7 @@ copy InstallFiles ..\Build\Release\ + diff --git a/util/copyrights b/util/copyrights index ba92b26e62..96f8dd7eec 100644 --- a/util/copyrights +++ b/util/copyrights @@ -1852,6 +1852,7 @@ ./lib/isc/include/isc/atomic.h C 2018,2019,2020,2021 ./lib/isc/include/isc/attributes.h C 2020,2021 ./lib/isc/include/isc/backtrace.h C 2009,2016,2018,2019,2020,2021 +./lib/isc/include/isc/barrier.h C 2021 ./lib/isc/include/isc/base32.h C 2008,2014,2016,2018,2019,2020,2021 ./lib/isc/include/isc/base64.h C 1999,2000,2001,2004,2005,2006,2007,2016,2018,2019,2020,2021 ./lib/isc/include/isc/bind9.h C 2009,2013,2016,2018,2019,2020,2021 From c44423127d2573f7907649512d0a2bc6984a4215 Mon Sep 17 00:00:00 2001 From: Evan Hunt Date: Wed, 5 May 2021 14:54:53 -0700 Subject: [PATCH 10/13] fix shutdown deadlocks - ensure isc_nm_pause() and isc_nm_resume() work the same whether run from inside or outside of the netmgr. - promote 'stop' events to the priority event level so they can run while the netmgr is pausing or paused. - when pausing, drain the priority queue before acquiring an interlock; this prevents a deadlock when another thread is waiting for us to complete a task. - release interlock after pausing, reacquire it when resuming, so that stop events can happen. some incidental changes: - use a function to enqueue pause and resume events (this was part of a different change attempt that didn't work out; I kept it because I thought was more readable). - make mgr->nworkers a signed int to remove some annoying integer casts. --- lib/isc/netmgr/netmgr-int.h | 26 +++++----- lib/isc/netmgr/netmgr.c | 97 +++++++++++++++++++++++-------------- 2 files changed, 74 insertions(+), 49 deletions(-) diff --git a/lib/isc/netmgr/netmgr-int.h b/lib/isc/netmgr/netmgr-int.h index a126a4e403..e532b014cc 100644 --- a/lib/isc/netmgr/netmgr-int.h +++ b/lib/isc/netmgr/netmgr-int.h @@ -249,7 +249,6 @@ typedef enum isc__netievent_type { netievent_udpclose, netievent_udpsend, netievent_udpread, - netievent_udpstop, netievent_udpcancel, netievent_tcpconnect, @@ -258,7 +257,6 @@ typedef enum isc__netievent_type { netievent_tcpstartread, netievent_tcppauseread, netievent_tcpaccept, - netievent_tcpstop, netievent_tcpcancel, netievent_tcpdnsaccept, @@ -267,7 +265,6 @@ typedef enum isc__netievent_type { netievent_tcpdnssend, netievent_tcpdnsread, netievent_tcpdnscancel, - netievent_tcpdnsstop, netievent_tlsclose, netievent_tlssend, @@ -282,12 +279,10 @@ typedef enum isc__netievent_type { netievent_tlsdnssend, netievent_tlsdnsread, netievent_tlsdnscancel, - netievent_tlsdnsstop, netievent_tlsdnscycle, netievent_tlsdnsshutdown, netievent_httpclose, - netievent_httpstop, netievent_httpsend, netievent_shutdown, @@ -301,19 +296,26 @@ typedef enum isc__netievent_type { netievent_task, netievent_privilegedtask, - netievent_prio = 0xff, /* event type values higher than this - * will be treated as high-priority - * events, which can be processed - * while the netmgr is paused. - */ + /* + * event type values higher than this will be treated + * as high-priority events, which can be processed + * while the netmgr is pausing or paused. + */ + netievent_prio = 0xff, + netievent_udplisten, + netievent_udpstop, netievent_tcplisten, + netievent_tcpstop, netievent_tcpdnslisten, + netievent_tcpdnsstop, netievent_tlsdnslisten, + netievent_tlsdnsstop, + netievent_httpstop, + netievent_resume, netievent_detach, netievent_close, - } isc__netievent_type; typedef union { @@ -658,7 +660,7 @@ struct isc_nm { int magic; isc_refcount_t references; isc_mem_t *mctx; - uint32_t nworkers; + int nworkers; isc_mutex_t lock; isc_condition_t wkstatecond; isc_condition_t wkpausecond; diff --git a/lib/isc/netmgr/netmgr.c b/lib/isc/netmgr/netmgr.c index b795dd5aba..05bbae5b61 100644 --- a/lib/isc/netmgr/netmgr.c +++ b/lib/isc/netmgr/netmgr.c @@ -336,7 +336,7 @@ nm_destroy(isc_nm_t **mgr0) { mgr->magic = 0; - for (size_t i = 0; i < mgr->nworkers; i++) { + for (int i = 0; i < mgr->nworkers; i++) { isc__networker_t *worker = &mgr->workers[i]; isc__netievent_t *event = isc__nm_get_netievent_stop(mgr); isc__nm_enqueue_ievent(worker, event); @@ -348,7 +348,7 @@ nm_destroy(isc_nm_t **mgr0) { } UNLOCK(&mgr->lock); - for (size_t i = 0; i < mgr->nworkers; i++) { + for (int i = 0; i < mgr->nworkers; i++) { isc__networker_t *worker = &mgr->workers[i]; isc__netievent_t *ievent = NULL; int r; @@ -414,33 +414,45 @@ nm_destroy(isc_nm_t **mgr0) { #endif /* WIN32 */ } +static void +enqueue_pause(isc__networker_t *worker) { + isc__netievent_pause_t *event = + isc__nm_get_netievent_pause(worker->mgr); + isc__nm_enqueue_ievent(worker, (isc__netievent_t *)event); +} + +static void +isc__nm_async_pause(isc__networker_t *worker, isc__netievent_t *ev0) { + UNUSED(ev0); + REQUIRE(worker->paused == false); + + worker->paused = true; + uv_stop(&worker->loop); +} + void isc_nm_pause(isc_nm_t *mgr) { REQUIRE(VALID_NM(mgr)); - uint_fast32_t pausing = 0; REQUIRE(!atomic_load(&mgr->paused)); isc__nm_acquire_interlocked_force(mgr); - for (size_t i = 0; i < mgr->nworkers; i++) { + for (int i = 0; i < mgr->nworkers; i++) { isc__networker_t *worker = &mgr->workers[i]; - if (i != (size_t)isc_nm_tid()) { - isc__netievent_resume_t *event = - isc__nm_get_netievent_pause(mgr); - pausing++; - isc__nm_enqueue_ievent(worker, - (isc__netievent_t *)event); - } else { + if (i == isc_nm_tid()) { isc__nm_async_pause(worker, NULL); + } else { + enqueue_pause(worker); } } if (isc__nm_in_netthread()) { isc_barrier_wait(&mgr->pausing); + drain_priority_queue(&mgr->workers[isc_nm_tid()]); } LOCK(&mgr->lock); - while (atomic_load(&mgr->workers_paused) != pausing) { + while (atomic_load(&mgr->workers_paused) != mgr->workers_running) { WAIT(&mgr->wkstatecond, &mgr->lock); } UNLOCK(&mgr->lock); @@ -449,24 +461,39 @@ isc_nm_pause(isc_nm_t *mgr) { true)); } +static void +enqueue_resume(isc__networker_t *worker) { + isc__netievent_resume_t *event = + isc__nm_get_netievent_resume(worker->mgr); + isc__nm_enqueue_ievent(worker, (isc__netievent_t *)event); +} + +static void +isc__nm_async_resume(isc__networker_t *worker, isc__netievent_t *ev0) { + UNUSED(ev0); + REQUIRE(worker->paused == true); + + worker->paused = false; +} + void isc_nm_resume(isc_nm_t *mgr) { REQUIRE(VALID_NM(mgr)); REQUIRE(atomic_load(&mgr->paused)); - for (size_t i = 0; i < mgr->nworkers; i++) { + for (int i = 0; i < mgr->nworkers; i++) { isc__networker_t *worker = &mgr->workers[i]; - if (i != (size_t)isc_nm_tid()) { - isc__netievent_resume_t *event = - isc__nm_get_netievent_resume(mgr); - isc__nm_enqueue_ievent(worker, - (isc__netievent_t *)event); - } else { + if (i == isc_nm_tid()) { isc__nm_async_resume(worker, NULL); + } else { + enqueue_resume(worker); } } if (isc__nm_in_netthread()) { + drain_privilege_queue(&mgr->workers[isc_nm_tid()]); + + atomic_fetch_sub(&mgr->workers_paused, 1); isc_barrier_wait(&mgr->resuming); } @@ -512,7 +539,7 @@ isc__netmgr_shutdown(isc_nm_t *mgr) { REQUIRE(VALID_NM(mgr)); atomic_store(&mgr->closing, true); - for (size_t i = 0; i < mgr->nworkers; i++) { + for (int i = 0; i < mgr->nworkers; i++) { isc__netievent_t *event = NULL; event = isc__nm_get_netievent_shutdown(mgr); isc__nm_enqueue_ievent(&mgr->workers[i], event); @@ -742,23 +769,6 @@ isc__nm_async_stop(isc__networker_t *worker, isc__netievent_t *ev0) { uv_close((uv_handle_t *)&worker->async, NULL); } -static void -isc__nm_async_pause(isc__networker_t *worker, isc__netievent_t *ev0) { - UNUSED(ev0); - REQUIRE(worker->paused == false); - - worker->paused = true; - uv_stop(&worker->loop); -} - -static void -isc__nm_async_resume(isc__networker_t *worker, isc__netievent_t *ev0) { - UNUSED(ev0); - REQUIRE(worker->paused == true); - - worker->paused = false; -} - void isc_nm_task_enqueue(isc_nm_t *nm, isc_task_t *task, int threadid) { isc__netievent_t *event = NULL; @@ -2756,16 +2766,25 @@ isc__nm_async_shutdown(isc__networker_t *worker, isc__netievent_t *ev0) { bool isc__nm_acquire_interlocked(isc_nm_t *mgr) { + if (!isc__nm_in_netthread()) { + return (false); + } + LOCK(&mgr->lock); bool success = atomic_compare_exchange_strong( &mgr->interlocked, &(int){ ISC_NETMGR_NON_INTERLOCKED }, isc_nm_tid()); + UNLOCK(&mgr->lock); return (success); } void isc__nm_drop_interlocked(isc_nm_t *mgr) { + if (!isc__nm_in_netthread()) { + return; + } + LOCK(&mgr->lock); int tid = atomic_exchange(&mgr->interlocked, ISC_NETMGR_NON_INTERLOCKED); @@ -2776,6 +2795,10 @@ isc__nm_drop_interlocked(isc_nm_t *mgr) { void isc__nm_acquire_interlocked_force(isc_nm_t *mgr) { + if (!isc__nm_in_netthread()) { + return; + } + LOCK(&mgr->lock); while (!atomic_compare_exchange_strong( &mgr->interlocked, &(int){ ISC_NETMGR_NON_INTERLOCKED }, From 365c6a9851fd6f37cf16129a3d5d624541f2f6a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Sur=C3=BD?= Date: Thu, 6 May 2021 16:11:43 +0200 Subject: [PATCH 11/13] ensure interlocked netmgr events run on worker[0] Network manager events that require interlock (pause, resume, listen) are now always executed in the same worker thread, mgr->workers[0], to prevent races. "stoplistening" events no longer require interlock. --- bin/named/server.c | 2 +- lib/isc/netmgr/http.c | 3 ++- lib/isc/netmgr/netmgr.c | 11 ++++++++++- lib/isc/netmgr/tcp.c | 36 +++++++++++++---------------------- lib/isc/netmgr/tcpdns.c | 38 ++++++++++++------------------------- lib/isc/netmgr/tlsdns.c | 40 +++++++++++++-------------------------- lib/isc/netmgr/udp.c | 39 +++++++++++++------------------------- lib/isc/task.c | 2 ++ lib/isc/tests/isctest.c | 2 +- lib/isc/tests/task_test.c | 12 ++++++++---- lib/ns/tests/nstest.c | 2 +- 11 files changed, 76 insertions(+), 111 deletions(-) diff --git a/bin/named/server.c b/bin/named/server.c index ea343340ad..d411647c44 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -10201,7 +10201,7 @@ named_server_create(isc_mem_t *mctx, named_server_t **serverp) { * startup and shutdown of the server, as well as all exclusive * tasks. */ - CHECKFATAL(isc_task_create(named_g_taskmgr, 0, &server->task), + CHECKFATAL(isc_task_create_bound(named_g_taskmgr, 0, &server->task, 0), "creating server task"); isc_task_setname(server->task, "server", server); isc_taskmgr_setexcltask(named_g_taskmgr, server->task); diff --git a/lib/isc/netmgr/http.c b/lib/isc/netmgr/http.c index e64a018e5d..b76c0b263b 100644 --- a/lib/isc/netmgr/http.c +++ b/lib/isc/netmgr/http.c @@ -2171,7 +2171,7 @@ isc_nm_listenhttp(isc_nm_t *mgr, isc_nmiface_t *iface, int backlog, sock->nchildren = sock->outer->nchildren; sock->result = ISC_R_UNSET; - sock->tid = isc_random_uniform(sock->nchildren); + sock->tid = 0; sock->fd = (uv_os_sock_t)-1; atomic_store(&sock->listening, true); @@ -2254,6 +2254,7 @@ isc__nm_http_stoplistening(isc_nmsocket_t *sock) { isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid], (isc__netievent_t *)ievent); } else { + REQUIRE(isc_nm_tid() == sock->tid); isc__netievent_httpstop_t ievent = { .sock = sock }; isc__nm_async_httpstop(NULL, (isc__netievent_t *)&ievent); } diff --git a/lib/isc/netmgr/netmgr.c b/lib/isc/netmgr/netmgr.c index 05bbae5b61..2baa4419f9 100644 --- a/lib/isc/netmgr/netmgr.c +++ b/lib/isc/netmgr/netmgr.c @@ -437,6 +437,10 @@ isc_nm_pause(isc_nm_t *mgr) { isc__nm_acquire_interlocked_force(mgr); + if (isc__nm_in_netthread()) { + REQUIRE(isc_nm_tid() == 0); + } + for (int i = 0; i < mgr->nworkers; i++) { isc__networker_t *worker = &mgr->workers[i]; if (i == isc_nm_tid()) { @@ -447,8 +451,8 @@ isc_nm_pause(isc_nm_t *mgr) { } if (isc__nm_in_netthread()) { + atomic_fetch_add(&mgr->workers_paused, 1); isc_barrier_wait(&mgr->pausing); - drain_priority_queue(&mgr->workers[isc_nm_tid()]); } LOCK(&mgr->lock); @@ -481,6 +485,11 @@ isc_nm_resume(isc_nm_t *mgr) { REQUIRE(VALID_NM(mgr)); REQUIRE(atomic_load(&mgr->paused)); + if (isc__nm_in_netthread()) { + REQUIRE(isc_nm_tid() == 0); + drain_priority_queue(&mgr->workers[isc_nm_tid()]); + } + for (int i = 0; i < mgr->nworkers; i++) { isc__networker_t *worker = &mgr->workers[i]; if (i == isc_nm_tid()) { diff --git a/lib/isc/netmgr/tcp.c b/lib/isc/netmgr/tcp.c index 052030bf79..c7030ecccc 100644 --- a/lib/isc/netmgr/tcp.c +++ b/lib/isc/netmgr/tcp.c @@ -409,6 +409,14 @@ start_tcp_child(isc_nm_t *mgr, isc_nmiface_t *iface, isc_nmsocket_t *sock, (isc__netievent_t *)ievent); } +static void +enqueue_stoplistening(isc_nmsocket_t *sock) { + isc__netievent_tcpstop_t *ievent = + isc__nm_get_netievent_tcpstop(sock->mgr, sock); + isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid], + (isc__netievent_t *)ievent); +} + isc_result_t isc_nm_listentcp(isc_nm_t *mgr, isc_nmiface_t *iface, isc_nm_accept_cb_t accept_cb, void *accept_cbarg, @@ -442,11 +450,7 @@ isc_nm_listentcp(isc_nm_t *mgr, isc_nmiface_t *iface, sock->backlog = backlog; sock->pquota = quota; - if (isc__nm_in_netthread()) { - sock->tid = isc_nm_tid(); - } else { - sock->tid = isc_random_uniform(sock->nchildren); - } + sock->tid = 0; sock->fd = -1; #if !HAVE_SO_REUSEPORT_LB && !defined(WIN32) @@ -485,7 +489,7 @@ isc_nm_listentcp(isc_nm_t *mgr, isc_nmiface_t *iface, *sockp = sock; } else { atomic_store(&sock->active, false); - isc_nm_stoplistening(sock); + enqueue_stoplistening(sock); isc_nmsocket_close(&sock); } @@ -642,14 +646,6 @@ done: } } -static void -enqueue_stoplistening(isc_nmsocket_t *sock) { - isc__netievent_tcpstop_t *ievent = - isc__nm_get_netievent_tcpstop(sock->mgr, sock); - isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid], - (isc__netievent_t *)ievent); -} - void isc__nm_tcp_stoplistening(isc_nmsocket_t *sock) { REQUIRE(VALID_NMSOCK(sock)); @@ -663,11 +659,8 @@ isc__nm_tcp_stoplistening(isc_nmsocket_t *sock) { if (!isc__nm_in_netthread()) { enqueue_stoplistening(sock); - } else if (!isc__nm_acquire_interlocked(sock->mgr)) { - enqueue_stoplistening(sock); } else { stop_tcp_parent(sock); - isc__nm_drop_interlocked(sock->mgr); } } @@ -686,12 +679,7 @@ isc__nm_async_tcpstop(isc__networker_t *worker, isc__netievent_t *ev0) { return; } - if (!isc__nm_acquire_interlocked(sock->mgr)) { - enqueue_stoplistening(sock); - } else { - stop_tcp_parent(sock); - isc__nm_drop_interlocked(sock->mgr); - } + stop_tcp_parent(sock); } void @@ -1248,7 +1236,9 @@ stop_tcp_child(isc_nmsocket_t *sock) { static void stop_tcp_parent(isc_nmsocket_t *sock) { isc_nmsocket_t *csock = NULL; + REQUIRE(VALID_NMSOCK(sock)); + REQUIRE(sock->tid == isc_nm_tid()); REQUIRE(sock->type == isc_nm_tcplistener); isc_barrier_init(&sock->stoplistening, sock->nchildren); diff --git a/lib/isc/netmgr/tcpdns.c b/lib/isc/netmgr/tcpdns.c index 142d8b30fa..0d9bd85f99 100644 --- a/lib/isc/netmgr/tcpdns.c +++ b/lib/isc/netmgr/tcpdns.c @@ -343,6 +343,14 @@ isc__nm_tcpdns_lb_socket(sa_family_t sa_family) { return (sock); } +static void +enqueue_stoplistening(isc_nmsocket_t *sock) { + isc__netievent_tcpdnsstop_t *ievent = + isc__nm_get_netievent_tcpdnsstop(sock->mgr, sock); + isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid], + (isc__netievent_t *)ievent); +} + static void start_tcpdns_child(isc_nm_t *mgr, isc_nmiface_t *iface, isc_nmsocket_t *sock, uv_os_sock_t fd, int tid) { @@ -412,11 +420,7 @@ isc_nm_listentcpdns(isc_nm_t *mgr, isc_nmiface_t *iface, sock->backlog = backlog; sock->pquota = quota; - if (isc__nm_in_netthread()) { - sock->tid = isc_nm_tid(); - } else { - sock->tid = isc_random_uniform(sock->nchildren); - } + sock->tid = 0; sock->fd = -1; #if !HAVE_SO_REUSEPORT_LB && !defined(WIN32) @@ -455,7 +459,7 @@ isc_nm_listentcpdns(isc_nm_t *mgr, isc_nmiface_t *iface, *sockp = sock; } else { atomic_store(&sock->active, false); - isc_nm_stoplistening(sock); + enqueue_stoplistening(sock); isc_nmsocket_close(&sock); } @@ -612,14 +616,6 @@ done: } } -static void -enqueue_stoplistening(isc_nmsocket_t *sock) { - isc__netievent_tcpdnsstop_t *ievent = - isc__nm_get_netievent_tcpdnsstop(sock->mgr, sock); - isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid], - (isc__netievent_t *)ievent); -} - void isc__nm_tcpdns_stoplistening(isc_nmsocket_t *sock) { REQUIRE(VALID_NMSOCK(sock)); @@ -633,11 +629,8 @@ isc__nm_tcpdns_stoplistening(isc_nmsocket_t *sock) { if (!isc__nm_in_netthread()) { enqueue_stoplistening(sock); - } else if (!isc__nm_acquire_interlocked(sock->mgr)) { - enqueue_stoplistening(sock); } else { stop_tcpdns_parent(sock); - isc__nm_drop_interlocked(sock->mgr); } } @@ -657,15 +650,7 @@ isc__nm_async_tcpdnsstop(isc__networker_t *worker, isc__netievent_t *ev0) { return; } - /* - * If network manager is paused, re-enqueue the event for later. - */ - if (!isc__nm_acquire_interlocked(sock->mgr)) { - enqueue_stoplistening(sock); - } else { - stop_tcpdns_parent(sock); - isc__nm_drop_interlocked(sock->mgr); - } + stop_tcpdns_parent(sock); } void @@ -1283,6 +1268,7 @@ stop_tcpdns_parent(isc_nmsocket_t *sock) { isc_nmsocket_t *csock = NULL; REQUIRE(VALID_NMSOCK(sock)); + REQUIRE(sock->tid == isc_nm_tid()); REQUIRE(sock->type == isc_nm_tcpdnslistener); isc_barrier_init(&sock->stoplistening, sock->nchildren); diff --git a/lib/isc/netmgr/tlsdns.c b/lib/isc/netmgr/tlsdns.c index 3f68c7f513..212d81539c 100644 --- a/lib/isc/netmgr/tlsdns.c +++ b/lib/isc/netmgr/tlsdns.c @@ -445,6 +445,14 @@ start_tlsdns_child(isc_nm_t *mgr, isc_nmiface_t *iface, isc_nmsocket_t *sock, (isc__netievent_t *)ievent); } +static void +enqueue_stoplistening(isc_nmsocket_t *sock) { + isc__netievent_tlsdnsstop_t *ievent = + isc__nm_get_netievent_tlsdnsstop(sock->mgr, sock); + isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid], + (isc__netievent_t *)ievent); +} + isc_result_t isc_nm_listentlsdns(isc_nm_t *mgr, isc_nmiface_t *iface, isc_nm_recv_cb_t recv_cb, void *recv_cbarg, @@ -480,13 +488,9 @@ isc_nm_listentlsdns(isc_nm_t *mgr, isc_nmiface_t *iface, sock->backlog = backlog; sock->pquota = quota; - if (isc__nm_in_netthread()) { - sock->tid = isc_nm_tid(); - } else { - sock->tid = isc_random_uniform(sock->nchildren); - } - sock->tls.ctx = sslctx; + + sock->tid = 0; sock->fd = -1; #if !HAVE_SO_REUSEPORT_LB && !defined(WIN32) @@ -525,7 +529,7 @@ isc_nm_listentlsdns(isc_nm_t *mgr, isc_nmiface_t *iface, *sockp = sock; } else { atomic_store(&sock->active, false); - isc_nm_stoplistening(sock); + enqueue_stoplistening(sock); isc_nmsocket_close(&sock); } @@ -683,14 +687,6 @@ done: } } -static void -enqueue_stoplistening(isc_nmsocket_t *sock) { - isc__netievent_tlsdnsstop_t *ievent = - isc__nm_get_netievent_tlsdnsstop(sock->mgr, sock); - isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid], - (isc__netievent_t *)ievent); -} - void isc__nm_tlsdns_stoplistening(isc_nmsocket_t *sock) { REQUIRE(VALID_NMSOCK(sock)); @@ -704,11 +700,8 @@ isc__nm_tlsdns_stoplistening(isc_nmsocket_t *sock) { if (!isc__nm_in_netthread()) { enqueue_stoplistening(sock); - } else if (!isc__nm_acquire_interlocked(sock->mgr)) { - enqueue_stoplistening(sock); } else { stop_tlsdns_parent(sock); - isc__nm_drop_interlocked(sock->mgr); } } @@ -803,15 +796,7 @@ isc__nm_async_tlsdnsstop(isc__networker_t *worker, isc__netievent_t *ev0) { return; } - /* - * If network manager is paused, re-enqueue the event for later. - */ - if (!isc__nm_acquire_interlocked(sock->mgr)) { - enqueue_stoplistening(sock); - } else { - stop_tlsdns_parent(sock); - isc__nm_drop_interlocked(sock->mgr); - } + stop_tlsdns_parent(sock); } void @@ -1831,6 +1816,7 @@ stop_tlsdns_parent(isc_nmsocket_t *sock) { isc_nmsocket_t *csock = NULL; REQUIRE(VALID_NMSOCK(sock)); + REQUIRE(sock->tid == isc_nm_tid()); REQUIRE(sock->type == isc_nm_tlsdnslistener); isc_barrier_init(&sock->stoplistening, sock->nchildren); diff --git a/lib/isc/netmgr/udp.c b/lib/isc/netmgr/udp.c index ca4900c5ea..ba61418ce0 100644 --- a/lib/isc/netmgr/udp.c +++ b/lib/isc/netmgr/udp.c @@ -108,6 +108,14 @@ start_udp_child(isc_nm_t *mgr, isc_nmiface_t *iface, isc_nmsocket_t *sock, (isc__netievent_t *)ievent); } +static void +enqueue_stoplistening(isc_nmsocket_t *sock) { + isc__netievent_udpstop_t *ievent = + isc__nm_get_netievent_udpstop(sock->mgr, sock); + isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid], + (isc__netievent_t *)ievent); +} + isc_result_t isc_nm_listenudp(isc_nm_t *mgr, isc_nmiface_t *iface, isc_nm_recv_cb_t cb, void *cbarg, size_t extrahandlesize, isc_nmsocket_t **sockp) { @@ -139,11 +147,8 @@ isc_nm_listenudp(isc_nm_t *mgr, isc_nmiface_t *iface, isc_nm_recv_cb_t cb, sock->recv_cbarg = cbarg; sock->extrahandlesize = extrahandlesize; sock->result = ISC_R_UNSET; - if (isc__nm_in_netthread()) { - sock->tid = isc_nm_tid(); - } else { - sock->tid = isc_random_uniform(sock->nchildren); - } + + sock->tid = 0; sock->fd = -1; #if !HAVE_SO_REUSEPORT_LB && !defined(WIN32) @@ -182,7 +187,7 @@ isc_nm_listenudp(isc_nm_t *mgr, isc_nmiface_t *iface, isc_nm_recv_cb_t cb, *sockp = sock; } else { atomic_store(&sock->active, false); - isc_nm_stoplistening(sock); + enqueue_stoplistening(sock); isc_nmsocket_close(&sock); } @@ -298,14 +303,6 @@ done: isc_barrier_wait(&sock->parent->startlistening); } -static void -enqueue_stoplistening(isc_nmsocket_t *sock) { - isc__netievent_udpstop_t *ievent = - isc__nm_get_netievent_udpstop(sock->mgr, sock); - isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid], - (isc__netievent_t *)ievent); -} - void isc__nm_udp_stoplistening(isc_nmsocket_t *sock) { REQUIRE(VALID_NMSOCK(sock)); @@ -319,11 +316,8 @@ isc__nm_udp_stoplistening(isc_nmsocket_t *sock) { if (!isc__nm_in_netthread()) { enqueue_stoplistening(sock); - } else if (!isc__nm_acquire_interlocked(sock->mgr)) { - enqueue_stoplistening(sock); } else { stop_udp_parent(sock); - isc__nm_drop_interlocked(sock->mgr); } } @@ -345,15 +339,7 @@ isc__nm_async_udpstop(isc__networker_t *worker, isc__netievent_t *ev0) { return; } - /* - * If network manager is paused, re-enqueue the event for later. - */ - if (!isc__nm_acquire_interlocked(sock->mgr)) { - enqueue_stoplistening(sock); - } else { - stop_udp_parent(sock); - isc__nm_drop_interlocked(sock->mgr); - } + stop_udp_parent(sock); } /* @@ -1016,6 +1002,7 @@ stop_udp_parent(isc_nmsocket_t *sock) { isc_nmsocket_t *csock = NULL; REQUIRE(VALID_NMSOCK(sock)); + REQUIRE(sock->tid == isc_nm_tid()); REQUIRE(sock->type == isc_nm_udplistener); isc_barrier_init(&sock->stoplistening, sock->nchildren); diff --git a/lib/isc/task.c b/lib/isc/task.c index 49e2c4fcc6..9849faeafc 100644 --- a/lib/isc/task.c +++ b/lib/isc/task.c @@ -1095,6 +1095,8 @@ void isc_taskmgr_setexcltask(isc_taskmgr_t *mgr, isc_task_t *task) { REQUIRE(VALID_MANAGER(mgr)); REQUIRE(VALID_TASK(task)); + REQUIRE(task->threadid == 0); + LOCK(&mgr->excl_lock); if (mgr->excl != NULL) { isc_task_detach(&mgr->excl); diff --git a/lib/isc/tests/isctest.c b/lib/isc/tests/isctest.c index 89ef18c5c7..6975501ef7 100644 --- a/lib/isc/tests/isctest.c +++ b/lib/isc/tests/isctest.c @@ -83,7 +83,7 @@ create_managers(unsigned int workers) { isc_managers_create(test_mctx, workers, 0, 0, &netmgr, &taskmgr, &timermgr, &socketmgr); - CHECK(isc_task_create(taskmgr, 0, &maintask)); + CHECK(isc_task_create_bound(taskmgr, 0, &maintask, 0)); isc_taskmgr_setexcltask(taskmgr, maintask); return (ISC_R_SUCCESS); diff --git a/lib/isc/tests/task_test.c b/lib/isc/tests/task_test.c index 4edcfc902f..b47770bfff 100644 --- a/lib/isc/tests/task_test.c +++ b/lib/isc/tests/task_test.c @@ -708,12 +708,16 @@ task_exclusive(void **state) { tasks[i] = NULL; - result = isc_task_create(taskmgr, 0, &tasks[i]); - assert_int_equal(result, ISC_R_SUCCESS); - - /* task chosen from the middle of the range */ if (i == 6) { + /* task chosen from the middle of the range */ + result = isc_task_create_bound(taskmgr, 0, &tasks[i], + 0); + assert_int_equal(result, ISC_R_SUCCESS); + isc_taskmgr_setexcltask(taskmgr, tasks[6]); + } else { + result = isc_task_create(taskmgr, 0, &tasks[i]); + assert_int_equal(result, ISC_R_SUCCESS); } v = isc_mem_get(test_mctx, sizeof *v); diff --git a/lib/ns/tests/nstest.c b/lib/ns/tests/nstest.c index b291335afa..6cf7ac91a9 100644 --- a/lib/ns/tests/nstest.c +++ b/lib/ns/tests/nstest.c @@ -227,7 +227,7 @@ create_managers(void) { isc_managers_create(mctx, ncpus, 0, 0, &netmgr, &taskmgr, &timermgr, &socketmgr); - CHECK(isc_task_create(taskmgr, 0, &maintask)); + CHECK(isc_task_create_bound(taskmgr, 0, &maintask, 0)); isc_taskmgr_setexcltask(taskmgr, maintask); CHECK(isc_task_onshutdown(maintask, shutdown_managers, NULL)); From 0133096c88902493323dac2b4669178ec2467a42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Sur=C3=BD?= Date: Fri, 7 May 2021 15:34:22 +0200 Subject: [PATCH 12/13] improvements to socket_test - be more strict, but patient, waiting for event completion. - use an atomic pointer for the socket to silence TSAN warnings. --- lib/isc/tests/socket_test.c | 32 +++++++++++--------------------- 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/lib/isc/tests/socket_test.c b/lib/isc/tests/socket_test.c index 003571848e..28a436b7d2 100644 --- a/lib/isc/tests/socket_test.c +++ b/lib/isc/tests/socket_test.c @@ -12,6 +12,7 @@ /*! \file */ #if HAVE_CMOCKA +#include #include /* IWYU pragma: keep */ #include #include @@ -79,14 +80,14 @@ _teardown(void **state) { typedef struct { atomic_bool done; + atomic_uintptr_t socket; isc_result_t result; - isc_socket_t *socket; } completion_t; static void completion_init(completion_t *completion) { atomic_init(&completion->done, false); - completion->socket = NULL; + atomic_init(&completion->socket, (uintptr_t)NULL); } static void @@ -99,7 +100,7 @@ accept_done(isc_task_t *task, isc_event_t *event) { completion->result = nevent->result; atomic_store(&completion->done, true); if (completion->result == ISC_R_SUCCESS) { - completion->socket = nevent->newsocket; + atomic_store(&completion->socket, (uintptr_t)nevent->newsocket); } isc_event_free(&event); @@ -136,35 +137,24 @@ event_done(isc_task_t *task, isc_event_t *event) { isc_event_free(&event); } -static isc_result_t +static void waitfor(completion_t *completion) { int i = 0; while (!atomic_load(&completion->done) && i++ < 5000) { - isc_test_nap(1000); + isc_test_nap(10000); } - if (atomic_load(&completion->done)) { - return (ISC_R_SUCCESS); - } - return (ISC_R_FAILURE); + assert_true(atomic_load(&completion->done)); } static void -waitbody(void) { - isc_test_nap(1000); -} - -static isc_result_t waitfor2(completion_t *c1, completion_t *c2) { int i = 0; while (!(atomic_load(&c1->done) && atomic_load(&c2->done)) && i++ < 5000) { - waitbody(); + isc_test_nap(10000); } - if (atomic_load(&c1->done) && atomic_load(&c2->done)) { - return (ISC_R_SUCCESS); - } - return (ISC_R_FAILURE); + assert_true(atomic_load(&c1->done) && atomic_load(&c2->done)); } /* @@ -525,7 +515,7 @@ tcp_dscp_v4_test(void **state) { assert_int_equal(completion.result, ISC_R_SUCCESS); assert_true(atomic_load(&completion2.done)); assert_int_equal(completion2.result, ISC_R_SUCCESS); - s3 = completion2.socket; + s3 = (isc_socket_t *)atomic_load(&completion2.socket); isc_socket_dscp(s2, 056); /* EF */ @@ -613,7 +603,7 @@ tcp_dscp_v6_test(void **state) { assert_int_equal(completion.result, ISC_R_SUCCESS); assert_true(atomic_load(&completion2.done)); assert_int_equal(completion2.result, ISC_R_SUCCESS); - s3 = completion2.socket; + s3 = (isc_socket_t *)atomic_load(&completion2.socket); isc_socket_dscp(s2, 056); /* EF */ From 19431b1c831dfebda3d409c6c5354b852cb7fdf2 Mon Sep 17 00:00:00 2001 From: Evan Hunt Date: Fri, 7 May 2021 11:32:21 -0700 Subject: [PATCH 13/13] CHANGES --- CHANGES | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/CHANGES b/CHANGES index fee79f4487..962de85927 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,17 @@ +5638. [bug] Improvements related to network manager/task manager + integration: + - added isc_managers_create() and _destroy() functions + to handle setup and teardown of netmgr, taskmgr, + timermgr and socketmgr since these require a + precise order of operations now. + - event queue processing is now quantized to prevent + infinite looping. + - the netmgr can now be paused from within a netmgr + thread. + - fixed deadlocks due to conflict between netmgr + pause/resume and listen/stoplistening operations. + [GL #2654] + 5637. [placeholder] 5636. [bug] Check that zone files for 'dnssec-policy' zones are