Skip to content

Commit

Permalink
Upgrade Dasynq to 2.0, and associated changes
Browse files Browse the repository at this point in the history
The visible effect of this is that the full "int" exit status of
applications is available to Dinit (not just the lower 8 bits), on OSes
which support this (eg FreeBSD, but not Linux).
  • Loading branch information
davmac314 committed Sep 15, 2024
1 parent ff159b7 commit eb50c6d
Show file tree
Hide file tree
Showing 27 changed files with 655 additions and 225 deletions.
52 changes: 36 additions & 16 deletions dasynq/include/dasynq.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,12 +84,16 @@
#if _POSIX_TIMERS > 0
#include "dasynq/posixtimer.h"
namespace dasynq {
inline namespace v2 {
template <typename T, bool provide_mono_timer = true> using timer_events = posix_timer_events<T, provide_mono_timer>;
} // namespace v2
} // namespace dasynq
#else
#include "dasynq/itimer.h"
namespace dasynq {
inline namespace v2 {
template <typename T, bool provide_mono_timer = true> using timer_events = itimer_events<T, provide_mono_timer>;
} // namespace v2
} // namespace dasynq
#endif
#endif
Expand All @@ -99,38 +103,48 @@ namespace dasynq {
#include "dasynq/kqueue-macos.h"
#include "dasynq/childproc.h"
namespace dasynq {
inline namespace v2 {
template <typename T> using loop_t = macos_kqueue_loop<timer_events<child_proc_events<interrupt_channel<T>>, false>>;
using loop_traits_t = macos_kqueue_traits;
} // namespace v2
} // namespace dasynq
#else
#include "dasynq/kqueue.h"
#include "dasynq/childproc.h"
namespace dasynq {
inline namespace v2 {
template <typename T> using loop_t = kqueue_loop<timer_events<child_proc_events<interrupt_channel<T>>, false>>;
using loop_traits_t = kqueue_traits;
} // namespace v2
} // namespace dasynq
#endif
#elif DASYNQ_HAVE_EPOLL
#include "dasynq/epoll.h"
#include "dasynq/timerfd.h"
#include "dasynq/childproc.h"
namespace dasynq {
inline namespace v2 {
template <typename T> using loop_t = epoll_loop<interrupt_channel<timer_fd_events<child_proc_events<T>>>>;
using loop_traits_t = epoll_traits;
} // namespace v2
} // namespace dasynq
#else
#include "dasynq/childproc.h"
#if DASYNQ_HAVE_PSELECT
#include "dasynq/pselect.h"
namespace dasynq {
inline namespace v2 {
template <typename T> using loop_t = pselect_events<timer_events<interrupt_channel<child_proc_events<T>>, false>>;
using loop_traits_t = select_traits;
} // namespace v2
} // namespace dasynq
#else
#include "dasynq/select.h"
namespace dasynq {
inline namespace v2 {
template <typename T> using loop_t = select_events<timer_events<interrupt_channel<child_proc_events<T>>, false>>;
using loop_traits_t = select_traits;
} // namespace v2
} // namespace dasynq
#endif
#endif
Expand Down Expand Up @@ -174,6 +188,7 @@ class delayed_init {
DASYNQ_EMPTY_BODY
};

inline namespace v2 {
namespace dprivate {

// Classes for implementing a fair(ish) wait queue.
Expand Down Expand Up @@ -438,7 +453,7 @@ namespace dprivate {
prio_queue event_queue;

using base_signal_watcher = dprivate::base_signal_watcher<typename traits_t::sigdata_t>;
using base_child_watcher = dprivate::base_child_watcher;
using base_child_watcher = dprivate::base_child_watcher<typename traits_t::proc_status_t>;
using base_timer_watcher = dprivate::base_timer_watcher;

// Add a watcher into the queueing system (but don't queue it). Call with lock held.
Expand Down Expand Up @@ -535,7 +550,7 @@ namespace dprivate {
}

// Child process terminated. Called with both the main lock and the reaper lock held.
void receive_child_stat(pid_t child, int status, void * userdata) noexcept
void receive_child_stat(pid_t child, typename LoopTraits::backend_traits_t::proc_status_t status, void * userdata) noexcept
{
base_child_watcher * watcher = static_cast<base_child_watcher *>(userdata);
watcher->child_status = status;
Expand Down Expand Up @@ -635,7 +650,7 @@ namespace dprivate {
event_dispatch(const event_dispatch &) = delete;
};

} // namespace dasynq
} // namespace dprivate

// This is the main event_loop implementation. It serves as an interface to the event loop backend (of which
// it maintains an internal instance). It also serialises polling the backend and provides safe deletion of
Expand Down Expand Up @@ -681,7 +696,7 @@ class event_loop
using base_signal_watcher = dprivate::base_signal_watcher<typename loop_traits_t::sigdata_t>;
using base_fd_watcher = dprivate::base_fd_watcher;
using base_bidi_fd_watcher = dprivate::base_bidi_fd_watcher;
using base_child_watcher = dprivate::base_child_watcher;
using base_child_watcher = dprivate::base_child_watcher<typename loop_traits_t::proc_status_t>;
using base_timer_watcher = dprivate::base_timer_watcher;
using watch_type_t = dprivate::watch_type_t;

Expand All @@ -705,8 +720,8 @@ class event_loop
// To solve that, we:
// - allow only one thread to poll for events at a time, using a lock
// - use the same lock to prevent polling, if we want to unwatch an event source
// - generate an event to interrupt any polling that may already be occurring in
// another thread
// - generate an event to interrupt, when necessary, any polling that may already be occurring
// in another thread
// - mark handlers as active if they are currently executing, and
// - when removing an active handler, simply set a flag which causes it to be
// removed once the current processing is finished, rather than removing it
Expand Down Expand Up @@ -740,6 +755,9 @@ class event_loop
// - if the node is at the head of the queue, lock is claimed; return
// - otherwise, if a poll is in progress, interrupt it
// - wait until our node is at the head of the attn_waitqueue
//
// Some backends also need to interrupted in order to add a new watch (eg select/pselect).
// However, the attn_waitqueue lock doesn't generally need to be obtained for this.

mutex_t wait_lock; // protects the wait/attention queues
bool long_poll_running = false; // whether any thread is polling the backend (with non-zero timeout)
Expand Down Expand Up @@ -1021,28 +1039,28 @@ class event_loop
}
}

void set_timer(base_timer_watcher *callBack, const timespec &timeout, clock_type clock) noexcept
void set_timer(base_timer_watcher *callback, const timespec &timeout, clock_type clock) noexcept
{
struct timespec interval {0, 0};
loop_mech.set_timer(callBack->timer_handle, timeout, interval, true, clock);
loop_mech.set_timer(callback->timer_handle, timeout, interval, true, clock);
}

void set_timer(base_timer_watcher *callBack, const timespec &timeout, const timespec &interval,
void set_timer(base_timer_watcher *callback, const timespec &timeout, const timespec &interval,
clock_type clock) noexcept
{
loop_mech.set_timer(callBack->timer_handle, timeout, interval, true, clock);
loop_mech.set_timer(callback->timer_handle, timeout, interval, true, clock);
}

void set_timer_rel(base_timer_watcher *callBack, const timespec &timeout, clock_type clock) noexcept
void set_timer_rel(base_timer_watcher *callback, const timespec &timeout, clock_type clock) noexcept
{
struct timespec interval {0, 0};
loop_mech.set_timer_rel(callBack->timer_handle, timeout, interval, true, clock);
loop_mech.set_timer_rel(callback->timer_handle, timeout, interval, true, clock);
}

void set_timer_rel(base_timer_watcher *callBack, const timespec &timeout,
void set_timer_rel(base_timer_watcher *callback, const timespec &timeout,
const timespec &interval, clock_type clock) noexcept
{
loop_mech.set_timer_rel(callBack->timer_handle, timeout, interval, true, clock);
loop_mech.set_timer_rel(callback->timer_handle, timeout, interval, true, clock);
}

void set_timer_enabled(base_timer_watcher *callback, clock_type clock, bool enabled) noexcept
Expand Down Expand Up @@ -2034,7 +2052,7 @@ class bidi_fd_watcher_impl : public bidi_fd_watcher<EventLoop>

// Child process event watcher
template <typename EventLoop>
class child_proc_watcher : private dprivate::base_child_watcher
class child_proc_watcher : private dprivate::base_child_watcher<typename EventLoop::loop_traits_t::proc_status_t>
{
template <typename, typename> friend class child_proc_watcher_impl;

Expand All @@ -2044,6 +2062,7 @@ class child_proc_watcher : private dprivate::base_child_watcher
public:

using event_loop_t = EventLoop;
using proc_status_t = typename EventLoop::loop_traits_t::proc_status_t;

// send a signal to this process, if it is still running, in a race-free manner.
// return is as for POSIX kill(); return is -1 with errno=ESRCH if process has
Expand Down Expand Up @@ -2202,7 +2221,7 @@ class child_proc_watcher : private dprivate::base_child_watcher
}
}

// virtual rearm child_status(EventLoop &eloop, pid_t child, int status) = 0;
// virtual rearm child_status(EventLoop &eloop, pid_t child, proc_status_t status) = 0;
};

template <typename EventLoop, typename Derived>
Expand Down Expand Up @@ -2367,6 +2386,7 @@ class timer_impl : public timer<EventLoop>

} // namespace dprivate

} // namespace v2
} // namespace dasynq

