tevent: release 0.16.1
[Samba.git] / lib / tevent / tevent_standard.c
blob749cad0db772acfc0804c26fe7f0aae1c3c75fdc
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
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
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.
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.
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/>.
26 This is SAMBA's default event loop code
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()
34 #include "replace.h"
35 #include "tevent.h"
36 #include "tevent_util.h"
37 #include "tevent_internal.h"
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;
46 static int std_event_context_init(struct tevent_context *ev);
48 static const struct tevent_ops std_event_ops = {
49 .context_init = std_event_context_init,
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().
57 #ifdef HAVE_EPOLL
58 static bool std_fallback_to_poll(struct tevent_context *ev, bool replay)
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;
67 glue->fallback_replay = replay;
69 /* First switch all the ops to poll. */
70 glue->epoll_ops = NULL;
73 * Set custom_ops the same as poll.
75 *glue->glue_ops = *glue->poll_ops;
76 glue->glue_ops->context_init = std_event_context_init;
78 /* Next initialize the poll backend. */
79 ret = glue->poll_ops->context_init(ev);
80 if (ret != 0) {
81 return false;
85 * Now we have to change all the existing file descriptor
86 * events from the epoll backend to the poll backend.
88 for (fde = ev->fd_events; fde; fde = fde->next) {
89 bool ok;
91 /* Re-add this event as a poll backend event. */
92 ok = tevent_poll_event_add_fd_internal(ev, fde);
93 if (!ok) {
94 return false;
98 return true;
100 #endif
102 static int std_event_loop_once(struct tevent_context *ev, const char *location)
104 void *glue_ptr = talloc_parent(ev->ops);
105 struct std_event_glue *glue =
106 talloc_get_type_abort(glue_ptr,
107 struct std_event_glue);
108 int ret;
110 ret = glue->epoll_ops->loop_once(ev, location);
112 * If the above hasn't panicked due to an epoll interface failure,
113 * std_fallback_to_poll() wasn't called, and hasn't cleared epoll_ops to
114 * signify fallback to poll_ops.
116 if (glue->epoll_ops != NULL) {
117 /* No fallback */
118 return ret;
121 if (!glue->fallback_replay) {
123 * The problem happened while modifying an event.
124 * An event handler was triggered in this case
125 * and there is no need to call loop_once() again.
127 return ret;
130 return glue->poll_ops->loop_once(ev, location);
133 static int std_event_loop_wait(struct tevent_context *ev, const char *location)
135 void *glue_ptr = talloc_parent(ev->ops);
136 struct std_event_glue *glue =
137 talloc_get_type_abort(glue_ptr,
138 struct std_event_glue);
139 int ret;
141 ret = glue->epoll_ops->loop_wait(ev, location);
143 * If the above hasn't panicked due to an epoll interface failure,
144 * std_fallback_to_poll() wasn't called, and hasn't cleared epoll_ops to
145 * signify fallback to poll_ops.
147 if (glue->epoll_ops != NULL) {
148 /* No fallback */
149 return ret;
152 return glue->poll_ops->loop_wait(ev, location);
155 Initialize the epoll backend and allow it to call a
156 switch function if epoll fails at runtime.
158 static int std_event_context_init(struct tevent_context *ev)
160 struct std_event_glue *glue;
161 int ret;
164 * If this is the first initialization
165 * we need to set up the allocated ops
166 * pointers.
169 if (ev->ops->loop_once == NULL) {
170 glue = talloc_zero(ev, struct std_event_glue);
171 if (glue == NULL) {
172 return -1;
175 glue->epoll_ops = tevent_find_ops_byname("epoll");
177 glue->poll_ops = tevent_find_ops_byname("poll");
178 if (glue->poll_ops == NULL) {
179 return -1;
183 * Allocate space for our custom ops.
184 * Allocate as a child of our epoll_ops pointer
185 * so we can easily get to it using talloc_parent.
187 glue->glue_ops = talloc_zero(glue, struct tevent_ops);
188 if (glue->glue_ops == NULL) {
189 talloc_free(glue);
190 return -1;
193 ev->ops = glue->glue_ops;
194 } else {
195 void *glue_ptr = talloc_parent(ev->ops);
196 glue = talloc_get_type_abort(glue_ptr, struct std_event_glue);
199 if (glue->epoll_ops != NULL) {
201 * Set custom_ops the same as epoll,
202 * except re-init using std_event_context_init()
203 * and use std_event_loop_once() to add the
204 * ability to fallback to a poll backend on
205 * epoll runtime error.
207 *glue->glue_ops = *glue->epoll_ops;
208 glue->glue_ops->context_init = std_event_context_init;
209 glue->glue_ops->loop_once = std_event_loop_once;
210 glue->glue_ops->loop_wait = std_event_loop_wait;
212 ret = glue->epoll_ops->context_init(ev);
213 if (ret == -1) {
214 goto fallback;
216 #ifdef HAVE_EPOLL
217 tevent_epoll_set_panic_fallback(ev, std_fallback_to_poll);
218 #endif
220 return ret;
223 fallback:
224 glue->epoll_ops = NULL;
227 * Set custom_ops the same as poll.
229 *glue->glue_ops = *glue->poll_ops;
230 glue->glue_ops->context_init = std_event_context_init;
232 return glue->poll_ops->context_init(ev);
235 _PRIVATE_ bool tevent_standard_init(void)
237 return tevent_register_backend("standard", &std_event_ops);