This updates bundled Dasynq to 1.0 (plus minor changes since).
--- /dev/null
+// Dasynq: early declarations and base watchers.
+//
+// Here we define watcher functionality that is not dependent on the event loop type. In particular,
+// base classes for the various watcher types. These are not part of the public API.
+//
+// In general access to the members of the basewatcher should be protected by a mutex. The
+// event_dispatch lock is used for this purpose.
+
+namespace dasynq {
+
+namespace dprivate {
+ // POSIX says that sigprocmask has unspecified behaviour if used in a multi-threaded process. We can use
+ // pthread_sigmask instead, but that may require linking with the threads library. This function is
+ // specialised to call one or the other depending on the mutex type:
+ template <typename T_Mutex> void sigmaskf(int how, const sigset_t *set, sigset_t *oset)
+ {
+ sigprocmask(how, set, oset);
+ }
+
+ template <> inline void sigmaskf<null_mutex>(int how, const sigset_t *set, sigset_t *oset)
+ {
+ pthread_sigmask(how, set, oset);
+ }
+}
+
+// A template to generate suitable default loop traits for a given type of mutex:
+template <typename T_Mutex> class default_traits
+{
+ public:
+ using mutex_t = T_Mutex;
+ template <typename Base> using backend_t = dasynq::loop_t<Base>;
+ using backend_traits_t = dasynq::loop_traits_t;
+
+ // Alter the current thread signal mask using the correct function
+ // (sigprocmask or pthread_sigmask):
+ static void sigmaskf(int how, const sigset_t *set, sigset_t *oset)
+ {
+ dprivate::sigmaskf<T_Mutex>(how, set, oset);
+ }
+};
+
+// Forward declarations:
+template <typename T_Mutex, typename Traits = default_traits<T_Mutex>>
+class event_loop;
+
+inline namespace {
+ constexpr int DEFAULT_PRIORITY = 50;
+}
+
+namespace dprivate {
+ // (non-public API)
+
+ class base_watcher;
+ using prio_queue = NaryHeap<dprivate::base_watcher *, int>;
+
+ template <typename T_Loop> class fd_watcher;
+ template <typename T_Loop> class bidi_fd_watcher;
+ template <typename T_Loop> class signal_watcher;
+ template <typename T_Loop> class child_proc_watcher;
+ template <typename T_Loop> class timer;
+
+ template <typename, typename> class fd_watcher_impl;
+ template <typename, typename> class bidi_fd_watcher_impl;
+ template <typename, typename> class signal_watcher_impl;
+ template <typename, typename> class child_proc_watcher_impl;
+ template <typename, typename> class timer_impl;
+
+ enum class watch_type_t
+ {
+ SIGNAL,
+ FD,
+ CHILD,
+ SECONDARYFD,
+ TIMER
+ };
+
+ template <typename T_Mutex, typename Traits, typename LoopTraits> class event_dispatch;
+
+ // For FD watchers:
+ // Use this watch flag to indicate that in and out events should be reported separately,
+ // that is, watcher should not be disabled until all watched event types are queued.
+ constexpr static int multi_watch = 4;
+
+ // Represents a queued event notification. Various event watchers derive from this type.
+ class base_watcher
+ {
+ public:
+ watch_type_t watchType;
+ int active : 1; // currently executing handler?
+ int deleteme : 1; // delete when handler finished?
+ int emulatefd : 1; // emulate file watch (by re-queueing)
+ int emulate_enabled : 1; // whether an emulated watch is enabled
+ int child_termd : 1; // child process has terminated
+
+ prio_queue::handle_t heap_handle;
+ int priority;
+
+ static void set_priority(base_watcher &p, int prio)
+ {
+ p.priority = prio;
+ }
+
+ // Perform initialisation necessary before registration with an event loop
+ void init()
+ {
+ active = false;
+ deleteme = false;
+ emulatefd = false;
+ emulate_enabled = false;
+ child_termd = false;
+ prio_queue::init_handle(heap_handle);
+ priority = DEFAULT_PRIORITY;
+ }
+
+ base_watcher(watch_type_t wt) noexcept : watchType(wt) { }
+ base_watcher(const base_watcher &) = delete;
+ base_watcher &operator=(const base_watcher &) = delete;
+
+ // The dispatch function is called to process a watcher's callback. It is the "real" callback
+ // function; it usually delegates to a user-provided callback.
+ virtual void dispatch(void *loop_ptr) noexcept { };
+
+ // Bi-directional file descriptor watches have a secondary dispatch function for the secondary
+ // watcher (i.e. the output watcher):
+ virtual void dispatch_second(void *loop_ptr) noexcept { }
+
+ virtual ~base_watcher() noexcept { }
+
+ // Called when the watcher has been removed.
+ // It is guaranteed by the caller that:
+ // - the dispatch method is not currently running
+ // - the dispatch method will not be called.
+ virtual void watch_removed() noexcept
+ {
+ // Later: the "delete" behaviour could be dependent on a flag, perhaps?
+ // delete this;
+ }
+ };
+
+ // Base signal event - not part of public API
+ template <typename T_Mutex, typename T_Sigdata>
+ class base_signal_watcher : public base_watcher
+ {
+ template <typename, typename, typename> friend class event_dispatch;
+ template <typename, typename> friend class dasynq::event_loop;
+
+ protected:
+ T_Sigdata siginfo;
+ base_signal_watcher() : base_watcher(watch_type_t::SIGNAL) { }
+
+ public:
+ using siginfo_t = T_Sigdata;
+ typedef siginfo_t &siginfo_p;
+ };
+
+ template <typename T_Mutex>
+ class base_fd_watcher : public base_watcher
+ {
+ template <typename, typename, typename> friend class event_dispatch;
+ template <typename, typename> friend class dasynq::event_loop;
+
+ protected:
+ int watch_fd;
+
+ // These flags are protected by the loop's internal lock:
+ int watch_flags; // events being watched
+ int event_flags; // events pending (queued)
+
+ // watch_flags: for a regular fd_watcher, this specifies the events that the watcher
+ // is watching (or was watching if disabled). For a bidi_fd_watcher, specifies
+ // the events that the watcher is currently watching (i.e. specifies which
+ // halves of the Bidi watcher are enabled).
+
+ base_fd_watcher() noexcept : base_watcher(watch_type_t::FD) { }
+ };
+
+ template <typename T_Mutex>
+ class base_bidi_fd_watcher : public base_fd_watcher<T_Mutex>
+ {
+ template <typename, typename, typename> friend class event_dispatch;
+ template <typename, typename> friend class dasynq::event_loop;
+
+ protected:
+
+ // The main instance is the "input" watcher only; we keep a secondary watcher with a secondary set
+ // of flags for the "output" watcher. Note that some of the flags in the secondary watcher aren't
+ // used; it exists mainly so that it can be queued independently of the primary watcher.
+ base_watcher out_watcher {watch_type_t::SECONDARYFD};
+
+ int read_removed : 1; // read watch removed?
+ int write_removed : 1; // write watch removed?
+ };
+
+ template <typename T_Mutex>
+ class base_child_watcher : public base_watcher
+ {
+ template <typename, typename, typename> friend class event_dispatch;
+ template <typename, typename> friend class dasynq::event_loop;
+
+ protected:
+ pid_watch_handle_t watch_handle;
+ pid_t watch_pid;
+ int child_status;
+
+ base_child_watcher() : base_watcher(watch_type_t::CHILD) { }
+ };
+
+
+ template <typename T_Mutex>
+ class base_timer_watcher : public base_watcher
+ {
+ template <typename, typename, typename> friend class event_dispatch;
+ template <typename, typename> friend class dasynq::event_loop;
+
+ protected:
+ timer_handle_t timer_handle;
+ int intervals;
+ clock_type clock;
+
+ base_timer_watcher() : base_watcher(watch_type_t::TIMER)
+ {
+ init_timer_handle(timer_handle);
+ }
+ };
+} // dprivate
+} // dasynq
#ifndef DASYNQ_BTREE_SET_H
#define DASYNQ_BTREE_SET_H
-#include <vector>
#include <functional>
namespace dasynq {
~btree_set()
{
+ while (left_sept != nullptr) {
+ remove(*(left_sept->hn_p[0]));
+ }
+
while (sn_reserve != nullptr) {
auto *next = sn_reserve->parent;
delete sn_reserve;
namespace dasynq {
+namespace dprivate {
+
// Map of pid_t to void *, with possibility of reserving entries so that mappings can
// be later added with no danger of allocator exhaustion (bad_alloc).
class pid_map
// hurt in any case).
}
-using pid_watch_handle_t = pid_map::pid_handle_t;
+} // dprivate namespace
+
+using pid_watch_handle_t = dprivate::pid_map::pid_handle_t;
-template <class Base> class ChildProcEvents : public Base
+template <class Base> class child_proc_events : public Base
{
+ public:
+ using reaper_mutex_t = typename Base::mutex_t;
+
+ class traits_t : public Base::traits_t
+ {
+ public:
+ constexpr static bool supports_childwatch_reservation = true;
+ };
+
private:
- pid_map child_waiters;
+ dprivate::pid_map child_waiters;
+ reaper_mutex_t reaper_lock; // used to prevent reaping while trying to signal a process
protected:
- using SigInfo = typename Base::SigInfo;
+ using sigdata_t = typename traits_t::sigdata_t;
template <typename T>
- bool receive_signal(T & loop_mech, SigInfo &siginfo, void *userdata)
+ 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) {
- pid_map::entry ent = child_waiters.remove(child);
+ auto ent = child_waiters.remove(child);
if (ent.first) {
- Base::receiveChildStat(child, status, ent.second);
+ Base::receive_child_stat(child, status, ent.second);
}
}
+ reaper_lock.unlock();
return false; // leave signal watch enabled
}
else {
}
public:
- void reserveChildWatch(pid_watch_handle_t &handle)
+ void reserve_child_watch_nolock(pid_watch_handle_t &handle)
{
- std::lock_guard<decltype(Base::lock)> guard(Base::lock);
child_waiters.reserve(handle);
}
- void unreserveChildWatch(pid_watch_handle_t &handle) noexcept
+ void unreserve_child_watch(pid_watch_handle_t &handle) noexcept
{
std::lock_guard<decltype(Base::lock)> guard(Base::lock);
- unreserveChildWatch_nolock(handle);
+ unreserve_child_watch_nolock(handle);
}
- void unreserveChildWatch_nolock(pid_watch_handle_t &handle) noexcept
+ void unreserve_child_watch_nolock(pid_watch_handle_t &handle) noexcept
{
child_waiters.unreserve(handle);
}
- void addChildWatch(pid_watch_handle_t &handle, pid_t child, void *val)
+ void add_child_watch_nolock(pid_watch_handle_t &handle, pid_t child, void *val)
{
- std::lock_guard<decltype(Base::lock)> guard(Base::lock);
child_waiters.add(handle, child, val);
}
- void addReservedChildWatch(pid_watch_handle_t &handle, pid_t child, void *val) noexcept
+ void add_reserved_child_watch(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(handle, child, val);
}
- void addReservedChildWatch_nolock(pid_watch_handle_t &handle, pid_t child, void *val) noexcept
+ void add_reserved_child_watch_nolock(pid_watch_handle_t &handle, pid_t child, void *val) noexcept
{
child_waiters.add_from_reserve(handle, child, val);
}
child_waiters.remove(handle);
}
- void removeChildWatch(pid_watch_handle_t &handle) noexcept
+ void remove_child_watch(pid_watch_handle_t &handle) noexcept
{
std::lock_guard<decltype(Base::lock)> guard(Base::lock);
- removeChildWatch_nolock(handle);
+ remove_child_watch_nolock(handle);
}
- void removeChildWatch_nolock(pid_watch_handle_t &handle) noexcept
+ void remove_child_watch_nolock(pid_watch_handle_t &handle) noexcept
{
child_waiters.remove(handle);
child_waiters.unreserve(handle);
}
+ // Get the reaper lock, which can be used to ensure that a process is not reaped while attempting to
+ // signal it.
+ reaper_mutex_t &get_reaper_lock() noexcept
+ {
+ return reaper_lock;
+ }
+
template <typename T> void init(T *loop_mech)
{
+ // Mask SIGCHLD:
+ sigset_t sigmask;
+ this->sigmaskf(SIG_UNBLOCK, nullptr, &sigmask);
+ sigaddset(&sigmask, SIGCHLD);
+ this->sigmaskf(SIG_SETMASK, &sigmask, nullptr);
+
+ // On some systems a SIGCHLD handler must be established, or SIGCHLD will not be
+ // generated:
struct sigaction chld_action;
- chld_action.sa_handler = sigchld_handler;
+ chld_action.sa_handler = dprivate::sigchld_handler;
sigemptyset(&chld_action.sa_mask);
chld_action.sa_flags = 0;
sigaction(SIGCHLD, &chld_action, nullptr);
- loop_mech->addSignalWatch(SIGCHLD, nullptr);
+ loop_mech->add_signal_watch(SIGCHLD, nullptr);
Base::init(loop_mech);
}
};
#ifndef DASYNQ_CONFIG_H_INCLUDED
#define DASYNQ_CONFIG_H_INCLUDED
+// You can customise Dasynq's build options in this file. Typically, you won't need to do anything; the
+// defaults are sensible for a range of operating systems, though for some BSD family OSes you may need
+// to explicitly define DASYNQ_HAVE_KQUEUE to 1. Either kqueue or epoll must be available (i.e. either
+// DASYNQ_HAVE_KQUEUE or DASYNQ_HAVE_EPOLL need to be defined to 1). There are two parts to the file: the
+// first is the custom configuration section, where you may specify custom settings, and the second
+// section contains automatic configuration to fill in remaining settings based on known features in
+// certain operating systems and compilers.
+
+// ---------------------------------------------------------------------------------------------------------
+// Part 1: Custom configuration, please edit to suit your system / requirements.
+// ---------------------------------------------------------------------------------------------------------
+
+// Specify custom configuration here. You can define the following to 1 if the
+// corresponding feature is available and should be used, or to 0 otherwise.
+//
+// If the kqueue/kevent system calls are available:
+// #define DASYNQ_HAVE_KQUEUE 1
+//
+// If the epoll family of system calls are available:
+// #define DASYNQ_HAVE_KQUEUE 1
+//
+// If the pipe2 system call is available:
+// #define HAVE_PIPE2 1
+//
+// A tag to include at the end of a class body for a class which is allowed to have zero size.
+// Normally, C++ mandates that all objects (except empty base subobjects) have non-zero size, but on some
+// compilers (at least GCC and LLVM-Clang) there are tricks to get around this awkward limitation. Note that
+// using this theoretically creates an ABI issue if two different compilers are used to compile different
+// parts of the same program which both use Dasynq, if one supports zero-sized classes and the other does
+// not; consider defining this empty here if that concerns you:
+// #define DASYNQ_EMPTY_BODY /* compiler specific! */
+//
+// A statement to tell the compiler that the current line of code is unreachable, that is, it will never be
+// the case that program execution flow reaches this statement:
+// #define DASYNQ_UNREACHABLE /* compiler specific! */
+
+// ---------------------------------------------------------------------------------------------------------
+// Part 2: Automatic configuration begins here; you should not need to edit beyond this point.
+// ---------------------------------------------------------------------------------------------------------
+
+#if ! defined(DASYNQ_HAVE_KQUEUE)
#if defined(__OpenBSD__) || defined(__APPLE__) || defined(__FreeBSD__)
#define DASYNQ_HAVE_KQUEUE 1
#endif
+#endif
#if defined(__linux__)
+#if ! defined(DASYNQ_HAVE_EPOLL)
#define DASYNQ_HAVE_EPOLL 1
#endif
+#endif
// General feature availability
-#if defined(__OpenBSD__) || defined(__linux__)
-#define HAVE_PIPE2 1
+#if (defined(__OpenBSD__) || defined(__linux__)) && ! defined(HAVE_PIPE2)
+#define DASYNQ_HAVE_PIPE2 1
#endif
#else
#define DASYNQ_EMPTY_BODY char empty[0] __attribute__((unused)); // Make class instances take up no space (clang)
#endif
+
+#if ! defined(DASYNQ_UNREACHABLE)
#define DASYNQ_UNREACHABLE __builtin_unreachable()
#endif
+#endif /* __GNUC__ */
#endif
namespace dasynq {
-template <class Base> class EpollLoop;
+template <class Base> class epoll_loop;
-class EpollTraits
+class epoll_traits
{
- template <class Base> friend class EpollLoop;
+ template <class Base> friend class epoll_loop;
public:
- class SigInfo
+ class sigdata_t
{
- template <class Base> friend class EpollLoop;
+ template <class Base> friend class epoll_loop;
struct signalfd_siginfo info;
public:
+ // mandatory:
int get_signo() { return info.ssi_signo; }
int get_sicode() { return info.ssi_code; }
- int get_siint() { return info.ssi_int; }
- int get_ssiptr() { return info.ssi_ptr; }
- int get_ssiaddr() { return info.ssi_addr; }
+ pid_t get_sipid() { return info.ssi_pid; }
+ uid_t get_siuid() { return info.ssi_uid; }
+ void * get_siaddr() { return reinterpret_cast<void *>(info.ssi_addr); }
+ int get_sistatus() { return info.ssi_status; }
+ int get_sival_int() { return info.ssi_int; }
+ void * get_sival_ptr() { return reinterpret_cast<void *>(info.ssi_ptr); }
+
+ // XSI
+ int get_sierrno() { return info.ssi_errno; }
+
+ // XSR (streams) OB (obselete)
+ int get_siband() { return info.ssi_band; }
+
+ // Linux:
+ int32_t get_sifd() { return info.ssi_fd; }
+ uint32_t get_sittimerid() { return info.ssi_tid; }
+ uint32_t get_sioverrun() { return info.ssi_overrun; }
+ uint32_t get_sitrapno() { return info.ssi_trapno; }
+ uint32_t get_siutime() { return info.ssi_utime; }
+ uint32_t get_sistime() { return info.ssi_stime; }
+ // Field exposed by Linux kernel but not Glibc:
+ // uint16_t get_siaddr_lsb() { return info.ssi_addr_lsb; }
void set_signo(int signo) { info.ssi_signo = signo; }
};
- class FD_r;
+ class fd_r;
// File descriptor optional storage. If the mechanism can return the file descriptor, this
// class will be empty, otherwise it can hold a file descriptor.
- class FD_s {
- friend class FD_r;
+ class fd_s {
+ friend class fd_r;
// Epoll doesn't return the file descriptor (it can, but it can't return both file
// descriptor and user data).
// File descriptor reference (passed to event callback). If the mechanism can return the
// file descriptor, this class holds the file descriptor. Otherwise, the file descriptor
- // must be stored in an FD_s instance.
- class FD_r {
+ // must be stored in an fd_s instance.
+ class fd_r {
public:
- int getFd(FD_s ss)
+ int getFd(fd_s ss)
{
return ss.fd;
}
const static bool has_bidi_fd_watch = true;
const static bool has_separate_rw_fd_watches = false;
- const static bool supports_childwatch_reservation = true;
};
-template <class Base> class EpollLoop : public Base
+template <class Base> class epoll_loop : public Base
{
int epfd; // epoll fd
int sigfd; // signalfd fd; -1 if not initialised
// Base contains:
// lock - a lock that can be used to protect internal structure.
// receive*() methods will be called with lock held.
- // receive_signal(SigInfo &, user *) noexcept
- // receiveFdEvent(FD_r, user *, int flags) noexcept
+ // receive_signal(sigdata_t &, user *) noexcept
+ // receive_fd_event(fd_r, user *, int flags) noexcept
- using SigInfo = EpollTraits::SigInfo;
- using FD_r = typename EpollTraits::FD_r;
+ using sigdata_t = epoll_traits::sigdata_t;
+ using fd_r = typename epoll_traits::fd_r;
- void processEvents(epoll_event *events, int r)
+ void process_events(epoll_event *events, int r)
{
std::lock_guard<decltype(Base::lock)> guard(Base::lock);
if (ptr == &sigfd) {
// Signal
- SigInfo siginfo;
+ sigdata_t siginfo;
while (true) {
int r = read(sigfd, &siginfo.info, sizeof(siginfo.info));
if (r == -1) break;
(events[i].events & EPOLLHUP) && (flags |= IN_EVENTS);
(events[i].events & EPOLLOUT) && (flags |= OUT_EVENTS);
(events[i].events & EPOLLERR) && (flags |= IN_EVENTS | OUT_EVENTS | ERR_EVENTS);
- Base::receiveFdEvent(*this, FD_r(), ptr, flags);
+ Base::receive_fd_event(*this, fd_r(), ptr, flags);
}
}
}
public:
/**
- * EpollLoop constructor.
+ * epoll_loop constructor.
*
* Throws std::system_error or std::bad_alloc if the event loop cannot be initialised.
*/
- EpollLoop() : sigfd(-1)
+ epoll_loop() : sigfd(-1)
{
epfd = epoll_create1(EPOLL_CLOEXEC);
if (epfd == -1) {
Base::init(this);
}
- ~EpollLoop()
+ ~epoll_loop()
{
close(epfd);
if (sigfd != -1) {
// of throwing an exception
// returns: true on success; false if file descriptor type isn't supported and soft_fail == true
// throws: std::system_error or std::bad_alloc on failure
- bool addFdWatch(int fd, void *userdata, int flags, bool enabled = true, bool soft_fail = false)
+ bool add_fd_watch(int fd, void *userdata, int flags, bool enabled = true, bool soft_fail = false)
{
struct epoll_event epevent;
// epevent.data.fd = fd;
return true;
}
- bool addBidiFdWatch(int fd, void *userdata, int flags, bool emulate)
+ bool add_bidi_fd_watch(int fd, void *userdata, int flags, bool emulate)
{
// No implementation.
throw std::system_error(std::make_error_code(std::errc::not_supported));
// flags specifies which watch to remove; ignored if the loop doesn't support
// separate read/write watches.
- void removeFdWatch(int fd, int flags) noexcept
+ void remove_fd_watch(int fd, int flags) noexcept
{
epoll_ctl(epfd, EPOLL_CTL_DEL, fd, nullptr);
}
- void removeFdWatch_nolock(int fd, int flags) noexcept
+ void remove_fd_watch_nolock(int fd, int flags) noexcept
{
- removeFdWatch(fd, flags);
+ remove_fd_watch(fd, flags);
}
- void removeBidiFdWatch(int fd) noexcept
+ void remove_bidi_fd_watch(int fd) noexcept
{
// Shouldn't be called for epoll.
- removeFdWatch(fd, IN_EVENTS | OUT_EVENTS);
+ remove_fd_watch(fd, IN_EVENTS | OUT_EVENTS);
}
// Note this will *replace* the old flags with the new, that is,
// it can enable *or disable* read/write events.
- void enableFdWatch(int fd, void *userdata, int flags) noexcept
+ void enable_fd_watch(int fd, void *userdata, int flags) noexcept
{
struct epoll_event epevent;
// epevent.data.fd = fd;
}
}
- void enableFdWatch_nolock(int fd, void *userdata, int flags)
+ void enable_fd_watch_nolock(int fd, void *userdata, int flags)
{
- enableFdWatch(fd, userdata, flags);
+ enable_fd_watch(fd, userdata, flags);
}
- void disableFdWatch(int fd, int flags) noexcept
+ void disable_fd_watch(int fd, int flags) noexcept
{
struct epoll_event epevent;
// epevent.data.fd = fd;
}
}
- void disableFdWatch_nolock(int fd, int flags) noexcept
+ void disable_fd_watch_nolock(int fd, int flags) noexcept
{
- disableFdWatch(fd, flags);
+ disable_fd_watch(fd, flags);
}
-
+
// Note signal should be masked before call.
- void addSignalWatch(int signo, void *userdata)
+ void add_signal_watch(int signo, void *userdata)
{
std::lock_guard<decltype(Base::lock)> guard(Base::lock);
+ add_signal_watch_nolock(signo, userdata);
+ }
+ // Note signal should be masked before call.
+ void add_signal_watch_nolock(int signo, void *userdata)
+ {
sigdataMap[signo] = userdata;
// Modify the signal fd to watch the new signal
}
// Note, called with lock held:
- void rearmSignalWatch_nolock(int signo) noexcept
+ void rearm_signal_watch_nolock(int signo, void *userdata) noexcept
{
sigaddset(&sigmask, signo);
signalfd(sigfd, &sigmask, SFD_NONBLOCK | SFD_CLOEXEC);
}
- void removeSignalWatch_nolock(int signo) noexcept
+ void remove_signal_watch_nolock(int signo) noexcept
{
sigdelset(&sigmask, signo);
signalfd(sigfd, &sigmask, 0);
}
- void removeSignalWatch(int signo) noexcept
+ void remove_signal_watch(int signo) noexcept
{
std::lock_guard<decltype(Base::lock)> guard(Base::lock);
- removeSignalWatch_nolock(signo);
+ remove_signal_watch_nolock(signo);
}
// If events are pending, process an unspecified number of them.
// simultaneously).
// If processing an event removes a watch, there is a possibility
// that the watched event will still be reported (if it has
- // occurred) before pullEvents() returns.
+ // occurred) before pull_events() returns.
//
// do_wait - if false, returns immediately if no events are
// pending.
- void pullEvents(bool do_wait)
+ void pull_events(bool do_wait)
{
epoll_event events[16];
int r = epoll_wait(epfd, events, 16, do_wait ? -1 : 0);
}
do {
- processEvents(events, r);
+ process_events(events, r);
r = epoll_wait(epfd, events, 16, 0);
} while (r > 0);
}
#include "dasynq-config.h"
#include "dasynq-mutex.h"
+#include "dasynq-util.h"
/*
* Mechanism for interrupting an event loop wait.
template <typename Base, typename Mutex> class interrupt_channel : public Base
{
-#ifdef HAVE_PIPE2
- int create_pipe(int filedes[2])
+ static inline int create_pipe(int filedes[2])
{
return pipe2(filedes, O_CLOEXEC | O_NONBLOCK);
}
-#else
- int create_pipe(int filedes[2])
- {
- int r = pipe(filedes);
- if (r != -1) {
- fcntl(filedes[0], F_SETFD, O_CLOEXEC);
- fcntl(filedes[1], F_SETFD, O_CLOEXEC);
- fcntl(filedes[0], F_SETFL, O_NONBLOCK);
- fcntl(filedes[1], F_SETFL, O_NONBLOCK);
- }
- return r;
- }
-#endif
int pipe_r_fd;
int pipe_w_fd;
pipe_w_fd = pipedes[1];
try {
- loop_mech->addFdWatch(pipe_r_fd, &pipe_r_fd, IN_EVENTS);
+ loop_mech->add_fd_watch(pipe_r_fd, &pipe_r_fd, IN_EVENTS);
}
catch (...) {
close (pipe_r_fd);
}
template <typename T>
- void receiveFdEvent(T &loop_mech, typename Base::FD_r fd_r, void * userdata, int flags)
+ void receive_fd_event(T &loop_mech, typename Base::traits_t::fd_r fd_r_a, void * userdata, int flags)
{
if (userdata == &pipe_r_fd) {
// try to clear the pipe
read(pipe_r_fd, buf, 64);
}
else {
- Base::receiveFdEvent(loop_mech, fd_r, userdata, flags);
+ Base::receive_fd_event(loop_mech, fd_r_a, userdata, flags);
}
}
// Timer implementation based on the (basically obsolete) POSIX itimer interface.
-template <class Base> class ITimerEvents : public timer_base<Base>
+// With this timer implementation, we only use one clock, and allow no distinction between the
+// monotonic and system time.
+
+template <class Base> class itimer_events : public timer_base<Base>
{
private:
- timer_queue_t timer_queue;
-
-#if defined(CLOCK_MONOTONIC)
- static inline void get_curtime(struct timespec &curtime)
- {
- clock_gettime(CLOCK_MONOTONIC, &curtime);
- }
-#else
- static inline void get_curtime(struct timespec &curtime)
- {
- struct timeval curtime_tv;
- gettimeofday(&curtime_tv, nullptr);
- curtime.tv_sec = curtime_tv.tv_sec;
- curtime.tv_nsec = curtime_tv.tv_usec * 1000;
- }
-#endif
- // Set the timerfd timeout to match the first timer in the queue (disable the timerfd
- // if there are no active timers).
+ // Set the alarm timeout to match the first timer in the queue (disable the alarm if there are no
+ // active timers).
void set_timer_from_queue()
{
time_val newtime;
struct itimerval newalarm;
- if (timer_queue.empty()) {
- newalarm.it_value = {0, 0};
- newalarm.it_interval = {0, 0};
- setitimer(ITIMER_REAL, &newalarm, nullptr);
- return;
+
+ bool interval_set = false;
+ time_val interval_tv = {0, 0};
+
+ auto &timer_queue = this->queue_for_clock(clock_type::SYSTEM);
+ if (! timer_queue.empty()) {
+ newtime = timer_queue.get_root_priority();
+
+ time_val curtimev;
+ timer_base<Base>::get_time(curtimev, clock_type::SYSTEM, true);
+
+ // interval before next timeout:
+ if (curtimev < newtime) {
+ interval_tv = newtime - curtimev;
+ }
+
+ interval_set = true;
}
- newtime = timer_queue.get_root_priority();
-
- struct timespec curtime;
- get_curtime(curtime);
- time_val curtimev = curtime;
-
- newalarm.it_interval = {0, 0};
- if (curtimev < newtime) {
- newalarm.it_value.tv_sec = newtime.seconds() - curtime.tv_sec;
- if (curtimev.nseconds() > newtime.nseconds()) {
- newalarm.it_value.tv_usec = (1000000000 - curtimev.nseconds()
- + newtime.nseconds()) / 1000;
- newalarm.it_value.tv_sec--;
+#ifdef CLOCK_MONOTONIC
+ auto &mono_timer_queue = this->queue_for_clock(clock_type::MONOTONIC);
+
+ if (! mono_timer_queue.empty()) {
+
+ // If we have a separate monotonic clock, we get the interval for the expiry of the next monotonic
+ // timer and use the lesser of the system interval and monotonic interval:
+ time_val mono_newtime = mono_timer_queue.get_root_priority();
+
+ time_val curtimev_mono;
+ timer_base<Base>::get_time(curtimev_mono, clock_type::MONOTONIC, true);
+
+ time_val interval_mono = {0, 0};
+ if (curtimev_mono < mono_newtime) {
+ interval_mono = mono_newtime - curtimev_mono;
}
- else {
- newalarm.it_value.tv_usec = (newtime.nseconds() - curtime.tv_nsec) / 1000;
+
+ if (! interval_set || interval_mono < interval_tv) {
+ interval_tv = interval_mono;
}
+
+ interval_set = true;
}
- else {
+#endif
+
+ newalarm.it_value.tv_sec = interval_tv.seconds();
+ newalarm.it_value.tv_usec = interval_tv.nseconds() / 1000;
+ newalarm.it_interval.tv_sec = 0;
+ newalarm.it_interval.tv_usec = 0;
+
+ if (interval_set && newalarm.it_value.tv_sec == 0 && newalarm.it_value.tv_usec == 0) {
// We passed the timeout: set alarm to expire immediately (we must use {0,1} as
// {0,0} disables the timer).
// TODO: it would be better if we just processed the appropriate timers here,
protected:
- using SigInfo = typename Base::SigInfo;
+ using sigdata_t = typename Base::sigdata_t;
template <typename T>
- bool receive_signal(T & loop_mech, SigInfo &siginfo, void *userdata)
+ bool receive_signal(T & loop_mech, sigdata_t &siginfo, void *userdata)
{
if (siginfo.get_signo() == SIGALRM) {
- struct timespec curtime;
- get_curtime(curtime);
+ auto &timer_queue = this->queue_for_clock(clock_type::SYSTEM);
+ if (! timer_queue.empty()) {
+ struct timespec curtime;
+ timer_base<Base>::get_time(curtime, clock_type::SYSTEM, true);
+ timer_base<Base>::process_timer_queue(timer_queue, curtime);
+ }
- timer_base<Base>::process_timer_queue(timer_queue, curtime);
+#ifdef CLOCK_MONOTONIC
+ auto &mono_timer_queue = this->queue_for_clock(clock_type::MONOTONIC);
+ if (! mono_timer_queue.empty()) {
+ struct timespec curtime_mono;
+ timer_base<Base>::get_time(curtime_mono, clock_type::MONOTONIC, true);
+ timer_base<Base>::process_timer_queue(mono_timer_queue, curtime_mono);
+ }
+#endif
- // arm timerfd with timeout from head of queue
+ // arm alarm with timeout from head of queue
set_timer_from_queue();
return false; // don't disable signal watch
}
public:
+ class traits_t : public Base::traits_t
+ {
+ constexpr static bool full_timer_support = false;
+ };
+
template <typename T> void init(T *loop_mech)
{
sigset_t sigmask;
- sigprocmask(SIG_UNBLOCK, nullptr, &sigmask);
+ this->sigmaskf(SIG_UNBLOCK, nullptr, &sigmask);
sigaddset(&sigmask, SIGALRM);
- sigprocmask(SIG_SETMASK, &sigmask, nullptr);
- loop_mech->addSignalWatch(SIGALRM, nullptr);
+ this->sigmaskf(SIG_SETMASK, &sigmask, nullptr);
+ loop_mech->add_signal_watch(SIGALRM, nullptr);
Base::init(loop_mech);
}
-
- void addTimer(timer_handle_t &h, void *userdata, clock_type clock = clock_type::MONOTONIC)
- {
- std::lock_guard<decltype(Base::lock)> guard(Base::lock);
- timer_queue.allocate(h, userdata);
- }
-
- void removeTimer(timer_handle_t &timer_id, clock_type clock = clock_type::MONOTONIC) noexcept
- {
- std::lock_guard<decltype(Base::lock)> guard(Base::lock);
- removeTimer_nolock(timer_id, clock);
- }
-
- void removeTimer_nolock(timer_handle_t &timer_id, clock_type clock = clock_type::MONOTONIC) noexcept
- {
- if (timer_queue.is_queued(timer_id)) {
- timer_queue.remove(timer_id);
- }
- timer_queue.deallocate(timer_id);
- }
// starts (if not started) a timer to timeout at the given time. Resets the expiry count to 0.
// enable: specifies whether to enable reporting of timeouts/intervals
- void setTimer(timer_handle_t &timer_id, const time_val &timeouttv, const time_val &intervaltv,
+ void set_timer(timer_handle_t &timer_id, const time_val &timeouttv, const time_val &intervaltv,
bool enable, clock_type clock = clock_type::MONOTONIC) noexcept
{
+ auto &timer_queue = this->queue_for_clock(clock);
timespec timeout = timeouttv;
timespec interval = intervaltv;
}
// Set timer relative to current time:
- void setTimerRel(timer_handle_t &timer_id, const time_val &timeouttv, const time_val &intervaltv,
+ void set_timer_rel(timer_handle_t &timer_id, const time_val &timeouttv, const time_val &intervaltv,
bool enable, clock_type clock = clock_type::MONOTONIC) noexcept
{
timespec timeout = timeouttv;
timespec interval = intervaltv;
- // TODO consider caching current time somehow; need to decide then when to update cached value.
struct timespec curtime;
- get_curtime(curtime);
+ timer_base<Base>::get_time(curtime, clock, false);
curtime.tv_sec += timeout.tv_sec;
curtime.tv_nsec += timeout.tv_nsec;
if (curtime.tv_nsec > 1000000000) {
curtime.tv_nsec -= 1000000000;
curtime.tv_sec++;
}
- setTimer(timer_id, curtime, interval, enable, clock);
- }
-
- // Enables or disabling report of timeouts (does not stop timer)
- void enableTimer(timer_handle_t &timer_id, bool enable, clock_type clock = clock_type::MONOTONIC) noexcept
- {
- std::lock_guard<decltype(Base::lock)> guard(Base::lock);
- enableTimer_nolock(timer_id, enable, clock);
+ set_timer(timer_id, curtime, interval, enable, clock);
}
- void enableTimer_nolock(timer_handle_t &timer_id, bool enable, clock_type = clock_type::MONOTONIC) noexcept
- {
- auto &node_data = timer_queue.node_data(timer_id);
- auto expiry_count = node_data.expiry_count;
- if (expiry_count != 0) {
- node_data.expiry_count = 0;
- Base::receiveTimerExpiry(timer_id, node_data.userdata, expiry_count);
- }
- else {
- timer_queue.node_data(timer_id).enabled = enable;
- }
- }
-
void stop_timer(timer_handle_t &timer_id, clock_type clock = clock_type::MONOTONIC) noexcept
{
std::lock_guard<decltype(Base::lock)> guard(Base::lock);
void stop_timer_nolock(timer_handle_t &timer_id, clock_type clock = clock_type::MONOTONIC) noexcept
{
+ auto &timer_queue = this->queue_for_clock(clock);
if (timer_queue.is_queued(timer_id)) {
bool was_first = (&timer_queue.get_root()) == &timer_id;
timer_queue.remove(timer_id);
}
}
}
-
- void get_time(time_val &tv, clock_type clock, bool force_update) noexcept
- {
- timespec ts;
- get_time(ts, clock, force_update);
- tv = ts;
- }
-
- void get_time(timespec &ts, clock_type clock, bool force_update) noexcept
- {
- get_curtime(ts);
- }
};
}
#include <unordered_map>
#include <vector>
-#ifdef __OpenBSD__
-#include <sys/signal.h> // for __thrsigdivert aka sigtimedwait
-#include <sys/syscall.h>
-extern "C" {
- int __thrsigdivert(sigset_t set, siginfo_t *info, const struct timespec * timeout);
-}
-#endif
-
#include <sys/event.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
+#include <sys/stat.h>
#include <unistd.h>
#include <signal.h>
namespace dasynq {
-template <class Base> class KqueueLoop;
+template <class Base> class kqueue_loop;
-class KqueueTraits
+class kqueue_traits
{
- template <class Base> friend class KqueueLoop;
+ template <class Base> friend class kqueue_loop;
public:
- class SigInfo
+ class sigdata_t
{
- template <class Base> friend class KqueueLoop;
+ template <class Base> friend class kqueue_loop;
siginfo_t info;
public:
+ // mandatory:
int get_signo() { return info.si_signo; }
int get_sicode() { return info.si_code; }
- void * get_ssiaddr() { return info.si_addr; }
+ pid_t get_sipid() { return info.si_pid; }
+ uid_t get_siuid() { return info.si_uid; }
+ void * get_siaddr() { return info.si_addr; }
+ int get_sistatus() { return info.si_status; }
+ int get_sival_int() { return info.si_value.sival_int; }
+ void * get_sival_ptr() { return info.si_value.sival_ptr; }
+ // XSI
+ int get_sierrno() { return info.si_errno; }
+
+ // XSR (streams) OB (obselete)
+#if !defined(__OpenBSD__)
+ // Note: OpenBSD doesn't have this; most other systems do. Technically it is part of the STREAMS
+ // interface.
+ int get_siband() { return info.si_band; }
+#endif
+
void set_signo(int signo) { info.si_signo = signo; }
};
- class FD_r;
+ class fd_r;
// File descriptor optional storage. If the mechanism can return the file descriptor, this
// class will be empty, otherwise it can hold a file descriptor.
- class FD_s {
+ class fd_s {
DASYNQ_EMPTY_BODY
};
// File descriptor reference (passed to event callback). If the mechanism can return the
// file descriptor, this class holds the file descriptor. Otherwise, the file descriptor
- // must be stored in an FD_s instance.
- class FD_r {
+ // must be stored in an fd_s instance.
+ class fd_r {
int fd;
public:
- int getFd(FD_s ss)
+ int getFd(fd_s ss)
{
return fd;
}
- FD_r(int nfd) : fd(nfd)
+ fd_r(int nfd) : fd(nfd)
{
}
};
const static bool has_bidi_fd_watch = false;
const static bool has_separate_rw_fd_watches = true;
- const static bool supports_childwatch_reservation = true;
};
-#if defined(__OpenBSD__) && _POSIX_REALTIME_SIGNALS <= 0
-// OpenBSD has no sigtimedwait (or sigwaitinfo) but does have "__thrsigdivert", which is
-// essentially an incomplete version of the same thing. Discussion with OpenBSD developer
-// Ted Unangst suggested that the siginfo_t structure returned might not always have all fields
-// set correctly. Furthermore there is a bug (at least in 5.9) such that specifying a zero
-// timeout (or indeed any timeout less than a tick) results in NO timeout. We get around this by
-// instead specifying an *invalid* timeout, which won't error out if a signal is pending.
-static inline int sigtimedwait(const sigset_t *ssp, siginfo_t *info, struct timespec *timeout)
-{
- // We know that we're only called with a timeout of 0 (which doesn't work properly) and
- // that we safely overwrite the timeout. So, we set tv_nsec to an invalid value, which
- // will cause EINVAL to be returned, but will still pick up any pending signals *first*.
- timeout->tv_nsec = 1000000001;
- return __thrsigdivert(*ssp, info, timeout);
-}
-#endif
-
-#if defined(__OpenBSD__) || _POSIX_REALTIME_SIGNALS > 0
+#if _POSIX_REALTIME_SIGNALS > 0
static inline void prepare_signal(int signo) { }
static inline void unprep_signal(int signo) { }
#endif
-template <class Base> class KqueueLoop : public Base
+template <class Base> class kqueue_loop : public Base
{
int kqfd; // kqueue fd
- sigset_t sigmask; // enabled signal watch mask
- // Map of signal number to user data pointer. If kqueue had been better thought-through,
- // we shouldn't need this. Although we can associate user data with an EVFILT_SIGNAL kqueue
- // filter, the problem is that the kqueue signal report *coexists* with the regular signal
+ // The kqueue signal reporting mechanism *coexists* with the regular signal
// delivery mechanism without having any connection to it. Whereas regular signals can be
// queued (especially "realtime" signals, via sigqueue()), kqueue just maintains a counter
// of delivery attempts and clears this when we read the event. What this means is that
// kqueue won't necessarily tell us if signals are pending, in the case that:
// 1) it already reported the attempted signal delivery and
- // 2) more than one of the same signal was pending at that time and
+ // 2) more than instance one of the same signal was pending at that time and
// 3) no more deliveries of the same signal have been attempted in the meantime.
- // Of course, if kqueue doesn't report the signal, then it doesn't give us the data associated
- // with the event, so we need to maintain that separately too:
- std::unordered_map<int, void *> sigdataMap;
-
+ // Also, kqueue won't tell us about signals that were pending at the time the signal filter
+ // was added. Finally, because pending signals can be merged, the count of delivery attempts
+ // provided by kqueue does not necessarily match the number of signals actually pending.
+ //
+ // Note that POSIX allows for multiple instances of a signal to be pending even on systems
+ // that don't support queueing of signals.
+ //
+ // Ultimately, this means we need to check for pending signals independently of what kqueue
+ // tells us.
+
// Base contains:
// lock - a lock that can be used to protect internal structure.
// receive*() methods will be called with lock held.
- // receiveSignal(SigInfo &, user *) noexcept
- // receiveFdEvent(FD_r, user *, int flags) noexcept
+ // receive_signal(sigdata_t &, user *) noexcept
+ // receive_fd_event(fd_r, user *, int flags) noexcept
- using SigInfo = KqueueTraits::SigInfo;
- using FD_r = typename KqueueTraits::FD_r;
+ using sigdata_t = kqueue_traits::sigdata_t;
+ using fd_r = typename kqueue_traits::fd_r;
- void processEvents(struct kevent *events, int r)
+ // The flag to specify poll() semantics for regular file readiness: that is, we want
+ // ready-for-read to be returned even at end of file:
+#if defined(NOTE_FILE_POLL)
+ // FreeBSD:
+ constexpr static int POLL_SEMANTICS = NOTE_FILE_POLL;
+#else
+ // Note that macOS has an "EV_POLL" defined that looks like it should give poll semantics
+ // when passed as a flag. However, it is filtered at the syscall entry so we cannot use it in
+ // kqueue. (The kernel uses it internally to implement poll()).
+ constexpr static int POLL_SEMANTICS = 0;
+#endif
+
+ void process_events(struct kevent *events, int r)
{
std::lock_guard<decltype(Base::lock)> guard(Base::lock);
for (int i = 0; i < r; i++) {
if (events[i].filter == EVFILT_SIGNAL) {
- SigInfo siginfo;
- if (get_siginfo(events[i].ident, &siginfo.info)
- && Base::receive_signal(*this, siginfo, (void *)events[i].udata)) {
- sigdelset(&sigmask, events[i].ident);
- events[i].flags = EV_DISABLE;
- }
- else {
- events[i].flags = EV_ENABLE;
- }
+ bool reenable = pull_signal(events[i].ident, events[i].udata);
+ events[i].flags = reenable ? EV_ENABLE : EV_DISABLE;
}
else if (events[i].filter == EVFILT_READ || events[i].filter == EVFILT_WRITE) {
int flags = events[i].filter == EVFILT_READ ? IN_EVENTS : OUT_EVENTS;
- Base::receiveFdEvent(*this, FD_r(events[i].ident), events[i].udata, flags);
+ Base::receive_fd_event(*this, fd_r(events[i].ident), events[i].udata, flags);
events[i].flags = EV_DISABLE | EV_CLEAR;
// we use EV_CLEAR to clear the EOF status of fifos/pipes (and wait for
// another connection).
kevent(kqfd, events, r, nullptr, 0, nullptr);
}
+ // Pull a signal from pending, and report it, until it is no longer pending or the watch
+ // should be disabled. Call with lock held.
+ // Returns: true if watcher should be enabled, false if disabled.
+ bool pull_signal(int signo, void *userdata)
+ {
+ bool enable_filt = true;
+ sigdata_t siginfo;
+
+#if _POSIX_REALTIME_SIGNALS > 0
+ struct timespec timeout = {0, 0};
+ sigset_t sigw_mask;
+ sigemptyset(&sigw_mask);
+ sigaddset(&sigw_mask, signo);
+ int rsigno = sigtimedwait(&sigw_mask, &siginfo.info, &timeout);
+ while (rsigno > 0) {
+ if (Base::receive_signal(*this, siginfo, userdata)) {
+ enable_filt = false;
+ break;
+ }
+ rsigno = sigtimedwait(&sigw_mask, &siginfo.info, &timeout);
+ }
+#else
+ // we have no sigtimedwait.
+ sigset_t pending_sigs;
+ sigpending(&pending_sigs);
+ while (sigismember(&pending_sigs, signo)) {
+ get_siginfo(signo, &siginfo.info);
+ if (Base::receive_signal(*this, siginfo, userdata)) {
+ enable_filt = false;
+ break;
+ }
+ sigpending(&pending_sigs);
+ }
+#endif
+ return enable_filt;
+ }
+
public:
/**
- * KqueueLoop constructor.
+ * kqueue_loop constructor.
*
* Throws std::system_error or std::bad_alloc if the event loop cannot be initialised.
*/
- KqueueLoop()
+ kqueue_loop()
{
kqfd = kqueue();
if (kqfd == -1) {
throw std::system_error(errno, std::system_category());
}
- sigemptyset(&sigmask);
Base::init(this);
}
- ~KqueueLoop()
+ ~kqueue_loop()
{
close(kqfd);
}
- void setFilterEnabled(short filterType, uintptr_t ident, void *udata, bool enable)
+ void set_filter_enabled(short filterType, uintptr_t ident, void *udata, bool enable)
{
- // Note, on OpenBSD enabling or disabling filter will not alter the filter parameters (udata etc);
+ // Note, on OpenBSD enabling or disabling filter will not alter the filter parameters (udata etc);
// on OS X however, it will. Therefore we set udata here (to the same value as it was originally
// set) in order to work correctly on both kernels.
struct kevent kev;
- EV_SET(&kev, ident, filterType, enable ? EV_ENABLE : EV_DISABLE, 0, 0, udata);
+ int fflags = (filterType == EVFILT_READ) ? POLL_SEMANTICS : 0;
+ EV_SET(&kev, ident, filterType, enable ? EV_ENABLE : EV_DISABLE, fflags, 0, udata);
kevent(kqfd, &kev, 1, nullptr, 0, nullptr);
}
- void removeFilter(short filterType, uintptr_t ident)
+ void remove_filter(short filterType, uintptr_t ident)
{
struct kevent kev;
EV_SET(&kev, ident, filterType, EV_DELETE, 0, 0, 0);
// (only one of IN_EVENTS/OUT_EVENTS can be specified)
// soft_fail: true if unsupported file descriptors should fail by returning false instead
// of throwing an exception
- // returns: true on success; false if file descriptor type isn't supported and soft_fail == true
+ // returns: true on success; false if file descriptor type isn't supported and emulate == true
// throws: std::system_error or std::bad_alloc on failure
- bool addFdWatch(int fd, void *userdata, int flags, bool enabled = true, bool emulate = false)
+ bool add_fd_watch(int fd, void *userdata, int flags, bool enabled = true, bool emulate = false)
{
short filter = (flags & IN_EVENTS) ? EVFILT_READ : EVFILT_WRITE;
+ if (filter == EVFILT_READ && POLL_SEMANTICS == 0 && emulate) {
+ // We can't request poll semantics, so check for regular file:
+ struct stat statbuf;
+ if (fstat(fd, &statbuf) == -1) {
+ throw new std::system_error(errno, std::system_category());
+ }
+ if ((statbuf.st_mode & S_IFMT) == S_IFREG) {
+ // Regular file: emulation required
+ return false;
+ }
+ }
+
+ int fflags = (filter == EVFILT_READ) ? POLL_SEMANTICS : 0;
+
struct kevent kev;
- EV_SET(&kev, fd, filter, EV_ADD | (enabled ? 0 : EV_DISABLE), 0, 0, userdata);
+ EV_SET(&kev, fd, filter, EV_ADD | (enabled ? 0 : EV_DISABLE), fflags, 0, userdata);
if (kevent(kqfd, &kev, 1, nullptr, 0, nullptr) == -1) {
- // Note that kqueue supports EVFILT_READ on regular file fd's, but not EVFIL_WRITE.
- if (filter == EVFILT_WRITE && errno == EINVAL) {
+ // Note that kqueue supports EVFILT_READ on regular file fd's, but not EVFILT_WRITE.
+ if (filter == EVFILT_WRITE && errno == EINVAL && emulate) {
return false; // emulate
}
throw new std::system_error(errno, std::system_category());
// returns: 0 on success
// IN_EVENTS if in watch requires emulation
// OUT_EVENTS if out watch requires emulation
- int addBidiFdWatch(int fd, void *userdata, int flags, bool emulate = false)
+ int add_bidi_fd_watch(int fd, void *userdata, int flags, bool emulate = false)
{
#ifdef EV_RECEIPT
struct kevent kev[2];
struct kevent kev_r[2];
short rflags = EV_ADD | ((flags & IN_EVENTS) ? 0 : EV_DISABLE) | EV_RECEIPT;
short wflags = EV_ADD | ((flags & OUT_EVENTS) ? 0 : EV_DISABLE) | EV_RECEIPT;
- EV_SET(&kev[0], fd, EVFILT_READ, rflags, 0, 0, userdata);
+ EV_SET(&kev[0], fd, EVFILT_READ, rflags, POLL_SEMANTICS, 0, userdata);
EV_SET(&kev[1], fd, EVFILT_WRITE, wflags, 0, 0, userdata);
int r = kevent(kqfd, kev, 2, kev_r, 2, nullptr);
if (kev_r[1].data != 0) {
if (emulate) {
- return OUT_EVENTS;
+ // We can emulate, but, do we have correct semantics?
+ if (POLL_SEMANTICS != 0) {
+ return OUT_EVENTS;
+ }
+
+ // if we can't get poll semantics, emulate for read as well:
+ // first remove read watch:
+ EV_SET(&kev[0], fd, EVFILT_READ, EV_DELETE, 0, 0, userdata);
+ kevent(kqfd, kev, 1, nullptr, 0, nullptr);
+ return IN_EVENTS | OUT_EVENTS;
}
// remove read watch
EV_SET(&kev[0], fd, EVFILT_READ, EV_DELETE, 0, 0, userdata);
// flags specifies which watch to remove; ignored if the loop doesn't support
// separate read/write watches.
- void removeFdWatch(int fd, int flags)
+ void remove_fd_watch(int fd, int flags)
{
- removeFilter((flags & IN_EVENTS) ? EVFILT_READ : EVFILT_WRITE, fd);
+ remove_filter((flags & IN_EVENTS) ? EVFILT_READ : EVFILT_WRITE, fd);
}
- void removeFdWatch_nolock(int fd, int flags)
+ void remove_fd_watch_nolock(int fd, int flags)
{
- removeFdWatch(fd, flags);
+ remove_fd_watch(fd, flags);
}
- void removeBidiFdWatch(int fd) noexcept
+ void remove_bidi_fd_watch(int fd) noexcept
{
struct kevent kev[2];
EV_SET(&kev[0], fd, EVFILT_READ, EV_DELETE, 0, 0, nullptr);
kevent(kqfd, kev, 2, nullptr, 0, nullptr);
}
- void enableFdWatch(int fd, void *userdata, int flags)
+ void enable_fd_watch(int fd, void *userdata, int flags)
{
- setFilterEnabled((flags & IN_EVENTS) ? EVFILT_READ : EVFILT_WRITE, fd, userdata, true);
+ set_filter_enabled((flags & IN_EVENTS) ? EVFILT_READ : EVFILT_WRITE, fd, userdata, true);
}
- void enableFdWatch_nolock(int fd, void *userdata, int flags)
+ void enable_fd_watch_nolock(int fd, void *userdata, int flags)
{
- enableFdWatch(fd, userdata, flags);
+ enable_fd_watch(fd, userdata, flags);
}
- void disableFdWatch(int fd, int flags)
+ void disable_fd_watch(int fd, int flags)
{
- setFilterEnabled((flags & IN_EVENTS) ? EVFILT_READ : EVFILT_WRITE, fd, nullptr, false);
+ set_filter_enabled((flags & IN_EVENTS) ? EVFILT_READ : EVFILT_WRITE, fd, nullptr, false);
}
- void disableFdWatch_nolock(int fd, int flags)
+ void disable_fd_watch_nolock(int fd, int flags)
{
- disableFdWatch(fd, flags);
+ disable_fd_watch(fd, flags);
}
-
+
// Note signal should be masked before call.
- void addSignalWatch(int signo, void *userdata)
+ void add_signal_watch(int signo, void *userdata)
{
std::lock_guard<decltype(Base::lock)> guard(Base::lock);
-
- sigdataMap[signo] = userdata;
- sigaddset(&sigmask, signo);
-
+ add_signal_watch_nolock(signo, userdata);
+ }
+
+ // Note signal should be masked before call.
+ void add_signal_watch_nolock(int signo, void *userdata)
+ {
prepare_signal(signo);
+ // We need to register the filter with the kqueue early, to avoid a race where we miss
+ // signals:
struct kevent evt;
- EV_SET(&evt, signo, EVFILT_SIGNAL, EV_ADD, 0, 0, userdata);
- // TODO use EV_DISPATCH if available (not on OpenBSD/OS X)
-
+ EV_SET(&evt, signo, EVFILT_SIGNAL, EV_ADD | EV_DISABLE, 0, 0, userdata);
if (kevent(kqfd, &evt, 1, nullptr, 0, nullptr) == -1) {
throw new std::system_error(errno, std::system_category());
}
+ // TODO use EV_DISPATCH if available (not on OpenBSD/OS X)
+
+ // The signal might be pending already but won't be reported by kqueue in that case. We can queue
+ // it immediately (note that it might be pending multiple times, so we need to re-check once signal
+ // processing finishes if it is re-armed).
+
+ bool enable_filt = pull_signal(signo, userdata);
+
+ if (enable_filt) {
+ evt.flags = EV_ENABLE;
+ if (kevent(kqfd, &evt, 1, nullptr, 0, nullptr) == -1) {
+ throw new std::system_error(errno, std::system_category());
+ }
+ }
}
// Note, called with lock held:
- void rearmSignalWatch_nolock(int signo) noexcept
+ void rearm_signal_watch_nolock(int signo, void *userdata) noexcept
{
- sigaddset(&sigmask, signo);
-
- struct kevent evt;
- EV_SET(&evt, signo, EVFILT_SIGNAL, EV_ENABLE, 0, 0, 0);
- // TODO use EV_DISPATCH if available (not on OpenBSD)
-
- kevent(kqfd, &evt, 1, nullptr, 0, nullptr);
+ if (pull_signal(signo, userdata)) {
+ struct kevent evt;
+ EV_SET(&evt, signo, EVFILT_SIGNAL, EV_ENABLE, 0, 0, userdata);
+ // TODO use EV_DISPATCH if available (not on OpenBSD)
+
+ kevent(kqfd, &evt, 1, nullptr, 0, nullptr);
+ }
}
- void removeSignalWatch_nolock(int signo) noexcept
+ void remove_signal_watch_nolock(int signo) noexcept
{
unprep_signal(signo);
- sigdelset(&sigmask, signo);
struct kevent evt;
EV_SET(&evt, signo, EVFILT_SIGNAL, EV_DELETE, 0, 0, 0);
kevent(kqfd, &evt, 1, nullptr, 0, nullptr);
}
- void removeSignalWatch(int signo) noexcept
+ void remove_signal_watch(int signo) noexcept
{
std::lock_guard<decltype(Base::lock)> guard(Base::lock);
- removeSignalWatch_nolock(signo);
- }
-
- private:
-
- // We actually need to check pending signals before polling the kqueue, since kqueue can
- // count signals as they are delivered but the count is cleared when we poll the kqueue,
- // meaning that signals might still be pending if they were queued multiple times at the
- // last poll (since we report only one signal delivery at a time and the watch is
- // automatically disabled each time).
- //
- // The check is not necessary on systems that don't queue signals.
-void pull_signals()
- {
-#if _POSIX_REALTIME_SIGNALS > 0
- // TODO we should only poll for signals that *have* been reported
- // as being raised more than once prior via kevent, rather than all
- // signals that have been registered - in many cases that may allow
- // us to skip the sigtimedwait call altogether.
- {
- std::lock_guard<decltype(Base::lock)> guard(Base::lock);
-
- struct timespec timeout;
- timeout.tv_sec = 0;
- timeout.tv_nsec = 0;
- SigInfo siginfo;
- int rsigno = sigtimedwait(&sigmask, &siginfo.info, &timeout);
- while (rsigno > 0) {
- if (Base::receiveSignal(*this, siginfo, sigdataMap[rsigno])) {
- sigdelset(&sigmask, rsigno);
- // TODO accumulate and disable multiple filters with a single kevents call
- // rather than disabling each individually
- setFilterEnabled(EVFILT_SIGNAL, rsigno, sigdataMap[rsigno], false);
- }
- rsigno = sigtimedwait(&sigmask, &siginfo.info, &timeout);
- }
- }
-#endif
+ remove_signal_watch_nolock(signo);
}
public:
// simultaneously).
// If processing an event removes a watch, there is a possibility
// that the watched event will still be reported (if it has
- // occurred) before pullEvents() returns.
+ // occurred) before pull_events() returns.
//
// do_wait - if false, returns immediately if no events are
// pending.
- void pullEvents(bool do_wait)
+ void pull_events(bool do_wait)
{
- pull_signals();
-
struct kevent events[16];
struct timespec ts;
+
ts.tv_sec = 0;
ts.tv_nsec = 0;
int r = kevent(kqfd, nullptr, 0, events, 16, do_wait ? nullptr : &ts);
}
do {
- processEvents(events, r);
+ process_events(events, r);
r = kevent(kqfd, nullptr, 0, events, 16, &ts);
} while (r > 0);
}
#ifndef DASYNQ_MUTEX_H_INCLUDED
#define DASYNQ_MUTEX_H_INCLUDED
-//#include <pthread.h>
#include <mutex>
namespace dasynq {
-// Simple non-recursive mutex, with priority inheritance to avoid priority inversion.
-/*
-class DMutex
-{
- private:
- pthread_mutex_t mutex;
-
- public:
- DMutex()
- {
- // Avoid priority inversion by using PTHREAD_PRIO_INHERIT
- pthread_mutexattr_t attribs;
- pthread_mutexattr_init(&attribs);
- pthread_mutexattr_setprotocol(&attribs, PTHREAD_PRIO_INHERIT);
- pthread_mutex_init(&mutex, &attribs);
- }
-
- void lock()
- {
- pthread_mutex_lock(&mutex);
- }
-
- void unlock()
- {
- pthread_mutex_unlock(&mutex);
- }
-};
-*/
-
-using DMutex = std::mutex;
-
// A "null" mutex, for which locking / unlocking actually does nothing.
class null_mutex
{
- DASYNQ_EMPTY_BODY
-
public:
void lock() { }
void unlock() { }
void try_lock() { }
-};
+ private:
+ DASYNQ_EMPTY_BODY;
+};
} // end of namespace
-
#endif
#ifndef DASYNC_NARYHEAP_H_INCLUDED
#define DASYNC_NARYHEAP_H_INCLUDED
-#include "dasynq-svec.h"
+#include <vector>
#include <type_traits>
#include <functional>
#include <limits>
HeapNode() { }
};
- svector<HeapNode> hvec;
+ std::vector<HeapNode> hvec;
using hindex_t = typename decltype(hvec)::size_type;
{
new (& hnd.hd) T(u...);
hnd.heap_index = -1;
- constexpr hindex_t max_allowed = std::numeric_limits<hindex_t>::is_signed ?
- std::numeric_limits<hindex_t>::max() : ((hindex_t) - 2);
+ hindex_t max_allowed = hvec.max_size();
if (num_nodes == max_allowed) {
throw std::bad_alloc();
{
num_nodes--;
- // shrink the capacity of hvec if num_nodes is sufficiently less than
- // its current capacity:
+ // shrink the capacity of hvec if num_nodes is sufficiently less than its current capacity. Why
+ // capacity/4? Because in general, capacity must be at least doubled when it is exceeded to get
+ // O(N) amortised insertion cost. If we shrink-to-fit every time num_nodes < capacity/2, this
+ // means we might potentially get pathological "bouncing" as num_nodes crosses the threshold
+ // repeatedly as nodes get added and removed.
+
if (num_nodes < hvec.capacity() / 4) {
- hvec.shrink_to(num_nodes * 2);
+ hvec.shrink_to_fit();
}
}
// Timer implementation based on POSIX create_timer et al.
// May require linking with -lrt
-template <class Base> class PosixTimerEvents : public timer_base<Base>
+template <class Base> class posix_timer_events : public timer_base<Base>
{
private:
- timer_queue_t real_timer_queue;
- timer_queue_t mono_timer_queue;
-
timer_t real_timer;
timer_t mono_timer;
protected:
- using SigInfo = typename Base::SigInfo;
+ using sigdata_t = typename Base::sigdata_t;
template <typename T>
- bool receive_signal(T & loop_mech, SigInfo &siginfo, void *userdata)
+ bool receive_signal(T & loop_mech, sigdata_t &siginfo, void *userdata)
{
+ auto &real_timer_queue = this->queue_for_clock(clock_type::SYSTEM);
+ auto &mono_timer_queue = this->queue_for_clock(clock_type::MONOTONIC);
+
if (siginfo.get_signo() == SIGALRM) {
- struct timespec curtime;
+ time_val curtime;
if (! real_timer_queue.empty()) {
- clock_gettime(CLOCK_REALTIME, &curtime);
- timer_base<Base>::process_timer_queue(real_timer_queue, curtime);
+ this->get_time(curtime, clock_type::SYSTEM, true);
+ this->process_timer_queue(real_timer_queue, curtime.get_timespec());
set_timer_from_queue(real_timer, real_timer_queue);
}
if (! mono_timer_queue.empty()) {
- clock_gettime(CLOCK_MONOTONIC, &curtime);
- timer_base<Base>::process_timer_queue(mono_timer_queue, curtime);
+ this->get_time(curtime, clock_type::MONOTONIC, true);
+ this->process_timer_queue(mono_timer_queue, curtime);
set_timer_from_queue(mono_timer, mono_timer_queue);
}
- // loop_mech.rearmSignalWatch_nolock(SIGALRM);
return false; // don't disable signal watch
}
else {
}
}
- timer_queue_t &queue_for_clock(clock_type clock)
- {
- switch (clock) {
- case clock_type::MONOTONIC:
- return mono_timer_queue;
- case clock_type::SYSTEM:
- return real_timer_queue;
- default:
- DASYNQ_UNREACHABLE;
- }
- }
-
timer_t &timer_for_clock(clock_type clock)
{
switch (clock) {
public:
+ class traits_t : public Base::traits_t
+ {
+ constexpr static bool full_timer_support = true;
+ };
+
template <typename T> void init(T *loop_mech)
{
sigset_t sigmask;
- sigprocmask(SIG_UNBLOCK, nullptr, &sigmask);
+ this->sigmaskf(SIG_UNBLOCK, nullptr, &sigmask);
sigaddset(&sigmask, SIGALRM);
- sigprocmask(SIG_SETMASK, &sigmask, nullptr);
- loop_mech->addSignalWatch(SIGALRM, nullptr);
+ this->sigmaskf(SIG_SETMASK, &sigmask, nullptr);
+ loop_mech->add_signal_watch(SIGALRM, nullptr);
struct sigevent timer_sigevent;
timer_sigevent.sigev_notify = SIGEV_SIGNAL;
Base::init(loop_mech);
}
- void addTimer(timer_handle_t &h, void *userdata, clock_type clock = clock_type::MONOTONIC)
- {
- std::lock_guard<decltype(Base::lock)> guard(Base::lock);
- queue_for_clock(clock).allocate(h, userdata);
- }
-
- void removeTimer(timer_handle_t &timer_id, clock_type clock = clock_type::MONOTONIC) noexcept
- {
- std::lock_guard<decltype(Base::lock)> guard(Base::lock);
- removeTimer_nolock(timer_id, clock);
- }
-
- void removeTimer_nolock(timer_handle_t &timer_id, clock_type clock = clock_type::MONOTONIC) noexcept
- {
- timer_queue_t &timer_queue = queue_for_clock(clock);
-
- if (timer_queue.is_queued(timer_id)) {
- timer_queue.remove(timer_id);
- }
- timer_queue.deallocate(timer_id);
- }
-
// starts (if not started) a timer to timeout at the given time. Resets the expiry count to 0.
// enable: specifies whether to enable reporting of timeouts/intervals
- void setTimer(timer_handle_t &timer_id, time_val &timeouttv, struct timespec &interval,
+ void set_timer(timer_handle_t &timer_id, time_val &timeouttv, struct timespec &interval,
bool enable, clock_type clock = clock_type::MONOTONIC) noexcept
{
timespec timeout = timeouttv;
std::lock_guard<decltype(Base::lock)> guard(Base::lock);
- timer_queue_t &timer_queue = queue_for_clock(clock);
+ timer_queue_t &timer_queue = this->queue_for_clock(clock);
timer_t &timer = timer_for_clock(clock);
auto &ts = timer_queue.node_data(timer_id);
}
// Set timer relative to current time:
- void setTimerRel(timer_handle_t &timer_id, const time_val &timeouttv, const time_val &intervaltv,
+ void set_timer_rel(timer_handle_t &timer_id, const time_val &timeouttv, const time_val &intervaltv,
bool enable, clock_type clock = clock_type::MONOTONIC) noexcept
{
timespec timeout = timeouttv;
curtime.tv_nsec -= 1000000000;
curtime.tv_sec++;
}
- setTimer(timer_id, curtime, interval, enable, clock);
- }
-
- // Enables or disabling report of timeouts (does not stop timer)
- void enableTimer(timer_handle_t &timer_id, bool enable, clock_type clock = clock_type::MONOTONIC) noexcept
- {
- std::lock_guard<decltype(Base::lock)> guard(Base::lock);
- enableTimer_nolock(timer_id, enable, clock);
- }
-
- void enableTimer_nolock(timer_handle_t &timer_id, bool enable, clock_type clock = clock_type::MONOTONIC) noexcept
- {
- timer_queue_t &timer_queue = queue_for_clock(clock);
-
- auto &node_data = timer_queue.node_data(timer_id);
- auto expiry_count = node_data.expiry_count;
- if (expiry_count != 0) {
- node_data.expiry_count = 0;
- Base::receiveTimerExpiry(timer_id, node_data.userdata, expiry_count);
- }
- else {
- timer_queue.node_data(timer_id).enabled = enable;
- }
+ set_timer(timer_id, curtime, interval, enable, clock);
}
void stop_timer(timer_handle_t &timer_id, clock_type clock = clock_type::MONOTONIC) noexcept
void stop_timer_nolock(timer_handle_t &timer_id, clock_type clock = clock_type::MONOTONIC) noexcept
{
- timer_queue_t &timer_queue = queue_for_clock(clock);
+ timer_queue_t &timer_queue = this->queue_for_clock(clock);
timer_t &timer = timer_for_clock(clock);
if (timer_queue.is_queued(timer_id)) {
}
}
- void get_time(timeval &tv, clock_type clock, bool force_update) noexcept
- {
- timespec ts;
- get_time(ts, clock, force_update);
- tv = ts;
- }
-
- void get_time(timespec &ts, clock_type clock, bool force_update) noexcept
- {
- int posix_clock_id = (clock == clock_type::MONOTONIC) ? CLOCK_MONOTONIC : CLOCK_REALTIME;
- clock_gettime(posix_clock_id, &ts);
- }
-
- ~PosixTimerEvents()
+ ~posix_timer_events()
{
timer_delete(mono_timer);
timer_delete(real_timer);
+++ /dev/null
-#ifndef DASYNQ_SVEC_H_INCLUDED
-#define DASYNQ_SVEC_H_INCLUDED
-
-#include <cstddef>
-#include <cstdlib>
-#include <utility>
-#include <new>
-
-// Vector with possibility to shrink capacity arbitrarily
-
-namespace dasynq {
-
-template <typename T>
-class svector
-{
- private:
- T * array;
- size_t size_v;
- size_t capacity_v;
-
- void check_capacity()
- {
- if (size_v == capacity_v) {
- // double capacity now:
- if (capacity_v == 0) capacity_v = 1;
- T * new_array = (T *) std::malloc(capacity_v * 2 * sizeof(T));
- if (new_array == nullptr) {
- throw std::bad_alloc();
- }
- for (size_t i = 0; i < size_v; i++) {
- new (&new_array[i]) T(std::move(array[i]));
- array[i].T::~T();
- }
- std::free(array);
- array = new_array;
- capacity_v *= 2;
- }
- }
-
- public:
- using size_type = size_t;
-
- svector() : array(nullptr), size_v(0), capacity_v(0)
- {
-
- }
-
- svector(const svector<T> &other)
- {
- capacity_v = other.size_v;
- size_v = other.size_v;
- array = (T *) std::malloc(capacity_v * sizeof(T));
- if (array == nullptr) {
- throw std::bad_alloc();
- }
- for (size_t i = 0; i < size_v; i++) {
- new (&array[i]) T(other[i]);
- }
- }
-
- ~svector()
- {
- for (size_t i = 0; i < size_v; i++) {
- array[i].T::~T();
- }
- std::free(array);
- }
-
- void push_back(const T &t)
- {
- check_capacity();
- new (&array[size_v]) T(t);
- size_v++;
- }
-
- void push_back(T &&t)
- {
- check_capacity();
- new (&array[size_v]) T(t);
- size_v++;
- }
-
- template <typename ...U>
- void emplace_back(U... args)
- {
- check_capacity();
- new (&array[size_v]) T(args...);
- size_v++;
- }
-
- void pop_back()
- {
- size_v--;
- }
-
- T &operator[](size_t index)
- {
- return array[index];
- }
-
- const T &operator[](size_t index) const
- {
- return array[index];
- }
-
- size_t size() const
- {
- return size_v;
- }
-
- size_t capacity() const
- {
- return capacity_v;
- }
-
- bool empty() const
- {
- return size_v == 0;
- }
-
- void reserve(size_t amount)
- {
- if (capacity_v < amount) {
- T * new_array = (T *) std::malloc(amount * sizeof(T));
- if (new_array == nullptr) {
- throw std::bad_alloc();
- }
- for (size_t i = 0; i < size_v; i++) {
- new (&new_array[i]) T(std::move(array[i]));
- array[i].T::~T();
- }
- std::free(array);
- array = new_array;
- capacity_v = amount;
- }
- }
-
- void shrink_to(size_t amount)
- {
- if (capacity_v > amount) {
- T * new_array = (T *) std::malloc(amount * sizeof(T));
- if (new_array == nullptr) {
- return;
- }
- for (size_t i = 0; i < size_v; i++) {
- new (&new_array[i]) T(std::move(array[i]));
- array[i].T::~T();
- }
- std::free(array);
- array = new_array;
- capacity_v = amount;
- }
- }
-
- T &back()
- {
- return array[size_v - 1];
- }
-
- T* begin()
- {
- return array;
- }
-
- const T *begin() const
- {
- return array;
- }
-
- T* end()
- {
- return array + size_v;
- }
-
- const T *end() const
- {
- return array + size_v;
- }
-};
-
-
-} // namespace
-
-#endif
#define DASYNQ_TIMERBASE_H_INCLUDED
#include <utility>
+#include <mutex>
#include "dasynq-naryheap.h"
using second_t = decltype(time.tv_sec);
using nsecond_t = decltype(time.tv_nsec);
- time_val()
+ time_val() noexcept
{
// uninitialised!
}
- time_val(const struct timespec &t)
+ time_val(const struct timespec &t) noexcept
{
time = t;
}
- time_val(second_t s, nsecond_t ns)
+ time_val(second_t s, nsecond_t ns) noexcept
{
time.tv_sec = s;
time.tv_nsec = ns;
}
- second_t seconds() const { return time.tv_sec; }
- nsecond_t nseconds() const { return time.tv_nsec; }
+ second_t seconds() const noexcept{ return time.tv_sec; }
+ nsecond_t nseconds() const noexcept { return time.tv_nsec; }
- second_t & seconds() { return time.tv_sec; }
- nsecond_t & nseconds() { return time.tv_nsec; }
+ second_t & seconds() noexcept { return time.tv_sec; }
+ nsecond_t & nseconds() noexcept { return time.tv_nsec; }
- //void set_seconds(second_t s) { time.tv_sec = s; }
- //void set_nseconds(nsecond_t ns) { time.tv_nsec = ns; }
- //void dec_seconds() { time.tv_sec--; }
- //void inc_seconds() { time.tv_sec++; }
+ timespec & get_timespec() noexcept
+ {
+ return time;
+ }
+
+ const timespec & get_timespec() const noexcept
+ {
+ return time;
+ }
- operator timespec() const
+ operator timespec() const noexcept
{
return time;
}
};
-inline time_val operator-(const time_val &t1, const time_val &t2)
+inline time_val operator-(const time_val &t1, const time_val &t2) noexcept
{
time_val diff;
diff.seconds() = t1.seconds() - t2.seconds();
return diff;
}
-inline time_val operator+(const time_val &t1, const time_val &t2)
+inline time_val operator+(const time_val &t1, const time_val &t2) noexcept
{
auto ns = t1.nseconds() + t2.nseconds();
auto s = t1.seconds() + t2.seconds();
return time_val(s, ns);
}
-inline time_val &operator+=(time_val &t1, const time_val &t2)
+inline time_val &operator+=(time_val &t1, const time_val &t2) noexcept
{
auto nsum = t1.nseconds() + t2.nseconds();
t1.seconds() = t1.seconds() + t2.seconds();
return t1;
}
-inline bool operator<(const time_val &t1, const time_val &t2)
+inline time_val &operator-=(time_val &t1, const time_val &t2) noexcept
+{
+ time_val diff;
+ t1.seconds() = t1.seconds() - t2.seconds();
+ if (t1.nseconds() >= t2.nseconds()) {
+ t1.nseconds() = t1.nseconds() - t2.nseconds();
+ }
+ else {
+ t1.nseconds() = 1000000000 - t2.nseconds() + t1.nseconds();
+ t1.seconds()--;
+ }
+ return t1;
+}
+
+inline bool operator<(const time_val &t1, const time_val &t2) noexcept
{
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)
+inline bool operator==(const time_val &t1, const time_val &t2) noexcept
{
return (t1.seconds() == t2.seconds() && t1.nseconds() == t2.nseconds());
}
-inline bool operator<=(const time_val &t1, const time_val &t2)
+inline bool operator<=(const time_val &t1, const time_val &t2) noexcept
{
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; }
+inline bool operator!=(const time_val &t1, const time_val &t2) noexcept { return !(t1 == t2); }
+inline bool operator>(const time_val &t1, const time_val &t2) noexcept { return t2 < t1; }
+inline bool operator>=(const time_val &t1, const time_val &t2) noexcept { return t2 <= t1; }
+
+static inline int divide_timespec(const struct timespec &num, const struct timespec &den, struct timespec &rem) noexcept;
+
+inline int operator/(const time_val &t1, const time_val &t2) noexcept
+{
+ struct timespec remainder;
+ return divide_timespec(t1.get_timespec(), t2.get_timespec(), remainder);
+}
+
+inline time_val & operator<<=(time_val &t, int n) noexcept
+{
+ for (int i = 0; i < n; i++) {
+ t.seconds() *= 2;
+ t.nseconds() *= 2;
+ if (t.nseconds() >= 1000000000) {
+ t.nseconds() -= 1000000000;
+ t.seconds()++;
+ }
+ }
+ return t;
+}
+
+inline time_val operator<<(time_val &t, int n) noexcept
+{
+ auto r = t;
+ r <<= n;
+ return r;
+}
+
+inline time_val & operator>>=(time_val &t, int n) noexcept
+{
+ for (int i = 0; i < n; i++) {
+ bool low = t.seconds() & 1;
+ t.nseconds() /= 2;
+ t.nseconds() += low ? 500000000ULL : 0;
+ t.seconds() /= 2;
+ }
+ return t;
+}
+
+inline time_val operator>>(time_val &t, int n) noexcept
+{
+ auto r = t;
+ r >>= n;
+ return r;
+}
// Data corresponding to a single timer
class timer_data
bool enabled; // whether timer reports events
void *userdata;
- timer_data(void *udata = nullptr) : interval_time(0,0), expiry_count(0), enabled(true), userdata(udata)
+ timer_data(void *udata = nullptr) noexcept : interval_time(0,0), expiry_count(0), enabled(true), userdata(udata)
{
// constructor
}
class compare_timespec
{
public:
- bool operator()(const struct timespec &a, const struct timespec &b)
+ bool operator()(const struct timespec &a, const struct timespec &b) noexcept
{
if (a.tv_sec < b.tv_sec) {
return true;
}
};
-using timer_queue_t = NaryHeap<timer_data, struct timespec, compare_timespec>;
+using timer_queue_t = NaryHeap<timer_data, time_val, compare_timespec>;
using timer_handle_t = timer_queue_t::handle_t;
static inline void init_timer_handle(timer_handle_t &hnd) noexcept
timer_queue_t::init_handle(hnd);
}
-static inline int divide_timespec(const struct timespec &num, const struct timespec &den, struct timespec &rem)
+static inline int divide_timespec(const struct timespec &num, const struct timespec &den, struct timespec &rem) noexcept
{
if (num.tv_sec < den.tv_sec) {
rem = num;
// At this point, num.tv_sec >= 1.
- auto &r_sec = rem.tv_sec;
- auto &r_nsec = rem.tv_nsec;
- r_sec = num.tv_sec;
- r_nsec = num.tv_nsec;
- auto d_sec = den.tv_sec;
- auto d_nsec = den.tv_nsec;
+ time_val n = { num.tv_sec, num.tv_nsec };
+ time_val d = { den.tv_sec, den.tv_nsec };
+ time_val r = n;
- r_sec -= d_sec;
- if (r_nsec >= d_nsec) {
- r_nsec -= d_nsec;
- }
- else {
- r_nsec += (1000000000ULL - d_nsec);
- r_sec -= 1;
- }
+ // starting with numerator, subtract 1*denominator
+ r -= d;
// Check now for common case: one timer expiry with no overrun
- if (r_sec < d_sec || (r_sec == d_sec && r_nsec < d_nsec)) {
+ if (r < d) {
+ rem = r;
return 1;
}
int nval = 1;
int rval = 1; // we have subtracted 1*D already
- // shift denominator until it is greater than/equal to numerator:
- while (d_sec < r_sec) {
- d_sec *= 2;
- d_nsec *= 2;
- if (d_nsec >= 1000000000) {
- d_nsec -= 1000000000;
- d_sec++;
- }
+ // shift denominator until it is greater than / roughly equal to numerator:
+ while (d.seconds() < r.seconds()) {
+ d <<= 1;
nval *= 2;
}
while (nval > 0) {
- if (d_sec < r_sec || (d_sec == r_sec && d_nsec <= r_nsec)) {
- // subtract:
- r_sec -= d_sec;
- if (d_nsec > r_nsec) {
- r_nsec += 1000000000;
- r_sec--;
- }
- r_nsec -= d_nsec;
-
+ if (d <= r) {
+ r -= d;
rval += nval;
}
- bool low = d_sec & 1;
- d_nsec /= 2;
- d_nsec += low ? 500000000ULL : 0;
- d_sec /= 2;
+ d >>= 1;
nval /= 2;
}
+ rem = r;
return rval;
}
template <typename Base> class timer_base : public Base
{
+ private:
+ timer_queue_t timer_queue;
+
+#if defined(CLOCK_MONOTONIC)
+ timer_queue_t mono_timer_queue;
+
protected:
+ inline timer_queue_t &queue_for_clock(clock_type clock)
+ {
+ if (clock == clock_type::MONOTONIC) {
+ return mono_timer_queue;
+ }
+ else {
+ return timer_queue;
+ }
+ }
+
+ inline bool timer_queues_empty()
+ {
+ return timer_queue.empty() && mono_timer_queue.empty();
+ }
+#else
+ protected:
+ inline timer_queue_t &queue_for_clock(clock_type clock)
+ {
+ return timer_queue;
+ }
+
+ inline bool timer_queues_empty()
+ {
+ return timer_queue.empty();
+ }
+#endif
- void process_timer_queue(timer_queue_t &queue, const struct timespec &curtime)
+ // For the specified timer queue, issue expirations for all timers set to expire on or before the given
+ // time (curtime). The timer queue must not be empty.
+ void process_timer_queue(timer_queue_t &queue, const struct timespec &curtime) noexcept
{
// Peek timer queue; calculate difference between current time and timeout
- const struct timespec * timeout = &queue.get_root_priority();
- while (timeout->tv_sec < curtime.tv_sec || (timeout->tv_sec == curtime.tv_sec &&
- timeout->tv_nsec <= curtime.tv_nsec)) {
+ const time_val * timeout = &queue.get_root_priority();
+ time_val curtime_tv = curtime;
+ while (*timeout <= curtime_tv) {
auto & thandle = queue.get_root();
timer_data &data = queue.node_data(thandle);
time_val &interval = data.interval_time;
data.enabled = false;
int expiry_count = data.expiry_count;
data.expiry_count = 0;
- Base::receiveTimerExpiry(thandle, data.userdata, expiry_count);
+ Base::receive_timer_expiry(thandle, data.userdata, expiry_count);
}
if (queue.empty()) {
break;
data.enabled = false;
int expiry_count = data.expiry_count;
data.expiry_count = 0;
- Base::receiveTimerExpiry(thandle, data.userdata, expiry_count);
+ Base::receive_timer_expiry(thandle, data.userdata, expiry_count);
}
}
timeout = &queue.get_root_priority();
}
}
+
+ public:
+
+ void get_time(time_val &tv, clock_type clock, bool force_update) noexcept
+ {
+ get_time(tv.get_timespec(), clock, force_update);
+ }
+
+#ifdef CLOCK_MONOTONIC
+ void get_time(timespec &ts, clock_type clock, bool force_update) noexcept
+ {
+ clockid_t posix_clock_id = (clock == clock_type::MONOTONIC) ? CLOCK_MONOTONIC : CLOCK_REALTIME;
+ clock_gettime(posix_clock_id, &ts);
+ }
+#else
+ // If CLOCK_MONOTONIC is not defined, assume we only have gettimeofday():
+ void get_time(timespec &ts, clock_type clock, bool force_update) noexcept
+ {
+ struct timeval curtime_tv;
+ gettimeofday(&curtime_tv, nullptr);
+ ts.tv_sec = curtime_tv.tv_sec;
+ ts.tv_nsec = curtime_tv.tv_usec * 1000;
+ }
+#endif
+
+ void add_timer_nolock(timer_handle_t &h, void *userdata, clock_type clock = clock_type::MONOTONIC)
+ {
+ this->queue_for_clock(clock).allocate(h, userdata);
+ }
+
+ void remove_timer(timer_handle_t &timer_id, clock_type clock = clock_type::MONOTONIC) noexcept
+ {
+ std::lock_guard<decltype(Base::lock)> guard(Base::lock);
+ remove_timer_nolock(timer_id, clock);
+ }
+
+ void remove_timer_nolock(timer_handle_t &timer_id, clock_type clock = clock_type::MONOTONIC) noexcept
+ {
+ auto &timer_queue = this->queue_for_clock(clock);
+ if (timer_queue.is_queued(timer_id)) {
+ timer_queue.remove(timer_id);
+ }
+ timer_queue.deallocate(timer_id);
+ }
+
+ // Enables or disabling report of timeouts (does not stop timer)
+ void enable_timer(timer_handle_t &timer_id, bool enable, clock_type clock = clock_type::MONOTONIC) noexcept
+ {
+ std::lock_guard<decltype(Base::lock)> guard(Base::lock);
+ enable_timer_nolock(timer_id, enable, clock);
+ }
+
+ void enable_timer_nolock(timer_handle_t &timer_id, bool enable, clock_type clock = clock_type::MONOTONIC) noexcept
+ {
+ auto &timer_queue = this->queue_for_clock(clock);
+ auto &node_data = timer_queue.node_data(timer_id);
+ auto expiry_count = node_data.expiry_count;
+ if (expiry_count != 0 && enable) {
+ node_data.expiry_count = 0;
+ Base::receive_timer_expiry(timer_id, node_data.userdata, expiry_count);
+ }
+ else {
+ timer_queue.node_data(timer_id).enabled = enable;
+ }
+ }
};
}
namespace dasynq {
+// Timer implementation based on Linux's "timerfd".
+
// We could use one timerfd per timer, but then we need to differentiate timer
// descriptors from regular file descriptors when events are reported by the loop
// mechanism so that we can correctly report a timer event or fd event.
// we are given a handle; we need to use this to modify the watch. We delegate the
// process of allocating a handle to a priority heap implementation (BinaryHeap).
-template <class Base> class TimerFdEvents : public timer_base<Base>
+template <class Base> class timer_fd_events : public timer_base<Base>
{
private:
int timerfd_fd = -1;
int systemtime_fd = -1;
-
- timer_queue_t timer_queue;
- timer_queue_t wallclock_queue;
// Set the timerfd timeout to match the first timer in the queue (disable the timerfd
// if there are no active timers).
timerfd_settime(fd, TFD_TIMER_ABSTIME, &newtime, nullptr);
}
- void process_timer(clock_type clock, int fd, timer_queue_t &queue) noexcept
+ void process_timer(clock_type clock, int fd) noexcept
{
+ timer_queue_t &queue = this->queue_for_clock(clock);
struct timespec curtime;
switch (clock) {
case clock_type::SYSTEM:
set_timer_from_queue(fd, queue);
}
- void setTimer(timer_handle_t & timer_id, const time_val &timeouttv, const time_val &intervaltv,
+ void set_timer(timer_handle_t & timer_id, const time_val &timeouttv, const time_val &intervaltv,
timer_queue_t &queue, int fd, bool enable) noexcept
{
timespec timeout = timeouttv;
}
}
- timer_queue_t & get_queue(clock_type clock)
+ public:
+
+ class traits_t : public Base::traits_t
{
- switch(clock) {
- case clock_type::SYSTEM:
- return wallclock_queue;
- case clock_type::MONOTONIC:
- return timer_queue;
- default:
- DASYNQ_UNREACHABLE;
- }
- }
+ constexpr static bool full_timer_support = true;
+ };
- public:
template <typename T>
- void receiveFdEvent(T &loop_mech, typename Base::FD_r fd_r, void * userdata, int flags)
+ void receive_fd_event(T &loop_mech, typename traits_t::fd_r fd_r_a, void * userdata, int flags)
{
if (userdata == &timerfd_fd) {
- process_timer(clock_type::MONOTONIC, timerfd_fd, timer_queue);
+ process_timer(clock_type::MONOTONIC, timerfd_fd);
}
else if (userdata == &systemtime_fd) {
- process_timer(clock_type::SYSTEM, systemtime_fd, wallclock_queue);
+ process_timer(clock_type::SYSTEM, systemtime_fd);
}
else {
- Base::receiveFdEvent(loop_mech, fd_r, userdata, flags);
+ Base::receive_fd_event(loop_mech, fd_r_a, userdata, flags);
}
}
}
try {
- loop_mech->addFdWatch(timerfd_fd, &timerfd_fd, IN_EVENTS);
- loop_mech->addFdWatch(systemtime_fd, &systemtime_fd, IN_EVENTS);
+ loop_mech->add_fd_watch(timerfd_fd, &timerfd_fd, IN_EVENTS);
+ loop_mech->add_fd_watch(systemtime_fd, &systemtime_fd, IN_EVENTS);
Base::init(loop_mech);
}
catch (...) {
}
}
- // Add timer, store into given handle
- void addTimer(timer_handle_t &h, void *userdata, clock_type clock = clock_type::MONOTONIC)
- {
- std::lock_guard<decltype(Base::lock)> guard(Base::lock);
- timer_queue_t & queue = get_queue(clock);
- queue.allocate(h, userdata);
- }
-
- void removeTimer(timer_handle_t &timer_id, clock_type clock = clock_type::MONOTONIC) noexcept
- {
- std::lock_guard<decltype(Base::lock)> guard(Base::lock);
- removeTimer_nolock(timer_id, clock);
- }
-
- void removeTimer_nolock(timer_handle_t &timer_id, clock_type clock = clock_type::MONOTONIC) noexcept
- {
- stop_timer_nolock(timer_id, clock);
- timer_queue_t & queue = get_queue(clock);
- queue.deallocate(timer_id);
- }
-
void stop_timer(timer_handle_t &timer_id, clock_type clock = clock_type::MONOTONIC) noexcept
{
std::lock_guard<decltype(Base::lock)> guard(Base::lock);
void stop_timer_nolock(timer_handle_t &timer_id, clock_type clock = clock_type::MONOTONIC) noexcept
{
- timer_queue_t & queue = get_queue(clock);
+ timer_queue_t & queue = this->queue_for_clock(clock);
int fd = (clock == clock_type::MONOTONIC) ? timerfd_fd : systemtime_fd;
if (queue.is_queued(timer_id)) {
- bool was_first = (&timer_queue.get_root()) == &timer_id;
+ bool was_first = (&queue.get_root()) == &timer_id;
queue.remove(timer_id);
if (was_first) {
set_timer_from_queue(fd, queue);
// starts (if not started) a timer to timeout at the given time. Resets the expiry count to 0.
// enable: specifies whether to enable reporting of timeouts/intervals
- void setTimer(timer_handle_t & timer_id, const time_val &timeouttv, const time_val &intervaltv,
+ void set_timer(timer_handle_t & timer_id, const time_val &timeouttv, const time_val &intervaltv,
bool enable, clock_type clock = clock_type::MONOTONIC) noexcept
{
timespec timeout = timeouttv;
timespec interval = intervaltv;
+ timer_queue_t queue = this->queue_for_clock(clock);
switch (clock) {
case clock_type::SYSTEM:
- setTimer(timer_id, timeout, interval, wallclock_queue, systemtime_fd, enable);
+ set_timer(timer_id, timeout, interval, queue, systemtime_fd, enable);
break;
case clock_type::MONOTONIC:
- setTimer(timer_id, timeout, interval, timer_queue, timerfd_fd, enable);
+ set_timer(timer_id, timeout, interval, queue, timerfd_fd, enable);
break;
default:
DASYNQ_UNREACHABLE;
}
// Set timer relative to current time:
- void setTimerRel(timer_handle_t & timer_id, const time_val &timeouttv, const time_val &intervaltv,
+ void set_timer_rel(timer_handle_t & timer_id, const time_val &timeout, const time_val &interval,
bool enable, clock_type clock = clock_type::MONOTONIC) noexcept
{
- timespec timeout = timeouttv;
- timespec interval = intervaltv;
-
- clockid_t sclock;
- switch (clock) {
- case clock_type::SYSTEM:
- sclock = CLOCK_REALTIME;
- break;
- case clock_type::MONOTONIC:
- sclock = CLOCK_MONOTONIC;
- break;
- default:
- DASYNQ_UNREACHABLE;
- }
+ time_val alarmtime;
+ this->get_time(alarmtime, clock, false);
+ alarmtime += timeout;
- // TODO consider caching current time somehow; need to decide then when to update cached value.
- struct timespec curtime;
- clock_gettime(sclock, &curtime);
- curtime.tv_sec += timeout.tv_sec;
- curtime.tv_nsec += timeout.tv_nsec;
- if (curtime.tv_nsec > 1000000000) {
- curtime.tv_nsec -= 1000000000;
- curtime.tv_sec++;
- }
-
- setTimer(timer_id, curtime, interval, enable, clock);
- }
-
- // Enables or disabling report of timeouts (does not stop timer)
- void enableTimer(timer_handle_t & timer_id, bool enable, clock_type clock = clock_type::MONOTONIC) noexcept
- {
- std::lock_guard<decltype(Base::lock)> guard(Base::lock);
- enableTimer_nolock(timer_id, enable, clock);
+ set_timer(timer_id, alarmtime, interval, enable, clock);
}
- void enableTimer_nolock(timer_handle_t & timer_id, bool enable, clock_type clock = clock_type::MONOTONIC) noexcept
- {
- timer_queue_t & queue = get_queue(clock);
-
- auto &node_data = queue.node_data(timer_id);
- auto expiry_count = node_data.expiry_count;
- if (expiry_count != 0) {
- node_data.expiry_count = 0;
- Base::receiveTimerExpiry(timer_id, node_data.userdata, expiry_count);
- }
- else {
- queue.node_data(timer_id).enabled = enable;
- }
- }
-
- void get_time(time_val &tv, clock_type clock, bool force_update) noexcept
- {
- timespec ts;
- get_time(ts, clock, force_update);
- tv = ts;
- }
-
- void get_time(timespec &ts, clock_type clock, bool force_update) noexcept
- {
- int posix_clock_id = (clock == clock_type::MONOTONIC) ? CLOCK_MONOTONIC : CLOCK_REALTIME;
- clock_gettime(posix_clock_id, &ts);
- }
-
- ~TimerFdEvents()
+ ~timer_fd_events()
{
close(timerfd_fd);
close(systemtime_fd);
--- /dev/null
+#ifndef DASYNQ_UTIL_H_INCLUDED
+#define DASYNQ_UTIL_H_INCLUDED 1
+
+#include <dasynq-config.h>
+#include <unistd.h>
+
+namespace dasynq {
+
+// Define pipe2, if it's not present in the sytem library. pipe2 is like pipe with an additional flags
+// argument which can set file/descriptor flags atomically. The emulated version that we generate cannot
+// do this atomically, of course.
+
+#if DASYNQ_HAVE_PIPE2 == 0
+inline int pipe2(int filedes[2], int flags)
+{
+ if (pipe(filedes) == -1) {
+ return -1;
+ }
+
+ if (flags & O_CLOEXEC) {
+ fcntl(filedes[0], F_SETFD, FD_CLOEXEC);
+ fcntl(filedes[1], F_SETFD, FD_CLOEXEC);
+ }
+
+ if (flags & O_NONBLOCK) {
+ fcntl(filedes[0], F_SETFL, O_NONBLOCK);
+ fcntl(filedes[1], F_SETFL, O_NONBLOCK);
+ }
+
+ return 0;
+}
+#else
+
+using ::pipe2;
+
+#endif
+
+
+}
+
+#endif
#include "dasynq-flags.h"
#include "dasynq-naryheap.h"
#include "dasynq-interrupt.h"
+#include "dasynq-util.h"
// Dasynq uses a "mix-in" pattern to produce an event loop implementation incorporating selectable implementations of
// various components (main backend, timers, child process watch mechanism etc). In C++ this can be achieved by
// We can chain several such components together (and so so below) to "mix in" the functionality of each into the final
// class, eg:
//
-// template <typename T> using Loop = EpollLoop<interrupt_channel<TimerFdEvents<ChildProcEvents<T>>>>;
+// template <typename T> using Loop = epoll_loop<interrupt_channel<timer_fd_events<child_proc_events<T>>>>;
//
// (which defines an alias template "Loop", whose implementation will use the epoll backend, a standard interrupt channel
// implementation, a timerfd-based timer implementation, and the standard child process watch implementation).
// Base::init(derived);
// }
//
-// At the base all this is the EventDispatch class, defined below, which receives event notifications and inserts
+// At the base all this is the event_dispatch class, defined below, which receives event notifications and inserts
// them into a queue for processing. The event_loop class, also below, wraps this (via composition) in an interface
// which can be used to register/de-regsiter/enable/disable event watchers, and which can process the queued events
// by calling the watcher callbacks. The event_loop class also provides some synchronisation to ensure thread-safety.
-#if defined(DASYNQ_CUSTOM_LOOP_IMPLEMENTATION)
-// Loop and LoopTraits defined already; used for testing
-#elif defined(DASYNQ_HAVE_KQUEUE)
+#if DASYNQ_HAVE_KQUEUE
#include "dasynq-kqueue.h"
#if _POSIX_TIMERS > 0
#include "dasynq-posixtimer.h"
namespace dasynq {
- template <typename T> using TimerEvents = PosixTimerEvents<T>;
+ template <typename T> using timer_events = posix_timer_events<T>;
}
#else
#include "dasynq-itimer.h"
namespace dasynq {
- template <typename T> using TimerEvents = ITimerEvents<T>;
+ template <typename T> using timer_events = itimer_events<T>;
}
#endif
#include "dasynq-childproc.h"
namespace dasynq {
- template <typename T> using Loop = KqueueLoop<interrupt_channel<TimerEvents<ChildProcEvents<T>>>>;
- using LoopTraits = KqueueTraits;
+ template <typename T> using loop_t = kqueue_loop<interrupt_channel<timer_events<child_proc_events<T>>>>;
+ using loop_traits_t = kqueue_traits;
}
-#elif defined(DASYNQ_HAVE_EPOLL)
+#elif DASYNQ_HAVE_EPOLL
#include "dasynq-epoll.h"
#include "dasynq-timerfd.h"
#include "dasynq-childproc.h"
namespace dasynq {
- template <typename T> using Loop = EpollLoop<interrupt_channel<TimerFdEvents<ChildProcEvents<T>>>>;
- using LoopTraits = EpollTraits;
+ template <typename T> using loop_t = epoll_loop<interrupt_channel<timer_fd_events<child_proc_events<T>>>>;
+ using loop_traits_t = epoll_traits;
}
+#else
+#error No loop backened defined - see dasynq-config.h
#endif
#include <atomic>
#include "dasynq-mutex.h"
-namespace dasynq {
-
-#if HAVE_PIPE2 == 0
-inline int pipe2(int filedes[2], int flags)
-{
- if (pipe(filedes) == -1) {
- return -1;
- }
+#include "dasynq-basewatchers.h"
- if (flags & O_CLOEXEC) {
- fcntl(filedes[0], F_SETFD, FD_CLOEXEC);
- fcntl(filedes[1], F_SETFD, FD_CLOEXEC);
- }
-
- if (flags & O_NONBLOCK) {
- fcntl(filedes[0], F_SETFL, O_NONBLOCK);
- fcntl(filedes[1], F_SETFL, O_NONBLOCK);
- }
-
- return 0;
-}
-#endif
-
-namespace dprivate {
- class base_watcher;
-}
-
-using PrioQueue = NaryHeap<dprivate::base_watcher *, int>;
-
-inline namespace {
- constexpr int DEFAULT_PRIORITY = 50;
-}
+namespace dasynq {
/**
* Values for rearm/disarm return from event handlers
REQUEUE
};
-// Forward declarations:
-template <typename T_Mutex, template <typename> class Loop = dasynq::Loop, typename LoopTraits = dasynq::LoopTraits>
-class event_loop;
-
namespace dprivate {
- // (non-public API)
-
- template <typename T_Loop> class fd_watcher;
- template <typename T_Loop> class bidi_fd_watcher;
- template <typename T_Loop> class signal_watcher;
- template <typename T_Loop> class child_proc_watcher;
- template <typename T_Loop> class timer;
-
- template <typename, typename> class fd_watcher_impl;
- template <typename, typename> class bidi_fd_watcher_impl;
- template <typename, typename> class signal_watcher_impl;
- template <typename, typename> class child_proc_watcher_impl;
- template <typename, typename> class timer_impl;
-
- enum class watch_type_t
- {
- SIGNAL,
- FD,
- CHILD,
- SECONDARYFD,
- TIMER
- };
-
- template <typename T_Mutex, typename Traits> class EventDispatch;
-
- // For FD watchers:
- // Use this watch flag to indicate that in and out events should be reported separately,
- // that is, watcher should not be disabled until all watched event types are queued.
- constexpr static int multi_watch = 4;
-
- // Represents a queued event notification. Various event watchers derive from this type.
- class base_watcher
- {
- template <typename T_Mutex, typename Traits> friend class EventDispatch;
- template <typename T_Mutex, template <typename> class, typename> friend class dasynq::event_loop;
- friend inline void basewatcher_set_active(base_watcher &watcher, bool active);
- friend inline bool basewatcher_get_deleteme(const base_watcher &watcher);
- friend inline bool basewatcher_get_emulatefd(const base_watcher &watcher);
-
- protected:
- watch_type_t watchType;
- int active : 1; // currently executing handler?
- int deleteme : 1; // delete when handler finished?
- int emulatefd : 1; // emulate file watch (by re-queueing)
- int emulate_enabled : 1; // whether an emulated watch is enabled
-
- PrioQueue::handle_t heap_handle;
- int priority;
-
- static void set_priority(base_watcher &p, int prio)
- {
- p.priority = prio;
- }
-
- public:
-
- // Perform initialisation necessary before registration with an event loop
- void init()
- {
- active = false;
- deleteme = false;
- emulatefd = false;
- emulate_enabled = false;
- PrioQueue::init_handle(heap_handle);
- priority = DEFAULT_PRIORITY;
- }
-
- base_watcher(watch_type_t wt) noexcept : watchType(wt) { }
-
- virtual void dispatch(void *loop_ptr) noexcept { };
- virtual void dispatch_second(void *loop_ptr) noexcept { }
-
- virtual ~base_watcher() noexcept { }
-
- // Called when the watcher has been removed.
- // It is guaranteed by the caller that:
- // - the dispatch method is not currently running
- // - the dispatch method will not be called.
- virtual void watch_removed() noexcept
- {
- // TODO this "delete" behaviour could be dependent on a flag, perhaps?
- // delete this;
- }
- };
-
- inline void basewatcher_set_active(base_watcher &watcher, bool active)
- {
- watcher.active = active;
- }
-
- inline bool basewatcher_get_deleteme(const base_watcher &watcher)
- {
- return watcher.deleteme;
- }
-
- inline bool basewatcher_get_emulatefd(const base_watcher &watcher)
- {
- return watcher.emulatefd;
- }
-
- // Base signal event - not part of public API
- template <typename T_Mutex, typename Traits>
- class base_signal_watcher : public base_watcher
- {
- friend class EventDispatch<T_Mutex, Traits>;
- template <typename, template <typename> class, typename> friend class dasynq::event_loop;
-
- protected:
- typename Traits::SigInfo siginfo;
- base_signal_watcher() : base_watcher(watch_type_t::SIGNAL) { }
-
- public:
- using siginfo_t = typename Traits::SigInfo;
- typedef siginfo_t &siginfo_p;
- };
-
- template <typename T_Mutex>
- class base_fd_watcher : public base_watcher
- {
- template <typename, typename Traits> friend class EventDispatch;
- template <typename, template <typename> class, typename> friend class dasynq::event_loop;
-
- protected:
- int watch_fd;
-
- // These flags are protected by the loop's internal lock:
- int watch_flags; // events being watched
- int event_flags; // events pending (queued)
-
- // watch_flags: for a regular fd_watcher, this specifies the events that the watcher
- // is watching (or was watching if disabled). For a bidi_fd_watcher, specifies
- // the events that the watcher is currently watching (i.e. specifies which
- // halves of the Bidi watcher are enabled).
-
- base_fd_watcher() noexcept : base_watcher(watch_type_t::FD) { }
- };
-
- template <typename T_Mutex>
- class base_bidi_fd_watcher : public base_fd_watcher<T_Mutex>
- {
- template <typename, typename Traits> friend class EventDispatch;
- template <typename, template <typename> class, typename> friend class dasynq::event_loop;
-
- protected:
-
- // The main instance is the "input" watcher only; we keep a secondary watcher
- // with a secondary set of flags for the "output" watcher:
- base_watcher outWatcher {watch_type_t::SECONDARYFD};
-
- int read_removed : 1; // read watch removed?
- int write_removed : 1; // write watch removed?
- };
-
- template <typename T_Mutex>
- class base_child_watcher : public base_watcher
- {
- template <typename, typename Traits> friend class EventDispatch;
- 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;
-
- base_child_watcher() : base_watcher(watch_type_t::CHILD) { }
- };
-
-
- template <typename T_Mutex>
- class base_timer_watcher : public base_watcher
- {
- template <typename, typename Traits> friend class EventDispatch;
- template <typename, template <typename> class, typename> friend class dasynq::event_loop;
-
- protected:
- timer_handle_t timer_handle;
- int intervals;
- clock_type clock;
-
- base_timer_watcher() : base_watcher(watch_type_t::TIMER)
- {
- init_timer_handle(timer_handle);
- }
- };
// Classes for implementing a fair(ish) wait queue.
// A queue node can be signalled when it reaches the head of
// Select an appropriate condition variable type for a mutex:
// condition_variable if mutex is std::mutex, or condition_variable_any
// otherwise.
- template <class T_Mutex> class condvarSelector;
+ template <class T_Mutex> class condvar_selector;
- template <> class condvarSelector<std::mutex>
+ template <> class condvar_selector<std::mutex>
{
public:
typedef std::condition_variable condvar;
};
- template <class T_Mutex> class condvarSelector
+ template <class T_Mutex> class condvar_selector
{
public:
typedef std::condition_variable_any condvar;
template <typename T_Mutex> class waitqueue_node
{
- typename condvarSelector<T_Mutex>::condvar condvar;
+ typename condvar_selector<T_Mutex>::condvar condvar;
friend class waitqueue<T_Mutex>;
- waitqueue_node * next = nullptr;
+
+ // ptr to next node in queue, set to null when added to queue tail:
+ waitqueue_node * next;
public:
void signal()
template <> class waitqueue<null_mutex>
{
public:
+ // remove current head of queue, return new head:
waitqueue_node<null_mutex> * unqueue()
{
return nullptr;
}
- waitqueue_node<null_mutex> * getHead()
+ waitqueue_node<null_mutex> * get_head()
{
return nullptr;
}
- bool checkHead(waitqueue_node<null_mutex> &node)
+ bool check_head(waitqueue_node<null_mutex> &node)
{
return true;
}
- bool isEmpty()
+ bool is_empty()
{
return true;
}
waitqueue_node<T_Mutex> * head = nullptr;
public:
+ // remove current head of queue, return new head:
waitqueue_node<T_Mutex> * unqueue()
{
head = head->next;
return head;
}
- waitqueue_node<T_Mutex> * getHead()
+ waitqueue_node<T_Mutex> * get_head()
{
return head;
}
- bool checkHead(waitqueue_node<T_Mutex> &node)
+ bool check_head(waitqueue_node<T_Mutex> &node)
{
return head == &node;
}
- bool isEmpty()
+ bool is_empty()
{
return head == nullptr;
}
void queue(waitqueue_node<T_Mutex> *node)
{
+ node->next = nullptr;
if (tail) {
tail->next = node;
}
// Do standard post-dispatch processing for a watcher. This handles the case of removing or
// re-queing watchers depending on the rearm type.
- template <typename Loop> void post_dispatch(Loop &loop, base_watcher *watcher, rearm rearmType)
+ template <typename Loop> void post_dispatch(Loop &loop, base_watcher *watcher, rearm rearm_type)
{
- if (rearmType == rearm::REMOVE) {
- loop.getBaseLock().unlock();
+ if (rearm_type == rearm::REMOVE) {
+ loop.get_base_lock().unlock();
watcher->watch_removed();
- loop.getBaseLock().lock();
+ loop.get_base_lock().lock();
}
- else if (rearmType == rearm::REQUEUE) {
+ else if (rearm_type == rearm::REQUEUE) {
loop.requeue_watcher(watcher);
}
}
// This class serves as the base class (mixin) for the AEN mechanism class.
//
- // The EventDispatch class maintains the queued event data structures. It inserts watchers
- // into the queue when events are received (receiveXXX methods).
- template <typename T_Mutex, typename Traits> class EventDispatch : public Traits
+ // The event_dispatch class maintains the queued event data structures. It inserts watchers
+ // into the queue when events are received (receiveXXX methods). It also owns a mutex used
+ // to protect those structures.
+ //
+ // In general the methods should be called with lock held. In practice this means that the
+ // event loop backend implementations must obtain the lock; they are also free to use it to
+ // protect their own internal data structures.
+ template <typename T_Mutex, typename Traits, typename LoopTraits> class event_dispatch
{
- template <typename, template <typename> class, typename> friend class dasynq::event_loop;
+ friend class dasynq::event_loop<T_Mutex, LoopTraits>;;
+
+ public:
+ using mutex_t = T_Mutex;
+ using traits_t = Traits;
+
+ private:
// queue data structure/pointer
- PrioQueue event_queue;
+ prio_queue event_queue;
- using BaseSignalWatcher = dasynq::dprivate::base_signal_watcher<T_Mutex,Traits>;
- using BaseFdWatcher = dasynq::dprivate::base_fd_watcher<T_Mutex>;
- using BaseBidiFdWatcher = dasynq::dprivate::base_bidi_fd_watcher<T_Mutex>;
- using BaseChildWatcher = dasynq::dprivate::base_child_watcher<T_Mutex>;
- using BaseTimerWatcher = dasynq::dprivate::base_timer_watcher<T_Mutex>;
+ using base_signal_watcher = dasynq::dprivate::base_signal_watcher<mutex_t, typename traits_t::sigdata_t>;
+ using base_fd_watcher = dasynq::dprivate::base_fd_watcher<mutex_t>;
+ using base_bidi_fd_watcher = dasynq::dprivate::base_bidi_fd_watcher<mutex_t>;
+ using base_child_watcher = dasynq::dprivate::base_child_watcher<mutex_t>;
+ using base_timer_watcher = dasynq::dprivate::base_timer_watcher<mutex_t>;
- // Add a watcher into the queuing system (but don't queue it)
+ // Add a watcher into the queueing system (but don't queue it). Call with lock held.
// may throw: std::bad_alloc
void prepare_watcher(base_watcher *bwatcher)
{
event_queue.allocate(bwatcher->heap_handle, bwatcher);
}
- void queueWatcher(base_watcher *bwatcher) noexcept
+ void queue_watcher(base_watcher *bwatcher) noexcept
{
event_queue.insert(bwatcher->heap_handle, bwatcher->priority);
}
- bool isQueued(base_watcher *bwatcher) noexcept
- {
- return event_queue.is_queued(bwatcher->heap_handle);
- }
-
- void dequeueWatcher(base_watcher *bwatcher) noexcept
+ void dequeue_watcher(base_watcher *bwatcher) noexcept
{
if (event_queue.is_queued(bwatcher->heap_handle)) {
event_queue.remove(bwatcher->heap_handle);
}
protected:
- T_Mutex lock;
+ mutex_t lock;
template <typename T> void init(T *loop) noexcept { }
- // Receive a signal; return true to disable signal watch or false to leave enabled
+ void sigmaskf(int how, const sigset_t *set, sigset_t *oset)
+ {
+ LoopTraits::sigmaskf(how, set, oset);
+ }
+
+ // Receive a signal; return true to disable signal watch or false to leave enabled.
+ // Called with lock held.
template <typename T>
- bool receive_signal(T &loop_mech, typename Traits::SigInfo & siginfo, void * userdata) noexcept
+ bool receive_signal(T &loop_mech, typename Traits::sigdata_t & siginfo, void * userdata) noexcept
{
- BaseSignalWatcher * bwatcher = static_cast<BaseSignalWatcher *>(userdata);
+ base_signal_watcher * bwatcher = static_cast<base_signal_watcher *>(userdata);
bwatcher->siginfo = siginfo;
- queueWatcher(bwatcher);
+ queue_watcher(bwatcher);
return true;
}
template <typename T>
- void receiveFdEvent(T &loop_mech, typename Traits::FD_r fd_r, void * userdata, int flags) noexcept
+ void receive_fd_event(T &loop_mech, typename Traits::fd_r fd_r, void * userdata, int flags) noexcept
{
- BaseFdWatcher * bfdw = static_cast<BaseFdWatcher *>(userdata);
+ base_fd_watcher * bfdw = static_cast<base_fd_watcher *>(userdata);
bfdw->event_flags |= flags;
bool is_multi_watch = bfdw->watch_flags & multi_watch;
if (is_multi_watch) {
- BaseBidiFdWatcher *bbdw = static_cast<BaseBidiFdWatcher *>(bwatcher);
+ base_bidi_fd_watcher *bbdw = static_cast<base_bidi_fd_watcher *>(bwatcher);
bbdw->watch_flags &= ~flags;
if ((flags & IN_EVENTS) && (flags & OUT_EVENTS)) {
// Queue the secondary watcher first:
- queueWatcher(&bbdw->outWatcher);
+ queue_watcher(&bbdw->out_watcher);
}
else if (flags & OUT_EVENTS) {
// Use the secondary watcher for queueing:
- bwatcher = &(bbdw->outWatcher);
+ bwatcher = &(bbdw->out_watcher);
}
}
- queueWatcher(bwatcher);
+ queue_watcher(bwatcher);
- if (! LoopTraits::has_separate_rw_fd_watches) {
+ if (! traits_t::has_separate_rw_fd_watches) {
// If this is a bidirectional fd-watch, it has been disabled in *both* directions
// as the event was delivered. However, the other direction should not be disabled
// yet, so we need to re-enable:
int in_out_mask = IN_EVENTS | OUT_EVENTS;
if (is_multi_watch && (bfdw->watch_flags & in_out_mask) != 0) {
// We need to re-enable the other channel now:
- loop_mech.enableFdWatch_nolock(bfdw->watch_fd, userdata,
+ loop_mech.enable_fd_watch_nolock(bfdw->watch_fd, userdata,
(bfdw->watch_flags & in_out_mask) | ONE_SHOT);
}
}
}
- void receiveChildStat(pid_t child, int status, void * userdata) noexcept
+ // 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
{
- BaseChildWatcher * watcher = static_cast<BaseChildWatcher *>(userdata);
+ base_child_watcher * watcher = static_cast<base_child_watcher *>(userdata);
watcher->child_status = status;
- queueWatcher(watcher);
+ watcher->child_termd = true;
+ queue_watcher(watcher);
}
- void receiveTimerExpiry(timer_handle_t & timer_handle, void * userdata, int intervals) noexcept
+ void receive_timer_expiry(timer_handle_t & timer_handle, void * userdata, int intervals) noexcept
{
- BaseTimerWatcher * watcher = static_cast<BaseTimerWatcher *>(userdata);
- watcher->intervals = intervals;
- queueWatcher(watcher);
+ base_timer_watcher * watcher = static_cast<base_timer_watcher *>(userdata);
+ watcher->intervals += intervals;
+ queue_watcher(watcher);
}
// Pull a single event from the queue; returns nullptr if the queue is empty.
- base_watcher * pullEvent() noexcept
+ // Call with lock held.
+ base_watcher * pull_event() noexcept
{
if (event_queue.empty()) {
return nullptr;
return r;
}
- void issueDelete(base_watcher *watcher) noexcept
+ // Queue a watcher for reomval, or issue "removed" callback to it.
+ // Call with lock free.
+ void issue_delete(base_watcher *watcher) noexcept
{
// This is only called when the attention lock is held, so if the watcher is not
// active/queued now, it cannot become active (and will not be reported with an event)
}
else {
// Actually do the delete.
- dequeueWatcher(watcher);
+ dequeue_watcher(watcher);
release_watcher(watcher);
lock.unlock();
}
}
- void issueDelete(BaseBidiFdWatcher *watcher) noexcept
+ // Queue a watcher for reomval, or issue "removed" callback to it.
+ // Call with lock free.
+ void issue_delete(base_bidi_fd_watcher *watcher) noexcept
{
lock.lock();
release_watcher(watcher);
}
else {
- dequeueWatcher(watcher);
+ dequeue_watcher(watcher);
release_watcher(watcher);
watcher->read_removed = true;
}
- base_watcher *secondary = &(watcher->outWatcher);
+ base_watcher *secondary = &(watcher->out_watcher);
if (secondary->active) {
secondary->deleteme = true;
release_watcher(watcher);
}
else {
- dequeueWatcher(secondary);
+ dequeue_watcher(secondary);
release_watcher(watcher);
watcher->write_removed = true;
}
lock.unlock();
}
}
-
- public:
- using mutex_t = T_Mutex;
};
}
-
-template <typename T_Mutex, template <typename> class Loop, typename LoopTraits>
+// 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 waits the backend
+// and provides safe deletion of watchers (see comments inline).
+template <typename T_Mutex, typename Traits>
class event_loop
{
- using my_event_loop_t = event_loop<T_Mutex, Loop, LoopTraits>;
+ using my_event_loop_t = event_loop<T_Mutex, Traits>;
friend class dprivate::fd_watcher<my_event_loop_t>;
friend class dprivate::bidi_fd_watcher<my_event_loop_t>;
friend class dprivate::signal_watcher<my_event_loop_t>;
friend class dprivate::timer<my_event_loop_t>;
friend void dprivate::post_dispatch<my_event_loop_t>(my_event_loop_t &loop,
- dprivate::base_watcher *watcher, rearm rearmType);
+ dprivate::base_watcher *watcher, rearm rearm_type);
template <typename, typename> friend class dprivate::fd_watcher_impl;
template <typename, typename> friend class dprivate::bidi_fd_watcher_impl;
template <typename, typename> friend class dprivate::child_proc_watcher_impl;
template <typename, typename> friend class dprivate::timer_impl;
+ using backend_traits_t = typename Traits::backend_traits_t;
+
+ template <typename T, typename U> using event_dispatch = dprivate::event_dispatch<T,U,Traits>;
+ using loop_mech_t = typename Traits::template backend_t<event_dispatch<T_Mutex, backend_traits_t>>;
+ using reaper_mutex_t = typename loop_mech_t::reaper_mutex_t;
+
public:
- using loop_traits_t = LoopTraits;
- using time_val = dasynq::time_val;
+ using traits_t = Traits;
+ using loop_traits_t = typename loop_mech_t::traits_t;
+ using mutex_t = T_Mutex;
private:
- template <typename T, typename U> using EventDispatch = dprivate::EventDispatch<T,U>;
template <typename T> using waitqueue = dprivate::waitqueue<T>;
template <typename T> using waitqueue_node = dprivate::waitqueue_node<T>;
- using BaseWatcher = dprivate::base_watcher;
- using BaseSignalWatcher = dprivate::base_signal_watcher<T_Mutex,LoopTraits>;
- using BaseFdWatcher = dprivate::base_fd_watcher<T_Mutex>;
- using BaseBidiFdWatcher = dprivate::base_bidi_fd_watcher<T_Mutex>;
- using BaseChildWatcher = dprivate::base_child_watcher<T_Mutex>;
- using BaseTimerWatcher = dprivate::base_timer_watcher<T_Mutex>;
+ using base_watcher = dprivate::base_watcher;
+ using base_signal_watcher = dprivate::base_signal_watcher<T_Mutex, typename loop_traits_t::sigdata_t>;
+ using base_fd_watcher = dprivate::base_fd_watcher<T_Mutex>;
+ using base_bidi_fd_watcher = dprivate::base_bidi_fd_watcher<T_Mutex>;
+ using base_child_watcher = dprivate::base_child_watcher<T_Mutex>;
+ using base_timer_watcher = dprivate::base_timer_watcher<T_Mutex>;
using watch_type_t = dprivate::watch_type_t;
-
- Loop<EventDispatch<T_Mutex, LoopTraits>> loop_mech;
+
+ loop_mech_t loop_mech;
// There is a complex problem with most asynchronous event notification mechanisms
// when used in a multi-threaded environment. Generally, a file descriptor or other
// - The mutex only protects manipulation of the wait queues, and so should not
// be highly contended.
- T_Mutex wait_lock; // wait lock, used to prevent multiple threads from waiting
+ mutex_t wait_lock; // wait lock, used to prevent multiple threads from waiting
// on the event queue simultaneously.
- waitqueue<T_Mutex> attn_waitqueue;
- waitqueue<T_Mutex> wait_waitqueue;
+ waitqueue<mutex_t> attn_waitqueue;
+ waitqueue<mutex_t> wait_waitqueue;
- T_Mutex &getBaseLock() noexcept
+ mutex_t &get_base_lock() noexcept
{
return loop_mech.lock;
}
- void registerSignal(BaseSignalWatcher *callBack, int signo)
+ reaper_mutex_t &get_reaper_lock() noexcept
+ {
+ return loop_mech.get_reaper_lock();
+ }
+
+ void register_signal(base_signal_watcher *callBack, int signo)
{
+ auto & ed = (event_dispatch<T_Mutex, backend_traits_t> &) loop_mech;
+ std::lock_guard<mutex_t> guard(ed.lock);
+
loop_mech.prepare_watcher(callBack);
try {
- loop_mech.addSignalWatch(signo, callBack);
+ loop_mech.add_signal_watch_nolock(signo, callBack);
}
catch (...) {
loop_mech.release_watcher(callBack);
}
}
- void deregister(BaseSignalWatcher *callBack, int signo) noexcept
+ void deregister(base_signal_watcher *callBack, int signo) noexcept
{
- loop_mech.removeSignalWatch(signo);
+ loop_mech.remove_signal_watch(signo);
waitqueue_node<T_Mutex> qnode;
get_attn_lock(qnode);
- EventDispatch<T_Mutex, LoopTraits> & ed = (EventDispatch<T_Mutex, LoopTraits> &) loop_mech;
- ed.issueDelete(callBack);
+ auto & ed = (event_dispatch<T_Mutex, backend_traits_t> &) loop_mech;
+ ed.issue_delete(callBack);
release_lock(qnode);
}
- void registerFd(BaseFdWatcher *callback, int fd, int eventmask, bool enabled, bool emulate = false)
+ void register_fd(base_fd_watcher *callback, int fd, int eventmask, bool enabled, bool emulate = false)
{
+ auto & ed = (event_dispatch<T_Mutex, backend_traits_t> &) loop_mech;
+ std::lock_guard<mutex_t> guard(ed.lock);
+
loop_mech.prepare_watcher(callback);
+
try {
- if (! loop_mech.addFdWatch(fd, callback, eventmask | ONE_SHOT, enabled, emulate)) {
+ if (! loop_mech.add_fd_watch(fd, callback, eventmask | ONE_SHOT, enabled, emulate)) {
callback->emulatefd = true;
callback->emulate_enabled = enabled;
if (enabled) {
}
}
- void registerFd(BaseBidiFdWatcher *callback, int fd, int eventmask, bool emulate = false)
+ void register_fd(base_bidi_fd_watcher *callback, int fd, int eventmask, bool emulate = false)
{
+ auto & ed = (event_dispatch<T_Mutex, backend_traits_t> &) loop_mech;
+ std::lock_guard<mutex_t> guard(ed.lock);
+
loop_mech.prepare_watcher(callback);
try {
- loop_mech.prepare_watcher(&callback->outWatcher);
+ loop_mech.prepare_watcher(&callback->out_watcher);
try {
- if (LoopTraits::has_separate_rw_fd_watches) {
- int r = loop_mech.addBidiFdWatch(fd, callback, eventmask | ONE_SHOT, emulate);
+ if (backend_traits_t::has_separate_rw_fd_watches) {
+ int r = loop_mech.add_bidi_fd_watch(fd, callback, eventmask | ONE_SHOT, emulate);
if (r & IN_EVENTS) {
callback->emulatefd = true;
if (eventmask & IN_EVENTS) {
}
}
if (r & OUT_EVENTS) {
- callback->outWatcher.emulatefd = true;
+ callback->out_watcher.emulatefd = true;
if (eventmask & OUT_EVENTS) {
- requeue_watcher(&callback->outWatcher);
+ requeue_watcher(&callback->out_watcher);
}
}
}
else {
- if (! loop_mech.addFdWatch(fd, callback, eventmask | ONE_SHOT, true, emulate)) {
+ if (! loop_mech.add_fd_watch(fd, callback, eventmask | ONE_SHOT, true, emulate)) {
callback->emulatefd = true;
- callback->outWatcher.emulatefd = true;
+ callback->out_watcher.emulatefd = true;
if (eventmask & IN_EVENTS) {
requeue_watcher(callback);
}
if (eventmask & OUT_EVENTS) {
- requeue_watcher(&callback->outWatcher);
+ requeue_watcher(&callback->out_watcher);
}
}
}
}
catch (...) {
- loop_mech.release_watcher(&callback->outWatcher);
+ loop_mech.release_watcher(&callback->out_watcher);
throw;
}
}
}
}
- void setFdEnabled(BaseWatcher *watcher, int fd, int watch_flags, bool enabled) noexcept
+ void set_fd_enabled(base_watcher *watcher, int fd, int watch_flags, bool enabled) noexcept
{
if (enabled) {
- loop_mech.enableFdWatch(fd, watcher, watch_flags | ONE_SHOT);
+ loop_mech.enable_fd_watch(fd, watcher, watch_flags | ONE_SHOT);
}
else {
- loop_mech.disableFdWatch(fd, watch_flags);
+ loop_mech.disable_fd_watch(fd, watch_flags);
}
}
- void setFdEnabled_nolock(BaseWatcher *watcher, int fd, int watch_flags, bool enabled) noexcept
+ void set_fd_enabled_nolock(base_watcher *watcher, int fd, int watch_flags, bool enabled) noexcept
{
if (enabled) {
- loop_mech.enableFdWatch_nolock(fd, watcher, watch_flags | ONE_SHOT);
+ loop_mech.enable_fd_watch_nolock(fd, watcher, watch_flags | ONE_SHOT);
}
else {
- loop_mech.disableFdWatch_nolock(fd, watch_flags);
+ loop_mech.disable_fd_watch_nolock(fd, watch_flags);
}
}
- void deregister(BaseFdWatcher *callback, int fd) noexcept
+ void deregister(base_fd_watcher *callback, int fd) noexcept
{
if (callback->emulatefd) {
- auto & ed = (EventDispatch<T_Mutex, LoopTraits> &) loop_mech;
- ed.issueDelete(callback);
+ auto & ed = (event_dispatch<T_Mutex, backend_traits_t> &) loop_mech;
+ ed.issue_delete(callback);
return;
}
- loop_mech.removeFdWatch(fd, callback->watch_flags);
+ loop_mech.remove_fd_watch(fd, callback->watch_flags);
waitqueue_node<T_Mutex> qnode;
get_attn_lock(qnode);
- auto & ed = (EventDispatch<T_Mutex, LoopTraits> &) loop_mech;
- ed.issueDelete(callback);
+ auto & ed = (event_dispatch<T_Mutex, backend_traits_t> &) loop_mech;
+ ed.issue_delete(callback);
release_lock(qnode);
}
- void deregister(BaseBidiFdWatcher *callback, int fd) noexcept
+ void deregister(base_bidi_fd_watcher *callback, int fd) noexcept
{
- if (LoopTraits::has_separate_rw_fd_watches) {
- loop_mech.removeBidiFdWatch(fd);
+ if (backend_traits_t::has_separate_rw_fd_watches) {
+ loop_mech.remove_bidi_fd_watch(fd);
}
else {
- loop_mech.removeFdWatch(fd, callback->watch_flags);
+ loop_mech.remove_fd_watch(fd, callback->watch_flags);
}
waitqueue_node<T_Mutex> qnode;
get_attn_lock(qnode);
- EventDispatch<T_Mutex, LoopTraits> & ed = (EventDispatch<T_Mutex, LoopTraits> &) loop_mech;
- ed.issueDelete(callback);
+ event_dispatch<T_Mutex, backend_traits_t> & ed = (event_dispatch<T_Mutex, backend_traits_t> &) loop_mech;
+ ed.issue_delete(callback);
release_lock(qnode);
}
- void reserveChildWatch(BaseChildWatcher *callback)
+ void reserve_child_watch(base_child_watcher *callback)
{
+ auto & ed = (event_dispatch<T_Mutex, backend_traits_t> &) loop_mech;
+ std::lock_guard<mutex_t> guard(ed.lock);
+
loop_mech.prepare_watcher(callback);
try {
- loop_mech.reserveChildWatch(callback->watch_handle);
+ loop_mech.reserve_child_watch_nolock(callback->watch_handle);
}
catch (...) {
loop_mech.release_watcher(callback);
}
}
- void unreserve(BaseChildWatcher *callback) noexcept
+ void unreserve(base_child_watcher *callback) noexcept
{
- loop_mech.unreserveChildWatch(callback->watch_handle);
+ auto & ed = (event_dispatch<T_Mutex, backend_traits_t> &) loop_mech;
+ std::lock_guard<mutex_t> guard(ed.lock);
+
+ loop_mech.unreserve_child_watch(callback->watch_handle);
loop_mech.release_watcher(callback);
}
- void registerChild(BaseChildWatcher *callback, pid_t child)
+ void register_child(base_child_watcher *callback, pid_t child)
{
+ auto & ed = (event_dispatch<T_Mutex, backend_traits_t> &) loop_mech;
+ std::lock_guard<mutex_t> guard(ed.lock);
+
loop_mech.prepare_watcher(callback);
try {
- loop_mech.addChildWatch(callback->watch_handle, child, callback);
+ loop_mech.add_child_watch_nolock(callback->watch_handle, child, callback);
}
catch (...) {
loop_mech.release_watcher(callback);
}
}
- void registerReservedChild(BaseChildWatcher *callback, pid_t child) noexcept
+ void register_reserved_child(base_child_watcher *callback, pid_t child) noexcept
{
- loop_mech.addReservedChildWatch(callback->watch_handle, child, callback);
+ loop_mech.add_reserved_child_watch(callback->watch_handle, child, callback);
}
- void registerReservedChild_nolock(BaseChildWatcher *callback, pid_t child) noexcept
+ void register_reserved_child_nolock(base_child_watcher *callback, pid_t child) noexcept
{
- loop_mech.addReservedChildWatch_nolock(callback->watch_handle, child, callback);
+ loop_mech.add_reserved_child_watch_nolock(callback->watch_handle, child, callback);
}
- void deregister(BaseChildWatcher *callback, pid_t child) noexcept
+ void deregister(base_child_watcher *callback, pid_t child) noexcept
{
- loop_mech.removeChildWatch(callback->watch_handle);
+ loop_mech.remove_child_watch(callback->watch_handle);
waitqueue_node<T_Mutex> qnode;
get_attn_lock(qnode);
- EventDispatch<T_Mutex, LoopTraits> & ed = (EventDispatch<T_Mutex, LoopTraits> &) loop_mech;
- ed.issueDelete(callback);
+ event_dispatch<T_Mutex, backend_traits_t> & ed = (event_dispatch<T_Mutex, backend_traits_t> &) loop_mech;
+ ed.issue_delete(callback);
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
+ void stop_watch(base_child_watcher *callback) noexcept
{
loop_mech.stop_child_watch(callback->watch_handle);
}
- void registerTimer(BaseTimerWatcher *callback, clock_type clock)
+ void register_timer(base_timer_watcher *callback, clock_type clock)
{
+ auto & ed = (event_dispatch<T_Mutex, backend_traits_t> &) loop_mech;
+ std::lock_guard<mutex_t> guard(ed.lock);
+
loop_mech.prepare_watcher(callback);
try {
- loop_mech.addTimer(callback->timer_handle, callback, clock);
+ loop_mech.add_timer_nolock(callback->timer_handle, callback, clock);
}
catch (...) {
loop_mech.release_watcher(callback);
}
}
- void setTimer(BaseTimerWatcher *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.setTimer(callBack->timer_handle, timeout, interval, true, clock);
+ loop_mech.set_timer(callBack->timer_handle, timeout, interval, true, clock);
}
- void setTimer(BaseTimerWatcher *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.setTimer(callBack->timer_handle, timeout, interval, true, clock);
+ loop_mech.set_timer(callBack->timer_handle, timeout, interval, true, clock);
}
- void setTimerRel(BaseTimerWatcher *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.setTimerRel(callBack->timer_handle, timeout, interval, true, clock);
+ loop_mech.set_timer_rel(callBack->timer_handle, timeout, interval, true, clock);
}
- void setTimerRel(BaseTimerWatcher *callBack, const timespec &timeout,
+ void set_timer_rel(base_timer_watcher *callBack, const timespec &timeout,
const timespec &interval, clock_type clock) noexcept
{
- loop_mech.setTimerRel(callBack->timer_handle, timeout, interval, true, clock);
+ loop_mech.set_timer_rel(callBack->timer_handle, timeout, interval, true, clock);
}
- void stop_timer(BaseTimerWatcher *callback, clock_type clock) noexcept
+ void set_timer_enabled(base_timer_watcher *callback, clock_type clock, bool enabled) noexcept
+ {
+ loop_mech.enable_timer(callback->timer_handle, enabled, clock);
+ }
+
+ void set_timer_enabled_nolock(base_timer_watcher *callback, clock_type clock, bool enabled) noexcept
+ {
+ loop_mech.enable_timer_nolock(callback->timer_handle, enabled, clock);
+ }
+
+ void stop_timer(base_timer_watcher *callback, clock_type clock) noexcept
{
loop_mech.stop_timer(callback->timer_handle, clock);
}
- void deregister(BaseTimerWatcher *callback, clock_type clock) noexcept
+ void deregister(base_timer_watcher *callback, clock_type clock) noexcept
{
- loop_mech.removeTimer(callback->timer_handle, clock);
+ loop_mech.remove_timer(callback->timer_handle, clock);
waitqueue_node<T_Mutex> qnode;
get_attn_lock(qnode);
- EventDispatch<T_Mutex, LoopTraits> & ed = (EventDispatch<T_Mutex, LoopTraits> &) loop_mech;
- ed.issueDelete(callback);
+ event_dispatch<T_Mutex, backend_traits_t> & ed = (event_dispatch<T_Mutex, backend_traits_t> &) loop_mech;
+ ed.issue_delete(callback);
release_lock(qnode);
}
- void dequeue_watcher(BaseWatcher *watcher) noexcept
+ void dequeue_watcher(base_watcher *watcher) noexcept
{
- loop_mech.dequeueWatcher(watcher);
+ loop_mech.dequeue_watcher(watcher);
}
- void requeue_watcher(BaseWatcher *watcher) noexcept
+ void requeue_watcher(base_watcher *watcher) noexcept
{
- loop_mech.queueWatcher(watcher);
+ loop_mech.queue_watcher(watcher);
+
+ // We need to signal any thread that is currently waiting on the loop mechanism, so that it wakes
+ // and processes the newly queued watcher:
+
+ wait_lock.lock();
+ bool attn_q_empty = attn_waitqueue.is_empty();
+ wait_lock.unlock();
+
+ if (! attn_q_empty) {
+ loop_mech.interrupt_wait();
+ }
}
// Acquire the attention lock (when held, ensures that no thread is polling the AEN
{
std::unique_lock<T_Mutex> ulock(wait_lock);
attn_waitqueue.queue(&qnode);
- if (! attn_waitqueue.checkHead(qnode)) {
+ if (! attn_waitqueue.check_head(qnode)) {
loop_mech.interrupt_wait();
- while (! attn_waitqueue.checkHead(qnode)) {
+ while (! attn_waitqueue.check_head(qnode)) {
qnode.wait(ulock);
}
}
void get_pollwait_lock(waitqueue_node<T_Mutex> &qnode) noexcept
{
std::unique_lock<T_Mutex> ulock(wait_lock);
- if (attn_waitqueue.isEmpty()) {
+ if (attn_waitqueue.is_empty()) {
// Queue is completely empty:
attn_waitqueue.queue(&qnode);
}
wait_waitqueue.queue(&qnode);
}
- while (! attn_waitqueue.checkHead(qnode)) {
+ while (! attn_waitqueue.check_head(qnode)) {
qnode.wait(ulock);
}
}
nhead->signal();
}
else {
- if (! wait_waitqueue.isEmpty()) {
- auto nhead = wait_waitqueue.getHead();
+ if (! wait_waitqueue.is_empty()) {
+ auto nhead = wait_waitqueue.get_head();
wait_waitqueue.unqueue();
attn_waitqueue.queue(nhead);
nhead->signal();
}
}
- void processSignalRearm(BaseSignalWatcher * bsw, rearm rearmType) noexcept
+ void process_signal_rearm(base_signal_watcher * bsw, rearm rearm_type) noexcept
{
// Called with lock held
- if (rearmType == rearm::REARM) {
- loop_mech.rearmSignalWatch_nolock(bsw->siginfo.get_signo());
+ if (rearm_type == rearm::REARM) {
+ loop_mech.rearm_signal_watch_nolock(bsw->siginfo.get_signo(), bsw);
}
- else if (rearmType == rearm::REMOVE) {
- loop_mech.removeSignalWatch_nolock(bsw->siginfo.get_signo());
+ else if (rearm_type == rearm::REMOVE) {
+ loop_mech.remove_signal_watch_nolock(bsw->siginfo.get_signo());
}
// Note that signal watchers cannot (currently) be disarmed
}
// Process rearm return for fd_watcher, including the primary watcher of a bidi_fd_watcher
- rearm processFdRearm(BaseFdWatcher * bfw, rearm rearmType, bool is_multi_watch) noexcept
+ rearm process_fd_rearm(base_fd_watcher * bfw, rearm rearm_type, bool is_multi_watch) noexcept
{
- bool emulatedfd = static_cast<BaseWatcher *>(bfw)->emulatefd;
+ bool emulatedfd = static_cast<base_watcher *>(bfw)->emulatefd;
// Called with lock held
if (is_multi_watch) {
- BaseBidiFdWatcher * bdfw = static_cast<BaseBidiFdWatcher *>(bfw);
+ base_bidi_fd_watcher * bdfw = static_cast<base_bidi_fd_watcher *>(bfw);
- if (rearmType == rearm::REMOVE) {
+ if (rearm_type == rearm::REMOVE) {
bdfw->read_removed = 1;
- if (LoopTraits::has_separate_rw_fd_watches) {
+ if (backend_traits_t::has_separate_rw_fd_watches) {
bdfw->watch_flags &= ~IN_EVENTS;
if (! emulatedfd) {
- loop_mech.removeFdWatch_nolock(bdfw->watch_fd, IN_EVENTS);
+ loop_mech.remove_fd_watch_nolock(bdfw->watch_fd, IN_EVENTS);
}
return bdfw->write_removed ? rearm::REMOVE : rearm::NOOP;
}
if (bdfw->watch_flags & IN_EVENTS) {
bdfw->watch_flags &= ~IN_EVENTS;
if (! emulatedfd) {
- loop_mech.enableFdWatch_nolock(bdfw->watch_fd, bdfw, bdfw->watch_flags);
+ loop_mech.enable_fd_watch_nolock(bdfw->watch_fd, bdfw, bdfw->watch_flags);
}
}
return rearm::NOOP;
else {
// both removed: actually remove
if (! emulatedfd) {
- loop_mech.removeFdWatch_nolock(bdfw->watch_fd, 0 /* not used */);
+ loop_mech.remove_fd_watch_nolock(bdfw->watch_fd, 0 /* not used */);
}
return rearm::REMOVE;
}
}
}
- else if (rearmType == rearm::DISARM) {
+ else if (rearm_type == rearm::DISARM) {
bdfw->watch_flags &= ~IN_EVENTS;
if (! emulatedfd) {
- if (! LoopTraits::has_separate_rw_fd_watches) {
+ if (! backend_traits_t::has_separate_rw_fd_watches) {
int watch_flags = bdfw->watch_flags;
- // without separate r/w watches, enableFdWatch actually sets
+ // without separate r/w watches, enable_fd_watch actually sets
// which sides are enabled (i.e. can be used to disable):
- loop_mech.enableFdWatch_nolock(bdfw->watch_fd,
- static_cast<BaseWatcher *>(bdfw),
+ loop_mech.enable_fd_watch_nolock(bdfw->watch_fd,
+ static_cast<base_watcher *>(bdfw),
(watch_flags & (IN_EVENTS | OUT_EVENTS)) | ONE_SHOT);
}
else {
- loop_mech.disableFdWatch_nolock(bdfw->watch_fd, IN_EVENTS);
+ loop_mech.disable_fd_watch_nolock(bdfw->watch_fd, IN_EVENTS);
}
}
}
- else if (rearmType == rearm::REARM) {
+ else if (rearm_type == rearm::REARM) {
bdfw->watch_flags |= IN_EVENTS;
if (! emulatedfd) {
- if (! LoopTraits::has_separate_rw_fd_watches) {
+ if (! backend_traits_t::has_separate_rw_fd_watches) {
int watch_flags = bdfw->watch_flags;
- loop_mech.enableFdWatch_nolock(bdfw->watch_fd,
- static_cast<BaseWatcher *>(bdfw),
+ loop_mech.enable_fd_watch_nolock(bdfw->watch_fd,
+ static_cast<base_watcher *>(bdfw),
(watch_flags & (IN_EVENTS | OUT_EVENTS)) | ONE_SHOT);
}
else {
- loop_mech.enableFdWatch_nolock(bdfw->watch_fd,
- static_cast<BaseWatcher *>(bdfw),
+ loop_mech.enable_fd_watch_nolock(bdfw->watch_fd,
+ static_cast<base_watcher *>(bdfw),
IN_EVENTS | ONE_SHOT);
}
}
else {
- rearmType = rearm::REQUEUE;
+ rearm_type = rearm::REQUEUE;
}
}
- else if (rearmType == rearm::NOOP) {
+ else if (rearm_type == rearm::NOOP) {
if (bdfw->emulatefd) {
if (bdfw->watch_flags & IN_EVENTS) {
- rearmType = rearm::REQUEUE;
+ rearm_type = rearm::REQUEUE;
}
}
}
- return rearmType;
+ return rearm_type;
}
else { // Not multi-watch:
if (emulatedfd) {
- if (rearmType == rearm::REARM) {
+ if (rearm_type == rearm::REARM) {
bfw->emulate_enabled = true;
- rearmType = rearm::REQUEUE;
+ rearm_type = rearm::REQUEUE;
}
- else if (rearmType == rearm::DISARM) {
+ else if (rearm_type == rearm::DISARM) {
bfw->emulate_enabled = false;
}
- else if (rearmType == rearm::NOOP) {
+ else if (rearm_type == rearm::NOOP) {
if (bfw->emulate_enabled) {
- rearmType = rearm::REQUEUE;
+ rearm_type = rearm::REQUEUE;
}
}
}
- else if (rearmType == rearm::REARM) {
- loop_mech.enableFdWatch_nolock(bfw->watch_fd, bfw,
+ else if (rearm_type == rearm::REARM) {
+ loop_mech.enable_fd_watch_nolock(bfw->watch_fd, bfw,
(bfw->watch_flags & (IN_EVENTS | OUT_EVENTS)) | ONE_SHOT);
}
- else if (rearmType == rearm::DISARM) {
- loop_mech.disableFdWatch_nolock(bfw->watch_fd, bfw->watch_flags);
+ else if (rearm_type == rearm::DISARM) {
+ loop_mech.disable_fd_watch_nolock(bfw->watch_fd, bfw->watch_flags);
}
- else if (rearmType == rearm::REMOVE) {
- loop_mech.removeFdWatch_nolock(bfw->watch_fd, bfw->watch_flags);
+ else if (rearm_type == rearm::REMOVE) {
+ loop_mech.remove_fd_watch_nolock(bfw->watch_fd, bfw->watch_flags);
}
- return rearmType;
+ return rearm_type;
}
}
// Process re-arm for the secondary (output) watcher in a Bi-direction Fd watcher.
- rearm processSecondaryRearm(BaseBidiFdWatcher * bdfw, BaseWatcher * outw, rearm rearmType) noexcept
+ rearm process_secondary_rearm(base_bidi_fd_watcher * bdfw, base_watcher * outw, rearm rearm_type) noexcept
{
bool emulatedfd = outw->emulatefd;
// Called with lock held
if (emulatedfd) {
- if (rearmType == rearm::REMOVE) {
+ if (rearm_type == rearm::REMOVE) {
bdfw->write_removed = 1;
bdfw->watch_flags &= ~OUT_EVENTS;
- rearmType = bdfw->read_removed ? rearm::REMOVE : rearm::NOOP;
+ rearm_type = bdfw->read_removed ? rearm::REMOVE : rearm::NOOP;
}
- else if (rearmType == rearm::DISARM) {
+ else if (rearm_type == rearm::DISARM) {
bdfw->watch_flags &= ~OUT_EVENTS;
}
- else if (rearmType == rearm::REARM) {
+ else if (rearm_type == rearm::REARM) {
bdfw->watch_flags |= OUT_EVENTS;
- rearmType = rearm::REQUEUE;
+ rearm_type = rearm::REQUEUE;
}
- else if (rearmType == rearm::NOOP) {
+ else if (rearm_type == rearm::NOOP) {
if (bdfw->watch_flags & OUT_EVENTS) {
- rearmType = rearm::REQUEUE;
+ rearm_type = rearm::REQUEUE;
}
}
- return rearmType;
+ return rearm_type;
}
- else if (rearmType == rearm::REMOVE) {
+ else if (rearm_type == rearm::REMOVE) {
bdfw->write_removed = 1;
- if (LoopTraits::has_separate_rw_fd_watches) {
+ if (backend_traits_t::has_separate_rw_fd_watches) {
bdfw->watch_flags &= ~OUT_EVENTS;
- loop_mech.removeFdWatch_nolock(bdfw->watch_fd, OUT_EVENTS);
+ loop_mech.remove_fd_watch_nolock(bdfw->watch_fd, OUT_EVENTS);
return bdfw->read_removed ? rearm::REMOVE : rearm::NOOP;
}
else {
if (! bdfw->read_removed) {
if (bdfw->watch_flags & OUT_EVENTS) {
bdfw->watch_flags &= ~OUT_EVENTS;
- loop_mech.enableFdWatch_nolock(bdfw->watch_fd, bdfw, bdfw->watch_flags);
+ loop_mech.enable_fd_watch_nolock(bdfw->watch_fd, bdfw, bdfw->watch_flags);
}
return rearm::NOOP;
}
else {
// both removed: actually remove
- loop_mech.removeFdWatch_nolock(bdfw->watch_fd, 0 /* not used */);
+ loop_mech.remove_fd_watch_nolock(bdfw->watch_fd, 0 /* not used */);
return rearm::REMOVE;
}
}
}
- else if (rearmType == rearm::DISARM) {
+ else if (rearm_type == rearm::DISARM) {
bdfw->watch_flags &= ~OUT_EVENTS;
- if (! LoopTraits::has_separate_rw_fd_watches) {
+ if (! backend_traits_t::has_separate_rw_fd_watches) {
int watch_flags = bdfw->watch_flags;
- loop_mech.enableFdWatch_nolock(bdfw->watch_fd,
- static_cast<BaseWatcher *>(bdfw),
+ loop_mech.enable_fd_watch_nolock(bdfw->watch_fd,
+ static_cast<base_watcher *>(bdfw),
(watch_flags & (IN_EVENTS | OUT_EVENTS)) | ONE_SHOT);
}
else {
- loop_mech.disableFdWatch_nolock(bdfw->watch_fd, OUT_EVENTS);
+ loop_mech.disable_fd_watch_nolock(bdfw->watch_fd, OUT_EVENTS);
}
}
- else if (rearmType == rearm::REARM) {
+ else if (rearm_type == rearm::REARM) {
bdfw->watch_flags |= OUT_EVENTS;
- if (! LoopTraits::has_separate_rw_fd_watches) {
+ if (! backend_traits_t::has_separate_rw_fd_watches) {
int watch_flags = bdfw->watch_flags;
- loop_mech.enableFdWatch_nolock(bdfw->watch_fd,
- static_cast<BaseWatcher *>(bdfw),
+ loop_mech.enable_fd_watch_nolock(bdfw->watch_fd,
+ static_cast<base_watcher *>(bdfw),
(watch_flags & (IN_EVENTS | OUT_EVENTS)) | ONE_SHOT);
}
else {
- loop_mech.enableFdWatch_nolock(bdfw->watch_fd,
- static_cast<BaseWatcher *>(bdfw),
+ loop_mech.enable_fd_watch_nolock(bdfw->watch_fd,
+ static_cast<base_watcher *>(bdfw),
OUT_EVENTS | ONE_SHOT);
}
}
- return rearmType;
+ return rearm_type;
}
- void process_child_watch_rearm(BaseChildWatcher *bcw, rearm rearm_type) noexcept
+ void process_child_watch_rearm(base_child_watcher *bcw, rearm rearm_type) noexcept
{
if (rearm_type == rearm::REMOVE || rearm_type == rearm::DISARM) {
- loop_mech.unreserveChildWatch_nolock(bcw->watch_handle);
+ loop_mech.unreserve_child_watch_nolock(bcw->watch_handle);
}
}
- void processTimerRearm(BaseTimerWatcher *btw, rearm rearmType) noexcept
+ void process_timer_rearm(base_timer_watcher *btw, rearm rearm_type) noexcept
{
// Called with lock held
- if (rearmType == rearm::REARM) {
- loop_mech.enableTimer_nolock(btw->timer_handle, true, btw->clock);
+ if (rearm_type == rearm::REARM) {
+ loop_mech.enable_timer_nolock(btw->timer_handle, true, btw->clock);
}
- else if (rearmType == rearm::REMOVE) {
- loop_mech.removeTimer_nolock(btw->timer_handle, btw->clock);
+ else if (rearm_type == rearm::REMOVE) {
+ loop_mech.remove_timer_nolock(btw->timer_handle, btw->clock);
}
- else if (rearmType == rearm::DISARM) {
- loop_mech.enableTimer_nolock(btw->timer_handle, false, btw->clock);
+ else if (rearm_type == rearm::DISARM) {
+ loop_mech.enable_timer_nolock(btw->timer_handle, false, btw->clock);
}
}
- // Process all queued events; returns true if any events were processed.
- bool processEvents() noexcept
+ // Process queued events; returns true if any events were processed.
+ // limit - maximum number of events to process before returning; -1 for
+ // no limit.
+ bool process_events(int limit) noexcept
{
- EventDispatch<T_Mutex, LoopTraits> & ed = (EventDispatch<T_Mutex, LoopTraits> &) loop_mech;
+ auto & ed = (event_dispatch<T_Mutex, backend_traits_t> &) loop_mech;
ed.lock.lock();
// So this pulls *all* currently pending events and processes them in the current thread.
// That's probably good for throughput, but maybe the behaviour should be configurable.
- BaseWatcher * pqueue = ed.pullEvent();
+ base_watcher * pqueue = ed.pull_event();
bool active = false;
- while (pqueue != nullptr) {
+ while (pqueue != nullptr && limit != 0) {
pqueue->active = true;
active = true;
- BaseBidiFdWatcher *bbfw = nullptr;
+ base_bidi_fd_watcher *bbfw = nullptr;
// (Above variables are initialised only to silence compiler warnings).
if (pqueue->watchType == watch_type_t::SECONDARYFD) {
- // construct a pointer to the main watcher:
+ // construct a pointer to the main watcher (using char* arithmetic):
char * rp = (char *)pqueue;
+
+ // Here we take the offset of a member from a non-standard-layout class, which is
+ // specified to have undefined result by the C++ language standard, but which
+ // in practice works fine:
_Pragma ("GCC diagnostic push")
_Pragma ("GCC diagnostic ignored \"-Winvalid-offsetof\"")
- rp -= offsetof(BaseBidiFdWatcher, outWatcher);
+ rp -= offsetof(base_bidi_fd_watcher, out_watcher);
_Pragma ("GCC diagnostic pop")
- bbfw = (BaseBidiFdWatcher *)rp;
+ bbfw = (base_bidi_fd_watcher *)rp;
// issue a secondary dispatch:
bbfw->dispatch_second(this);
- pqueue = ed.pullEvent();
+ pqueue = ed.pull_event();
continue;
}
pqueue->dispatch(this);
- pqueue = ed.pullEvent();
+ pqueue = ed.pull_event();
+ if (limit > 0) limit--;
}
ed.lock.unlock();
}
public:
- using mutex_t = T_Mutex;
using fd_watcher = dprivate::fd_watcher<my_event_loop_t>;
using bidi_fd_watcher = dprivate::bidi_fd_watcher<my_event_loop_t>;
template <typename D> using child_proc_watcher_impl = dprivate::child_proc_watcher_impl<my_event_loop_t, D>;
template <typename D> using timer_impl = dprivate::timer_impl<my_event_loop_t, D>;
- // Poll the event loop and process any pending events. If no events are pending, wait
+ // Poll the event loop and process any pending events (up to a limit). If no events are pending, wait
// for and process at least one event.
- void run() noexcept
+ void run(int limit = -1) noexcept
{
// Poll the mechanism first, in case high-priority events are pending:
waitqueue_node<T_Mutex> qnode;
get_pollwait_lock(qnode);
- loop_mech.pullEvents(false);
+ loop_mech.pull_events(false);
release_lock(qnode);
- while (! processEvents()) {
+ while (! process_events(limit)) {
// Pull events from the AEN mechanism and insert them in our internal queue:
get_pollwait_lock(qnode);
- loop_mech.pullEvents(true);
+ loop_mech.pull_events(true);
release_lock(qnode);
}
}
- // Poll the event loop and process any pending events
- void poll() noexcept
+ // Poll the event loop and process any pending events (up to a limit).
+ void poll(int limit = -1) noexcept
{
waitqueue_node<T_Mutex> qnode;
get_pollwait_lock(qnode);
- loop_mech.pullEvents(false);
+ loop_mech.pull_events(false);
release_lock(qnode);
- processEvents();
+ process_events(limit);
}
// Get the current time corresponding to a specific clock.
// Posix signal event watcher
template <typename EventLoop>
-class signal_watcher : private dprivate::base_signal_watcher<typename EventLoop::mutex_t, typename EventLoop::loop_traits_t>
+class signal_watcher : private dprivate::base_signal_watcher<typename EventLoop::mutex_t, typename EventLoop::loop_traits_t::sigdata_t>
{
template <typename, typename> friend class signal_watcher_impl;
- using BaseWatcher = dprivate::base_watcher;
+ using base_watcher = dprivate::base_watcher;
using T_Mutex = typename EventLoop::mutex_t;
public:
- using siginfo_p = typename dprivate::base_signal_watcher<T_Mutex, typename EventLoop::loop_traits_t>::siginfo_p;
+ using event_loop_t = EventLoop;
+ using siginfo_p = typename signal_watcher::siginfo_p;
// Register this watcher to watch the specified signal.
// If an attempt is made to register with more than one event loop at
// a time, behaviour is undefined. The signal should be masked before
// call.
- inline void add_watch(EventLoop &eloop, int signo, int prio = DEFAULT_PRIORITY)
+ inline void add_watch(event_loop_t &eloop, int signo, int prio = DEFAULT_PRIORITY)
{
- BaseWatcher::init();
+ base_watcher::init();
this->priority = prio;
this->siginfo.set_signo(signo);
- eloop.registerSignal(this, signo);
+ eloop.register_signal(this, signo);
}
- inline void deregister(EventLoop &eloop) noexcept
+ inline void deregister(event_loop_t &eloop) noexcept
{
eloop.deregister(this, this->siginfo.get_signo());
}
template <typename T>
- static signal_watcher<EventLoop> *add_watch(EventLoop &eloop, int signo, T watchHndlr)
+ static signal_watcher<event_loop_t> *add_watch(event_loop_t &eloop, int signo, T watch_hndlr)
{
- class LambdaSigWatcher : public signal_watcher_impl<EventLoop, LambdaSigWatcher>
+ class lambda_sig_watcher : public signal_watcher_impl<event_loop_t, lambda_sig_watcher>
{
private:
- T watchHndlr;
+ T watch_hndlr;
public:
- LambdaSigWatcher(T watchHandlr_a) : watchHndlr(watchHandlr_a)
+ lambda_sig_watcher(T watch_handlr_a) : watch_hndlr(watch_handlr_a)
{
//
}
- rearm received(EventLoop &eloop, int signo, siginfo_p siginfo)
+ rearm received(event_loop_t &eloop, int signo, siginfo_p siginfo)
{
- return watchHndlr(eloop, signo, siginfo);
+ return watch_hndlr(eloop, signo, siginfo);
}
void watch_removed() noexcept override
}
};
- LambdaSigWatcher * lsw = new LambdaSigWatcher(watchHndlr);
+ lambda_sig_watcher * lsw = new lambda_sig_watcher(watch_hndlr);
lsw->add_watch(eloop, signo);
return lsw;
}
void dispatch(void *loop_ptr) noexcept override
{
EventLoop &loop = *static_cast<EventLoop *>(loop_ptr);
- loop.getBaseLock().unlock();
+ loop.get_base_lock().unlock();
- auto rearmType = static_cast<Derived *>(this)->received(loop, this->siginfo.get_signo(), this->siginfo);
+ auto rearm_type = static_cast<Derived *>(this)->received(loop, this->siginfo.get_signo(), this->siginfo);
- loop.getBaseLock().lock();
+ loop.get_base_lock().lock();
- if (rearmType != rearm::REMOVED) {
+ if (rearm_type != rearm::REMOVED) {
this->active = false;
if (this->deleteme) {
// We don't want a watch that is marked "deleteme" to re-arm itself.
- rearmType = rearm::REMOVE;
+ rearm_type = rearm::REMOVE;
}
- loop.processSignalRearm(this, rearmType);
+ loop.process_signal_rearm(this, rearm_type);
- post_dispatch(loop, this, rearmType);
+ post_dispatch(loop, this, rearm_type);
}
}
};
{
template <typename, typename> friend class fd_watcher_impl;
- using BaseWatcher = dprivate::base_watcher;
- using T_Mutex = typename EventLoop::mutex_t;
+ using base_watcher = dprivate::base_watcher;
+ using mutex_t = typename EventLoop::mutex_t;
protected:
- // Set the types of event to watch. Only supported if LoopTraits::has_bidi_fd_watch
+ // Set the types of event to watch. Only supported if loop_traits_t_t::has_bidi_fd_watch
// is true; otherwise has unspecified behavior.
// Only safe to call from within the callback handler (fdEvent). Might not take
// effect until the current callback handler returns with REARM.
public:
+ using event_loop_t = EventLoop;
+
// Register a file descriptor watcher with an event loop. Flags
// can be any combination of dasynq::IN_EVENTS / dasynq::OUT_EVENTS.
// Exactly one of IN_EVENTS/OUT_EVENTS must be specified if the event
// loop does not support bi-directional fd watchers (i.e. if
- // ! LoopTraits::has_bidi_fd_watch).
+ // ! loop_traits_t::has_bidi_fd_watch).
//
// Mechanisms supporting dual watchers allow for two watchers for a
// single file descriptor (one watching read status and the other
// causes undefined behavior.
//
// Can fail with std::bad_alloc or std::system_error.
- void add_watch(EventLoop &eloop, int fd, int flags, bool enabled = true, int prio = DEFAULT_PRIORITY)
+ void add_watch(event_loop_t &eloop, int fd, int flags, bool enabled = true, int prio = DEFAULT_PRIORITY)
{
- BaseWatcher::init();
+ base_watcher::init();
this->priority = prio;
this->watch_fd = fd;
this->watch_flags = flags;
- eloop.registerFd(this, fd, flags, enabled, true);
+ eloop.register_fd(this, fd, flags, enabled, true);
}
- void add_watch_noemu(EventLoop &eloop, int fd, int flags, bool enabled = true, int prio = DEFAULT_PRIORITY)
+ void add_watch_noemu(event_loop_t &eloop, int fd, int flags, bool enabled = true, int prio = DEFAULT_PRIORITY)
{
- BaseWatcher::init();
+ base_watcher::init();
this->priority = prio;
this->watch_fd = fd;
this->watch_flags = flags;
- eloop.registerFd(this, fd, flags, enabled, false);
+ eloop.register_fd(this, fd, flags, enabled, false);
}
int get_watched_fd()
// In a single threaded environment, it is safe to delete the watcher after
// calling this method as long as the handler (if it is active) accesses no
// internal state and returns rearm::REMOVED.
- void deregister(EventLoop &eloop) noexcept
+ void deregister(event_loop_t &eloop) noexcept
{
eloop.deregister(this, this->watch_fd);
}
- void set_enabled(EventLoop &eloop, bool enable) noexcept
+ void set_enabled(event_loop_t &eloop, bool enable) noexcept
{
- std::lock_guard<T_Mutex> guard(eloop.getBaseLock());
+ std::lock_guard<mutex_t> guard(eloop.get_base_lock());
if (this->emulatefd) {
this->emulate_enabled = enable;
}
else {
- eloop.setFdEnabled_nolock(this, this->watch_fd, this->watch_flags, enable);
+ eloop.set_fd_enabled_nolock(this, this->watch_fd, this->watch_flags, enable);
}
if (! enable) {
eloop.dequeue_watcher(this);
// Add an Fd watch via a lambda. The watch is allocated dynamically and destroys
// itself when removed from the event loop.
template <typename T>
- static fd_watcher<EventLoop> *add_watch(EventLoop &eloop, int fd, int flags, T watchHndlr)
+ static fd_watcher<EventLoop> *add_watch(event_loop_t &eloop, int fd, int flags, T watchHndlr)
{
- class lambda_fd_watcher : public fd_watcher_impl<EventLoop, lambda_fd_watcher>
+ class lambda_fd_watcher : public fd_watcher_impl<event_loop_t, lambda_fd_watcher>
{
private:
T watchHndlr;
//
}
- rearm fd_event(EventLoop &eloop, int fd, int flags)
+ rearm fd_event(event_loop_t &eloop, int fd, int flags)
{
return watchHndlr(eloop, fd, flags);
}
// In case emulating, clear enabled here; REARM or explicit set_enabled will re-enable.
this->emulate_enabled = false;
- loop.getBaseLock().unlock();
+ loop.get_base_lock().unlock();
- auto rearmType = static_cast<Derived *>(this)->fd_event(loop, this->watch_fd, this->event_flags);
+ auto rearm_type = static_cast<Derived *>(this)->fd_event(loop, this->watch_fd, this->event_flags);
- loop.getBaseLock().lock();
+ loop.get_base_lock().lock();
- if (rearmType != rearm::REMOVED) {
+ if (rearm_type != rearm::REMOVED) {
this->event_flags = 0;
this->active = false;
if (this->deleteme) {
// We don't want a watch that is marked "deleteme" to re-arm itself.
- rearmType = rearm::REMOVE;
+ rearm_type = rearm::REMOVE;
}
- rearmType = loop.processFdRearm(this, rearmType, false);
+ rearm_type = loop.process_fd_rearm(this, rearm_type, false);
- post_dispatch(loop, this, rearmType);
+ post_dispatch(loop, this, rearm_type);
}
}
};
{
template <typename, typename> friend class bidi_fd_watcher_impl;
- using BaseWatcher = dprivate::base_watcher;
- using T_Mutex = typename EventLoop::mutex_t;
+ using base_watcher = dprivate::base_watcher;
+ using mutex_t = typename EventLoop::mutex_t;
void set_watch_enabled(EventLoop &eloop, bool in, bool b)
{
this->watch_flags &= ~events;
}
- dprivate::base_watcher * watcher = in ? this : &this->outWatcher;
+ dprivate::base_watcher * watcher = in ? this : &this->out_watcher;
- if (! basewatcher_get_emulatefd(*watcher)) {
+ if (! watcher->emulatefd) {
if (EventLoop::loop_traits_t::has_separate_rw_fd_watches) {
- eloop.setFdEnabled_nolock(watcher, this->watch_fd, events | ONE_SHOT, b);
+ eloop.set_fd_enabled_nolock(this, this->watch_fd, events | ONE_SHOT, b);
}
else {
- eloop.setFdEnabled_nolock(this, this->watch_fd,
+ eloop.set_fd_enabled_nolock(this, this->watch_fd,
(this->watch_flags & IO_EVENTS) | ONE_SHOT,
(this->watch_flags & IO_EVENTS) != 0);
}
public:
- void set_in_watch_enabled(EventLoop &eloop, bool b) noexcept
+ using event_loop_t = EventLoop;
+
+ void set_in_watch_enabled(event_loop_t &eloop, bool b) noexcept
{
- eloop.getBaseLock().lock();
+ eloop.get_base_lock().lock();
set_watch_enabled(eloop, true, b);
- eloop.getBaseLock().unlock();
+ eloop.get_base_lock().unlock();
}
- void set_out_watch_enabled(EventLoop &eloop, bool b) noexcept
+ void set_out_watch_enabled(event_loop_t &eloop, bool b) noexcept
{
- eloop.getBaseLock().lock();
+ eloop.get_base_lock().lock();
set_watch_enabled(eloop, false, b);
- eloop.getBaseLock().unlock();
+ eloop.get_base_lock().unlock();
}
// Set the watch flags, which enables/disables both the in-watch and the out-watch accordingly.
// (i.e. it is ok to call setWatchFlags from within the readReady/writeReady handlers if no other
// thread will poll the event loop; it is always ok to *dis*able a watcher that might be active,
// though the re-arm action returned by the callback may undo the effect).
- void set_watches(EventLoop &eloop, int newFlags)
+ void set_watches(event_loop_t &eloop, int new_flags) noexcept
{
- std::lock_guard<T_Mutex> guard(eloop.getBaseLock());
- bool use_emulation = this->emulatefd || basewatcher_get_emulatefd(this->outWatcher);
+ std::lock_guard<mutex_t> guard(eloop.get_base_lock());
+ bool use_emulation = this->emulatefd || this->out_watcher.emulatefd;
if (use_emulation || EventLoop::loop_traits_t::has_separate_rw_fd_watches) {
- set_watch_enabled(eloop, true, (newFlags & IN_EVENTS) != 0);
- set_watch_enabled(eloop, false, (newFlags & OUT_EVENTS) != 0);
+ set_watch_enabled(eloop, true, (new_flags & IN_EVENTS) != 0);
+ set_watch_enabled(eloop, false, (new_flags & OUT_EVENTS) != 0);
}
else {
- this->watch_flags = (this->watch_flags & ~IO_EVENTS) | newFlags;
- eloop.setFdEnabled((dprivate::base_watcher *) this, this->watch_fd, this->watch_flags & IO_EVENTS, true);
+ this->watch_flags = (this->watch_flags & ~IO_EVENTS) | new_flags;
+ eloop.set_fd_enabled_nolock((dprivate::base_watcher *) this, this->watch_fd, this->watch_flags & IO_EVENTS, true);
}
}
// can be any combination of dasynq::IN_EVENTS / dasynq::OUT_EVENTS.
//
// Can fail with std::bad_alloc or std::system_error.
- void add_watch(EventLoop &eloop, int fd, int flags, int inprio = DEFAULT_PRIORITY, int outprio = DEFAULT_PRIORITY)
+ void add_watch(event_loop_t &eloop, int fd, int flags, int inprio = DEFAULT_PRIORITY, int outprio = DEFAULT_PRIORITY)
{
- BaseWatcher::init();
- this->outWatcher.BaseWatcher::init();
+ base_watcher::init();
+ this->out_watcher.base_watcher::init();
this->watch_fd = fd;
this->watch_flags = flags | dprivate::multi_watch;
this->read_removed = false;
this->write_removed = false;
this->priority = inprio;
- this->set_priority(this->outWatcher, outprio);
- eloop.registerFd(this, fd, flags, true);
+ this->set_priority(this->out_watcher, outprio);
+ eloop.register_fd(this, fd, flags, true);
}
- void add_watch_noemu(EventLoop &eloop, int fd, int flags, int inprio = DEFAULT_PRIORITY, int outprio = DEFAULT_PRIORITY)
+ void add_watch_noemu(event_loop_t &eloop, int fd, int flags, int inprio = DEFAULT_PRIORITY, int outprio = DEFAULT_PRIORITY)
{
- BaseWatcher::init();
- this->outWatcher.BaseWatcher::init();
+ base_watcher::init();
+ this->out_watcher.base_watcher::init();
this->watch_fd = fd;
this->watch_flags = flags | dprivate::multi_watch;
this->read_removed = false;
this->write_removed = false;
this->priority = inprio;
- this->set_priority(this->outWatcher, outprio);
- eloop.registerFd(this, fd, flags, false);
+ this->set_priority(this->out_watcher, outprio);
+ eloop.register_fd(this, fd, flags, false);
}
int get_watched_fd()
// In a single threaded environment, it is safe to delete the watcher after
// calling this method as long as the handler (if it is active) accesses no
// internal state and returns rearm::REMOVED.
- void deregister(EventLoop &eloop) noexcept
+ void deregister(event_loop_t &eloop) noexcept
{
eloop.deregister(this, this->watch_fd);
}
template <typename T>
- static bidi_fd_watcher<EventLoop> *add_watch(EventLoop &eloop, int fd, int flags, T watchHndlr)
+ static bidi_fd_watcher<event_loop_t> *add_watch(event_loop_t &eloop, int fd, int flags, T watch_hndlr)
{
- class LambdaBidiWatcher : public bidi_fd_watcher_impl<EventLoop, LambdaBidiWatcher>
+ class lambda_bidi_watcher : public bidi_fd_watcher_impl<event_loop_t, lambda_bidi_watcher>
{
private:
- T watchHndlr;
+ T watch_hndlr;
public:
- LambdaBidiWatcher(T watchHandlr_a) : watchHndlr(watchHandlr_a)
+ lambda_bidi_watcher(T watch_handlr_a) : watch_hndlr(watch_handlr_a)
{
//
}
- rearm read_ready(EventLoop &eloop, int fd)
+ rearm read_ready(event_loop_t &eloop, int fd)
{
- return watchHndlr(eloop, fd, IN_EVENTS);
+ return watch_hndlr(eloop, fd, IN_EVENTS);
}
- rearm write_ready(EventLoop &eloop, int fd)
+ rearm write_ready(event_loop_t &eloop, int fd)
{
- return watchHndlr(eloop, fd, OUT_EVENTS);
+ return watch_hndlr(eloop, fd, OUT_EVENTS);
}
void watch_removed() noexcept override
}
};
- LambdaBidiWatcher * lfd = new LambdaBidiWatcher(watchHndlr);
+ lambda_bidi_watcher * lfd = new lambda_bidi_watcher(watch_hndlr);
lfd->add_watch(eloop, fd, flags);
return lfd;
}
{
EventLoop &loop = *static_cast<EventLoop *>(loop_ptr);
this->emulate_enabled = false;
- loop.getBaseLock().unlock();
+ loop.get_base_lock().unlock();
- auto rearmType = static_cast<Derived *>(this)->read_ready(loop, this->watch_fd);
+ auto rearm_type = static_cast<Derived *>(this)->read_ready(loop, this->watch_fd);
- loop.getBaseLock().lock();
+ loop.get_base_lock().lock();
- if (rearmType != rearm::REMOVED) {
+ if (rearm_type != rearm::REMOVED) {
this->event_flags &= ~IN_EVENTS;
this->active = false;
if (this->deleteme) {
// We don't want a watch that is marked "deleteme" to re-arm itself.
- rearmType = rearm::REMOVE;
+ rearm_type = rearm::REMOVE;
}
- rearmType = loop.processFdRearm(this, rearmType, true);
+ rearm_type = loop.process_fd_rearm(this, rearm_type, true);
- post_dispatch(loop, this, rearmType);
+ post_dispatch(loop, this, rearm_type);
}
}
void dispatch_second(void *loop_ptr) noexcept override
{
- auto &outwatcher = bidi_fd_watcher<EventLoop>::outWatcher;
+ auto &outwatcher = bidi_fd_watcher<EventLoop>::out_watcher;
EventLoop &loop = *static_cast<EventLoop *>(loop_ptr);
- loop.getBaseLock().unlock();
+ loop.get_base_lock().unlock();
- auto rearmType = static_cast<Derived *>(this)->write_ready(loop, this->watch_fd);
+ auto rearm_type = static_cast<Derived *>(this)->write_ready(loop, this->watch_fd);
- loop.getBaseLock().lock();
+ loop.get_base_lock().lock();
- if (rearmType != rearm::REMOVED) {
+ if (rearm_type != rearm::REMOVED) {
this->event_flags &= ~OUT_EVENTS;
- basewatcher_set_active(outwatcher, false);
- if (basewatcher_get_deleteme(outwatcher)) {
+ outwatcher.active = false;
+ if (outwatcher.deleteme) {
// We don't want a watch that is marked "deleteme" to re-arm itself.
- rearmType = rearm::REMOVE;
+ rearm_type = rearm::REMOVE;
}
- rearmType = loop.processSecondaryRearm(this, &outwatcher, rearmType);
+ rearm_type = loop.process_secondary_rearm(this, &outwatcher, rearm_type);
- if (rearmType == rearm::REQUEUE) {
- post_dispatch(loop, &outwatcher, rearmType);
+ if (rearm_type == rearm::REQUEUE) {
+ post_dispatch(loop, &outwatcher, rearm_type);
}
else {
- post_dispatch(loop, this, rearmType);
+ post_dispatch(loop, this, rearm_type);
}
}
}
{
template <typename, typename> friend class child_proc_watcher_impl;
- using BaseWatcher = dprivate::base_watcher;
- using T_Mutex = typename EventLoop::mutex_t;
+ using base_watcher = dprivate::base_watcher;
+ using mutex_t = typename EventLoop::mutex_t;
public:
+
+ using event_loop_t = EventLoop;
+
+ // 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
+ // already terminated.
+ int send_signal(event_loop_t &loop, int signo) noexcept
+ {
+ auto reaper_mutex = loop.get_reaper_mutex();
+ std::lock_guard<decltype(reaper_mutex)> guard(reaper_mutex);
+
+ if (this->child_termd) {
+ errno = ESRCH;
+ return -1;
+ }
+
+ return kill(this->watch_pid, signo);
+ }
+
// Reserve resources for a child watcher with the given event loop.
// Reservation can fail with std::bad_alloc. Some backends do not support
- // reservation (it will always fail) - check LoopTraits::supports_childwatch_reservation.
- void reserve_watch(EventLoop &eloop)
+ // reservation (it will always fail) - check loop_traits_t::supports_childwatch_reservation.
+ void reserve_watch(event_loop_t &eloop)
{
- eloop.reserveChildWatch(this);
+ eloop.reserve_child_watch(this);
}
- void unreserve(EventLoop &eloop)
+ void unreserve(event_loop_t &eloop)
{
eloop.unreserve(this);
}
// Registration can fail with std::bad_alloc.
// Note that in multi-threaded programs, use of this function may be prone to a
// race condition such that the child terminates before the watcher is registered.
- void add_watch(EventLoop &eloop, pid_t child, int prio = DEFAULT_PRIORITY)
+ void add_watch(event_loop_t &eloop, pid_t child, int prio = DEFAULT_PRIORITY)
{
- BaseWatcher::init();
+ base_watcher::init();
this->watch_pid = child;
this->priority = prio;
- eloop.registerChild(this, child);
+ eloop.register_child(this, child);
}
// Register a watcher for the given child process with an event loop,
// Note that in multi-threaded programs, use of this function may be prone to a
// race condition such that the child terminates before the watcher is registered;
// use the "fork" member function to avoid this.
- void add_reserved(EventLoop &eloop, pid_t child, int prio = DEFAULT_PRIORITY) noexcept
+ void add_reserved(event_loop_t &eloop, pid_t child, int prio = DEFAULT_PRIORITY) noexcept
{
- BaseWatcher::init();
+ base_watcher::init();
this->watch_pid = child;
this->priority = prio;
- eloop.registerReservedChild(this, child);
+ eloop.register_reserved_child(this, child);
}
- void deregister(EventLoop &eloop, pid_t child) noexcept
+ void deregister(event_loop_t &eloop, pid_t child) noexcept
{
eloop.deregister(this, child);
}
// Stop watching the currently watched child, but retain watch reservation.
- void stop_watch(EventLoop &eloop) noexcept
+ void stop_watch(event_loop_t &eloop) noexcept
{
eloop.stop_watch(this);
}
// Returns:
// - the child pid in the parent
// - 0 in the child
- pid_t fork(EventLoop &eloop, bool from_reserved = false, int prio = DEFAULT_PRIORITY)
+ pid_t fork(event_loop_t &eloop, bool from_reserved = false, int prio = DEFAULT_PRIORITY)
{
- BaseWatcher::init();
+ base_watcher::init();
this->priority = prio;
if (EventLoop::loop_traits_t::supports_childwatch_reservation) {
reserve_watch(eloop);
}
- auto &lock = eloop.getBaseLock();
+ auto &lock = eloop.get_base_lock();
lock.lock();
pid_t child = ::fork();
// Register this watcher.
this->watch_pid = child;
- eloop.registerReservedChild_nolock(this, child);
+ eloop.register_reserved_child_nolock(this, child);
lock.unlock();
return child;
}
throw std::system_error(errno, std::system_category());
}
- std::lock_guard<T_Mutex> guard(eloop.getBaseLock());
+ std::lock_guard<mutex_t> guard(eloop.get_base_lock());
pid_t child = ::fork();
if (child == -1) {
// Register this watcher.
try {
this->watch_pid = child;
- eloop.registerChild(this, child);
+ eloop.register_child(this, child);
// Continue in child (it doesn't matter what is written):
write(pipefds[1], &pipefds, sizeof(int));
void dispatch(void *loop_ptr) noexcept override
{
EventLoop &loop = *static_cast<EventLoop *>(loop_ptr);
- loop.getBaseLock().unlock();
+ loop.get_base_lock().unlock();
- auto rearmType = static_cast<Derived *>(this)->status_change(loop, this->watch_pid, this->child_status);
+ auto rearm_type = static_cast<Derived *>(this)->status_change(loop, this->watch_pid, this->child_status);
- loop.getBaseLock().lock();
+ loop.get_base_lock().lock();
- if (rearmType != rearm::REMOVED) {
+ if (rearm_type != rearm::REMOVED) {
this->active = false;
if (this->deleteme) {
// We don't want a watch that is marked "deleteme" to re-arm itself.
- rearmType = rearm::REMOVE;
+ rearm_type = rearm::REMOVE;
}
- loop.process_child_watch_rearm(this, rearmType);
+ loop.process_child_watch_rearm(this, rearm_type);
- // rearmType = loop.process??;
- post_dispatch(loop, this, rearmType);
+ // rearm_type = loop.process??;
+ post_dispatch(loop, this, rearm_type);
}
}
};
{
template <typename, typename> friend class timer_impl;
using base_t = base_timer_watcher<typename EventLoop::mutex_t>;
+ using mutex_t = typename EventLoop::mutex_t;
public:
+ using event_loop_t = EventLoop;
- void add_timer(EventLoop &eloop, clock_type clock = clock_type::MONOTONIC, int prio = DEFAULT_PRIORITY)
+ void add_timer(event_loop_t &eloop, clock_type clock = clock_type::MONOTONIC, int prio = DEFAULT_PRIORITY)
{
base_watcher::init();
this->priority = prio;
this->clock = clock;
- eloop.registerTimer(this, clock);
+ this->intervals = 0;
+ eloop.register_timer(this, clock);
}
- void arm_timer(EventLoop &eloop, const timespec &timeout) noexcept
+ void arm_timer(event_loop_t &eloop, const timespec &timeout) noexcept
{
- eloop.setTimer(this, timeout, base_t::clock);
+ eloop.set_timer(this, timeout, base_t::clock);
}
- void arm_timer(EventLoop &eloop, const timespec &timeout, const timespec &interval) noexcept
+ void arm_timer(event_loop_t &eloop, const timespec &timeout, const timespec &interval) noexcept
{
- eloop.setTimer(this, timeout, interval, base_t::clock);
+ eloop.set_timer(this, timeout, interval, base_t::clock);
}
// Arm timer, relative to now:
- void arm_timer_rel(EventLoop &eloop, const timespec &timeout) noexcept
+ void arm_timer_rel(event_loop_t &eloop, const timespec &timeout) noexcept
{
- eloop.setTimerRel(this, timeout, base_t::clock);
+ eloop.set_timer_rel(this, timeout, base_t::clock);
}
- void arm_timer_rel(EventLoop &eloop, const timespec &timeout,
+ void arm_timer_rel(event_loop_t &eloop, const timespec &timeout,
const timespec &interval) noexcept
{
- eloop.setTimerRel(this, timeout, interval, base_t::clock);
+ eloop.set_timer_rel(this, timeout, interval, base_t::clock);
}
- void stop_timer(EventLoop &eloop) noexcept
+ void stop_timer(event_loop_t &eloop) noexcept
{
eloop.stop_timer(this, base_t::clock);
}
- void deregister(EventLoop &eloop) noexcept
+ void set_enabled(event_loop_t &eloop, clock_type clock, bool enabled) noexcept
+ {
+ std::lock_guard<mutex_t> guard(eloop.get_base_lock());
+ eloop.set_timer_enabled_nolock(this, clock, enabled);
+ if (! enabled) {
+ eloop.dequeue_watcher(this);
+ }
+ }
+
+ void deregister(event_loop_t &eloop) noexcept
{
eloop.deregister(this, this->clock);
}
template <typename T>
static timer<EventLoop> *add_timer(EventLoop &eloop, clock_type clock, bool relative,
- struct timespec &timeout, struct timespec &interval, T watchHndlr)
+ struct timespec &timeout, struct timespec &interval, T watch_hndlr)
{
- class lambda_timer : public timer_impl<EventLoop, lambda_timer>
+ class lambda_timer : public timer_impl<event_loop_t, lambda_timer>
{
private:
- T watchHndlr;
+ T watch_hndlr;
public:
- lambda_timer(T watchHandlr_a) : watchHndlr(watchHandlr_a)
+ lambda_timer(T watch_handlr_a) : watch_hndlr(watch_handlr_a)
{
//
}
- rearm timer_expiry(EventLoop &eloop, int intervals)
+ rearm timer_expiry(event_loop_t &eloop, int intervals)
{
- return watchHndlr(eloop, intervals);
+ return watch_hndlr(eloop, intervals);
}
void watch_removed() noexcept override
}
};
- lambda_timer * lt = new lambda_timer(watchHndlr);
+ lambda_timer * lt = new lambda_timer(watch_hndlr);
lt->add_timer(eloop, clock);
if (relative) {
lt->arm_timer_rel(eloop, timeout, interval);
// Timer expired, and the given number of intervals have elapsed before
// expiry event was queued. Normally intervals == 1 to indicate no
// overrun.
- // virtual rearm timer_expiry(EventLoop &eloop, int intervals) = 0;
+ // virtual rearm timer_expiry(event_loop_t &eloop, int intervals) = 0;
};
template <typename EventLoop, typename Derived>
void dispatch(void *loop_ptr) noexcept override
{
EventLoop &loop = *static_cast<EventLoop *>(loop_ptr);
- loop.getBaseLock().unlock();
+ loop.get_base_lock().unlock();
- auto rearmType = static_cast<Derived *>(this)->timer_expiry(loop, this->intervals);
+ auto intervals_report = this->intervals;
+ this->intervals = 0;
+ auto rearm_type = static_cast<Derived *>(this)->timer_expiry(loop, intervals_report);
- loop.getBaseLock().lock();
+ loop.get_base_lock().lock();
- if (rearmType != rearm::REMOVED) {
+ if (rearm_type != rearm::REMOVED) {
this->active = false;
if (this->deleteme) {
// We don't want a watch that is marked "deleteme" to re-arm itself.
- rearmType = rearm::REMOVE;
+ rearm_type = rearm::REMOVE;
}
- loop.processTimerRearm(this, rearmType);
+ loop.process_timer_rearm(this, rearm_type);
- post_dispatch(loop, this, rearmType);
+ post_dispatch(loop, this, rearm_type);
}
}
};
bool base_process_service::restart_ps_process() noexcept
{
- using time_val = eventloop_t::time_val;
+ using time_val = dasynq::time_val;
time_val current_time;
eventLoop.get_time(current_time, clock_type::MONOTONIC);