Incorporate upstream changes from Dasynq.
authorDavin McCall <davmac@davmac.org>
Mon, 11 Dec 2017 18:45:12 +0000 (05:45 +1100)
committerDavin McCall <davmac@davmac.org>
Mon, 11 Dec 2017 18:45:12 +0000 (05:45 +1100)
This updates bundled Dasynq to 1.0 (plus minor changes since).

17 files changed:
src/dasynq/dasynq-basewatchers.h [new file with mode: 0644]
src/dasynq/dasynq-btree_set.h
src/dasynq/dasynq-childproc.h
src/dasynq/dasynq-config.h
src/dasynq/dasynq-epoll.h
src/dasynq/dasynq-interrupt.h
src/dasynq/dasynq-itimer.h
src/dasynq/dasynq-kqueue.h
src/dasynq/dasynq-mutex.h
src/dasynq/dasynq-naryheap.h
src/dasynq/dasynq-posixtimer.h
src/dasynq/dasynq-svec.h [deleted file]
src/dasynq/dasynq-timerbase.h
src/dasynq/dasynq-timerfd.h
src/dasynq/dasynq-util.h [new file with mode: 0644]
src/dasynq/dasynq.h
src/service.cc

diff --git a/src/dasynq/dasynq-basewatchers.h b/src/dasynq/dasynq-basewatchers.h
new file mode 100644 (file)
index 0000000..cbabe91
--- /dev/null
@@ -0,0 +1,226 @@
+// 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
index 80885e924b5290130463d9012c35c3f2d1ba5f69..2e2c09043c086a1666e96d561f0810a0fc64e299 100644 (file)
@@ -1,7 +1,6 @@
 #ifndef DASYNQ_BTREE_SET_H
 #define DASYNQ_BTREE_SET_H
 
-#include <vector>
 #include <functional>
 
 namespace dasynq {
@@ -577,6 +576,10 @@ class btree_set
 
     ~btree_set()
     {
+        while (left_sept != nullptr) {
+            remove(*(left_sept->hn_p[0]));
+        }
+
         while (sn_reserve != nullptr) {
             auto *next = sn_reserve->parent;
             delete sn_reserve;
index 8d364f762b5d828f00c5692aac939bbbc04138ea..f3f5e7ff98ea93a539b419855e32ea4dbfe2a47a 100644 (file)
@@ -3,6 +3,8 @@
 
 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
@@ -76,28 +78,42 @@ inline void sigchld_handler(int signum)
     // 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 {
@@ -106,36 +122,34 @@ template <class Base> class ChildProcEvents : public Base
     }
     
     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);
     }
@@ -147,26 +161,41 @@ template <class Base> class ChildProcEvents : public Base
         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);
     }
 };
index 6c1cbcae1d60ae60600643dadfb01df3ea6af796..cea00c3e1fc2f86dae4cd73f4cd26e8549398d0a 100644 (file)
@@ -1,18 +1,62 @@
 #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
index cdc70466e2e626fe86fab002d7553e9135d46a9e..0cc5466473c6606cc3922343d4ba1f970d3087cc 100644 (file)
 
 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).
@@ -52,10 +72,10 @@ class EpollTraits
 
     // 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;
         }
@@ -63,11 +83,10 @@ class EpollTraits
     
     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
@@ -78,13 +97,13 @@ template <class Base> class EpollLoop : public Base
     // 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);
         
@@ -93,7 +112,7 @@ template <class Base> class EpollLoop : public Base
             
             if (ptr == &sigfd) {
                 // Signal
-                SigInfo siginfo;
+                sigdata_t siginfo;
                 while (true) {
                     int r = read(sigfd, &siginfo.info, sizeof(siginfo.info));
                     if (r == -1) break;
@@ -113,7 +132,7 @@ template <class Base> class EpollLoop : public Base
                 (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);
             }            
         }
     }
