diff --git a/lib/dns/include/dns/qp.h b/lib/dns/include/dns/qp.h index a860c5a901..09da5ad1a5 100644 --- a/lib/dns/include/dns/qp.h +++ b/lib/dns/include/dns/qp.h @@ -53,6 +53,12 @@ * lifetime of a `dns_qpread_t`, instead of using locks. Readers are * not blocked by any write activity, and vice versa. * + * For read-only access outside the scope of a loop, such as from an + * isc_work callback, `dns_qpmulti_lockedread()`. This looks like a + * query transaction; the difference is that a locked read transaction + * takes the `dns_qpmulti_t` mutex. When you have finished with a + * `dns_qpread_t`, call `dns_qpread_destroy()` to release the mutex. + * * For reads that need a stable view of the trie for multiple cycles * of an isc_loop, or which can be used from any thread, call * `dns_qpmulti_snapshot()` to get a `dns_qpsnap_t`. A snapshot is for @@ -598,10 +604,27 @@ dns_qpmulti_query(dns_qpmulti_t *multi, dns_qpread_t *qpr); * \li `qpr` is a valid read-only qp-trie handle */ +void +dns_qpmulti_lockedread(dns_qpmulti_t *multi, dns_qpread_t *qpr); +/*%< + * Start a read-only transaction that takes the `dns_qpmulti_t` mutex. + * + * The `dns_qpmulti_lockedread()` function must NOT be called from an + * isc_loop thread. We keep query and read transactions separate to + * avoid accidentally taking or failing to take the mutex. + * + * Requires: + * \li `multi` is a pointer to a valid multi-threaded qp-trie + * \li `qpr != NULL` + * + * Returns: + * \li `qpr` is a valid read-only qp-trie handle + */ + void dns_qpread_destroy(dns_qpmulti_t *multi, dns_qpread_t *qpr); /*%< - * End a lightweight read transaction. + * End a lightweight query or read transaction. * * Requires: * \li `multi` is a pointer to a valid multi-threaded qp-trie diff --git a/lib/dns/qp.c b/lib/dns/qp.c index c6bfa74953..bec2ef8485 100644 --- a/lib/dns/qp.c +++ b/lib/dns/qp.c @@ -1282,12 +1282,31 @@ dns_qpmulti_query(dns_qpmulti_t *multi, dns_qpread_t *qp) { REQUIRE(QPMULTI_VALID(multi)); REQUIRE(qp != NULL); - dns_qpmulti_t *whence = reader_open(multi, qp); - INSIST(whence == multi); - - /* we must be in an isc_loop thread */ + /* we MUST be in an isc_loop thread */ qp->tid = isc_tid(); REQUIRE(qp->tid != ISC_TID_UNKNOWN); + + dns_qpmulti_t *whence = reader_open(multi, qp); + INSIST(whence == multi); +} + +/* + * a locked read takes the mutex + */ + +void +dns_qpmulti_lockedread(dns_qpmulti_t *multi, dns_qpread_t *qp) { + REQUIRE(QPMULTI_VALID(multi)); + REQUIRE(qp != NULL); + + /* we MUST NOT be in an isc_loop thread */ + qp->tid = isc_tid(); + REQUIRE(qp->tid == ISC_TID_UNKNOWN); + + LOCK(&multi->mutex); + + dns_qpmulti_t *whence = reader_open(multi, qp); + INSIST(whence == multi); } void @@ -1295,6 +1314,9 @@ dns_qpread_destroy(dns_qpmulti_t *multi, dns_qpread_t *qp) { REQUIRE(QPMULTI_VALID(multi)); REQUIRE(QP_VALID(qp)); REQUIRE(qp->tid == isc_tid()); + if (qp->tid == ISC_TID_UNKNOWN) { + UNLOCK(&multi->mutex); + } *qp = (dns_qpread_t){}; }