2 #include "dasynq-btree_set.h"
8 // Map of pid_t to void *, with possibility of reserving entries so that mappings can
9 // be later added with no danger of allocator exhaustion (bad_alloc).
12 using bmap_t = btree_set<void *, pid_t>;
16 using pid_handle_t = bmap_t::handle_t;
18 // Map entry: present (bool), data (void *)
19 using entry = std::pair<bool, void *>;
21 entry get(pid_t key) noexcept
23 auto it = b_map.find(key);
25 return entry(false, nullptr);
27 return entry(true, b_map.node_data(*it));
30 entry remove(pid_t key) noexcept
32 auto it = b_map.find(key);
34 return entry(false, nullptr);
37 return entry(true, b_map.node_data(*it));
40 void remove(pid_handle_t &hndl)
42 if (b_map.is_queued(hndl)) {
47 // Throws bad_alloc on reservation failure
48 void reserve(pid_handle_t &hndl)
53 void unreserve(pid_handle_t &hndl) noexcept
55 b_map.deallocate(hndl);
58 void add(pid_handle_t &hndl, pid_t key, void *val) // throws std::bad_alloc
61 b_map.node_data(hndl) = val;
62 b_map.insert(hndl, key);
65 void add_from_reserve(pid_handle_t &hndl, pid_t key, void *val) noexcept
67 b_map.node_data(hndl) = val;
68 b_map.insert(hndl, key);
72 inline void sigchld_handler(int signum)
74 // If SIGCHLD has no handler (is ignored), SIGCHLD signals will
75 // not be queued for terminated child processes. (On Linux, the
76 // default disposition for SIGCHLD is to be ignored but *not* have
77 // this behavior, which seems inconsistent. Setting a handler doesn't
81 } // dprivate namespace
83 using pid_watch_handle_t = dprivate::pid_map::pid_handle_t;
85 template <class Base> class child_proc_events : public Base
88 using reaper_mutex_t = typename Base::mutex_t;
90 class traits_t : public Base::traits_t
93 constexpr static bool supports_childwatch_reservation = true;
97 dprivate::pid_map child_waiters;
98 reaper_mutex_t reaper_lock; // used to prevent reaping while trying to signal a process
101 using sigdata_t = typename traits_t::sigdata_t;
103 template <typename T>
104 bool receive_signal(T & loop_mech, sigdata_t &siginfo, void *userdata)
106 if (siginfo.get_signo() == SIGCHLD) {
110 while ((child = waitpid(-1, &status, WNOHANG)) > 0) {
111 auto ent = child_waiters.remove(child);
113 Base::receive_child_stat(child, status, ent.second);
116 reaper_lock.unlock();
117 return false; // leave signal watch enabled
120 return Base::receive_signal(loop_mech, siginfo, userdata);
125 void reserve_child_watch_nolock(pid_watch_handle_t &handle)
127 child_waiters.reserve(handle);
130 void unreserve_child_watch(pid_watch_handle_t &handle) noexcept
132 std::lock_guard<decltype(Base::lock)> guard(Base::lock);
133 unreserve_child_watch_nolock(handle);
136 void unreserve_child_watch_nolock(pid_watch_handle_t &handle) noexcept
138 child_waiters.unreserve(handle);
141 void add_child_watch_nolock(pid_watch_handle_t &handle, pid_t child, void *val)
143 child_waiters.add(handle, child, val);
146 void add_reserved_child_watch(pid_watch_handle_t &handle, pid_t child, void *val) noexcept
148 std::lock_guard<decltype(Base::lock)> guard(Base::lock);
149 child_waiters.add_from_reserve(handle, child, val);
152 void add_reserved_child_watch_nolock(pid_watch_handle_t &handle, pid_t child, void *val) noexcept
154 child_waiters.add_from_reserve(handle, child, val);
157 // Stop watching a child, but retain watch reservation
158 void stop_child_watch(pid_watch_handle_t &handle) noexcept
160 std::lock_guard<decltype(Base::lock)> guard(Base::lock);
161 child_waiters.remove(handle);
164 void remove_child_watch(pid_watch_handle_t &handle) noexcept
166 std::lock_guard<decltype(Base::lock)> guard(Base::lock);
167 remove_child_watch_nolock(handle);
170 void remove_child_watch_nolock(pid_watch_handle_t &handle) noexcept
172 child_waiters.remove(handle);
173 child_waiters.unreserve(handle);
176 // Get the reaper lock, which can be used to ensure that a process is not reaped while attempting to
178 reaper_mutex_t &get_reaper_lock() noexcept
183 template <typename T> void init(T *loop_mech)
187 this->sigmaskf(SIG_UNBLOCK, nullptr, &sigmask);
188 sigaddset(&sigmask, SIGCHLD);
189 this->sigmaskf(SIG_SETMASK, &sigmask, nullptr);
191 // On some systems a SIGCHLD handler must be established, or SIGCHLD will not be
193 struct sigaction chld_action;
194 chld_action.sa_handler = dprivate::sigchld_handler;
195 sigemptyset(&chld_action.sa_mask);
196 chld_action.sa_flags = 0;
197 sigaction(SIGCHLD, &chld_action, nullptr);
198 loop_mech->add_signal_watch(SIGCHLD, nullptr);
199 Base::init(loop_mech);