/src/samba/lib/tevent/tevent_standard.c
Line | Count | Source |
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 | 0 | { |
60 | 0 | void *glue_ptr = talloc_parent(ev->ops); |
61 | 0 | struct std_event_glue *glue = |
62 | 0 | talloc_get_type_abort(glue_ptr, |
63 | 0 | struct std_event_glue); |
64 | 0 | int ret; |
65 | 0 | struct tevent_fd *fde; |
66 | |
|
67 | 0 | glue->fallback_replay = replay; |
68 | | |
69 | | /* First switch all the ops to poll. */ |
70 | 0 | glue->epoll_ops = NULL; |
71 | | |
72 | | /* |
73 | | * Set custom_ops the same as poll. |
74 | | */ |
75 | 0 | *glue->glue_ops = *glue->poll_ops; |
76 | 0 | glue->glue_ops->context_init = std_event_context_init; |
77 | | |
78 | | /* Next initialize the poll backend. */ |
79 | 0 | ret = glue->poll_ops->context_init(ev); |
80 | 0 | if (ret != 0) { |
81 | 0 | return false; |
82 | 0 | } |
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 | 0 | for (fde = ev->fd_events; fde; fde = fde->next) { |
89 | 0 | bool ok; |
90 | | |
91 | | /* Re-add this event as a poll backend event. */ |
92 | 0 | ok = tevent_poll_event_add_fd_internal(ev, fde); |
93 | 0 | if (!ok) { |
94 | 0 | return false; |
95 | 0 | } |
96 | 0 | } |
97 | | |
98 | 0 | return true; |
99 | 0 | } |
100 | | #endif |
101 | | |
102 | | static int std_event_loop_once(struct tevent_context *ev, const char *location) |
103 | 0 | { |
104 | 0 | void *glue_ptr = talloc_parent(ev->ops); |
105 | 0 | struct std_event_glue *glue = |
106 | 0 | talloc_get_type_abort(glue_ptr, |
107 | 0 | struct std_event_glue); |
108 | 0 | int ret; |
109 | |
|
110 | 0 | ret = glue->epoll_ops->loop_once(ev, location); |
111 | | /* |
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. |
115 | | */ |
116 | 0 | if (glue->epoll_ops != NULL) { |
117 | | /* No fallback */ |
118 | 0 | return ret; |
119 | 0 | } |
120 | | |
121 | 0 | if (!glue->fallback_replay) { |
122 | | /* |
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. |
126 | | */ |
127 | 0 | return ret; |
128 | 0 | } |
129 | | |
130 | 0 | return glue->poll_ops->loop_once(ev, location); |
131 | 0 | } |
132 | | |
133 | | static int std_event_loop_wait(struct tevent_context *ev, const char *location) |
134 | 0 | { |
135 | 0 | void *glue_ptr = talloc_parent(ev->ops); |
136 | 0 | struct std_event_glue *glue = |
137 | 0 | talloc_get_type_abort(glue_ptr, |
138 | 0 | struct std_event_glue); |
139 | 0 | int ret; |
140 | |
|
141 | 0 | ret = glue->epoll_ops->loop_wait(ev, location); |
142 | | /* |
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. |
146 | | */ |
147 | 0 | if (glue->epoll_ops != NULL) { |
148 | | /* No fallback */ |
149 | 0 | return ret; |
150 | 0 | } |
151 | | |
152 | 0 | return glue->poll_ops->loop_wait(ev, location); |
153 | 0 | } |
154 | | /* |
155 | | Initialize the epoll backend and allow it to call a |
156 | | switch function if epoll fails at runtime. |
157 | | */ |
158 | | static int std_event_context_init(struct tevent_context *ev) |
159 | 0 | { |
160 | 0 | struct std_event_glue *glue; |
161 | 0 | int ret; |
162 | | |
163 | | /* |
164 | | * If this is the first initialization |
165 | | * we need to set up the allocated ops |
166 | | * pointers. |
167 | | */ |
168 | |
|
169 | 0 | if (ev->ops->loop_once == NULL) { |
170 | 0 | glue = talloc_zero(ev, struct std_event_glue); |
171 | 0 | if (glue == NULL) { |
172 | 0 | return -1; |
173 | 0 | } |
174 | | |
175 | 0 | glue->epoll_ops = tevent_find_ops_byname("epoll"); |
176 | |
|
177 | 0 | glue->poll_ops = tevent_find_ops_byname("poll"); |
178 | 0 | if (glue->poll_ops == NULL) { |
179 | 0 | return -1; |
180 | 0 | } |
181 | | |
182 | | /* |
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. |
186 | | */ |
187 | 0 | glue->glue_ops = talloc_zero(glue, struct tevent_ops); |
188 | 0 | if (glue->glue_ops == NULL) { |
189 | 0 | talloc_free(glue); |
190 | 0 | return -1; |
191 | 0 | } |
192 | | |
193 | 0 | ev->ops = glue->glue_ops; |
194 | 0 | } else { |
195 | 0 | void *glue_ptr = talloc_parent(ev->ops); |
196 | 0 | glue = talloc_get_type_abort(glue_ptr, struct std_event_glue); |
197 | 0 | } |
198 | | |
199 | 0 | if (glue->epoll_ops != NULL) { |
200 | | /* |
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. |
206 | | */ |
207 | 0 | *glue->glue_ops = *glue->epoll_ops; |
208 | 0 | glue->glue_ops->context_init = std_event_context_init; |
209 | 0 | glue->glue_ops->loop_once = std_event_loop_once; |
210 | 0 | glue->glue_ops->loop_wait = std_event_loop_wait; |
211 | |
|
212 | 0 | ret = glue->epoll_ops->context_init(ev); |
213 | 0 | if (ret == -1) { |
214 | 0 | goto fallback; |
215 | 0 | } |
216 | 0 | #ifdef HAVE_EPOLL |
217 | 0 | tevent_epoll_set_panic_fallback(ev, std_fallback_to_poll); |
218 | 0 | #endif |
219 | |
|
220 | 0 | return ret; |
221 | 0 | } |
222 | | |
223 | 0 | fallback: |
224 | 0 | glue->epoll_ops = NULL; |
225 | | |
226 | | /* |
227 | | * Set custom_ops the same as poll. |
228 | | */ |
229 | 0 | *glue->glue_ops = *glue->poll_ops; |
230 | 0 | glue->glue_ops->context_init = std_event_context_init; |
231 | |
|
232 | 0 | return glue->poll_ops->context_init(ev); |
233 | 0 | } |
234 | | |
235 | | _PRIVATE_ bool tevent_standard_init(void) |
236 | 0 | { |
237 | 0 | return tevent_register_backend("standard", &std_event_ops); |
238 | 0 | } |