Browse Source

Upgrade bundled Dasynq to 1.2.4 (current release)

Davin McCall 7 months ago
parent
commit
ece704f51d

+ 25 - 20
dasynq/include/dasynq/btree_set.h

@@ -3,6 +3,9 @@
 
 #include <functional>
 #include <utility>
+#include <cstddef>
+
+#include "config.h"
 
 namespace dasynq {
 
@@ -106,10 +109,10 @@ class btree_set
     septnode * left_sept = nullptr; // leftmost child (cache)
     septnode * sn_reserve = nullptr;
 
-    int num_alloced = 0;
-    int num_septs = 0;
-    int num_septs_needed = 0;
-    int next_sept = 1;  // next num_allocd for which we need another septnode in reserve.
+    size_t num_alloced = 0;
+    unsigned num_septs = 0;
+    unsigned num_septs_needed = 0;
+    unsigned next_sept = 1;  // next num_allocd for which we need another septnode in reserve.
 
     // Note that sept nodes are always at least half full, except for the root sept node.
     // For up to N nodes, one sept node is needed;
@@ -122,8 +125,10 @@ class btree_set
     void alloc_slot()
     {
         num_alloced++;
+        // No need to be concerned for overflow in num_alloced: if we've maxed out size_t then
+        // allocation would have failed much earlier on.
 
-        if (__builtin_expect(num_alloced == next_sept, 0)) {
+        if (DASYNQ_EXPECT(num_alloced == next_sept, 0)) {
             if (++num_septs_needed > num_septs) {
                 try {
                     septnode *new_res = new septnode();
@@ -137,6 +142,12 @@ class btree_set
                     throw;
                 }
             }
+            if (num_septs_needed == 0) {
+                // wrapped around
+                num_septs_needed--;
+                num_alloced--;
+                throw std::bad_alloc();
+            }
             next_sept += N/2;
         }
     }
@@ -309,7 +320,7 @@ class btree_set
         num_alloced--;
 
         // Potentially release reserved sept node
-        if (__builtin_expect(num_alloced < next_sept - N/2, 0)) {
+        if (DASYNQ_EXPECT(num_alloced < next_sept - N/2, 0)) {
             next_sept -= N/2;
             num_septs_needed--;
             if (num_septs_needed < num_septs - 1) {
@@ -322,8 +333,9 @@ class btree_set
         }
     }
 
-    // Insert an allocated slot into the heap.
-    // Return true if it is the leftmost value.
+    // Insert an allocated slot, with a unique value, into the set. Does nothing if the value is
+    // already in the set.
+    // Return true if the value was inserted (or false if already present).
     bool insert(handle_t & hndl, P pval = P()) noexcept
     {
         if (root_sept == nullptr) {
@@ -333,8 +345,6 @@ class btree_set
 
         septnode * srch_sept = root_sept;
 
-        bool leftmost = true;
-
         while (! srch_sept->is_leaf()) {
             int min = 0;
             int max = N - 1;
@@ -345,7 +355,7 @@ class btree_set
                     max = i - 1;
                 }
                 else if (srch_sept->prio[i] == pval) {
-                    // Already present?
+                    // Already present
                     return false;
                 }
                 else {
@@ -353,16 +363,12 @@ class btree_set
                 }
             }
 
-            if (min != 0) {
-                leftmost = false;
-            }
-
             // go up to the right:
             srch_sept = srch_sept->children[max + 1];
         }
 
         // We got to a leaf: does it have space?
-        // First check if we can add to a linked list
+        // First check if already present
         int children = srch_sept->num_vals();
 
         {
@@ -371,11 +377,11 @@ class btree_set
             while (min <= max) {
                 int i = (min + max) / 2;
 
-                if (srch_sept->hn_p[i] == nullptr || pval < srch_sept->prio[i]) {
+                if (pval < srch_sept->prio[i]) {
                     max = i - 1;
                 }
                 else if (srch_sept->prio[i] == pval) {
-                    // Already present?
+                    // Already present
                     return false;
                 }
                 else {
@@ -386,7 +392,6 @@ class btree_set
 
         septnode * left_down = nullptr; // left node going down
         septnode * right_down = nullptr; // right node going down
-        leftmost = leftmost && pval < srch_sept->prio[0];
 
         handle_t * hndl_p = &hndl;
 
@@ -488,7 +493,7 @@ class btree_set
         srch_sept->children[inspos] = left_down;
         srch_sept->children[inspos+1] = right_down;
         hndl_p->parent = srch_sept;
-        return leftmost;
+        return true;
     }
 
     // Remove a slot from the heap (but don't deallocate it)

+ 15 - 3
dasynq/include/dasynq/config.h

@@ -28,10 +28,10 @@
 //     #define DASYNQ_HAVE_EVENTFD 1
 //
 // If the pipe2 system call is available:
-//     #define HAVE_PIPE2 1
+//     #define DASYNQ_HAVE_PIPE2 1
 //
 // If the pselect system call is available:
-//     #define HAVE_PSELECT 1
+//     #define DASYNQ_HAVE_PSELECT 1
 //
 // A tag to include at the end of a class body for a class which is allowed to have zero size.
 // Normally, C++ mandates that all objects (except empty base subobjects) have non-zero size, but on some
@@ -44,6 +44,9 @@
 // A statement to tell the compiler that the current line of code is unreachable, that is, it will never be
 // the case that program execution flow reaches this statement:
 //     #define DASYNQ_UNREACHABLE /* compiler specific! */
+//
+// A compiler builtin to specify the expected (integral) value of an integral expression:
+//     #define DASYNQ_EXPECT(expr,expected) /* compiler specific! */
 
 // ---------------------------------------------------------------------------------------------------------
 // Part 2: Automatic configuration begins here; you should not need to edit beyond this point.
@@ -85,7 +88,7 @@
 
 // General feature availability
 
-#if (defined(__OpenBSD__) || defined(__linux__)) && ! defined(HAVE_PIPE2)
+#if (defined(__OpenBSD__) || defined(__linux__)) && ! defined(DASYNQ_HAVE_PIPE2)
 #define DASYNQ_HAVE_PIPE2 1
 #endif
 
@@ -104,6 +107,15 @@
 #if ! defined(DASYNQ_UNREACHABLE)
 #define DASYNQ_UNREACHABLE          __builtin_unreachable()
 #endif
+
+#if ! defined(DASYNQ_EXPECT)
+#define DASYNQ_EXPECT(a,b)          __builtin_expect(a,b)
+#endif
+
 #endif /* __GNUC__ */
 
+#if ! defined(DASYNQ_EXPECT)
+#define DASYNQ_EXPECT(a,b)          (a)
+#endif
+
 #endif /* DASYNQ_CONFIG_H_ */

+ 16 - 9
dasynq/include/dasynq/daryheap.h

@@ -203,11 +203,8 @@ class dary_heap
     //  u... : parameters for data constructor T::T(...)
     template <typename ...U> void allocate(handle_t & hnd, U&&... u)
     {
-        new (& hnd.hd_u.hd) T(std::forward<U>(u)...);
-        hnd.heap_index = -1;
-
-        // largest object size is PTRDIFF_MAX, so we expect the largest vector is that / sizeof node:
-        constexpr hindex_t max_allowed = (std::numeric_limits<std::ptrdiff_t>::max() - 1) / sizeof(heap_node);
+        // Note: this can be constexpr in C++20
+        const hindex_t max_allowed = hvec.max_size();
 
         if (num_nodes == max_allowed) {
             throw std::bad_alloc();
@@ -215,20 +212,30 @@ class dary_heap
 
         num_nodes++;
 
-        if (__builtin_expect(hvec.capacity() < num_nodes, 0)) {
+        if (DASYNQ_EXPECT(hvec.capacity() < num_nodes, 0)) {
             hindex_t half_point = max_allowed / 2;
             try {
-                if (__builtin_expect(num_nodes < half_point, 1)) {
+                if (DASYNQ_EXPECT(num_nodes < half_point, 1)) {
                     hvec.reserve(num_nodes * 2);
                 }
                 else {
                     hvec.reserve(max_allowed);
                 }
             }
-            catch (std::bad_alloc &e) {
-                hvec.reserve(num_nodes);
+            catch (...) {
+                // try with just the needed number of nodes:
+                try {
+                    hvec.reserve(num_nodes);
+                }
+                catch (...) {
+                    num_nodes--;
+                    throw;
+                }
             }
         }
+
+        new (& hnd.hd_u.hd) T(std::forward<U>(u)...);
+        hnd.heap_index = -1;
     }
 
     // Deallocate a slot

+ 2 - 1
dasynq/include/dasynq/kqueue.h

@@ -362,7 +362,8 @@ template <class Base> class kqueue_loop : public Base
         EV_SET(&kev, fd, filter, EV_ADD | (enabled ? 0 : EV_DISABLE), fflags, 0, userdata);
         if (kevent(kqfd, &kev, 1, nullptr, 0, nullptr) == -1) {
             // Note that kqueue supports EVFILT_READ on regular file fd's, but not EVFILT_WRITE.
-            // /dev/null apparently gives ENODEV.
+            // Regular files return EINVAL, but /dev/null apparently gives ENODEV (which
+            // documentation doesn't mention).
             if (filter == EVFILT_WRITE && (errno == EINVAL || errno == ENODEV) && emulate) {
                 return false; // emulate
             }

+ 2 - 0
dasynq/include/dasynq/svec.h

@@ -230,6 +230,8 @@ public:
 
     void reserve(size_t amount)
     {
+        // Note that std::vector would throw length_error if amount > max_size(). We don't bother;
+        // allocation will certainly fail in that case, so we'll throw bad_alloc instead.
         if (!ensure_capacity(amount, true)) {
             throw std::bad_alloc();
         }