HACK tevent_standard no fallback
[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 static bool std_fallback_to_poll(struct tevent_context *ev, bool replay)
58 {
59         void *glue_ptr = talloc_parent(ev->ops);
60         struct std_event_glue *glue =
61                 talloc_get_type_abort(glue_ptr,
62                 struct std_event_glue);
63         int ret;
64         struct tevent_fd *fde;
65         struct tevent_fd *fde_next;
66
67         glue->fallback_replay = replay;
68
69         /* First switch all the ops to poll. */
70         glue->epoll_ops = NULL;
71
72         /*
73          * Set custom_ops the same as poll.
74          */
75         *glue->glue_ops = *glue->poll_ops;
76         glue->glue_ops->context_init = std_event_context_init;
77
78         /* Next initialize the poll backend. */
79         ret = glue->poll_ops->context_init(ev);
80         if (ret != 0) {
81                 return false;
82         }
83
84         /*
85          * Now we have to change all the existing file descriptor
86          * events from the epoll backend to the poll backend.
87          */
88         for (fde = ev->fd_events; fde; fde = fde_next) {
89                 /*
90                  * We must remove this fde off the ev->fd_events list.
91                  */
92                 fde_next = fde->next;
93
94                 /* Remove from the ev->fd_events list. */
95                 DLIST_REMOVE(ev->fd_events, fde);
96
97                 /* Re-add this event as a poll backend event. */
98                 tevent_poll_event_add_fd_internal(ev, fde);
99         }
100
101         return true;
102 }
103
104 static int std_event_loop_once(struct tevent_context *ev, const char *location)
105 {
106         void *glue_ptr = talloc_parent(ev->ops);
107         struct std_event_glue *glue =
108                 talloc_get_type_abort(glue_ptr,
109                 struct std_event_glue);
110         int ret;
111
112         ret = glue->epoll_ops->loop_once(ev, location);
113         if (glue->epoll_ops != NULL) {
114                 /* No fallback */
115                 return ret;
116         }
117
118         if (!glue->fallback_replay) {
119                 /*
120                  * The problem happened while modifying an event.
121                  * An event handler was triggered in this case
122                  * and there is no need to call loop_once() again.
123                  */
124                 return ret;
125         }
126
127         return glue->poll_ops->loop_once(ev, location);
128 }
129
130 static int std_event_loop_wait(struct tevent_context *ev, const char *location)
131 {
132         void *glue_ptr = talloc_parent(ev->ops);
133         struct std_event_glue *glue =
134                 talloc_get_type_abort(glue_ptr,
135                 struct std_event_glue);
136         int ret;
137
138         ret = glue->epoll_ops->loop_wait(ev, location);
139         if (glue->epoll_ops != NULL) {
140                 /* No fallback */
141                 return ret;
142         }
143
144         return glue->poll_ops->loop_wait(ev, location);
145 }
146 /*
147   Initialize the epoll backend and allow it to call a
148   switch function if epoll fails at runtime.
149 */
150 static int std_event_context_init(struct tevent_context *ev)
151 {
152         struct std_event_glue *glue;
153         int ret;
154
155         /*
156          * If this is the first initialization
157          * we need to set up the allocated ops
158          * pointers.
159          */
160
161         if (ev->ops == &std_event_ops) {
162                 glue = talloc_zero(ev, struct std_event_glue);
163                 if (glue == NULL) {
164                         return -1;
165                 }
166
167                 glue->epoll_ops = tevent_find_ops_byname("epoll");
168
169                 glue->poll_ops = tevent_find_ops_byname("poll");
170                 if (glue->poll_ops == NULL) {
171                         return -1;
172                 }
173
174                 /*
175                  * Allocate space for our custom ops.
176                  * Allocate as a child of our epoll_ops pointer
177                  * so we can easily get to it using talloc_parent.
178                  */
179                 glue->glue_ops = talloc_zero(glue, struct tevent_ops);
180                 if (glue->glue_ops == NULL) {
181                         talloc_free(glue);
182                         return -1;
183                 }
184
185                 ev->ops = glue->glue_ops;
186         } else {
187                 void *glue_ptr = talloc_parent(ev->ops);
188                 glue = talloc_get_type_abort(glue_ptr, struct std_event_glue);
189         }
190
191         if (glue->epoll_ops != NULL) {
192                 /*
193                  * Set custom_ops the same as epoll,
194                  * except re-init using std_event_context_init()
195                  * and use std_event_loop_once() to add the
196                  * ability to fallback to a poll backend on
197                  * epoll runtime error.
198                  */
199                 *glue->glue_ops = *glue->epoll_ops;
200                 glue->glue_ops->context_init = std_event_context_init;
201                 glue->glue_ops->loop_once = std_event_loop_once;
202                 glue->glue_ops->loop_wait = std_event_loop_wait;
203
204                 ret = glue->epoll_ops->context_init(ev);
205                 if (ret == -1) {
206                         goto fallback;
207                 }
208 #if 0 //def HAVE_EPOLL
209                 if (!tevent_epoll_set_panic_fallback(ev, std_fallback_to_poll)) {
210                         TALLOC_FREE(ev->additional_data);
211                         goto fallback;
212                 }
213 #endif
214
215                 return ret;
216         }
217
218 fallback:
219         glue->epoll_ops = NULL;
220
221         /*
222          * Set custom_ops the same as poll.
223          */
224         *glue->glue_ops = *glue->poll_ops;
225         glue->glue_ops->context_init = std_event_context_init;
226
227         return glue->poll_ops->context_init(ev);
228 }
229
230 _PRIVATE_ bool tevent_standard_init(void)
231 {
232         return tevent_register_backend("standard", &std_event_ops);
233 }