aboutsummaryrefslogtreecommitdiff
path: root/src/lib/daemon_epoll.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/daemon_epoll.c')
-rw-r--r--src/lib/daemon_epoll.c504
1 files changed, 504 insertions, 0 deletions
diff --git a/src/lib/daemon_epoll.c b/src/lib/daemon_epoll.c
new file mode 100644
index 00000000..2acb3cc6
--- /dev/null
+++ b/src/lib/daemon_epoll.c
@@ -0,0 +1,504 @@
1/*
2 This file is part of libmicrohttpd
3 Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18*/
19
20/**
21 * @file lib/daemon_epoll.c
22 * @brief functions to run epoll()-based event loop
23 * @author Christian Grothoff
24 */
25#include "internal.h"
26#include "daemon_epoll.h"
27
28#ifdef EPOLL_SUPPORT
29
30/**
31 * How many events to we process at most per epoll() call? Trade-off
32 * between required stack-size and number of system calls we have to
33 * make; 128 should be way enough to avoid more than one system call
34 * for most scenarios, and still be moderate in stack size
35 * consumption. Embedded systems might want to choose a smaller value
36 * --- but why use epoll() on such a system in the first place?
37 */
38#define MAX_EVENTS 128
39
40
41#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT)
42
43/**
44 * Checks whether @a urh has some data to process.
45 *
46 * @param urh upgrade handler to analyse
47 * @return 'true' if @a urh has some data to process,
48 * 'false' otherwise
49 */
50static bool
51is_urh_ready (struct MHD_UpgradeResponseHandle * const urh)
52{
53 const struct MHD_Connection * const connection = urh->connection;
54
55 if ( (0 == urh->in_buffer_size) &&
56 (0 == urh->out_buffer_size) &&
57 (0 == urh->in_buffer_used) &&
58 (0 == urh->out_buffer_used) )
59 return false;
60
61 if (connection->daemon->shutdown)
62 return true;
63
64 if ( ( (0 != (MHD_EPOLL_STATE_READ_READY & urh->app.celi)) ||
65 (connection->tls_read_ready) ) &&
66 (urh->in_buffer_used < urh->in_buffer_size) )
67 return true;
68
69 if ( (0 != (MHD_EPOLL_STATE_READ_READY & urh->mhd.celi)) &&
70 (urh->out_buffer_used < urh->out_buffer_size) )
71 return true;
72
73 if ( (0 != (MHD_EPOLL_STATE_WRITE_READY & urh->app.celi)) &&
74 (urh->out_buffer_used > 0) )
75 return true;
76
77 if ( (0 != (MHD_EPOLL_STATE_WRITE_READY & urh->mhd.celi)) &&
78 (urh->in_buffer_used > 0) )
79 return true;
80
81 return false;
82}
83
84
85/**
86 * Do epoll()-based processing for TLS connections that have been
87 * upgraded. This requires a separate epoll() invocation as we
88 * cannot use the `struct MHD_Connection` data structures for
89 * the `union epoll_data` in this case.
90 * @remark To be called only from thread that process
91 * daemon's select()/poll()/etc.
92 *
93 * @param daemon the daemmon for which we process connections
94 * @return #MHD_SC_OK on success
95 */
96static enum MHD_StatusCode
97run_epoll_for_upgrade (struct MHD_Daemon *daemon)
98{
99 struct epoll_event events[MAX_EVENTS];
100 int num_events;
101 struct MHD_UpgradeResponseHandle * pos;
102 struct MHD_UpgradeResponseHandle * prev;
103
104 num_events = MAX_EVENTS;
105 while (MAX_EVENTS == num_events)
106 {
107 unsigned int i;
108
109 /* update event masks */
110 num_events = epoll_wait (daemon->epoll_upgrade_fd,
111 events,
112 MAX_EVENTS,
113 0);
114 if (-1 == num_events)
115 {
116 const int err = MHD_socket_get_error_ ();
117 if (MHD_SCKT_ERR_IS_EINTR_ (err))
118 return MHD_SC_OK;
119#ifdef HAVE_MESSAGES
120 MHD_DLOG (daemon,
121 MHD_SC_UNEXPECTED_EPOLL_WAIT_ERROR,
122 _("Call to epoll_wait failed: %s\n"),
123 MHD_socket_strerr_ (err));
124#endif
125 return MHD_SC_UNEXPECTED_EPOLL_WAIT_ERROR;
126 }
127 for (i = 0; i < (unsigned int) num_events; i++)
128 {
129 struct UpgradeEpollHandle * const ueh = events[i].data.ptr;
130 struct MHD_UpgradeResponseHandle * const urh = ueh->urh;
131 bool new_err_state = false;
132
133 if (urh->clean_ready)
134 continue;
135
136 /* Update ueh state based on what is ready according to epoll() */
137 if (0 != (events[i].events & EPOLLIN))
138 ueh->celi |= MHD_EPOLL_STATE_READ_READY;
139 if (0 != (events[i].events & EPOLLOUT))
140 ueh->celi |= MHD_EPOLL_STATE_WRITE_READY;
141 if (0 != (events[i].events & EPOLLHUP))
142 ueh->celi |= MHD_EPOLL_STATE_READ_READY | MHD_EPOLL_STATE_WRITE_READY;
143
144 if ( (0 == (ueh->celi & MHD_EPOLL_STATE_ERROR)) &&
145 (0 != (events[i].events & (EPOLLERR | EPOLLPRI))) )
146 {
147 /* Process new error state only one time
148 * and avoid continuously marking this connection
149 * as 'ready'. */
150 ueh->celi |= MHD_EPOLL_STATE_ERROR;
151 new_err_state = true;
152 }
153
154 if (! urh->in_eready_list)
155 {
156 if (new_err_state ||
157 is_urh_ready(urh))
158 {
159 EDLL_insert (daemon->eready_urh_head,
160 daemon->eready_urh_tail,
161 urh);
162 urh->in_eready_list = true;
163 }
164 }
165 }
166 }
167 prev = daemon->eready_urh_tail;
168 while (NULL != (pos = prev))
169 {
170 prev = pos->prevE;
171 process_urh (pos);
172 if (! is_urh_ready(pos))
173 {
174 EDLL_remove (daemon->eready_urh_head,
175 daemon->eready_urh_tail,
176 pos);
177 pos->in_eready_list = false;
178 }
179 /* Finished forwarding? */
180 if ( (0 == pos->in_buffer_size) &&
181 (0 == pos->out_buffer_size) &&
182 (0 == pos->in_buffer_used) &&
183 (0 == pos->out_buffer_used) )
184 {
185 MHD_connection_finish_forward_ (pos->connection);
186 pos->clean_ready = true;
187 /* If 'pos->was_closed' already was set to true, connection
188 * will be moved immediately to cleanup list. Otherwise
189 * connection will stay in suspended list until 'pos' will
190 * be marked with 'was_closed' by application. */
191 MHD_resume_connection (pos->connection);
192 }
193 }
194
195 return MHD_SC_OK;
196}
197#endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */
198
199
200/**
201 * Do epoll()-based processing (this function is allowed to
202 * block if @a may_block is set to #MHD_YES).
203 *
204 * @param daemon daemon to run poll loop for
205 * @param may_block true if blocking, false if non-blocking
206 * @return #MHD_SC_OK on success
207 */
208enum MHD_StatusCode
209MHD_daemon_epoll_ (struct MHD_Daemon *daemon,
210 bool may_block)
211{
212#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT)
213 static const char * const upgrade_marker = "upgrade_ptr";
214#endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */
215 struct MHD_Connection *pos;
216 struct MHD_Connection *prev;
217 struct epoll_event events[MAX_EVENTS];
218 struct epoll_event event;
219 int timeout_ms;
220 MHD_UNSIGNED_LONG_LONG timeout_ll;
221 int num_events;
222 unsigned int i;
223 MHD_socket ls;
224#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT)
225 bool run_upgraded = false;
226#endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */
227
228 if (-1 == daemon->epoll_fd)
229 return MHD_SC_EPOLL_FD_INVALID; /* we're down! */
230 if (daemon->shutdown)
231 return MHD_SC_DAEMON_ALREADY_SHUTDOWN;
232 if ( (MHD_INVALID_SOCKET != (ls = daemon->listen_socket)) &&
233 (! daemon->was_quiesced) &&
234 (daemon->connections < daemon->global_connection_limit) &&
235 (! daemon->listen_socket_in_epoll) &&
236 (! daemon->at_limit) )
237 {
238 event.events = EPOLLIN;
239 event.data.ptr = daemon;
240 if (0 != epoll_ctl (daemon->epoll_fd,
241 EPOLL_CTL_ADD,
242 ls,
243 &event))
244 {
245#ifdef HAVE_MESSAGES
246 MHD_DLOG (daemon,
247 MHD_SC_EPOLL_CTL_ADD_FAILED,
248 _("Call to epoll_ctl failed: %s\n"),
249 MHD_socket_last_strerr_ ());
250#endif
251 return MHD_SC_EPOLL_CTL_ADD_FAILED;
252 }
253 daemon->listen_socket_in_epoll = true;
254 }
255 if ( (daemon->was_quiesced) &&
256 (daemon->listen_socket_in_epoll) )
257 {
258 if (0 != epoll_ctl (daemon->epoll_fd,
259 EPOLL_CTL_DEL,
260 ls,
261 NULL))
262 MHD_PANIC ("Failed to remove listen FD from epoll set\n");
263 daemon->listen_socket_in_epoll = false;
264 }
265
266#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT)
267 if ( (! daemon->upgrade_fd_in_epoll) &&
268 (-1 != daemon->epoll_upgrade_fd) )
269 {
270 event.events = EPOLLIN | EPOLLOUT;
271 event.data.ptr = (void *) upgrade_marker;
272 if (0 != epoll_ctl (daemon->epoll_fd,
273 EPOLL_CTL_ADD,
274 daemon->epoll_upgrade_fd,
275 &event))
276 {
277#ifdef HAVE_MESSAGES
278 MHD_DLOG (daemon,
279 MHD_SC_EPOLL_CTL_ADD_FAILED,
280 _("Call to epoll_ctl failed: %s\n"),
281 MHD_socket_last_strerr_ ());
282#endif
283 return MHD_SC_EPOLL_CTL_ADD_FAILED;
284 }
285 daemon->upgrade_fd_in_epoll = true;
286 }
287#endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */
288 if ( (daemon->listen_socket_in_epoll) &&
289 ( (daemon->connections == daemon->global_connection_limit) ||
290 (daemon->at_limit) ||
291 (daemon->was_quiesced) ) )
292 {
293 /* we're at the connection limit, disable listen socket
294 for event loop for now */
295 if (0 != epoll_ctl (daemon->epoll_fd,
296 EPOLL_CTL_DEL,
297 ls,
298 NULL))
299 MHD_PANIC (_("Failed to remove listen FD from epoll set\n"));
300 daemon->listen_socket_in_epoll = false;
301 }
302
303 if ( (! daemon->disallow_suspend_resume) &&
304 (MHD_YES == resume_suspended_connections (daemon)) )
305 may_block = false;
306
307 if (may_block)
308 {
309 if (MHD_SC_OK == /* FIXME: distinguish between NO_TIMEOUT and errors */
310 MHD_daemon_get_timeout (daemon,
311 &timeout_ll))
312 {
313 if (timeout_ll >= (MHD_UNSIGNED_LONG_LONG) INT_MAX)
314 timeout_ms = INT_MAX;
315 else
316 timeout_ms = (int) timeout_ll;
317 }
318 else
319 timeout_ms = -1;
320 }
321 else
322 timeout_ms = 0;
323
324 /* Reset. New value will be set when connections are processed. */
325 /* Note: Used mostly for uniformity here as same situation is
326 * signaled in epoll mode by non-empty eready DLL. */
327 daemon->data_already_pending = false;
328
329 /* drain 'epoll' event queue; need to iterate as we get at most
330 MAX_EVENTS in one system call here; in practice this should
331 pretty much mean only one round, but better an extra loop here
332 than unfair behavior... */
333 num_events = MAX_EVENTS;
334 while (MAX_EVENTS == num_events)
335 {
336 /* update event masks */
337 num_events = epoll_wait (daemon->epoll_fd,
338 events,
339 MAX_EVENTS,
340 timeout_ms);
341 if (-1 == num_events)
342 {
343 const int err = MHD_socket_get_error_ ();
344 if (MHD_SCKT_ERR_IS_EINTR_ (err))
345 return MHD_SC_OK;
346#ifdef HAVE_MESSAGES
347 MHD_DLOG (daemon,
348 MHD_SC_UNEXPECTED_EPOLL_WAIT_ERROR,
349 _("Call to epoll_wait failed: %s\n"),
350 MHD_socket_strerr_ (err));
351#endif
352 return MHD_SC_UNEXPECTED_EPOLL_WAIT_ERROR;
353 }
354 for (i=0;i<(unsigned int) num_events;i++)
355 {
356 /* First, check for the values of `ptr` that would indicate
357 that this event is not about a normal connection. */
358 if (NULL == events[i].data.ptr)
359 continue; /* shutdown signal! */
360#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT)
361 if (upgrade_marker == events[i].data.ptr)
362 {
363 /* activity on an upgraded connection, we process
364 those in a separate epoll() */
365 run_upgraded = true;
366 continue;
367 }
368#endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */
369 if (daemon->epoll_itc_marker == events[i].data.ptr)
370 {
371 /* It's OK to clear ITC here as all external
372 conditions will be processed later. */
373 MHD_itc_clear_ (daemon->itc);
374 continue;
375 }
376 if (daemon == events[i].data.ptr)
377 {
378 /* Check for error conditions on listen socket. */
379 /* FIXME: Initiate MHD_quiesce_daemon() to prevent busy waiting? */
380 if (0 == (events[i].events & (EPOLLERR | EPOLLHUP)))
381 {
382 unsigned int series_length = 0;
383 /* Run 'accept' until it fails or daemon at limit of connections.
384 * Do not accept more then 10 connections at once. The rest will
385 * be accepted on next turn (level trigger is used for listen
386 * socket). */
387 while ( (MHD_YES ==
388 MHD_accept_connection (daemon)) &&
389 (series_length < 10) &&
390 (daemon->connections < daemon->global_connection_limit) &&
391 (! daemon->at_limit) )
392 series_length++;
393 }
394 continue;
395 }
396 /* this is an event relating to a 'normal' connection,
397 remember the event and if appropriate mark the
398 connection as 'eready'. */
399 pos = events[i].data.ptr;
400 /* normal processing: update read/write data */
401 if (0 != (events[i].events & (EPOLLPRI | EPOLLERR | EPOLLHUP)))
402 {
403 pos->epoll_state |= MHD_EPOLL_STATE_ERROR;
404 if (0 == (pos->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL))
405 {
406 EDLL_insert (daemon->eready_head,
407 daemon->eready_tail,
408 pos);
409 pos->epoll_state |= MHD_EPOLL_STATE_IN_EREADY_EDLL;
410 }
411 }
412 else
413 {
414 if (0 != (events[i].events & EPOLLIN))
415 {
416 pos->epoll_state |= MHD_EPOLL_STATE_READ_READY;
417 if ( ( (MHD_EVENT_LOOP_INFO_READ == pos->request.event_loop_info) ||
418 (pos->request.read_buffer_size > pos->request.read_buffer_offset) ) &&
419 (0 == (pos->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL) ) )
420 {
421 EDLL_insert (daemon->eready_head,
422 daemon->eready_tail,
423 pos);
424 pos->epoll_state |= MHD_EPOLL_STATE_IN_EREADY_EDLL;
425 }
426 }
427 if (0 != (events[i].events & EPOLLOUT))
428 {
429 pos->epoll_state |= MHD_EPOLL_STATE_WRITE_READY;
430 if ( (MHD_EVENT_LOOP_INFO_WRITE == pos->request.event_loop_info) &&
431 (0 == (pos->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL) ) )
432 {
433 EDLL_insert (daemon->eready_head,
434 daemon->eready_tail,
435 pos);
436 pos->epoll_state |= MHD_EPOLL_STATE_IN_EREADY_EDLL;
437 }
438 }
439 }
440 }
441 }
442
443#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT)
444 if (run_upgraded)
445 run_epoll_for_upgrade (daemon); /* FIXME: return value? */
446#endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */
447
448 /* process events for connections */
449 prev = daemon->eready_tail;
450 while (NULL != (pos = prev))
451 {
452 prev = pos->prevE;
453 call_handlers (pos,
454 0 != (pos->epoll_state & MHD_EPOLL_STATE_READ_READY),
455 0 != (pos->epoll_state & MHD_EPOLL_STATE_WRITE_READY),
456 0 != (pos->epoll_state & MHD_EPOLL_STATE_ERROR));
457 if (MHD_EPOLL_STATE_IN_EREADY_EDLL ==
458 (pos->epoll_state & (MHD_EPOLL_STATE_SUSPENDED | MHD_EPOLL_STATE_IN_EREADY_EDLL)))
459 {
460 if ( (MHD_EVENT_LOOP_INFO_READ == pos->request.event_loop_info &&
461 0 == (pos->epoll_state & MHD_EPOLL_STATE_READ_READY) ) ||
462 (MHD_EVENT_LOOP_INFO_WRITE == pos->request.event_loop_info &&
463 0 == (pos->epoll_state & MHD_EPOLL_STATE_WRITE_READY) ) ||
464 MHD_EVENT_LOOP_INFO_CLEANUP == pos->request.event_loop_info)
465 {
466 EDLL_remove (daemon->eready_head,
467 daemon->eready_tail,
468 pos);
469 pos->epoll_state &= ~MHD_EPOLL_STATE_IN_EREADY_EDLL;
470 }
471 }
472 }
473
474 /* Finally, handle timed-out connections; we need to do this here
475 as the epoll mechanism won't call the 'MHD_connection_handle_idle()' on everything,
476 as the other event loops do. As timeouts do not get an explicit
477 event, we need to find those connections that might have timed out
478 here.
479
480 Connections with custom timeouts must all be looked at, as we
481 do not bother to sort that (presumably very short) list. */
482 prev = daemon->manual_timeout_tail;
483 while (NULL != (pos = prev))
484 {
485 prev = pos->prevX;
486 MHD_connection_handle_idle (pos);
487 }
488 /* Connections with the default timeout are sorted by prepending
489 them to the head of the list whenever we touch the connection;
490 thus it suffices to iterate from the tail until the first
491 connection is NOT timed out */
492 prev = daemon->normal_timeout_tail;
493 while (NULL != (pos = prev))
494 {
495 prev = pos->prevX;
496 MHD_connection_handle_idle (pos);
497 if (MHD_REQUEST_CLOSED != pos->request.state)
498 break; /* sorted by timeout, no need to visit the rest! */
499 }
500 return MHD_SC_OK;
501}
502#endif
503
504/* end of daemon_epoll.c */