Updated bundled Dasynq to 1.1.5.
authorDavin McCall <davmac@davmac.org>
Fri, 14 Sep 2018 15:54:01 +0000 (16:54 +0100)
committerDavin McCall <davmac@davmac.org>
Fri, 14 Sep 2018 15:54:01 +0000 (16:54 +0100)
README.md
src/dasynq/dasynq-basewatchers.h
src/dasynq/dasynq-btree_set.h
src/dasynq/dasynq-daryheap.h
src/dasynq/dasynq-kqueue.h
src/dasynq/dasynq.h

index 6c295dc6ec425f442916217e3824d37797a7a4cb..41c2400df4e41e27d18af97c54d858bfe3267aa3 100644 (file)
--- a/README.md
+++ b/README.md
@@ -36,7 +36,10 @@ down and restarting the system.
 
 Dinit is designed to work on POSIXy operating systems such as Linux and
 OpenBSD. It is written in C++ and uses the [Dasynq](http://davmac.org/projects/dasynq/)
-event handling library, which was written especially to support Dinit.
+event handling library, which was written especially to support Dinit. (Note
+that a copy of Dasynq is bundled with Dinit, so a separate copy is not
+required for compilation; however, the bundled copy does not include the
+documentation or test suite).
 
 Development goals include clean design, robustness, portability, and
 avoiding feature bloat (whilst still handling a variety of use cases).
index fc5f6f57189b8276e77b98aad01779999528ce18..cc8dc7f29c30e0cfbef891413ada0dc47a239535 100644 (file)
@@ -6,6 +6,8 @@
 // In general access to the members of the basewatcher should be protected by a mutex. The
 // event_dispatch lock is used for this purpose.
 
+#include <type_traits>
+
 namespace dasynq {
 
 namespace dprivate {
@@ -51,8 +53,31 @@ namespace dprivate {
     // (non-public API)
 
     class base_watcher;
+
+    class empty_node
+    {
+        DASYNQ_EMPTY_BODY
+    };
+
+    // heap_def decides the queue implementation that we use. It must be stable:
     template <typename A, typename B, typename C> using dary_heap_def = dary_heap<A,B,C>;
-    using prio_queue = stable_heap<dary_heap_def, dprivate::base_watcher *, int>;
+    template <typename A, typename B> using heap_def = stable_heap<dary_heap_def,A,B>;
+
+    namespace {
+        // use empty handles (not containing basewatcher *) if the handles returned from the
+        // queue are handle references, because we can derive a pointer to the containing basewatcher
+        // via the address of the handle in that case:
+        constexpr bool use_empty_node = std::is_same<
+                typename heap_def<empty_node, int>::handle_t_r,
+                typename heap_def<empty_node, int>::handle_t &>::value;
+
+        using node_type = std::conditional<use_empty_node, empty_node, base_watcher *>::type;
+    }
+
+    using prio_queue = heap_def<node_type, int>;
+
+    using prio_queue_emptynode = heap_def<empty_node, int>;
+    using prio_queue_bwnode = heap_def<base_watcher *, int>;
 
     template <typename T_Loop> class fd_watcher;
     template <typename T_Loop> class bidi_fd_watcher;
@@ -138,6 +163,33 @@ namespace dprivate {
         }
     };
 
+    // Retrieve watcher from queue handle:
+    inline base_watcher * get_watcher(prio_queue_emptynode &q, prio_queue_emptynode::handle_t &n)
+    {
+        uintptr_t bptr = (uintptr_t)&n;
+        _Pragma ("GCC diagnostic push")
+        _Pragma ("GCC diagnostic ignored \"-Winvalid-offsetof\"")
+        bptr -= offsetof(base_watcher, heap_handle);
+        _Pragma ("GCC diagnostic pop")
+        return (base_watcher *)bptr;
+    }
+
+    inline dprivate::base_watcher * get_watcher(prio_queue_bwnode &q, prio_queue_bwnode::handle_t &n)
+    {
+        return q.node_data(n);
+    }
+
+    // Allocate queue handle:
+    inline void allocate_handle(prio_queue_emptynode &q, prio_queue_emptynode::handle_t &n, base_watcher *bw)
+    {
+        q.allocate(n);
+    }
+
+    inline void allocate_handle(prio_queue_bwnode &q, prio_queue_bwnode::handle_t &n, base_watcher *bw)
+    {
+        q.allocate(n, bw);
+    }
+
     // Base signal event - not part of public API
     template <typename T_Sigdata>
     class base_signal_watcher : public base_watcher
index 2e2c09043c086a1666e96d561f0810a0fc64e299..bc547a26deb876e6f5f50041bac7425fa12289c2 100644 (file)
@@ -10,22 +10,22 @@ namespace dasynq {
 template <typename T, typename P, typename Compare = std::less<P>, int N = 8>
 class btree_set
 {
-    struct HeapNode;
+    struct heapnode;
 
     public:
-    using handle_t = HeapNode;
-    using handle_t_r = HeapNode &;
+    using handle_t = heapnode;
+    using handle_t_r = heapnode &;
 
     private:
 
-    struct SeptNode
+    struct septnode
     {
         P prio[N];
         handle_t * hn_p[N];  // pointer to handle
-        SeptNode * children[N + 1];
-        SeptNode * parent;
+        septnode * children[N + 1];
+        septnode * parent;
 
-        SeptNode()
+        septnode()
         {
             // Do nothing; initialisation will be run later
         }
@@ -83,30 +83,32 @@ class btree_set
         }
     };
 
-    struct HeapNode
+    struct heapnode
     {
-        T data; // TODO this should be obscured to avoid early construction
-        SeptNode * parent = nullptr;
-
-        HeapNode()
+        union nodedata_u
         {
+            T data; // TODO this should be obscured to avoid early construction
 
-        }
+            nodedata_u() {}
+        };
+
+        nodedata_u nodedata;
+        septnode * parent = nullptr;
 
-        template <typename ...U> HeapNode(U... u) : data(u...)
+        heapnode()
         {
-            parent = nullptr;
+
         }
     };
 
-    SeptNode * root_sept = nullptr; // root of the B-Tree
-    SeptNode * left_sept = nullptr; // leftmost child (cache)
-    SeptNode * sn_reserve = nullptr;
+    septnode * root_sept = nullptr; // root of the B-Tree
+    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.
+    int 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;
@@ -123,7 +125,7 @@ class btree_set
         if (__builtin_expect(num_alloced == next_sept, 0)) {
             if (++num_septs_needed > num_septs) {
                 try {
-                    SeptNode *new_res = new SeptNode();
+                    septnode *new_res = new septnode();
                     new_res->parent = sn_reserve;
                     sn_reserve = new_res;
                     num_septs++;
@@ -138,15 +140,15 @@ class btree_set
         }
     }
 
-    SeptNode * alloc_sept()
+    septnode * alloc_sept()
     {
-        SeptNode * r = sn_reserve;
+        septnode * r = sn_reserve;
         sn_reserve = r->parent;
         r->init();
         return r;
     }
 
-    void release_sept(SeptNode *s)
+    void release_sept(septnode *s)
     {
         s->parent = sn_reserve;
         sn_reserve = s;
@@ -154,7 +156,7 @@ class btree_set
 
     // Merge rsibling, and one value from the parent, into lsibling.
     // Index is the index of the parent value.
-    void merge(SeptNode *lsibling, SeptNode *rsibling, int index) noexcept
+    void merge(septnode *lsibling, septnode *rsibling, int index) noexcept
     {
         int lchildren = lsibling->num_vals();
         lsibling->hn_p[lchildren] = lsibling->parent->hn_p[index];
@@ -195,10 +197,10 @@ class btree_set
 
     // borrow values from, or merge with, a sibling node so that the node
     // is suitably (~>=50%) populated.
-    void repop_node(SeptNode *sept, int children) noexcept
+    void repop_node(septnode *sept, int children) noexcept
     {
         start:
-        SeptNode *parent = sept->parent;
+        septnode *parent = sept->parent;
         if (parent == nullptr) {
             // It's the root node, so don't worry about it, unless empty
             if (sept->hn_p[0] == nullptr) {
@@ -212,7 +214,7 @@ class btree_set
         // Find a suitable sibling to the left or right:
         if (parent->children[0] == sept) {
             // take right sibling
-            SeptNode *rsibling = parent->children[1];
+            septnode *rsibling = parent->children[1];
             if (rsibling->num_vals() + children + 1 <= N) {
                 // We can merge
                 merge(sept, rsibling, 0);
@@ -249,7 +251,7 @@ class btree_set
                 }
             }
 
-            SeptNode *lsibling = parent->children[i-1];
+            septnode *lsibling = parent->children[i-1];
             int lchildren = lsibling->num_vals();
             if (lchildren + children + 1 <= N) {
                 // merge
@@ -285,7 +287,7 @@ class btree_set
 
     T & node_data(handle_t & hn) noexcept
     {
-        return hn.data;
+        return hn.nodedata.data;
     }
 
     static void init_handle(handle_t &hn) noexcept
@@ -294,18 +296,15 @@ class btree_set
     }
 
     // Allocate a slot, but do not incorporate into the heap:
-    template <typename ...U> void allocate(handle_t &hndl, U... u)
+    template <typename ...U> void allocate(handle_t &hn, U... u)
     {
         alloc_slot();
-        // TODO should not really new over an existing object.
-        //      T element in HeapNode should be obscured so we don't need a default-constructed
-        //      T in it.
-        new (& hndl) HeapNode(u...);
+        new (& hn.nodedata.data) T(u...);
     }
 
     void deallocate(handle_t & hn) noexcept
     {
-        // hn.HeapNode::~HeapNode();
+        hn.nodedata.data.T::~T();
         num_alloced--;
 
         // Potentially release reserved sept node
@@ -314,7 +313,7 @@ class btree_set
             num_septs_needed--;
             if (num_septs_needed < num_septs - 1) {
                 // Note the "-1" margin is to alleviate bouncing allocation/deallocation
-                SeptNode * r = sn_reserve;
+                septnode * r = sn_reserve;
                 sn_reserve = r->parent;
                 delete r;
                 num_septs--;
@@ -331,7 +330,7 @@ class btree_set
             left_sept = root_sept;
         }
 
-        SeptNode * srch_sept = root_sept;
+        septnode * srch_sept = root_sept;
 
         bool leftmost = true;
 
@@ -384,15 +383,15 @@ class btree_set
             }
         }
 
-        SeptNode * left_down = nullptr; // left node going down
-        SeptNode * right_down = nullptr; // right node going down
+        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;
 
         while (children == N) {
             // split and push value towards root
-            SeptNode * new_sibling = alloc_sept();
+            septnode * new_sibling = alloc_sept();
             new_sibling->parent = srch_sept->parent;
 
             // create new sibling to the right:
@@ -498,14 +497,14 @@ class btree_set
         // Pull nodes from a child, all the way down
         // the tree. Then re-balance back up the tree,
         // merging nodes if necessary.
-        SeptNode * sept = hndl.parent;
+        septnode * sept = hndl.parent;
 
         int i;
         for (i = 0; i < N; i++) {
             if (sept->hn_p[i] == &hndl) {
                 // Ok, go right, then as far as we can to the left:
-                SeptNode * lsrch = sept->children[i+1];
-                SeptNode * prev = sept;
+                septnode * lsrch = sept->children[i+1];
+                septnode * prev = sept;
                 while (lsrch != nullptr) {
                     prev = lsrch;
                     lsrch = lsrch->children[0];
@@ -547,7 +546,7 @@ class btree_set
 
     handle_t *find(const P &pval)
     {
-        SeptNode * cursept = root_sept;
+        septnode * cursept = root_sept;
         while (cursept != nullptr) {
             int i;
             for (i = 0; i < N && cursept->hn_p[i] != nullptr; i++) {
index 9bb61ca16a48d7d8b4a7b583b6938724699138e1..bf98c7d788ccc8f79138d9039259b72c2d959a02 100644 (file)
@@ -67,6 +67,8 @@ class dary_heap
         hindex_t heap_index;
 
         handle_t(const handle_t &) = delete;
+        void operator=(const handle_t &) = delete;
+
         handle_t() { }
     };
 
index 0b1c0e5025d11db2e3eec1f8867a29c71f6f823c..9d5f46c43f5328ec1b09f54f0376d00f4a8465bb 100644 (file)
@@ -100,35 +100,29 @@ class kqueue_traits
     constexpr static bool supports_non_oneshot_fd = false;
 };
 
+namespace dprivate {
+namespace dkqueue {
+
 #if _POSIX_REALTIME_SIGNALS > 0
+
 static inline void prepare_signal(int signo) { }
 static inline void unprep_signal(int signo) { }
+// get_siginfo is not required in this case.
 
-inline bool get_siginfo(int signo, siginfo_t *siginfo)
-{
-    struct timespec timeout;
-    timeout.tv_sec = 0;
-    timeout.tv_nsec = 0;
-
-    sigset_t mask;
-    sigemptyset(&mask);
-    sigaddset(&mask, signo);
-    return (sigtimedwait(&mask, siginfo, &timeout) != -1);
-}
 #else
 
 // If we have no sigtimedwait implementation, we have to retrieve signal data by establishing a
 // signal handler.
 
 // We need to declare and define a non-static data variable, "siginfo_p", in this header, without
-// violating the "one definition rule". The only way to do that is via a template, even though we
-// don't otherwise need a template here:
+// violating the "one definition rule". The only way to do that (before C++17) is via a template,
+// even though we don't otherwise need a template here:
 template <typename T = decltype(nullptr)> class sig_capture_templ
 {
     public:
     static siginfo_t * siginfo_p;
 
-    static void signalHandler(int signo, siginfo_t *siginfo, void *v)
+    static void signal_handler(int signo, siginfo_t *siginfo, void *v)
     {
         *siginfo_p = *siginfo;
     }
@@ -140,7 +134,7 @@ using sig_capture = sig_capture_templ<>;
 inline void prepare_signal(int signo)
 {
     struct sigaction the_action;
-    the_action.sa_sigaction = sig_capture::signalHandler;
+    the_action.sa_sigaction = sig_capture::signal_handler;
     the_action.sa_flags = SA_SIGINFO;
     sigfillset(&the_action.sa_mask);
 
@@ -164,6 +158,7 @@ inline bool get_siginfo(int signo, siginfo_t *siginfo)
 }
 
 #endif
+} }  // namespace dprivate :: dkqueue
 
 template <class Base> class kqueue_loop : public Base
 {
@@ -264,7 +259,7 @@ template <class Base> class kqueue_loop : public Base
         sigset_t pending_sigs;
         sigpending(&pending_sigs);
         while (sigismember(&pending_sigs, signo)) {
-            get_siginfo(signo, &siginfo.info);
+            dprivate::dkqueue::get_siginfo(signo, &siginfo.info);
             if (Base::receive_signal(*this, siginfo, userdata)) {
                 enable_filt = false;
                 break;
@@ -487,7 +482,7 @@ template <class Base> class kqueue_loop : public Base
     // Note signal should be masked before call.
     void add_signal_watch_nolock(int signo, void *userdata)
     {
-        prepare_signal(signo);
+        dprivate::dkqueue::prepare_signal(signo);
 
         // We need to register the filter with the kqueue early, to avoid a race where we miss
         // signals:
@@ -526,7 +521,7 @@ template <class Base> class kqueue_loop : public Base
     
     void remove_signal_watch_nolock(int signo) noexcept
     {
-        unprep_signal(signo);
+        dprivate::dkqueue::unprep_signal(signo);
         
         struct kevent evt;
         EV_SET(&evt, signo, EVFILT_SIGNAL, EV_DELETE, 0, 0, 0);
index 97beecdd430fd015c52235a9ef744562f643f91b..e65bad6f6242110ca94a5d96ee4aba6e68e64af3 100644 (file)
@@ -396,7 +396,7 @@ namespace dprivate {
         //   may throw: std::bad_alloc
         void prepare_watcher(base_watcher *bwatcher)
         {
-            event_queue.allocate(bwatcher->heap_handle, bwatcher);
+            allocate_handle(event_queue, bwatcher->heap_handle, bwatcher);
         }
         
         void queue_watcher(base_watcher *bwatcher) noexcept
@@ -509,7 +509,7 @@ namespace dprivate {
             }
             
             auto & rhndl = event_queue.get_root();
-            base_watcher *r = event_queue.node_data(rhndl);
+            base_watcher *r = dprivate::get_watcher(event_queue, rhndl);
             event_queue.pull_root();
             return r;
         }
@@ -1326,8 +1326,9 @@ class event_loop
             // (Above variables are initialised only to silence compiler warnings).
             
             if (pqueue->watchType == watch_type_t::SECONDARYFD) {
-                // construct a pointer to the main watcher (using char* arithmetic):
-                char * rp = (char *)pqueue;
+                // construct a pointer to the main watcher, using integer arithmetic to avoid undefined
+                // pointer arithmetic:
+                uintptr_t rp = (uintptr_t)pqueue;
 
                 // Here we take the offset of a member from a non-standard-layout class, which is
                 // specified to have undefined result by the C++ language standard, but which