libmicrohttpd

HTTP/1.x server C library (MHD 1.x, stable)
Log | Files | Refs | Submodules | README | LICENSE

commit 1c56a767212c4808ee892fb967bfc956f2246cdf
parent 88dde2121306c6eb3f68980afd9755d7ce001a5e
Author: Christian Grothoff <christian@grothoff.org>
Date:   Wed, 14 Oct 2009 12:26:42 +0000

From: 
Mike Crowe <mac@mcrowe.com>
  To: 
  libmicrohttpd@gnu.org
    Date: 
    Yesterday 17:41:12
       
	 I've found a difficult to reproduce deadlock in v0.4.2 when calling
	 MHD_stop_daemon while a connection is active. I narrowed it down to
	 MHD_handle_connection racing against MHD_cleanup_connections. It can
	 be reproduced much more easily by inserting a sleep just before the
	 call to SELECT in MHD_handle_connection. I'm using
	 MHD_USE_THREAD_PER_CONNECTION.

	 The problem appears to occur when MHD_stop_daemon sets
	 daemon->shutdown and sends SIGALRM signal to the thread running inside
	 MHD_handle_connection while it is between the top of the loop where
	 con->daemon->shutdown is checked and the call to SELECT. The signal
	 has no persistent effect and the thread ends up blocked forever inside
	 select.

	 The only workarounds I can come up with are using an explicit pipe to
	 wake the thread (which is stateful so it can be relied upon to wake
	 the select) or to force the socket closed so that the select wakes up
	 (but this sounds rather more dangerous and is racey if anyone else is
	 opening file descriptors.)

	 Here's a patch which reproduces the problem under 'make check'. I'm
	 running on Debian GNU/Linux 5.0 on amd64 but I've also reproduced the
	 problem on i386.

	 I can clean up the patch to make it suitable for checking in but the
	 chances of hitting the problem without the sleep in
	 MHD_handle_connection are very small.


Diffstat:
Msrc/testcurl/daemontest_get.c | 77+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------
Msrc/testcurl/daemontest_large_put.c | 12+++++++++---
2 files changed, 70 insertions(+), 19 deletions(-)

diff --git a/src/testcurl/daemontest_get.c b/src/testcurl/daemontest_get.c @@ -1,6 +1,6 @@ /* This file is part of libmicrohttpd - (C) 2007 Christian Grothoff + (C) 2007, 2009 Christian Grothoff libmicrohttpd is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published @@ -118,9 +118,9 @@ testInternalGet () curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); else curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); - // NOTE: use of CONNECTTIMEOUT without also - // setting NOSIGNAL results in really weird - // crashes on my system! + /* NOTE: use of CONNECTTIMEOUT without also + setting NOSIGNAL results in really weird + crashes on my system!*/ curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1); if (CURLE_OK != (errornum = curl_easy_perform (c))) { @@ -167,9 +167,9 @@ testMultithreadedGet () else curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 15L); - // NOTE: use of CONNECTTIMEOUT without also - // setting NOSIGNAL results in really weird - // crashes on my system! + /* NOTE: use of CONNECTTIMEOUT without also + setting NOSIGNAL results in really weird + crashes on my system! */ curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1); if (CURLE_OK != (errornum = curl_easy_perform (c))) { @@ -217,9 +217,9 @@ testMultithreadedPoolGet () else curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 15L); - // NOTE: use of CONNECTTIMEOUT without also - // setting NOSIGNAL results in really weird - // crashes on my system! + /* NOTE: use of CONNECTTIMEOUT without also + setting NOSIGNAL results in really weird + crashes on my system!*/ curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1); if (CURLE_OK != (errornum = curl_easy_perform (c))) { @@ -276,9 +276,9 @@ testExternalGet () curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 15L); - // NOTE: use of CONNECTTIMEOUT without also - // setting NOSIGNAL results in really weird - // crashes on my system! + /* NOTE: use of CONNECTTIMEOUT without also + setting NOSIGNAL results in really weird + crashes on my system! */ curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1); @@ -412,9 +412,9 @@ testUnknownPortGet () curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); else curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); - // NOTE: use of CONNECTTIMEOUT without also - // setting NOSIGNAL results in really weird - // crashes on my system! + /* NOTE: use of CONNECTTIMEOUT without also + setting NOSIGNAL results in really weird + crashes on my system! */ curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1); if (CURLE_OK != (errornum = curl_easy_perform (c))) { @@ -435,6 +435,50 @@ testUnknownPortGet () } +static int +testStopRace () +{ + struct sockaddr_in sin; + int fd; + struct MHD_Daemon *d; + + d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG, + 1081, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END); + if (d == NULL) + return 16; + + fd = socket(PF_INET, SOCK_STREAM, 0); + if (fd < 0) + { + fprintf(stderr, "socket: %m\n"); + return 256; + } + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = htons(1081); + sin.sin_addr.s_addr = htonl(0x7f000001); + + if (connect(fd, (struct sockaddr *)(&sin), sizeof(sin)) < 0) + { + fprintf(stderr, "connect: %m\n"); + return 512; + } + + /* printf("Waiting\n"); */ + /* Let the thread get going. */ + usleep(500000); + + /* printf("Stopping daemon\n"); */ + MHD_stop_daemon (d); + + close(fd); + + /* printf("good\n"); */ + return 0; +} + + int main (int argc, char *const *argv) @@ -449,6 +493,7 @@ main (int argc, char *const *argv) errorCount += testMultithreadedPoolGet (); errorCount += testExternalGet (); errorCount += testUnknownPortGet (); + errorCount += testStopRace (); if (errorCount != 0) fprintf (stderr, "Error (code: %u)\n", errorCount); curl_global_cleanup (); diff --git a/src/testcurl/daemontest_large_put.c b/src/testcurl/daemontest_large_put.c @@ -146,7 +146,9 @@ testInternalPut () cbc.pos = 0; d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG, 1080, - NULL, NULL, &ahc_echo, &done_flag, MHD_OPTION_END); + NULL, NULL, &ahc_echo, &done_flag, + MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (1024*1024), + MHD_OPTION_END); if (d == NULL) return 1; c = curl_easy_init (); @@ -202,7 +204,9 @@ testMultithreadedPut () cbc.pos = 0; d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG, 1081, - NULL, NULL, &ahc_echo, &done_flag, MHD_OPTION_END); + NULL, NULL, &ahc_echo, &done_flag, + MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (1024*1024), + MHD_OPTION_END); if (d == NULL) return 16; c = curl_easy_init (); @@ -262,7 +266,9 @@ testMultithreadedPoolPut () d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG, 1081, NULL, NULL, &ahc_echo, &done_flag, - MHD_OPTION_THREAD_POOL_SIZE, 4, MHD_OPTION_END); + MHD_OPTION_THREAD_POOL_SIZE, 4, + MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (1024*1024), + MHD_OPTION_END); if (d == NULL) return 16; c = curl_easy_init ();