Incorporate changes from Dasynq upstream.
authorDavin McCall <davmac@davmac.org>
Fri, 23 Jun 2017 08:56:53 +0000 (09:56 +0100)
committerDavin McCall <davmac@davmac.org>
Fri, 23 Jun 2017 09:26:56 +0000 (10:26 +0100)
src/dasynq/dasynq-btree_set.h [new file with mode: 0644]
src/dasynq/dasynq-childproc.h
src/dasynq/dasynq-timerbase.h
src/dasynq/dasynq.h

diff --git a/src/dasynq/dasynq-btree_set.h b/src/dasynq/dasynq-btree_set.h
new file mode 100644 (file)
index 0000000..80885e9
--- /dev/null
@@ -0,0 +1,590 @@
+#ifndef DASYNQ_BTREE_SET_H
+#define DASYNQ_BTREE_SET_H
+
+#include <vector>
+#include <functional>
+
+namespace dasynq {
+
+// A sorted set based on a B-Tree data structure, supporting pre-allocation of nodes.
+
+template <typename T, typename P, typename Compare = std::less<P>, int N = 8>
+class btree_set
+{
+    struct HeapNode;
+
+    public:
+    using handle_t = HeapNode;
+    using handle_t_r = HeapNode &;
+
+    private:
+
+    struct SeptNode
+    {
+        P prio[N];
+        handle_t * hn_p[N];  // pointer to handle
+        SeptNode * children[N + 1];
+        SeptNode * parent;
+
+        SeptNode()
+        {
+            // Do nothing; initialisation will be run later
+        }
+
+        void init()
+        {
+            for (int i = 0; i < N; i++) {
+                hn_p[i] = nullptr;
+                children[i] = nullptr;
+            }
+            children[N] = nullptr;
+            parent = nullptr;
+        }
+
+        int num_vals() noexcept
+        {
+            // We expect to be >50% full, so count backwards:
+            for (int i = N - 1; i >= 0; i--) {
+                if (hn_p[i] != nullptr) {
+                    return i + 1;
+                }
+            }
+            return 0;
+        }
+
+        bool is_leaf() noexcept
+        {
+            return children[0] == nullptr;
+        }
+
+        void shift_elems_left(int pos, int newpos, int num)
+        {
+            int diff = pos - newpos;
+            int end = pos + num;
+
+            for (int i = pos; i < end; i++) {
+                prio[i - diff] = prio[i];
+                hn_p[i - diff] = hn_p[i];
+                children[i - diff] = children[i];
+            }
+            children[end - diff] = children[end];
+        }
+
+        void shift_elems_right(int pos, int newpos, int num)
+        {
+            int diff = newpos - pos;
+            int end = pos + num;
+
+            children[end + diff] = children[end];
+            for (int i = (end - 1); i >= pos; i--) {
+                prio[i + diff] = prio[i];
+                hn_p[i + diff] = hn_p[i];
+                children[i + diff] = children[i];
+            }
+        }
+    };
+
+    struct HeapNode
+    {
+        T data; // TODO this should be obscured to avoid early construction
+        SeptNode * parent = nullptr;
+
+        HeapNode()
+        {
+
+        }
+
+        template <typename ...U> HeapNode(U... u) : data(u...)
+        {
+            parent = 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.
+
+    // 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;
+    //        at N+1 nodes, three sept nodes are needed: a root and two leaves;
+    //     for every N/2 nodes thereafter, an additional sept node may be required.
+    // A simple approximation is, s = (n * 2 + N - 1) / N.
+    // (Actually we get away with much less, if nodes have the same priority, since they are
+    // then linked in list and effectively become a single node).
+
+    void alloc_slot()
+    {
+        num_alloced++;
+
+        if (__builtin_expect(num_alloced == next_sept, 0)) {
+            if (++num_septs_needed > num_septs) {
+                try {
+                    SeptNode *new_res = new SeptNode();
+                    new_res->parent = sn_reserve;
+                    sn_reserve = new_res;
+                    num_septs++;
+                }
+                catch (...) {
+                    num_septs_needed--;
+                    num_alloced--;
+                    throw;
+                }
+            }
+            next_sept += N/2;
+        }
+    }
+
+    SeptNode * alloc_sept()
+    {
+        SeptNode * r = sn_reserve;
+        sn_reserve = r->parent;
+        r->init();
+        return r;
+    }
+
+    void release_sept(SeptNode *s)
+    {
+        s->parent = sn_reserve;
+        sn_reserve = s;
+    }
+
+    // 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
+    {
+        int lchildren = lsibling->num_vals();
+        lsibling->hn_p[lchildren] = lsibling->parent->hn_p[index];
+        lsibling->prio[lchildren] = lsibling->parent->prio[index];
+        lsibling->hn_p[lchildren]->parent = lsibling;
+        lchildren++;
+
+        // bool leaf = lsibling->is_leaf();
+
+        int ri = 0;
+        for (ri = 0; rsibling->hn_p[ri] != nullptr; ri++) {
+            lsibling->hn_p[lchildren] = rsibling->hn_p[ri];
+            lsibling->prio[lchildren] = rsibling->prio[ri];
+            lsibling->children[lchildren] = rsibling->children[ri];
+            if (lsibling->children[lchildren]) lsibling->children[lchildren]->parent = lsibling;
+            lsibling->hn_p[lchildren]->parent = lsibling;
+            lchildren++;
+        }
+        lsibling->children[lchildren] = rsibling->children[ri];
+        if (lsibling->children[lchildren]) lsibling->children[lchildren]->parent = lsibling;
+        release_sept(rsibling);
+
+        // Now delete in the parent:
+        for (int i = index; i < (N-1); i++) {
+            lsibling->parent->hn_p[i] = lsibling->parent->hn_p[i + 1];
+            lsibling->parent->prio[i] = lsibling->parent->prio[i + 1];
+            lsibling->parent->children[i + 1] = lsibling->parent->children[i + 2];
+        }
+        lsibling->parent->hn_p[N-1] = nullptr;
+
+        if (lsibling->parent->hn_p[0] == nullptr) {
+            // parent is now empty; it must be root. Make us the new root.
+            release_sept(lsibling->parent);
+            root_sept = lsibling;
+            lsibling->parent = nullptr;
+        }
+    }
+
+    // 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
+    {
+        start:
+        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) {
+                root_sept = nullptr;
+                left_sept = nullptr;
+                release_sept(sept);
+            }
+            return;
+        }
+
+        // Find a suitable sibling to the left or right:
+        if (parent->children[0] == sept) {
+            // take right sibling
+            SeptNode *rsibling = parent->children[1];
+            if (rsibling->num_vals() + children + 1 <= N) {
+                // We can merge
+                merge(sept, rsibling, 0);
+                if (sept->parent != nullptr) {
+                    children = sept->parent->num_vals();
+                    if (children < N/2) {
+                        sept = sept->parent;
+                        goto start;
+                    }
+                }
+            }
+            else {
+                sept->hn_p[children] = parent->hn_p[0];
+                sept->prio[children] = parent->prio[0];
+                sept->hn_p[children]->parent = sept;
+                sept->children[children + 1] = rsibling->children[0];
+                if (sept->children[children + 1]) sept->children[children + 1]->parent = sept;
+
+                parent->hn_p[0] = rsibling->hn_p[0];
+                parent->prio[0] = rsibling->prio[0];
+                parent->hn_p[0]->parent = parent;
+
+                rsibling->shift_elems_left(1, 0, N-1);
+                rsibling->hn_p[N-1] = nullptr;
+                return;
+            }
+        }
+        else {
+            // find left sibling
+            int i;
+            for (i = 1; i < N; i++) {
+                if (parent->children[i] == sept) {
+                    break;
+                }
+            }
+
+            SeptNode *lsibling = parent->children[i-1];
+            int lchildren = lsibling->num_vals();
+            if (lchildren + children + 1 <= N) {
+                // merge
+                merge(lsibling, sept, i - 1);
+                if (lsibling->parent != nullptr) {
+                    children = lsibling->parent->num_vals();
+                    if (children < N/2) {
+                        sept = lsibling->parent;
+                        goto start;
+                    }
+                }
+            }
+            else {
+                sept->shift_elems_right(0, 1, children);
+
+                sept->hn_p[0] = parent->hn_p[i - 1];
+                sept->prio[0] = parent->prio[i - 1];
+                sept->hn_p[0]->parent = sept;
+                sept->children[0] = lsibling->children[lchildren];
+                if (sept->children[0]) sept->children[0]->parent = sept;
+
+                parent->hn_p[i - 1] = lsibling->hn_p[lchildren - 1];
+                parent->prio[i - 1] = lsibling->prio[lchildren - 1];
+                parent->hn_p[i - 1]->parent = parent;
+                lsibling->hn_p[lchildren - 1] = nullptr;
+
+                return;
+            }
+        }
+    }
+
+    public:
+
+    T & node_data(handle_t & hn) noexcept
+    {
+        return hn.data;
+    }
+
+    static void init_handle(handle_t &hn) noexcept
+    {
+        // nothing to do
+    }
+
+    // Allocate a slot, but do not incorporate into the heap:
+    template <typename ...U> void allocate(handle_t &hndl, 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...);
+    }
+
+    void deallocate(handle_t & hn) noexcept
+    {
+        // hn.HeapNode::~HeapNode();
+        num_alloced--;
+
+        // Potentially release reserved sept node
+        if (__builtin_expect(num_alloced < next_sept - N/2, 0)) {
+            next_sept -= N/2;
+            num_septs_needed--;
+            if (num_septs_needed < num_septs - 1) {
+                // Note the "-1" margin is to alleviate bouncing allocation/deallocation
+                SeptNode * r = sn_reserve;
+                sn_reserve = r->parent;
+                delete r;
+                num_septs--;
+            }
+        }
+    }
+
+    // Insert an allocated slot into the heap.
+    // Return true if it is the leftmost value.
+    bool insert(handle_t & hndl, P pval = P()) noexcept
+    {
+        if (root_sept == nullptr) {
+            root_sept = alloc_sept();
+            left_sept = root_sept;
+        }
+
+        SeptNode * srch_sept = root_sept;
+
+        bool leftmost = true;
+
+        while (! srch_sept->is_leaf()) {
+            int min = 0;
+            int max = N - 1;
+            while (min <= max) {
+                int i = (min + max) / 2;
+
+                if (srch_sept->hn_p[i] == nullptr || pval < srch_sept->prio[i]) {
+                    max = i - 1;
+                }
+                else if (srch_sept->prio[i] == pval) {
+                    // Already present?
+                    return false;
+                }
+                else {
+                    min = i + 1;
+                }
+            }
+
+            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
+        int children = srch_sept->num_vals();
+
+        {
+            int min = 0;
+            int max = children - 1;
+            while (min <= max) {
+                int i = (min + max) / 2;
+
+                if (srch_sept->hn_p[i] == nullptr || pval < srch_sept->prio[i]) {
+                    max = i - 1;
+                }
+                else if (srch_sept->prio[i] == pval) {
+                    // Already present?
+                    return false;
+                }
+                else {
+                    min = i + 1;
+                }
+            }
+        }
+
+        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();
+            new_sibling->parent = srch_sept->parent;
+
+            // create new sibling to the right:
+            for (int i = N/2; i < N; i++) {
+                new_sibling->prio[i - N/2] = srch_sept->prio[i];  // new[0] = old[4]
+                new_sibling->hn_p[i - N/2] = srch_sept->hn_p[i];
+                new_sibling->children[i - N/2 + 1] = srch_sept->children[i + 1];
+                if (new_sibling->children[i - N/2 + 1]) new_sibling->children[i - N/2 + 1]->parent = new_sibling;
+                new_sibling->hn_p[i - N/2]->parent = new_sibling;
+                srch_sept->hn_p[i] = nullptr;
+            }
+            // Note that new_sibling->children[0] has not yet been set.
+
+            if (pval < srch_sept->prio[N/2 - 1])  {
+                auto o_prio = srch_sept->prio[N/2 - 1];
+                auto o_hidx = srch_sept->hn_p[N/2 - 1];
+
+                new_sibling->children[0] = srch_sept->children[N/2];
+                if (new_sibling->children[0]) new_sibling->children[0]->parent = new_sibling;
+
+                int i = N/2 - 1;
+                for ( ; i > 0 && pval < srch_sept->prio[i - 1]; i--) {
+                    srch_sept->prio[i] = srch_sept->prio[i - 1];
+                    srch_sept->children[i+1] = srch_sept->children[i];
+                    srch_sept->hn_p[i] = srch_sept->hn_p[i - 1];
+                }
+                srch_sept->prio[i] = pval;
+                srch_sept->hn_p[i] = hndl_p;
+                hndl_p->parent = srch_sept;
+                srch_sept->children[i] = left_down;
+                srch_sept->children[i+1] = right_down;
+                hndl_p = o_hidx;
+                pval = o_prio;
+            }
+            else if (pval < new_sibling->prio[0]) {
+                // new value is right in the middle
+                srch_sept->children[N/2] = left_down;
+                new_sibling->children[0] = right_down;
+                if (left_down) left_down->parent = srch_sept;
+                if (right_down) right_down->parent = new_sibling;
+            }
+            else {
+                auto o_prio = new_sibling->prio[0];
+                auto o_hidx = new_sibling->hn_p[0];
+                int i = 0;
+                for ( ; i < (N/2 - 1) && new_sibling->prio[i + 1] < pval; i++) {
+                    new_sibling->prio[i] = new_sibling->prio[i + 1];
+                    new_sibling->children[i] = new_sibling->children[i + 1];
+                    new_sibling->hn_p[i] = new_sibling->hn_p[i + 1];
+                }
+                new_sibling->prio[i] = pval;
+                new_sibling->hn_p[i] = hndl_p;
+                hndl_p->parent = new_sibling;
+                new_sibling->children[i] = left_down;
+                new_sibling->children[i+1] = right_down;
+                if (left_down) left_down->parent = new_sibling;
+                if (right_down) right_down->parent = new_sibling;
+                hndl_p = o_hidx;
+                pval = o_prio;
+            }
+
+            left_down = srch_sept;
+            right_down = new_sibling;
+
+            srch_sept = srch_sept->parent;
+            if (srch_sept == nullptr) {
+                // Need new root node:
+                srch_sept = alloc_sept();
+                root_sept = srch_sept;
+                left_down->parent = root_sept;
+                right_down->parent = root_sept;
+                children = 0;
+            }
+            else {
+                children = srch_sept->num_vals();
+            }
+        }
+
+        // Insert into non-full node:
+        int inspos;
+        for (inspos = children; inspos > 0; inspos--) {
+            if (srch_sept->prio[inspos - 1] < pval) {
+                break;
+            }
+
+            srch_sept->prio[inspos] = srch_sept->prio[inspos-1];
+            srch_sept->hn_p[inspos] = srch_sept->hn_p[inspos-1];
+            srch_sept->children[inspos+1] = srch_sept->children[inspos];
+        }
+
+        srch_sept->prio[inspos] = pval;
+        srch_sept->hn_p[inspos] = hndl_p;
+        srch_sept->children[inspos] = left_down;
+        srch_sept->children[inspos+1] = right_down;
+        hndl_p->parent = srch_sept;
+        return leftmost;
+    }
+
+    // Remove a slot from the heap (but don't deallocate it)
+    void remove(handle_t & hndl) noexcept
+    {
+        // we have to remove from the Btree itself
+        // 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;
+
+        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;
+                while (lsrch != nullptr) {
+                    prev = lsrch;
+                    lsrch = lsrch->children[0];
+                }
+
+                if (prev != sept) {
+                    sept->hn_p[i] = prev->hn_p[0];
+                    sept->prio[i] = prev->prio[0];
+                    sept->hn_p[i]->parent = sept;
+                    prev->hn_p[0] = &hndl;
+                    sept = prev;
+                    i = 0;
+                }
+
+                // Now we have:
+                // - sept is a leaf in the BTree
+                // - i is the index of the child to remove from it
+
+                for ( ; i < (N-1); i++) {
+                    sept->hn_p[i] = sept->hn_p[i+1];
+                    sept->prio[i] = sept->prio[i+1];
+                    if (sept->hn_p[i] == nullptr) {
+                        break;
+                    }
+                }
+
+                sept->hn_p[N-1] = nullptr;
+
+                // Now if the node is underpopulated, we need to merge with or
+                // borrow from a sibling
+                if (i < N/2) {
+                    repop_node(sept, i);
+                }
+
+                return;
+            }
+        }
+    }
+
+    handle_t *find(const P &pval)
+    {
+        SeptNode * cursept = root_sept;
+        while (cursept != nullptr) {
+            int i;
+            for (i = 0; i < N && cursept->hn_p[i] != nullptr; i++) {
+                if (cursept->prio[i] == pval) {
+                    return cursept->hn_p[i];
+                }
+                if (cursept->prio[i] > pval) {
+                    break;
+                }
+            }
+            cursept = cursept->children[i];
+        }
+
+        return nullptr;
+    }
+
+    bool is_queued(handle_t & hndl) noexcept
+    {
+        return hndl.parent != nullptr;
+    }
+
+    bool empty() noexcept
+    {
+        return root_sept == nullptr;
+    }
+
+    ~btree_set()
+    {
+        while (sn_reserve != nullptr) {
+            auto *next = sn_reserve->parent;
+            delete sn_reserve;
+            sn_reserve = next;
+        }
+    }
+};
+
+}
+
+#endif
index 3b37e4b49c5c1674a33e80a0b2f7dc7f4356406e..8d364f762b5d828f00c5692aac939bbbc04138ea 100644 (file)
@@ -1,4 +1,5 @@
 #include <signal.h>
