basewatchers.h 9.9 KB

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