diff --git a/doc/dev/loopmgr.md b/doc/dev/loopmgr.md index 9c4518154c..15831e6236 100644 --- a/doc/dev/loopmgr.md +++ b/doc/dev/loopmgr.md @@ -104,3 +104,13 @@ functions MUST be called from the thread that created the network manager socket. The ``isc_nm_listen*()`` functions MUST be called from the ``main`` loop. + +The general design of Network Manager is based on callbacks. An extra care must +be taken when implementing new functions because the callbacks MUST be called +asynchronously because the caller might be inside a lock and the same lock must +be acquired in the callback. This doesn't mean that the callback must be always +called asynchronously, because sometimes we are already in the libuv callback +and thus we can just call the callback directly, but in other places, especially +when returning an error, the control hasn't been returned to the caller yet and +in such case, the callback must be scheduled onto the event loop instead of +executing it directly. diff --git a/lib/isc/netmgr/udp.c b/lib/isc/netmgr/udp.c index 9b715d9b6c..587e368203 100644 --- a/lib/isc/netmgr/udp.c +++ b/lib/isc/netmgr/udp.c @@ -685,6 +685,7 @@ isc__nm_udp_send(isc_nmhandle_t *handle, const isc_region_t *region, isc__networker_t *worker = NULL; uint32_t maxudp; int r; + isc_result_t result; REQUIRE(VALID_NMSOCK(sock)); REQUIRE(sock->type == isc_nm_udpsocket); @@ -706,16 +707,6 @@ isc__nm_udp_send(isc_nmhandle_t *handle, const isc_region_t *region, return; } - if (isc__nm_closing(worker)) { - cb(handle, ISC_R_SHUTTINGDOWN, cbarg); - return; - } - - if (isc__nmsocket_closing(sock)) { - cb(handle, ISC_R_CANCELED, cbarg); - return; - } - uvreq = isc__nm_uvreq_get(sock->worker, sock); uvreq->uvbuf.base = (char *)region->base; uvreq->uvbuf.len = region->length; @@ -725,6 +716,16 @@ isc__nm_udp_send(isc_nmhandle_t *handle, const isc_region_t *region, uvreq->cb.send = cb; uvreq->cbarg = cbarg; + if (isc__nm_closing(worker)) { + result = ISC_R_SHUTTINGDOWN; + goto fail; + } + + if (isc__nmsocket_closing(sock)) { + result = ISC_R_CANCELED; + goto fail; + } + /* * We used uv_udp_connect(), so the peer address has to be * set to NULL or else uv_udp_send() could fail or assert, @@ -738,8 +739,12 @@ isc__nm_udp_send(isc_nmhandle_t *handle, const isc_region_t *region, &uvreq->uvbuf, 1, sa, udp_send_cb); if (r < 0) { isc__nm_incstats(sock, STATID_SENDFAIL); - isc__nm_failed_send_cb(sock, uvreq, isc_uverr2result(r)); + result = isc_uverr2result(r); + goto fail; } + return; +fail: + isc__nm_failed_send_cb(sock, uvreq, result); } static isc_result_t