93918cc14dc9146bf738d5337e9f4f724ae77a9f
[ira/wip.git] / lib / tevent / tevent.c
1 /* 
2    Unix SMB/CIFS implementation.
3    main select loop and event handling
4    Copyright (C) Andrew Tridgell 2003
5    
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15    
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 /*
21   PLEASE READ THIS BEFORE MODIFYING!
22
23   This module is a general abstraction for the main select loop and
24   event handling. Do not ever put any localised hacks in here, instead
25   register one of the possible event types and implement that event
26   somewhere else.
27
28   There are 2 types of event handling that are handled in this module:
29
30   1) a file descriptor becoming readable or writeable. This is mostly
31      used for network sockets, but can be used for any type of file
32      descriptor. You may only register one handler for each file
33      descriptor/io combination or you will get unpredictable results
34      (this means that you can have a handler for read events, and a
35      separate handler for write events, but not two handlers that are
36      both handling read events)
37
38   2) a timed event. You can register an event that happens at a
39      specific time.  You can register as many of these as you
40      like. They are single shot - add a new timed event in the event
41      handler to get another event.
42
43   To setup a set of events you first need to create a event_context
44   structure using the function tevent_context_init(); This returns a
45   'struct tevent_context' that you use in all subsequent calls.
46
47   After that you can add/remove events that you are interested in
48   using tevent_add_*() and talloc_free()
49
50   Finally, you call tevent_loop_wait_once() to block waiting for one of the
51   events to occor or tevent_loop_wait() which will loop
52   forever.
53
54 */
55 #include "replace.h"
56 #include "tevent.h"
57 #include "tevent_internal.h"
58 #include "tevent_util.h"
59
60 struct tevent_ops_list {
61         struct tevent_ops_list *next, *prev;
62         const char *name;
63         const struct tevent_ops *ops;
64 };
65
66 /* list of registered event backends */
67 static struct tevent_ops_list *tevent_backends = NULL;
68 static char *tevent_default_backend = NULL;
69
70 /*
71   register an events backend
72 */
73 bool tevent_register_backend(const char *name, const struct tevent_ops *ops)
74 {
75         struct tevent_ops_list *e;
76
77         for (e = tevent_backends; e != NULL; e = e->next) {
78                 if (0 == strcmp(e->name, name)) {
79                         /* already registered, skip it */
80                         return true;
81                 }
82         }
83
84         e = talloc(talloc_autofree_context(), struct tevent_ops_list);
85         if (e == NULL) return false;
86
87         e->name = name;
88         e->ops = ops;
89         DLIST_ADD(tevent_backends, e);
90
91         return true;
92 }
93
94 /*
95   set the default event backend
96  */
97 void tevent_set_default_backend(const char *backend)
98 {
99         talloc_free(tevent_default_backend);
100         tevent_default_backend = talloc_strdup(talloc_autofree_context(),
101                                                backend);
102 }
103
104 /*
105   initialise backends if not already done
106 */
107 static void tevent_backend_init(void)
108 {
109         tevent_select_init();
110         tevent_standard_init();
111 #ifdef HAVE_EPOLL
112         tevent_epoll_init();
113 #endif
114 #ifdef HAVE_LINUX_AIO
115         tevent_aio_init();
116 #endif
117 }
118
119 /*
120   list available backends
121 */
122 const char **tevent_backend_list(TALLOC_CTX *mem_ctx)
123 {
124         const char **list = NULL;
125         struct tevent_ops_list *e;
126
127         tevent_backend_init();
128
129         for (e=tevent_backends;e;e=e->next) {
130                 list = ev_str_list_add(list, e->name);
131         }
132
133         talloc_steal(mem_ctx, list);
134
135         return list;
136 }
137
138 /*
139   create a event_context structure for a specific implemementation.
140   This must be the first events call, and all subsequent calls pass
141   this event_context as the first element. Event handlers also
142   receive this as their first argument.
143
144   This function is for allowing third-party-applications to hook in gluecode
145   to their own event loop code, so that they can make async usage of our client libs
146
147   NOTE: use tevent_context_init() inside of samba!
148 */
149 static struct tevent_context *tevent_context_init_ops(TALLOC_CTX *mem_ctx,
150                                                       const struct tevent_ops *ops)
151 {
152         struct tevent_context *ev;
153         int ret;
154
155         ev = talloc_zero(mem_ctx, struct tevent_context);
156         if (!ev) return NULL;
157
158         ev->ops = ops;
159
160         ret = ev->ops->context_init(ev);
161         if (ret != 0) {
162                 talloc_free(ev);
163                 return NULL;
164         }
165
166         return ev;
167 }
168
169 /*
170   create a event_context structure. This must be the first events
171   call, and all subsequent calls pass this event_context as the first
172   element. Event handlers also receive this as their first argument.
173 */
174 struct tevent_context *tevent_context_init_byname(TALLOC_CTX *mem_ctx,
175                                                   const char *name)
176 {
177         struct tevent_ops_list *e;
178
179         tevent_backend_init();
180
181         if (name == NULL) {
182                 name = tevent_default_backend;
183         }
184         if (name == NULL) {
185                 name = "standard";
186         }
187
188         for (e=tevent_backends;e;e=e->next) {
189                 if (strcmp(name, e->name) == 0) {
190                         return tevent_context_init_ops(mem_ctx, e->ops);
191                 }
192         }
193         return NULL;
194 }
195
196
197 /*
198   create a event_context structure. This must be the first events
199   call, and all subsequent calls pass this event_context as the first
200   element. Event handlers also receive this as their first argument.
201 */
202 struct tevent_context *tevent_context_init(TALLOC_CTX *mem_ctx)
203 {
204         return tevent_context_init_byname(mem_ctx, NULL);
205 }
206
207 /*
208   add a fd based event
209   return NULL on failure (memory allocation error)
210
211   if flags contains TEVENT_FD_AUTOCLOSE then the fd will be closed when
212   the returned fd_event context is freed
213 */
214 struct tevent_fd *_tevent_add_fd(struct tevent_context *ev,
215                                  TALLOC_CTX *mem_ctx,
216                                  int fd,
217                                  uint16_t flags,
218                                  tevent_fd_handler_t handler,
219                                  void *private_data,
220                                  const char *handler_name,
221                                  const char *location)
222 {
223         return ev->ops->add_fd(ev, mem_ctx, fd, flags, handler, private_data,
224                                handler_name, location);
225 }
226
227 /*
228   add a disk aio event
229 */
230 struct tevent_aio *_tevent_add_aio(struct tevent_context *ev,
231                                    TALLOC_CTX *mem_ctx,
232                                    struct iocb *iocb,
233                                    tevent_aio_handler_t handler,
234                                    void *private_data,
235                                    const char *handler_name,
236                                    const char *location)
237 {
238         if (ev->ops->add_aio == NULL) return NULL;
239         return ev->ops->add_aio(ev, mem_ctx, iocb, handler, private_data,
240                                 handler_name, location);
241 }
242
243 /*
244   set a close function on the fd event
245 */
246 void tevent_fd_set_close_fn(struct tevent_fd *fde,
247                             tevent_fd_close_fn_t close_fn)
248 {
249         if (!fde) return;
250         fde->event_ctx->ops->set_fd_close_fn(fde, close_fn);
251 }
252
253 /*
254   return the fd event flags
255 */
256 uint16_t tevent_fd_get_flags(struct tevent_fd *fde)
257 {
258         if (!fde) return 0;
259         return fde->event_ctx->ops->get_fd_flags(fde);
260 }
261
262 /*
263   set the fd event flags
264 */
265 void tevent_fd_set_flags(struct tevent_fd *fde, uint16_t flags)
266 {
267         if (!fde) return;
268         fde->event_ctx->ops->set_fd_flags(fde, flags);
269 }
270
271 /*
272   add a timer event
273   return NULL on failure
274 */
275 struct tevent_timer *_tevent_add_timer(struct tevent_context *ev,
276                                        TALLOC_CTX *mem_ctx,
277                                        struct timeval next_event,
278                                        tevent_timer_handler_t handler,
279                                        void *private_data,
280                                        const char *handler_name,
281                                        const char *location)
282 {
283         return ev->ops->add_timer(ev, mem_ctx, next_event, handler, private_data,
284                                   handler_name, location);
285 }
286
287 /*
288   add a signal event
289
290   sa_flags are flags to sigaction(2)
291
292   return NULL on failure
293 */
294 struct tevent_signal *_tevent_add_signal(struct tevent_context *ev,
295                                          TALLOC_CTX *mem_ctx,
296                                          int signum,
297                                          int sa_flags,
298                                          tevent_signal_handler_t handler,
299                                          void *private_data,
300                                          const char *handler_name,
301                                          const char *location)
302 {
303         return ev->ops->add_signal(ev, mem_ctx, signum, sa_flags, handler, private_data,
304                                    handler_name, location);
305 }
306
307 /*
308   do a single event loop using the events defined in ev 
309 */
310 int tevent_loop_once(struct tevent_context *ev)
311 {
312         return ev->ops->loop_once(ev);
313 }
314
315 /*
316   return on failure or (with 0) if all fd events are removed
317 */
318 int tevent_loop_wait(struct tevent_context *ev)
319 {
320         return ev->ops->loop_wait(ev);
321 }