diff --git a/lib/multi.c b/lib/multi.c index d0fa68ab4c..d6ae111d8e 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -531,6 +531,8 @@ CURLMcode curl_multi_add_handle(CURLM *m, CURL *curl) /* set the easy handle */ multistate(data, MSTATE_INIT); + /* not yet passed INIT state */ + data->state.really_alive = FALSE; #ifdef USE_LIBPSL /* Do the same for PSL. */ @@ -570,12 +572,6 @@ CURLMcode curl_multi_add_handle(CURLM *m, CURL *curl) data->set.server_response_timeout; multi->admin->set.no_signal = data->set.no_signal; - mresult = multi_assess_wakeup(multi); - if(mresult) { - failf(data, "error enabling wakeup listening: %d", mresult); - return mresult; - } - CURL_TRC_M(data, "added to multi, mid=%u, running=%u, total=%u", data->mid, Curl_multi_xfers_running(multi), Curl_uint32_tbl_count(&multi->xfers)); @@ -851,6 +847,12 @@ CURLMcode curl_multi_remove_handle(CURLM *m, CURL *curl) /* If in `msgsent`, it was deducted from `multi->xfers_alive` already. */ if(!Curl_uint32_bset_contains(&multi->msgsent, data->mid)) --multi->xfers_alive; + if(data->state.really_alive) { + data->state.really_alive = FALSE; + --multi->xfers_really_alive; + if(!multi->xfers_really_alive) + (void)multi_assess_wakeup(multi); + } Curl_wildcard_dtor(&data->wildcard); @@ -1151,7 +1153,9 @@ CURLMcode Curl_multi_pollset(struct Curl_easy *data, /* The admin handle always listens on the wakeup socket when there * are transfers alive. */ if(data->multi && (data == data->multi->admin) && - data->multi->xfers_alive) { + data->multi->xfers_really_alive) { + CURL_TRC_M(data, "adding wakeup, %u xfers really alive", + data->multi->xfers_really_alive); result = Curl_pollset_add_in(data, ps, data->multi->wakeup_pair[0]); } #endif @@ -2459,6 +2463,12 @@ static void handle_completed(struct Curl_multi *multi, Curl_uint32_bset_remove(&multi->dirty, data->mid); Curl_uint32_bset_remove(&multi->pending, data->mid); Curl_uint32_bset_add(&multi->msgsent, data->mid); + if(data->state.really_alive) { + data->state.really_alive = FALSE; + --multi->xfers_really_alive; + if(!multi->xfers_really_alive) + (void)multi_assess_wakeup(multi); + } --multi->xfers_alive; if(!multi->xfers_alive) multi_assess_wakeup(multi); @@ -2466,6 +2476,18 @@ static void handle_completed(struct Curl_multi *multi, static CURLMcode multistate_init(struct Curl_easy *data, CURLcode *result) { + if(!data->state.really_alive) { + data->state.really_alive = TRUE; + ++data->multi->xfers_really_alive; + if(data->multi->xfers_really_alive == 1) { + CURLMcode mresult = multi_assess_wakeup(data->multi); + if(mresult) { + failf(data, "error enabling wakeup listening: %d", mresult); + return mresult; + } + } + } + *result = Curl_pretransfer(data); if(*result) return CURLM_OK; diff --git a/lib/multihandle.h b/lib/multihandle.h index c5cdfbe82e..19dd2ffcdf 100644 --- a/lib/multihandle.h +++ b/lib/multihandle.h @@ -85,6 +85,8 @@ struct Curl_multi { unsigned int xfers_alive; /* amount of added transfers that have not yet reached COMPLETE state */ + unsigned int xfers_really_alive; /* amount of added transfers that have + passed INIT state but are not COMPLETE yet */ curl_off_t xfers_total_ever; /* total of added transfers, ever. */ struct uint32_tbl xfers; /* transfers added to this multi */ /* Each transfer's mid may be present in at most one of these */ diff --git a/lib/urldata.h b/lib/urldata.h index 232364fcf3..d4d336d8db 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -704,11 +704,7 @@ struct UrlState { uint8_t httpreq; /* Curl_HttpReq; what kind of HTTP request (if any) is this */ - /* when curl_easy_perform() is called, the multi handle is "owned" by - the easy handle so curl_easy_cleanup() on such an easy handle will - also close the multi handle! */ - BIT(multi_owned_by_easy); - + BIT(really_alive); /* transfer is really alive in multi, passed INIT */ BIT(this_is_a_follow); /* this is a followed Location: request */ BIT(refused_stream); /* this was refused, try again */ BIT(errorbuf); /* Set to TRUE if the error buffer is already filled in. diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index f537ba181b..705b8be4cd 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -262,7 +262,7 @@ test2300 test2301 test2302 test2303 test2304 test2306 test2307 test2308 \ test2309 test2310 test2311 \ \ test2400 test2401 test2402 test2403 test2404 test2405 test2406 test2407 \ -test2408 test2409 test2410 test2411 \ +test2408 test2409 test2410 test2411 test2412 \ \ test2500 test2501 test2502 test2503 test2504 test2505 test2506 \ \ diff --git a/tests/data/test2412 b/tests/data/test2412 new file mode 100644 index 0000000000..e0320e2ce4 --- /dev/null +++ b/tests/data/test2412 @@ -0,0 +1,50 @@ + + + + +multi + + + +# Server-side + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT +ETag: "21025-dc7-39462498" +Accept-Ranges: bytes +Content-Length: 6007 +Connection: close +Content-Type: text/html +Funny-head: yesyes + +-foo- +%repeat[1000 x foobar]% + + + +# Client-side + + +wakeup + + +http + + +lib%TESTNUMBER + + +checking curl_multi_fdset on nothing to do + + +http://%HOSTIP:%HTTPPORT/%TESTNUMBER + + + +# Verify data after the test has been "shot" + + + diff --git a/tests/libtest/Makefile.inc b/tests/libtest/Makefile.inc index 98c9939994..bec648542b 100644 --- a/tests/libtest/Makefile.inc +++ b/tests/libtest/Makefile.inc @@ -115,6 +115,7 @@ TESTS_C = \ lib2023.c lib2032.c lib2082.c \ lib2301.c lib2302.c lib2304.c lib2306.c lib2308.c lib2309.c \ lib2402.c lib2404.c lib2405.c \ + lib2412.c \ lib2502.c lib2504.c lib2505.c lib2506.c \ lib2700.c \ lib3010.c lib3025.c lib3026.c lib3027.c lib3033.c lib3034.c \ diff --git a/tests/libtest/lib2412.c b/tests/libtest/lib2412.c new file mode 100644 index 0000000000..79d49a2d76 --- /dev/null +++ b/tests/libtest/lib2412.c @@ -0,0 +1,95 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Dmitry Karpov + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "first.h" +#include "testtrace.h" + +static CURLcode test_lib2412(const char *URL) +{ + CURLcode result = CURLE_OK; + CURLM *multi = NULL; + CURL *easy = NULL; + CURLMcode rc; + fd_set readFdSet, writeFdSet, exceptFdSet; + int maxFd; + + (void)URL; + global_init(CURL_GLOBAL_ALL); + + multi = curl_multi_init(); + if(!multi) { + curl_mfprintf(stderr, "curl_multi_init() failed\n"); + result = TEST_ERR_MAJOR_BAD; + goto test_cleanup; + } + + easy = curl_easy_init(); + if(!easy) { + curl_mfprintf(stderr, "curl_easy_init() failed\n"); + result = TEST_ERR_MAJOR_BAD; + goto test_cleanup; + } + debug_config.nohex = TRUE; + debug_config.tracetime = TRUE; + easy_setopt(easy, CURLOPT_DEBUGDATA, &debug_config); + easy_setopt(easy, CURLOPT_DEBUGFUNCTION, libtest_debug_cb); + easy_setopt(easy, CURLOPT_VERBOSE, 1L); + + rc = curl_multi_add_handle(multi, easy); + if(rc) { + curl_mfprintf(stderr, "curl_multi_add_handle() failed: %d\n", rc); + result = TEST_ERR_MAJOR_BAD; + goto test_cleanup; + } + + FD_ZERO(&readFdSet); + FD_ZERO(&writeFdSet); + FD_ZERO(&exceptFdSet); + maxFd = -1; + rc = curl_multi_fdset(multi, &readFdSet, &writeFdSet, &exceptFdSet, + &maxFd); + if(rc) { + curl_mfprintf(stderr, "curl_multi_fdset() failed: %d\n", rc); + result = TEST_ERR_MAJOR_BAD; + goto test_cleanup; + } + + if(maxFd == -1) + curl_mfprintf(stderr, "There are no file descriptors to wait for\n"); + else { + curl_mfprintf(stderr, "libcurl supplied a file descriptor to " + "wait for (maxFd=%d). Waiting now ...\n", maxFd); + result = TEST_ERR_FAILURE; + } + +test_cleanup: + if(easy) { + curl_multi_remove_handle(multi, easy); + curl_easy_cleanup(easy); + } + if(multi) + curl_multi_cleanup(multi); + curl_global_cleanup(); + return result; +} diff --git a/tests/libtest/lib530.c b/tests/libtest/lib530.c index d4c894d1d0..bddb857be0 100644 --- a/tests/libtest/lib530.c +++ b/tests/libtest/lib530.c @@ -29,6 +29,7 @@ */ #include "first.h" +#include "testtrace.h" static struct t530_ctx { int socket_calls; @@ -300,6 +301,8 @@ static CURLcode testone(const char *URL, int timer_fail_at, int socket_fail_at) easy_setopt(curl, CURLOPT_URL, URL); /* go verbose */ + easy_setopt(curl, CURLOPT_DEBUGDATA, &debug_config); + easy_setopt(curl, CURLOPT_DEBUGFUNCTION, libtest_debug_cb); easy_setopt(curl, CURLOPT_VERBOSE, 1L); multi_init(multi);