@@ -121,11 +140,11 @@ template <class Base> class EpollLoop : public Base
     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) {
@@ -135,7 +154,7 @@ template <class Base> class EpollLoop : public Base
         Base::init(this);
     }
     
-    ~EpollLoop()
+    ~epoll_loop()
     {
         close(epfd);
         if (sigfd != -1) {
@@ -150,7 +169,7 @@ template <class Base> class EpollLoop : public Base
     //             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;
@@ -176,7 +195,7 @@ template <class Base> class EpollLoop : public Base
         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));
@@ -184,25 +203,25 @@ template <class Base> class EpollLoop : public Base
     
     // 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;
@@ -225,12 +244,12 @@ template <class Base> class EpollLoop : public Base
         }
     }
     
-    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;
@@ -246,16 +265,21 @@ template <class Base> class EpollLoop : public Base
         }
     }
     
-    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
@@ -282,22 +306,22 @@ template <class Base> class EpollLoop : public Base
     }
     
     // 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.
@@ -306,11 +330,11 @@ template <class Base> class EpollLoop : public Base
     // 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);
@@ -320,7 +344,7 @@ template <class Base> class EpollLoop : public Base
         }
     
         do {
-            processEvents(events, r);
+            process_events(events, r);
             r = epoll_wait(epfd, events, 16, 0);
         } while (r > 0);
     }
index 58642207f2463c0ebe04eab7454dc69c3892799f..07ee12d6d8f4be4a66ff75b5e37dc8b62cf880fb 100644 (file)
@@ -6,6 +6,7 @@
 
 #include "dasynq-config.h"
 #include "dasynq-mutex.h"
