1 // Dasynq: early declarations and base watchers.
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.
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.
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)
19 pthread_sigmask(how, set, oset);
22 template <> inline void sigmaskf<null_mutex>(int how, const sigset_t *set, sigset_t *oset)
24 sigprocmask(how, set, oset);
28 // A template to generate suitable default loop traits for a given type of mutex:
29 template <typename T_Mutex> class default_traits
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;
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)
40 dprivate::sigmaskf<T_Mutex>(how, set, oset);
44 // Forward declarations:
45 template <typename T_Mutex, typename Traits = default_traits<T_Mutex>>
49 constexpr int DEFAULT_PRIORITY = 50;
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>;
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;
74 using node_type = std::conditional<use_empty_node, empty_node, base_watcher *>::type;
77 using prio_queue = heap_def<node_type, int>;
79 using prio_queue_emptynode = heap_def<empty_node, int>;
80 using prio_queue_bwnode = heap_def<base_watcher *, int>;
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;
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;
94 enum class watch_type_t
103 template <typename Traits, typename LoopTraits> class event_dispatch;
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;
110 // Represents a queued event notification. Various event watchers derive from this type.
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
121 prio_queue::handle_t heap_handle;
124 static void set_priority(base_watcher &p, int prio)
129 // Perform initialisation necessary before registration with an event loop
135 emulate_enabled = false;
137 prio_queue::init_handle(heap_handle);
138 priority = DEFAULT_PRIORITY;
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;
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 { };
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 { }
153 virtual ~base_watcher() noexcept { }
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
161 // Later: the "delete" behaviour could be dependent on a flag, perhaps?
166 // Retrieve watcher from queue handle:
167 inline base_watcher * get_watcher(prio_queue_emptynode &q, prio_queue_emptynode::handle_t &n)
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;
177 inline dprivate::base_watcher * get_watcher(prio_queue_bwnode &q, prio_queue_bwnode::handle_t &n)
179 return q.node_data(n);
182 // Allocate queue handle:
183 inline void allocate_handle(prio_queue_emptynode &q, prio_queue_emptynode::handle_t &n, base_watcher *bw)
188 inline void allocate_handle(prio_queue_bwnode &q, prio_queue_bwnode::handle_t &n, base_watcher *bw)
193 // Base signal event - not part of public API
194 template <typename T_Sigdata>
195 class base_signal_watcher : public base_watcher
197 template <typename, typename> friend class event_dispatch;
198 template <typename, typename> friend class dasynq::event_loop;
202 base_signal_watcher() : base_watcher(watch_type_t::SIGNAL) { }
205 using siginfo_t = T_Sigdata;
206 typedef siginfo_t &siginfo_p;
209 class base_fd_watcher : public base_watcher
211 template <typename, typename> friend class event_dispatch;
212 template <typename, typename> friend class dasynq::event_loop;
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)
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).
226 base_fd_watcher() noexcept : base_watcher(watch_type_t::FD) { }
229 class base_bidi_fd_watcher : public base_fd_watcher
231 template <typename, typename> friend class event_dispatch;
232 template <typename, typename> friend class dasynq::event_loop;
234 base_bidi_fd_watcher(const base_bidi_fd_watcher &) = delete;
237 base_bidi_fd_watcher() noexcept { }
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};
244 int read_removed : 1; // read watch removed?
245 int write_removed : 1; // write watch removed?
248 class base_child_watcher : public base_watcher
250 template <typename, typename> friend class event_dispatch;
251 template <typename, typename> friend class dasynq::event_loop;
254 pid_watch_handle_t watch_handle;
258 base_child_watcher() : base_watcher(watch_type_t::CHILD) { }
262 class base_timer_watcher : public base_watcher
264 template <typename, typename> friend class event_dispatch;
265 template <typename, typename> friend class dasynq::event_loop;
268 timer_handle_t timer_handle;
272 base_timer_watcher() : base_watcher(watch_type_t::TIMER)
274 init_timer_handle(timer_handle);