#endif /* DASYNQ_H_ */
8 changes: 6 additions & 2 deletions dasynq/include/dasynq/basewatchers.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ namespace dprivate {
}
} // namespace dprivate

inline namespace v2 {

// A template to generate suitable default loop traits for a given type of mutex:
template <typename T_Mutex> class default_traits
{
Expand All @@ -40,7 +42,7 @@ template <typename T_Mutex> class default_traits
// (sigprocmask or pthread_sigmask):
static void sigmaskf(int how, const sigset_t *set, sigset_t *oset)
{
dprivate::sigmaskf<T_Mutex>(how, set, oset);
dasynq::dprivate::sigmaskf<T_Mutex>(how, set, oset);
}
};

Expand Down Expand Up @@ -248,6 +250,7 @@ namespace dprivate {
unsigned write_removed : 1; // write watch removed?
};

template <typename ChildData>
class base_child_watcher : public base_watcher
{
template <typename, typename> friend class event_dispatch;
Expand All @@ -256,7 +259,7 @@ namespace dprivate {
protected:
pid_watch_handle_t watch_handle;
pid_t watch_pid;
int child_status;
ChildData child_status;

base_child_watcher() : base_watcher(watch_type_t::CHILD) { }
};
Expand All @@ -280,6 +283,7 @@ namespace dprivate {

} // namespace dprivate

} // namespace v2
} // namespace dasynq

