"Fossies" - the Fresh Open Source Software Archive

Member "bind-9.17.5/lib/isc/netmgr/netmgr.c" (4 Sep 2020, 37544 Bytes) of package /linux/misc/dns/bind9/9.17.5/bind-9.17.5.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "netmgr.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 9.17.3_vs_9.17.4.

    1 /*
    2  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
    3  *
    4  * This Source Code Form is subject to the terms of the Mozilla Public
    5  * License, v. 2.0. If a copy of the MPL was not distributed with this
    6  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
    7  *
    8  * See the COPYRIGHT file distributed with this work for additional
    9  * information regarding copyright ownership.
   10  */
   11 
   12 #include <inttypes.h>
   13 #include <unistd.h>
   14 #include <uv.h>
   15 
   16 #include <isc/atomic.h>
   17 #include <isc/buffer.h>
   18 #include <isc/condition.h>
   19 #include <isc/magic.h>
   20 #include <isc/mem.h>
   21 #include <isc/netmgr.h>
   22 #include <isc/print.h>
   23 #include <isc/quota.h>
   24 #include <isc/random.h>
   25 #include <isc/refcount.h>
   26 #include <isc/region.h>
   27 #include <isc/result.h>
   28 #include <isc/sockaddr.h>
   29 #include <isc/stats.h>
   30 #include <isc/thread.h>
   31 #include <isc/util.h>
   32 
   33 #include "netmgr-int.h"
   34 #include "uv-compat.h"
   35 
   36 /*%
   37  * How many isc_nmhandles and isc_nm_uvreqs will we be
   38  * caching for reuse in a socket.
   39  */
   40 #define ISC_NM_HANDLES_STACK_SIZE 600
   41 #define ISC_NM_REQS_STACK_SIZE    600
   42 
   43 /*%
   44  * Shortcut index arrays to get access to statistics counters.
   45  */
   46 
   47 static const isc_statscounter_t udp4statsindex[] = {
   48     isc_sockstatscounter_udp4open,
   49     isc_sockstatscounter_udp4openfail,
   50     isc_sockstatscounter_udp4close,
   51     isc_sockstatscounter_udp4bindfail,
   52     isc_sockstatscounter_udp4connectfail,
   53     isc_sockstatscounter_udp4connect,
   54     -1,
   55     -1,
   56     isc_sockstatscounter_udp4sendfail,
   57     isc_sockstatscounter_udp4recvfail,
   58     isc_sockstatscounter_udp4active
   59 };
   60 
   61 static const isc_statscounter_t udp6statsindex[] = {
   62     isc_sockstatscounter_udp6open,
   63     isc_sockstatscounter_udp6openfail,
   64     isc_sockstatscounter_udp6close,
   65     isc_sockstatscounter_udp6bindfail,
   66     isc_sockstatscounter_udp6connectfail,
   67     isc_sockstatscounter_udp6connect,
   68     -1,
   69     -1,
   70     isc_sockstatscounter_udp6sendfail,
   71     isc_sockstatscounter_udp6recvfail,
   72     isc_sockstatscounter_udp6active
   73 };
   74 
   75 static const isc_statscounter_t tcp4statsindex[] = {
   76     isc_sockstatscounter_tcp4open,        isc_sockstatscounter_tcp4openfail,
   77     isc_sockstatscounter_tcp4close,       isc_sockstatscounter_tcp4bindfail,
   78     isc_sockstatscounter_tcp4connectfail, isc_sockstatscounter_tcp4connect,
   79     isc_sockstatscounter_tcp4acceptfail,  isc_sockstatscounter_tcp4accept,
   80     isc_sockstatscounter_tcp4sendfail,    isc_sockstatscounter_tcp4recvfail,
   81     isc_sockstatscounter_tcp4active
   82 };
   83 
   84 static const isc_statscounter_t tcp6statsindex[] = {
   85     isc_sockstatscounter_tcp6open,        isc_sockstatscounter_tcp6openfail,
   86     isc_sockstatscounter_tcp6close,       isc_sockstatscounter_tcp6bindfail,
   87     isc_sockstatscounter_tcp6connectfail, isc_sockstatscounter_tcp6connect,
   88     isc_sockstatscounter_tcp6acceptfail,  isc_sockstatscounter_tcp6accept,
   89     isc_sockstatscounter_tcp6sendfail,    isc_sockstatscounter_tcp6recvfail,
   90     isc_sockstatscounter_tcp6active
   91 };
   92 
   93 #if 0
   94 /* XXX: not currently used */
   95 static const isc_statscounter_t unixstatsindex[] = {
   96     isc_sockstatscounter_unixopen,
   97     isc_sockstatscounter_unixopenfail,
   98     isc_sockstatscounter_unixclose,
   99     isc_sockstatscounter_unixbindfail,
  100     isc_sockstatscounter_unixconnectfail,
  101     isc_sockstatscounter_unixconnect,
  102     isc_sockstatscounter_unixacceptfail,
  103     isc_sockstatscounter_unixaccept,
  104     isc_sockstatscounter_unixsendfail,
  105     isc_sockstatscounter_unixrecvfail,
  106     isc_sockstatscounter_unixactive
  107 };
  108 #endif /* if 0 */
  109 
  110 /*
  111  * libuv is not thread safe, but has mechanisms to pass messages
  112  * between threads. Each socket is owned by a thread. For UDP
  113  * sockets we have a set of sockets for each interface and we can
  114  * choose a sibling and send the message directly. For TCP, or if
  115  * we're calling from a non-networking thread, we need to pass the
  116  * request using async_cb.
  117  */
  118 
  119 static thread_local int isc__nm_tid_v = ISC_NETMGR_TID_UNKNOWN;
  120 
  121 static void
  122 nmsocket_maybe_destroy(isc_nmsocket_t *sock);
  123 static void
  124 nmhandle_free(isc_nmsocket_t *sock, isc_nmhandle_t *handle);
  125 static isc_threadresult_t
  126 nm_thread(isc_threadarg_t worker0);
  127 static void
  128 async_cb(uv_async_t *handle);
  129 static void
  130 process_queue(isc__networker_t *worker, isc_queue_t *queue);
  131 
  132 int
  133 isc_nm_tid(void) {
  134     return (isc__nm_tid_v);
  135 }
  136 
  137 bool
  138 isc__nm_in_netthread(void) {
  139     return (isc__nm_tid_v >= 0);
  140 }
  141 
  142 isc_nm_t *
  143 isc_nm_start(isc_mem_t *mctx, uint32_t workers) {
  144     isc_nm_t *mgr = NULL;
  145     char name[32];
  146 
  147     mgr = isc_mem_get(mctx, sizeof(*mgr));
  148     *mgr = (isc_nm_t){ .nworkers = workers };
  149 
  150     isc_mem_attach(mctx, &mgr->mctx);
  151     isc_mutex_init(&mgr->lock);
  152     isc_condition_init(&mgr->wkstatecond);
  153     isc_refcount_init(&mgr->references, 1);
  154     atomic_init(&mgr->workers_running, 0);
  155     atomic_init(&mgr->workers_paused, 0);
  156     atomic_init(&mgr->maxudp, 0);
  157     atomic_init(&mgr->paused, false);
  158     atomic_init(&mgr->interlocked, false);
  159 
  160     /*
  161      * Default TCP timeout values.
  162      * May be updated by isc_nm_tcptimeouts().
  163      */
  164     mgr->init = 30000;
  165     mgr->idle = 30000;
  166     mgr->keepalive = 30000;
  167     mgr->advertised = 30000;
  168 
  169     isc_mutex_init(&mgr->reqlock);
  170     isc_mempool_create(mgr->mctx, sizeof(isc__nm_uvreq_t), &mgr->reqpool);
  171     isc_mempool_setname(mgr->reqpool, "nm_reqpool");
  172     isc_mempool_setfreemax(mgr->reqpool, 4096);
  173     isc_mempool_associatelock(mgr->reqpool, &mgr->reqlock);
  174     isc_mempool_setfillcount(mgr->reqpool, 32);
  175 
  176     isc_mutex_init(&mgr->evlock);
  177     isc_mempool_create(mgr->mctx, sizeof(isc__netievent_storage_t),
  178                &mgr->evpool);
  179     isc_mempool_setname(mgr->evpool, "nm_evpool");
  180     isc_mempool_setfreemax(mgr->evpool, 4096);
  181     isc_mempool_associatelock(mgr->evpool, &mgr->evlock);
  182     isc_mempool_setfillcount(mgr->evpool, 32);
  183 
  184     mgr->workers = isc_mem_get(mctx, workers * sizeof(isc__networker_t));
  185     for (size_t i = 0; i < workers; i++) {
  186         int r;
  187         isc__networker_t *worker = &mgr->workers[i];
  188         *worker = (isc__networker_t){
  189             .mgr = mgr,
  190             .id = i,
  191         };
  192 
  193         r = uv_loop_init(&worker->loop);
  194         RUNTIME_CHECK(r == 0);
  195 
  196         worker->loop.data = &mgr->workers[i];
  197 
  198         r = uv_async_init(&worker->loop, &worker->async, async_cb);
  199         RUNTIME_CHECK(r == 0);
  200 
  201         isc_mutex_init(&worker->lock);
  202         isc_condition_init(&worker->cond);
  203 
  204         worker->ievents = isc_queue_new(mgr->mctx, 128);
  205         worker->ievents_prio = isc_queue_new(mgr->mctx, 128);
  206         worker->recvbuf = isc_mem_get(mctx, ISC_NETMGR_RECVBUF_SIZE);
  207 
  208         /*
  209          * We need to do this here and not in nm_thread to avoid a
  210          * race - we could exit isc_nm_start, launch nm_destroy,
  211          * and nm_thread would still not be up.
  212          */
  213         atomic_fetch_add_explicit(&mgr->workers_running, 1,
  214                       memory_order_relaxed);
  215         isc_thread_create(nm_thread, &mgr->workers[i], &worker->thread);
  216 
  217         snprintf(name, sizeof(name), "isc-net-%04zu", i);
  218         isc_thread_setname(worker->thread, name);
  219     }
  220 
  221     mgr->magic = NM_MAGIC;
  222     return (mgr);
  223 }
  224 
  225 /*
  226  * Free the resources of the network manager.
  227  */
  228 static void
  229 nm_destroy(isc_nm_t **mgr0) {
  230     REQUIRE(VALID_NM(*mgr0));
  231     REQUIRE(!isc__nm_in_netthread());
  232 
  233     isc_nm_t *mgr = *mgr0;
  234     *mgr0 = NULL;
  235 
  236     isc_refcount_destroy(&mgr->references);
  237 
  238     mgr->magic = 0;
  239 
  240     for (size_t i = 0; i < mgr->nworkers; i++) {
  241         isc__netievent_t *event = NULL;
  242 
  243         LOCK(&mgr->workers[i].lock);
  244         mgr->workers[i].finished = true;
  245         UNLOCK(&mgr->workers[i].lock);
  246         event = isc__nm_get_ievent(mgr, netievent_stop);
  247         isc__nm_enqueue_ievent(&mgr->workers[i], event);
  248     }
  249 
  250     LOCK(&mgr->lock);
  251     while (atomic_load(&mgr->workers_running) > 0) {
  252         WAIT(&mgr->wkstatecond, &mgr->lock);
  253     }
  254     UNLOCK(&mgr->lock);
  255 
  256     for (size_t i = 0; i < mgr->nworkers; i++) {
  257         isc__networker_t *worker = &mgr->workers[i];
  258         isc__netievent_t *ievent = NULL;
  259         int r;
  260 
  261         /* Empty the async event queues */
  262         while ((ievent = (isc__netievent_t *)isc_queue_dequeue(
  263                 worker->ievents)) != NULL)
  264         {
  265             isc_mempool_put(mgr->evpool, ievent);
  266         }
  267 
  268         while ((ievent = (isc__netievent_t *)isc_queue_dequeue(
  269                 worker->ievents_prio)) != NULL)
  270         {
  271             isc_mempool_put(mgr->evpool, ievent);
  272         }
  273 
  274         r = uv_loop_close(&worker->loop);
  275         INSIST(r == 0);
  276 
  277         isc_queue_destroy(worker->ievents);
  278         isc_queue_destroy(worker->ievents_prio);
  279         isc_mutex_destroy(&worker->lock);
  280         isc_condition_destroy(&worker->cond);
  281 
  282         isc_mem_put(mgr->mctx, worker->recvbuf,
  283                 ISC_NETMGR_RECVBUF_SIZE);
  284         isc_thread_join(worker->thread, NULL);
  285     }
  286 
  287     if (mgr->stats != NULL) {
  288         isc_stats_detach(&mgr->stats);
  289     }
  290 
  291     isc_condition_destroy(&mgr->wkstatecond);
  292     isc_mutex_destroy(&mgr->lock);
  293 
  294     isc_mempool_destroy(&mgr->evpool);
  295     isc_mutex_destroy(&mgr->evlock);
  296 
  297     isc_mempool_destroy(&mgr->reqpool);
  298     isc_mutex_destroy(&mgr->reqlock);
  299 
  300     isc_mem_put(mgr->mctx, mgr->workers,
  301             mgr->nworkers * sizeof(isc__networker_t));
  302     isc_mem_putanddetach(&mgr->mctx, mgr, sizeof(*mgr));
  303 }
  304 
  305 void
  306 isc_nm_pause(isc_nm_t *mgr) {
  307     REQUIRE(VALID_NM(mgr));
  308     REQUIRE(!isc__nm_in_netthread());
  309 
  310     atomic_store(&mgr->paused, true);
  311     isc__nm_acquire_interlocked_force(mgr);
  312 
  313     for (size_t i = 0; i < mgr->nworkers; i++) {
  314         isc__netievent_t *event = NULL;
  315 
  316         LOCK(&mgr->workers[i].lock);
  317         mgr->workers[i].paused = true;
  318         UNLOCK(&mgr->workers[i].lock);
  319 
  320         /*
  321          * We have to issue a stop, otherwise the uv_run loop will
  322          * run indefinitely!
  323          */
  324         event = isc__nm_get_ievent(mgr, netievent_stop);
  325         isc__nm_enqueue_ievent(&mgr->workers[i], event);
  326     }
  327 
  328     LOCK(&mgr->lock);
  329     while (atomic_load_relaxed(&mgr->workers_paused) !=
  330            atomic_load_relaxed(&mgr->workers_running))
  331     {
  332         WAIT(&mgr->wkstatecond, &mgr->lock);
  333     }
  334     UNLOCK(&mgr->lock);
  335 }
  336 
  337 void
  338 isc_nm_resume(isc_nm_t *mgr) {
  339     REQUIRE(VALID_NM(mgr));
  340     REQUIRE(!isc__nm_in_netthread());
  341 
  342     for (size_t i = 0; i < mgr->nworkers; i++) {
  343         LOCK(&mgr->workers[i].lock);
  344         mgr->workers[i].paused = false;
  345         SIGNAL(&mgr->workers[i].cond);
  346         UNLOCK(&mgr->workers[i].lock);
  347     }
  348     isc__nm_drop_interlocked(mgr);
  349 
  350     /*
  351      * We're not waiting for all the workers to come back to life;
  352      * they eventually will, we don't care.
  353      */
  354 }
  355 
  356 void
  357 isc_nm_attach(isc_nm_t *mgr, isc_nm_t **dst) {
  358     REQUIRE(VALID_NM(mgr));
  359     REQUIRE(dst != NULL && *dst == NULL);
  360 
  361     isc_refcount_increment(&mgr->references);
  362 
  363     *dst = mgr;
  364 }
  365 
  366 void
  367 isc_nm_detach(isc_nm_t **mgr0) {
  368     isc_nm_t *mgr = NULL;
  369 
  370     REQUIRE(mgr0 != NULL);
  371     REQUIRE(VALID_NM(*mgr0));
  372 
  373     mgr = *mgr0;
  374     *mgr0 = NULL;
  375 
  376     if (isc_refcount_decrement(&mgr->references) == 1) {
  377         nm_destroy(&mgr);
  378     }
  379 }
  380 
  381 void
  382 isc_nm_closedown(isc_nm_t *mgr) {
  383     REQUIRE(VALID_NM(mgr));
  384 
  385     atomic_store(&mgr->closing, true);
  386     for (size_t i = 0; i < mgr->nworkers; i++) {
  387         isc__netievent_t *event = NULL;
  388         event = isc__nm_get_ievent(mgr, netievent_shutdown);
  389         isc__nm_enqueue_ievent(&mgr->workers[i], event);
  390     }
  391 }
  392 
  393 void
  394 isc_nm_destroy(isc_nm_t **mgr0) {
  395     isc_nm_t *mgr = NULL;
  396 
  397     REQUIRE(mgr0 != NULL);
  398     REQUIRE(VALID_NM(*mgr0));
  399 
  400     mgr = *mgr0;
  401 
  402     /*
  403      * Close active connections.
  404      */
  405     isc_nm_closedown(mgr);
  406 
  407     /*
  408      * Wait for the manager to be dereferenced elsewhere.
  409      */
  410     while (isc_refcount_current(&mgr->references) > 1) {
  411         /*
  412          * Sometimes libuv gets stuck, pausing and unpausing
  413          * netmgr goes over all events in async queue for all
  414          * the workers, and since it's done only on shutdown it
  415          * doesn't cost us anything.
  416          */
  417         isc_nm_pause(mgr);
  418         isc_nm_resume(mgr);
  419 #ifdef WIN32
  420         _sleep(10);
  421 #else  /* ifdef WIN32 */
  422         usleep(10000);
  423 #endif /* ifdef WIN32 */
  424     }
  425 
  426     /*
  427      * Detach final reference.
  428      */
  429     isc_nm_detach(mgr0);
  430 }
  431 
  432 void
  433 isc_nm_maxudp(isc_nm_t *mgr, uint32_t maxudp) {
  434     REQUIRE(VALID_NM(mgr));
  435 
  436     atomic_store(&mgr->maxudp, maxudp);
  437 }
  438 
  439 void
  440 isc_nm_tcp_settimeouts(isc_nm_t *mgr, uint32_t init, uint32_t idle,
  441                uint32_t keepalive, uint32_t advertised) {
  442     REQUIRE(VALID_NM(mgr));
  443 
  444     mgr->init = init * 100;
  445     mgr->idle = idle * 100;
  446     mgr->keepalive = keepalive * 100;
  447     mgr->advertised = advertised * 100;
  448 }
  449 
  450 void
  451 isc_nm_tcp_gettimeouts(isc_nm_t *mgr, uint32_t *initial, uint32_t *idle,
  452                uint32_t *keepalive, uint32_t *advertised) {
  453     REQUIRE(VALID_NM(mgr));
  454 
  455     if (initial != NULL) {
  456         *initial = mgr->init / 100;
  457     }
  458 
  459     if (idle != NULL) {
  460         *idle = mgr->idle / 100;
  461     }
  462 
  463     if (keepalive != NULL) {
  464         *keepalive = mgr->keepalive / 100;
  465     }
  466 
  467     if (advertised != NULL) {
  468         *advertised = mgr->advertised / 100;
  469     }
  470 }
  471 
  472 /*
  473  * nm_thread is a single worker thread, that runs uv_run event loop
  474  * until asked to stop.
  475  */
  476 static isc_threadresult_t
  477 nm_thread(isc_threadarg_t worker0) {
  478     isc__networker_t *worker = (isc__networker_t *)worker0;
  479 
  480     isc__nm_tid_v = worker->id;
  481     isc_thread_setaffinity(isc__nm_tid_v);
  482 
  483     while (true) {
  484         int r = uv_run(&worker->loop, UV_RUN_DEFAULT);
  485         bool pausing = false;
  486 
  487         /*
  488          * or there's nothing to do. In the first case - wait
  489          * for condition. In the latter - timedwait
  490          */
  491         LOCK(&worker->lock);
  492         while (worker->paused) {
  493             LOCK(&worker->mgr->lock);
  494             if (!pausing) {
  495                 atomic_fetch_add_explicit(
  496                     &worker->mgr->workers_paused, 1,
  497                     memory_order_acquire);
  498                 pausing = true;
  499             }
  500 
  501             SIGNAL(&worker->mgr->wkstatecond);
  502             UNLOCK(&worker->mgr->lock);
  503 
  504             WAIT(&worker->cond, &worker->lock);
  505 
  506             /* Process priority events */
  507             process_queue(worker, worker->ievents_prio);
  508         }
  509         if (pausing) {
  510             uint32_t wp = atomic_fetch_sub_explicit(
  511                 &worker->mgr->workers_paused, 1,
  512                 memory_order_release);
  513             if (wp == 1) {
  514                 atomic_store(&worker->mgr->paused, false);
  515             }
  516         }
  517         bool finished = worker->finished;
  518         UNLOCK(&worker->lock);
  519 
  520         if (finished) {
  521             /*
  522              * We need to launch the loop one more time
  523              * in UV_RUN_NOWAIT mode to make sure that
  524              * worker->async is closed, so that we can
  525              * close the loop cleanly.  We don't care
  526              * about the callback, as in this case we can
  527              * be certain that uv_run() will eat the event.
  528              *
  529              * XXX: We may need to take steps here to ensure
  530              * that all netmgr handles are freed.
  531              */
  532             uv_close((uv_handle_t *)&worker->async, NULL);
  533             uv_run(&worker->loop, UV_RUN_NOWAIT);
  534             break;
  535         }
  536 
  537         if (r == 0) {
  538             /*
  539              * XXX: uv_run() in UV_RUN_DEFAULT mode returns
  540              * zero if there are still active uv_handles.
  541              * This shouldn't happen, but if it does, we just
  542              * keep checking until they're done. We nap for a
  543              * tenth of a second on each loop so as not to burn
  544              * CPU. (We do a conditional wait instead, but it
  545              * seems like overkill for this case.)
  546              */
  547 #ifdef WIN32
  548             _sleep(100);
  549 #else  /* ifdef WIN32 */
  550             usleep(100000);
  551 #endif /* ifdef WIN32 */
  552         }
  553 
  554         /*
  555          * Empty the async queue.
  556          */
  557         process_queue(worker, worker->ievents_prio);
  558         process_queue(worker, worker->ievents);
  559     }
  560 
  561     LOCK(&worker->mgr->lock);
  562     atomic_fetch_sub_explicit(&worker->mgr->workers_running, 1,
  563                   memory_order_relaxed);
  564     SIGNAL(&worker->mgr->wkstatecond);
  565     UNLOCK(&worker->mgr->lock);
  566 
  567     return ((isc_threadresult_t)0);
  568 }
  569 
  570 /*
  571  * async_cb is a universal callback for 'async' events sent to event loop.
  572  * It's the only way to safely pass data to the libuv event loop. We use a
  573  * single async event and a lockless queue of 'isc__netievent_t' structures
  574  * passed from other threads.
  575  */
  576 static void
  577 async_cb(uv_async_t *handle) {
  578     isc__networker_t *worker = (isc__networker_t *)handle->loop->data;
  579     process_queue(worker, worker->ievents_prio);
  580     process_queue(worker, worker->ievents);
  581 }
  582 
  583 static void
  584 process_queue(isc__networker_t *worker, isc_queue_t *queue) {
  585     isc__netievent_t *ievent = NULL;
  586 
  587     while ((ievent = (isc__netievent_t *)isc_queue_dequeue(queue)) != NULL)
  588     {
  589         switch (ievent->type) {
  590         case netievent_stop:
  591             uv_stop(&worker->loop);
  592             isc_mempool_put(worker->mgr->evpool, ievent);
  593             return;
  594         case netievent_udplisten:
  595             isc__nm_async_udplisten(worker, ievent);
  596             break;
  597         case netievent_udpstop:
  598             isc__nm_async_udpstop(worker, ievent);
  599             break;
  600         case netievent_udpsend:
  601             isc__nm_async_udpsend(worker, ievent);
  602             break;
  603         case netievent_tcpconnect:
  604             isc__nm_async_tcpconnect(worker, ievent);
  605             break;
  606         case netievent_tcplisten:
  607             isc__nm_async_tcplisten(worker, ievent);
  608             break;
  609         case netievent_tcpchildaccept:
  610             isc__nm_async_tcpchildaccept(worker, ievent);
  611             break;
  612         case netievent_tcpaccept:
  613             isc__nm_async_tcpaccept(worker, ievent);
  614             break;
  615         case netievent_tcpstartread:
  616             isc__nm_async_tcp_startread(worker, ievent);
  617             break;
  618         case netievent_tcppauseread:
  619             isc__nm_async_tcp_pauseread(worker, ievent);
  620             break;
  621         case netievent_tcpsend:
  622             isc__nm_async_tcpsend(worker, ievent);
  623             break;
  624         case netievent_tcpdnssend:
  625             isc__nm_async_tcpdnssend(worker, ievent);
  626             break;
  627         case netievent_tcpstop:
  628             isc__nm_async_tcpstop(worker, ievent);
  629             break;
  630         case netievent_tcpclose:
  631             isc__nm_async_tcpclose(worker, ievent);
  632             break;
  633         case netievent_tcpdnsclose:
  634             isc__nm_async_tcpdnsclose(worker, ievent);
  635             break;
  636         case netievent_closecb:
  637             isc__nm_async_closecb(worker, ievent);
  638             break;
  639         case netievent_shutdown:
  640             isc__nm_async_shutdown(worker, ievent);
  641             break;
  642         default:
  643             INSIST(0);
  644             ISC_UNREACHABLE();
  645         }
  646 
  647         isc__nm_put_ievent(worker->mgr, ievent);
  648     }
  649 }
  650 
  651 void *
  652 isc__nm_get_ievent(isc_nm_t *mgr, isc__netievent_type type) {
  653     isc__netievent_storage_t *event = isc_mempool_get(mgr->evpool);
  654 
  655     *event = (isc__netievent_storage_t){ .ni.type = type };
  656     return (event);
  657 }
  658 
  659 void
  660 isc__nm_put_ievent(isc_nm_t *mgr, void *ievent) {
  661     isc_mempool_put(mgr->evpool, ievent);
  662 }
  663 
  664 void
  665 isc__nm_enqueue_ievent(isc__networker_t *worker, isc__netievent_t *event) {
  666     if (event->type > netievent_prio) {
  667         /*
  668          * We need to make sure this signal will be delivered and
  669          * the queue will be processed.
  670          */
  671         LOCK(&worker->lock);
  672         isc_queue_enqueue(worker->ievents_prio, (uintptr_t)event);
  673         SIGNAL(&worker->cond);
  674         UNLOCK(&worker->lock);
  675     } else {
  676         isc_queue_enqueue(worker->ievents, (uintptr_t)event);
  677     }
  678     uv_async_send(&worker->async);
  679 }
  680 
  681 bool
  682 isc__nmsocket_active(isc_nmsocket_t *sock) {
  683     REQUIRE(VALID_NMSOCK(sock));
  684     if (sock->parent != NULL) {
  685         return (atomic_load(&sock->parent->active));
  686     }
  687 
  688     return (atomic_load(&sock->active));
  689 }
  690 
  691 void
  692 isc__nmsocket_attach(isc_nmsocket_t *sock, isc_nmsocket_t **target) {
  693     REQUIRE(VALID_NMSOCK(sock));
  694     REQUIRE(target != NULL && *target == NULL);
  695 
  696     if (sock->parent != NULL) {
  697         INSIST(sock->parent->parent == NULL); /* sanity check */
  698         isc_refcount_increment0(&sock->parent->references);
  699     } else {
  700         isc_refcount_increment0(&sock->references);
  701     }
  702 
  703     *target = sock;
  704 }
  705 
  706 /*
  707  * Free all resources inside a socket (including its children if any).
  708  */
  709 static void
  710 nmsocket_cleanup(isc_nmsocket_t *sock, bool dofree) {
  711     isc_nmhandle_t *handle = NULL;
  712     isc__nm_uvreq_t *uvreq = NULL;
  713 
  714     REQUIRE(VALID_NMSOCK(sock));
  715     REQUIRE(!isc__nmsocket_active(sock));
  716 
  717     atomic_store(&sock->destroying, true);
  718 
  719     if (sock->parent == NULL && sock->children != NULL) {
  720         /*
  721          * We shouldn't be here unless there are no active handles,
  722          * so we can clean up and free the children.
  723          */
  724         for (int i = 0; i < sock->nchildren; i++) {
  725             if (!atomic_load(&sock->children[i].destroying)) {
  726                 nmsocket_cleanup(&sock->children[i], false);
  727             }
  728         }
  729 
  730         /*
  731          * This was a parent socket; free the children.
  732          */
  733         isc_mem_put(sock->mgr->mctx, sock->children,
  734                 sock->nchildren * sizeof(*sock));
  735         sock->children = NULL;
  736         sock->nchildren = 0;
  737     }
  738     if (sock->statsindex != NULL) {
  739         isc__nm_decstats(sock->mgr, sock->statsindex[STATID_ACTIVE]);
  740     }
  741 
  742     sock->tcphandle = NULL;
  743 
  744     if (sock->outerhandle != NULL) {
  745         isc_nmhandle_unref(sock->outerhandle);
  746         sock->outerhandle = NULL;
  747     }
  748 
  749     if (sock->outer != NULL) {
  750         isc__nmsocket_detach(&sock->outer);
  751     }
  752 
  753     while ((handle = isc_astack_pop(sock->inactivehandles)) != NULL) {
  754         nmhandle_free(sock, handle);
  755     }
  756 
  757     if (sock->buf != NULL) {
  758         isc_mem_free(sock->mgr->mctx, sock->buf);
  759     }
  760 
  761     if (sock->quota != NULL) {
  762         isc_quota_detach(&sock->quota);
  763     }
  764 
  765     sock->pquota = NULL;
  766 
  767     if (sock->timer_initialized) {
  768         sock->timer_initialized = false;
  769         /* We might be in timer callback */
  770         if (!uv_is_closing((uv_handle_t *)&sock->timer)) {
  771             uv_timer_stop(&sock->timer);
  772             uv_close((uv_handle_t *)&sock->timer, NULL);
  773         }
  774     }
  775 
  776     isc_astack_destroy(sock->inactivehandles);
  777 
  778     while ((uvreq = isc_astack_pop(sock->inactivereqs)) != NULL) {
  779         isc_mempool_put(sock->mgr->reqpool, uvreq);
  780     }
  781 
  782     isc_astack_destroy(sock->inactivereqs);
  783     sock->magic = 0;
  784 
  785     isc_mem_free(sock->mgr->mctx, sock->ah_frees);
  786     isc_mem_free(sock->mgr->mctx, sock->ah_handles);
  787     isc_mutex_destroy(&sock->lock);
  788     isc_condition_destroy(&sock->cond);
  789 
  790     if (dofree) {
  791         isc_nm_t *mgr = sock->mgr;
  792         isc_mem_put(mgr->mctx, sock, sizeof(*sock));
  793         isc_nm_detach(&mgr);
  794     } else {
  795         isc_nm_detach(&sock->mgr);
  796     }
  797 }
  798 
  799 static void
  800 nmsocket_maybe_destroy(isc_nmsocket_t *sock) {
  801     int active_handles;
  802     bool destroy = false;
  803 
  804     if (sock->parent != NULL) {
  805         /*
  806          * This is a child socket and cannot be destroyed except
  807          * as a side effect of destroying the parent, so let's go
  808          * see if the parent is ready to be destroyed.
  809          */
  810         nmsocket_maybe_destroy(sock->parent);
  811         return;
  812     }
  813 
  814     /*
  815      * This is a parent socket (or a standalone). See whether the
  816      * children have active handles before deciding whether to
  817      * accept destruction.
  818      */
  819     LOCK(&sock->lock);
  820     if (atomic_load(&sock->active) || atomic_load(&sock->destroying) ||
  821         !atomic_load(&sock->closed) || atomic_load(&sock->references) != 0)
  822     {
  823         UNLOCK(&sock->lock);
  824         return;
  825     }
  826 
  827     active_handles = atomic_load(&sock->ah);
  828     if (sock->children != NULL) {
  829         for (int i = 0; i < sock->nchildren; i++) {
  830             LOCK(&sock->children[i].lock);
  831             active_handles += atomic_load(&sock->children[i].ah);
  832             UNLOCK(&sock->children[i].lock);
  833         }
  834     }
  835 
  836     if (active_handles == 0 || sock->tcphandle != NULL) {
  837         destroy = true;
  838     }
  839 
  840     if (destroy) {
  841         atomic_store(&sock->destroying, true);
  842         UNLOCK(&sock->lock);
  843         nmsocket_cleanup(sock, true);
  844     } else {
  845         UNLOCK(&sock->lock);
  846     }
  847 }
  848 
  849 void
  850 isc__nmsocket_prep_destroy(isc_nmsocket_t *sock) {
  851     REQUIRE(sock->parent == NULL);
  852 
  853     /*
  854      * The final external reference to the socket is gone. We can try
  855      * destroying the socket, but we have to wait for all the inflight
  856      * handles to finish first.
  857      */
  858     atomic_store(&sock->active, false);
  859 
  860     /*
  861      * If the socket has children, they'll need to be marked inactive
  862      * so they can be cleaned up too.
  863      */
  864     if (sock->children != NULL) {
  865         for (int i = 0; i < sock->nchildren; i++) {
  866             atomic_store(&sock->children[i].active, false);
  867         }
  868     }
  869 
  870     /*
  871      * If we're here then we already stopped listening; otherwise
  872      * we'd have a hanging reference from the listening process.
  873      *
  874      * If it's a regular socket we may need to close it.
  875      */
  876     if (!atomic_load(&sock->closed)) {
  877         switch (sock->type) {
  878         case isc_nm_tcpsocket:
  879             isc__nm_tcp_close(sock);
  880             return;
  881         case isc_nm_tcpdnssocket:
  882             isc__nm_tcpdns_close(sock);
  883             return;
  884         default:
  885             break;
  886         }
  887     }
  888 
  889     nmsocket_maybe_destroy(sock);
  890 }
  891 
  892 void
  893 isc__nmsocket_detach(isc_nmsocket_t **sockp) {
  894     REQUIRE(sockp != NULL && *sockp != NULL);
  895     REQUIRE(VALID_NMSOCK(*sockp));
  896 
  897     isc_nmsocket_t *sock = *sockp, *rsock = NULL;
  898     *sockp = NULL;
  899 
  900     /*
  901      * If the socket is a part of a set (a child socket) we are
  902      * counting references for the whole set at the parent.
  903      */
  904     if (sock->parent != NULL) {
  905         rsock = sock->parent;
  906         INSIST(rsock->parent == NULL); /* Sanity check */
  907     } else {
  908         rsock = sock;
  909     }
  910 
  911     if (isc_refcount_decrement(&rsock->references) == 1) {
  912         isc__nmsocket_prep_destroy(rsock);
  913     }
  914 }
  915 
  916 void
  917 isc_nmsocket_close(isc_nmsocket_t **sockp) {
  918     REQUIRE(sockp != NULL);
  919     REQUIRE(VALID_NMSOCK(*sockp));
  920     REQUIRE((*sockp)->type == isc_nm_udplistener ||
  921         (*sockp)->type == isc_nm_tcplistener ||
  922         (*sockp)->type == isc_nm_tcpdnslistener);
  923 
  924     isc__nmsocket_detach(sockp);
  925 }
  926 
  927 void
  928 isc__nmsocket_init(isc_nmsocket_t *sock, isc_nm_t *mgr, isc_nmsocket_type type,
  929            isc_nmiface_t *iface) {
  930     uint16_t family;
  931 
  932     REQUIRE(sock != NULL);
  933     REQUIRE(mgr != NULL);
  934     REQUIRE(iface != NULL);
  935 
  936     family = iface->addr.type.sa.sa_family;
  937 
  938     *sock = (isc_nmsocket_t){ .type = type,
  939                   .iface = iface,
  940                   .fd = -1,
  941                   .ah_size = 32,
  942                   .inactivehandles = isc_astack_new(
  943                       mgr->mctx, ISC_NM_HANDLES_STACK_SIZE),
  944                   .inactivereqs = isc_astack_new(
  945                       mgr->mctx, ISC_NM_REQS_STACK_SIZE) };
  946 
  947     isc_nm_attach(mgr, &sock->mgr);
  948     sock->uv_handle.handle.data = sock;
  949 
  950     sock->ah_frees = isc_mem_allocate(mgr->mctx,
  951                       sock->ah_size * sizeof(size_t));
  952     sock->ah_handles = isc_mem_allocate(
  953         mgr->mctx, sock->ah_size * sizeof(isc_nmhandle_t *));
  954     ISC_LINK_INIT(&sock->quotacb, link);
  955     for (size_t i = 0; i < 32; i++) {
  956         sock->ah_frees[i] = i;
  957         sock->ah_handles[i] = NULL;
  958     }
  959 
  960     switch (type) {
  961     case isc_nm_udpsocket:
  962     case isc_nm_udplistener:
  963         if (family == AF_INET) {
  964             sock->statsindex = udp4statsindex;
  965         } else {
  966             sock->statsindex = udp6statsindex;
  967         }
  968         isc__nm_incstats(sock->mgr, sock->statsindex[STATID_ACTIVE]);
  969         break;
  970     case isc_nm_tcpsocket:
  971     case isc_nm_tcplistener:
  972         if (family == AF_INET) {
  973             sock->statsindex = tcp4statsindex;
  974         } else {
  975             sock->statsindex = tcp6statsindex;
  976         }
  977         isc__nm_incstats(sock->mgr, sock->statsindex[STATID_ACTIVE]);
  978         break;
  979     default:
  980         break;
  981     }
  982 
  983     isc_mutex_init(&sock->lock);
  984     isc_condition_init(&sock->cond);
  985     isc_refcount_init(&sock->references, 1);
  986 
  987     atomic_init(&sock->active, true);
  988     atomic_init(&sock->sequential, false);
  989     atomic_init(&sock->overlimit, false);
  990     atomic_init(&sock->processing, false);
  991     atomic_init(&sock->readpaused, false);
  992 
  993     sock->magic = NMSOCK_MAGIC;
  994 }
  995 
  996 void
  997 isc__nmsocket_clearcb(isc_nmsocket_t *sock) {
  998     REQUIRE(VALID_NMSOCK(sock));
  999 
 1000     sock->rcb.recv = NULL;
 1001     sock->rcbarg = NULL;
 1002     sock->accept_cb.accept = NULL;
 1003     sock->accept_cbarg = NULL;
 1004 }
 1005 
 1006 void
 1007 isc__nm_free_uvbuf(isc_nmsocket_t *sock, const uv_buf_t *buf) {
 1008     isc__networker_t *worker = NULL;
 1009 
 1010     REQUIRE(VALID_NMSOCK(sock));
 1011     if (buf->base == NULL) {
 1012         /* Empty buffer: might happen in case of error. */
 1013         return;
 1014     }
 1015     worker = &sock->mgr->workers[sock->tid];
 1016 
 1017     REQUIRE(worker->recvbuf_inuse);
 1018     if (sock->type == isc_nm_udpsocket && buf->base > worker->recvbuf &&
 1019         buf->base <= worker->recvbuf + ISC_NETMGR_RECVBUF_SIZE)
 1020     {
 1021         /* Can happen in case of out-of-order recvmmsg in libuv1.36 */
 1022         return;
 1023     }
 1024     REQUIRE(buf->base == worker->recvbuf);
 1025     worker->recvbuf_inuse = false;
 1026 }
 1027 
 1028 static isc_nmhandle_t *
 1029 alloc_handle(isc_nmsocket_t *sock) {
 1030     isc_nmhandle_t *handle =
 1031         isc_mem_get(sock->mgr->mctx,
 1032                 sizeof(isc_nmhandle_t) + sock->extrahandlesize);
 1033 
 1034     *handle = (isc_nmhandle_t){ .magic = NMHANDLE_MAGIC };
 1035     isc_refcount_init(&handle->references, 1);
 1036 
 1037     return (handle);
 1038 }
 1039 
 1040 isc_nmhandle_t *
 1041 isc__nmhandle_get(isc_nmsocket_t *sock, isc_sockaddr_t *peer,
 1042           isc_sockaddr_t *local) {
 1043     isc_nmhandle_t *handle = NULL;
 1044     size_t handlenum;
 1045     int pos;
 1046 
 1047     REQUIRE(VALID_NMSOCK(sock));
 1048 
 1049     handle = isc_astack_pop(sock->inactivehandles);
 1050 
 1051     if (handle == NULL) {
 1052         handle = alloc_handle(sock);
 1053     } else {
 1054         isc_refcount_increment0(&handle->references);
 1055         INSIST(VALID_NMHANDLE(handle));
 1056     }
 1057 
 1058     isc__nmsocket_attach(sock, &handle->sock);
 1059 
 1060     if (peer != NULL) {
 1061         memcpy(&handle->peer, peer, sizeof(isc_sockaddr_t));
 1062     } else {
 1063         memcpy(&handle->peer, &sock->peer, sizeof(isc_sockaddr_t));
 1064     }
 1065 
 1066     if (local != NULL) {
 1067         memcpy(&handle->local, local, sizeof(isc_sockaddr_t));
 1068     } else if (sock->iface != NULL) {
 1069         memcpy(&handle->local, &sock->iface->addr,
 1070                sizeof(isc_sockaddr_t));
 1071     } else {
 1072         INSIST(0);
 1073         ISC_UNREACHABLE();
 1074     }
 1075 
 1076     LOCK(&sock->lock);
 1077     /* We need to add this handle to the list of active handles */
 1078     if ((size_t)atomic_load(&sock->ah) == sock->ah_size) {
 1079         sock->ah_frees =
 1080             isc_mem_reallocate(sock->mgr->mctx, sock->ah_frees,
 1081                        sock->ah_size * 2 * sizeof(size_t));
 1082         sock->ah_handles = isc_mem_reallocate(
 1083             sock->mgr->mctx, sock->ah_handles,
 1084             sock->ah_size * 2 * sizeof(isc_nmhandle_t *));
 1085 
 1086         for (size_t i = sock->ah_size; i < sock->ah_size * 2; i++) {
 1087             sock->ah_frees[i] = i;
 1088             sock->ah_handles[i] = NULL;
 1089         }
 1090 
 1091         sock->ah_size *= 2;
 1092     }
 1093 
 1094     handlenum = atomic_fetch_add(&sock->ah, 1);
 1095     pos = sock->ah_frees[handlenum];
 1096 
 1097     INSIST(sock->ah_handles[pos] == NULL);
 1098     sock->ah_handles[pos] = handle;
 1099     handle->ah_pos = pos;
 1100     UNLOCK(&sock->lock);
 1101 
 1102     if (sock->type == isc_nm_tcpsocket) {
 1103         INSIST(sock->tcphandle == NULL);
 1104         sock->tcphandle = handle;
 1105     }
 1106 
 1107     return (handle);
 1108 }
 1109 
 1110 void
 1111 isc_nmhandle_ref(isc_nmhandle_t *handle) {
 1112     REQUIRE(VALID_NMHANDLE(handle));
 1113 
 1114     isc_refcount_increment(&handle->references);
 1115 }
 1116 
 1117 bool
 1118 isc_nmhandle_is_stream(isc_nmhandle_t *handle) {
 1119     REQUIRE(VALID_NMHANDLE(handle));
 1120 
 1121     return (handle->sock->type == isc_nm_tcpsocket ||
 1122         handle->sock->type == isc_nm_tcpdnssocket);
 1123 }
 1124 
 1125 static void
 1126 nmhandle_free(isc_nmsocket_t *sock, isc_nmhandle_t *handle) {
 1127     size_t extra = sock->extrahandlesize;
 1128 
 1129     isc_refcount_destroy(&handle->references);
 1130 
 1131     if (handle->dofree != NULL) {
 1132         handle->dofree(handle->opaque);
 1133     }
 1134 
 1135     *handle = (isc_nmhandle_t){ .magic = 0 };
 1136 
 1137     isc_mem_put(sock->mgr->mctx, handle, sizeof(isc_nmhandle_t) + extra);
 1138 }
 1139 
 1140 static void
 1141 nmhandle_deactivate(isc_nmsocket_t *sock, isc_nmhandle_t *handle) {
 1142     size_t handlenum;
 1143     bool reuse = false;
 1144 
 1145     /*
 1146      * We do all of this under lock to avoid races with socket
 1147      * destruction.  We have to do this now, because at this point the
 1148      * socket is either unused or still attached to event->sock.
 1149      */
 1150     LOCK(&sock->lock);
 1151 
 1152     INSIST(sock->ah_handles[handle->ah_pos] == handle);
 1153     INSIST(sock->ah_size > handle->ah_pos);
 1154     INSIST(atomic_load(&sock->ah) > 0);
 1155 
 1156     sock->ah_handles[handle->ah_pos] = NULL;
 1157     handlenum = atomic_fetch_sub(&sock->ah, 1) - 1;
 1158     sock->ah_frees[handlenum] = handle->ah_pos;
 1159     handle->ah_pos = 0;
 1160     if (atomic_load(&sock->active)) {
 1161         reuse = isc_astack_trypush(sock->inactivehandles, handle);
 1162     }
 1163     if (!reuse) {
 1164         nmhandle_free(sock, handle);
 1165     }
 1166     UNLOCK(&sock->lock);
 1167 }
 1168 
 1169 void
 1170 isc_nmhandle_unref(isc_nmhandle_t *handle) {
 1171     isc_nmsocket_t *sock = NULL;
 1172 
 1173     REQUIRE(VALID_NMHANDLE(handle));
 1174 
 1175     if (isc_refcount_decrement(&handle->references) > 1) {
 1176         return;
 1177     }
 1178     /* We need an acquire memory barrier here */
 1179     (void)isc_refcount_current(&handle->references);
 1180 
 1181     sock = handle->sock;
 1182     handle->sock = NULL;
 1183 
 1184     if (handle->doreset != NULL) {
 1185         handle->doreset(handle->opaque);
 1186     }
 1187 
 1188     nmhandle_deactivate(sock, handle);
 1189 
 1190     /*
 1191      * The handle is gone now. If the socket has a callback configured
 1192      * for that (e.g., to perform cleanup after request processing),
 1193      * call it now, or schedule it to run asynchronously.
 1194      */
 1195     if (sock->closehandle_cb != NULL) {
 1196         if (sock->tid == isc_nm_tid()) {
 1197             sock->closehandle_cb(sock);
 1198         } else {
 1199             isc__netievent_closecb_t *event = isc__nm_get_ievent(
 1200                 sock->mgr, netievent_closecb);
 1201             /*
 1202              * The socket will be finally detached by the closecb
 1203              * event handler.
 1204              */
 1205             isc__nmsocket_attach(sock, &event->sock);
 1206             isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
 1207                            (isc__netievent_t *)event);
 1208         }
 1209     }
 1210 
 1211     isc__nmsocket_detach(&sock);
 1212 }
 1213 
 1214 void *
 1215 isc_nmhandle_getdata(isc_nmhandle_t *handle) {
 1216     REQUIRE(VALID_NMHANDLE(handle));
 1217 
 1218     return (handle->opaque);
 1219 }
 1220 
 1221 void
 1222 isc_nmhandle_setdata(isc_nmhandle_t *handle, void *arg,
 1223              isc_nm_opaquecb_t doreset, isc_nm_opaquecb_t dofree) {
 1224     REQUIRE(VALID_NMHANDLE(handle));
 1225 
 1226     handle->opaque = arg;
 1227     handle->doreset = doreset;
 1228     handle->dofree = dofree;
 1229 }
 1230 
 1231 void *
 1232 isc_nmhandle_getextra(isc_nmhandle_t *handle) {
 1233     REQUIRE(VALID_NMHANDLE(handle));
 1234 
 1235     return (handle->extra);
 1236 }
 1237 
 1238 isc_sockaddr_t
 1239 isc_nmhandle_peeraddr(isc_nmhandle_t *handle) {
 1240     REQUIRE(VALID_NMHANDLE(handle));
 1241 
 1242     return (handle->peer);
 1243 }
 1244 
 1245 isc_sockaddr_t
 1246 isc_nmhandle_localaddr(isc_nmhandle_t *handle) {
 1247     REQUIRE(VALID_NMHANDLE(handle));
 1248 
 1249     return (handle->local);
 1250 }
 1251 
 1252 isc_nm_t *
 1253 isc_nmhandle_netmgr(isc_nmhandle_t *handle) {
 1254     REQUIRE(VALID_NMHANDLE(handle));
 1255     REQUIRE(VALID_NMSOCK(handle->sock));
 1256 
 1257     return (handle->sock->mgr);
 1258 }
 1259 
 1260 isc__nm_uvreq_t *
 1261 isc__nm_uvreq_get(isc_nm_t *mgr, isc_nmsocket_t *sock) {
 1262     isc__nm_uvreq_t *req = NULL;
 1263 
 1264     REQUIRE(VALID_NM(mgr));
 1265     REQUIRE(VALID_NMSOCK(sock));
 1266 
 1267     if (sock != NULL && atomic_load(&sock->active)) {
 1268         /* Try to reuse one */
 1269         req = isc_astack_pop(sock->inactivereqs);
 1270     }
 1271 
 1272     if (req == NULL) {
 1273         req = isc_mempool_get(mgr->reqpool);
 1274     }
 1275 
 1276     *req = (isc__nm_uvreq_t){ .magic = 0 };
 1277     req->uv_req.req.data = req;
 1278     isc__nmsocket_attach(sock, &req->sock);
 1279     req->magic = UVREQ_MAGIC;
 1280 
 1281     return (req);
 1282 }
 1283 
 1284 void
 1285 isc__nm_uvreq_put(isc__nm_uvreq_t **req0, isc_nmsocket_t *sock) {
 1286     isc__nm_uvreq_t *req = NULL;
 1287     isc_nmhandle_t *handle = NULL;
 1288 
 1289     REQUIRE(req0 != NULL);
 1290     REQUIRE(VALID_UVREQ(*req0));
 1291 
 1292     req = *req0;
 1293     *req0 = NULL;
 1294 
 1295     INSIST(sock == req->sock);
 1296 
 1297     req->magic = 0;
 1298 
 1299     /*
 1300      * We need to save this first to make sure that handle,
 1301      * sock, and the netmgr won't all disappear.
 1302      */
 1303     handle = req->handle;
 1304     req->handle = NULL;
 1305 
 1306     if (!atomic_load(&sock->active) ||
 1307         !isc_astack_trypush(sock->inactivereqs, req)) {
 1308         isc_mempool_put(sock->mgr->reqpool, req);
 1309     }
 1310 
 1311     if (handle != NULL) {
 1312         isc_nmhandle_unref(handle);
 1313     }
 1314 
 1315     isc__nmsocket_detach(&sock);
 1316 }
 1317 
 1318 isc_result_t
 1319 isc_nm_send(isc_nmhandle_t *handle, isc_region_t *region, isc_nm_cb_t cb,
 1320         void *cbarg) {
 1321     REQUIRE(VALID_NMHANDLE(handle));
 1322 
 1323     switch (handle->sock->type) {
 1324     case isc_nm_udpsocket:
 1325     case isc_nm_udplistener:
 1326         return (isc__nm_udp_send(handle, region, cb, cbarg));
 1327     case isc_nm_tcpsocket:
 1328         return (isc__nm_tcp_send(handle, region, cb, cbarg));
 1329     case isc_nm_tcpdnssocket:
 1330         return (isc__nm_tcpdns_send(handle, region, cb, cbarg));
 1331     default:
 1332         INSIST(0);
 1333         ISC_UNREACHABLE();
 1334     }
 1335 }
 1336 
 1337 isc_result_t
 1338 isc_nm_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg) {
 1339     REQUIRE(VALID_NMHANDLE(handle));
 1340 
 1341     switch (handle->sock->type) {
 1342     case isc_nm_tcpsocket:
 1343         return (isc__nm_tcp_read(handle, cb, cbarg));
 1344     default:
 1345         INSIST(0);
 1346         ISC_UNREACHABLE();
 1347     }
 1348 }
 1349 
 1350 void
 1351 isc_nm_cancelread(isc_nmhandle_t *handle) {
 1352     REQUIRE(VALID_NMHANDLE(handle));
 1353 
 1354     switch (handle->sock->type) {
 1355     case isc_nm_tcpsocket:
 1356         isc__nm_tcp_cancelread(handle->sock);
 1357         break;
 1358     default:
 1359         INSIST(0);
 1360         ISC_UNREACHABLE();
 1361     }
 1362 }
 1363 
 1364 isc_result_t
 1365 isc_nm_pauseread(isc_nmhandle_t *handle) {
 1366     REQUIRE(VALID_NMHANDLE(handle));
 1367 
 1368     isc_nmsocket_t *sock = handle->sock;
 1369 
 1370     switch (sock->type) {
 1371     case isc_nm_tcpsocket:
 1372         return (isc__nm_tcp_pauseread(sock));
 1373     default:
 1374         INSIST(0);
 1375         ISC_UNREACHABLE();
 1376     }
 1377 }
 1378 
 1379 isc_result_t
 1380 isc_nm_resumeread(isc_nmhandle_t *handle) {
 1381     REQUIRE(VALID_NMHANDLE(handle));
 1382 
 1383     isc_nmsocket_t *sock = handle->sock;
 1384 
 1385     switch (sock->type) {
 1386     case isc_nm_tcpsocket:
 1387         return (isc__nm_tcp_resumeread(sock));
 1388     default:
 1389         INSIST(0);
 1390         ISC_UNREACHABLE();
 1391     }
 1392 }
 1393 
 1394 void
 1395 isc_nm_stoplistening(isc_nmsocket_t *sock) {
 1396     REQUIRE(VALID_NMSOCK(sock));
 1397 
 1398     switch (sock->type) {
 1399     case isc_nm_udplistener:
 1400         isc__nm_udp_stoplistening(sock);
 1401         break;
 1402     case isc_nm_tcpdnslistener:
 1403         isc__nm_tcpdns_stoplistening(sock);
 1404         break;
 1405     case isc_nm_tcplistener:
 1406         isc__nm_tcp_stoplistening(sock);
 1407         break;
 1408     default:
 1409         INSIST(0);
 1410         ISC_UNREACHABLE();
 1411     }
 1412 }
 1413 
 1414 void
 1415 isc__nm_async_closecb(isc__networker_t *worker, isc__netievent_t *ev0) {
 1416     isc__netievent_closecb_t *ievent = (isc__netievent_closecb_t *)ev0;
 1417 
 1418     REQUIRE(VALID_NMSOCK(ievent->sock));
 1419     REQUIRE(ievent->sock->tid == isc_nm_tid());
 1420     REQUIRE(ievent->sock->closehandle_cb != NULL);
 1421 
 1422     UNUSED(worker);
 1423 
 1424     ievent->sock->closehandle_cb(ievent->sock);
 1425     isc__nmsocket_detach(&ievent->sock);
 1426 }
 1427 
 1428 static void
 1429 shutdown_walk_cb(uv_handle_t *handle, void *arg) {
 1430     UNUSED(arg);
 1431 
 1432     switch (handle->type) {
 1433     case UV_TCP:
 1434         isc__nm_tcp_shutdown(uv_handle_get_data(handle));
 1435         break;
 1436     default:
 1437         break;
 1438     }
 1439 }
 1440 
 1441 void
 1442 isc__nm_async_shutdown(isc__networker_t *worker, isc__netievent_t *ev0) {
 1443     UNUSED(ev0);
 1444     uv_walk(&worker->loop, shutdown_walk_cb, NULL);
 1445 }
 1446 
 1447 bool
 1448 isc__nm_acquire_interlocked(isc_nm_t *mgr) {
 1449     LOCK(&mgr->lock);
 1450     bool success = atomic_compare_exchange_strong(&mgr->interlocked,
 1451                               &(bool){ false }, true);
 1452     UNLOCK(&mgr->lock);
 1453     return (success);
 1454 }
 1455 
 1456 void
 1457 isc__nm_drop_interlocked(isc_nm_t *mgr) {
 1458     LOCK(&mgr->lock);
 1459     bool success = atomic_compare_exchange_strong(&mgr->interlocked,
 1460                               &(bool){ true }, false);
 1461     INSIST(success);
 1462     BROADCAST(&mgr->wkstatecond);
 1463     UNLOCK(&mgr->lock);
 1464 }
 1465 
 1466 void
 1467 isc__nm_acquire_interlocked_force(isc_nm_t *mgr) {
 1468     LOCK(&mgr->lock);
 1469     while (!atomic_compare_exchange_strong(&mgr->interlocked,
 1470                            &(bool){ false }, true))
 1471     {
 1472         WAIT(&mgr->wkstatecond, &mgr->lock);
 1473     }
 1474     UNLOCK(&mgr->lock);
 1475 }
 1476 
 1477 void
 1478 isc_nm_setstats(isc_nm_t *mgr, isc_stats_t *stats) {
 1479     REQUIRE(VALID_NM(mgr));
 1480     REQUIRE(mgr->stats == NULL);
 1481     REQUIRE(isc_stats_ncounters(stats) == isc_sockstatscounter_max);
 1482 
 1483     isc_stats_attach(stats, &mgr->stats);
 1484 }
 1485 
 1486 void
 1487 isc__nm_incstats(isc_nm_t *mgr, isc_statscounter_t counterid) {
 1488     REQUIRE(VALID_NM(mgr));
 1489     REQUIRE(counterid != -1);
 1490 
 1491     if (mgr->stats != NULL) {
 1492         isc_stats_increment(mgr->stats, counterid);
 1493     }
 1494 }
 1495 
 1496 void
 1497 isc__nm_decstats(isc_nm_t *mgr, isc_statscounter_t counterid) {
 1498     REQUIRE(VALID_NM(mgr));
 1499     REQUIRE(counterid != -1);
 1500 
 1501     if (mgr->stats != NULL) {
 1502         isc_stats_decrement(mgr->stats, counterid);
 1503     }
 1504 }
 1505 
 1506 #define setsockopt_on(socket, level, name) \
 1507     setsockopt(socket, level, name, &(int){ 1 }, sizeof(int))
 1508 
 1509 isc_result_t
 1510 isc__nm_socket_freebind(const uv_handle_t *handle) {
 1511     /*
 1512      * Set the IP_FREEBIND (or equivalent option) on the uv_handle.
 1513      */
 1514     isc_result_t result = ISC_R_SUCCESS;
 1515     uv_os_fd_t fd;
 1516     if (uv_fileno(handle, &fd) != 0) {
 1517         return (ISC_R_FAILURE);
 1518     }
 1519 #ifdef IP_FREEBIND
 1520     if (setsockopt_on(fd, IPPROTO_IP, IP_FREEBIND) == -1) {
 1521         return (ISC_R_FAILURE);
 1522     }
 1523 #elif defined(IP_BINDANY) || defined(IPV6_BINDANY)
 1524     struct sockaddr_in sockfd;
 1525 
 1526     if (getsockname(fd, (struct sockaddr *)&sockfd,
 1527             &(socklen_t){ sizeof(sockfd) }) == -1)
 1528     {
 1529         return (ISC_R_FAILURE);
 1530     }
 1531 #if defined(IP_BINDANY)
 1532     if (sockfd.sin_family == AF_INET) {
 1533         if (setsockopt_on(fd, IPPROTO_IP, IP_BINDANY) == -1) {
 1534             return (ISC_R_FAILURE);
 1535         }
 1536     }
 1537 #endif
 1538 #if defined(IPV6_BINDANY)
 1539     if (sockfd.sin_family == AF_INET6) {
 1540         if (setsockopt_on(fd, IPPROTO_IPV6, IPV6_BINDANY) == -1) {
 1541             return (ISC_R_FAILURE);
 1542         }
 1543     }
 1544 #endif
 1545 #elif defined(SO_BINDANY)
 1546     if (setsockopt_on(fd, SOL_SOCKET, SO_BINDANY) == -1) {
 1547         return (ISC_R_FAILURE);
 1548     }
 1549 #else
 1550     UNUSED(handle);
 1551     UNUSED(fd);
 1552     result = ISC_R_NOTIMPLEMENTED;
 1553 #endif
 1554     return (result);
 1555 }