diff options
author | Brad King <brad.king@kitware.com> | 2020-06-26 13:30:18 (GMT) |
---|---|---|
committer | Kitware Robot <kwrobot@kitware.com> | 2020-06-26 13:30:25 (GMT) |
commit | 42d9b3dc6af8a70f1238fce5f5db2e5910452f0b (patch) | |
tree | 8701572d53316e111fec262c78450ecc22e8762d /Utilities/cmcurl/lib/multi.c | |
parent | 1f47a65c7ac1b09f013543d2bd2ca38d2502f3a1 (diff) | |
parent | e5ff413f41877dc46498c384fa4f6e5b23ec1476 (diff) | |
download | CMake-42d9b3dc6af8a70f1238fce5f5db2e5910452f0b.zip CMake-42d9b3dc6af8a70f1238fce5f5db2e5910452f0b.tar.gz CMake-42d9b3dc6af8a70f1238fce5f5db2e5910452f0b.tar.bz2 |
Merge topic 'update-curl' into release-3.18
e5ff413f41 curl: Set build options the way we need for CMake
0ef8fa5000 Merge branch 'upstream-curl' into update-curl
5717fdc114 curl 2020-06-23 (e9db32a0)
39f7cfad31 curl: Update script to get curl 7.71.0
Acked-by: Kitware Robot <kwrobot@kitware.com>
Merge-request: !4937
Diffstat (limited to 'Utilities/cmcurl/lib/multi.c')
-rw-r--r-- | Utilities/cmcurl/lib/multi.c | 283 |
1 files changed, 223 insertions, 60 deletions
diff --git a/Utilities/cmcurl/lib/multi.c b/Utilities/cmcurl/lib/multi.c index cef2805..b106c94 100644 --- a/Utilities/cmcurl/lib/multi.c +++ b/Utilities/cmcurl/lib/multi.c @@ -79,7 +79,6 @@ static CURLMcode add_next_timeout(struct curltime now, static CURLMcode multi_timeout(struct Curl_multi *multi, long *timeout_ms); static void process_pending_handles(struct Curl_multi *multi); -static void detach_connnection(struct Curl_easy *data); #ifdef DEBUGBUILD static const char * const statename[]={ @@ -112,7 +111,7 @@ static void Curl_init_completed(struct Curl_easy *data) /* Important: reset the conn pointer so that we don't point to memory that could be freed anytime */ - detach_connnection(data); + Curl_detach_connnection(data); Curl_expire_clear(data); /* stop all timers */ } @@ -169,9 +168,11 @@ static void mstate(struct Curl_easy *data, CURLMstate state } #endif - if(state == CURLM_STATE_COMPLETED) + if(state == CURLM_STATE_COMPLETED) { /* changing to COMPLETED means there's one less easy handle 'alive' */ + DEBUGASSERT(data->multi->num_alive > 0); data->multi->num_alive--; + } /* if this state has an init-function, run it */ if(finit[state]) @@ -373,6 +374,11 @@ struct Curl_multi *Curl_multi_handle(int hashsize, /* socket hash */ multi->max_concurrent_streams = 100; multi->ipv6_works = Curl_ipv6works(NULL); +#ifdef USE_WINSOCK + multi->wsa_event = WSACreateEvent(); + if(multi->wsa_event == WSA_INVALID_EVENT) + goto error; +#else #ifdef ENABLE_WAKEUP if(Curl_socketpair(AF_UNIX, SOCK_STREAM, 0, multi->wakeup_pair) < 0) { multi->wakeup_pair[0] = CURL_SOCKET_BAD; @@ -386,6 +392,7 @@ struct Curl_multi *Curl_multi_handle(int hashsize, /* socket hash */ multi->wakeup_pair[1] = CURL_SOCKET_BAD; } #endif +#endif return multi; @@ -506,6 +513,7 @@ CURLMcode curl_multi_add_handle(struct Curl_multi *multi, easy handle is added */ memset(&multi->timer_lastcall, 0, sizeof(multi->timer_lastcall)); + CONNCACHE_LOCK(data); /* The closure handle only ever has default timeouts set. To improve the state somewhat we clone the timeouts from each added handle so that the closure handle always has the same timeouts as the most recently added @@ -515,6 +523,7 @@ CURLMcode curl_multi_add_handle(struct Curl_multi *multi, data->set.server_response_timeout; data->state.conn_cache->closure_handle->set.no_signal = data->set.no_signal; + CONNCACHE_UNLOCK(data); Curl_update_timer(multi); return CURLM_OK; @@ -589,14 +598,14 @@ static CURLcode multi_done(struct Curl_easy *data, process_pending_handles(data->multi); /* connection / multiplex */ - CONN_LOCK(data); - detach_connnection(data); + CONNCACHE_LOCK(data); + Curl_detach_connnection(data); if(CONN_INUSE(conn)) { /* Stop if still used. */ /* conn->data must not remain pointing to this transfer since it is going away! Find another to own it! */ conn->data = conn->easyq.head->ptr; - CONN_UNLOCK(data); + CONNCACHE_UNLOCK(data); DEBUGF(infof(data, "Connection still in use %zu, " "no more multi_done now!\n", conn->easyq.size)); @@ -615,7 +624,7 @@ static CURLcode multi_done(struct Curl_easy *data, /* if the transfer was completed in a paused state there can be buffered data left to free */ for(i = 0; i < data->state.tempcount; i++) { - free(data->state.tempwrite[i].buf); + Curl_dyn_free(&data->state.tempwrite[i].b); } data->state.tempcount = 0; @@ -647,7 +656,8 @@ static CURLcode multi_done(struct Curl_easy *data, || (premature && !(conn->handler->flags & PROTOPT_STREAM))) { CURLcode res2; connclose(conn, "disconnecting"); - CONN_UNLOCK(data); + Curl_conncache_remove_conn(data, conn, FALSE); + CONNCACHE_UNLOCK(data); res2 = Curl_disconnect(data, conn, premature); /* If we had an error already, make sure we return that one. But @@ -657,16 +667,20 @@ static CURLcode multi_done(struct Curl_easy *data, } else { char buffer[256]; + const char *host = +#ifndef CURL_DISABLE_PROXY + conn->bits.socksproxy ? + conn->socks_proxy.host.dispname : + conn->bits.httpproxy ? conn->http_proxy.host.dispname : +#endif + conn->bits.conn_to_host ? conn->conn_to_host.dispname : + conn->host.dispname; /* create string before returning the connection */ msnprintf(buffer, sizeof(buffer), "Connection #%ld to host %s left intact", - conn->connection_id, - conn->bits.socksproxy ? conn->socks_proxy.host.dispname : - conn->bits.httpproxy ? conn->http_proxy.host.dispname : - conn->bits.conn_to_host ? conn->conn_to_host.dispname : - conn->host.dispname); + conn->connection_id, host); /* the connection is no longer in use by this transfer */ - CONN_UNLOCK(data); + CONNCACHE_UNLOCK(data); if(Curl_conncache_return_conn(data, conn)) { /* remember the most recently used connection */ data->state.lastconnect = conn; @@ -676,6 +690,7 @@ static CURLcode multi_done(struct Curl_easy *data, data->state.lastconnect = NULL; } + Curl_safefree(data->state.buffer); Curl_free_request_state(data); return result; } @@ -700,6 +715,10 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi, if(!data->multi) return CURLM_OK; /* it is already removed so let's say it is fine! */ + /* Prevent users from trying to remove an easy handle from the wrong multi */ + if(data->multi != multi) + return CURLM_BAD_EASY_HANDLE; + if(multi->in_callback) return CURLM_RECURSIVE_API_CALL; @@ -774,8 +793,7 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi, vanish with this handle */ /* Remove the association between the connection and the handle */ - if(data->conn) - detach_connnection(data); + Curl_detach_connnection(data); #ifdef USE_LIBPSL /* Remove the PSL association. */ @@ -824,9 +842,13 @@ bool Curl_multiplex_wanted(const struct Curl_multi *multi) return (multi && (multi->multiplexing)); } -/* This is the only function that should clear data->conn. This will - occasionally be called with the pointer already cleared. */ -static void detach_connnection(struct Curl_easy *data) +/* + * Curl_detach_connnection() removes the given transfer from the connection. + * + * This is the only function that should clear data->conn. This will + * occasionally be called with the data->conn pointer already cleared. + */ +void Curl_detach_connnection(struct Curl_easy *data) { struct connectdata *conn = data->conn; if(conn) @@ -834,7 +856,11 @@ static void detach_connnection(struct Curl_easy *data) data->conn = NULL; } -/* This is the only function that should assign data->conn */ +/* + * Curl_attach_connnection() attaches this transfer to this connection. + * + * This is the only function that should assign data->conn + */ void Curl_attach_connnection(struct Curl_easy *data, struct connectdata *conn) { @@ -853,9 +879,11 @@ static int waitconnect_getsock(struct connectdata *conn, int rc = 0; #ifdef USE_SSL +#ifndef CURL_DISABLE_PROXY if(CONNECT_FIRSTSOCKET_PROXY_SSL()) return Curl_ssl_getsock(conn, sock); #endif +#endif if(SOCKS_STATE(conn->cnnct.state)) return Curl_SOCKS_getsock(conn, sock, FIRSTSOCKET); @@ -1045,11 +1073,15 @@ static CURLMcode Curl_multi_wait(struct Curl_multi *multi, unsigned int i; unsigned int nfds = 0; unsigned int curlfds; - bool ufds_malloc = FALSE; long timeout_internal; int retcode = 0; +#ifndef USE_WINSOCK struct pollfd a_few_on_stack[NUM_POLLS_ON_STACK]; struct pollfd *ufds = &a_few_on_stack[0]; + bool ufds_malloc = FALSE; +#else + DEBUGASSERT(multi->wsa_event != WSA_INVALID_EVENT); +#endif if(!GOOD_MULTI_HANDLE(multi)) return CURLM_BAD_HANDLE; @@ -1095,11 +1127,16 @@ static CURLMcode Curl_multi_wait(struct Curl_multi *multi, nfds += extra_nfds; /* add the externally provided ones */ #ifdef ENABLE_WAKEUP +#ifdef USE_WINSOCK + if(use_wakeup) { +#else if(use_wakeup && multi->wakeup_pair[0] != CURL_SOCKET_BAD) { +#endif ++nfds; } #endif +#ifndef USE_WINSOCK 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 @@ -1110,7 +1147,9 @@ static CURLMcode Curl_multi_wait(struct Curl_multi *multi, return CURLM_OUT_OF_MEMORY; ufds_malloc = TRUE; } + nfds = 0; +#endif /* only do the second loop if we found descriptors in the first stage run above */ @@ -1123,22 +1162,36 @@ static CURLMcode Curl_multi_wait(struct Curl_multi *multi, for(i = 0; i< MAX_SOCKSPEREASYHANDLE; i++) { curl_socket_t s = CURL_SOCKET_BAD; - +#ifdef USE_WINSOCK + long mask = 0; +#endif if(bitmap & GETSOCK_READSOCK(i)) { +#ifdef USE_WINSOCK + mask |= FD_READ; +#else ufds[nfds].fd = sockbunch[i]; ufds[nfds].events = POLLIN; ++nfds; +#endif s = sockbunch[i]; } if(bitmap & GETSOCK_WRITESOCK(i)) { +#ifdef USE_WINSOCK + mask |= FD_WRITE; +#else ufds[nfds].fd = sockbunch[i]; ufds[nfds].events = POLLOUT; ++nfds; +#endif s = sockbunch[i]; } if(s == CURL_SOCKET_BAD) { break; } +#ifdef USE_WINSOCK + if(WSAEventSelect(s, multi->wsa_event, mask) != 0) + return CURLM_INTERNAL_ERROR; +#endif } data = data->next; /* check next handle */ @@ -1147,6 +1200,17 @@ static CURLMcode Curl_multi_wait(struct Curl_multi *multi, /* Add external file descriptions from poll-like struct curl_waitfd */ for(i = 0; i < extra_nfds; i++) { +#ifdef USE_WINSOCK + long events = 0; + if(extra_fds[i].events & CURL_WAIT_POLLIN) + events |= FD_READ; + if(extra_fds[i].events & CURL_WAIT_POLLPRI) + events |= FD_OOB; + if(extra_fds[i].events & CURL_WAIT_POLLOUT) + events |= FD_WRITE; + if(WSAEventSelect(extra_fds[i].fd, multi->wsa_event, events) != 0) + return CURLM_INTERNAL_ERROR; +#else ufds[nfds].fd = extra_fds[i].fd; ufds[nfds].events = 0; if(extra_fds[i].events & CURL_WAIT_POLLIN) @@ -1156,28 +1220,61 @@ static CURLMcode Curl_multi_wait(struct Curl_multi *multi, if(extra_fds[i].events & CURL_WAIT_POLLOUT) ufds[nfds].events |= POLLOUT; ++nfds; +#endif } #ifdef ENABLE_WAKEUP +#ifndef USE_WINSOCK if(use_wakeup && multi->wakeup_pair[0] != CURL_SOCKET_BAD) { ufds[nfds].fd = multi->wakeup_pair[0]; ufds[nfds].events = POLLIN; ++nfds; } #endif +#endif if(nfds) { int pollrc; /* wait... */ +#ifdef USE_WINSOCK + DWORD waitrc = WSAWaitForMultipleEvents(1, &multi->wsa_event, FALSE, + timeout_ms, FALSE); + /* WSA_WAIT_EVENT_0 is 0, so waitrc >= WSA_WAIT_EVENT_0 warns */ + if(waitrc == WSA_WAIT_EVENT_0) + pollrc = 1; + else + pollrc = -1; +#else pollrc = Curl_poll(ufds, nfds, timeout_ms); +#endif if(pollrc > 0) { +#ifdef USE_WINSOCK + retcode = 0; +#else retcode = pollrc; +#endif /* copy revents results from the poll to the curl_multi_wait poll struct, the bit values of the actual underlying poll() implementation may not be the same as the ones in the public libcurl API! */ for(i = 0; i < extra_nfds; i++) { unsigned short mask = 0; +#ifdef USE_WINSOCK + WSANETWORKEVENTS events = {0}; + if(WSAEnumNetworkEvents(extra_fds[i].fd, multi->wsa_event, + &events) == 0) { + if(events.lNetworkEvents & FD_READ) + mask |= CURL_WAIT_POLLIN; + if(events.lNetworkEvents & FD_WRITE) + mask |= CURL_WAIT_POLLOUT; + if(events.lNetworkEvents & FD_OOB) + mask |= CURL_WAIT_POLLPRI; + + if(events.lNetworkEvents != 0) + retcode++; + } + WSAEventSelect(extra_fds[i].fd, multi->wsa_event, 0); +#else unsigned r = ufds[curlfds + i].revents; if(r & POLLIN) @@ -1186,24 +1283,53 @@ static CURLMcode Curl_multi_wait(struct Curl_multi *multi, mask |= CURL_WAIT_POLLOUT; if(r & POLLPRI) mask |= CURL_WAIT_POLLPRI; +#endif extra_fds[i].revents = mask; } +#ifdef USE_WINSOCK + /* Count up all our own sockets that had activity, + and remove them from the event. */ + if(curlfds) { + data = multi->easyp; + while(data) { + bitmap = multi_getsock(data, sockbunch); + + for(i = 0; i < MAX_SOCKSPEREASYHANDLE; i++) { + if(bitmap & (GETSOCK_READSOCK(i) | GETSOCK_WRITESOCK(i))) { + WSANETWORKEVENTS events = {0}; + if(WSAEnumNetworkEvents(sockbunch[i], multi->wsa_event, + &events) == 0) { + if(events.lNetworkEvents != 0) + retcode++; + } + WSAEventSelect(sockbunch[i], multi->wsa_event, 0); + } + else + break; + } + + data = data->next; + } + } + + WSAResetEvent(multi->wsa_event); +#else #ifdef ENABLE_WAKEUP if(use_wakeup && multi->wakeup_pair[0] != CURL_SOCKET_BAD) { if(ufds[curlfds + extra_nfds].revents & POLLIN) { char buf[64]; + ssize_t nread; 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) + nread = sread(multi->wakeup_pair[0], buf, sizeof(buf)); + if(nread <= 0) { + if(nread < 0 && EINTR == SOCKERRNO) continue; -#endif break; } } @@ -1212,11 +1338,14 @@ static CURLMcode Curl_multi_wait(struct Curl_multi *multi, } } #endif +#endif } } +#ifndef USE_WINSOCK if(ufds_malloc) free(ufds); +#endif if(ret) *ret = retcode; if(!extrawait || nfds) @@ -1233,7 +1362,7 @@ static CURLMcode Curl_multi_wait(struct Curl_multi *multi, timeout */ else if((sleep_ms < 0) && extrawait) sleep_ms = timeout_ms; - Curl_wait_ms((int)sleep_ms); + Curl_wait_ms(sleep_ms); } } @@ -1271,6 +1400,10 @@ CURLMcode curl_multi_wakeup(struct Curl_multi *multi) return CURLM_BAD_HANDLE; #ifdef ENABLE_WAKEUP +#ifdef USE_WINSOCK + if(WSASetEvent(multi->wsa_event)) + return CURLM_OK; +#else /* 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 */ @@ -1304,6 +1437,7 @@ CURLMcode curl_multi_wakeup(struct Curl_multi *multi) } } #endif +#endif return CURLM_WAKEUP_FAILURE; } @@ -1474,7 +1608,7 @@ static CURLcode protocol_connect(struct connectdata *conn, } if(!conn->bits.protoconnstart) { - +#ifndef CURL_DISABLE_PROXY result = Curl_proxy_connect(conn, FIRSTSOCKET); if(result) return result; @@ -1488,7 +1622,7 @@ static CURLcode protocol_connect(struct connectdata *conn, /* when using an HTTP tunnel proxy, await complete tunnel establishment before proceeding further. Return CURLE_OK so we'll be called again */ return CURLE_OK; - +#endif if(conn->handler->connect_it) { /* is there a protocol-specific connect() procedure? */ @@ -1507,6 +1641,21 @@ static CURLcode protocol_connect(struct connectdata *conn, return result; /* pass back status */ } +/* + * Curl_preconnect() is called immediately before a connect starts. When a + * redirect is followed, this is then called multiple times during a single + * transfer. + */ +CURLcode Curl_preconnect(struct Curl_easy *data) +{ + if(!data->state.buffer) { + data->state.buffer = malloc(data->set.buffer_size + 1); + if(!data->state.buffer) + return CURLE_OUT_OF_MEMORY; + } + return CURLE_OK; +} + static CURLMcode multi_runsingle(struct Curl_multi *multi, struct curltime now, @@ -1534,19 +1683,6 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, bool stream_error = FALSE; rc = CURLM_OK; - DEBUGASSERT((data->mstate <= CURLM_STATE_CONNECT) || - (data->mstate >= CURLM_STATE_DONE) || - data->conn); - if(!data->conn && - data->mstate > CURLM_STATE_CONNECT && - data->mstate < CURLM_STATE_DONE) { - /* In all these states, the code will blindly access 'data->conn' - so this is precaution that it isn't NULL. And it silences static - analyzers. */ - failf(data, "In state %d with no conn, bail out!\n", data->mstate); - return CURLM_INTERNAL_ERROR; - } - if(multi_ischanged(multi, TRUE)) { DEBUGF(infof(data, "multi changed, check CONNECT_PEND queue!\n")); process_pending_handles(multi); /* multiplexed */ @@ -1627,6 +1763,11 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, case CURLM_STATE_CONNECT: /* Connect. We want to get a connection identifier filled in. */ + /* init this transfer. */ + result = Curl_preconnect(data); + if(result) + break; + Curl_pgrsTime(data, TIMER_STARTSINGLE); if(data->set.timeout) Curl_expire(data, data->set.timeout, EXPIRE_TIMEOUT); @@ -1684,9 +1825,12 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, const char *hostname; DEBUGASSERT(conn); +#ifndef CURL_DISABLE_PROXY if(conn->bits.httpproxy) hostname = conn->http_proxy.host.name; - else if(conn->bits.conn_to_host) + else +#endif + if(conn->bits.conn_to_host) hostname = conn->conn_to_host.name; else hostname = conn->host.name; @@ -1752,7 +1896,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* this is HTTP-specific, but sending CONNECT to a proxy is HTTP... */ DEBUGASSERT(data->conn); result = Curl_http_connect(data->conn, &protocol_connected); - +#ifndef CURL_DISABLE_PROXY if(data->conn->bits.proxy_connect_closed) { rc = CURLM_CALL_MULTI_PERFORM; /* connect back to proxy again */ @@ -1760,15 +1904,20 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, multi_done(data, CURLE_OK, FALSE); multistate(data, CURLM_STATE_CONNECT); } - else if(!result) { - if((data->conn->http_proxy.proxytype != CURLPROXY_HTTPS || - data->conn->bits.proxy_ssl_connected[FIRSTSOCKET]) && - Curl_connect_complete(data->conn)) { - rc = CURLM_CALL_MULTI_PERFORM; - /* initiate protocol connect phase */ - multistate(data, CURLM_STATE_SENDPROTOCONNECT); + else +#endif + if(!result) { + if( +#ifndef CURL_DISABLE_PROXY + (data->conn->http_proxy.proxytype != CURLPROXY_HTTPS || + data->conn->bits.proxy_ssl_connected[FIRSTSOCKET]) && +#endif + Curl_connect_complete(data->conn)) { + rc = CURLM_CALL_MULTI_PERFORM; + /* initiate protocol connect phase */ + multistate(data, CURLM_STATE_SENDPROTOCONNECT); + } } - } else if(result) stream_error = TRUE; break; @@ -1780,17 +1929,25 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, result = Curl_is_connected(data->conn, FIRSTSOCKET, &connected); if(connected && !result) { #ifndef CURL_DISABLE_HTTP - if((data->conn->http_proxy.proxytype == CURLPROXY_HTTPS && - !data->conn->bits.proxy_ssl_connected[FIRSTSOCKET]) || - Curl_connect_ongoing(data->conn)) { + if( +#ifndef CURL_DISABLE_PROXY + (data->conn->http_proxy.proxytype == CURLPROXY_HTTPS && + !data->conn->bits.proxy_ssl_connected[FIRSTSOCKET]) || +#endif + Curl_connect_ongoing(data->conn)) { multistate(data, CURLM_STATE_WAITPROXYCONNECT); break; } #endif rc = CURLM_CALL_MULTI_PERFORM; - multistate(data, data->conn->bits.tunnel_proxy? +#ifndef CURL_DISABLE_PROXY + multistate(data, + data->conn->bits.tunnel_proxy? CURLM_STATE_WAITPROXYCONNECT: CURLM_STATE_SENDPROTOCONNECT); +#else + multistate(data, CURLM_STATE_SENDPROTOCONNECT); +#endif } else if(result) { /* failure detected */ @@ -2056,7 +2213,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, char *newurl = NULL; bool retry = FALSE; bool comeback = FALSE; - + DEBUGASSERT(data->state.buffer); /* check if over send speed */ send_timeout_ms = 0; if(data->set.max_send_speed > 0) @@ -2229,8 +2386,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, * access free'd data, if the connection is free'd and the handle * removed before we perform the processing in CURLM_STATE_COMPLETED */ - if(data->conn) - detach_connnection(data); + Curl_detach_connnection(data); } #ifndef CURL_DISABLE_FTP @@ -2282,7 +2438,10 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* This is where we make sure that the conn pointer is reset. We don't have to do this in every case block above where a failure is detected */ - detach_connnection(data); + Curl_detach_connnection(data); + + /* remove connection from cache */ + Curl_conncache_remove_conn(data, conn, TRUE); /* disconnect properly */ Curl_disconnect(data, conn, dead_connection); @@ -2437,10 +2596,14 @@ CURLMcode curl_multi_cleanup(struct Curl_multi *multi) Curl_hash_destroy(&multi->hostcache); Curl_psl_destroy(&multi->psl); +#ifdef USE_WINSOCK + WSACloseEvent(multi->wsa_event); +#else #ifdef ENABLE_WAKEUP sclose(multi->wakeup_pair[0]); sclose(multi->wakeup_pair[1]); #endif +#endif free(multi); return CURLM_OK; |