#endif /* DASYNQ_BASEWATCHERS_H_ */
36 changes: 32 additions & 4 deletions dasynq/include/dasynq/childproc.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "btree_set.h"

namespace dasynq {
inline namespace v2 {

namespace dprivate {

Expand Down Expand Up @@ -85,6 +86,24 @@ inline void sigchld_handler(int signum)
// hurt in any case).
}

class proc_status {
int wait_si_code; // CLD_EXITED or a signal-related status
int wait_si_status; // exit status as per exit(...), or signal number

public:
proc_status() noexcept {}
proc_status(int wait_si_code, int wait_si_status) noexcept
: wait_si_code(wait_si_code), wait_si_status(wait_si_status) {}
proc_status(const proc_status &) noexcept = default;
proc_status &operator=(const proc_status &) noexcept = default;

bool did_exit() noexcept { return wait_si_code == CLD_EXITED; }
bool did_exit_clean() noexcept { return wait_si_status == 0; }
bool was_signalled() noexcept { return !did_exit(); }
int get_exit_status() noexcept { return wait_si_status; }
int get_signal() noexcept { return wait_si_status; }
};

} // namespace dprivate

using pid_watch_handle_t = dprivate::pid_map::pid_handle_t;
Expand All @@ -98,6 +117,7 @@ template <class Base> class child_proc_events : public Base
{
public:
constexpr static bool supports_childwatch_reservation = true;
using proc_status_t = dprivate::proc_status;
};

private:
Expand All @@ -111,15 +131,22 @@ template <class Base> class child_proc_events : public Base
bool receive_signal(T & loop_mech, sigdata_t &siginfo, void *userdata)
{
if (siginfo.get_signo() == SIGCHLD) {
int status;
pid_t child;
reaper_lock.lock();
while ((child = waitpid(-1, &status, WNOHANG)) > 0) {

siginfo_t child_info;
child_info.si_pid = 0; // for portability
while (waitid(P_ALL, 0 /* ignored */, &child_info, WNOHANG | WEXITED) == 0) {
pid_t child = child_info.si_pid;
if (child == 0) break;
auto ent = child_waiters.remove(child);
if (ent.first) {
Base::receive_child_stat(child, status, ent.second);
reaper_lock.unlock();
Base::receive_child_stat(child, { child_info.si_code, child_info.si_status },
ent.second);
reaper_lock.lock();
}
}

reaper_lock.unlock();
return false; // leave signal watch enabled
}
Expand Down Expand Up @@ -209,6 +236,7 @@ template <class Base> class child_proc_events : public Base
}
};

} // namespace v2
} // namespace dasynq

