mirror of
https://github.com/curl/curl.git
synced 2026-08-25 12:23:44 +03:00
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
This commit is contained in:
parent
0a65febccf
commit
f3c35e371c
20 changed files with 684 additions and 17 deletions
|
|
@ -179,7 +179,7 @@ test1525 test1526 test1527 test1528 test1529 test1530 test1531 test1532 \
|
|||
test1533 test1534 test1535 test1536 test1537 test1538 \
|
||||
test1540 test1541 \
|
||||
test1550 test1551 test1552 test1553 test1554 test1555 test1556 test1557 \
|
||||
test1558 test1559 test1560 test1561 test1562 test1563 \
|
||||
test1558 test1559 test1560 test1561 test1562 test1563 test1564 test1565 \
|
||||
\
|
||||
test1590 test1591 test1592 test1593 test1594 test1595 test1596 \
|
||||
\
|
||||
|
|
|
|||
|
|
@ -92,6 +92,7 @@ CURL_EXTERN CURLMcode curl_multi_remove_handle(CURLM *multi_handle,
|
|||
CURL_EXTERN CURLMcode curl_multi_fdset(CURLM *multi_handle,
|
||||
CURL_EXTERN CURLMcode curl_multi_wait(CURLM *multi_handle,
|
||||
CURL_EXTERN CURLMcode curl_multi_poll(CURLM *multi_handle,
|
||||
CURL_EXTERN CURLMcode curl_multi_wakeup(CURLM *multi_handle);
|
||||
CURL_EXTERN CURLMcode curl_multi_perform(CURLM *multi_handle,
|
||||
CURL_EXTERN CURLMcode curl_multi_cleanup(CURLM *multi_handle);
|
||||
CURL_EXTERN CURLMsg *curl_multi_info_read(CURLM *multi_handle,
|
||||
|
|
|
|||
|
|
@ -139,7 +139,8 @@ m5: Invalid socket argument
|
|||
m6: Unknown option
|
||||
m7: The easy handle is already added to a multi handle
|
||||
m8: API function called from within callback
|
||||
m9: Unknown error
|
||||
m9: Wakeup is unavailable or failed
|
||||
m10: Unknown error
|
||||
s0: No error
|
||||
s1: Unknown share option
|
||||
s2: Share currently in use
|
||||
|
|
|
|||
31
tests/data/test1564
Normal file
31
tests/data/test1564
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
<testcase>
|
||||
<info>
|
||||
<keywords>
|
||||
multi
|
||||
wakeup
|
||||
</keywords>
|
||||
</info>
|
||||
|
||||
# Server-side
|
||||
<reply>
|
||||
</reply>
|
||||
|
||||
# Client-side
|
||||
<client>
|
||||
<server>
|
||||
none
|
||||
</server>
|
||||
<tool>
|
||||
lib1564
|
||||
</tool>
|
||||
<name>
|
||||
wakeup before poll with no easy handles
|
||||
</name>
|
||||
<command>
|
||||
</command>
|
||||
</client>
|
||||
|
||||
# Verify data after the test has been "shot"
|
||||
<verify>
|
||||
</verify>
|
||||
</testcase>
|
||||
41
tests/data/test1565
Normal file
41
tests/data/test1565
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
<testcase>
|
||||
<info>
|
||||
<keywords>
|
||||
HTTP
|
||||
HTTP GET
|
||||
multi
|
||||
multi-threaded
|
||||
wakeup
|
||||
</keywords>
|
||||
</info>
|
||||
|
||||
# Server-side
|
||||
<reply>
|
||||
<data nocheck="yes">
|
||||
HTTP/1.1 200 OK
|
||||
Content-Length: 3
|
||||
|
||||
OK
|
||||
</data>
|
||||
</reply>
|
||||
|
||||
# Client-side
|
||||
<client>
|
||||
<server>
|
||||
http
|
||||
</server>
|
||||
<tool>
|
||||
lib1565
|
||||
</tool>
|
||||
<name>
|
||||
wakeup from another thread
|
||||
</name>
|
||||
<command>
|
||||
http://%HOSTIP:%HTTPPORT/1
|
||||
</command>
|
||||
</client>
|
||||
|
||||
# Verify data after the test has been "shot"
|
||||
<verify>
|
||||
</verify>
|
||||
</testcase>
|
||||
|
|
@ -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
|
||||
|
|
|
|||
142
tests/libtest/lib1564.c
Normal file
142
tests/libtest/lib1564.c
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, 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;
|
||||
}
|
||||
204
tests/libtest/lib1565.c
Normal file
204
tests/libtest/lib1565.c
Normal file
|
|
@ -0,0 +1,204 @@
|
|||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, 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 <pthread.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#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
|
||||
|
|
@ -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) { \
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue