1 /*
2  * Copyright (C) 2010-2020 Red Hat, Inc.
3  *
4  * Author: Angus Salkeld <asalkeld@redhat.com>
5  *
6  * This file is part of libqb.
7  *
8  * libqb is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation, either version 2.1 of the License, or
11  * (at your option) any later version.
12  *
13  * libqb is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
20  */
21 #ifndef QB_LOOP_H_DEFINED
22 #define QB_LOOP_H_DEFINED
23 
24 /* *INDENT-OFF* */
25 #ifdef __cplusplus
26 extern "C" {
27 #endif
28 /* *INDENT-ON* */
29 
30 #include <signal.h>
31 #include <stdint.h>
32 #include <poll.h>  /* make POLLIN etc. readily available */
33 
34 /**
35  * @file qbloop.h
36  *
37  * Main loop manages timers, jobs and polling sockets.
38  *
39  * Only a weaker sense of priorities is implemented, alluding to distinct
40  * set of pros and cons compared to the stronger, strict approach to them
41  * as widely applied in this problem space (since the latter gives the
42  * application more control as the effect of the former can still be
43  * achieved with some reductions, whereas it is not straightforward the
44  * other way around; cf. static priority task scheduling vs. relative
45  * fine-tuning within a single priority domain with nice(2)):
46  *
47  * + implicit mitigation for deadlock-prone priority arrangements
48  *
49  * - less predictable (proportional probability based, we can talk
50  *   about an advisory effect of the priorities) responses to the arrival
51  *   of the high-ranked events (i.e. in the process of the picking the next
52  *   event to handle from the priority queue when at least two different
53  *   priorities are eligible at the moment)
54  *
55  * One practical application for this module of libqb is in combination with
56  * IPC servers based on qbipcs.h published one (the #qb_ipcs_poll_handlers
57  * structure maps fittingly to the control functions published here).
58  *
59  * @example tcpserver.c
60  */
61 
62 
63 /**
64  * Priorites for jobs, timers & poll
65  */
66 enum qb_loop_priority {
67 	QB_LOOP_LOW = 0,
68 	QB_LOOP_MED = 1,
69 	QB_LOOP_HIGH = 2,
70 };
71 
72 /**
73  * An opaque data type representing the main loop.
74  */
75 typedef struct qb_loop qb_loop_t;
76 
77 typedef uint64_t qb_loop_timer_handle;
78 
79 typedef void *qb_loop_signal_handle;
80 
81 typedef int32_t (*qb_loop_poll_dispatch_fn) (int32_t fd, int32_t revents, void *data);
82 typedef void (*qb_loop_job_dispatch_fn)(void *data);
83 typedef void (*qb_loop_timer_dispatch_fn)(void *data);
84 typedef int32_t (*qb_loop_signal_dispatch_fn)(int32_t rsignal, void *data);
85 
86 typedef void (*qb_loop_poll_low_fds_event_fn) (int32_t not_enough, int32_t fds_available);
87 
88 /**
89  * Create a new main loop.
90  *
91  * @return loop instance.
92  */
93 qb_loop_t * qb_loop_create(void);
94 
95 /**
96  *
97  */
98 void qb_loop_destroy(struct qb_loop * l);
99 
100 /**
101  * Stop the main loop.
102  * @param l pointer to the loop instance
103  */
104 void qb_loop_stop(qb_loop_t *l);
105 
106 /**
107  * Run the main loop.
108  *
109  * @param l pointer to the loop instance
110  */
111 void qb_loop_run(qb_loop_t *l);
112 
113 
114 /**
115  * Add a job to the mainloop.
116  *
117  * This is run in the next cycle of the loop.
118  * @note it is a one-shot job.
119  *
120  * @param l pointer to the loop instance
121  * @param p the priority
122  * @param data user data passed into the dispatch function
123  * @param dispatch_fn callback function
124  * @return status (0 == ok, -errno == failure)
125  */
126 int32_t qb_loop_job_add(qb_loop_t *l,
127 			enum qb_loop_priority p,
128 			void *data,
129 			qb_loop_job_dispatch_fn dispatch_fn);
130 
131 
132 /**
133  * Delete a job from the mainloop.
134  *
135  * This will try to delete the job if it hasn't run yet.
136  *
137  * @note this will remove the first job that matches the
138  * parameters (priority, data, dispatch_fn).
139  *
140  * @param l pointer to the loop instance
141  * @param p the priority
142  * @param data user data passed into the dispatch function
143  * @param dispatch_fn callback function
144  * @return status (0 == ok, -errno == failure)
145  */
146 int32_t qb_loop_job_del(struct qb_loop *l,
147 			enum qb_loop_priority p,
148 			void *data,
149 			qb_loop_job_dispatch_fn dispatch_fn);
150 
151 /**
152  * Add a timer to the mainloop.
153  * @note it is a one-shot job.
154  *
155  * @param l pointer to the loop instance
156  * @param p the priority
157  * @param nsec_duration nano-secs in the future to run the dispatch.
158  * @param data user data passed into the dispatch function
159  * @param dispatch_fn callback function
160  * @param timer_handle_out handle to delete the timer if needed.
161  * @return status (0 == ok, -errno == failure)
162  */
163 int32_t qb_loop_timer_add(qb_loop_t *l,
164 			  enum qb_loop_priority p,
165 			  uint64_t nsec_duration,
166 			  void *data,
167 			  qb_loop_timer_dispatch_fn dispatch_fn,
168 			  qb_loop_timer_handle * timer_handle_out);
169 
170 /**
171  * Delete a timer that is still outstanding.
172  *
173  * @param l pointer to the loop instance
174  * @param th handle to delete the timer if needed.
175  * @return status (0 == ok, -errno == failure)
176  */
177 int32_t qb_loop_timer_del(qb_loop_t *l, qb_loop_timer_handle th);
178 
179 /**
180  * Check to see if a timer that is still outstanding.
181  *
182  * @param l pointer to the loop instance
183  * @param th handle to delete the timer if needed.
184  * @retval QB_TRUE yes this timer is outstanding
185  * @retval QB_FALSE this timer does not exist or has expired
186  */
187 int32_t qb_loop_timer_is_running(qb_loop_t *l, qb_loop_timer_handle th);
188 
189 /**
190  * Get the expiration time of the timer, as set when the timer was created
191  *
192  * @note if the timer has already expired it will return 0
193  *
194  * @param l pointer to the loop instance
195  * @param th timer handle.
196  * @return nano seconds at which the timer will expire
197  */
198 uint64_t qb_loop_timer_expire_time_get(struct qb_loop *l, qb_loop_timer_handle th);
199 
200 /**
201  * Get the time remaining before the timer expires
202  *
203  * @note if the timer has already expired it will return 0
204  *
205  * @param l pointer to the loop instance
206  * @param th timer handle.
207  * @return nano seconds remaining until the timer expires
208  */
209 uint64_t qb_loop_timer_expire_time_remaining(struct qb_loop *l, qb_loop_timer_handle th);
210 
211 /**
212  * Set a callback to receive events on file descriptors
213  * getting low.
214  * @param l pointer to the loop instance
215  * @param fn callback function.
216  * @return status (0 == ok, -errno == failure)
217  */
218 int32_t qb_loop_poll_low_fds_event_set(qb_loop_t *l,
219 				       qb_loop_poll_low_fds_event_fn fn);
220 
221 /**
222  * Add a poll job to the mainloop.
223  * @note it is a re-occurring job.
224  *
225  * @param l pointer to the loop instance
226  * @param p the priority
227  * @param fd file descriptor.
228  * @param events (POLLIN|POLLOUT) etc ....
229  * @param data user data passed into the dispatch function
230  * @param dispatch_fn callback function
231  * @return status (0 == ok, -errno == failure)
232  */
233 int32_t qb_loop_poll_add(qb_loop_t *l,
234 			     enum qb_loop_priority p,
235 			     int32_t fd,
236 			     int32_t events,
237 			     void *data,
238 			     qb_loop_poll_dispatch_fn dispatch_fn);
239 
240 /**
241  * Modify a poll job.
242  *
243  * @param l pointer to the loop instance
244  * @param p the priority
245  * @param fd file descriptor.
246  * @param events (POLLIN|POLLOUT) etc ....
247  * @param data user data passed into the dispatch function
248  * @param dispatch_fn callback function
249  * @return status (0 == ok, -errno == failure)
250  */
251 int32_t qb_loop_poll_mod(qb_loop_t *l,
252 			 enum qb_loop_priority p,
253 			 int32_t fd,
254 			 int32_t events,
255 			 void *data,
256 			 qb_loop_poll_dispatch_fn dispatch_fn);
257 
258 /**
259  * Delete a poll job.
260  *
261  * @param l pointer to the loop instance
262  * @param fd file descriptor.
263  * @return status (0 == ok, -errno == failure)
264  */
265 int32_t qb_loop_poll_del(qb_loop_t *l, int32_t fd);
266 
267 /**
268  * Add a signal job.
269  *
270  * Get a callback on this signal (not in the context of the signal).
271  *
272  * @param l pointer to the loop instance
273  * @param p the priority
274  * @param sig (SIGHUP or SIGINT) etc ....
275  * @param data user data passed into the dispatch function
276  * @param dispatch_fn callback function
277  * @param handle (out) a reference to the signal job
278  * @return status (0 == ok, -errno == failure)
279  */
280 int32_t qb_loop_signal_add(qb_loop_t *l,
281 			   enum qb_loop_priority p,
282 			   int32_t sig,
283 			   void *data,
284 			   qb_loop_signal_dispatch_fn dispatch_fn,
285 			   qb_loop_signal_handle *handle);
286 
287 /**
288  * Modify the signal job
289  *
290  * @param l pointer to the loop instance
291  * @param p the priority
292  * @param sig (SIGHUP or SIGINT) etc ....
293  * @param data user data passed into the dispatch function
294  * @param dispatch_fn callback function
295  * @param handle (in) a reference to the signal job
296  * @return status (0 == ok, -errno == failure)
297  */
298 int32_t qb_loop_signal_mod(qb_loop_t *l,
299 			   enum qb_loop_priority p,
300 			   int32_t sig,
301 			   void *data,
302 			   qb_loop_signal_dispatch_fn dispatch_fn,
303 			   qb_loop_signal_handle handle);
304 
305 /**
306  * Delete the signal job.
307  *
308  * @param l pointer to the loop instance
309  * @param handle (in) a reference to the signal job
310  * @return status (0 == ok, -errno == failure)
311  */
312 int32_t qb_loop_signal_del(qb_loop_t *l,
313 			   qb_loop_signal_handle handle);
314 
315 /* *INDENT-OFF* */
316 #ifdef __cplusplus
317 }
318 #endif /* __cplusplus */
319 /* *INDENT-ON* */
320 #endif /* QB_LOOP_H_DEFINED */
321 
322