+#include "dasynq-btree_set.h"
 
 namespace dasynq {
 
@@ -6,78 +7,63 @@ namespace dasynq {
 // be later added with no danger of allocator exhaustion (bad_alloc).
 class pid_map
 {
-    using pair = std::pair<pid_t, void *>;
-    std::unordered_map<pid_t, void *> base_map;
-    std::vector<pair> backup_vector;
-    
-    // Number of entries in backup_vector that are actually in use (as opposed
-    // to simply reserved):
-    int backup_size = 0;
+    using bmap_t = btree_set<void *, pid_t>;
+    bmap_t b_map;
     
     public:
+    using pid_handle_t = bmap_t::handle_t;
+    
+    // Map entry: present (bool), data (void *)
     using entry = std::pair<bool, void *>;
 
     entry get(pid_t key) noexcept
     {
-        auto it = base_map.find(key);
-        if (it == base_map.end()) {
-            // Not in map; look in vector
-            for (int i = 0; i < backup_size; i++) {
-                if (backup_vector[i].first == key) {
-                    return entry(true, backup_vector[i].second);
-                }
-            }
-        
+        auto it = b_map.find(key);
+        if (it == nullptr) {
             return entry(false, nullptr);
         }
-        
-        return entry(true, it->second);
+        return entry(true, b_map.node_data(*it));
     }
     
-    entry erase(pid_t key) noexcept
+    entry remove(pid_t key) noexcept
     {
-        auto iter = base_map.find(key);
-        if (iter != base_map.end()) {
-            entry r(true, iter->second);
-            base_map.erase(iter);
-            return r;
-        }
-        for (int i = 0; i < backup_size; i++) {
-            if (backup_vector[i].first == key) {
-                entry r(true, backup_vector[i].second);
-                backup_vector.erase(backup_vector.begin() + i);
-                return r;
-            }
+        auto it = b_map.find(key);
+        if (it == nullptr) {
+            return entry(false, nullptr);
         }
-        return entry(false, nullptr);
+        b_map.remove(*it);
+        return entry(true, b_map.node_data(*it));
     }
     
+    void remove(pid_handle_t &hndl)
+    {
+        if (b_map.is_queued(hndl)) {
+            b_map.remove(hndl);
+        }
+    }
+
     // Throws bad_alloc on reservation failure
-    void reserve()
+    void reserve(pid_handle_t &hndl)
     {
-        backup_vector.resize(backup_vector.size() + 1);
+        b_map.allocate(hndl);
     }
     
-    void unreserve() noexcept
+    void unreserve(pid_handle_t &hndl) noexcept
     {
-        backup_vector.resize(backup_vector.size() - 1);
+        b_map.deallocate(hndl);
     }
     
-    void add(pid_t key, void *val) // throws std::bad_alloc
+    void add(pid_handle_t &hndl, pid_t key, void *val) // throws std::bad_alloc
     {
-        base_map[key] = val;
+        reserve(hndl);
+        b_map.node_data(hndl) = val;
+        b_map.insert(hndl, key);
     }
     
-    void add_from_reserve(pid_t key, void *val) noexcept
+    void add_from_reserve(pid_handle_t &hndl, pid_t key, void *val) noexcept
     {
-        try {
-            base_map[key] = val;
-            backup_vector.resize(backup_vector.size() - 1);
-        }
-        catch (std::bad_alloc &) {
-            // We couldn't add into the map, use the reserve:
-            backup_vector[backup_size++] = pair(key, val);
-        }
+        b_map.node_data(hndl) = val;
+        b_map.insert(hndl, key);
     }
 };
 
@@ -90,6 +76,8 @@ inline void sigchld_handler(int signum)
     // hurt in any case).
 }
 
+using pid_watch_handle_t = pid_map::pid_handle_t;
+
 template <class Base> class ChildProcEvents : public Base
 {
     private:
@@ -105,7 +93,7 @@ template <class Base> class ChildProcEvents : public Base
             int status;
             pid_t child;
             while ((child = waitpid(-1, &status, WNOHANG)) > 0) {
-                pid_map::entry ent = child_waiters.erase(child);
+                pid_map::entry ent = child_waiters.remove(child);
                 if (ent.first) {
                     Base::receiveChildStat(child, status, ent.second);
                 }
@@ -118,39 +106,57 @@ template <class Base> class ChildProcEvents : public Base
     }
     
     public:
-    void reserveChildWatch()
+    void reserveChildWatch(pid_watch_handle_t &handle)
     {
         std::lock_guard<decltype(Base::lock)> guard(Base::lock);
-        child_waiters.reserve();
+        child_waiters.reserve(handle);
     }
     
-    void unreserveChildWatch() noexcept
+    void unreserveChildWatch(pid_watch_handle_t &handle) noexcept
     {
         std::lock_guard<decltype(Base::lock)> guard(Base::lock);
-        child_waiters.unreserve();
+        unreserveChildWatch_nolock(handle);
     }
     
-    void addChildWatch(pid_t child, void *val)
+    void unreserveChildWatch_nolock(pid_watch_handle_t &handle) noexcept
+    {
+        child_waiters.unreserve(handle);
+    }
+
+    void addChildWatch(pid_watch_handle_t &handle, pid_t child, void *val)
     {
         std::lock_guard<decltype(Base::lock)> guard(Base::lock);
-        child_waiters.add(child, val);
+        child_waiters.add(handle, child, val);
     }
     
-    void addReservedChildWatch(pid_t child, void *val) noexcept
+    void addReservedChildWatch(pid_watch_handle_t &handle, pid_t child, void *val) noexcept
     {
         std::lock_guard<decltype(Base::lock)> guard(Base::lock);
-        child_waiters.add_from_reserve(child, val);
+        child_waiters.add_from_reserve(handle, child, val);
     }
 
-    void addReservedChildWatch_nolock(pid_t child, void *val) noexcept
+    void addReservedChildWatch_nolock(pid_watch_handle_t &handle, pid_t child, void *val) noexcept
     {
-        child_waiters.add_from_reserve(child, val);
+        child_waiters.add_from_reserve(handle, child, val);
     }
     
-    void removeChildWatch(pid_t child) noexcept
+    // Stop watching a child, but retain watch reservation
+    void stop_child_watch(pid_watch_handle_t &handle) noexcept
+    {
+        std::lock_guard<decltype(Base::lock)> guard(Base::lock);
+        child_waiters.remove(handle);
+    }
+
+    void removeChildWatch(pid_watch_handle_t &handle) noexcept
     {
         std::lock_guard<decltype(Base::lock)> guard(Base::lock);
-        child_waiters.erase(child);
+        removeChildWatch_nolock(handle);
+    }
+
+    void removeChildWatch_nolock(pid_watch_handle_t &handle) noexcept
+    {
+        child_waiters.remove(handle);
+        child_waiters.unreserve(handle);
     }
     
     template <typename T> void init(T *loop_mech)
index b45ecb0f9e456d8bb98f095bb16942d590d661e0..87ae20e1ac61c32be7b0252a812d18a0f8e870a8 100644 (file)
@@ -99,10 +99,16 @@ inline bool operator==(const time_val &t1, const time_val &t2)
     return (t1.seconds() == t2.seconds() && t1.nseconds() == t2.nseconds());
 }
 
-using std::rel_ops::operator !=;
-using std::rel_ops::operator <=;
-using std::rel_ops::operator >;
-using std::rel_ops::operator >=;
+inline bool operator<=(const time_val &t1, const time_val &t2)
+{
+    if (t1.seconds() < t2.seconds()) return true;
+    if (t1.seconds() == t2.seconds() && t1.nseconds() <= t2.nseconds()) return true;
+    return false;
+}
+
+inline bool operator!=(const time_val &t1, const time_val &t2) { return !(t1 == t2); }
+inline bool operator>(const time_val &t1, const time_val &t2) { return t2 < t1; }
+inline bool operator>=(const time_val &t1, const time_val &t2) { return t2 <= t1; }
 
 // Data corresponding to a single timer
 class timer_data
index 746fae6efd1dd82171dd2fc0fedd344373f03e37..bff5a0d8db61b6a226ff3088727cabc74fc988f1 100644 (file)
@@ -294,6 +294,7 @@ namespace dprivate {
         template <typename, template <typename> class, typename> friend class dasynq::event_loop;
         
         protected:
+        pid_watch_handle_t watch_handle;
         pid_t watch_pid;
         int child_status;
         
@@ -890,7 +891,7 @@ class event_loop
     {
         loop_mech.prepare_watcher(callback);
         try {
-            loop_mech.reserveChildWatch();
+            loop_mech.reserveChildWatch(callback->watch_handle);
         }
         catch (...) {
             loop_mech.release_watcher(callback);
@@ -900,7 +901,7 @@ class event_loop
     
     void unreserve(BaseChildWatcher *callback) noexcept
     {
-        loop_mech.unreserveChildWatch();
+        loop_mech.unreserveChildWatch(callback->watch_handle);
         loop_mech.release_watcher(callback);
     }
     
@@ -908,7 +909,7 @@ class event_loop
     {
         loop_mech.prepare_watcher(callback);
         try {
-            loop_mech.addChildWatch(child, callback);
+            loop_mech.addChildWatch(callback->watch_handle, child, callback);
         }
         catch (...) {
             loop_mech.release_watcher(callback);
@@ -916,19 +917,19 @@ class event_loop
         }
     }
     
-    void registerReservedChild(BaseChildWatcher *callBack, pid_t child) noexcept
+    void registerReservedChild(BaseChildWatcher *callback, pid_t child) noexcept
     {
-        loop_mech.addReservedChildWatch(child, callBack);
+        loop_mech.addReservedChildWatch(callback->watch_handle, child, callback);
     }
 
-    void registerReservedChild_nolock(BaseChildWatcher *callBack, pid_t child) noexcept
+    void registerReservedChild_nolock(BaseChildWatcher *callback, pid_t child) noexcept
     {
-        loop_mech.addReservedChildWatch_nolock(child, callBack);
+        loop_mech.addReservedChildWatch_nolock(callback->watch_handle, child, callback);
     }
     
     void deregister(BaseChildWatcher *callback, pid_t child) noexcept
     {
-        loop_mech.removeChildWatch(child);
+        loop_mech.removeChildWatch(callback->watch_handle);
 
         waitqueue_node<T_Mutex> qnode;
         get_attn_lock(qnode);
@@ -939,6 +940,13 @@ class event_loop
         release_lock(qnode);
     }
     
+    // Stop watching a child process, but retain watch reservation so that another child can be
+    // watched without running into resource allocation issues.
+    void stop_watch(BaseChildWatcher *callback) noexcept
+    {
+        loop_mech.stop_child_watch(callback->watch_handle);
+    }
+
     void registerTimer(BaseTimerWatcher *callback, clock_type clock)
     {
         loop_mech.prepare_watcher(callback);
@@ -1260,6 +1268,13 @@ class event_loop
         return rearmType;
     }
     
+    void process_child_watch_rearm(BaseChildWatcher *bcw, rearm rearm_type) noexcept
+    {
+        if (rearm_type == rearm::REMOVE || rearm_type == rearm::DISARM) {
+            loop_mech.unreserveChildWatch_nolock(bcw->watch_handle);
+        }
+    }
+
     void processTimerRearm(BaseTimerWatcher *btw, rearm rearmType) noexcept
     {
         // Called with lock held
@@ -1902,6 +1917,12 @@ class child_proc_watcher : private dprivate::base_child_watcher<typename EventLo
         eloop.deregister(this, child);
     }
     
+    // Stop watching the currently watched child, but retain watch reservation.
+    void stop_watch(EventLoop &eloop) noexcept
+    {
+        eloop.stop_watch(this);
+    }
+
     // Fork and watch the child with this watcher on the given event loop.
     // If resource limitations prevent the child process from being watched, it is
     // terminated immediately (or if the implementation allows, never started),
@@ -2015,6 +2036,8 @@ class child_proc_watcher_impl : public child_proc_watcher<EventLoop>
                 rearmType = rearm::REMOVE;
             }
 
+            loop.process_child_watch_rearm(this, rearmType);
+
             // rearmType = loop.process??;
             post_dispatch(loop, this, rearmType);
         }