tevent: Only build "std_fallback_to_poll" when epoll is around
[sfrench/samba-autobuild/.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         if (glue->epoll_ops != NULL) {
116                 /* No fallback */
117                 return ret;
118         }
119
120         if (!glue->fallback_replay) {
121                 /*
122                  * The problem happened while modifying an event.
123                  * An event handler was triggered in this case
124                  * and there is no need to call loop_once() again.
125                  */
126                 return ret;
127         }
128
129         return glue->poll_ops->loop_once(ev, location);
130 }
131
132 static int std_event_loop_wait(struct tevent_context *ev, const char *location)
133 {
134         void *glue_ptr = talloc_parent(ev->ops);
135         struct std_event_glue *glue =
136                 talloc_get_type_abort(glue_ptr,
137                 struct std_event_glue);
138         int ret;
139
140         ret = glue->epoll_ops->loop_wait(ev, location);
141         if (glue->epoll_ops != NULL) {
142                 /* No fallback */
143                 return ret;
144         }
145
146         return glue->poll_ops->loop_wait(ev, location);
147 }
148 /*
149   Initialize the epoll backend and allow it to call a
150   switch function if epoll fails at runtime.
151 */
152 static int std_event_context_init(struct tevent_context *ev)
153 {
154         struct std_event_glue *glue;
155         int ret;
156
157         /*
158          * If this is the first initialization
159          * we need to set up the allocated ops
160          * pointers.
161          */
162
163         if (ev->ops == &std_event_ops) {
164                 glue = talloc_zero(ev, struct std_event_glue);
165                 if (glue == NULL) {
166                         return -1;
167                 }
168
169                 glue->epoll_ops = tevent_find_ops_byname("epoll");
170
171                 glue->poll_ops = tevent_find_ops_byname("poll");
172                 if (glue->poll_ops == NULL) {
173                         return -1;
174                 }
175
176                 /*
177                  * Allocate space for our custom ops.
178                  * Allocate as a child of our epoll_ops pointer
179                  * so we can easily get to it using talloc_parent.
180                  */
181                 glue->glue_ops = talloc_zero(glue, struct tevent_ops);
182                 if (glue->glue_ops == NULL) {
183                         talloc_free(glue);
184                         return -1;
185                 }
186
187                 ev->ops = glue->glue_ops;
188         } else {
189                 void *glue_ptr = talloc_parent(ev->ops);
190                 glue = talloc_get_type_abort(glue_ptr, struct std_event_glue);
191         }
192
193         if (glue->epoll_ops != NULL) {
194                 /*
195                  * Set custom_ops the same as epoll,
196                  * except re-init using std_event_context_init()
197                  * and use std_event_loop_once() to add the
198                  * ability to fallback to a poll backend on
199                  * epoll runtime error.
200                  */
201                 *glue->glue_ops = *glue->epoll_ops;
202                 glue->glue_ops->context_init = std_event_context_init;
203                 glue->glue_ops->loop_once = std_event_loop_once;
204                 glue->glue_ops->loop_wait = std_event_loop_wait;
205
206                 ret = glue->epoll_ops->context_init(ev);
207                 if (ret == -1) {
208                         goto fallback;
209                 }
210 #ifdef HAVE_EPOLL
211                 tevent_epoll_set_panic_fallback(ev, std_fallback_to_poll);
212 #endif
213
214                 return ret;
215         }
216
217 fallback:
218         glue->epoll_ops = NULL;
219
220         /*
221          * Set custom_ops the same as poll.
222          */
223         *glue->glue_ops = *glue->poll_ops;
224         glue->glue_ops->context_init = std_event_context_init;
225
226         return glue->poll_ops->context_init(ev);
227 }
228
229 _PRIVATE_ bool tevent_standard_init(void)
230 {
231         return tevent_register_backend("standard", &std_event_ops);
232 }