From b82ed1c374100b0cfbe9e31a1f3c28f8ae832680 Mon Sep 17 00:00:00 2001 From: Davin McCall Date: Fri, 23 Jun 2017 09:56:53 +0100 Subject: [PATCH] Incorporate changes from Dasynq upstream. --- src/dasynq/dasynq-btree_set.h | 590 ++++++++++++++++++++++++++++++++++ src/dasynq/dasynq-childproc.h | 126 ++++---- src/dasynq/dasynq-timerbase.h | 14 +- src/dasynq/dasynq.h | 39 ++- 4 files changed, 697 insertions(+), 72 deletions(-) create mode 100644 src/dasynq/dasynq-btree_set.h diff --git a/src/dasynq/dasynq-btree_set.h b/src/dasynq/dasynq-btree_set.h new file mode 100644 index 0000000..80885e9 --- /dev/null +++ b/src/dasynq/dasynq-btree_set.h @@ -0,0 +1,590 @@ +#ifndef DASYNQ_BTREE_SET_H +#define DASYNQ_BTREE_SET_H + +#include +#include + +namespace dasynq { + +// A sorted set based on a B-Tree data structure, supporting pre-allocation of nodes. + +template , 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 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 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 diff --git a/src/dasynq/dasynq-childproc.h b/src/dasynq/dasynq-childproc.h index 3b37e4b..8d364f7 100644 --- a/src/dasynq/dasynq-childproc.h +++ b/src/dasynq/dasynq-childproc.h @@ -1,4 +1,5 @@ #include +#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; - std::unordered_map base_map; - std::vector 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; + bmap_t b_map; public: + using pid_handle_t = bmap_t::handle_t; + + // Map entry: present (bool), data (void *) using entry = std::pair; 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 ChildProcEvents : public Base { private: @@ -105,7 +93,7 @@ template 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 ChildProcEvents : public Base } public: - void reserveChildWatch() + void reserveChildWatch(pid_watch_handle_t &handle) { std::lock_guard guard(Base::lock); - child_waiters.reserve(); + child_waiters.reserve(handle); } - void unreserveChildWatch() noexcept + void unreserveChildWatch(pid_watch_handle_t &handle) noexcept { std::lock_guard 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 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 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 guard(Base::lock); + child_waiters.remove(handle); + } + + void removeChildWatch(pid_watch_handle_t &handle) noexcept { std::lock_guard 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 void init(T *loop_mech) diff --git a/src/dasynq/dasynq-timerbase.h b/src/dasynq/dasynq-timerbase.h index b45ecb0..87ae20e 100644 --- a/src/dasynq/dasynq-timerbase.h +++ b/src/dasynq/dasynq-timerbase.h @@ -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 diff --git a/src/dasynq/dasynq.h b/src/dasynq/dasynq.h index 746fae6..bff5a0d 100644 --- a/src/dasynq/dasynq.h +++ b/src/dasynq/dasynq.h @@ -294,6 +294,7 @@ namespace dprivate { template 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 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 rearmType = rearm::REMOVE; } + loop.process_child_watch_rearm(this, rearmType); + // rearmType = loop.process??; post_dispatch(loop, this, rearmType); } -- 2.25.1