diff options
author | Christian Grothoff <christian@grothoff.org> | 2013-12-03 20:11:34 +0000 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2013-12-03 20:11:34 +0000 |
commit | 04ca76e78eaea8f40c4262aee530d2b2c3b281bf (patch) | |
tree | 0499583645c8477a6688a01f0f92bbf5735de1cc | |
parent | 4245c6e9c371a8434b13a37edbc4e6dc239813da (diff) | |
download | libmicrohttpd-04ca76e78eaea8f40c4262aee530d2b2c3b281bf.tar.gz libmicrohttpd-04ca76e78eaea8f40c4262aee530d2b2c3b281bf.zip |
Adding Matt Holiday's fixes to suspend/resume logic
(plus documentation updates).
-rw-r--r-- | ChangeLog | 29 | ||||
-rw-r--r-- | doc/libmicrohttpd.texi | 8 | ||||
-rw-r--r-- | src/include/microhttpd.h | 8 | ||||
-rw-r--r-- | src/microhttpd/connection.c | 15 | ||||
-rw-r--r-- | src/microhttpd/daemon.c | 250 | ||||
-rw-r--r-- | src/microhttpd/internal.h | 24 |
6 files changed, 264 insertions, 70 deletions
@@ -1,3 +1,32 @@ | |||
1 | Tue Dec 3 21:05:38 CET 2013 | ||
2 | Signaling n times for shutdown works, but for resume we need to | ||
3 | wake up the correct daemon. Even if we signal n times in that | ||
4 | case also, there's no guarantee that some daemon can't run | ||
5 | through its select loop more than once before the daemon we want | ||
6 | to wake up gets a chance to read. Thus we need a signal pipe | ||
7 | per thread in the thread pool IF MHD_suspend_connection is used. | ||
8 | This introduces a new flag MHD_USE_SUSPEND_RESUME to add those | ||
9 | additional pipes and only allow MHD_suspend_connection to be | ||
10 | used in conjunction with this flag. | ||
11 | |||
12 | Also, as MHD_resume_connection() will be called on a non-daemon | ||
13 | thread, but none of the queue insert/delete calls are thread safe, | ||
14 | we need to be concerned about (a) corrupting the queue, and (b) | ||
15 | having to add mutex protection around every access to the queues, | ||
16 | including loops through timer queues, etc. This wasn't a problem | ||
17 | before adding resume; even suspend should be safe since it happens | ||
18 | in a callback from the daemon. | ||
19 | |||
20 | I think it's easier to (a) have MHD_suspend_connection() move the | ||
21 | connection to a suspended queue, (b) have MHD_resume_connection() | ||
22 | mark the connection as resuming, and then (c) do all the actual | ||
23 | queue manipulations in MHD_select (poll, epoll, etc.) to move the | ||
24 | resumed connections back to their normal queues, in response to | ||
25 | the wake up. The changes are simpler & cleaner. There is a cost to | ||
26 | the basic select loop that is avoided by making suspend/resume a | ||
27 | startup option. The per-worker pipes can then also be enabled only | ||
28 | with that option set. -MH | ||
29 | |||
1 | Fri Nov 29 20:17:03 CET 2013 | 30 | Fri Nov 29 20:17:03 CET 2013 |
2 | Eliminating theoretical stack overflow by limiting length | 31 | Eliminating theoretical stack overflow by limiting length |
3 | of URIs in authentication headers to 32k (only applicable | 32 | of URIs in authentication headers to 32k (only applicable |
diff --git a/doc/libmicrohttpd.texi b/doc/libmicrohttpd.texi index 6fa82ab5..6780ef68 100644 --- a/doc/libmicrohttpd.texi +++ b/doc/libmicrohttpd.texi | |||
@@ -520,6 +520,12 @@ use of this option is automatic (as in, you do not even have to | |||
520 | specify it), if @code{MHD_USE_NO_LISTEN_SOCKET} is specified. In | 520 | specify it), if @code{MHD_USE_NO_LISTEN_SOCKET} is specified. In |
521 | "external" select mode, this option is always simply ignored. | 521 | "external" select mode, this option is always simply ignored. |
522 | 522 | ||
523 | @item MHD_USE_SUSPEND_RESUME | ||
524 | Enables using @code{MHD_suspend_connection} and | ||
525 | @code{MHD_resume_connection}, as performing these calls requires some | ||
526 | additional pipes to be created, and code not using these calls should | ||
527 | not pay the cost. | ||
528 | |||
523 | @end table | 529 | @end table |
524 | @end deftp | 530 | @end deftp |
525 | 531 | ||
@@ -1856,7 +1862,7 @@ select, internal select or thread pool; not applicable to | |||
1856 | thread-per-connection!) for a while. | 1862 | thread-per-connection!) for a while. |
1857 | 1863 | ||
1858 | If you use this API in conjunction with a internal select or a | 1864 | If you use this API in conjunction with a internal select or a |
1859 | thread pool, you must set the option @code{MHD_USE_PIPE_FOR_SHUTDOWN} to | 1865 | thread pool, you must set the option @code{MHD_USE_SUSPEND_RESUME} to |
1860 | ensure that a resumed connection is immediately processed by MHD. | 1866 | ensure that a resumed connection is immediately processed by MHD. |
1861 | 1867 | ||
1862 | Suspended connections continue to count against the total number of | 1868 | Suspended connections continue to count against the total number of |
diff --git a/src/include/microhttpd.h b/src/include/microhttpd.h index 96dc2f27..4f8aee0c 100644 --- a/src/include/microhttpd.h +++ b/src/include/microhttpd.h | |||
@@ -518,7 +518,13 @@ enum MHD_FLAG | |||
518 | * Enalbed always on W32 as winsock does not properly behave | 518 | * Enalbed always on W32 as winsock does not properly behave |
519 | * with `shutdown()` and this then fixes potential problems. | 519 | * with `shutdown()` and this then fixes potential problems. |
520 | */ | 520 | */ |
521 | MHD_USE_EPOLL_TURBO = 4096 | 521 | MHD_USE_EPOLL_TURBO = 4096, |
522 | |||
523 | /** | ||
524 | * Enable suspend/resume functions, which also implies setting up | ||
525 | * pipes to signal resume. | ||
526 | */ | ||
527 | MHD_USE_SUSPEND_RESUME = 8192 | MHD_USE_PIPE_FOR_SHUTDOWN | ||
522 | 528 | ||
523 | }; | 529 | }; |
524 | 530 | ||
diff --git a/src/microhttpd/connection.c b/src/microhttpd/connection.c index 858540c2..de478c86 100644 --- a/src/microhttpd/connection.c +++ b/src/microhttpd/connection.c | |||
@@ -2079,16 +2079,23 @@ cleanup_connection (struct MHD_Connection *connection) | |||
2079 | XDLL_remove (daemon->manual_timeout_head, | 2079 | XDLL_remove (daemon->manual_timeout_head, |
2080 | daemon->manual_timeout_tail, | 2080 | daemon->manual_timeout_tail, |
2081 | connection); | 2081 | connection); |
2082 | DLL_remove (daemon->connections_head, | 2082 | if (MHD_YES == connection->suspended) |
2083 | daemon->connections_tail, | 2083 | DLL_remove (daemon->suspended_connections_head, |
2084 | connection); | 2084 | daemon->suspended_connections_tail, |
2085 | connection); | ||
2086 | else | ||
2087 | DLL_remove (daemon->connections_head, | ||
2088 | daemon->connections_tail, | ||
2089 | connection); | ||
2085 | DLL_insert (daemon->cleanup_head, | 2090 | DLL_insert (daemon->cleanup_head, |
2086 | daemon->cleanup_tail, | 2091 | daemon->cleanup_tail, |
2087 | connection); | 2092 | connection); |
2093 | connection->suspended = MHD_NO; | ||
2094 | connection->resuming = MHD_NO; | ||
2095 | connection->in_idle = MHD_NO; | ||
2088 | if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) && | 2096 | if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) && |
2089 | (0 != pthread_mutex_unlock(&daemon->cleanup_connection_mutex)) ) | 2097 | (0 != pthread_mutex_unlock(&daemon->cleanup_connection_mutex)) ) |
2090 | MHD_PANIC ("Failed to release cleanup mutex\n"); | 2098 | MHD_PANIC ("Failed to release cleanup mutex\n"); |
2091 | connection->in_idle = MHD_NO; | ||
2092 | } | 2099 | } |
2093 | 2100 | ||
2094 | 2101 | ||
diff --git a/src/microhttpd/daemon.c b/src/microhttpd/daemon.c index ac0299b9..171ab2cb 100644 --- a/src/microhttpd/daemon.c +++ b/src/microhttpd/daemon.c | |||
@@ -1026,7 +1026,6 @@ internal_add_connection (struct MHD_Daemon *daemon, | |||
1026 | #if OSX | 1026 | #if OSX |
1027 | static int on = 1; | 1027 | static int on = 1; |
1028 | #endif | 1028 | #endif |
1029 | |||
1030 | if (NULL != daemon->worker_pool) | 1029 | if (NULL != daemon->worker_pool) |
1031 | { | 1030 | { |
1032 | /* have a pool, try to find a pool with capacity; we use the | 1031 | /* have a pool, try to find a pool with capacity; we use the |
@@ -1069,7 +1068,7 @@ internal_add_connection (struct MHD_Daemon *daemon, | |||
1069 | 1068 | ||
1070 | #if HAVE_MESSAGES | 1069 | #if HAVE_MESSAGES |
1071 | #if DEBUG_CONNECT | 1070 | #if DEBUG_CONNECT |
1072 | MHD_DLOG (daemon, "Accepted connection on socket %d\n", s); | 1071 | MHD_DLOG (daemon, "Accepted connection on socket %d\n", client_socket); |
1073 | #endif | 1072 | #endif |
1074 | #endif | 1073 | #endif |
1075 | if ( (0 == daemon->max_connections) || | 1074 | if ( (0 == daemon->max_connections) || |
@@ -1396,14 +1395,17 @@ MHD_suspend_connection (struct MHD_Connection *connection) | |||
1396 | struct MHD_Daemon *daemon; | 1395 | struct MHD_Daemon *daemon; |
1397 | 1396 | ||
1398 | daemon = connection->daemon; | 1397 | daemon = connection->daemon; |
1399 | if (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) | 1398 | if (0 == (daemon->options & MHD_USE_SUSPEND_RESUME)) |
1400 | MHD_PANIC ("Cannot suspend connections in THREAD_PER_CONNECTION mode!\n"); | 1399 | MHD_PANIC ("Cannot suspend connections without enabling MHD_USE_SUSPEND_RESUME!\n"); |
1401 | if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) && | 1400 | if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) && |
1402 | (0 != pthread_mutex_lock (&daemon->cleanup_connection_mutex)) ) | 1401 | (0 != pthread_mutex_lock (&daemon->cleanup_connection_mutex)) ) |
1403 | MHD_PANIC ("Failed to acquire cleanup mutex\n"); | 1402 | MHD_PANIC ("Failed to acquire cleanup mutex\n"); |
1404 | DLL_remove (daemon->connections_head, | 1403 | DLL_remove (daemon->connections_head, |
1405 | daemon->connections_tail, | 1404 | daemon->connections_tail, |
1406 | connection); | 1405 | connection); |
1406 | DLL_insert (daemon->suspended_connections_head, | ||
1407 | daemon->suspended_connections_tail, | ||
1408 | connection); | ||
1407 | if (connection->connection_timeout == daemon->connection_timeout) | 1409 | if (connection->connection_timeout == daemon->connection_timeout) |
1408 | XDLL_remove (daemon->normal_timeout_head, | 1410 | XDLL_remove (daemon->normal_timeout_head, |
1409 | daemon->normal_timeout_tail, | 1411 | daemon->normal_timeout_tail, |
@@ -1414,25 +1416,26 @@ MHD_suspend_connection (struct MHD_Connection *connection) | |||
1414 | connection); | 1416 | connection); |
1415 | #if EPOLL_SUPPORT | 1417 | #if EPOLL_SUPPORT |
1416 | if (0 != (daemon->options & MHD_USE_EPOLL_LINUX_ONLY)) | 1418 | if (0 != (daemon->options & MHD_USE_EPOLL_LINUX_ONLY)) |
1417 | { | 1419 | { |
1418 | if (0 != (connection->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL)) | 1420 | if (0 != (connection->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL)) |
1419 | { | 1421 | { |
1420 | EDLL_remove (daemon->eready_head, | 1422 | EDLL_remove (daemon->eready_head, |
1421 | daemon->eready_tail, | 1423 | daemon->eready_tail, |
1422 | connection); | 1424 | connection); |
1423 | } | 1425 | } |
1424 | if (0 != (connection->epoll_state & MHD_EPOLL_STATE_IN_EPOLL_SET)) | 1426 | if (0 != (connection->epoll_state & MHD_EPOLL_STATE_IN_EPOLL_SET)) |
1425 | { | 1427 | { |
1426 | if (0 != epoll_ctl (daemon->epoll_fd, | 1428 | if (0 != epoll_ctl (daemon->epoll_fd, |
1427 | EPOLL_CTL_DEL, | 1429 | EPOLL_CTL_DEL, |
1428 | connection->socket_fd, | 1430 | connection->socket_fd, |
1429 | NULL)) | 1431 | NULL)) |
1430 | MHD_PANIC ("Failed to remove FD from epoll set\n"); | 1432 | MHD_PANIC ("Failed to remove FD from epoll set\n"); |
1431 | connection->epoll_state &= ~MHD_EPOLL_STATE_IN_EPOLL_SET; | 1433 | connection->epoll_state &= ~MHD_EPOLL_STATE_IN_EPOLL_SET; |
1432 | } | 1434 | } |
1433 | connection->epoll_state |= MHD_EPOLL_STATE_SUSPENDED; | 1435 | connection->epoll_state |= MHD_EPOLL_STATE_SUSPENDED; |
1434 | } | 1436 | } |
1435 | #endif | 1437 | #endif |
1438 | connection->suspended = MHD_YES; | ||
1436 | if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) && | 1439 | if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) && |
1437 | (0 != pthread_mutex_unlock (&daemon->cleanup_connection_mutex)) ) | 1440 | (0 != pthread_mutex_unlock (&daemon->cleanup_connection_mutex)) ) |
1438 | MHD_PANIC ("Failed to release cleanup mutex\n"); | 1441 | MHD_PANIC ("Failed to release cleanup mutex\n"); |
@@ -1453,50 +1456,15 @@ MHD_resume_connection (struct MHD_Connection *connection) | |||
1453 | struct MHD_Daemon *daemon; | 1456 | struct MHD_Daemon *daemon; |
1454 | 1457 | ||
1455 | daemon = connection->daemon; | 1458 | daemon = connection->daemon; |
1456 | if (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) | 1459 | if (0 == (daemon->options & MHD_USE_SUSPEND_RESUME)) |
1457 | MHD_PANIC ("Cannot resume connections in THREAD_PER_CONNECTION mode!\n"); | 1460 | MHD_PANIC ("Cannot resume connections without enabling MHD_USE_SUSPEND_RESUME!\n"); |
1458 | if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) && | 1461 | if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) && |
1459 | (0 != pthread_mutex_lock (&daemon->cleanup_connection_mutex)) ) | 1462 | (0 != pthread_mutex_lock (&daemon->cleanup_connection_mutex)) ) |
1460 | MHD_PANIC ("Failed to acquire cleanup mutex\n"); | 1463 | MHD_PANIC ("Failed to acquire cleanup mutex\n"); |
1461 | DLL_insert (daemon->connections_head, | 1464 | connection->resuming = MHD_YES; |
1462 | daemon->connections_tail, | 1465 | daemon->resuming = MHD_YES; |
1463 | connection); | ||
1464 | if (connection->connection_timeout == daemon->connection_timeout) | ||
1465 | XDLL_insert (daemon->normal_timeout_head, | ||
1466 | daemon->normal_timeout_tail, | ||
1467 | connection); | ||
1468 | else | ||
1469 | XDLL_insert (daemon->manual_timeout_head, | ||
1470 | daemon->manual_timeout_tail, | ||
1471 | connection); | ||
1472 | #if EPOLL_SUPPORT | ||
1473 | if (0 != (daemon->options & MHD_USE_EPOLL_LINUX_ONLY)) | ||
1474 | { | ||
1475 | if (0 != (connection->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL)) | ||
1476 | { | ||
1477 | EDLL_insert (daemon->eready_head, | ||
1478 | daemon->eready_tail, | ||
1479 | connection); | ||
1480 | } | ||
1481 | else | ||
1482 | { | ||
1483 | struct epoll_event event; | ||
1484 | |||
1485 | event.events = EPOLLIN | EPOLLOUT | EPOLLET; | ||
1486 | event.data.ptr = connection; | ||
1487 | if (0 != epoll_ctl (daemon->epoll_fd, | ||
1488 | EPOLL_CTL_ADD, | ||
1489 | connection->socket_fd, | ||
1490 | &event)) | ||
1491 | MHD_PANIC ("Failed to add FD to epoll set\n"); | ||
1492 | else | ||
1493 | connection->epoll_state |= MHD_EPOLL_STATE_IN_EPOLL_SET; | ||
1494 | } | ||
1495 | connection->epoll_state &= ~MHD_EPOLL_STATE_SUSPENDED; | ||
1496 | } | ||
1497 | #endif | ||
1498 | if ( (-1 != daemon->wpipe[1]) && | 1466 | if ( (-1 != daemon->wpipe[1]) && |
1499 | (1 != WRITE (daemon->wpipe[1], "n", 1)) ) | 1467 | (1 != WRITE (daemon->wpipe[1], "r", 1)) ) |
1500 | { | 1468 | { |
1501 | #if HAVE_MESSAGES | 1469 | #if HAVE_MESSAGES |
1502 | MHD_DLOG (daemon, | 1470 | MHD_DLOG (daemon, |
@@ -1508,6 +1476,80 @@ MHD_resume_connection (struct MHD_Connection *connection) | |||
1508 | MHD_PANIC ("Failed to release cleanup mutex\n"); | 1476 | MHD_PANIC ("Failed to release cleanup mutex\n"); |
1509 | } | 1477 | } |
1510 | 1478 | ||
1479 | /** | ||
1480 | * Run through the suspended connections and move any that are no | ||
1481 | * longer suspended back to the active state. | ||
1482 | * | ||
1483 | * @param daemon daemon context | ||
1484 | */ | ||
1485 | static void | ||
1486 | resume_suspended_connections (struct MHD_Daemon *daemon) | ||
1487 | { | ||
1488 | struct MHD_Connection *pos; | ||
1489 | struct MHD_Connection *next = NULL; | ||
1490 | |||
1491 | if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) && | ||
1492 | (0 != pthread_mutex_lock (&daemon->cleanup_connection_mutex)) ) | ||
1493 | MHD_PANIC ("Failed to acquire cleanup mutex\n"); | ||
1494 | |||
1495 | if (MHD_YES == daemon->resuming) | ||
1496 | next = daemon->suspended_connections_head; | ||
1497 | |||
1498 | while (NULL != (pos = next)) | ||
1499 | { | ||
1500 | next = pos->next; | ||
1501 | if (MHD_NO == pos->resuming) | ||
1502 | continue; | ||
1503 | |||
1504 | DLL_remove (daemon->suspended_connections_head, | ||
1505 | daemon->suspended_connections_tail, | ||
1506 | pos); | ||
1507 | DLL_insert (daemon->connections_head, | ||
1508 | daemon->connections_tail, | ||
1509 | pos); | ||
1510 | if (pos->connection_timeout == daemon->connection_timeout) | ||
1511 | XDLL_insert (daemon->normal_timeout_head, | ||
1512 | daemon->normal_timeout_tail, | ||
1513 | pos); | ||
1514 | else | ||
1515 | XDLL_insert (daemon->manual_timeout_head, | ||
1516 | daemon->manual_timeout_tail, | ||
1517 | pos); | ||
1518 | #if EPOLL_SUPPORT | ||
1519 | if (0 != (daemon->options & MHD_USE_EPOLL_LINUX_ONLY)) | ||
1520 | { | ||
1521 | if (0 != (pos->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL)) | ||
1522 | { | ||
1523 | EDLL_insert (daemon->eready_head, | ||
1524 | daemon->eready_tail, | ||
1525 | pos); | ||
1526 | } | ||
1527 | else | ||
1528 | { | ||
1529 | struct epoll_event event; | ||
1530 | |||
1531 | event.events = EPOLLIN | EPOLLOUT | EPOLLET; | ||
1532 | event.data.ptr = pos; | ||
1533 | if (0 != epoll_ctl (daemon->epoll_fd, | ||
1534 | EPOLL_CTL_ADD, | ||
1535 | pos->socket_fd, | ||
1536 | &event)) | ||
1537 | MHD_PANIC ("Failed to add FD to epoll set\n"); | ||
1538 | else | ||
1539 | pos->epoll_state |= MHD_EPOLL_STATE_IN_EPOLL_SET; | ||
1540 | } | ||
1541 | pos->epoll_state &= ~MHD_EPOLL_STATE_SUSPENDED; | ||
1542 | } | ||
1543 | #endif | ||
1544 | pos->suspended = MHD_NO; | ||
1545 | pos->resuming = MHD_NO; | ||
1546 | } | ||
1547 | daemon->resuming = MHD_NO; | ||
1548 | if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) && | ||
1549 | (0 != pthread_mutex_unlock (&daemon->cleanup_connection_mutex)) ) | ||
1550 | MHD_PANIC ("Failed to release cleanup mutex\n"); | ||
1551 | } | ||
1552 | |||
1511 | 1553 | ||
1512 | /** | 1554 | /** |
1513 | * Change socket options to be non-blocking, non-inheritable. | 1555 | * Change socket options to be non-blocking, non-inheritable. |
@@ -1995,6 +2037,9 @@ MHD_select (struct MHD_Daemon *daemon, | |||
1995 | max = -1; | 2037 | max = -1; |
1996 | if (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) | 2038 | if (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) |
1997 | { | 2039 | { |
2040 | if (0 != (daemon->options & MHD_USE_SUSPEND_RESUME)) | ||
2041 | resume_suspended_connections (daemon); | ||
2042 | |||
1998 | /* single-threaded, go over everything */ | 2043 | /* single-threaded, go over everything */ |
1999 | if (MHD_NO == MHD_get_fdset (daemon, &rs, &ws, &es, &max)) | 2044 | if (MHD_NO == MHD_get_fdset (daemon, &rs, &ws, &es, &max)) |
2000 | return MHD_NO; | 2045 | return MHD_NO; |
@@ -2072,6 +2117,9 @@ MHD_poll_all (struct MHD_Daemon *daemon, | |||
2072 | struct MHD_Connection *pos; | 2117 | struct MHD_Connection *pos; |
2073 | struct MHD_Connection *next; | 2118 | struct MHD_Connection *next; |
2074 | 2119 | ||
2120 | if (0 != (daemon->options & MHD_USE_SUSPEND_RESUME)) | ||
2121 | resume_suspended_connections (daemon); | ||
2122 | |||
2075 | /* count number of connections and thus determine poll set size */ | 2123 | /* count number of connections and thus determine poll set size */ |
2076 | num_connections = 0; | 2124 | num_connections = 0; |
2077 | for (pos = daemon->connections_head; NULL != pos; pos = pos->next) | 2125 | for (pos = daemon->connections_head; NULL != pos; pos = pos->next) |
@@ -2322,6 +2370,7 @@ MHD_epoll (struct MHD_Daemon *daemon, | |||
2322 | int num_events; | 2370 | int num_events; |
2323 | unsigned int i; | 2371 | unsigned int i; |
2324 | unsigned int series_length; | 2372 | unsigned int series_length; |
2373 | char tmp; | ||
2325 | 2374 | ||
2326 | if (-1 == daemon->epoll_fd) | 2375 | if (-1 == daemon->epoll_fd) |
2327 | return MHD_NO; /* we're down! */ | 2376 | return MHD_NO; /* we're down! */ |
@@ -2402,6 +2451,12 @@ MHD_epoll (struct MHD_Daemon *daemon, | |||
2402 | { | 2451 | { |
2403 | if (NULL == events[i].data.ptr) | 2452 | if (NULL == events[i].data.ptr) |
2404 | continue; /* shutdown signal! */ | 2453 | continue; /* shutdown signal! */ |
2454 | if ( (-1 != daemon->wpipe[0]) && | ||
2455 | (daemon->wpipe[0] == events[i].data.fd) ) | ||
2456 | { | ||
2457 | (void) read (daemon->wpipe[0], &tmp, sizeof (tmp)); | ||
2458 | continue; | ||
2459 | } | ||
2405 | if (daemon != events[i].data.ptr) | 2460 | if (daemon != events[i].data.ptr) |
2406 | { | 2461 | { |
2407 | /* this is an event relating to a 'normal' connection, | 2462 | /* this is an event relating to a 'normal' connection, |
@@ -2447,6 +2502,11 @@ MHD_epoll (struct MHD_Daemon *daemon, | |||
2447 | } | 2502 | } |
2448 | } | 2503 | } |
2449 | 2504 | ||
2505 | /* we handle resumes here because we may have ready connections | ||
2506 | that will not be placed into the epoll list immediately. */ | ||
2507 | if (0 != (daemon->options & MHD_USE_SUSPEND_RESUME)) | ||
2508 | resume_suspended_connections (daemon); | ||
2509 | |||
2450 | /* process events for connections */ | 2510 | /* process events for connections */ |
2451 | while (NULL != (pos = daemon->eready_tail)) | 2511 | while (NULL != (pos = daemon->eready_tail)) |
2452 | { | 2512 | { |
@@ -3054,6 +3114,26 @@ setup_epoll_to_listen (struct MHD_Daemon *daemon) | |||
3054 | #endif | 3114 | #endif |
3055 | return MHD_NO; | 3115 | return MHD_NO; |
3056 | } | 3116 | } |
3117 | if ( (-1 != daemon->wpipe[0]) && | ||
3118 | (0 != (daemon->options & MHD_USE_SUSPEND_RESUME)) ) | ||
3119 | { | ||
3120 | event.events = EPOLLIN | EPOLLET; | ||
3121 | event.data.ptr = NULL; | ||
3122 | event.data.fd = daemon->wpipe[0]; | ||
3123 | if (0 != epoll_ctl (daemon->epoll_fd, | ||
3124 | EPOLL_CTL_ADD, | ||
3125 | daemon->wpipe[0], | ||
3126 | &event)) | ||
3127 | { | ||
3128 | #if HAVE_MESSAGES | ||
3129 | if (0 != (daemon->options & MHD_USE_DEBUG)) | ||
3130 | MHD_DLOG (daemon, | ||
3131 | "Call to epoll_ctl failed: %s\n", | ||
3132 | STRERROR (errno)); | ||
3133 | #endif | ||
3134 | return MHD_NO; | ||
3135 | } | ||
3136 | } | ||
3057 | daemon->listen_socket_in_epoll = MHD_YES; | 3137 | daemon->listen_socket_in_epoll = MHD_YES; |
3058 | return MHD_YES; | 3138 | return MHD_YES; |
3059 | } | 3139 | } |
@@ -3274,6 +3354,16 @@ MHD_start_daemon_va (unsigned int flags, | |||
3274 | goto free_and_fail; | 3354 | goto free_and_fail; |
3275 | } | 3355 | } |
3276 | 3356 | ||
3357 | if ( (0 != (flags & MHD_USE_SUSPEND_RESUME)) && | ||
3358 | (0 != (flags & MHD_USE_THREAD_PER_CONNECTION)) ) | ||
3359 | { | ||
3360 | #if HAVE_MESSAGES | ||
3361 | MHD_DLOG (daemon, | ||
3362 | "Combining MHD_USE_THREAD_PER_CONNECTION and MHD_USE_SUSPEND_RESUME is not supported."); | ||
3363 | #endif | ||
3364 | goto free_and_fail; | ||
3365 | } | ||
3366 | |||
3277 | #ifdef __SYMBIAN32__ | 3367 | #ifdef __SYMBIAN32__ |
3278 | if (0 != (flags & (MHD_USE_SELECT_INTERNALLY | MHD_USE_THREAD_PER_CONNECTION))) | 3368 | if (0 != (flags & (MHD_USE_SELECT_INTERNALLY | MHD_USE_THREAD_PER_CONNECTION))) |
3279 | { | 3369 | { |
@@ -3588,6 +3678,38 @@ MHD_start_daemon_va (unsigned int flags, | |||
3588 | d->worker_pool_size = 0; | 3678 | d->worker_pool_size = 0; |
3589 | d->worker_pool = NULL; | 3679 | d->worker_pool = NULL; |
3590 | 3680 | ||
3681 | if ( (0 != (flags & MHD_USE_SUSPEND_RESUME)) && | ||
3682 | #ifdef WINDOWS | ||
3683 | (0 != SOCKETPAIR (AF_INET, SOCK_STREAM, IPPROTO_TCP, d->wpipe)) | ||
3684 | #else | ||
3685 | (0 != PIPE (d->wpipe)) | ||
3686 | #endif | ||
3687 | ) | ||
3688 | { | ||
3689 | #if HAVE_MESSAGES | ||
3690 | MHD_DLOG (daemon, | ||
3691 | "Failed to create worker control pipe: %s\n", | ||
3692 | STRERROR (errno)); | ||
3693 | #endif | ||
3694 | goto thread_failed; | ||
3695 | } | ||
3696 | #ifndef WINDOWS | ||
3697 | if ( (0 == (flags & MHD_USE_POLL)) && | ||
3698 | (0 != (flags & MHD_USE_SUSPEND_RESUME)) && | ||
3699 | (d->wpipe[0] >= FD_SETSIZE) ) | ||
3700 | { | ||
3701 | #if HAVE_MESSAGES | ||
3702 | MHD_DLOG (daemon, | ||
3703 | "file descriptor for worker control pipe exceeds maximum value\n"); | ||
3704 | #endif | ||
3705 | if (0 != CLOSE (d->wpipe[0])) | ||
3706 | MHD_PANIC ("close failed\n"); | ||
3707 | if (0 != CLOSE (d->wpipe[1])) | ||
3708 | MHD_PANIC ("close failed\n"); | ||
3709 | goto thread_failed; | ||
3710 | } | ||
3711 | #endif | ||
3712 | |||
3591 | /* Divide available connections evenly amongst the threads. | 3713 | /* Divide available connections evenly amongst the threads. |
3592 | * Thread indexes in [0, leftover_conns) each get one of the | 3714 | * Thread indexes in [0, leftover_conns) each get one of the |
3593 | * leftover connections. */ | 3715 | * leftover connections. */ |
@@ -3842,9 +3964,9 @@ MHD_stop_daemon (struct MHD_Daemon *daemon) | |||
3842 | /* MHD_USE_NO_LISTEN_SOCKET disables thread pools, hence we need to check */ | 3964 | /* MHD_USE_NO_LISTEN_SOCKET disables thread pools, hence we need to check */ |
3843 | for (i = 0; i < daemon->worker_pool_size; ++i) | 3965 | for (i = 0; i < daemon->worker_pool_size; ++i) |
3844 | { | 3966 | { |
3845 | if (-1 != daemon->wpipe[1]) | 3967 | if (-1 != daemon->worker_pool[i].wpipe[1]) |
3846 | { | 3968 | { |
3847 | if (1 != WRITE (daemon->wpipe[1], "e", 1)) | 3969 | if (1 != WRITE (daemon->worker_pool[i].wpipe[1], "e", 1)) |
3848 | MHD_PANIC ("failed to signal shutdown via pipe"); | 3970 | MHD_PANIC ("failed to signal shutdown via pipe"); |
3849 | } | 3971 | } |
3850 | if (0 != (rc = pthread_join (daemon->worker_pool[i].pid, &unused))) | 3972 | if (0 != (rc = pthread_join (daemon->worker_pool[i].pid, &unused))) |
diff --git a/src/microhttpd/internal.h b/src/microhttpd/internal.h index adf2c660..feee0aab 100644 --- a/src/microhttpd/internal.h +++ b/src/microhttpd/internal.h | |||
@@ -834,6 +834,15 @@ struct MHD_Connection | |||
834 | */ | 834 | */ |
835 | int tls_read_ready; | 835 | int tls_read_ready; |
836 | 836 | ||
837 | /** | ||
838 | * Is the connection suspended? | ||
839 | */ | ||
840 | int suspended; | ||
841 | |||
842 | /** | ||
843 | * Is the connection wanting to resume? | ||
844 | */ | ||
845 | int resuming; | ||
837 | #endif | 846 | #endif |
838 | }; | 847 | }; |
839 | 848 | ||
@@ -894,6 +903,16 @@ struct MHD_Daemon | |||
894 | struct MHD_Connection *connections_tail; | 903 | struct MHD_Connection *connections_tail; |
895 | 904 | ||
896 | /** | 905 | /** |
906 | * Head of doubly-linked list of our current but suspended connections. | ||
907 | */ | ||
908 | struct MHD_Connection *suspended_connections_head; | ||
909 | |||
910 | /** | ||
911 | * Tail of doubly-linked list of our current but suspended connections. | ||
912 | */ | ||
913 | struct MHD_Connection *suspended_connections_tail; | ||
914 | |||
915 | /** | ||
897 | * Head of doubly-linked list of connections to clean up. | 916 | * Head of doubly-linked list of connections to clean up. |
898 | */ | 917 | */ |
899 | struct MHD_Connection *cleanup_head; | 918 | struct MHD_Connection *cleanup_head; |
@@ -1088,6 +1107,11 @@ struct MHD_Daemon | |||
1088 | */ | 1107 | */ |
1089 | int shutdown; | 1108 | int shutdown; |
1090 | 1109 | ||
1110 | /* | ||
1111 | * Do we need to process resuming connections? | ||
1112 | */ | ||
1113 | int resuming; | ||
1114 | |||
1091 | /** | 1115 | /** |
1092 | * Limit on the number of parallel connections. | 1116 | * Limit on the number of parallel connections. |
1093 | */ | 1117 | */ |