--- /dev/null
+#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
#include <signal.h>
+#include "dasynq-btree_set.h"
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);
}
};
// hurt in any case).
}
+using pid_watch_handle_t = pid_map::pid_handle_t;
+
template <class Base> class ChildProcEvents : public Base
{
private:
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);
}
}
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)
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;
{
loop_mech.prepare_watcher(callback);
try {
- loop_mech.reserveChildWatch();
+ loop_mech.reserveChildWatch(callback->watch_handle);
}
catch (...) {
loop_mech.release_watcher(callback);
void unreserve(BaseChildWatcher *callback) noexcept
{
- loop_mech.unreserveChildWatch();
+ loop_mech.unreserveChildWatch(callback->watch_handle);
loop_mech.release_watcher(callback);
}
{
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);
}
}
- 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);
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);
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
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),
rearmType = rearm::REMOVE;
}
+ loop.process_child_watch_rearm(this, rearmType);
+
// rearmType = loop.process??;
post_dispatch(loop, this, rearmType);
}