aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2013-12-03 20:11:34 +0000
committerChristian Grothoff <christian@grothoff.org>2013-12-03 20:11:34 +0000
commit04ca76e78eaea8f40c4262aee530d2b2c3b281bf (patch)
tree0499583645c8477a6688a01f0f92bbf5735de1cc
parent4245c6e9c371a8434b13a37edbc4e6dc239813da (diff)
downloadlibmicrohttpd-04ca76e78eaea8f40c4262aee530d2b2c3b281bf.tar.gz
libmicrohttpd-04ca76e78eaea8f40c4262aee530d2b2c3b281bf.zip
Adding Matt Holiday's fixes to suspend/resume logic
(plus documentation updates).
-rw-r--r--ChangeLog29
-rw-r--r--doc/libmicrohttpd.texi8
-rw-r--r--src/include/microhttpd.h8
-rw-r--r--src/microhttpd/connection.c15
-rw-r--r--src/microhttpd/daemon.c250
-rw-r--r--src/microhttpd/internal.h24
6 files changed, 264 insertions, 70 deletions
diff --git a/ChangeLog b/ChangeLog
index cecfb9fe..8f71eeb1 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,32 @@
1Tue 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
1Fri Nov 29 20:17:03 CET 2013 30Fri 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
520specify it), if @code{MHD_USE_NO_LISTEN_SOCKET} is specified. In 520specify 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
524Enables using @code{MHD_suspend_connection} and
525@code{MHD_resume_connection}, as performing these calls requires some
526additional pipes to be created, and code not using these calls should
527not 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
1856thread-per-connection!) for a while. 1862thread-per-connection!) for a while.
1857 1863
1858If you use this API in conjunction with a internal select or a 1864If you use this API in conjunction with a internal select or a
1859thread pool, you must set the option @code{MHD_USE_PIPE_FOR_SHUTDOWN} to 1865thread pool, you must set the option @code{MHD_USE_SUSPEND_RESUME} to
1860ensure that a resumed connection is immediately processed by MHD. 1866ensure that a resumed connection is immediately processed by MHD.
1861 1867
1862Suspended connections continue to count against the total number of 1868Suspended 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 */
1485static void
1486resume_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 */