Fix use of old settings in sample config, see #16.
[oweals/dinit.git] / src / dasynq / dasynq-basewatchers.h
1 // Dasynq: early declarations and base watchers.
2 //
3 // Here we define watcher functionality that is not dependent on the event loop type. In particular,
4 // base classes for the various watcher types. These are not part of the public API.
5 //
6 // In general access to the members of the basewatcher should be protected by a mutex. The
7 // event_dispatch lock is used for this purpose.
8
9 #include <type_traits>
10
11 namespace dasynq {
12
13 namespace dprivate {
14     // POSIX says that sigprocmask has unspecified behaviour if used in a multi-threaded process. We can use
15     // pthread_sigmask instead, but that may require linking with the threads library. This function is
16     // specialised to call one or the other depending on the mutex type:
17     template <typename T_Mutex> void sigmaskf(int how, const sigset_t *set, sigset_t *oset)
18     {
19         pthread_sigmask(how, set, oset);
20     }
21
22     template <> inline void sigmaskf<null_mutex>(int how, const sigset_t *set, sigset_t *oset)
23     {
24         sigprocmask(how, set, oset);
25     }
26 }
27
28 // A template to generate suitable default loop traits for a given type of mutex:
29 template <typename T_Mutex> class default_traits
30 {
31     public:
32     using mutex_t  = T_Mutex;
33     template <typename Base> using backend_t = dasynq::loop_t<Base>;
34     using backend_traits_t = dasynq::loop_traits_t;
35
36     // Alter the current thread signal mask using the correct function
37     // (sigprocmask or pthread_sigmask):
38     static void sigmaskf(int how, const sigset_t *set, sigset_t *oset)
39     {
40         dprivate::sigmaskf<T_Mutex>(how, set, oset);
41     }
42 };
43
44 // Forward declarations:
45 template <typename T_Mutex, typename Traits = default_traits<T_Mutex>>
46 class event_loop;
47
48 inline namespace {
49     constexpr int DEFAULT_PRIORITY = 50;
50 }
51
52 namespace dprivate {
53     // (non-public API)
54
55     class base_watcher;
56
57     class empty_node
58     {
59         DASYNQ_EMPTY_BODY
60     };
61
62     // heap_def decides the queue implementation that we use. It must be stable:
63     template <typename A, typename B, typename C> using dary_heap_def = dary_heap<A,B,C>;
64     template <typename A, typename B> using heap_def = stable_heap<dary_heap_def,A,B>;
65
66     namespace {
67         // use empty handles (not containing basewatcher *) if the handles returned from the
68         // queue are handle references, because we can derive a pointer to the containing basewatcher
69         // via the address of the handle in that case:
70         constexpr bool use_empty_node = std::is_same<
71                 typename heap_def<empty_node, int>::handle_t_r,
72                 typename heap_def<empty_node, int>::handle_t &>::value;
73
74         using node_type = std::conditional<use_empty_node, empty_node, base_watcher *>::type;
75     }
76
77     using prio_queue = heap_def<node_type, int>;
78
79     using prio_queue_emptynode = heap_def<empty_node, int>;
80     using prio_queue_bwnode = heap_def<base_watcher *, int>;
81
82     template <typename T_Loop> class fd_watcher;
83     template <typename T_Loop> class bidi_fd_watcher;
84     template <typename T_Loop> class signal_watcher;
85     template <typename T_Loop> class child_proc_watcher;
86     template <typename T_Loop> class timer;
87
88     template <typename, typename> class fd_watcher_impl;
89     template <typename, typename> class bidi_fd_watcher_impl;
90     template <typename, typename> class signal_watcher_impl;
91     template <typename, typename> class child_proc_watcher_impl;
92     template <typename, typename> class timer_impl;
93
94     enum class watch_type_t
95     {
96         SIGNAL,
97         FD,
98         CHILD,
99         SECONDARYFD,
100         TIMER
101     };
102
103     template <typename Traits, typename LoopTraits> class event_dispatch;
104
105     // For FD watchers:
106     // Use this watch flag to indicate that in and out events should be reported separately,
107     // that is, watcher should not be disabled until all watched event types are queued.
108     constexpr static int multi_watch = 4;
109
110     // Represents a queued event notification. Various event watchers derive from this type.
111     class base_watcher
112     {
113         public:
114         watch_type_t watchType;
115         int active : 1;    // currently executing handler?
116         int deleteme : 1;  // delete when handler finished?
117         int emulatefd : 1; // emulate file watch (by re-queueing)
118         int emulate_enabled : 1;   // whether an emulated watch is enabled
119         int child_termd : 1;  // child process has terminated
120
121         prio_queue::handle_t heap_handle;
122         int priority;
123
124         static void set_priority(base_watcher &p, int prio)
125         {
126             p.priority = prio;
127         }
128
129         // Perform initialisation necessary before registration with an event loop
130         void init()
131         {
132             active = false;
133             deleteme = false;
134             emulatefd = false;
135             emulate_enabled = false;
136             child_termd = false;
137             prio_queue::init_handle(heap_handle);
138             priority = DEFAULT_PRIORITY;
139         }
140
141         base_watcher(watch_type_t wt) noexcept : watchType(wt) { }
142         base_watcher(const base_watcher &) = delete;
143         base_watcher &operator=(const base_watcher &) = delete;
144
145         // The dispatch function is called to process a watcher's callback. It is the "real" callback
146         // function; it usually delegates to a user-provided callback.
147         virtual void dispatch(void *loop_ptr) noexcept { };
148
149         // Bi-directional file descriptor watches have a secondary dispatch function for the secondary
150         // watcher (i.e. the output watcher):
151         virtual void dispatch_second(void *loop_ptr) noexcept { }
152
153         virtual ~base_watcher() noexcept { }
154
155         // Called when the watcher has been removed.
156         // It is guaranteed by the caller that:
157         // - the dispatch method is not currently running
158         // - the dispatch method will not be called.
159         virtual void watch_removed() noexcept
160         {
161             // Later: the "delete" behaviour could be dependent on a flag, perhaps?
162             // delete this;
163         }
164     };
165
166     // Retrieve watcher from queue handle:
167     inline base_watcher * get_watcher(prio_queue_emptynode &q, prio_queue_emptynode::handle_t &n)
168     {
169         uintptr_t bptr = (uintptr_t)&n;
170         _Pragma ("GCC diagnostic push")
171         _Pragma ("GCC diagnostic ignored \"-Winvalid-offsetof\"")
172         bptr -= offsetof(base_watcher, heap_handle);
173         _Pragma ("GCC diagnostic pop")
174         return (base_watcher *)bptr;
175     }
176
177     inline dprivate::base_watcher * get_watcher(prio_queue_bwnode &q, prio_queue_bwnode::handle_t &n)
178     {
179         return q.node_data(n);
180     }
181
182     // Allocate queue handle:
183     inline void allocate_handle(prio_queue_emptynode &q, prio_queue_emptynode::handle_t &n, base_watcher *bw)
184     {
185         q.allocate(n);
186     }
187
188     inline void allocate_handle(prio_queue_bwnode &q, prio_queue_bwnode::handle_t &n, base_watcher *bw)
189     {
190         q.allocate(n, bw);
191     }
192
193     // Base signal event - not part of public API
194     template <typename T_Sigdata>
195     class base_signal_watcher : public base_watcher
196     {
197         template <typename, typename> friend class event_dispatch;
198         template <typename, typename> friend class dasynq::event_loop;
199
200         protected:
201         T_Sigdata siginfo;
202         base_signal_watcher() : base_watcher(watch_type_t::SIGNAL) { }
203
204         public:
205         using siginfo_t = T_Sigdata;
206         typedef siginfo_t &siginfo_p;
207     };
208
209     class base_fd_watcher : public base_watcher
210     {
211         template <typename, typename> friend class event_dispatch;
212         template <typename, typename> friend class dasynq::event_loop;
213
214         protected:
215         int watch_fd;
216
217         // These flags are protected by the loop's internal lock:
218         int watch_flags;  // events being watched
219         int event_flags;  // events pending (queued)
220
221         // watch_flags: for a regular fd_watcher, this specifies the events that the watcher
222         //              is watching (or was watching if disabled). For a bidi_fd_watcher, specifies
223         //              the events that the watcher is currently watching (i.e. specifies which
224         //              halves of the Bidi watcher are enabled).
225
226         base_fd_watcher() noexcept : base_watcher(watch_type_t::FD) { }
227     };
228
229     class base_bidi_fd_watcher : public base_fd_watcher
230     {
231         template <typename, typename> friend class event_dispatch;
232         template <typename, typename> friend class dasynq::event_loop;
233
234         base_bidi_fd_watcher(const base_bidi_fd_watcher &) = delete;
235
236         protected:
237         base_bidi_fd_watcher() noexcept { }
238
239         // The main instance is the "input" watcher only; we keep a secondary watcher with a secondary set
240         // of flags for the "output" watcher. Note that some of the flags in the secondary watcher aren't
241         // used; it exists mainly so that it can be queued independently of the primary watcher.
242         base_watcher out_watcher {watch_type_t::SECONDARYFD};
243
244         int read_removed : 1; // read watch removed?
245         int write_removed : 1; // write watch removed?
246     };
247
248     class base_child_watcher : public base_watcher
249     {
250         template <typename, typename> friend class event_dispatch;
251         template <typename, typename> friend class dasynq::event_loop;
252
253         protected:
254         pid_watch_handle_t watch_handle;
255         pid_t watch_pid;
256         int child_status;
257
258         base_child_watcher() : base_watcher(watch_type_t::CHILD) { }
259     };
260
261
262     class base_timer_watcher : public base_watcher
263     {
264         template <typename, typename> friend class event_dispatch;
265         template <typename, typename> friend class dasynq::event_loop;
266
267         protected:
268         timer_handle_t timer_handle;
269         int intervals;
270         clock_type clock;
271
272         base_timer_watcher() : base_watcher(watch_type_t::TIMER)
273         {
274             init_timer_handle(timer_handle);
275         }
276     };
277 } // dprivate
278 } // dasynq