diff options
author | Christian Grothoff <christian@grothoff.org> | 2011-09-21 07:47:03 +0000 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2011-09-21 07:47:03 +0000 |
commit | 7ef544c9bed6159c46be44a3d130e0de094a345f (patch) | |
tree | 5ed9708867336e51f36dbd4e1c9778ae625e22cf | |
parent | 211920f3ef774ab6009814d40caca9358881ba34 (diff) | |
download | libmicrohttpd-7ef544c9bed6159c46be44a3d130e0de094a345f.tar.gz libmicrohttpd-7ef544c9bed6159c46be44a3d130e0de094a345f.zip |
From:
Will Bryant <will.bryant@gmail.com>
To:
libmicrohttpd development and user mailinglist <libmicrohttpd@gnu.org>
Date:
Yesterday 03:59:23 PM
Attachments:
0001-use-a-pipe-to-signal-shutdown-to-select-and-poll.patch
I attach my current work. I have attempted to preserve and restore as much of the 'old' pipe shutdown code that was present in earlier versions of the library, but have removed the sections that aren't relevant now that there is a socket-by-socket client shutdown procedure.
On OS X, all the 'regular' tests pass. The two perf test programs both fail on a random subset of the tests with "Failed to bind to port 1081: Address already in use" (or the other ports that the perf tests use).
I initially thought that this was a problem with this patch, but after I couldn't find any problem with it I tried backporting the perf test programs to the older 0.9.7 version of libmicrohttpd, which is before the listening socket shutdown code went in, and it turned out that the older versions failed in the same way.
On OpenIndiana (oi_151a - but should behave the same as other SunOS/Solaris/OpenSolaris/Illumos kernels), the same problem occurs. (I haven't tried the backport there too.)
Linux is known to have slightly different behavior for the SOL_REUSEADDR option which might explain this, but it's quite possible that this is just a timing issue with the cleanup of the client threads in these perf test programs (if you wait for 1s between the tests then the problem goes away).
On OS X, using both debugging printfs and shelling out to lsof -itcp, I have confirmed that the listening socket has been closed successfully before the error occurs. So I am starting to suspect that it is not possible to bind to an address that was used as a server socket until all the client sockets have also been cleaned up - but that's just a theory at this point.
Anyway, as far as this patch goes, OS X looks a lot healthier - as healthy as it was in 0.9.7 (0.9.8 and 0.9.9 both failed to build entirely).
There is one further test failure on openindiana, which I believe is not related to this patch - all the same 'regular' tests pass except daemontest_get_response_cleanup, which dies due to an unhandled SIGPIPE when trying to write to the socket after killing the curl process:
Program received signal SIGPIPE, Broken pipe.
[Switching to Thread 2 (LWP 2)]
0xfed634f7 in __so_send () from /lib/libc.so.1
(gdb) bt
#0 0xfed634f7 in __so_send () from /lib/libc.so.1
#1 0xfed511d7 in _so_send () from /lib/libc.so.1
#2 0xfe7dcb26 in send () from /lib/libsocket.so.1
#3 0xfef56d29 in send_param_adapter (connection=0x20, other=0xfe99080f, i=0) at daemon.c:764
#4 0xfef54a0b in do_write (connection=0x8073ad0) at connection.c:1495
#5 0xfef54f6f in MHD_connection_handle_write (connection=0x8073ad0) at connection.c:1909
#6 0xfef57b3c in MHD_select (daemon=0x80737a8, may_block=0) at daemon.c:1367
#7 0xfef57ff4 in MHD_select_thread (cls=0x80737a8) at daemon.c:1615
#8 0xfed5f0f3 in _thrp_setup () from /lib/libc.so.1
#9 0xfed5f3a0 in ?? () from /lib/libc.so.1
#10 0xfe9a0240 in ?? ()
#11 0x00000000 in ?? ()
(gdb) info threads
* 4 Thread 2 (LWP 2) 0xfed634f7 in __so_send () from /lib/libc.so.1
3 LWP 2 0xfed634f7 in __so_send () from /lib/libc.so.1
2 Thread 1 (LWP 1) 0xfed63e87 in __waitid () from /lib/libc.so.1
1 LWP 1 0xfed63e87 in __waitid () from /lib/libc.so.1
(gdb) thread 2
[Switching to thread 2 (Thread 1 (LWP 1))]#0 0xfed63e87 in __waitid () from /lib/libc.so.1
(gdb) bt
#0 0xfed63e87 in __waitid () from /lib/libc.so.1
#1 0xfed515bd in waitid () from /lib/libc.so.1
#2 0xfed00035 in waitpid () from /lib/libc.so.1
#3 0x08051423 in kill_curl (pid=4479) at daemontest_get_response_cleanup.c:70
#4 0x0805157d in main (argc=1, argv=0x6b) at daemontest_get_response_cleanup.c:141
0001-use-a-pipe-to-signal-shutdown-to-select-and-poll.patch
-rw-r--r-- | AUTHORS | 1 | ||||
-rw-r--r-- | src/daemon/daemon.c | 92 | ||||
-rw-r--r-- | src/daemon/internal.h | 7 |
3 files changed, 88 insertions, 12 deletions
@@ -33,6 +33,7 @@ Eivind Sarto <ivan@espial.com> | |||
33 | Thomas Stalder <gnunet@netsolux.ch> | 33 | Thomas Stalder <gnunet@netsolux.ch> |
34 | Zhimin Huang <zhimin.huang@bqvision.com> | 34 | Zhimin Huang <zhimin.huang@bqvision.com> |
35 | Jan Seeger <jan.seeger@thenybble.de> | 35 | Jan Seeger <jan.seeger@thenybble.de> |
36 | Will Bryant <will.bryant@gmail.com> | ||
36 | 37 | ||
37 | Documentation contributions also came from: | 38 | Documentation contributions also came from: |
38 | Marco Maggi <marco.maggi-ipsu@poste.it> | 39 | Marco Maggi <marco.maggi-ipsu@poste.it> |
diff --git a/src/daemon/daemon.c b/src/daemon/daemon.c index b0aca594..aedbf76c 100644 --- a/src/daemon/daemon.c +++ b/src/daemon/daemon.c | |||
@@ -1307,6 +1307,15 @@ MHD_select (struct MHD_Daemon *daemon, | |||
1307 | FD_SET (max, &rs); | 1307 | FD_SET (max, &rs); |
1308 | } | 1308 | } |
1309 | 1309 | ||
1310 | #ifndef HAVE_LISTEN_SHUTDOWN | ||
1311 | if (-1 == daemon->wpipe[0]) | ||
1312 | return MHD_NO; | ||
1313 | FD_SET (daemon->wpipe[0], &rs); | ||
1314 | /* update max file descriptor */ | ||
1315 | if (max < daemon->wpipe[0]) | ||
1316 | max = daemon->wpipe[0]; | ||
1317 | #endif | ||
1318 | |||
1310 | tv = NULL; | 1319 | tv = NULL; |
1311 | if (may_block == MHD_NO) | 1320 | if (may_block == MHD_NO) |
1312 | { | 1321 | { |
@@ -1389,7 +1398,11 @@ MHD_poll_all (struct MHD_Daemon *daemon, | |||
1389 | pos = pos->next; | 1398 | pos = pos->next; |
1390 | } | 1399 | } |
1391 | { | 1400 | { |
1401 | #ifdef HAVE_LISTEN_SHUTDOWN | ||
1392 | struct pollfd p[1 + num_connections]; | 1402 | struct pollfd p[1 + num_connections]; |
1403 | #else | ||
1404 | struct pollfd p[2 + num_connections]; | ||
1405 | #endif | ||
1393 | struct MHD_Pollfd mp; | 1406 | struct MHD_Pollfd mp; |
1394 | unsigned MHD_LONG_LONG ltimeout; | 1407 | unsigned MHD_LONG_LONG ltimeout; |
1395 | unsigned int i; | 1408 | unsigned int i; |
@@ -1399,10 +1412,17 @@ MHD_poll_all (struct MHD_Daemon *daemon, | |||
1399 | memset (p, 0, sizeof (p)); | 1412 | memset (p, 0, sizeof (p)); |
1400 | if ( (daemon->max_connections > 0) && (daemon->socket_fd != -1) ) | 1413 | if ( (daemon->max_connections > 0) && (daemon->socket_fd != -1) ) |
1401 | { | 1414 | { |
1402 | poll_server = 1; | ||
1403 | p[0].fd = daemon->socket_fd; | 1415 | p[0].fd = daemon->socket_fd; |
1404 | p[0].events = POLLIN; | 1416 | p[0].events = POLLIN; |
1405 | p[0].revents = 0; | 1417 | p[0].revents = 0; |
1418 | #ifdef HAVE_LISTEN_SHUTDOWN | ||
1419 | poll_server = 1; | ||
1420 | #else | ||
1421 | p[1].fd = daemon->wpipe[0]; | ||
1422 | p[1].events = POLLIN; | ||
1423 | p[1].revents = 0; | ||
1424 | poll_server = 2; | ||
1425 | #endif | ||
1406 | } | 1426 | } |
1407 | else | 1427 | else |
1408 | { | 1428 | { |
@@ -1464,7 +1484,7 @@ MHD_poll_all (struct MHD_Daemon *daemon, | |||
1464 | pos->idle_handler (pos); | 1484 | pos->idle_handler (pos); |
1465 | i++; | 1485 | i++; |
1466 | } | 1486 | } |
1467 | if ( (1 == poll_server) && | 1487 | if ( (0 != poll_server) && |
1468 | (0 != (p[0].revents & POLLIN)) ) | 1488 | (0 != (p[0].revents & POLLIN)) ) |
1469 | MHD_accept_connection (daemon); | 1489 | MHD_accept_connection (daemon); |
1470 | } | 1490 | } |
@@ -1483,22 +1503,27 @@ static int | |||
1483 | MHD_poll_listen_socket (struct MHD_Daemon *daemon, | 1503 | MHD_poll_listen_socket (struct MHD_Daemon *daemon, |
1484 | int may_block) | 1504 | int may_block) |
1485 | { | 1505 | { |
1486 | struct pollfd p; | 1506 | #ifdef HAVE_LISTEN_SHUTDOWN |
1507 | struct pollfd p[1]; | ||
1508 | #else | ||
1509 | struct pollfd p[2]; | ||
1510 | #endif | ||
1487 | int timeout; | 1511 | int timeout; |
1488 | 1512 | ||
1489 | memset (&p, 0, sizeof (p)); | 1513 | memset (&p, 0, sizeof (p)); |
1490 | p.fd = daemon->socket_fd; | 1514 | p[0].fd = daemon->socket_fd; |
1491 | p.events = POLLIN; | 1515 | p[0].events = POLLIN; |
1492 | p.revents = 0; | 1516 | p[0].revents = 0; |
1517 | #ifndef HAVE_LISTEN_SHUTDOWN | ||
1518 | p[1].fd = daemon->wpipe[0]; | ||
1519 | p[1].events = POLLIN; | ||
1520 | p[1].revents = 0; | ||
1521 | #endif | ||
1493 | if (may_block == MHD_NO) | 1522 | if (may_block == MHD_NO) |
1494 | timeout = 0; | 1523 | timeout = 0; |
1495 | else | 1524 | else |
1496 | timeout = -1; | 1525 | timeout = -1; |
1497 | #ifdef __CYGWIN__ | 1526 | if (poll (p, (sizeof(p)/sizeof(struct pollfd)), timeout) < 0) |
1498 | /* See https://gnunet.org/bugs/view.php?id=1674 */ | ||
1499 | timeout = ( (timeout > 2000) || (timeout < 0) ) ? 2000 : timeout; | ||
1500 | #endif | ||
1501 | if (poll (&p, 1, timeout) < 0) | ||
1502 | { | 1527 | { |
1503 | if (errno == EINTR) | 1528 | if (errno == EINTR) |
1504 | return MHD_YES; | 1529 | return MHD_YES; |
@@ -1512,7 +1537,7 @@ MHD_poll_listen_socket (struct MHD_Daemon *daemon, | |||
1512 | return MHD_NO; | 1537 | return MHD_NO; |
1513 | if (daemon->socket_fd < 0) | 1538 | if (daemon->socket_fd < 0) |
1514 | return MHD_YES; | 1539 | return MHD_YES; |
1515 | if (0 != (p.revents & POLLIN)) | 1540 | if (0 != (p[0].revents & POLLIN)) |
1516 | MHD_accept_connection (daemon); | 1541 | MHD_accept_connection (daemon); |
1517 | return MHD_YES; | 1542 | return MHD_YES; |
1518 | } | 1543 | } |
@@ -1971,6 +1996,34 @@ MHD_start_daemon_va (unsigned int options, | |||
1971 | retVal->pool_size = MHD_POOL_SIZE_DEFAULT; | 1996 | retVal->pool_size = MHD_POOL_SIZE_DEFAULT; |
1972 | retVal->unescape_callback = &MHD_http_unescape; | 1997 | retVal->unescape_callback = &MHD_http_unescape; |
1973 | retVal->connection_timeout = 0; /* no timeout */ | 1998 | retVal->connection_timeout = 0; /* no timeout */ |
1999 | #ifndef HAVE_LISTEN_SHUTDOWN | ||
2000 | retVal->wpipe[0] = -1; | ||
2001 | retVal->wpipe[1] = -1; | ||
2002 | if (0 != pipe (retVal->wpipe)) | ||
2003 | { | ||
2004 | #if HAVE_MESSAGES | ||
2005 | FPRINTF(stderr, | ||
2006 | "Failed to create control pipe: %s\n", | ||
2007 | STRERROR (errno)); | ||
2008 | #endif | ||
2009 | free (retVal); | ||
2010 | return NULL; | ||
2011 | } | ||
2012 | #ifndef WINDOWS | ||
2013 | if ( (0 == (options & MHD_USE_POLL)) && | ||
2014 | (retVal->wpipe[0] >= FD_SETSIZE) ) | ||
2015 | { | ||
2016 | #if HAVE_MESSAGES | ||
2017 | FPRINTF(stderr, | ||
2018 | "file descriptor for control pipe exceeds maximum value\n"); | ||
2019 | #endif | ||
2020 | close (retVal->wpipe[0]); | ||
2021 | close (retVal->wpipe[1]); | ||
2022 | free (retVal); | ||
2023 | return NULL; | ||
2024 | } | ||
2025 | #endif | ||
2026 | #endif | ||
1974 | #ifdef DAUTH_SUPPORT | 2027 | #ifdef DAUTH_SUPPORT |
1975 | retVal->digest_auth_rand_size = 0; | 2028 | retVal->digest_auth_rand_size = 0; |
1976 | retVal->digest_auth_random = NULL; | 2029 | retVal->digest_auth_random = NULL; |
@@ -2464,12 +2517,15 @@ MHD_stop_daemon (struct MHD_Daemon *daemon) | |||
2464 | int fd; | 2517 | int fd; |
2465 | unsigned int i; | 2518 | unsigned int i; |
2466 | int rc; | 2519 | int rc; |
2520 | char c; | ||
2467 | 2521 | ||
2468 | if (daemon == NULL) | 2522 | if (daemon == NULL) |
2469 | return; | 2523 | return; |
2470 | daemon->shutdown = MHD_YES; | 2524 | daemon->shutdown = MHD_YES; |
2471 | fd = daemon->socket_fd; | 2525 | fd = daemon->socket_fd; |
2472 | daemon->socket_fd = -1; | 2526 | daemon->socket_fd = -1; |
2527 | if (daemon->wpipe[1] != -1) | ||
2528 | write (daemon->wpipe[1], "e", 1); | ||
2473 | 2529 | ||
2474 | /* Prepare workers for shutdown */ | 2530 | /* Prepare workers for shutdown */ |
2475 | for (i = 0; i < daemon->worker_pool_size; ++i) | 2531 | for (i = 0; i < daemon->worker_pool_size; ++i) |
@@ -2548,6 +2604,18 @@ MHD_stop_daemon (struct MHD_Daemon *daemon) | |||
2548 | #endif | 2604 | #endif |
2549 | pthread_mutex_destroy (&daemon->per_ip_connection_mutex); | 2605 | pthread_mutex_destroy (&daemon->per_ip_connection_mutex); |
2550 | pthread_mutex_destroy (&daemon->cleanup_connection_mutex); | 2606 | pthread_mutex_destroy (&daemon->cleanup_connection_mutex); |
2607 | |||
2608 | #ifndef HAVE_LISTEN_SHUTDOWN | ||
2609 | if (daemon->wpipe[1] != -1) | ||
2610 | { | ||
2611 | /* just to be sure, remove the one char we | ||
2612 | wrote into the pipe */ | ||
2613 | (void) read (daemon->wpipe[0], &c, 1); | ||
2614 | close (daemon->wpipe[0]); | ||
2615 | close (daemon->wpipe[1]); | ||
2616 | } | ||
2617 | #endif | ||
2618 | |||
2551 | free (daemon); | 2619 | free (daemon); |
2552 | } | 2620 | } |
2553 | 2621 | ||
diff --git a/src/daemon/internal.h b/src/daemon/internal.h index 17f2a05c..1b252d23 100644 --- a/src/daemon/internal.h +++ b/src/daemon/internal.h | |||
@@ -896,6 +896,13 @@ struct MHD_Daemon | |||
896 | */ | 896 | */ |
897 | int socket_fd; | 897 | int socket_fd; |
898 | 898 | ||
899 | #ifndef HAVE_LISTEN_SHUTDOWN | ||
900 | /** | ||
901 | * Pipe we use to signal shutdown. | ||
902 | */ | ||
903 | int wpipe[2]; | ||
904 | #endif | ||
905 | |||
899 | /** | 906 | /** |
900 | * Are we shutting down? | 907 | * Are we shutting down? |
901 | */ | 908 | */ |