diff options
author | Gergely Nagy <ngg@tresorit.com> | 2019-11-17 15:12:15 +0100 |
---|---|---|
committer | Daniel Stenberg <daniel@haxx.se> | 2019-11-25 15:45:56 +0100 |
commit | f3c35e371cc70f1b6bc33f7faa904d37d1567eb3 (patch) | |
tree | 3c7668dec5726aa510d7be282ff440c9ee370802 /lib | |
parent | 0a65febccf0e31bc987be3f90c01194804e61b77 (diff) | |
download | curl-f3c35e371cc70f1b6bc33f7faa904d37d1567eb3.tar.gz |
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
Diffstat (limited to 'lib')
-rwxr-xr-x | lib/multi.c | 114 | ||||
-rw-r--r-- | lib/multihandle.h | 10 | ||||
-rw-r--r-- | lib/strerror.c | 3 |
3 files changed, 124 insertions, 3 deletions
diff --git a/lib/multi.c b/lib/multi.c index 9faad0e2e..f30e41a65 100755 --- a/lib/multi.c +++ b/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; diff --git a/lib/multihandle.h b/lib/multihandle.h index b65bd9638..a26fb619a 100644 --- a/lib/multihandle.h +++ b/lib/multihandle.h @@ -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 */ diff --git a/lib/strerror.c b/lib/strerror.c index ba2e7a6f9..baf5451ae 100644 --- a/lib/strerror.c +++ b/lib/strerror.c @@ -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; } |