HACK tevent_standard no epoll
[metze/samba/wip.git] / lib / tevent / tevent_standard.c
1 /* 
2    Unix SMB/CIFS implementation.
3    main select loop and event handling
4    Copyright (C) Stefan Metzmacher      2013
5    Copyright (C) Jeremy Allison         2013
6
7      ** NOTE! The following LGPL license applies to the tevent
8      ** library. This does NOT imply that all of Samba is released
9      ** under the LGPL
10
11    This library is free software; you can redistribute it and/or
12    modify it under the terms of the GNU Lesser General Public
13    License as published by the Free Software Foundation; either
14    version 3 of the License, or (at your option) any later version.
15
16    This library is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19    Lesser General Public License for more details.
20
21    You should have received a copy of the GNU Lesser General Public
22    License along with this library; if not, see <http://www.gnu.org/licenses/>.
23 */
24
25 /*
26   This is SAMBA's default event loop code
27
28   - we try to use epoll if configure detected support for it
29     otherwise we use poll()
30   - if epoll is broken on the system or the kernel doesn't support it
31     at runtime we fallback to poll()
32 */
33
34 #include "replace.h"
35 #include "tevent.h"
36 #include "tevent_util.h"
37 #include "tevent_internal.h"
38
39 struct std_event_glue {
40         const struct tevent_ops *epoll_ops;
41         const struct tevent_ops *poll_ops;
42         struct tevent_ops *glue_ops;
43         bool fallback_replay;
44 };
45
46 static int std_event_context_init(struct tevent_context *ev);
47
48 static const struct tevent_ops std_event_ops = {
49         .context_init           = std_event_context_init,
50 };
51
52 /*
53   If this function gets called. epoll failed at runtime.
54   Move us to using poll instead. If we return false here,
55   caller should abort().
56 */
57 #ifdef HAVE_EPOLL
58 static bool std_fallback_to_poll(struct tevent_context *ev, bool replay)
59 {
60         void *glue_ptr = talloc_parent(ev->ops);
61         struct std_event_glue *glue =
62                 talloc_get_type_abort(glue_ptr,
63                 struct std_event_glue);
64         int ret;
65         struct tevent_fd *fde;
66         struct tevent_fd *fde_next;
67
68         glue->fallback_replay = replay;
69
70         /* First switch all the ops to poll. */
71         glue->epoll_ops = NULL;
72
73         /*
74          * Set custom_ops the same as poll.
75          */
76         *glue->glue_ops = *glue->poll_ops;
77         glue->glue_ops->context_init = std_event_context_init;
78
79         /* Next initialize the poll backend. */
80         ret = glue->poll_ops->context_init(ev);
81         if (ret != 0) {
82                 return false;
83         }
84
85         /*
86          * Now we have to change all the existing file descriptor
87          * events from the epoll backend to the poll backend.
88          */
89         for (fde = ev->fd_events; fde; fde = fde_next) {
90                 /*
91                  * We must remove this fde off the ev->fd_events list.
92                  */
93                 fde_next = fde->next;
94
95                 /* Remove from the ev->fd_events list. */
96                 DLIST_REMOVE(ev->fd_events, fde);
97
98                 /* Re-add this event as a poll backend event. */
99                 tevent_poll_event_add_fd_internal(ev, fde);
100         }
101
102         return true;
103 }
104 #endif
105
106 static int std_event_loop_once(struct tevent_context *ev, const char *location)
107 {
108         void *glue_ptr = talloc_parent(ev->ops);
109         struct std_event_glue *glue =
110                 talloc_get_type_abort(glue_ptr,
111                 struct std_event_glue);
112         int ret;
113
114         ret = glue->epoll_ops->loop_once(ev, location);
115         /*
116          * If the above hasn't panicked due to an epoll interface failure,
117          * std_fallback_to_poll() wasn't called, and hasn't cleared epoll_ops to
118          * signify fallback to poll_ops.
119          */
120         if (glue->epoll_ops != NULL) {
121                 /* No fallback */
122                 return ret;
123         }
124
125         if (!glue->fallback_replay) {
126                 /*
127                  * The problem happened while modifying an event.
128                  * An event handler was triggered in this case
129                  * and there is no need to call loop_once() again.
130                  */
131                 return ret;
132         }
133
134         return glue->poll_ops->loop_once(ev, location);
135 }
136
137 static int std_event_loop_wait(struct tevent_context *ev, const char *location)
138 {
139         void *glue_ptr = talloc_parent(ev->ops);
140         struct std_event_glue *glue =
141                 talloc_get_type_abort(glue_ptr,
142                 struct std_event_glue);
143         int ret;
144
145         ret = glue->epoll_ops->loop_wait(ev, location);
146         /*
147          * If the above hasn't panicked due to an epoll interface failure,
148          * std_fallback_to_poll() wasn't called, and hasn't cleared epoll_ops to
149          * signify fallback to poll_ops.
150          */
151         if (glue->epoll_ops != NULL) {
152                 /* No fallback */
153                 return ret;
154         }
155
156         return glue->poll_ops->loop_wait(ev, location);
157 }
158 /*
159   Initialize the epoll backend and allow it to call a
160   switch function if epoll fails at runtime.
161 */
162 static int std_event_context_init(struct tevent_context *ev)
163 {
164         struct std_event_glue *glue;
165         int ret;
166
167         /*
168          * If this is the first initialization
169          * we need to set up the allocated ops
170          * pointers.
171          */
172
173         if (ev->ops == &std_event_ops) {
174                 glue = talloc_zero(ev, struct std_event_glue);
175                 if (glue == NULL) {
176                         return -1;
177                 }
178
179                 //glue->epoll_ops = tevent_find_ops_byname("epoll");
180
181                 glue->poll_ops = tevent_find_ops_byname("poll");
182                 if (glue->poll_ops == NULL) {
183                         return -1;
184                 }
185
186                 /*
187                  * Allocate space for our custom ops.
188                  * Allocate as a child of our epoll_ops pointer
189                  * so we can easily get to it using talloc_parent.
190                  */
191                 glue->glue_ops = talloc_zero(glue, struct tevent_ops);
192                 if (glue->glue_ops == NULL) {
193                         talloc_free(glue);
194                         return -1;
195                 }
196
197                 ev->ops = glue->glue_ops;
198         } else {
199                 void *glue_ptr = talloc_parent(ev->ops);
200                 glue = talloc_get_type_abort(glue_ptr, struct std_event_glue);
201         }
202
203         if (glue->epoll_ops != NULL) {
204                 /*
205                  * Set custom_ops the same as epoll,
206                  * except re-init using std_event_context_init()
207                  * and use std_event_loop_once() to add the
208                  * ability to fallback to a poll backend on
209                  * epoll runtime error.
210                  */
211                 *glue->glue_ops = *glue->epoll_ops;
212                 glue->glue_ops->context_init = std_event_context_init;
213                 glue->glue_ops->loop_once = std_event_loop_once;
214                 glue->glue_ops->loop_wait = std_event_loop_wait;
215
216                 ret = glue->epoll_ops->context_init(ev);
217                 if (ret == -1) {
218                         goto fallback;
219                 }
220 #ifdef HAVE_EPOLL
221                 tevent_epoll_set_panic_fallback(ev, std_fallback_to_poll);
222 #endif
223
224                 return ret;
225         }
226
227 fallback:
228         glue->epoll_ops = NULL;
229
230         /*
231          * Set custom_ops the same as poll.
232          */
233         *glue->glue_ops = *glue->poll_ops;
234         glue->glue_ops->context_init = std_event_context_init;
235
236         return glue->poll_ops->context_init(ev);
237 }
238
239 _PRIVATE_ bool tevent_standard_init(void)
240 {
241         return tevent_register_backend("standard", &std_event_ops);
242 }