]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Support for off-loop read-ony qp-trie transactions
authorTony Finch <fanf@isc.org>
Thu, 2 Mar 2023 13:30:24 +0000 (13:30 +0000)
committerTony Finch <fanf@isc.org>
Wed, 5 Apr 2023 11:35:04 +0000 (12:35 +0100)
It is sometimes necessary to access a qp-trie outside an isc_loop,
such as in tests or an isc_work callback. The best option was to use
a `dns_qpmulti_write()` transaction, but that has overheads that are
not necessary for read-only access, such as committing a new version
of the trie even when nothing changed.

So this commit adds a `dns_qpmulti_read()` transaction, which is
nearly as lightweight as a query transaction, but it takes the mutex
like a write transaction.

lib/dns/include/dns/qp.h
lib/dns/qp.c

index a860c5a901b494bc77f92d1acce740514e21ad66..09da5ad1a53ac92deb7a09fb5edec7e4762c534d 100644 (file)
  * 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
index c6bfa7495322dd0be41c29972232c8013957dd7f..bec2ef84850e51e902f5998cca38f0e74731821e 100644 (file)
@@ -1282,12 +1282,31 @@ dns_qpmulti_query(dns_qpmulti_t *multi, dns_qpread_t *qp) {
        REQUIRE(QPMULTI_VALID(multi));
        REQUIRE(qp != NULL);
 
+       /* 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
+ */
 
-       /* we must be in an isc_loop thread */
+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);
+       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){};
 }