Update Dasynq to 1.0.2.
[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 namespace dasynq {
10
11 namespace dprivate {
12     // POSIX says that sigprocmask has unspecified behaviour if used in a multi-threaded process. We can use
13     // pthread_sigmask instead, but that may require linking with the threads library. This function is
14     // specialised to call one or the other depending on the mutex type:
15     template <typename T_Mutex> void sigmaskf(int how, const sigset_t *set, sigset_t *oset)
16     {
17         pthread_sigmask(how, set, oset);
18     }
19
20     template <> inline void sigmaskf<null_mutex>(int how, const sigset_t *set, sigset_t *oset)
21     {
22         sigprocmask(how, set, oset);
23     }
24 }
25
26 // A template to generate suitable default loop traits for a given type of mutex:
27 template <typename T_Mutex> class default_traits
28 {
29     public:
30     using mutex_t  = T_Mutex;
31     template <typename Base> using backend_t = dasynq::loop_t<Base>;
32     using backend_traits_t = dasynq::loop_traits_t;
33
34     // Alter the current thread signal mask using the correct function
35     // (sigprocmask or pthread_sigmask):
36     static void sigmaskf(int how, const sigset_t *set, sigset_t *oset)
37     {
38         dprivate::sigmaskf<T_Mutex>(how, set, oset);
39     }
40 };
41
42 // Forward declarations:
43 template <typename T_Mutex, typename Traits = default_traits<T_Mutex>>
44 class event_loop;
45
46 inline namespace {
47     constexpr int DEFAULT_PRIORITY = 50;
48 }
49
50 namespace dprivate {
51     // (non-public API)
52
53     class base_watcher;
54     template <typename A, typename B, typename C> using dary_heap_def = dary_heap<A,B,C>;
55     using prio_queue = stable_heap<dary_heap_def, dprivate::base_watcher *, int>;
56
57     template <typename T_Loop> class fd_watcher;
58     template <typename T_Loop> class bidi_fd_watcher;
59     template <typename T_Loop> class signal_watcher;
60     template <typename T_Loop> class child_proc_watcher;
61     template <typename T_Loop> class timer;
62
63     template <typename, typename> class fd_watcher_impl;
64     template <typename, typename> class bidi_fd_watcher_impl;
65     template <typename, typename> class signal_watcher_impl;
66     template <typename, typename> class child_proc_watcher_impl;
67     template <typename, typename> class timer_impl;
68
69     enum class watch_type_t
70     {
71         SIGNAL,
72         FD,
73         CHILD,
74         SECONDARYFD,
75         TIMER
76     };
77
78     template <typename T_Mutex, typename Traits, typename LoopTraits> class event_dispatch;
79
80     // For FD watchers:
81     // Use this watch flag to indicate that in and out events should be reported separately,
82     // that is, watcher should not be disabled until all watched event types are queued.
83     constexpr static int multi_watch = 4;
84
85     // Represents a queued event notification. Various event watchers derive from this type.
86     class base_watcher
87     {
88         public:
89         watch_type_t watchType;
90         int active : 1;    // currently executing handler?
91         int deleteme : 1;  // delete when handler finished?
92         int emulatefd : 1; // emulate file watch (by re-queueing)
93         int emulate_enabled : 1;   // whether an emulated watch is enabled
94         int child_termd : 1;  // child process has terminated
95
96         prio_queue::handle_t heap_handle;
97         int priority;
98
99         static void set_priority(base_watcher &p, int prio)
100         {
101             p.priority = prio;
102         }
103
104         // Perform initialisation necessary before registration with an event loop
105         void init()
106         {
107             active = false;
108             deleteme = false;
109             emulatefd = false;
110             emulate_enabled = false;
111             child_termd = false;
112             prio_queue::init_handle(heap_handle);
113             priority = DEFAULT_PRIORITY;
114         }
115
116         base_watcher(watch_type_t wt) noexcept : watchType(wt) { }
117         base_watcher(const base_watcher &) = delete;
118         base_watcher &operator=(const base_watcher &) = delete;
119
120         // The dispatch function is called to process a watcher's callback. It is the "real" callback
121         // function; it usually delegates to a user-provided callback.
122         virtual void dispatch(void *loop_ptr) noexcept { };
123
124         // Bi-directional file descriptor watches have a secondary dispatch function for the secondary
125         // watcher (i.e. the output watcher):
126         virtual void dispatch_second(void *loop_ptr) noexcept { }
127
128         virtual ~base_watcher() noexcept { }
129
130         // Called when the watcher has been removed.
131         // It is guaranteed by the caller that:
132         // - the dispatch method is not currently running
133         // - the dispatch method will not be called.
134         virtual void watch_removed() noexcept
135         {
136             // Later: the "delete" behaviour could be dependent on a flag, perhaps?
137             // delete this;
138         }
139     };
140
141     // Base signal event - not part of public API
142     template <typename T_Mutex, typename T_Sigdata>
143     class base_signal_watcher : public base_watcher
144     {
145         template <typename, typename, typename> friend class event_dispatch;
146         template <typename, typename> friend class dasynq::event_loop;
147
148         protected:
149         T_Sigdata siginfo;
150         base_signal_watcher() : base_watcher(watch_type_t::SIGNAL) { }
151
152         public:
153         using siginfo_t = T_Sigdata;
154         typedef siginfo_t &siginfo_p;
155     };
156
157     template <typename T_Mutex>
158     class base_fd_watcher : public base_watcher
159     {
160         template <typename, typename, typename> friend class event_dispatch;
161         template <typename, typename> friend class dasynq::event_loop;
162
163         protected:
164         int watch_fd;
165
166         // These flags are protected by the loop's internal lock:
167         int watch_flags;  // events being watched
168         int event_flags;  // events pending (queued)
169
170         // watch_flags: for a regular fd_watcher, this specifies the events that the watcher
171         //              is watching (or was watching if disabled). For a bidi_fd_watcher, specifies
172         //              the events that the watcher is currently watching (i.e. specifies which
173         //              halves of the Bidi watcher are enabled).
174
175         base_fd_watcher() noexcept : base_watcher(watch_type_t::FD) { }
176     };
177
178     template <typename T_Mutex>
179     class base_bidi_fd_watcher : public base_fd_watcher<T_Mutex>
180     {
181         template <typename, typename, typename> friend class event_dispatch;
182         template <typename, typename> friend class dasynq::event_loop;
183
184         protected:
185
186         // The main instance is the "input" watcher only; we keep a secondary watcher with a secondary set
187         // of flags for the "output" watcher. Note that some of the flags in the secondary watcher aren't
188         // used; it exists mainly so that it can be queued independently of the primary watcher.
189         base_watcher out_watcher {watch_type_t::SECONDARYFD};
190
191         int read_removed : 1; // read watch removed?
192         int write_removed : 1; // write watch removed?
193     };
194
195     template <typename T_Mutex>
196     class base_child_watcher : public base_watcher
197     {
198         template <typename, typename, typename> friend class event_dispatch;
199         template <typename, typename> friend class dasynq::event_loop;
200
201         protected:
202         pid_watch_handle_t watch_handle;
203         pid_t watch_pid;
204         int child_status;
205
206         base_child_watcher() : base_watcher(watch_type_t::CHILD) { }
207     };
208
209
210     template <typename T_Mutex>
211     class base_timer_watcher : public base_watcher
212     {
213         template <typename, typename, typename> friend class event_dispatch;
214         template <typename, typename> friend class dasynq::event_loop;
215
216         protected:
217         timer_handle_t timer_handle;
218         int intervals;
219         clock_type clock;
220
221         base_timer_watcher() : base_watcher(watch_type_t::TIMER)
222         {
223             init_timer_handle(timer_handle);
224         }
225     };
226 } // dprivate
227 } // dasynq