Run only one event for each epoll_wait/select call
[metze/ctdb/wip.git] / lib / events / events_select.c
1 /* 
2    Unix SMB/CIFS implementation.
3    main select loop and event handling
4    Copyright (C) Andrew Tridgell        2003-2005
5    Copyright (C) Stefan Metzmacher      2005
6    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 /*
22   This is SAMBA's default event loop code
23
24 */
25
26 #include "includes.h"
27 #include "system/time.h"
28 #include "system/filesys.h"
29 #include "system/select.h"
30 #include "lib/util/dlinklist.h"
31 #include "lib/events/events.h"
32 #include "lib/events/events_internal.h"
33
34 extern pid_t ctdbd_pid;
35
36 struct select_event_context {
37         /* a pointer back to the generic event_context */
38         struct event_context *ev;
39
40         /* list of filedescriptor events */
41         struct fd_event *fd_events;
42
43         /* list of timed events */
44         struct timed_event *timed_events;
45
46         /* the maximum file descriptor number in fd_events */
47         int maxfd;
48
49         /* information for exiting from the event loop */
50         int exit_code;
51
52         /* this is incremented when the loop over events causes something which
53            could change the events yet to be processed */
54         uint32_t destruction_count;
55 };
56
57 /*
58   create a select_event_context structure.
59 */
60 static int select_event_context_init(struct event_context *ev)
61 {
62         struct select_event_context *select_ev;
63
64         select_ev = talloc_zero(ev, struct select_event_context);
65         if (!select_ev) return -1;
66         select_ev->ev = ev;
67
68         ev->additional_data = select_ev;
69         return 0;
70 }
71
72 /*
73   recalculate the maxfd
74 */
75 static void calc_maxfd(struct select_event_context *select_ev)
76 {
77         struct fd_event *fde;
78
79         select_ev->maxfd = 0;
80         for (fde = select_ev->fd_events; fde; fde = fde->next) {
81                 if (fde->fd > select_ev->maxfd) {
82                         select_ev->maxfd = fde->fd;
83                 }
84         }
85 }
86
87
88 /* to mark the ev->maxfd invalid
89  * this means we need to recalculate it
90  */
91 #define EVENT_INVALID_MAXFD (-1)
92
93 /*
94   destroy an fd_event
95 */
96 static int select_event_fd_destructor(struct fd_event *fde)
97 {
98         struct event_context *ev = fde->event_ctx;
99         struct select_event_context *select_ev = talloc_get_type(ev->additional_data,
100                                                            struct select_event_context);
101
102         if (select_ev->maxfd == fde->fd) {
103                 select_ev->maxfd = EVENT_INVALID_MAXFD;
104         }
105
106         DLIST_REMOVE(select_ev->fd_events, fde);
107         select_ev->destruction_count++;
108
109         if (fde->flags & EVENT_FD_AUTOCLOSE) {
110                 close(fde->fd);
111                 fde->fd = -1;
112         }
113
114         return 0;
115 }
116
117 /*
118   add a fd based event
119   return NULL on failure (memory allocation error)
120 */
121 static struct fd_event *select_event_add_fd(struct event_context *ev, TALLOC_CTX *mem_ctx,
122                                          int fd, uint16_t flags,
123                                          event_fd_handler_t handler,
124                                          void *private_data)
125 {
126         struct select_event_context *select_ev = talloc_get_type(ev->additional_data,
127                                                            struct select_event_context);
128         struct fd_event *fde;
129
130         fde = talloc(mem_ctx?mem_ctx:ev, struct fd_event);
131         if (!fde) return NULL;
132
133         fde->event_ctx          = ev;
134         fde->fd                 = fd;
135         fde->flags              = flags;
136         fde->handler            = handler;
137         fde->private_data       = private_data;
138         fde->additional_flags   = 0;
139         fde->additional_data    = NULL;
140
141         DLIST_ADD(select_ev->fd_events, fde);
142         if (fde->fd > select_ev->maxfd) {
143                 select_ev->maxfd = fde->fd;
144         }
145         talloc_set_destructor(fde, select_event_fd_destructor);
146
147         return fde;
148 }
149
150
151 /*
152   return the fd event flags
153 */
154 static uint16_t select_event_get_fd_flags(struct fd_event *fde)
155 {
156         return fde->flags;
157 }
158
159 /*
160   set the fd event flags
161 */
162 static void select_event_set_fd_flags(struct fd_event *fde, uint16_t flags)
163 {
164         struct event_context *ev;
165         struct select_event_context *select_ev;
166
167         if (fde->flags == flags) return;
168
169         ev = fde->event_ctx;
170         select_ev = talloc_get_type(ev->additional_data, struct select_event_context);
171
172         fde->flags = flags;
173 }
174
175 /*
176   event loop handling using select()
177 */
178 static int select_event_loop_select(struct select_event_context *select_ev, struct timeval *tvalp)
179 {
180         fd_set r_fds, w_fds;
181         struct fd_event *fde;
182         int selrtn;
183         uint32_t destruction_count = ++select_ev->destruction_count;
184
185         /* we maybe need to recalculate the maxfd */
186         if (select_ev->maxfd == EVENT_INVALID_MAXFD) {
187                 calc_maxfd(select_ev);
188         }
189
190         FD_ZERO(&r_fds);
191         FD_ZERO(&w_fds);
192
193         /* setup any fd events */
194         for (fde = select_ev->fd_events; fde; fde = fde->next) {
195                 if (fde->flags & EVENT_FD_READ) {
196                         FD_SET(fde->fd, &r_fds);
197                 }
198                 if (fde->flags & EVENT_FD_WRITE) {
199                         FD_SET(fde->fd, &w_fds);
200                 }
201         }
202
203         if (select_ev->ev->num_signal_handlers && 
204             common_event_check_signal(select_ev->ev)) {
205                 return 0;
206         }
207
208         selrtn = select(select_ev->maxfd+1, &r_fds, &w_fds, NULL, tvalp);
209
210         if (selrtn == -1 && errno == EINTR && 
211             select_ev->ev->num_signal_handlers) {
212                 common_event_check_signal(select_ev->ev);
213                 return 0;
214         }
215
216         if (selrtn == -1 && errno == EBADF) {
217                 /* the socket is dead! this should never
218                    happen as the socket should have first been
219                    made readable and that should have removed
220                    the event, so this must be a bug. This is a
221                    fatal error. */
222                 DEBUG(0,("ERROR: EBADF on select_event_loop_once\n"));
223                 select_ev->exit_code = EBADF;
224                 return -1;
225         }
226
227         if (selrtn == 0 && tvalp) {
228                 /* we don't care about a possible delay here */
229                 common_event_loop_timer_delay(select_ev->ev);
230                 return 0;
231         }
232
233         if (selrtn > 0) {
234                 /* at least one file descriptor is ready - check
235                    which ones and call the handler, being careful to allow
236                    the handler to remove itself when called */
237                 for (fde = select_ev->fd_events; fde; fde = fde->next) {
238                         uint16_t flags = 0;
239
240                         if (FD_ISSET(fde->fd, &r_fds)) flags |= EVENT_FD_READ;
241                         if (FD_ISSET(fde->fd, &w_fds)) flags |= EVENT_FD_WRITE;
242                         if (flags) {
243                                 fde->handler(select_ev->ev, fde, flags, fde->private_data);
244                                 break;
245                         }
246                 }
247         }
248
249         return 0;
250 }               
251
252 /*
253   do a single event loop using the events defined in ev 
254 */
255 static int select_event_loop_once(struct event_context *ev)
256 {
257         struct select_event_context *select_ev = talloc_get_type(ev->additional_data,
258                                                            struct select_event_context);
259         struct timeval tval;
260
261         tval = common_event_loop_timer_delay(ev);
262         if (timeval_is_zero(&tval)) {
263                 return 0;
264         }
265
266         return select_event_loop_select(select_ev, &tval);
267 }
268
269 /*
270   return on failure or (with 0) if all fd events are removed
271 */
272 static int select_event_loop_wait(struct event_context *ev)
273 {
274         static time_t t=0;
275         time_t new_t;
276         struct select_event_context *select_ev = talloc_get_type(ev->additional_data,
277                                                            struct select_event_context);
278         select_ev->exit_code = 0;
279
280         while (select_ev->fd_events && select_ev->exit_code == 0) {
281                 if (select_event_loop_once(ev) != 0) {
282                         break;
283                 }
284                 if (getpid() == ctdbd_pid) {
285                         new_t=time(NULL);
286                         if (t != 0) {
287                                 if (t > new_t) {
288                                         DEBUG(0,(__location__ " ERROR Time skipped backward by %d seconds\n", (int)(t-new_t)));
289                                 }
290                                 /* We assume here that we get at least one event every 5 seconds */
291                                 if (new_t > (t+5)) {
292                                         DEBUG(0,(__location__ " ERROR Time jumped forward by %d seconds\n", (int)(new_t-t)));
293                                 }
294                         }
295                         t=new_t;
296                 }
297         }
298
299         return select_ev->exit_code;
300 }
301
302 static const struct event_ops select_event_ops = {
303         .context_init   = select_event_context_init,
304         .add_fd         = select_event_add_fd,
305         .get_fd_flags   = select_event_get_fd_flags,
306         .set_fd_flags   = select_event_set_fd_flags,
307         .add_timed      = common_event_add_timed,
308         .add_signal     = common_event_add_signal,
309         .loop_once      = select_event_loop_once,
310         .loop_wait      = select_event_loop_wait,
311 };
312
313 bool events_select_init(void)
314 {
315         return event_register_backend("select", &select_event_ops);
316 }
317
318 #if _SAMBA_BUILD_
319 NTSTATUS s4_events_select_init(void)
320 {
321         if (!events_select_init()) {
322                 return NT_STATUS_INTERNAL_ERROR;
323         }
324         return NT_STATUS_OK;
325 }
326 #endif