Minor fixes to tests.
[oweals/dinit.git] / src / dasynq / dasynq.h
1 #ifndef DASYNQ_H_INCLUDED
2 #define DASYNQ_H_INCLUDED
3
4 #include "dasynq-config.h"
5
6 #include "dasynq-flags.h"
7 #include "dasynq-stableheap.h"
8 #include "dasynq-interrupt.h"
9 #include "dasynq-util.h"
10
11 // Dasynq uses a "mix-in" pattern to produce an event loop implementation incorporating selectable implementations of
12 // various components (main backend, timers, child process watch mechanism etc). In C++ this can be achieved by
13 // a template for some component which extends its own type parameter:
14 //
15 //     template <typename Base> class X : public B { .... }
16 //
17 // We can chain several such components together (and so so below) to "mix in" the functionality of each into the final
18 // class, eg:
19 //
20 //     template <typename T> using Loop = epoll_loop<interrupt_channel<timer_fd_events<child_proc_events<T>>>>;
21 //
22 // (which defines an alias template "Loop", whose implementation will use the epoll backend, a standard interrupt channel
23 // implementation, a timerfd-based timer implementation, and the standard child process watch implementation).
24 // We sometimes need the base class to be able to call derived-class members: to do this we pass a reference to
25 // the derived instance into a templated method in the base, called "init":
26 //
27 //     template <typename T> void init(T *derived)
28 //     {
29 //         // can call method on derived:
30 //         derived->add_listener();
31 //         // chain to next class:
32 //         Base::init(derived);
33 //     }
34 //
35 // At the base all this is the event_dispatch class, defined below, which receives event notifications and inserts
36 // them into a queue for processing. The event_loop class, also below, wraps this (via composition) in an interface
37 // which can be used to register/de-regsiter/enable/disable event watchers, and which can process the queued events
38 // by calling the watcher callbacks. The event_loop class also provides some synchronisation to ensure thread-safety.
39
40 #if DASYNQ_HAVE_KQUEUE
41 #include "dasynq-kqueue.h"
42 #if _POSIX_TIMERS > 0
43 #include "dasynq-posixtimer.h"
44 namespace dasynq {
45     template <typename T> using timer_events = posix_timer_events<T>;
46 }
47 #else
48 #include "dasynq-itimer.h"
49 namespace dasynq {
50     template <typename T> using timer_events = itimer_events<T>;
51 }
52 #endif
53 #include "dasynq-childproc.h"
54 namespace dasynq {
55     template <typename T> using loop_t = kqueue_loop<interrupt_channel<timer_events<child_proc_events<T>>>>;
56     using loop_traits_t = kqueue_traits;
57 }
58 #elif DASYNQ_HAVE_EPOLL
59 #include "dasynq-epoll.h"
60 #include "dasynq-timerfd.h"
61 #include "dasynq-childproc.h"
62 namespace dasynq {
63     template <typename T> using loop_t = epoll_loop<interrupt_channel<timer_fd_events<child_proc_events<T>>>>;
64     using loop_traits_t = epoll_traits;
65 }
66 #else
67 #error No loop backened defined - see dasynq-config.h
68 #endif
69
70 #include <atomic>
71 #include <condition_variable>
72 #include <cstdint>
73 #include <cstddef>
74 #include <system_error>
75
76 #include <unistd.h>
77 #include <fcntl.h>
78
79 #include "dasynq-mutex.h"
80
81 #include "dasynq-basewatchers.h"
82
83 namespace dasynq {
84
85 /**
86  * Values for rearm/disarm return from event handlers
87  */
88 enum class rearm
89 {
90     /** Re-arm the event watcher so that it receives further events */
91     REARM,
92     /** Disarm the event watcher so that it receives no further events, until it is re-armed explicitly */
93     DISARM,
94     /** Leave in current armed/disarmed state */
95     NOOP,
96     /** Remove the event watcher (and call "removed" callback) */
97     REMOVE,
98     /** The watcher has been removed - don't touch it! */
99     REMOVED,
100     /** RE-queue the watcher to have its notification called again */
101     REQUEUE
102 };
103
104 namespace dprivate {
105
106     // Classes for implementing a fair(ish) wait queue.
107     // A queue node can be signalled when it reaches the head of
108     // the queue.
109
110     template <typename T_Mutex> class waitqueue;
111     template <typename T_Mutex> class waitqueue_node;
112
113     // Select an appropriate condition variable type for a mutex:
114     // condition_variable if mutex is std::mutex, or condition_variable_any
115     // otherwise.
116     template <class T_Mutex> class condvar_selector;
117
118     template <> class condvar_selector<std::mutex>
119     {
120         public:
121         typedef std::condition_variable condvar;
122     };
123
124     template <class T_Mutex> class condvar_selector
125     {
126         public:
127         typedef std::condition_variable_any condvar;
128     };
129
130     template <> class waitqueue_node<null_mutex>
131     {
132         // Specialised waitqueue_node for null_mutex.
133         friend class waitqueue<null_mutex>;
134         
135         public:
136         void wait(std::unique_lock<null_mutex> &ul) { }
137         void signal() { }
138         
139         DASYNQ_EMPTY_BODY;
140     };
141
142     template <typename T_Mutex> class waitqueue_node
143     {
144         typename condvar_selector<T_Mutex>::condvar condvar;
145         friend class waitqueue<T_Mutex>;
146
147         // ptr to next node in queue, set to null when added to queue tail:
148         waitqueue_node * next;
149         
150         public:
151         void signal()
152         {
153             condvar.notify_one();
154         }
155         
156         void wait(std::unique_lock<T_Mutex> &mutex_lock)
157         {
158             condvar.wait(mutex_lock);
159         }
160     };
161
162     template <> class waitqueue<null_mutex>
163     {
164         public:
165         // remove current head of queue, return new head:
166         waitqueue_node<null_mutex> * unqueue()
167         {
168             return nullptr;
169         }
170         
171         waitqueue_node<null_mutex> * get_head()
172         {
173             return nullptr;
174         }
175         
176         bool check_head(waitqueue_node<null_mutex> &node)
177         {
178             return true;
179         }
180         
181         bool is_empty()
182         {
183             return true;
184         }
185         
186         void queue(waitqueue_node<null_mutex> *node)
187         {
188         }
189     };
190
191     template <typename T_Mutex> class waitqueue
192     {
193         waitqueue_node<T_Mutex> * tail = nullptr;
194         waitqueue_node<T_Mutex> * head = nullptr;
195
196         public:
197         // remove current head of queue, return new head:
198         waitqueue_node<T_Mutex> * unqueue()
199         {
200             head = head->next;
201             if (head == nullptr) {
202                 tail = nullptr;
203             }
204             return head;
205         }
206         
207         waitqueue_node<T_Mutex> * get_head()
208         {
209             return head;
210         }
211         
212         bool check_head(waitqueue_node<T_Mutex> &node)
213         {
214             return head == &node;
215         }
216         
217         bool is_empty()
218         {
219             return head == nullptr;
220         }
221         
222         void queue(waitqueue_node<T_Mutex> *node)
223         {
224             node->next = nullptr;
225             if (tail) {
226                 tail->next = node;
227             }
228             else {
229                 head = node;
230             }
231             tail = node;
232         }
233     };
234     
235     // Do standard post-dispatch processing for a watcher. This handles the case of removing or
236     // re-queing watchers depending on the rearm type.
237     template <typename Loop> void post_dispatch(Loop &loop, base_watcher *watcher, rearm rearm_type)
238     {
239         if (rearm_type == rearm::REMOVE) {
240             loop.get_base_lock().unlock();
241             watcher->watch_removed();
242             loop.get_base_lock().lock();
243         }
244         else if (rearm_type == rearm::REQUEUE) {
245             loop.requeue_watcher(watcher);
246         }
247     }
248
249     // This class serves as the base class (mixin) for the AEN mechanism class.
250     //
251     // The event_dispatch class maintains the queued event data structures. It inserts watchers
252     // into the queue when events are received (receiveXXX methods). It also owns a mutex used
253     // to protect those structures.
254     //
255     // In general the methods should be called with lock held. In practice this means that the
256     // event loop backend implementations must obtain the lock; they are also free to use it to
257     // protect their own internal data structures.
258     template <typename T_Mutex, typename Traits, typename LoopTraits> class event_dispatch
259     {
260         friend class dasynq::event_loop<T_Mutex, LoopTraits>;;
261
262         public:
263         using mutex_t = T_Mutex;
264         using traits_t = Traits;
265
266         private:
267
268         // queue data structure/pointer
269         prio_queue event_queue;
270         
271         using base_signal_watcher = dasynq::dprivate::base_signal_watcher<mutex_t, typename traits_t::sigdata_t>;
272         using base_fd_watcher = dasynq::dprivate::base_fd_watcher<mutex_t>;
273         using base_bidi_fd_watcher = dasynq::dprivate::base_bidi_fd_watcher<mutex_t>;
274         using base_child_watcher = dasynq::dprivate::base_child_watcher<mutex_t>;
275         using base_timer_watcher = dasynq::dprivate::base_timer_watcher<mutex_t>;
276         
277         // Add a watcher into the queueing system (but don't queue it). Call with lock held.
278         //   may throw: std::bad_alloc
279         void prepare_watcher(base_watcher *bwatcher)
280         {
281             event_queue.allocate(bwatcher->heap_handle, bwatcher);
282         }
283         
284         void queue_watcher(base_watcher *bwatcher) noexcept
285         {
286             event_queue.insert(bwatcher->heap_handle, bwatcher->priority);
287         }
288         
289         void dequeue_watcher(base_watcher *bwatcher) noexcept
290         {
291             if (event_queue.is_queued(bwatcher->heap_handle)) {
292                 event_queue.remove(bwatcher->heap_handle);
293             }
294         }
295
296         // Remove watcher from the queueing system
297         void release_watcher(base_watcher *bwatcher) noexcept
298         {
299             event_queue.deallocate(bwatcher->heap_handle);
300         }
301         
302         protected:
303         mutex_t lock;
304
305         template <typename T> void init(T *loop) noexcept { }
306         
307         void sigmaskf(int how, const sigset_t *set, sigset_t *oset)
308         {
309             LoopTraits::sigmaskf(how, set, oset);
310         }
311
312         // Receive a signal; return true to disable signal watch or false to leave enabled.
313         // Called with lock held.
314         template <typename T>
315         bool receive_signal(T &loop_mech, typename Traits::sigdata_t & siginfo, void * userdata) noexcept
316         {
317             base_signal_watcher * bwatcher = static_cast<base_signal_watcher *>(userdata);
318             bwatcher->siginfo = siginfo;
319             queue_watcher(bwatcher);
320             return true;
321         }
322         
323         template <typename T>
324         void receive_fd_event(T &loop_mech, typename Traits::fd_r fd_r, void * userdata, int flags) noexcept
325         {
326             base_fd_watcher * bfdw = static_cast<base_fd_watcher *>(userdata);
327             
328             bfdw->event_flags |= flags;
329             
330             base_watcher * bwatcher = bfdw;
331             
332             bool is_multi_watch = bfdw->watch_flags & multi_watch;
333             if (is_multi_watch) {                
334                 base_bidi_fd_watcher *bbdw = static_cast<base_bidi_fd_watcher *>(bwatcher);
335                 bbdw->watch_flags &= ~flags;
336                 if ((flags & IN_EVENTS) && (flags & OUT_EVENTS)) {
337                     // Queue the secondary watcher first:
338                     queue_watcher(&bbdw->out_watcher);
339                 }
340                 else if (flags & OUT_EVENTS) {                
341                     // Use the secondary watcher for queueing:
342                     bwatcher = &(bbdw->out_watcher);
343                 }
344             }
345
346             queue_watcher(bwatcher);
347             
348             if (! traits_t::has_separate_rw_fd_watches) {
349                 // If this is a bidirectional fd-watch, it has been disabled in *both* directions
350                 // as the event was delivered. However, the other direction should not be disabled
351                 // yet, so we need to re-enable:
352                 int in_out_mask = IN_EVENTS | OUT_EVENTS;
353                 if (is_multi_watch && (bfdw->watch_flags & in_out_mask) != 0) {
354                     // We need to re-enable the other channel now:
355                     loop_mech.enable_fd_watch_nolock(bfdw->watch_fd, userdata,
356                         (bfdw->watch_flags & in_out_mask) | ONE_SHOT);
357                 }
358             }
359         }
360         
361         // Child process terminated. Called with both the main lock and the reaper lock held.
362         void receive_child_stat(pid_t child, int status, void * userdata) noexcept
363         {
364             base_child_watcher * watcher = static_cast<base_child_watcher *>(userdata);
365             watcher->child_status = status;
366             watcher->child_termd = true;
367             queue_watcher(watcher);
368         }
369         
370         void receive_timer_expiry(timer_handle_t & timer_handle, void * userdata, int intervals) noexcept
371         {
372             base_timer_watcher * watcher = static_cast<base_timer_watcher *>(userdata);
373             watcher->intervals += intervals;
374             queue_watcher(watcher);
375         }
376         
377         // Pull a single event from the queue; returns nullptr if the queue is empty.
378         // Call with lock held.
379         base_watcher * pull_event() noexcept
380         {
381             if (event_queue.empty()) {
382                 return nullptr;
383             }
384             
385             auto & rhndl = event_queue.get_root();
386             base_watcher *r = event_queue.node_data(rhndl);
387             event_queue.pull_root();
388             return r;
389         }
390         
391         // Queue a watcher for reomval, or issue "removed" callback to it.
392         // Call with lock free.
393         void issue_delete(base_watcher *watcher) noexcept
394         {
395             // This is only called when the attention lock is held, so if the watcher is not
396             // active/queued now, it cannot become active (and will not be reported with an event)
397             // during execution of this function.
398             
399             lock.lock();
400             
401             if (watcher->active) {
402                 // If the watcher is active, set deleteme true; the watcher will be removed
403                 // at the end of current processing (i.e. when active is set false).
404                 watcher->deleteme = true;
405                 release_watcher(watcher);
406                 lock.unlock();
407             }
408             else {
409                 // Actually do the delete.
410                 dequeue_watcher(watcher);
411                 release_watcher(watcher);
412                 
413                 lock.unlock();
414                 watcher->watch_removed();
415             }
416         }
417         
418         // Queue a watcher for reomval, or issue "removed" callback to it.
419         // Call with lock free.
420         void issue_delete(base_bidi_fd_watcher *watcher) noexcept
421         {
422             lock.lock();
423             
424             if (watcher->active) {
425                 watcher->deleteme = true;
426                 release_watcher(watcher);
427             }
428             else {
429                 dequeue_watcher(watcher);
430                 release_watcher(watcher);
431                 watcher->read_removed = true;
432             }
433             
434             base_watcher *secondary = &(watcher->out_watcher);
435             if (secondary->active) {
436                 secondary->deleteme = true;
437                 release_watcher(watcher);
438             }
439             else {
440                 dequeue_watcher(secondary);
441                 release_watcher(watcher);
442                 watcher->write_removed = true;
443             }
444             
445             if (watcher->read_removed && watcher->write_removed) {
446                 lock.unlock();
447                 watcher->watch_removed();
448             }
449             else {
450                 lock.unlock();
451             }
452         }
453
454         event_dispatch() {  }
455         event_dispatch(const event_dispatch &) = delete;
456     };
457 }
458
459 // This is the main event_loop implementation. It serves as an interface to the event loop
460 // backend (of which it maintains an internal instance). It also serialises waits the backend
461 // and provides safe deletion of watchers (see comments inline).
462 template <typename T_Mutex, typename Traits>
463 class event_loop
464 {
465     using my_event_loop_t = event_loop<T_Mutex, Traits>;
466     friend class dprivate::fd_watcher<my_event_loop_t>;
467     friend class dprivate::bidi_fd_watcher<my_event_loop_t>;
468     friend class dprivate::signal_watcher<my_event_loop_t>;
469     friend class dprivate::child_proc_watcher<my_event_loop_t>;
470     friend class dprivate::timer<my_event_loop_t>;
471     
472     friend void dprivate::post_dispatch<my_event_loop_t>(my_event_loop_t &loop,
473             dprivate::base_watcher *watcher, rearm rearm_type);
474
475     template <typename, typename> friend class dprivate::fd_watcher_impl;
476     template <typename, typename> friend class dprivate::bidi_fd_watcher_impl;
477     template <typename, typename> friend class dprivate::signal_watcher_impl;
478     template <typename, typename> friend class dprivate::child_proc_watcher_impl;
479     template <typename, typename> friend class dprivate::timer_impl;
480
481     using backend_traits_t = typename Traits::backend_traits_t;
482
483     template <typename T, typename U> using event_dispatch = dprivate::event_dispatch<T,U,Traits>;
484     using loop_mech_t = typename Traits::template backend_t<event_dispatch<T_Mutex, backend_traits_t>>;
485     using reaper_mutex_t = typename loop_mech_t::reaper_mutex_t;
486
487     public:
488     using traits_t = Traits;
489     using loop_traits_t = typename loop_mech_t::traits_t;
490     using mutex_t = T_Mutex;
491     
492     private:
493     template <typename T> using waitqueue = dprivate::waitqueue<T>;
494     template <typename T> using waitqueue_node = dprivate::waitqueue_node<T>;
495     using base_watcher = dprivate::base_watcher;
496     using base_signal_watcher = dprivate::base_signal_watcher<T_Mutex, typename loop_traits_t::sigdata_t>;
497     using base_fd_watcher = dprivate::base_fd_watcher<T_Mutex>;
498     using base_bidi_fd_watcher = dprivate::base_bidi_fd_watcher<T_Mutex>;
499     using base_child_watcher = dprivate::base_child_watcher<T_Mutex>;
500     using base_timer_watcher = dprivate::base_timer_watcher<T_Mutex>;
501     using watch_type_t = dprivate::watch_type_t;
502
503     loop_mech_t loop_mech;
504
505     // There is a complex problem with most asynchronous event notification mechanisms
506     // when used in a multi-threaded environment. Generally, a file descriptor or other
507     // event type that we are watching will be associated with some data used to manage
508     // that event source. For example a web server needs to maintain information about
509     // each client connection, such as the state of the connection (what protocol version
510     // has been negotiated, etc; if a transfer is taking place, what file is being
511     // transferred etc).
512     //
513     // However, sometimes we want to remove an event source (eg webserver wants to drop
514     // a connection) and delete the associated data. The problem here is that it is
515     // difficult to be sure when it is ok to actually remove the data, since when
516     // requesting to unwatch the source in one thread it is still possible that an
517     // event from that source is just being reported to another thread (in which case
518     // the data will be needed).
519     //
520     // To solve that, we:
521     // - allow only one thread to poll for events at a time, using a lock
522     // - use the same lock to prevent polling, if we want to unwatch an event source
523     // - generate an event to interrupt any polling that may already be occurring in
524     //   another thread
525     // - mark handlers as active if they are currently executing, and
526     // - when removing an active handler, simply set a flag which causes it to be
527     //   removed once the current processing is finished, rather than removing it
528     //   immediately.
529     //
530     // In particular the lock mechanism for preventing multiple threads polling and
531     // for allowing polling to be interrupted is tricky. We can't use a simple mutex
532     // since there is significant chance that it will be highly contended and there
533     // are no guarantees that its acquisition will be fair. In particular, we don't
534     // want a thread that is trying to unwatch a source being starved while another
535     // thread polls the event source.
536     //
537     // So, we use two wait queues protected by a single mutex. The "attn_waitqueue"
538     // (attention queue) is the high-priority queue, used for threads wanting to
539     // unwatch event sources. The "wait_waitquueue" is the queue used by threads
540     // that wish to actually poll for events.
541     // - The head of the "attn_waitqueue" is always the holder of the lock
542     // - Therefore, a poll-waiter must be moved from the wait_waitqueue to the
543     //   attn_waitqueue to actually gain the lock. This is only done if the
544     //   attn_waitqueue is otherwise empty.
545     // - The mutex only protects manipulation of the wait queues, and so should not
546     //   be highly contended.
547     
548     mutex_t wait_lock;  // wait lock, used to prevent multiple threads from waiting
549                         // on the event queue simultaneously.
550     waitqueue<mutex_t> attn_waitqueue;
551     waitqueue<mutex_t> wait_waitqueue;
552     
553     mutex_t &get_base_lock() noexcept
554     {
555         return loop_mech.lock;
556     }
557     
558     reaper_mutex_t &get_reaper_lock() noexcept
559     {
560         return loop_mech.get_reaper_lock();
561     }
562
563     void register_signal(base_signal_watcher *callBack, int signo)
564     {
565         auto & ed = (event_dispatch<T_Mutex, backend_traits_t> &) loop_mech;
566         std::lock_guard<mutex_t> guard(ed.lock);
567
568         loop_mech.prepare_watcher(callBack);
569         try {
570             loop_mech.add_signal_watch_nolock(signo, callBack);
571         }
572         catch (...) {
573             loop_mech.release_watcher(callBack);
574             throw;
575         }
576     }
577     
578     void deregister(base_signal_watcher *callBack, int signo) noexcept
579     {
580         loop_mech.remove_signal_watch(signo);
581         
582         waitqueue_node<T_Mutex> qnode;
583         get_attn_lock(qnode);
584         
585         auto & ed = (event_dispatch<T_Mutex, backend_traits_t> &) loop_mech;
586         ed.issue_delete(callBack);
587         
588         release_lock(qnode);
589     }
590
591     void register_fd(base_fd_watcher *callback, int fd, int eventmask, bool enabled, bool emulate = false)
592     {
593         auto & ed = (event_dispatch<T_Mutex, backend_traits_t> &) loop_mech;
594         std::lock_guard<mutex_t> guard(ed.lock);
595
596         loop_mech.prepare_watcher(callback);
597
598         try {
599             if (! loop_mech.add_fd_watch(fd, callback, eventmask | ONE_SHOT, enabled, emulate)) {
600                 callback->emulatefd = true;
601                 callback->emulate_enabled = enabled;
602                 if (enabled) {
603                     callback->event_flags = eventmask & IO_EVENTS;
604                     if (eventmask & IO_EVENTS) {
605                         requeue_watcher(callback);
606                     }
607                 }
608             }
609         }
610         catch (...) {
611             loop_mech.release_watcher(callback);
612             throw;
613         }
614     }
615     
616     void register_fd(base_bidi_fd_watcher *callback, int fd, int eventmask, bool emulate = false)
617     {
618         auto & ed = (event_dispatch<T_Mutex, backend_traits_t> &) loop_mech;
619         std::lock_guard<mutex_t> guard(ed.lock);
620
621         loop_mech.prepare_watcher(callback);
622         try {
623             loop_mech.prepare_watcher(&callback->out_watcher);
624             try {
625                 if (backend_traits_t::has_separate_rw_fd_watches) {
626                     int r = loop_mech.add_bidi_fd_watch(fd, callback, eventmask | ONE_SHOT, emulate);
627                     if (r & IN_EVENTS) {
628                         callback->emulatefd = true;
629                         if (eventmask & IN_EVENTS) {
630                             requeue_watcher(callback);
631                         }
632                     }
633                     if (r & OUT_EVENTS) {
634                         callback->out_watcher.emulatefd = true;
635                         if (eventmask & OUT_EVENTS) {
636                             requeue_watcher(&callback->out_watcher);
637                         }
638                     }
639                 }
640                 else {
641                     if (! loop_mech.add_fd_watch(fd, callback, eventmask | ONE_SHOT, true, emulate)) {
642                         callback->emulatefd = true;
643                         callback->out_watcher.emulatefd = true;
644                         if (eventmask & IN_EVENTS) {
645                             requeue_watcher(callback);
646                         }
647                         if (eventmask & OUT_EVENTS) {
648                             requeue_watcher(&callback->out_watcher);
649                         }
650                     }
651                 }
652             }
653             catch (...) {
654                 loop_mech.release_watcher(&callback->out_watcher);
655                 throw;
656             }
657         }
658         catch (...) {
659             loop_mech.release_watcher(callback);
660             throw;
661         }
662     }
663     
664     void set_fd_enabled(base_watcher *watcher, int fd, int watch_flags, bool enabled) noexcept
665     {
666         if (enabled) {
667             loop_mech.enable_fd_watch(fd, watcher, watch_flags | ONE_SHOT);
668         }
669         else {
670             loop_mech.disable_fd_watch(fd, watch_flags);
671         }
672     }
673
674     void set_fd_enabled_nolock(base_watcher *watcher, int fd, int watch_flags, bool enabled) noexcept
675     {
676         if (enabled) {
677             loop_mech.enable_fd_watch_nolock(fd, watcher, watch_flags | ONE_SHOT);
678         }
679         else {
680             loop_mech.disable_fd_watch_nolock(fd, watch_flags);
681         }
682     }
683     
684     void deregister(base_fd_watcher *callback, int fd) noexcept
685     {
686         if (callback->emulatefd) {
687             auto & ed = (event_dispatch<T_Mutex, backend_traits_t> &) loop_mech;
688             ed.issue_delete(callback);
689             return;
690         }
691         
692         loop_mech.remove_fd_watch(fd, callback->watch_flags);
693
694         waitqueue_node<T_Mutex> qnode;
695         get_attn_lock(qnode);
696         
697         auto & ed = (event_dispatch<T_Mutex, backend_traits_t> &) loop_mech;
698         ed.issue_delete(callback);
699         
700         release_lock(qnode);        
701     }
702     
703     void deregister(base_bidi_fd_watcher *callback, int fd) noexcept
704     {
705         if (backend_traits_t::has_separate_rw_fd_watches) {
706             loop_mech.remove_bidi_fd_watch(fd);
707         }
708         else {
709             loop_mech.remove_fd_watch(fd, callback->watch_flags);
710         }
711         
712         waitqueue_node<T_Mutex> qnode;
713         get_attn_lock(qnode);
714         
715         event_dispatch<T_Mutex, backend_traits_t> & ed = (event_dispatch<T_Mutex, backend_traits_t> &) loop_mech;
716         ed.issue_delete(callback);
717         
718         release_lock(qnode);
719     }
720     
721     void reserve_child_watch(base_child_watcher *callback)
722     {
723         auto & ed = (event_dispatch<T_Mutex, backend_traits_t> &) loop_mech;
724         std::lock_guard<mutex_t> guard(ed.lock);
725
726         loop_mech.prepare_watcher(callback);
727         try {
728             loop_mech.reserve_child_watch_nolock(callback->watch_handle);
729         }
730         catch (...) {
731             loop_mech.release_watcher(callback);
732             throw;
733         }
734     }
735     
736     void unreserve(base_child_watcher *callback) noexcept
737     {
738         auto & ed = (event_dispatch<T_Mutex, backend_traits_t> &) loop_mech;
739         std::lock_guard<mutex_t> guard(ed.lock);
740
741         loop_mech.unreserve_child_watch(callback->watch_handle);
742         loop_mech.release_watcher(callback);
743     }
744     
745     void register_child(base_child_watcher *callback, pid_t child)
746     {
747         auto & ed = (event_dispatch<T_Mutex, backend_traits_t> &) loop_mech;
748         std::lock_guard<mutex_t> guard(ed.lock);
749         
750         loop_mech.prepare_watcher(callback);
751         try {
752             loop_mech.add_child_watch_nolock(callback->watch_handle, child, callback);
753         }
754         catch (...) {
755             loop_mech.release_watcher(callback);
756             throw;
757         }
758     }
759     
760     void register_reserved_child(base_child_watcher *callback, pid_t child) noexcept
761     {
762         loop_mech.add_reserved_child_watch(callback->watch_handle, child, callback);
763     }
764
765     void register_reserved_child_nolock(base_child_watcher *callback, pid_t child) noexcept
766     {
767         loop_mech.add_reserved_child_watch_nolock(callback->watch_handle, child, callback);
768     }
769     
770     void deregister(base_child_watcher *callback, pid_t child) noexcept
771     {
772         loop_mech.remove_child_watch(callback->watch_handle);
773
774         waitqueue_node<T_Mutex> qnode;
775         get_attn_lock(qnode);
776         
777         event_dispatch<T_Mutex, backend_traits_t> & ed = (event_dispatch<T_Mutex, backend_traits_t> &) loop_mech;
778         ed.issue_delete(callback);
779         
780         release_lock(qnode);
781     }
782     
783     // Stop watching a child process, but retain watch reservation so that another child can be
784     // watched without running into resource allocation issues.
785     void stop_watch(base_child_watcher *callback) noexcept
786     {
787         loop_mech.stop_child_watch(callback->watch_handle);
788     }
789
790     void register_timer(base_timer_watcher *callback, clock_type clock)
791     {
792         auto & ed = (event_dispatch<T_Mutex, backend_traits_t> &) loop_mech;
793         std::lock_guard<mutex_t> guard(ed.lock);
794     
795         loop_mech.prepare_watcher(callback);
796         try {
797             loop_mech.add_timer_nolock(callback->timer_handle, callback, clock);
798         }
799         catch (...) {
800             loop_mech.release_watcher(callback);
801         }
802     }
803     
804     void set_timer(base_timer_watcher *callBack, const timespec &timeout, clock_type clock) noexcept
805     {
806         struct timespec interval {0, 0};
807         loop_mech.set_timer(callBack->timer_handle, timeout, interval, true, clock);
808     }
809     
810     void set_timer(base_timer_watcher *callBack, const timespec &timeout, const timespec &interval,
811             clock_type clock) noexcept
812     {
813         loop_mech.set_timer(callBack->timer_handle, timeout, interval, true, clock);
814     }
815
816     void set_timer_rel(base_timer_watcher *callBack, const timespec &timeout, clock_type clock) noexcept
817     {
818         struct timespec interval {0, 0};
819         loop_mech.set_timer_rel(callBack->timer_handle, timeout, interval, true, clock);
820     }
821     
822     void set_timer_rel(base_timer_watcher *callBack, const timespec &timeout,
823             const timespec &interval, clock_type clock) noexcept
824     {
825         loop_mech.set_timer_rel(callBack->timer_handle, timeout, interval, true, clock);
826     }
827
828     void set_timer_enabled(base_timer_watcher *callback, clock_type clock, bool enabled) noexcept
829     {
830         loop_mech.enable_timer(callback->timer_handle, enabled, clock);
831     }
832
833     void set_timer_enabled_nolock(base_timer_watcher *callback, clock_type clock, bool enabled) noexcept
834     {
835         loop_mech.enable_timer_nolock(callback->timer_handle, enabled, clock);
836     }
837
838     void stop_timer(base_timer_watcher *callback, clock_type clock) noexcept
839     {
840         loop_mech.stop_timer(callback->timer_handle, clock);
841     }
842
843     void deregister(base_timer_watcher *callback, clock_type clock) noexcept
844     {
845         loop_mech.remove_timer(callback->timer_handle, clock);
846         
847         waitqueue_node<T_Mutex> qnode;
848         get_attn_lock(qnode);
849         
850         event_dispatch<T_Mutex, backend_traits_t> & ed = (event_dispatch<T_Mutex, backend_traits_t> &) loop_mech;
851         ed.issue_delete(callback);
852         
853         release_lock(qnode);
854     }
855     
856     void dequeue_watcher(base_watcher *watcher) noexcept
857     {
858         loop_mech.dequeue_watcher(watcher);
859     }
860
861     void requeue_watcher(base_watcher *watcher) noexcept
862     {
863         loop_mech.queue_watcher(watcher);
864
865         // We need to signal any thread that is currently waiting on the loop mechanism, so that it wakes
866         // and processes the newly queued watcher:
867
868         wait_lock.lock();
869         bool attn_q_empty = attn_waitqueue.is_empty();
870         wait_lock.unlock();
871
872         if (! attn_q_empty) {
873             loop_mech.interrupt_wait();
874         }
875     }
876
877     // Acquire the attention lock (when held, ensures that no thread is polling the AEN
878     // mechanism). This can be used to safely remove watches, since it is certain that
879     // notification callbacks won't be run while the attention lock is held.
880     void get_attn_lock(waitqueue_node<T_Mutex> &qnode) noexcept
881     {
882         std::unique_lock<T_Mutex> ulock(wait_lock);
883         attn_waitqueue.queue(&qnode);        
884         if (! attn_waitqueue.check_head(qnode)) {
885             loop_mech.interrupt_wait();
886             while (! attn_waitqueue.check_head(qnode)) {
887                 qnode.wait(ulock);
888             }
889         }
890     }
891     
892     // Acquire the poll-wait lock (to be held when polling the AEN mechanism; lower priority than
893     // the attention lock). The poll-wait lock is used to prevent more than a single thread from
894     // polling the event loop mechanism at a time; if this is not done, it is basically
895     // impossible to safely deregister watches.
896     void get_pollwait_lock(waitqueue_node<T_Mutex> &qnode) noexcept
897     {
898         std::unique_lock<T_Mutex> ulock(wait_lock);
899         if (attn_waitqueue.is_empty()) {
900             // Queue is completely empty:
901             attn_waitqueue.queue(&qnode);
902         }
903         else {
904             wait_waitqueue.queue(&qnode);
905         }
906         
907         while (! attn_waitqueue.check_head(qnode)) {
908             qnode.wait(ulock);
909         }    
910     }
911     
912     // Release the poll-wait/attention lock.
913     void release_lock(waitqueue_node<T_Mutex> &qnode) noexcept
914     {
915         std::unique_lock<T_Mutex> ulock(wait_lock);
916         waitqueue_node<T_Mutex> * nhead = attn_waitqueue.unqueue();
917         if (nhead != nullptr) {
918             nhead->signal();
919         }
920         else {
921             if (! wait_waitqueue.is_empty()) {
922                 auto nhead = wait_waitqueue.get_head();
923                 wait_waitqueue.unqueue();
924                 attn_waitqueue.queue(nhead);
925                 nhead->signal();
926             }
927         }                
928     }
929     
930     void process_signal_rearm(base_signal_watcher * bsw, rearm rearm_type) noexcept
931     {
932         // Called with lock held
933         if (rearm_type == rearm::REARM) {
934             loop_mech.rearm_signal_watch_nolock(bsw->siginfo.get_signo(), bsw);
935         }
936         else if (rearm_type == rearm::REMOVE) {
937             loop_mech.remove_signal_watch_nolock(bsw->siginfo.get_signo());
938         }
939         // Note that signal watchers cannot (currently) be disarmed
940     }
941
942     // Process rearm return for fd_watcher, including the primary watcher of a bidi_fd_watcher
943     rearm process_fd_rearm(base_fd_watcher * bfw, rearm rearm_type, bool is_multi_watch) noexcept
944     {
945         bool emulatedfd = static_cast<base_watcher *>(bfw)->emulatefd;
946
947         // Called with lock held
948         if (is_multi_watch) {
949             base_bidi_fd_watcher * bdfw = static_cast<base_bidi_fd_watcher *>(bfw);
950
951             if (rearm_type == rearm::REMOVE) {
952                 bdfw->read_removed = 1;
953                 
954                 if (backend_traits_t::has_separate_rw_fd_watches) {
955                     bdfw->watch_flags &= ~IN_EVENTS;
956                     if (! emulatedfd) {
957                         loop_mech.remove_fd_watch_nolock(bdfw->watch_fd, IN_EVENTS);
958                     }
959                     return bdfw->write_removed ? rearm::REMOVE : rearm::NOOP;
960                 }
961                 else {
962                     if (! bdfw->write_removed) {
963                         if (bdfw->watch_flags & IN_EVENTS) {
964                             bdfw->watch_flags &= ~IN_EVENTS;
965                             if (! emulatedfd) {
966                                 loop_mech.enable_fd_watch_nolock(bdfw->watch_fd, bdfw, bdfw->watch_flags);
967                             }
968                         }
969                         return rearm::NOOP;
970                     }
971                     else {
972                         // both removed: actually remove
973                         if (! emulatedfd) {
974                             loop_mech.remove_fd_watch_nolock(bdfw->watch_fd, 0 /* not used */);
975                         }
976                         return rearm::REMOVE;
977                     }
978                 }
979             }
980             else if (rearm_type == rearm::DISARM) {
981                 bdfw->watch_flags &= ~IN_EVENTS;
982
983                 if (! emulatedfd) {
984                     if (! backend_traits_t::has_separate_rw_fd_watches) {
985                         int watch_flags = bdfw->watch_flags;
986                         // without separate r/w watches, enable_fd_watch actually sets
987                         // which sides are enabled (i.e. can be used to disable):
988                         loop_mech.enable_fd_watch_nolock(bdfw->watch_fd,
989                                 static_cast<base_watcher *>(bdfw),
990                                 (watch_flags & (IN_EVENTS | OUT_EVENTS)) | ONE_SHOT);
991                     }
992                     else {
993                         loop_mech.disable_fd_watch_nolock(bdfw->watch_fd, IN_EVENTS);
994                     }
995                 }
996             }
997             else if (rearm_type == rearm::REARM) {
998                 bdfw->watch_flags |= IN_EVENTS;
999                 
1000                 if (! emulatedfd) {
1001                     if (! backend_traits_t::has_separate_rw_fd_watches) {
1002                         int watch_flags = bdfw->watch_flags;
1003                         loop_mech.enable_fd_watch_nolock(bdfw->watch_fd,
1004                                 static_cast<base_watcher *>(bdfw),
1005                                 (watch_flags & (IN_EVENTS | OUT_EVENTS)) | ONE_SHOT);
1006                     }
1007                     else {
1008                         loop_mech.enable_fd_watch_nolock(bdfw->watch_fd,
1009                                 static_cast<base_watcher *>(bdfw),
1010                                 IN_EVENTS | ONE_SHOT);
1011                     }
1012                 }
1013                 else {
1014                     rearm_type = rearm::REQUEUE;
1015                 }
1016             }
1017             else if (rearm_type == rearm::NOOP) {
1018                 if (bdfw->emulatefd) {
1019                     if (bdfw->watch_flags & IN_EVENTS) {
1020                         rearm_type = rearm::REQUEUE;
1021                     }
1022                 }
1023             }
1024             return rearm_type;
1025         }
1026         else { // Not multi-watch:
1027             if (emulatedfd) {
1028                 if (rearm_type == rearm::REARM) {
1029                     bfw->emulate_enabled = true;
1030                     rearm_type = rearm::REQUEUE;
1031                 }
1032                 else if (rearm_type == rearm::DISARM) {
1033                     bfw->emulate_enabled = false;
1034                 }
1035                 else if (rearm_type == rearm::NOOP) {
1036                     if (bfw->emulate_enabled) {
1037                         rearm_type = rearm::REQUEUE;
1038                     }
1039                 }
1040             }
1041             else  if (rearm_type == rearm::REARM) {
1042                 loop_mech.enable_fd_watch_nolock(bfw->watch_fd, bfw,
1043                         (bfw->watch_flags & (IN_EVENTS | OUT_EVENTS)) | ONE_SHOT);
1044             }
1045             else if (rearm_type == rearm::DISARM) {
1046                 loop_mech.disable_fd_watch_nolock(bfw->watch_fd, bfw->watch_flags);
1047             }
1048             else if (rearm_type == rearm::REMOVE) {
1049                 loop_mech.remove_fd_watch_nolock(bfw->watch_fd, bfw->watch_flags);
1050             }
1051             return rearm_type;
1052         }
1053     }
1054
1055     // Process re-arm for the secondary (output) watcher in a Bi-direction Fd watcher.
1056     rearm process_secondary_rearm(base_bidi_fd_watcher * bdfw, base_watcher * outw, rearm rearm_type) noexcept
1057     {
1058         bool emulatedfd = outw->emulatefd;
1059
1060         // Called with lock held
1061         if (emulatedfd) {
1062             if (rearm_type == rearm::REMOVE) {
1063                 bdfw->write_removed = 1;
1064                 bdfw->watch_flags &= ~OUT_EVENTS;
1065                 rearm_type = bdfw->read_removed ? rearm::REMOVE : rearm::NOOP;
1066             }
1067             else if (rearm_type == rearm::DISARM) {
1068                 bdfw->watch_flags &= ~OUT_EVENTS;
1069             }
1070             else if (rearm_type == rearm::REARM) {
1071                 bdfw->watch_flags |= OUT_EVENTS;
1072                 rearm_type = rearm::REQUEUE;
1073             }
1074             else if (rearm_type == rearm::NOOP) {
1075                 if (bdfw->watch_flags & OUT_EVENTS) {
1076                     rearm_type = rearm::REQUEUE;
1077                 }
1078             }
1079             return rearm_type;
1080         }
1081         else if (rearm_type == rearm::REMOVE) {
1082             bdfw->write_removed = 1;
1083
1084             if (backend_traits_t::has_separate_rw_fd_watches) {
1085                 bdfw->watch_flags &= ~OUT_EVENTS;
1086                 loop_mech.remove_fd_watch_nolock(bdfw->watch_fd, OUT_EVENTS);
1087                 return bdfw->read_removed ? rearm::REMOVE : rearm::NOOP;
1088             }
1089             else {
1090                 if (! bdfw->read_removed) {
1091                     if (bdfw->watch_flags & OUT_EVENTS) {
1092                         bdfw->watch_flags &= ~OUT_EVENTS;
1093                         loop_mech.enable_fd_watch_nolock(bdfw->watch_fd, bdfw, bdfw->watch_flags);
1094                     }
1095                     return rearm::NOOP;
1096                 }
1097                 else {
1098                     // both removed: actually remove
1099                     loop_mech.remove_fd_watch_nolock(bdfw->watch_fd, 0 /* not used */);
1100                     return rearm::REMOVE;
1101                 }
1102             }
1103         }
1104         else if (rearm_type == rearm::DISARM) {
1105             bdfw->watch_flags &= ~OUT_EVENTS;
1106
1107             if (! backend_traits_t::has_separate_rw_fd_watches) {
1108                 int watch_flags = bdfw->watch_flags;
1109                 loop_mech.enable_fd_watch_nolock(bdfw->watch_fd,
1110                         static_cast<base_watcher *>(bdfw),
1111                         (watch_flags & (IN_EVENTS | OUT_EVENTS)) | ONE_SHOT);
1112             }
1113             else {
1114                 loop_mech.disable_fd_watch_nolock(bdfw->watch_fd, OUT_EVENTS);
1115             }
1116         }
1117         else if (rearm_type == rearm::REARM) {
1118             bdfw->watch_flags |= OUT_EVENTS;
1119             
1120             if (! backend_traits_t::has_separate_rw_fd_watches) {
1121                 int watch_flags = bdfw->watch_flags;
1122                 loop_mech.enable_fd_watch_nolock(bdfw->watch_fd,
1123                         static_cast<base_watcher *>(bdfw),
1124                         (watch_flags & (IN_EVENTS | OUT_EVENTS)) | ONE_SHOT);
1125             }
1126             else {
1127                 loop_mech.enable_fd_watch_nolock(bdfw->watch_fd,
1128                         static_cast<base_watcher *>(bdfw),
1129                         OUT_EVENTS | ONE_SHOT);
1130             }
1131         }
1132         return rearm_type;
1133     }
1134     
1135     void process_child_watch_rearm(base_child_watcher *bcw, rearm rearm_type) noexcept
1136     {
1137         if (rearm_type == rearm::REMOVE || rearm_type == rearm::DISARM) {
1138             loop_mech.unreserve_child_watch_nolock(bcw->watch_handle);
1139         }
1140     }
1141
1142     void process_timer_rearm(base_timer_watcher *btw, rearm rearm_type) noexcept
1143     {
1144         // Called with lock held
1145         if (rearm_type == rearm::REARM) {
1146             loop_mech.enable_timer_nolock(btw->timer_handle, true, btw->clock);
1147         }
1148         else if (rearm_type == rearm::REMOVE) {
1149             loop_mech.remove_timer_nolock(btw->timer_handle, btw->clock);
1150         }
1151         else if (rearm_type == rearm::DISARM) {
1152             loop_mech.enable_timer_nolock(btw->timer_handle, false, btw->clock);
1153         }
1154     }
1155
1156     // Process queued events; returns true if any events were processed.
1157     //   limit - maximum number of events to process before returning; -1 for
1158     //           no limit.
1159     bool process_events(int limit) noexcept
1160     {
1161         auto & ed = (event_dispatch<T_Mutex, backend_traits_t> &) loop_mech;
1162         ed.lock.lock();
1163         
1164         if (limit == 0) {
1165             return false;
1166         }
1167         
1168         base_watcher * pqueue = ed.pull_event();
1169         bool active = false;
1170         
1171         while (pqueue != nullptr) {
1172         
1173             pqueue->active = true;
1174             active = true;
1175             
1176             base_bidi_fd_watcher *bbfw = nullptr;
1177             
1178             // (Above variables are initialised only to silence compiler warnings).
1179             
1180             if (pqueue->watchType == watch_type_t::SECONDARYFD) {
1181                 // construct a pointer to the main watcher (using char* arithmetic):
1182                 char * rp = (char *)pqueue;
1183
1184                 // Here we take the offset of a member from a non-standard-layout class, which is
1185                 // specified to have undefined result by the C++ language standard, but which
1186                 // in practice works fine:
1187                 _Pragma ("GCC diagnostic push")
1188                 _Pragma ("GCC diagnostic ignored \"-Winvalid-offsetof\"")
1189                 rp -= offsetof(base_bidi_fd_watcher, out_watcher);
1190                 _Pragma ("GCC diagnostic pop")
1191                 bbfw = (base_bidi_fd_watcher *)rp;
1192
1193                 // issue a secondary dispatch:
1194                 bbfw->dispatch_second(this);
1195                 pqueue = ed.pull_event();
1196                 continue;
1197             }
1198
1199             pqueue->dispatch(this);
1200             if (limit > 0) {
1201                 limit--;
1202                 if (limit == 0) break;
1203             }
1204             pqueue = ed.pull_event();
1205         }
1206         
1207         ed.lock.unlock();
1208         return active;
1209     }
1210
1211     public:
1212     
1213     using fd_watcher = dprivate::fd_watcher<my_event_loop_t>;
1214     using bidi_fd_watcher = dprivate::bidi_fd_watcher<my_event_loop_t>;
1215     using signal_watcher = dprivate::signal_watcher<my_event_loop_t>;
1216     using child_proc_watcher = dprivate::child_proc_watcher<my_event_loop_t>;
1217     using timer = dprivate::timer<my_event_loop_t>;
1218     
1219     template <typename D> using fd_watcher_impl = dprivate::fd_watcher_impl<my_event_loop_t, D>;
1220     template <typename D> using bidi_fd_watcher_impl = dprivate::bidi_fd_watcher_impl<my_event_loop_t, D>;
1221     template <typename D> using signal_watcher_impl = dprivate::signal_watcher_impl<my_event_loop_t, D>;
1222     template <typename D> using child_proc_watcher_impl = dprivate::child_proc_watcher_impl<my_event_loop_t, D>;
1223     template <typename D> using timer_impl = dprivate::timer_impl<my_event_loop_t, D>;
1224
1225     // Poll the event loop and process any pending events (up to a limit). If no events are pending, wait
1226     // for and process at least one event.
1227     void run(int limit = -1) noexcept
1228     {
1229         // Poll the mechanism first, in case high-priority events are pending:
1230         waitqueue_node<T_Mutex> qnode;
1231         get_pollwait_lock(qnode);
1232         loop_mech.pull_events(false);
1233         release_lock(qnode);
1234
1235         while (! process_events(limit)) {
1236             // Pull events from the AEN mechanism and insert them in our internal queue:
1237             get_pollwait_lock(qnode);
1238             loop_mech.pull_events(true);
1239             release_lock(qnode);
1240         }
1241     }
1242
1243     // Poll the event loop and process any pending events (up to a limit).
1244     void poll(int limit = -1) noexcept
1245     {
1246         waitqueue_node<T_Mutex> qnode;
1247         get_pollwait_lock(qnode);
1248         loop_mech.pull_events(false);
1249         release_lock(qnode);
1250
1251         process_events(limit);
1252     }
1253
1254     // Get the current time corresponding to a specific clock.
1255     //   ts - the timespec variable to receive the time
1256     //   clock - specifies the clock
1257     //   force_update (default = false) - if true, the time returned will be updated from
1258     //       the system rather than being a previously cached result. It may be more
1259     //       accurate, but note that reading from a system clock may be relatively expensive.
1260     void get_time(timespec &ts, clock_type clock, bool force_update = false) noexcept
1261     {
1262         loop_mech.get_time(ts, clock, force_update);
1263     }
1264
1265     void get_time(time_val &tv, clock_type clock, bool force_update = false) noexcept
1266     {
1267         loop_mech.get_time(tv, clock, force_update);
1268     }
1269
1270     event_loop() { }
1271     event_loop(const event_loop &other) = delete;
1272 };
1273
1274 typedef event_loop<null_mutex> event_loop_n;
1275 typedef event_loop<std::mutex> event_loop_th;
1276
1277 namespace dprivate {
1278
1279 // Posix signal event watcher
1280 template <typename EventLoop>
1281 class signal_watcher : private dprivate::base_signal_watcher<typename EventLoop::mutex_t, typename EventLoop::loop_traits_t::sigdata_t>
1282 {
1283     template <typename, typename> friend class signal_watcher_impl;
1284
1285     using base_watcher = dprivate::base_watcher;
1286     using T_Mutex = typename EventLoop::mutex_t;
1287     
1288     public:
1289     using event_loop_t = EventLoop;
1290     using siginfo_p = typename signal_watcher::siginfo_p;
1291
1292     // Register this watcher to watch the specified signal.
1293     // If an attempt is made to register with more than one event loop at
1294     // a time, behaviour is undefined. The signal should be masked before
1295     // call.
1296     inline void add_watch(event_loop_t &eloop, int signo, int prio = DEFAULT_PRIORITY)
1297     {
1298         base_watcher::init();
1299         this->priority = prio;
1300         this->siginfo.set_signo(signo);
1301         eloop.register_signal(this, signo);
1302     }
1303     
1304     inline void deregister(event_loop_t &eloop) noexcept
1305     {
1306         eloop.deregister(this, this->siginfo.get_signo());
1307     }
1308     
1309     template <typename T>
1310     static signal_watcher<event_loop_t> *add_watch(event_loop_t &eloop, int signo, T watch_hndlr)
1311     {
1312         class lambda_sig_watcher : public signal_watcher_impl<event_loop_t, lambda_sig_watcher>
1313         {
1314             private:
1315             T watch_hndlr;
1316
1317             public:
1318             lambda_sig_watcher(T watch_handlr_a) : watch_hndlr(watch_handlr_a)
1319             {
1320                 //
1321             }
1322
1323             rearm received(event_loop_t &eloop, int signo, siginfo_p siginfo)
1324             {
1325                 return watch_hndlr(eloop, signo, siginfo);
1326             }
1327
1328             void watch_removed() noexcept override
1329             {
1330                 delete this;
1331             }
1332         };
1333
1334         lambda_sig_watcher * lsw = new lambda_sig_watcher(watch_hndlr);
1335         lsw->add_watch(eloop, signo);
1336         return lsw;
1337     }
1338
1339     // virtual rearm received(EventLoop &eloop, int signo, siginfo_p siginfo) = 0;
1340 };
1341
1342 template <typename EventLoop, typename Derived>
1343 class signal_watcher_impl : public signal_watcher<EventLoop>
1344 {
1345     void dispatch(void *loop_ptr) noexcept override
1346     {
1347         EventLoop &loop = *static_cast<EventLoop *>(loop_ptr);
1348         loop.get_base_lock().unlock();
1349
1350         auto rearm_type = static_cast<Derived *>(this)->received(loop, this->siginfo.get_signo(), this->siginfo);
1351
1352         loop.get_base_lock().lock();
1353
1354         if (rearm_type != rearm::REMOVED) {
1355
1356             this->active = false;
1357             if (this->deleteme) {
1358                 // We don't want a watch that is marked "deleteme" to re-arm itself.
1359                 rearm_type = rearm::REMOVE;
1360             }
1361
1362             loop.process_signal_rearm(this, rearm_type);
1363
1364             post_dispatch(loop, this, rearm_type);
1365         }
1366     }
1367 };
1368
1369 // Posix file descriptor event watcher
1370 template <typename EventLoop>
1371 class fd_watcher : private dprivate::base_fd_watcher<typename EventLoop::mutex_t>
1372 {
1373     template <typename, typename> friend class fd_watcher_impl;
1374
1375     using base_watcher = dprivate::base_watcher;
1376     using mutex_t = typename EventLoop::mutex_t;
1377
1378     protected:
1379     
1380     // Set the types of event to watch. Only supported if loop_traits_t_t::has_bidi_fd_watch
1381     // is true; otherwise has unspecified behavior.
1382     // Only safe to call from within the callback handler (fdEvent). Might not take
1383     // effect until the current callback handler returns with REARM.
1384     void set_watch_flags(int newFlags)
1385     {
1386         this->watch_flags = newFlags;
1387     }
1388     
1389     public:
1390     
1391     using event_loop_t = EventLoop;
1392
1393     // Register a file descriptor watcher with an event loop. Flags
1394     // can be any combination of dasynq::IN_EVENTS / dasynq::OUT_EVENTS.
1395     // Exactly one of IN_EVENTS/OUT_EVENTS must be specified if the event
1396     // loop does not support bi-directional fd watchers (i.e. if
1397     // ! loop_traits_t::has_bidi_fd_watch).
1398     //
1399     // Mechanisms supporting dual watchers allow for two watchers for a
1400     // single file descriptor (one watching read status and the other
1401     // write status). Others mechanisms support only a single watcher
1402     // per file descriptor. Adding a watcher beyond what is supported
1403     // causes undefined behavior.
1404     //
1405     // Can fail with std::bad_alloc or std::system_error.
1406     void add_watch(event_loop_t &eloop, int fd, int flags, bool enabled = true, int prio = DEFAULT_PRIORITY)
1407     {
1408         base_watcher::init();
1409         this->priority = prio;
1410         this->watch_fd = fd;
1411         this->watch_flags = flags;
1412         eloop.register_fd(this, fd, flags, enabled, true);
1413     }
1414
1415     void add_watch_noemu(event_loop_t &eloop, int fd, int flags, bool enabled = true, int prio = DEFAULT_PRIORITY)
1416     {
1417         base_watcher::init();
1418         this->priority = prio;
1419         this->watch_fd = fd;
1420         this->watch_flags = flags;
1421         eloop.register_fd(this, fd, flags, enabled, false);
1422     }
1423     
1424     int get_watched_fd()
1425     {
1426         return this->watch_fd;
1427     }
1428     
1429     // Deregister a file descriptor watcher.
1430     //
1431     // If other threads may be polling the event loop, it is not safe to assume
1432     // the watcher is unregistered until the watch_removed() callback is issued
1433     // (which will not occur until the event handler returns, if it is active).
1434     // In a single threaded environment, it is safe to delete the watcher after
1435     // calling this method as long as the handler (if it is active) accesses no
1436     // internal state and returns rearm::REMOVED.
1437     void deregister(event_loop_t &eloop) noexcept
1438     {
1439         eloop.deregister(this, this->watch_fd);
1440     }
1441     
1442     void set_enabled(event_loop_t &eloop, bool enable) noexcept
1443     {
1444         std::lock_guard<mutex_t> guard(eloop.get_base_lock());
1445         if (this->emulatefd) {
1446             this->emulate_enabled = enable;
1447         }
1448         else {
1449             eloop.set_fd_enabled_nolock(this, this->watch_fd, this->watch_flags, enable);
1450         }
1451         if (! enable) {
1452             eloop.dequeue_watcher(this);
1453         }
1454     }
1455     
1456     // Add an Fd watch via a lambda. The watch is allocated dynamically and destroys
1457     // itself when removed from the event loop.
1458     template <typename T>
1459     static fd_watcher<EventLoop> *add_watch(event_loop_t &eloop, int fd, int flags, T watchHndlr)
1460     {
1461         class lambda_fd_watcher : public fd_watcher_impl<event_loop_t, lambda_fd_watcher>
1462         {
1463             private:
1464             T watchHndlr;
1465
1466             public:
1467             lambda_fd_watcher(T watchHandlr_a) : watchHndlr(watchHandlr_a)
1468             {
1469                 //
1470             }
1471
1472             rearm fd_event(event_loop_t &eloop, int fd, int flags)
1473             {
1474                 return watchHndlr(eloop, fd, flags);
1475             }
1476
1477             void watch_removed() noexcept override
1478             {
1479                 delete this;
1480             }
1481         };
1482         
1483         lambda_fd_watcher * lfd = new lambda_fd_watcher(watchHndlr);
1484         lfd->add_watch(eloop, fd, flags);
1485         return lfd;
1486     }
1487     
1488     // virtual rearm fd_event(EventLoop &eloop, int fd, int flags) = 0;
1489 };
1490
1491 template <typename EventLoop, typename Derived>
1492 class fd_watcher_impl : public fd_watcher<EventLoop>
1493 {
1494     void dispatch(void *loop_ptr) noexcept override
1495     {
1496         EventLoop &loop = *static_cast<EventLoop *>(loop_ptr);
1497
1498         // In case emulating, clear enabled here; REARM or explicit set_enabled will re-enable.
1499         this->emulate_enabled = false;
1500
1501         loop.get_base_lock().unlock();
1502
1503         auto rearm_type = static_cast<Derived *>(this)->fd_event(loop, this->watch_fd, this->event_flags);
1504
1505         loop.get_base_lock().lock();
1506
1507         if (rearm_type != rearm::REMOVED) {
1508             this->event_flags = 0;
1509             this->active = false;
1510             if (this->deleteme) {
1511                 // We don't want a watch that is marked "deleteme" to re-arm itself.
1512                 rearm_type = rearm::REMOVE;
1513             }
1514
1515             rearm_type = loop.process_fd_rearm(this, rearm_type, false);
1516
1517             post_dispatch(loop, this, rearm_type);
1518         }
1519     }
1520 };
1521
1522
1523 // A Bi-directional file descriptor watcher with independent read- and write- channels.
1524 // This watcher type has two event notification methods which can both potentially be
1525 // active at the same time.
1526 template <typename EventLoop>
1527 class bidi_fd_watcher : private dprivate::base_bidi_fd_watcher<typename EventLoop::mutex_t>
1528 {
1529     template <typename, typename> friend class bidi_fd_watcher_impl;
1530
1531     using base_watcher = dprivate::base_watcher;
1532     using mutex_t = typename EventLoop::mutex_t;
1533     
1534     void set_watch_enabled(EventLoop &eloop, bool in, bool b)
1535     {
1536         int events = in ? IN_EVENTS : OUT_EVENTS;
1537         
1538         if (b) {
1539             this->watch_flags |= events;
1540         }
1541         else {
1542             this->watch_flags &= ~events;
1543         }
1544
1545         dprivate::base_watcher * watcher = in ? this : &this->out_watcher;
1546
1547         if (! watcher->emulatefd) {
1548             if (EventLoop::loop_traits_t::has_separate_rw_fd_watches) {
1549                 eloop.set_fd_enabled_nolock(this, this->watch_fd, events | ONE_SHOT, b);
1550             }
1551             else {
1552                 eloop.set_fd_enabled_nolock(this, this->watch_fd,
1553                         (this->watch_flags & IO_EVENTS) | ONE_SHOT,
1554                         (this->watch_flags & IO_EVENTS) != 0);
1555             }
1556         }
1557
1558         if (! b) {
1559             eloop.dequeue_watcher(watcher);
1560         }
1561     }
1562     
1563     public:
1564
1565     using event_loop_t = EventLoop;
1566
1567     void set_in_watch_enabled(event_loop_t &eloop, bool b) noexcept
1568     {
1569         eloop.get_base_lock().lock();
1570         set_watch_enabled(eloop, true, b);
1571         eloop.get_base_lock().unlock();
1572     }
1573     
1574     void set_out_watch_enabled(event_loop_t &eloop, bool b) noexcept
1575     {
1576         eloop.get_base_lock().lock();
1577         set_watch_enabled(eloop, false, b);
1578         eloop.get_base_lock().unlock();
1579     }
1580     
1581     // Set the watch flags, which enables/disables both the in-watch and the out-watch accordingly.
1582     //
1583     // Concurrency: this method can only be called if
1584     //  - it does not enable a watcher that might currently be active
1585     ///   - unless the event loop will not be polled while the watcher is active.
1586     // (i.e. it is ok to call setWatchFlags from within the readReady/writeReady handlers if no other
1587     //  thread will poll the event loop; it is always ok to *dis*able a watcher that might be active,
1588     //  though the re-arm action returned by the callback may undo the effect).
1589     void set_watches(event_loop_t &eloop, int new_flags) noexcept
1590     {
1591         std::lock_guard<mutex_t> guard(eloop.get_base_lock());
1592         bool use_emulation = this->emulatefd || this->out_watcher.emulatefd;
1593         if (use_emulation || EventLoop::loop_traits_t::has_separate_rw_fd_watches) {
1594             set_watch_enabled(eloop, true, (new_flags & IN_EVENTS) != 0);
1595             set_watch_enabled(eloop, false, (new_flags & OUT_EVENTS) != 0);
1596         }
1597         else {
1598             this->watch_flags = (this->watch_flags & ~IO_EVENTS) | new_flags;
1599             eloop.set_fd_enabled_nolock((dprivate::base_watcher *) this, this->watch_fd, this->watch_flags & IO_EVENTS, true);
1600         }
1601     }
1602     
1603     // Register a bi-direction file descriptor watcher with an event loop. Flags
1604     // can be any combination of dasynq::IN_EVENTS / dasynq::OUT_EVENTS.
1605     //
1606     // Can fail with std::bad_alloc or std::system_error.
1607     void add_watch(event_loop_t &eloop, int fd, int flags, int inprio = DEFAULT_PRIORITY, int outprio = DEFAULT_PRIORITY)
1608     {
1609         base_watcher::init();
1610         this->out_watcher.base_watcher::init();
1611         this->watch_fd = fd;
1612         this->watch_flags = flags | dprivate::multi_watch;
1613         this->read_removed = false;
1614         this->write_removed = false;
1615         this->priority = inprio;
1616         this->set_priority(this->out_watcher, outprio);
1617         eloop.register_fd(this, fd, flags, true);
1618     }
1619
1620     void add_watch_noemu(event_loop_t &eloop, int fd, int flags, int inprio = DEFAULT_PRIORITY, int outprio = DEFAULT_PRIORITY)
1621     {
1622         base_watcher::init();
1623         this->out_watcher.base_watcher::init();
1624         this->watch_fd = fd;
1625         this->watch_flags = flags | dprivate::multi_watch;
1626         this->read_removed = false;
1627         this->write_removed = false;
1628         this->priority = inprio;
1629         this->set_priority(this->out_watcher, outprio);
1630         eloop.register_fd(this, fd, flags, false);
1631     }
1632
1633     int get_watched_fd()
1634     {
1635         return this->watch_fd;
1636     }
1637     
1638     // Deregister a bi-direction file descriptor watcher.
1639     //
1640     // If other threads may be polling the event loop, it is not safe to assume
1641     // the watcher is unregistered until the watch_removed() callback is issued
1642     // (which will not occur until the event handler returns, if it is active).
1643     // In a single threaded environment, it is safe to delete the watcher after
1644     // calling this method as long as the handler (if it is active) accesses no
1645     // internal state and returns rearm::REMOVED.
1646     void deregister(event_loop_t &eloop) noexcept
1647     {
1648         eloop.deregister(this, this->watch_fd);
1649     }
1650     
1651     template <typename T>
1652     static bidi_fd_watcher<event_loop_t> *add_watch(event_loop_t &eloop, int fd, int flags, T watch_hndlr)
1653     {
1654         class lambda_bidi_watcher : public bidi_fd_watcher_impl<event_loop_t, lambda_bidi_watcher>
1655         {
1656             private:
1657             T watch_hndlr;
1658
1659             public:
1660             lambda_bidi_watcher(T watch_handlr_a) : watch_hndlr(watch_handlr_a)
1661             {
1662                 //
1663             }
1664
1665             rearm read_ready(event_loop_t &eloop, int fd)
1666             {
1667                 return watch_hndlr(eloop, fd, IN_EVENTS);
1668             }
1669
1670             rearm write_ready(event_loop_t &eloop, int fd)
1671             {
1672                 return watch_hndlr(eloop, fd, OUT_EVENTS);
1673             }
1674
1675             void watch_removed() noexcept override
1676             {
1677                 delete this;
1678             }
1679         };
1680
1681         lambda_bidi_watcher * lfd = new lambda_bidi_watcher(watch_hndlr);
1682         lfd->add_watch(eloop, fd, flags);
1683         return lfd;
1684     }
1685
1686     // virtual rearm read_ready(EventLoop &eloop, int fd) noexcept = 0;
1687     // virtual rearm write_ready(EventLoop &eloop, int fd) noexcept = 0;
1688 };
1689
1690 template <typename EventLoop, typename Derived>
1691 class bidi_fd_watcher_impl : public bidi_fd_watcher<EventLoop>
1692 {
1693     void dispatch(void *loop_ptr) noexcept override
1694     {
1695         EventLoop &loop = *static_cast<EventLoop *>(loop_ptr);
1696         this->emulate_enabled = false;
1697         loop.get_base_lock().unlock();
1698
1699         auto rearm_type = static_cast<Derived *>(this)->read_ready(loop, this->watch_fd);
1700
1701         loop.get_base_lock().lock();
1702
1703         if (rearm_type != rearm::REMOVED) {
1704             this->event_flags &= ~IN_EVENTS;
1705             this->active = false;
1706             if (this->deleteme) {
1707                 // We don't want a watch that is marked "deleteme" to re-arm itself.
1708                 rearm_type = rearm::REMOVE;
1709             }
1710
1711             rearm_type = loop.process_fd_rearm(this, rearm_type, true);
1712
1713             post_dispatch(loop, this, rearm_type);
1714         }
1715     }
1716
1717     void dispatch_second(void *loop_ptr) noexcept override
1718     {
1719         auto &outwatcher = bidi_fd_watcher<EventLoop>::out_watcher;
1720
1721         EventLoop &loop = *static_cast<EventLoop *>(loop_ptr);
1722         loop.get_base_lock().unlock();
1723
1724         auto rearm_type = static_cast<Derived *>(this)->write_ready(loop, this->watch_fd);
1725
1726         loop.get_base_lock().lock();
1727
1728         if (rearm_type != rearm::REMOVED) {
1729             this->event_flags &= ~OUT_EVENTS;
1730             outwatcher.active = false;
1731             if (outwatcher.deleteme) {
1732                 // We don't want a watch that is marked "deleteme" to re-arm itself.
1733                 rearm_type = rearm::REMOVE;
1734             }
1735
1736             rearm_type = loop.process_secondary_rearm(this, &outwatcher, rearm_type);
1737
1738             if (rearm_type == rearm::REQUEUE) {
1739                 post_dispatch(loop, &outwatcher, rearm_type);
1740             }
1741             else {
1742                 post_dispatch(loop, this, rearm_type);
1743             }
1744         }
1745     }
1746 };
1747
1748 // Child process event watcher
1749 template <typename EventLoop>
1750 class child_proc_watcher : private dprivate::base_child_watcher<typename EventLoop::mutex_t>
1751 {
1752     template <typename, typename> friend class child_proc_watcher_impl;
1753
1754     using base_watcher = dprivate::base_watcher;
1755     using mutex_t = typename EventLoop::mutex_t;
1756
1757     public:
1758
1759     using event_loop_t = EventLoop;
1760
1761     // send a signal to this process, if it is still running, in a race-free manner.
1762     // return is as for POSIX kill(); return is -1 with errno=ESRCH if process has
1763     // already terminated.
1764     int send_signal(event_loop_t &loop, int signo) noexcept
1765     {
1766         auto reaper_mutex = loop.get_reaper_mutex();
1767         std::lock_guard<decltype(reaper_mutex)> guard(reaper_mutex);
1768
1769         if (this->child_termd) {
1770             errno = ESRCH;
1771             return -1;
1772         }
1773
1774         return kill(this->watch_pid, signo);
1775     }
1776
1777     // Reserve resources for a child watcher with the given event loop.
1778     // Reservation can fail with std::bad_alloc. Some backends do not support
1779     // reservation (it will always fail) - check loop_traits_t::supports_childwatch_reservation.
1780     void reserve_watch(event_loop_t &eloop)
1781     {
1782         eloop.reserve_child_watch(this);
1783     }
1784     
1785     void unreserve(event_loop_t &eloop)
1786     {
1787         eloop.unreserve(this);
1788     }
1789     
1790     // Register a watcher for the given child process with an event loop.
1791     // Registration can fail with std::bad_alloc.
1792     // Note that in multi-threaded programs, use of this function may be prone to a
1793     // race condition such that the child terminates before the watcher is registered.
1794     void add_watch(event_loop_t &eloop, pid_t child, int prio = DEFAULT_PRIORITY)
1795     {
1796         base_watcher::init();
1797         this->watch_pid = child;
1798         this->priority = prio;
1799         eloop.register_child(this, child);
1800     }
1801     
1802     // Register a watcher for the given child process with an event loop,
1803     // after having reserved resources previously (using reserveWith).
1804     // Registration cannot fail.
1805     // Note that in multi-threaded programs, use of this function may be prone to a
1806     // race condition such that the child terminates before the watcher is registered;
1807     // use the "fork" member function to avoid this.
1808     void add_reserved(event_loop_t &eloop, pid_t child, int prio = DEFAULT_PRIORITY) noexcept
1809     {
1810         base_watcher::init();
1811         this->watch_pid = child;
1812         this->priority = prio;
1813         eloop.register_reserved_child(this, child);
1814     }
1815     
1816     void deregister(event_loop_t &eloop, pid_t child) noexcept
1817     {
1818         eloop.deregister(this, child);
1819     }
1820     
1821     // Stop watching the currently watched child, but retain watch reservation.
1822     void stop_watch(event_loop_t &eloop) noexcept
1823     {
1824         eloop.stop_watch(this);
1825     }
1826
1827     // Fork and watch the child with this watcher on the given event loop.
1828     // If resource limitations prevent the child process from being watched, it is
1829     // terminated immediately (or if the implementation allows, never started),
1830     // and a suitable std::system_error or std::bad_alloc exception is thrown.
1831     // Returns:
1832     // - the child pid in the parent
1833     // - 0 in the child
1834     pid_t fork(event_loop_t &eloop, bool from_reserved = false, int prio = DEFAULT_PRIORITY)
1835     {
1836         base_watcher::init();
1837         this->priority = prio;
1838
1839         if (EventLoop::loop_traits_t::supports_childwatch_reservation) {
1840             // Reserve a watch, fork, then claim reservation
1841             if (! from_reserved) {
1842                 reserve_watch(eloop);
1843             }
1844             
1845             auto &lock = eloop.get_base_lock();
1846             lock.lock();
1847             
1848             pid_t child = ::fork();
1849             if (child == -1) {
1850                 // Unreserve watch.
1851                 lock.unlock();
1852                 unreserve(eloop);
1853                 throw std::system_error(errno, std::system_category());
1854             }
1855             
1856             if (child == 0) {
1857                 // I am the child
1858                 lock.unlock(); // may not really be necessary
1859                 return 0;
1860             }
1861             
1862             // Register this watcher.
1863             this->watch_pid = child;
1864             eloop.register_reserved_child_nolock(this, child);
1865             lock.unlock();
1866             return child;
1867         }
1868         else {
1869             int pipefds[2];
1870             if (pipe2(pipefds, O_CLOEXEC) == -1) {
1871                 throw std::system_error(errno, std::system_category());
1872             }
1873             
1874             std::lock_guard<mutex_t> guard(eloop.get_base_lock());
1875             
1876             pid_t child = ::fork();
1877             if (child == -1) {
1878                 throw std::system_error(errno, std::system_category());
1879             }
1880             
1881             if (child == 0) {
1882                 // I am the child
1883                 close(pipefds[1]);
1884                 
1885                 // Wait for message from parent before continuing:
1886                 int rr;
1887                 int r = read(pipefds[0], &rr, sizeof(rr));
1888                 while (r == -1 && errno == EINTR) {
1889                     r = read(pipefds[0], &rr, sizeof(rr));
1890                 }
1891                 
1892                 if (r <= 0) _exit(0);
1893                 
1894                 close(pipefds[0]);
1895                 return 0;
1896             }
1897             
1898             close(pipefds[0]); // close read end
1899             
1900             // Register this watcher.
1901             try {
1902                 this->watch_pid = child;
1903                 eloop.register_child(this, child);
1904                 
1905                 // Continue in child (it doesn't matter what is written):
1906                 write(pipefds[1], &pipefds, sizeof(int));
1907                 close(pipefds[1]);
1908                 
1909                 return child;
1910             }
1911             catch (...) {
1912                 close(pipefds[1]);
1913                 throw;
1914             }
1915         }
1916     }
1917     
1918     // virtual rearm child_status(EventLoop &eloop, pid_t child, int status) = 0;
1919 };
1920
1921 template <typename EventLoop, typename Derived>
1922 class child_proc_watcher_impl : public child_proc_watcher<EventLoop>
1923 {
1924     void dispatch(void *loop_ptr) noexcept override
1925     {
1926         EventLoop &loop = *static_cast<EventLoop *>(loop_ptr);
1927         loop.get_base_lock().unlock();
1928
1929         auto rearm_type = static_cast<Derived *>(this)->status_change(loop, this->watch_pid, this->child_status);
1930
1931         loop.get_base_lock().lock();
1932
1933         if (rearm_type != rearm::REMOVED) {
1934
1935             this->active = false;
1936             if (this->deleteme) {
1937                 // We don't want a watch that is marked "deleteme" to re-arm itself.
1938                 rearm_type = rearm::REMOVE;
1939             }
1940
1941             loop.process_child_watch_rearm(this, rearm_type);
1942
1943             // rearm_type = loop.process??;
1944             post_dispatch(loop, this, rearm_type);
1945         }
1946     }
1947 };
1948
1949 template <typename EventLoop>
1950 class timer : private base_timer_watcher<typename EventLoop::mutex_t>
1951 {
1952     template <typename, typename> friend class timer_impl;
1953     using base_t = base_timer_watcher<typename EventLoop::mutex_t>;
1954     using mutex_t = typename EventLoop::mutex_t;
1955
1956     public:
1957     using event_loop_t = EventLoop;
1958     
1959     void add_timer(event_loop_t &eloop, clock_type clock = clock_type::MONOTONIC, int prio = DEFAULT_PRIORITY)
1960     {
1961         base_watcher::init();
1962         this->priority = prio;
1963         this->clock = clock;
1964         this->intervals = 0;
1965         eloop.register_timer(this, clock);
1966     }
1967     
1968     void arm_timer(event_loop_t &eloop, const timespec &timeout) noexcept
1969     {
1970         eloop.set_timer(this, timeout, base_t::clock);
1971     }
1972     
1973     void arm_timer(event_loop_t &eloop, const timespec &timeout, const timespec &interval) noexcept
1974     {
1975         eloop.set_timer(this, timeout, interval, base_t::clock);
1976     }
1977
1978     // Arm timer, relative to now:
1979     void arm_timer_rel(event_loop_t &eloop, const timespec &timeout) noexcept
1980     {
1981         eloop.set_timer_rel(this, timeout, base_t::clock);
1982     }
1983     
1984     void arm_timer_rel(event_loop_t &eloop, const timespec &timeout,
1985             const timespec &interval) noexcept
1986     {
1987         eloop.set_timer_rel(this, timeout, interval, base_t::clock);
1988     }
1989     
1990     void stop_timer(event_loop_t &eloop) noexcept
1991     {
1992         eloop.stop_timer(this, base_t::clock);
1993     }
1994
1995     void set_enabled(event_loop_t &eloop, clock_type clock, bool enabled) noexcept
1996     {
1997         std::lock_guard<mutex_t> guard(eloop.get_base_lock());
1998         eloop.set_timer_enabled_nolock(this, clock, enabled);
1999         if (! enabled) {
2000             eloop.dequeue_watcher(this);
2001         }
2002     }
2003
2004     void deregister(event_loop_t &eloop) noexcept
2005     {
2006         eloop.deregister(this, this->clock);
2007     }
2008
2009     template <typename T>
2010     static timer<EventLoop> *add_timer(EventLoop &eloop, clock_type clock, bool relative,
2011             struct timespec &timeout, struct timespec &interval, T watch_hndlr)
2012     {
2013         class lambda_timer : public timer_impl<event_loop_t, lambda_timer>
2014         {
2015             private:
2016             T watch_hndlr;
2017
2018             public:
2019             lambda_timer(T watch_handlr_a) : watch_hndlr(watch_handlr_a)
2020             {
2021                 //
2022             }
2023
2024             rearm timer_expiry(event_loop_t &eloop, int intervals)
2025             {
2026                 return watch_hndlr(eloop, intervals);
2027             }
2028
2029             void watch_removed() noexcept override
2030             {
2031                 delete this;
2032             }
2033         };
2034
2035         lambda_timer * lt = new lambda_timer(watch_hndlr);
2036         lt->add_timer(eloop, clock);
2037         if (relative) {
2038             lt->arm_timer_rel(eloop, timeout, interval);
2039         }
2040         else {
2041             lt->arm_timer(eloop, timeout, interval);
2042         }
2043         return lt;
2044     }
2045
2046     // Timer expired, and the given number of intervals have elapsed before
2047     // expiry event was queued. Normally intervals == 1 to indicate no
2048     // overrun.
2049     // virtual rearm timer_expiry(event_loop_t &eloop, int intervals) = 0;
2050 };
2051
2052 template <typename EventLoop, typename Derived>
2053 class timer_impl : public timer<EventLoop>
2054 {
2055     void dispatch(void *loop_ptr) noexcept override
2056     {
2057         EventLoop &loop = *static_cast<EventLoop *>(loop_ptr);
2058         loop.get_base_lock().unlock();
2059
2060         auto intervals_report = this->intervals;
2061         this->intervals = 0;
2062         auto rearm_type = static_cast<Derived *>(this)->timer_expiry(loop, intervals_report);
2063
2064         loop.get_base_lock().lock();
2065
2066         if (rearm_type != rearm::REMOVED) {
2067
2068             this->active = false;
2069             if (this->deleteme) {
2070                 // We don't want a watch that is marked "deleteme" to re-arm itself.
2071                 rearm_type = rearm::REMOVE;
2072             }
2073
2074             loop.process_timer_rearm(this, rearm_type);
2075
2076             post_dispatch(loop, this, rearm_type);
2077         }
2078     }
2079 };
2080
2081 }  // namespace dasynq::dprivate
2082 }  // namespace dasynq
2083
2084 #endif