+#include "dasynq-util.h"
 
 /*
  * Mechanism for interrupting an event loop wait.
@@ -27,24 +28,10 @@ template <typename Base> class interrupt_channel<Base, null_mutex> : public Base
 
 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;
@@ -62,7 +49,7 @@ template <typename Base, typename Mutex> class interrupt_channel : public Base
         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);
@@ -74,7 +61,7 @@ template <typename Base, typename Mutex> class interrupt_channel : public Base
     }
 
     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
@@ -82,7 +69,7 @@ template <typename Base, typename Mutex> class interrupt_channel : public Base
             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);
         }
     }
 
index 99f360f92e1c3de7b0486ca884f91f467411b9ed..c4d42a17b36846546d72734a86ae28ea23f86a41 100644 (file)
@@ -10,58 +10,69 @@ namespace dasynq {
 
 // 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,
@@ -76,18 +87,29 @@ template <class Base> class ITimerEvents : public timer_base<Base>
     
     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
         }
@@ -98,41 +120,27 @@ template <class Base> class ITimerEvents : public timer_base<Base>
         
     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;
 
@@ -157,44 +165,23 @@ template <class Base> class ITimerEvents : public timer_base<Base>
     }
 
     // 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);
@@ -203,6 +190,7 @@ template <class Base> class ITimerEvents : public timer_base<Base>
 
     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);
@@ -211,18 +199,6 @@ template <class Base> class ITimerEvents : public timer_base<Base>
             }
         }
     }
-
-    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);
-    }
 };
 
 }
index 4f586b48475b8213d730e48a46b0c5f8fa78d96a..b475f00f275fc4aaa563f9a96e1bd93142cb083e 100644 (file)
@@ -4,18 +4,11 @@
 #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>
@@ -36,74 +29,72 @@ extern "C" {
 
 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) { }
 
@@ -168,53 +159,61 @@ inline bool get_siginfo(int signo, siginfo_t *siginfo)
 
 #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).
@@ -228,39 +227,76 @@ template <class Base> class KqueueLoop : public Base
         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);
@@ -273,17 +309,31 @@ template <class Base> class KqueueLoop : public Base
     //             (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());
@@ -294,14 +344,14 @@ template <class Base> class KqueueLoop : public Base
     // 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);
@@ -323,7 +373,16 @@ template <class Base> class KqueueLoop : public Base
 
         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);
@@ -368,17 +427,17 @@ template <class Base> class KqueueLoop : public Base
     
     // 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);
@@ -387,61 +446,76 @@ template <class Base> class KqueueLoop : public Base
         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);
@@ -449,47 +523,10 @@ template <class Base> class KqueueLoop : public Base
         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:
@@ -500,16 +537,15 @@ void pull_signals()
     // 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);
@@ -519,7 +555,7 @@ void pull_signals()
         }
         
         do {
-            processEvents(events, r);
+            process_events(events, r);
             r = kevent(kqfd, nullptr, 0, events, 16, &ts);
         } while (r > 0);
     }
index 312c22d0bfef6059ccf8a2fb5d4c5ab5962e0453..b4023e76926d05f090dd93d8be66aa892fa8a110 100644 (file)
@@ -1,55 +1,22 @@
 #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
index 4156a4258bdb37339dc0d6cf9fa20eede4aee456..7713b817162a950c99675920886a7bbf209bcb24 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef DASYNC_NARYHEAP_H_INCLUDED
 #define DASYNC_NARYHEAP_H_INCLUDED
 
-#include "dasynq-svec.h"
+#include <vector>
 #include <type_traits>
 #include <functional>
 #include <limits>
@@ -51,7 +51,7 @@ class NaryHeap
         HeapNode() { }
     };
 
-    svector<HeapNode> hvec;
+    std::vector<HeapNode> hvec;
 
     using hindex_t = typename decltype(hvec)::size_type;
 
@@ -259,8 +259,7 @@ class NaryHeap
     {
         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();
@@ -289,10 +288,14 @@ class NaryHeap
     {
         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();
         }
     }
 
index 85a0304f39636208ac73de5caf84b1d71bb00f87..3473330a01ad7a0cce8ef7e80b7ffe837c05ca99 100644 (file)
@@ -13,12 +13,9 @@ namespace dasynq {
 // 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;
 
@@ -42,27 +39,29 @@ template <class Base> class PosixTimerEvents : public timer_base<Base>
 
     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 {
@@ -70,18 +69,6 @@ template <class Base> class PosixTimerEvents : public timer_base<Base>
         }
     }
 
-    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) {
@@ -96,13 +83,18 @@ template <class Base> class PosixTimerEvents : public timer_base<Base>
 
     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;
@@ -123,38 +115,16 @@ template <class Base> class PosixTimerEvents : public timer_base<Base>
         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);
@@ -176,7 +146,7 @@ template <class Base> class PosixTimerEvents : public timer_base<Base>
     }
 
     // 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;
@@ -192,29 +162,7 @@ template <class Base> class PosixTimerEvents : public timer_base<Base>
             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
@@ -225,7 +173,7 @@ template <class Base> class PosixTimerEvents : public timer_base<Base>
 
     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)) {
@@ -237,20 +185,7 @@ template <class Base> class PosixTimerEvents : public timer_base<Base>
         }
     }
 
-    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);
diff --git a/src/dasynq/dasynq-svec.h b/src/dasynq/dasynq-svec.h
deleted file mode 100644 (file)
index 32131d2..0000000
+++ /dev/null
@@ -1,184 +0,0 @@
-#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
index 87ae20e1ac61c32be7b0252a812d18a0f8e870a8..3e013dca9d89143e9d426ab99f8a9431cdb76095 100644 (file)
@@ -2,6 +2,7 @@
 #define DASYNQ_TIMERBASE_H_INCLUDED
 
 #include <utility>
+#include <mutex>
 
 #include "dasynq-naryheap.h"
 
@@ -16,40 +17,45 @@ class time_val
     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();
@@ -63,7 +69,7 @@ inline time_val operator-(const time_val &t1, const time_val &t2)
     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();
@@ -75,7 +81,7 @@ inline time_val operator+(const time_val &t1, const time_val &t2)
     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();
@@ -87,28 +93,88 @@ inline time_val &operator+=(time_val &t1, const time_val &t2)
     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
@@ -119,7 +185,7 @@ 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
     }
@@ -128,7 +194,7 @@ class timer_data
 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;
@@ -142,7 +208,7 @@ class compare_timespec
     }
 };
 
-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
@@ -150,7 +216,7 @@ 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;
@@ -176,74 +242,86 @@ static inline int divide_timespec(const struct timespec &num, const struct times
 
     // 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;
@@ -255,7 +333,7 @@ template <typename Base> class timer_base : public Base
                     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;
@@ -279,7 +357,7 @@ template <typename Base> class timer_base : public Base
                     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);
                 }
             }
 
@@ -287,6 +365,71 @@ template <typename Base> class timer_base : public Base
             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;
+        }
+    }
 };
 
 }
index 032c5dae7f54eb161b3f3fe8bff56a1080860589..025bbba8b9493c3f8ecbe0b1e60a89f379374a3d 100644 (file)
@@ -8,6 +8,8 @@
 
 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.
@@ -17,14 +19,11 @@ namespace dasynq {
 // 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).
@@ -42,8 +41,9 @@ template <class Base> class TimerFdEvents : public timer_base<Base>
         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:
@@ -62,7 +62,7 @@ template <class Base> class TimerFdEvents : public timer_base<Base>
         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;
@@ -88,30 +88,24 @@ template <class Base> class TimerFdEvents : public timer_base<Base>
         }
     }
 
-    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);
         }
     }
 
@@ -128,8 +122,8 @@ template <class Base> class TimerFdEvents : public timer_base<Base>
         }
 
         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 (...) {
@@ -139,27 +133,6 @@ template <class Base> class TimerFdEvents : public timer_base<Base>
         }
     }
 
-    // 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);
@@ -168,10 +141,10 @@ template <class Base> class TimerFdEvents : public timer_base<Base>
 
     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);
@@ -181,18 +154,19 @@ template <class Base> class TimerFdEvents : public timer_base<Base>
 
     // 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;
@@ -200,73 +174,17 @@ template <class Base> class TimerFdEvents : public timer_base<Base>
     }
 
     // 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);
diff --git a/src/dasynq/dasynq-util.h b/src/dasynq/dasynq-util.h
new file mode 100644 (file)
index 0000000..b14c81d
--- /dev/null
@@ -0,0 +1,41 @@
+#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
index bff5a0d8db61b6a226ff3088727cabc74fc988f1..c471fa4546e3037dd978bca3e292b5ba34adfeae 100644 (file)
@@ -6,6 +6,7 @@
 #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
@@ -16,7 +17,7 @@
 // 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>
@@ -77,38 +78,9 @@ namespace dasynq {
 
 #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
@@ -129,195 +101,7 @@ enum class rearm
     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
@@ -329,15 +113,15 @@ namespace dprivate {
     // 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;
@@ -357,9 +141,11 @@ namespace dprivate {
 
     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()
@@ -376,22 +162,23 @@ namespace dprivate {
     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;
         }
@@ -407,6 +194,7 @@ namespace dprivate {
         waitqueue_node<T_Mutex> * head = nullptr;
 
         public:
+        // remove current head of queue, return new head:
         waitqueue_node<T_Mutex> * unqueue()
         {
             head = head->next;
@@ -416,23 +204,24 @@ namespace dprivate {
             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;
             }
@@ -445,53 +234,59 @@ namespace dprivate {
     
     // 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);
@@ -505,24 +300,30 @@ namespace dprivate {
         }
         
         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;
             
@@ -530,49 +331,52 @@ namespace dprivate {
             
             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;
@@ -584,7 +388,9 @@ namespace dprivate {
             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)
@@ -601,7 +407,7 @@ namespace dprivate {
             }
             else {
                 // Actually do the delete.
-                dequeueWatcher(watcher);
+                dequeue_watcher(watcher);
                 release_watcher(watcher);
                 
                 lock.unlock();
@@ -609,7 +415,9 @@ namespace dprivate {
             }
         }
         
-        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();
             
@@ -618,18 +426,18 @@ namespace dprivate {
                 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;
             }
@@ -642,17 +450,16 @@ namespace dprivate {
                 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>;
@@ -660,7 +467,7 @@ class event_loop
     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;
@@ -668,23 +475,29 @@ class event_loop
     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
@@ -729,21 +542,29 @@ class event_loop
     // - 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);
@@ -751,24 +572,28 @@ class event_loop
         }
     }
     
-    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) {
@@ -785,14 +610,17 @@ class event_loop
         }
     }
     
-    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) {
@@ -800,27 +628,27 @@ class event_loop
                         }
                     }
                     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;
             }
         }
@@ -830,68 +658,71 @@ class event_loop
         }
     }
     
-    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);
@@ -899,17 +730,23 @@ class event_loop
         }
     }
     
-    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);
@@ -917,97 +754,121 @@ class event_loop
         }
     }
     
-    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
@@ -1017,9 +878,9 @@ class event_loop
     {
         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);
             }
         }
@@ -1032,7 +893,7 @@ class event_loop
     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);
         }
@@ -1040,7 +901,7 @@ class event_loop
             wait_waitqueue.queue(&qnode);
         }
         
-        while (! attn_waitqueue.checkHead(qnode)) {
+        while (! attn_waitqueue.check_head(qnode)) {
             qnode.wait(ulock);
         }    
     }
@@ -1054,8 +915,8 @@ class event_loop
             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();
@@ -1063,34 +924,34 @@ class event_loop
         }                
     }
     
-    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;
                 }
@@ -1099,7 +960,7 @@ class event_loop
                         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;
@@ -1107,226 +968,233 @@ class event_loop
                     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();
@@ -1334,7 +1202,6 @@ class event_loop
     }
 
     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>;
@@ -1348,33 +1215,33 @@ class event_loop
     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.
@@ -1401,50 +1268,51 @@ namespace dprivate {
 
 // 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
@@ -1453,7 +1321,7 @@ class signal_watcher : private dprivate::base_signal_watcher<typename EventLoop:
             }
         };
 
-        LambdaSigWatcher * lsw = new LambdaSigWatcher(watchHndlr);
+        lambda_sig_watcher * lsw = new lambda_sig_watcher(watch_hndlr);
         lsw->add_watch(eloop, signo);
         return lsw;
     }
@@ -1467,23 +1335,23 @@ class signal_watcher_impl : public signal_watcher<EventLoop>
     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);
         }
     }
 };
@@ -1494,12 +1362,12 @@ class fd_watcher : private dprivate::base_fd_watcher<typename EventLoop::mutex_t
 {
     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.
@@ -1510,11 +1378,13 @@ class fd_watcher : private dprivate::base_fd_watcher<typename EventLoop::mutex_t
     
     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
@@ -1523,22 +1393,22 @@ class fd_watcher : private dprivate::base_fd_watcher<typename EventLoop::mutex_t
     // 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()
@@ -1554,19 +1424,19 @@ class fd_watcher : private dprivate::base_fd_watcher<typename EventLoop::mutex_t
     // 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);
@@ -1576,9 +1446,9 @@ class fd_watcher : private dprivate::base_fd_watcher<typename EventLoop::mutex_t
     // 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;
@@ -1589,7 +1459,7 @@ class fd_watcher : private dprivate::base_fd_watcher<typename EventLoop::mutex_t
                 //
             }
 
-            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);
             }
@@ -1618,23 +1488,23 @@ class fd_watcher_impl : public fd_watcher<EventLoop>
         // 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);
         }
     }
 };
@@ -1648,8 +1518,8 @@ class bidi_fd_watcher : private dprivate::base_bidi_fd_watcher<typename EventLoo
 {
     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)
     {
@@ -1662,14 +1532,14 @@ class bidi_fd_watcher : private dprivate::base_bidi_fd_watcher<typename EventLoo
             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);
             }
@@ -1682,18 +1552,20 @@ class bidi_fd_watcher : private dprivate::base_bidi_fd_watcher<typename EventLoo
     
     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.
@@ -1704,17 +1576,17 @@ class bidi_fd_watcher : private dprivate::base_bidi_fd_watcher<typename EventLoo
     // (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);
         }
     }
     
@@ -1722,30 +1594,30 @@ class bidi_fd_watcher : private dprivate::base_bidi_fd_watcher<typename EventLoo
     // 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()
@@ -1761,33 +1633,33 @@ class bidi_fd_watcher : private dprivate::base_bidi_fd_watcher<typename EventLoo
     // 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
@@ -1796,7 +1668,7 @@ class bidi_fd_watcher : private dprivate::base_bidi_fd_watcher<typename EventLoo
             }
         };
 
-        LambdaBidiWatcher * lfd = new LambdaBidiWatcher(watchHndlr);
+        lambda_bidi_watcher * lfd = new lambda_bidi_watcher(watch_hndlr);
         lfd->add_watch(eloop, fd, flags);
         return lfd;
     }
@@ -1812,52 +1684,52 @@ class bidi_fd_watcher_impl : public bidi_fd_watcher<EventLoop>
     {
         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);
             }
         }
     }
@@ -1869,19 +1741,38 @@ class child_proc_watcher : private dprivate::base_child_watcher<typename EventLo
 {
     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);
     }
@@ -1890,12 +1781,12 @@ class child_proc_watcher : private dprivate::base_child_watcher<typename EventLo
     // 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,
@@ -1904,21 +1795,21 @@ class child_proc_watcher : private dprivate::base_child_watcher<typename EventLo
     // 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);
     }
@@ -1930,9 +1821,9 @@ class child_proc_watcher : private dprivate::base_child_watcher<typename EventLo
     // 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) {
@@ -1941,7 +1832,7 @@ class child_proc_watcher : private dprivate::base_child_watcher<typename EventLo
                 reserve_watch(eloop);
             }
             
-            auto &lock = eloop.getBaseLock();
+            auto &lock = eloop.get_base_lock();
             lock.lock();
             
             pid_t child = ::fork();
@@ -1960,7 +1851,7 @@ class child_proc_watcher : private dprivate::base_child_watcher<typename EventLo
             
             // Register this watcher.
             this->watch_pid = child;
-            eloop.registerReservedChild_nolock(this, child);
+            eloop.register_reserved_child_nolock(this, child);
             lock.unlock();
             return child;
         }
@@ -1970,7 +1861,7 @@ class child_proc_watcher : private dprivate::base_child_watcher<typename EventLo
                 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) {
@@ -1998,7 +1889,7 @@ class child_proc_watcher : private dprivate::base_child_watcher<typename EventLo
             // 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));
@@ -2022,24 +1913,24 @@ class child_proc_watcher_impl : public child_proc_watcher<EventLoop>
     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);
         }
     }
 };
@@ -2049,67 +1940,79 @@ class timer : private base_timer_watcher<typename EventLoop::mutex_t>
 {
     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
@@ -2118,7 +2021,7 @@ class timer : private base_timer_watcher<typename EventLoop::mutex_t>
             }
         };
 
-        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);
@@ -2132,7 +2035,7 @@ class timer : private base_timer_watcher<typename EventLoop::mutex_t>
     // 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>
@@ -2141,23 +2044,25 @@ class timer_impl : public timer<EventLoop>
     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);
         }
     }
 };
index 5ef7d2dbbc89f5df294b2719058760e76b997f17..838d7ee565d609253102136f8bd4355525a0a639 100644 (file)
@@ -1368,7 +1368,7 @@ void base_process_service::do_restart() noexcept
 
 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);