From f3c35e371cc70f1b6bc33f7faa904d37d1567eb3 Mon Sep 17 00:00:00 2001 From: Gergely Nagy Date: Sun, 17 Nov 2019 15:12:15 +0100 Subject: multi: add curl_multi_wakeup() This commit adds curl_multi_wakeup() which was previously in the TODO list under the curl_multi_unblock name. On some platforms and with some configurations this feature might not be available or can fail, in these cases a new error code (CURLM_WAKEUP_FAILURE) is returned from curl_multi_wakeup(). Fixes #4418 Closes #4608 --- tests/libtest/Makefile.inc | 10 ++- tests/libtest/lib1564.c | 142 +++++++++++++++++++++++++++++++ tests/libtest/lib1565.c | 204 +++++++++++++++++++++++++++++++++++++++++++++ tests/libtest/test.h | 54 ++++++++++++ 4 files changed, 409 insertions(+), 1 deletion(-) create mode 100644 tests/libtest/lib1564.c create mode 100644 tests/libtest/lib1565.c (limited to 'tests/libtest') diff --git a/tests/libtest/Makefile.inc b/tests/libtest/Makefile.inc index 9ba72d7de..374a66747 100644 --- a/tests/libtest/Makefile.inc +++ b/tests/libtest/Makefile.inc @@ -31,7 +31,7 @@ noinst_PROGRAMS = chkhostname libauthretry libntlmconnect \ lib1534 lib1535 lib1536 lib1537 lib1538 \ lib1540 lib1541 \ lib1550 lib1551 lib1552 lib1553 lib1554 lib1555 lib1556 lib1557 \ - lib1558 lib1559 lib1560 \ + lib1558 lib1559 lib1560 lib1564 lib1565 \ lib1591 lib1592 lib1593 lib1594 lib1596 \ lib1900 lib1905 lib1906 lib1907 \ lib2033 @@ -536,6 +536,14 @@ lib1559_LDADD = $(TESTUTIL_LIBS) lib1560_SOURCES = lib1560.c $(SUPPORTFILES) $(TESTUTIL) $(WARNLESS) lib1560_LDADD = $(TESTUTIL_LIBS) +lib1564_SOURCES = lib1564.c $(SUPPORTFILES) $(TESTUTIL) $(WARNLESS) +lib1564_LDADD = $(TESTUTIL_LIBS) +lib1564_CPPFLAGS = $(AM_CPPFLAGS) + +lib1565_SOURCES = lib1565.c $(SUPPORTFILES) $(TESTUTIL) $(WARNLESS) +lib1565_LDADD = $(TESTUTIL_LIBS) +lib1565_CPPFLAGS = $(AM_CPPFLAGS) + lib1591_SOURCES = lib1591.c $(SUPPORTFILES) $(TESTUTIL) $(WARNLESS) lib1591_LDADD = $(TESTUTIL_LIBS) lib1591_CPPFLAGS = $(AM_CPPFLAGS) -DLIB1591 diff --git a/tests/libtest/lib1564.c b/tests/libtest/lib1564.c new file mode 100644 index 000000000..225c8c6d7 --- /dev/null +++ b/tests/libtest/lib1564.c @@ -0,0 +1,142 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * + * 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.haxx.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. + * + ***************************************************************************/ +#include "test.h" + +#include "testutil.h" +#include "warnless.h" +#include "memdebug.h" + +#define TEST_HANG_TIMEOUT 60 * 1000 +#define WAKEUP_NUM 1234567 + +int test(char *URL) +{ + CURLM *multi = NULL; + int numfds; + int i; + int res = 0; + struct timeval time_before_wait, time_after_wait; + + (void)URL; + + start_test_timing(); + + global_init(CURL_GLOBAL_ALL); + + multi_init(multi); + + /* no wakeup */ + + time_before_wait = tutil_tvnow(); + multi_poll(multi, NULL, 0, 1000, &numfds); + time_after_wait = tutil_tvnow(); + + if(tutil_tvdiff(time_after_wait, time_before_wait) < 500) { + fprintf(stderr, "%s:%d curl_multi_poll returned too early\n", + __FILE__, __LINE__); + res = TEST_ERR_MAJOR_BAD; + goto test_cleanup; + } + + abort_on_test_timeout(); + + /* try a single wakeup */ + + multi_wakeup(multi); + + time_before_wait = tutil_tvnow(); + multi_poll(multi, NULL, 0, 1000, &numfds); + time_after_wait = tutil_tvnow(); + + if(tutil_tvdiff(time_after_wait, time_before_wait) > 500) { + fprintf(stderr, "%s:%d curl_multi_poll returned too late\n", + __FILE__, __LINE__); + res = TEST_ERR_MAJOR_BAD; + goto test_cleanup; + } + + abort_on_test_timeout(); + + /* previous wakeup should not wake up this */ + + time_before_wait = tutil_tvnow(); + multi_poll(multi, NULL, 0, 1000, &numfds); + time_after_wait = tutil_tvnow(); + + if(tutil_tvdiff(time_after_wait, time_before_wait) < 500) { + fprintf(stderr, "%s:%d curl_multi_poll returned too early\n", + __FILE__, __LINE__); + res = TEST_ERR_MAJOR_BAD; + goto test_cleanup; + } + + abort_on_test_timeout(); + + /* try lots of wakeup */ + + for(i = 0; i < WAKEUP_NUM; ++i) + multi_wakeup(multi); + + time_before_wait = tutil_tvnow(); + multi_poll(multi, NULL, 0, 1000, &numfds); + time_after_wait = tutil_tvnow(); + + if(tutil_tvdiff(time_after_wait, time_before_wait) > 500) { + fprintf(stderr, "%s:%d curl_multi_poll returned too late\n", + __FILE__, __LINE__); + res = TEST_ERR_MAJOR_BAD; + goto test_cleanup; + } + + abort_on_test_timeout(); + +#if !defined(WIN32) && !defined(_WIN32) && !defined(__WIN32__) \ + && !defined(__CYGWIN__) + /* Even lots of previous wakeups should not wake up this. + + On Windows (particularly when using MinGW), the socketpair + used for curl_multi_wakeup() is really asynchronous, + meaning when it's called a lot, it can take some time + before all of the data can be read. Sometimes it can wake + up more than one curl_multi_poll() call. */ + + time_before_wait = tutil_tvnow(); + multi_poll(multi, NULL, 0, 1000, &numfds); + time_after_wait = tutil_tvnow(); + + if(tutil_tvdiff(time_after_wait, time_before_wait) < 500) { + fprintf(stderr, "%s:%d curl_multi_poll returned too early\n", + __FILE__, __LINE__); + res = TEST_ERR_MAJOR_BAD; + goto test_cleanup; + } + + abort_on_test_timeout(); +#endif + +test_cleanup: + + curl_multi_cleanup(multi); + curl_global_cleanup(); + + return res; +} diff --git a/tests/libtest/lib1565.c b/tests/libtest/lib1565.c new file mode 100644 index 000000000..b2fa40aaa --- /dev/null +++ b/tests/libtest/lib1565.c @@ -0,0 +1,204 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * + * 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.haxx.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. + * + ***************************************************************************/ +#include "test.h" + +#include "testutil.h" +#include "warnless.h" +#include "memdebug.h" + +#ifdef HAVE_PTHREAD_H +#include +#include + +#define TEST_HANG_TIMEOUT 60 * 1000 +#define CONN_NUM 3 +#define TIME_BETWEEN_START_SECS 2 + +static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; +static CURL *pending_handles[CONN_NUM]; +static int pending_num = 0; +static int test_failure = 0; + +static CURLM *multi = NULL; +static const char *url; + +static void *run_thread(void *ptr) +{ + CURL *easy = NULL; + int res = 0; + int i; + + (void)ptr; + + for(i = 0; i < CONN_NUM; i++) { + sleep(TIME_BETWEEN_START_SECS); + + easy_init(easy); + + easy_setopt(easy, CURLOPT_URL, url); + easy_setopt(easy, CURLOPT_VERBOSE, 0L); + + pthread_mutex_lock(&lock); + + if(test_failure) { + pthread_mutex_unlock(&lock); + goto test_cleanup; + } + + pending_handles[pending_num] = easy; + pending_num++; + easy = NULL; + + pthread_mutex_unlock(&lock); + + multi_wakeup(multi); + } + +test_cleanup: + + curl_easy_cleanup(easy); + + pthread_mutex_lock(&lock); + + if(!test_failure) + test_failure = res; + + pthread_mutex_unlock(&lock); + + return NULL; +} + +int test(char *URL) +{ + int still_running; + int num; + int i; + int res = 0; + CURL *started_handles[CONN_NUM]; + int started_num = 0; + int finished_num = 0; + pthread_t tid = 0; + struct CURLMsg *message; + + start_test_timing(); + + global_init(CURL_GLOBAL_ALL); + + multi_init(multi); + + url = URL; + + res = pthread_create(&tid, NULL, run_thread, NULL); + if(0 != res) { + fprintf(stderr, "%s:%d Couldn't create thread, errno %d\n", + __FILE__, __LINE__, res); + goto test_cleanup; + } + + while(1) { + multi_perform(multi, &still_running); + + abort_on_test_timeout(); + + while((message = curl_multi_info_read(multi, &num)) != NULL) { + if(message->msg == CURLMSG_DONE) { + res = message->data.result; + if(res) + goto test_cleanup; + multi_remove_handle(multi, message->easy_handle); + finished_num++; + } + else { + fprintf(stderr, "%s:%d Got an unexpected message from curl: %i\n", + __FILE__, __LINE__, (int)message->msg); + res = TEST_ERR_MAJOR_BAD; + goto test_cleanup; + } + + abort_on_test_timeout(); + } + + if(CONN_NUM == finished_num) + break; + + multi_poll(multi, NULL, 0, TEST_HANG_TIMEOUT, &num); + + abort_on_test_timeout(); + + pthread_mutex_lock(&lock); + + while(pending_num > 0) { + res_multi_add_handle(multi, pending_handles[pending_num - 1]); + if(res) { + pthread_mutex_unlock(&lock); + goto test_cleanup; + } + + started_handles[started_num] = pending_handles[pending_num - 1]; + started_num++; + pending_num--; + } + + pthread_mutex_unlock(&lock); + + abort_on_test_timeout(); + } + + if(CONN_NUM != started_num) { + fprintf(stderr, "%s:%d Not all connections started: %d of %d\n", + __FILE__, __LINE__, started_num, CONN_NUM); + goto test_cleanup; + } + + if(CONN_NUM != finished_num) { + fprintf(stderr, "%s:%d Not all connections finished: %d of %d\n", + __FILE__, __LINE__, started_num, CONN_NUM); + goto test_cleanup; + } + +test_cleanup: + + pthread_mutex_lock(&lock); + if(!test_failure) + test_failure = res; + pthread_mutex_unlock(&lock); + + if(0 != tid) + pthread_join(tid, NULL); + + curl_multi_cleanup(multi); + for(i = 0; i < pending_num; i++) + curl_easy_cleanup(pending_handles[i]); + for(i = 0; i < started_num; i++) + curl_easy_cleanup(started_handles[i]); + curl_global_cleanup(); + + return test_failure; +} + +#else /* without pthread, this test doesn't work */ +int test(char *URL) +{ + (void)URL; + return 0; +} +#endif diff --git a/tests/libtest/test.h b/tests/libtest/test.h index bb1acca0e..3c8323de4 100644 --- a/tests/libtest/test.h +++ b/tests/libtest/test.h @@ -355,6 +355,60 @@ extern int unitfail; /* ---------------------------------------------------------------- */ +#define exe_multi_poll(A,B,C,D,E,Y,Z) do { \ + CURLMcode ec; \ + if((ec = curl_multi_poll((A), (B), (C), (D), (E))) != CURLM_OK) { \ + fprintf(stderr, "%s:%d curl_multi_poll() failed, " \ + "with code %d (%s)\n", \ + (Y), (Z), (int)ec, curl_multi_strerror(ec)); \ + res = (int)ec; \ + } \ + else if(*((E)) < 0) { \ + fprintf(stderr, "%s:%d curl_multi_poll() succeeded, " \ + "but returned invalid numfds value (%d)\n", \ + (Y), (Z), (int)*((E))); \ + res = TEST_ERR_NUM_HANDLES; \ + } \ +} WHILE_FALSE + +#define res_multi_poll(A, B, C, D, E) \ + exe_multi_poll((A), (B), (C), (D), (E), (__FILE__), (__LINE__)) + +#define chk_multi_poll(A, B, C, D, E, Y, Z) do { \ + exe_multi_poll((A), (B), (C), (D), (E), (Y), (Z)); \ + if(res) \ + goto test_cleanup; \ +} WHILE_FALSE + +#define multi_poll(A, B, C, D, E) \ + chk_multi_poll((A), (B), (C), (D), (E), (__FILE__), (__LINE__)) + +/* ---------------------------------------------------------------- */ + +#define exe_multi_wakeup(A,Y,Z) do { \ + CURLMcode ec; \ + if((ec = curl_multi_wakeup((A))) != CURLM_OK) { \ + fprintf(stderr, "%s:%d curl_multi_wakeup() failed, " \ + "with code %d (%s)\n", \ + (Y), (Z), (int)ec, curl_multi_strerror(ec)); \ + res = (int)ec; \ + } \ +} WHILE_FALSE + +#define res_multi_wakeup(A) \ + exe_multi_wakeup((A), (__FILE__), (__LINE__)) + +#define chk_multi_wakeup(A, Y, Z) do { \ + exe_multi_wakeup((A), (Y), (Z)); \ + if(res) \ + goto test_cleanup; \ +} WHILE_FALSE + +#define multi_wakeup(A) \ + chk_multi_wakeup((A), (__FILE__), (__LINE__)) + +/* ---------------------------------------------------------------- */ + #define exe_select_test(A, B, C, D, E, Y, Z) do { \ int ec; \ if(select_wrapper((A), (B), (C), (D), (E)) == -1) { \ -- cgit v1.2.3