mirror of
https://github.com/moparisthebest/curl
synced 2024-12-23 00:28:48 -05: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
@ -51,7 +51,6 @@
|
||||
2.4 Split connect and authentication process
|
||||
2.5 Edge-triggered sockets should work
|
||||
2.6 multi upkeep
|
||||
2.7 curl_multi_unblock
|
||||
|
||||
3. Documentation
|
||||
3.2 Provide cmake config-file
|
||||
@ -448,13 +447,6 @@
|
||||
|
||||
See https://github.com/curl/curl/issues/3199
|
||||
|
||||
2.7 curl_multi_unblock
|
||||
|
||||
A portable way to unblock curl_multi_wait from another thread.
|
||||
|
||||
See https://github.com/curl/curl/issues/4418 and
|
||||
https://github.com/curl/curl/wiki/curl_multi_unblock
|
||||
|
||||
3. Documentation
|
||||
|
||||
3.2 Provide cmake config-file
|
||||
|
@ -54,6 +54,7 @@ man_MANS = \
|
||||
curl_multi_socket_all.3 \
|
||||
curl_multi_strerror.3 \
|
||||
curl_multi_timeout.3 \
|
||||
curl_multi_wakeup.3 \
|
||||
curl_multi_wait.3 \
|
||||
curl_share_cleanup.3 \
|
||||
curl_share_init.3 \
|
||||
|
@ -48,10 +48,16 @@ total number of file descriptors on which interesting events occurred. This
|
||||
number can include both libcurl internal descriptors as well as descriptors
|
||||
provided in \fIextra_fds\fP.
|
||||
|
||||
The \fIcurl_multi_wakeup(3)\fP function can be used from another thread to
|
||||
wake up this function and return faster. This is one of the details
|
||||
that makes this function different than \fIcurl_multi_wait(3)\fP which cannot
|
||||
be woken up this way.
|
||||
|
||||
If no extra file descriptors are provided and libcurl has no file descriptor
|
||||
to offer to wait for, this function will instead wait during \fItimeout_ms\fP
|
||||
milliseconds (or shorter if an internal timer indicates so). This is the
|
||||
detail that makes this function different than \fIcurl_multi_wait(3)\fP.
|
||||
other detail that makes this function different than
|
||||
\fIcurl_multi_wait(3)\fP.
|
||||
|
||||
This function is encouraged to be used instead of select(3) when using the
|
||||
multi interface to allow applications to easier circumvent the common problem
|
||||
@ -107,4 +113,5 @@ CURLMcode type, general libcurl multi interface error code. See
|
||||
.SH AVAILABILITY
|
||||
This function was added in libcurl 7.66.0.
|
||||
.SH "SEE ALSO"
|
||||
.BR curl_multi_fdset "(3), " curl_multi_perform "(3), " curl_multi_wait "(3)"
|
||||
.BR curl_multi_fdset "(3), " curl_multi_perform "(3), "
|
||||
.BR curl_multi_wait "(3), " curl_multi_wakeup "(3)"
|
||||
|
47
docs/libcurl/curl_multi_wakeup.3
Normal file
47
docs/libcurl/curl_multi_wakeup.3
Normal file
@ -0,0 +1,47 @@
|
||||
.\" **************************************************************************
|
||||
.\" * _ _ ____ _
|
||||
.\" * 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.
|
||||
.\" *
|
||||
.\" **************************************************************************
|
||||
.TH curl_multi_wakeup 3 "17 Nov 2019" "libcurl 7.68.0" "libcurl Manual"
|
||||
.SH NAME
|
||||
curl_multi_wakeup - wakes up a sleeping curl_multi_poll call
|
||||
.SH SYNOPSIS
|
||||
#include <curl/curl.h>
|
||||
|
||||
CURLMcode curl_multi_wakeup(CURLM *multi_handle);
|
||||
.ad
|
||||
.SH DESCRIPTION
|
||||
This function can be called from any thread and it wakes up a
|
||||
sleeping \fIcurl_multi_poll(3)\fP call that is currently (or will be)
|
||||
waiting for activity or a timeout.
|
||||
|
||||
If the function is called when there is no \fIcurl_multi_poll(3)\fP call,
|
||||
it will cause the next call to return immediately.
|
||||
|
||||
Calling this function only guarantees to wake up the current (or the next
|
||||
if there is no current) \fIcurl_multi_poll(3)\fP call, which means it is
|
||||
possible that multiple calls to this function will wake up the same waiting
|
||||
operation.
|
||||
|
||||
This function has no effect on \fIcurl_multi_wait(3)\fP calls.
|
||||
.SH RETURN VALUE
|
||||
CURLMcode type, general libcurl multi interface error code.
|
||||
.SH "SEE ALSO"
|
||||
.BR curl_multi_poll "(3), " curl_multi_wait "(3)"
|
@ -297,6 +297,8 @@ An easy handle already added to a multi handle was attempted to get added a
|
||||
second time. (Added in 7.32.1)
|
||||
.IP "CURLM_RECURSIVE_API_CALL (8)"
|
||||
An API function was called from inside a callback.
|
||||
.IP "CURLM_WAKEUP_FAILURE (9)"
|
||||
Wakeup is unavailable or failed.
|
||||
.SH "CURLSHcode"
|
||||
The "share" interface will return a CURLSHcode to indicate when an error has
|
||||
occurred. Also consider \fIcurl_share_strerror(3)\fP.
|
||||
|
@ -342,6 +342,7 @@ CURLM_INTERNAL_ERROR 7.9.6
|
||||
CURLM_OK 7.9.6
|
||||
CURLM_OUT_OF_MEMORY 7.9.6
|
||||
CURLM_RECURSIVE_API_CALL 7.59.0
|
||||
CURLM_WAKEUP_FAILURE 7.68.0
|
||||
CURLM_UNKNOWN_OPTION 7.15.4
|
||||
CURLOPTTYPE_FUNCTIONPOINT 7.1
|
||||
CURLOPTTYPE_LONG 7.1
|
||||
|
@ -72,6 +72,7 @@ typedef enum {
|
||||
attempted to get added - again */
|
||||
CURLM_RECURSIVE_API_CALL, /* an api function was called from inside a
|
||||
callback */
|
||||
CURLM_WAKEUP_FAILURE, /* wakeup is unavailable or failed */
|
||||
CURLM_LAST
|
||||
} CURLMcode;
|
||||
|
||||
@ -187,6 +188,15 @@ CURL_EXTERN CURLMcode curl_multi_poll(CURLM *multi_handle,
|
||||
int timeout_ms,
|
||||
int *ret);
|
||||
|
||||
/*
|
||||
* Name: curl_multi_wakeup()
|
||||
*
|
||||
* Desc: wakes up a sleeping curl_multi_poll call.
|
||||
*
|
||||
* Returns: CURLMcode type, general multi error code.
|
||||
*/
|
||||
CURL_EXTERN CURLMcode curl_multi_wakeup(CURLM *multi_handle);
|
||||
|
||||
/*
|
||||
* Name: curl_multi_perform()
|
||||
*
|
||||
|
114
lib/multi.c
114
lib/multi.c
@ -46,6 +46,7 @@
|
||||
#include "connect.h"
|
||||
#include "http_proxy.h"
|
||||
#include "http2.h"
|
||||
#include "socketpair.h"
|
||||
/* The last 3 #include files should be in this order */
|
||||
#include "curl_printf.h"
|
||||
#include "curl_memory.h"
|
||||
@ -367,6 +368,21 @@ struct Curl_multi *Curl_multi_handle(int hashsize, /* socket hash */
|
||||
|
||||
/* -1 means it not set by user, use the default value */
|
||||
multi->maxconnects = -1;
|
||||
|
||||
#ifdef ENABLE_WAKEUP
|
||||
if(Curl_socketpair(AF_UNIX, SOCK_STREAM, 0, multi->wakeup_pair) < 0) {
|
||||
multi->wakeup_pair[0] = CURL_SOCKET_BAD;
|
||||
multi->wakeup_pair[1] = CURL_SOCKET_BAD;
|
||||
}
|
||||
else if(curlx_nonblock(multi->wakeup_pair[0], TRUE) < 0 ||
|
||||
curlx_nonblock(multi->wakeup_pair[1], TRUE) < 0) {
|
||||
sclose(multi->wakeup_pair[0]);
|
||||
sclose(multi->wakeup_pair[1]);
|
||||
multi->wakeup_pair[0] = CURL_SOCKET_BAD;
|
||||
multi->wakeup_pair[1] = CURL_SOCKET_BAD;
|
||||
}
|
||||
#endif
|
||||
|
||||
return multi;
|
||||
|
||||
error:
|
||||
@ -1005,7 +1021,8 @@ static CURLMcode Curl_multi_wait(struct Curl_multi *multi,
|
||||
unsigned int extra_nfds,
|
||||
int timeout_ms,
|
||||
int *ret,
|
||||
bool extrawait) /* when no socket, wait */
|
||||
bool extrawait, /* when no socket, wait */
|
||||
bool use_wakeup)
|
||||
{
|
||||
struct Curl_easy *data;
|
||||
curl_socket_t sockbunch[MAX_SOCKSPEREASYHANDLE];
|
||||
@ -1059,6 +1076,12 @@ static CURLMcode Curl_multi_wait(struct Curl_multi *multi,
|
||||
curlfds = nfds; /* number of internal file descriptors */
|
||||
nfds += extra_nfds; /* add the externally provided ones */
|
||||
|
||||
#ifdef ENABLE_WAKEUP
|
||||
if(use_wakeup && multi->wakeup_pair[0] != CURL_SOCKET_BAD) {
|
||||
++nfds;
|
||||
}
|
||||
#endif
|
||||
|
||||
if(nfds > NUM_POLLS_ON_STACK) {
|
||||
/* 'nfds' is a 32 bit value and 'struct pollfd' is typically 8 bytes
|
||||
big, so at 2^29 sockets this value might wrap. When a process gets
|
||||
@ -1117,6 +1140,14 @@ static CURLMcode Curl_multi_wait(struct Curl_multi *multi,
|
||||
++nfds;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_WAKEUP
|
||||
if(use_wakeup && multi->wakeup_pair[0] != CURL_SOCKET_BAD) {
|
||||
ufds[nfds].fd = multi->wakeup_pair[0];
|
||||
ufds[nfds].events = POLLIN;
|
||||
++nfds;
|
||||
}
|
||||
#endif
|
||||
|
||||
if(nfds) {
|
||||
int pollrc;
|
||||
/* wait... */
|
||||
@ -1140,6 +1171,29 @@ static CURLMcode Curl_multi_wait(struct Curl_multi *multi,
|
||||
|
||||
extra_fds[i].revents = mask;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_WAKEUP
|
||||
if(use_wakeup && multi->wakeup_pair[0] != CURL_SOCKET_BAD) {
|
||||
if(ufds[curlfds + extra_nfds].revents & POLLIN) {
|
||||
char buf[64];
|
||||
while(1) {
|
||||
/* the reading socket is non-blocking, try to read
|
||||
data from it until it receives an error (except EINTR).
|
||||
In normal cases it will get EAGAIN or EWOULDBLOCK
|
||||
when there is no more data, breaking the loop. */
|
||||
if(sread(multi->wakeup_pair[0], buf, sizeof(buf)) < 0) {
|
||||
#ifndef USE_WINSOCK
|
||||
if(EINTR == SOCKERRNO)
|
||||
continue;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* do not count the wakeup socket into the returned value */
|
||||
retcode--;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@ -1174,7 +1228,8 @@ CURLMcode curl_multi_wait(struct Curl_multi *multi,
|
||||
int timeout_ms,
|
||||
int *ret)
|
||||
{
|
||||
return Curl_multi_wait(multi, extra_fds, extra_nfds, timeout_ms, ret, FALSE);
|
||||
return Curl_multi_wait(multi, extra_fds, extra_nfds, timeout_ms, ret, FALSE,
|
||||
FALSE);
|
||||
}
|
||||
|
||||
CURLMcode curl_multi_poll(struct Curl_multi *multi,
|
||||
@ -1183,7 +1238,55 @@ CURLMcode curl_multi_poll(struct Curl_multi *multi,
|
||||
int timeout_ms,
|
||||
int *ret)
|
||||
{
|
||||
return Curl_multi_wait(multi, extra_fds, extra_nfds, timeout_ms, ret, TRUE);
|
||||
return Curl_multi_wait(multi, extra_fds, extra_nfds, timeout_ms, ret, TRUE,
|
||||
TRUE);
|
||||
}
|
||||
|
||||
CURLMcode curl_multi_wakeup(struct Curl_multi *multi)
|
||||
{
|
||||
/* this function is usually called from another thread,
|
||||
it has to be careful only to access parts of the
|
||||
Curl_multi struct that are constant */
|
||||
|
||||
/* GOOD_MULTI_HANDLE can be safely called */
|
||||
if(!GOOD_MULTI_HANDLE(multi))
|
||||
return CURLM_BAD_HANDLE;
|
||||
|
||||
#ifdef ENABLE_WAKEUP
|
||||
/* the wakeup_pair variable is only written during init and cleanup,
|
||||
making it safe to access from another thread after the init part
|
||||
and before cleanup */
|
||||
if(multi->wakeup_pair[1] != CURL_SOCKET_BAD) {
|
||||
char buf[1];
|
||||
buf[0] = 1;
|
||||
while(1) {
|
||||
/* swrite() is not thread-safe in general, because concurrent calls
|
||||
can have their messages interleaved, but in this case the content
|
||||
of the messages does not matter, which makes it ok to call.
|
||||
|
||||
The write socket is set to non-blocking, this way this function
|
||||
cannot block, making it safe to call even from the same thread
|
||||
that will call Curl_multi_wait(). If swrite() returns that it
|
||||
would block, it's considered successful because it means that
|
||||
previous calls to this function will wake up the poll(). */
|
||||
if(swrite(multi->wakeup_pair[1], buf, sizeof(buf)) < 0) {
|
||||
int err = SOCKERRNO;
|
||||
int return_success;
|
||||
#ifdef USE_WINSOCK
|
||||
return_success = WSAEWOULDBLOCK == err;
|
||||
#else
|
||||
if(EINTR == err)
|
||||
continue;
|
||||
return_success = EWOULDBLOCK == err || EAGAIN == err;
|
||||
#endif
|
||||
if(!return_success)
|
||||
return CURLM_WAKEUP_FAILURE;
|
||||
}
|
||||
return CURLM_OK;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return CURLM_WAKEUP_FAILURE;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -2309,6 +2412,11 @@ CURLMcode curl_multi_cleanup(struct Curl_multi *multi)
|
||||
|
||||
Curl_hash_destroy(&multi->hostcache);
|
||||
Curl_psl_destroy(&multi->psl);
|
||||
|
||||
#ifdef ENABLE_WAKEUP
|
||||
sclose(multi->wakeup_pair[0]);
|
||||
sclose(multi->wakeup_pair[1]);
|
||||
#endif
|
||||
free(multi);
|
||||
|
||||
return CURLM_OK;
|
||||
|
@ -24,6 +24,7 @@
|
||||
|
||||
#include "conncache.h"
|
||||
#include "psl.h"
|
||||
#include "socketpair.h"
|
||||
|
||||
struct Curl_message {
|
||||
struct curl_llist_element list;
|
||||
@ -66,6 +67,10 @@ typedef enum {
|
||||
|
||||
#define CURLPIPE_ANY (CURLPIPE_MULTIPLEX)
|
||||
|
||||
#if defined(USE_SOCKETPAIR) && !defined(USE_BLOCKING_SOCKETS)
|
||||
#define ENABLE_WAKEUP
|
||||
#endif
|
||||
|
||||
/* This is the struct known as CURLM on the outside */
|
||||
struct Curl_multi {
|
||||
/* First a simple identifier to easier detect if a user mix up
|
||||
@ -134,6 +139,11 @@ struct Curl_multi {
|
||||
previous callback */
|
||||
bool in_callback; /* true while executing a callback */
|
||||
long max_concurrent_streams; /* max concurrent streams client to support */
|
||||
|
||||
#ifdef ENABLE_WAKEUP
|
||||
curl_socket_t wakeup_pair[2]; /* socketpair() used for wakeup
|
||||
0 is used for read, 1 is used for write */
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif /* HEADER_CURL_MULTIHANDLE_H */
|
||||
|
@ -389,6 +389,9 @@ curl_multi_strerror(CURLMcode error)
|
||||
case CURLM_RECURSIVE_API_CALL:
|
||||
return "API function called from within callback";
|
||||
|
||||
case CURLM_WAKEUP_FAILURE:
|
||||
return "Wakeup is unavailable or failed";
|
||||
|
||||
case CURLM_LAST:
|
||||
break;
|
||||
}
|
||||
|
@ -1804,7 +1804,11 @@
|
||||
d c 6
|
||||
d CURLM_ADDED_ALREADY...
|
||||
d c 7
|
||||
d CURLM_LAST c 8
|
||||
d CURLM_RECURSIVE_API_CALL...
|
||||
d c 8
|
||||
d CURLM_WAKEUP_FAILURE...
|
||||
d c 9
|
||||
d CURLM_LAST c 10
|
||||
*
|
||||
d CURLMSG s 10i 0 based(######ptr######) Enum
|
||||
d CURLMSG_NONE c 0
|
||||
|
@ -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…
Reference in New Issue
Block a user