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