#endif /* DASYNQ_CHILDPROC_H_ */
10 changes: 9 additions & 1 deletion dasynq/include/dasynq/epoll.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@
#include <csignal>

namespace dasynq {
inline namespace v2 {

namespace dprivate {
class proc_status; // forward declaration
}

template <class Base> class epoll_loop;

Expand Down Expand Up @@ -59,7 +64,9 @@ class epoll_traits
// uint16_t get_siaddr_lsb() { return info.ssi_addr_lsb; }

void set_signo(int signo) { info.ssi_signo = signo; }
};
};

using proc_status_t = dprivate::proc_status;

class fd_r;

Expand Down Expand Up @@ -383,6 +390,7 @@ template <class Base> class epoll_loop : public Base
}
};

} // namespace v2
} // namespace dasynq

#endif /* DASYNQ_EPOLL_H_ */
3 changes: 3 additions & 0 deletions dasynq/include/dasynq/itimer.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ namespace dasynq {
// run a timer against. However, if the system has both clocks, we still maintain two separate queues.
// If provide_mono_timer is false we actually provide no system timer and rely on the event mechanism
// which extends this class to measure time and run timeouts (via process_monotonic_timers functions).
// In this case, if a monotonic timer is set with a timeout which precedes the previously outstanding
// next timeout, an interrupt is issued (interrupt_wait()) to interrupt any current polling and allow
// the mechanism to resume polling with an adjusted timeout.

template <class Base, bool provide_mono_timer = true>
class itimer_events : public timer_base<Base>
Expand Down
8 changes: 8 additions & 0 deletions dasynq/include/dasynq/kqueue-macos.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@
// mechanism must be used together with kqueue.

namespace dasynq {
inline namespace v2 {

namespace dprivate {
class proc_status; // forward declaration
}

template <class Base> class kqueue_loop;

Expand Down Expand Up @@ -67,6 +72,8 @@ class macos_kqueue_traits : public signal_traits
}
};

using proc_status_t = dprivate::proc_status;

constexpr static bool has_bidi_fd_watch = false;
constexpr static bool has_separate_rw_fd_watches = true;
constexpr static bool interrupt_after_fd_add = false;
Expand Down Expand Up @@ -411,6 +418,7 @@ template <class Base> class macos_kqueue_loop : public signal_events<Base, true>
}
};

} // namespace v2
} // namespace dasynq

#endif /* DASYNQ_KQUEUE_MACOS_H_ */
Loading

0 comments on commit eb50c6d

Please sign in to comment.