From: Stefan Eissing Date: Mon, 4 Aug 2025 14:17:37 +0000 (+0200) Subject: lib: replace `getsock()` logic with pollsets X-Git-Tag: curl-8_16_0~261 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5b80b4c0121b69e9a9209d6734098181356b3d23;p=thirdparty%2Fcurl.git lib: replace `getsock()` logic with pollsets `getsock()` calls operated on a global limit that could not be configure beyond 16 sockets. This is no longer adequate with the new happy eyeballing strategy. Instead, do the following: - make `struct easy_pollset` dynamic. Starting with a minimal room for two sockets, the very common case, allow it to grow on demand. - replace all protocol handler getsock() calls with pollsets and a CURLcode to return failures - add CURLcode return for all connection filter `adjust_pollset()` callbacks, since they too can now fail. - use appropriately in multi.c and multi_ev.c - fix unit2600 to trigger pollset growth Closes #18164 --- diff --git a/lib/asyn-ares.c b/lib/asyn-ares.c index 007f1d7123..375f1a6eb6 100644 --- a/lib/asyn-ares.c +++ b/lib/asyn-ares.c @@ -273,15 +273,15 @@ static void async_ares_cleanup(struct Curl_easy *data) } /* - * Curl_async_getsock() is called when someone from the outside world + * Curl_async_pollset() is called when someone from the outside world * (using curl_multi_fdset()) wants to get our fd_set setup. */ -int Curl_async_getsock(struct Curl_easy *data, curl_socket_t *socks) +CURLcode Curl_async_pollset(struct Curl_easy *data, struct easy_pollset *ps) { struct async_ares_ctx *ares = &data->state.async.ares; DEBUGASSERT(ares->channel); - return Curl_ares_getsock(data, ares->channel, socks); + return Curl_ares_pollset(data, ares->channel, ps); } /* diff --git a/lib/asyn-base.c b/lib/asyn-base.c index ea89fba15c..c0df7aac5e 100644 --- a/lib/asyn-base.c +++ b/lib/asyn-base.c @@ -70,7 +70,7 @@ #endif /* - * Curl_ares_getsock() is called when the outside world (using + * Curl_ares_pollset() is called when the outside world (using * curl_multi_fdset()) wants to get our fd_set setup and we are talking with * ares. The caller must make sure that this function is only called when we * have a working ares channel. @@ -78,18 +78,38 @@ * Returns: sockets-in-use-bitmap */ -int Curl_ares_getsock(struct Curl_easy *data, - ares_channel channel, - curl_socket_t *socks) + +CURLcode Curl_ares_pollset(struct Curl_easy *data, + ares_channel channel, + struct easy_pollset *ps) { struct timeval maxtime = { CURL_TIMEOUT_RESOLVE, 0 }; struct timeval timebuf; - int max = ares_getsock(channel, - (ares_socket_t *)socks, MAX_SOCKSPEREASYHANDLE); - struct timeval *timeout = ares_timeout(channel, &maxtime, &timebuf); - timediff_t milli = curlx_tvtoms(timeout); + curl_socket_t sockets[16]; /* ARES documented limit */ + unsigned int bitmap, i; + struct timeval *timeout; + timediff_t milli; + CURLcode result = CURLE_OK; + + bitmap = ares_getsock(channel, (ares_socket_t *)sockets, + CURL_ARRAYSIZE(sockets)); + for(i = 0; i < CURL_ARRAYSIZE(sockets); ++i) { + int flags = 0; + if(ARES_GETSOCK_READABLE(bitmap, i)) + flags |= CURL_POLL_IN; + if(ARES_GETSOCK_WRITABLE(bitmap, i)) + flags |= CURL_POLL_OUT; + if(!flags) + break; + result = Curl_pollset_change(data, ps, sockets[i], flags, 0); + if(result) + return result; + } + + timeout = ares_timeout(channel, &maxtime, &timebuf); + milli = curlx_tvtoms(timeout); Curl_expire(data, milli, EXPIRE_ASYNC_NAME); - return max; + return result; } /* diff --git a/lib/asyn-thrdd.c b/lib/asyn-thrdd.c index 19d1336854..6a56f92c4e 100644 --- a/lib/asyn-thrdd.c +++ b/lib/asyn-thrdd.c @@ -63,6 +63,7 @@ #include "url.h" #include "multiif.h" #include "curl_threads.h" +#include "select.h" #include "strdup.h" #ifdef USE_ARES @@ -629,33 +630,25 @@ CURLcode Curl_async_is_resolved(struct Curl_easy *data, } } -int Curl_async_getsock(struct Curl_easy *data, curl_socket_t *socks) +CURLcode Curl_async_pollset(struct Curl_easy *data, struct easy_pollset *ps) { struct async_thrdd_ctx *thrdd = &data->state.async.thrdd; - int ret_val = 0; -#if !defined(CURL_DISABLE_SOCKETPAIR) || defined(USE_HTTPSRR_ARES) - int socketi = 0; -#else - (void)socks; -#endif + CURLcode result = CURLE_OK; #ifdef USE_HTTPSRR_ARES if(thrdd->rr.channel) { - ret_val = Curl_ares_getsock(data, thrdd->rr.channel, socks); - for(socketi = 0; socketi < (MAX_SOCKSPEREASYHANDLE - 1); socketi++) - if(!ARES_GETSOCK_READABLE(ret_val, socketi) && - !ARES_GETSOCK_WRITABLE(ret_val, socketi)) - break; + result = Curl_ares_pollset(data, thrdd->rr.channel, ps); + if(result) + return result; } #endif if(!thrdd->addr) - return ret_val; + return result; #ifndef CURL_DISABLE_SOCKETPAIR if(thrdd->addr) { /* return read fd to client for polling the DNS resolution status */ - socks[socketi] = thrdd->addr->sock_pair[0]; - ret_val |= GETSOCK_READSOCK(socketi); + result = Curl_pollset_add_in(data, ps, thrdd->addr->sock_pair[0]); } else #endif @@ -673,7 +666,7 @@ int Curl_async_getsock(struct Curl_easy *data, curl_socket_t *socks) Curl_expire(data, milli, EXPIRE_ASYNC_NAME); } - return ret_val; + return result; } #ifndef HAVE_GETADDRINFO diff --git a/lib/asyn.h b/lib/asyn.h index a336c1e254..69aeb8b482 100644 --- a/lib/asyn.h +++ b/lib/asyn.h @@ -37,6 +37,7 @@ struct Curl_dns_entry; struct addrinfo; struct hostent; struct connectdata; +struct easy_pollset; #if defined(CURLRES_ARES) && defined(CURLRES_THREADED) #error cannot have both CURLRES_ARES and CURLRES_THREADED defined @@ -70,15 +71,15 @@ void Curl_async_global_cleanup(void); */ CURLcode Curl_async_get_impl(struct Curl_easy *easy, void **impl); -/* Curl_async_getsock() +/* Curl_async_pollset() * - * This function is called from the Curl_multi_getsock() function. 'sock' is a + * This function is called from the Curl_multi_pollset() function. 'sock' is a * pointer to an array to hold the file descriptors, with 'numsock' being the * size of that array (in number of entries). This function is supposed to * return bitmask indicating what file descriptors (referring to array indexes * in the 'sock' array) to wait for, read/write. */ -int Curl_async_getsock(struct Curl_easy *data, curl_socket_t *sock); +CURLcode Curl_async_pollset(struct Curl_easy *data, struct easy_pollset *ps); /* * Curl_async_is_resolved() @@ -127,9 +128,10 @@ struct Curl_addrinfo *Curl_async_getaddrinfo(struct Curl_easy *data, /* common functions for c-ares and threaded resolver with HTTPSRR */ #include -int Curl_ares_getsock(struct Curl_easy *data, - ares_channel channel, - curl_socket_t *socks); +CURLcode Curl_ares_pollset(struct Curl_easy *data, + ares_channel channel, + struct easy_pollset *ps); + int Curl_ares_perform(ares_channel channel, timediff_t timeout_ms); #endif diff --git a/lib/cf-h1-proxy.c b/lib/cf-h1-proxy.c index 4aac877fc1..798d6e9f66 100644 --- a/lib/cf-h1-proxy.c +++ b/lib/cf-h1-proxy.c @@ -682,11 +682,12 @@ out: return result; } -static void cf_h1_proxy_adjust_pollset(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct easy_pollset *ps) +static CURLcode cf_h1_proxy_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps) { struct h1_tunnel_state *ts = cf->ctx; + CURLcode result = CURLE_OK; if(!cf->connected) { /* If we are not connected, but the filter "below" is @@ -698,13 +699,14 @@ static void cf_h1_proxy_adjust_pollset(struct Curl_cfilter *cf, response headers or if we are still sending the request, wait for write. */ if(tunnel_want_send(ts)) - Curl_pollset_set_out_only(data, ps, sock); + result = Curl_pollset_set_out_only(data, ps, sock); else - Curl_pollset_set_in_only(data, ps, sock); + result = Curl_pollset_set_in_only(data, ps, sock); } else - Curl_pollset_set_out_only(data, ps, sock); + result = Curl_pollset_set_out_only(data, ps, sock); } + return result; } static void cf_h1_proxy_destroy(struct Curl_cfilter *cf, diff --git a/lib/cf-h2-proxy.c b/lib/cf-h2-proxy.c index 162881b17e..18803ed50b 100644 --- a/lib/cf-h2-proxy.c +++ b/lib/cf-h2-proxy.c @@ -41,6 +41,7 @@ #include "http_proxy.h" #include "multiif.h" #include "sendf.h" +#include "select.h" #include "cf-h2-proxy.h" /* The last 3 #include files should be in this order */ @@ -1202,14 +1203,15 @@ static bool cf_h2_proxy_data_pending(struct Curl_cfilter *cf, return cf->next ? cf->next->cft->has_data_pending(cf->next, data) : FALSE; } -static void cf_h2_proxy_adjust_pollset(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct easy_pollset *ps) +static CURLcode cf_h2_proxy_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps) { struct cf_h2_proxy_ctx *ctx = cf->ctx; struct cf_call_data save; curl_socket_t sock = Curl_conn_cf_get_socket(cf, data); bool want_recv, want_send; + CURLcode result = CURLE_OK; if(!cf->connected && ctx->h2) { want_send = nghttp2_session_want_write(ctx->h2) || @@ -1234,9 +1236,9 @@ static void cf_h2_proxy_adjust_pollset(struct Curl_cfilter *cf, !Curl_bufq_is_empty(&ctx->outbufq) || !Curl_bufq_is_empty(&ctx->tunnel.sendbuf); - Curl_pollset_set(data, ps, sock, want_recv, want_send); - CURL_TRC_CF(data, cf, "adjust_pollset, want_recv=%d want_send=%d", - want_recv, want_send); + result = Curl_pollset_set(data, ps, sock, want_recv, want_send); + CURL_TRC_CF(data, cf, "adjust_pollset, want_recv=%d want_send=%d -> %d", + want_recv, want_send, result); CF_DATA_RESTORE(cf, save); } else if(ctx->sent_goaway && !cf->shutdown) { @@ -1246,11 +1248,12 @@ static void cf_h2_proxy_adjust_pollset(struct Curl_cfilter *cf, !Curl_bufq_is_empty(&ctx->outbufq) || !Curl_bufq_is_empty(&ctx->tunnel.sendbuf); want_recv = nghttp2_session_want_read(ctx->h2); - Curl_pollset_set(data, ps, sock, want_recv, want_send); - CURL_TRC_CF(data, cf, "adjust_pollset, want_recv=%d want_send=%d", - want_recv, want_send); + result = Curl_pollset_set(data, ps, sock, want_recv, want_send); + CURL_TRC_CF(data, cf, "adjust_pollset, want_recv=%d want_send=%d -> %d", + want_recv, want_send, result); CF_DATA_RESTORE(cf, save); } + return result; } static CURLcode h2_handle_tunnel_close(struct Curl_cfilter *cf, diff --git a/lib/cf-haproxy.c b/lib/cf-haproxy.c index 62ac89e819..2d66efea90 100644 --- a/lib/cf-haproxy.c +++ b/lib/cf-haproxy.c @@ -32,6 +32,7 @@ #include "cf-haproxy.h" #include "curl_trc.h" #include "multiif.h" +#include "select.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" @@ -178,15 +179,17 @@ static void cf_haproxy_close(struct Curl_cfilter *cf, cf->next->cft->do_close(cf->next, data); } -static void cf_haproxy_adjust_pollset(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct easy_pollset *ps) +static CURLcode cf_haproxy_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps) { if(cf->next->connected && !cf->connected) { /* If we are not connected, but the filter "below" is * and not waiting on something, we are sending. */ - Curl_pollset_set_out_only(data, ps, Curl_conn_cf_get_socket(cf, data)); + return Curl_pollset_set_out_only( + data, ps, Curl_conn_cf_get_socket(cf, data)); } + return CURLE_OK; } struct Curl_cftype Curl_cft_haproxy = { diff --git a/lib/cf-https-connect.c b/lib/cf-https-connect.c index f36d227b11..b4a4605295 100644 --- a/lib/cf-https-connect.c +++ b/lib/cf-https-connect.c @@ -35,6 +35,7 @@ #include "multiif.h" #include "cf-https-connect.h" #include "http2.h" +#include "select.h" #include "vquic/vquic.h" /* The last 3 #include files should be in this order */ @@ -426,22 +427,24 @@ static CURLcode cf_hc_shutdown(struct Curl_cfilter *cf, return result; } -static void cf_hc_adjust_pollset(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct easy_pollset *ps) +static CURLcode cf_hc_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps) { + CURLcode result = CURLE_OK; if(!cf->connected) { struct cf_hc_ctx *ctx = cf->ctx; size_t i; - for(i = 0; i < ctx->baller_count; i++) { + for(i = 0; (i < ctx->baller_count) && !result; i++) { struct cf_hc_baller *b = &ctx->ballers[i]; if(!cf_hc_baller_is_active(b)) continue; - Curl_conn_cf_adjust_pollset(b->cf, data, ps); + result = Curl_conn_cf_adjust_pollset(b->cf, data, ps); } - CURL_TRC_CF(data, cf, "adjust_pollset -> %d socks", ps->num); + CURL_TRC_CF(data, cf, "adjust_pollset -> %d, %d socks", result, ps->n); } + return result; } static bool cf_hc_data_pending(struct Curl_cfilter *cf, diff --git a/lib/cf-ip-happy.c b/lib/cf-ip-happy.c index 6dbb5c5fdc..59b34ae46f 100644 --- a/lib/cf-ip-happy.c +++ b/lib/cf-ip-happy.c @@ -60,6 +60,7 @@ #include "curl_trc.h" #include "multiif.h" #include "progress.h" +#include "select.h" #include "vquic/vquic.h" /* for quic cfilters */ /* The last 3 #include files should be in this order */ @@ -533,16 +534,18 @@ static CURLcode cf_ip_ballers_shutdown(struct cf_ip_ballers *bs, return CURLE_OK; } -static void cf_ip_ballers_pollset(struct cf_ip_ballers *bs, - struct Curl_easy *data, - struct easy_pollset *ps) +static CURLcode cf_ip_ballers_pollset(struct cf_ip_ballers *bs, + struct Curl_easy *data, + struct easy_pollset *ps) { struct cf_ip_attempt *a; - for(a = bs->running; a; a = a->next) { + CURLcode result = CURLE_OK; + for(a = bs->running; a && !result; a = a->next) { if(a->result) continue; - Curl_conn_cf_adjust_pollset(a->cf, data, ps); + result = Curl_conn_cf_adjust_pollset(a->cf, data, ps); } + return result; } static bool cf_ip_ballers_pending(struct cf_ip_ballers *bs, @@ -713,16 +716,18 @@ static CURLcode cf_ip_happy_shutdown(struct Curl_cfilter *cf, return result; } -static void cf_ip_happy_adjust_pollset(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct easy_pollset *ps) +static CURLcode cf_ip_happy_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps) { struct cf_ip_happy_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; if(!cf->connected) { - cf_ip_ballers_pollset(&ctx->ballers, data, ps); - CURL_TRC_CF(data, cf, "adjust_pollset -> %d socks", ps->num); + result = cf_ip_ballers_pollset(&ctx->ballers, data, ps); + CURL_TRC_CF(data, cf, "adjust_pollset -> %d, %d socks", result, ps->n); } + return result; } static CURLcode cf_ip_happy_connect(struct Curl_cfilter *cf, diff --git a/lib/cf-socket.c b/lib/cf-socket.c index 06c9a74368..7f3f8e60ff 100644 --- a/lib/cf-socket.c +++ b/lib/cf-socket.c @@ -1376,11 +1376,12 @@ out: return result; } -static void cf_socket_adjust_pollset(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct easy_pollset *ps) +static CURLcode cf_socket_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps) { struct cf_socket_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; if(ctx->sock != CURL_SOCKET_BAD) { /* A listening socket filter needs to be connected before the accept @@ -1388,21 +1389,22 @@ static void cf_socket_adjust_pollset(struct Curl_cfilter *cf, * FTP no longer does the socket checks and accept calls and delegates * all that to the filter. */ if(ctx->listening) { - Curl_pollset_set_in_only(data, ps, ctx->sock); + result = Curl_pollset_set_in_only(data, ps, ctx->sock); CURL_TRC_CF(data, cf, "adjust_pollset, listening, POLLIN fd=%" FMT_SOCKET_T, ctx->sock); } else if(!cf->connected) { - Curl_pollset_set_out_only(data, ps, ctx->sock); + result = Curl_pollset_set_out_only(data, ps, ctx->sock); CURL_TRC_CF(data, cf, "adjust_pollset, !connected, POLLOUT fd=%" FMT_SOCKET_T, ctx->sock); } else if(!ctx->active) { - Curl_pollset_add_in(data, ps, ctx->sock); + result = Curl_pollset_add_in(data, ps, ctx->sock); CURL_TRC_CF(data, cf, "adjust_pollset, !active, POLLIN fd=%" FMT_SOCKET_T, ctx->sock); } } + return result; } #ifdef USE_WINSOCK diff --git a/lib/cfilters.c b/lib/cfilters.c index c7333e12b0..e4b630bc8f 100644 --- a/lib/cfilters.c +++ b/lib/cfilters.c @@ -67,14 +67,15 @@ CURLcode Curl_cf_def_shutdown(struct Curl_cfilter *cf, static void conn_report_connect_stats(struct Curl_easy *data, struct connectdata *conn); -void Curl_cf_def_adjust_pollset(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct easy_pollset *ps) +CURLcode Curl_cf_def_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps) { /* NOP */ (void)cf; (void)data; (void)ps; + return CURLE_OK; } bool Curl_cf_def_data_pending(struct Curl_cfilter *cf, @@ -469,6 +470,7 @@ CURLcode Curl_conn_connect(struct Curl_easy *data, { #define CF_CONN_NUM_POLLS_ON_STACK 5 struct pollfd a_few_on_stack[CF_CONN_NUM_POLLS_ON_STACK]; + struct easy_pollset ps; struct curl_pollfds cpfds; struct Curl_cfilter *cf; CURLcode result = CURLE_OK; @@ -486,6 +488,7 @@ CURLcode Curl_conn_connect(struct Curl_easy *data, if(*done) return CURLE_OK; + Curl_pollset_init(&ps); Curl_pollfds_init(&cpfds, a_few_on_stack, CF_CONN_NUM_POLLS_ON_STACK); while(!*done) { if(Curl_conn_needs_flush(data, sockindex)) { @@ -523,7 +526,6 @@ CURLcode Curl_conn_connect(struct Curl_easy *data, /* check allowed time left */ const timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE); curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data); - struct easy_pollset ps; int rc; if(timeout_ms < 0) { @@ -534,12 +536,14 @@ CURLcode Curl_conn_connect(struct Curl_easy *data, } CURL_TRC_CF(data, cf, "Curl_conn_connect(block=1), do poll"); + Curl_pollset_reset(&ps); Curl_pollfds_reset(&cpfds); - memset(&ps, 0, sizeof(ps)); /* In general, we want to send after connect, wait on that. */ if(sockfd != CURL_SOCKET_BAD) Curl_pollset_set_out_only(data, &ps, sockfd); - Curl_conn_adjust_pollset(data, data->conn, &ps); + result = Curl_conn_adjust_pollset(data, data->conn, &ps); + if(result) + goto out; result = Curl_pollfds_add_ps(&cpfds, &ps); if(result) goto out; @@ -557,6 +561,7 @@ CURLcode Curl_conn_connect(struct Curl_easy *data, } out: + Curl_pollset_cleanup(&ps); Curl_pollfds_cleanup(&cpfds); return result; } @@ -710,10 +715,11 @@ bool Curl_conn_needs_flush(struct Curl_easy *data, int sockindex) return Curl_conn_cf_needs_flush(data->conn->cfilter[sockindex], data); } -void Curl_conn_cf_adjust_pollset(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct easy_pollset *ps) +CURLcode Curl_conn_cf_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps) { + CURLcode result = CURLE_OK; /* Get the lowest not-connected filter, if there are any */ while(cf && !cf->connected && cf->next && !cf->next->connected) cf = cf->next; @@ -722,23 +728,26 @@ void Curl_conn_cf_adjust_pollset(struct Curl_cfilter *cf, cf = cf->next; /* From there on, give all filters a chance to adjust the pollset. * Lower filters are called later, so they may override */ - while(cf) { - cf->cft->adjust_pollset(cf, data, ps); + while(cf && !result) { + result = cf->cft->adjust_pollset(cf, data, ps); cf = cf->next; } + return result; } -void Curl_conn_adjust_pollset(struct Curl_easy *data, - struct connectdata *conn, - struct easy_pollset *ps) +CURLcode Curl_conn_adjust_pollset(struct Curl_easy *data, + struct connectdata *conn, + struct easy_pollset *ps) { + CURLcode result = CURLE_OK; int i; DEBUGASSERT(data); DEBUGASSERT(conn); - for(i = 0; i < 2; ++i) { - Curl_conn_cf_adjust_pollset(conn->cfilter[i], data, ps); + for(i = 0; (i < 2) && !result; ++i) { + result = Curl_conn_cf_adjust_pollset(conn->cfilter[i], data, ps); } + return result; } int Curl_conn_cf_poll(struct Curl_cfilter *cf, @@ -746,35 +755,18 @@ int Curl_conn_cf_poll(struct Curl_cfilter *cf, timediff_t timeout_ms) { struct easy_pollset ps; - struct pollfd pfds[MAX_SOCKSPEREASYHANDLE]; - unsigned int i, npfds = 0; + int result; DEBUGASSERT(cf); DEBUGASSERT(data); DEBUGASSERT(data->conn); - memset(&ps, 0, sizeof(ps)); - memset(pfds, 0, sizeof(pfds)); - - Curl_conn_cf_adjust_pollset(cf, data, &ps); - DEBUGASSERT(ps.num <= MAX_SOCKSPEREASYHANDLE); - for(i = 0; i < ps.num; ++i) { - short events = 0; - if(ps.actions[i] & CURL_POLL_IN) { - events |= POLLIN; - } - if(ps.actions[i] & CURL_POLL_OUT) { - events |= POLLOUT; - } - if(events) { - pfds[npfds].fd = ps.sockets[i]; - pfds[npfds].events = events; - ++npfds; - } - } + Curl_pollset_init(&ps); - if(!npfds) - DEBUGF(infof(data, "no sockets to poll!")); - return Curl_poll(pfds, npfds, timeout_ms); + result = Curl_conn_cf_adjust_pollset(cf, data, &ps); + if(!result) + result = Curl_pollset_poll(data, &ps, timeout_ms); + Curl_pollset_cleanup(&ps); + return result; } void Curl_conn_get_current_host(struct Curl_easy *data, int sockindex, @@ -1099,142 +1091,3 @@ CURLcode Curl_conn_send(struct Curl_easy *data, int sockindex, *pnwritten = 0; return CURLE_FAILED_INIT; } - -void Curl_pollset_reset(struct Curl_easy *data, - struct easy_pollset *ps) -{ - size_t i; - (void)data; - memset(ps, 0, sizeof(*ps)); - for(i = 0; i < MAX_SOCKSPEREASYHANDLE; i++) - ps->sockets[i] = CURL_SOCKET_BAD; -} - -/** - * - */ -void Curl_pollset_change(struct Curl_easy *data, - struct easy_pollset *ps, curl_socket_t sock, - int add_flags, int remove_flags) -{ - unsigned int i; - - (void)data; - DEBUGASSERT(VALID_SOCK(sock)); - if(!VALID_SOCK(sock)) - return; - - DEBUGASSERT(add_flags <= (CURL_POLL_IN|CURL_POLL_OUT)); - DEBUGASSERT(remove_flags <= (CURL_POLL_IN|CURL_POLL_OUT)); - DEBUGASSERT((add_flags&remove_flags) == 0); /* no overlap */ - for(i = 0; i < ps->num; ++i) { - if(ps->sockets[i] == sock) { - ps->actions[i] &= (unsigned char)(~remove_flags); - ps->actions[i] |= (unsigned char)add_flags; - /* all gone? remove socket */ - if(!ps->actions[i]) { - if((i + 1) < ps->num) { - memmove(&ps->sockets[i], &ps->sockets[i + 1], - (ps->num - (i + 1)) * sizeof(ps->sockets[0])); - memmove(&ps->actions[i], &ps->actions[i + 1], - (ps->num - (i + 1)) * sizeof(ps->actions[0])); - } - --ps->num; - } - return; - } - } - /* not present */ - if(add_flags) { - /* Having more SOCKETS per easy handle than what is defined - * is a programming error. This indicates that we need - * to raise this limit, making easy_pollset larger. - * Since we use this in tight loops, we do not want to make - * the pollset dynamic unnecessarily. - * The current maximum in practise is HTTP/3 eyeballing where - * we have up to 4 sockets involved in connection setup. - */ - DEBUGASSERT(i < MAX_SOCKSPEREASYHANDLE); - if(i < MAX_SOCKSPEREASYHANDLE) { - ps->sockets[i] = sock; - ps->actions[i] = (unsigned char)add_flags; - ps->num = i + 1; - } - } -} - -void Curl_pollset_set(struct Curl_easy *data, - struct easy_pollset *ps, curl_socket_t sock, - bool do_in, bool do_out) -{ - Curl_pollset_change(data, ps, sock, - (do_in ? CURL_POLL_IN : 0)| - (do_out ? CURL_POLL_OUT : 0), - (!do_in ? CURL_POLL_IN : 0)| - (!do_out ? CURL_POLL_OUT : 0)); -} - -static void ps_add(struct Curl_easy *data, struct easy_pollset *ps, - int bitmap, curl_socket_t *socks) -{ - if(bitmap) { - int i; - for(i = 0; i < MAX_SOCKSPEREASYHANDLE; ++i) { - if(!(bitmap & GETSOCK_MASK_RW(i)) || !VALID_SOCK((socks[i]))) { - break; - } - if(bitmap & GETSOCK_READSOCK(i)) { - if(bitmap & GETSOCK_WRITESOCK(i)) - Curl_pollset_add_inout(data, ps, socks[i]); - else - /* is READ, since we checked MASK_RW above */ - Curl_pollset_add_in(data, ps, socks[i]); - } - else - Curl_pollset_add_out(data, ps, socks[i]); - } - } -} - -void Curl_pollset_add_socks(struct Curl_easy *data, - struct easy_pollset *ps, - int (*get_socks_cb)(struct Curl_easy *data, - curl_socket_t *socks)) -{ - curl_socket_t socks[MAX_SOCKSPEREASYHANDLE]; - int bitmap; - - bitmap = get_socks_cb(data, socks); - ps_add(data, ps, bitmap, socks); -} - -void Curl_pollset_check(struct Curl_easy *data, - struct easy_pollset *ps, curl_socket_t sock, - bool *pwant_read, bool *pwant_write) -{ - unsigned int i; - - (void)data; - DEBUGASSERT(VALID_SOCK(sock)); - for(i = 0; i < ps->num; ++i) { - if(ps->sockets[i] == sock) { - *pwant_read = !!(ps->actions[i] & CURL_POLL_IN); - *pwant_write = !!(ps->actions[i] & CURL_POLL_OUT); - return; - } - } - *pwant_read = *pwant_write = FALSE; -} - -bool Curl_pollset_want_read(struct Curl_easy *data, - struct easy_pollset *ps, - curl_socket_t sock) -{ - unsigned int i; - (void)data; - for(i = 0; i < ps->num; ++i) { - if((ps->sockets[i] == sock) && (ps->actions[i] & CURL_POLL_IN)) - return TRUE; - } - return FALSE; -} diff --git a/lib/cfilters.h b/lib/cfilters.h index 0b6e1044fc..458ce58937 100644 --- a/lib/cfilters.h +++ b/lib/cfilters.h @@ -80,7 +80,7 @@ struct easy_pollset; * @param data the easy handle the pollset is about * @param ps the pollset (inout) for the easy handle */ -typedef void Curl_cft_adjust_pollset(struct Curl_cfilter *cf, +typedef CURLcode Curl_cft_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data, struct easy_pollset *ps); @@ -244,7 +244,7 @@ void Curl_cf_def_destroy_this(struct Curl_cfilter *cf, /* Default implementations for the type functions, implementing pass-through * the filter chain. */ -void Curl_cf_def_adjust_pollset(struct Curl_cfilter *cf, +CURLcode Curl_cf_def_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data, struct easy_pollset *ps); bool Curl_cf_def_data_pending(struct Curl_cfilter *cf, @@ -477,16 +477,16 @@ void Curl_conn_forget_socket(struct Curl_easy *data, int sockindex); /** * Adjust the pollset for the filter chain starting at `cf`. */ -void Curl_conn_cf_adjust_pollset(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct easy_pollset *ps); +CURLcode Curl_conn_cf_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps); /** * Adjust pollset from filters installed at transfer's connection. */ -void Curl_conn_adjust_pollset(struct Curl_easy *data, - struct connectdata *conn, - struct easy_pollset *ps); +CURLcode Curl_conn_adjust_pollset(struct Curl_easy *data, + struct connectdata *conn, + struct easy_pollset *ps); /** * Curl_poll() the filter chain at `cf` with timeout `timeout_ms`. @@ -632,56 +632,6 @@ CURLcode Curl_conn_send(struct Curl_easy *data, int sockindex, size_t *pnwritten); -void Curl_pollset_reset(struct Curl_easy *data, - struct easy_pollset *ps); - -/* Change the poll flags (CURL_POLL_IN/CURL_POLL_OUT) to the poll set for - * socket `sock`. If the socket is not already part of the poll set, it - * will be added. - * If the socket is present and all poll flags are cleared, it will be removed. - */ -void Curl_pollset_change(struct Curl_easy *data, - struct easy_pollset *ps, curl_socket_t sock, - int add_flags, int remove_flags); - -void Curl_pollset_set(struct Curl_easy *data, - struct easy_pollset *ps, curl_socket_t sock, - bool do_in, bool do_out); - -#define Curl_pollset_add_in(data, ps, sock) \ - Curl_pollset_change((data), (ps), (sock), CURL_POLL_IN, 0) -#define Curl_pollset_add_out(data, ps, sock) \ - Curl_pollset_change((data), (ps), (sock), CURL_POLL_OUT, 0) -#define Curl_pollset_add_inout(data, ps, sock) \ - Curl_pollset_change((data), (ps), (sock), \ - CURL_POLL_IN|CURL_POLL_OUT, 0) -#define Curl_pollset_set_in_only(data, ps, sock) \ - Curl_pollset_change((data), (ps), (sock), \ - CURL_POLL_IN, CURL_POLL_OUT) -#define Curl_pollset_set_out_only(data, ps, sock) \ - Curl_pollset_change((data), (ps), (sock), \ - CURL_POLL_OUT, CURL_POLL_IN) - -void Curl_pollset_add_socks(struct Curl_easy *data, - struct easy_pollset *ps, - int (*get_socks_cb)(struct Curl_easy *data, - curl_socket_t *socks)); - -/** - * Check if the pollset, as is, wants to read and/or write regarding - * the given socket. - */ -void Curl_pollset_check(struct Curl_easy *data, - struct easy_pollset *ps, curl_socket_t sock, - bool *pwant_read, bool *pwant_write); - -/** - * Return TRUE if the pollset contains socket with CURL_POLL_IN. - */ -bool Curl_pollset_want_read(struct Curl_easy *data, - struct easy_pollset *ps, - curl_socket_t sock); - /** * Types and macros used to keep the current easy handle in filter calls, * allowing for nested invocations. See #10336. diff --git a/lib/cshutdn.c b/lib/cshutdn.c index aeea9bdc3e..1c144c6025 100644 --- a/lib/cshutdn.c +++ b/lib/cshutdn.c @@ -486,18 +486,24 @@ void Curl_cshutdn_setfds(struct cshutdn *cshutdn, { if(Curl_llist_head(&cshutdn->list)) { struct Curl_llist_node *e; + struct easy_pollset ps; + Curl_pollset_init(&ps); for(e = Curl_llist_head(&cshutdn->list); e; e = Curl_node_next(e)) { - struct easy_pollset ps; unsigned int i; struct connectdata *conn = Curl_node_elem(e); - memset(&ps, 0, sizeof(ps)); + CURLcode result; + + Curl_pollset_reset(&ps); Curl_attach_connection(data, conn); - Curl_conn_adjust_pollset(data, conn, &ps); + result = Curl_conn_adjust_pollset(data, conn, &ps); Curl_detach_connection(data); - for(i = 0; i < ps.num; i++) { + if(result) + continue; + + for(i = 0; i < ps.n; i++) { #ifdef __DJGPP__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Warith-conversion" @@ -514,6 +520,7 @@ void Curl_cshutdn_setfds(struct cshutdn *cshutdn, *maxfd = (int)ps.sockets[i]; } } + Curl_pollset_cleanup(&ps); } } @@ -528,17 +535,21 @@ unsigned int Curl_cshutdn_add_waitfds(struct cshutdn *cshutdn, struct Curl_llist_node *e; struct easy_pollset ps; struct connectdata *conn; + CURLcode result; + Curl_pollset_init(&ps); for(e = Curl_llist_head(&cshutdn->list); e; e = Curl_node_next(e)) { conn = Curl_node_elem(e); - memset(&ps, 0, sizeof(ps)); + Curl_pollset_reset(&ps); Curl_attach_connection(data, conn); - Curl_conn_adjust_pollset(data, conn, &ps); + result = Curl_conn_adjust_pollset(data, conn, &ps); Curl_detach_connection(data); - need += Curl_waitfds_add_ps(cwfds, &ps); + if(!result) + need += Curl_waitfds_add_ps(cwfds, &ps); } + Curl_pollset_cleanup(&ps); } return need; } @@ -554,20 +565,24 @@ CURLcode Curl_cshutdn_add_pollfds(struct cshutdn *cshutdn, struct easy_pollset ps; struct connectdata *conn; + Curl_pollset_init(&ps); for(e = Curl_llist_head(&cshutdn->list); e; e = Curl_node_next(e)) { conn = Curl_node_elem(e); - memset(&ps, 0, sizeof(ps)); + Curl_pollset_reset(&ps); Curl_attach_connection(data, conn); - Curl_conn_adjust_pollset(data, conn, &ps); + result = Curl_conn_adjust_pollset(data, conn, &ps); Curl_detach_connection(data); - result = Curl_pollfds_add_ps(cpfds, &ps); + if(!result) + result = Curl_pollfds_add_ps(cpfds, &ps); if(result) { + Curl_pollset_cleanup(&ps); Curl_pollfds_cleanup(cpfds); goto out; } } + Curl_pollset_cleanup(&ps); } out: return result; diff --git a/lib/curl_rtmp.c b/lib/curl_rtmp.c index 0be1f7c768..584c8a9df6 100644 --- a/lib/curl_rtmp.c +++ b/lib/curl_rtmp.c @@ -81,10 +81,10 @@ const struct Curl_handler Curl_handler_rtmp = { rtmp_connect, /* connect_it */ ZERO_NULL, /* connecting */ ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_getsock */ - ZERO_NULL, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ + ZERO_NULL, /* proto_pollset */ + ZERO_NULL, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + ZERO_NULL, /* perform_pollset */ rtmp_disconnect, /* disconnect */ ZERO_NULL, /* write_resp */ ZERO_NULL, /* write_resp_hd */ @@ -106,10 +106,10 @@ const struct Curl_handler Curl_handler_rtmpt = { rtmp_connect, /* connect_it */ ZERO_NULL, /* connecting */ ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_getsock */ - ZERO_NULL, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ + ZERO_NULL, /* proto_pollset */ + ZERO_NULL, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + ZERO_NULL, /* perform_pollset */ rtmp_disconnect, /* disconnect */ ZERO_NULL, /* write_resp */ ZERO_NULL, /* write_resp_hd */ @@ -131,10 +131,10 @@ const struct Curl_handler Curl_handler_rtmpe = { rtmp_connect, /* connect_it */ ZERO_NULL, /* connecting */ ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_getsock */ - ZERO_NULL, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ + ZERO_NULL, /* proto_pollset */ + ZERO_NULL, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + ZERO_NULL, /* perform_pollset */ rtmp_disconnect, /* disconnect */ ZERO_NULL, /* write_resp */ ZERO_NULL, /* write_resp_hd */ @@ -156,10 +156,10 @@ const struct Curl_handler Curl_handler_rtmpte = { rtmp_connect, /* connect_it */ ZERO_NULL, /* connecting */ ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_getsock */ - ZERO_NULL, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ + ZERO_NULL, /* proto_pollset */ + ZERO_NULL, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + ZERO_NULL, /* perform_pollset */ rtmp_disconnect, /* disconnect */ ZERO_NULL, /* write_resp */ ZERO_NULL, /* write_resp_hd */ @@ -181,10 +181,10 @@ const struct Curl_handler Curl_handler_rtmps = { rtmp_connect, /* connect_it */ ZERO_NULL, /* connecting */ ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_getsock */ - ZERO_NULL, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ + ZERO_NULL, /* proto_pollset */ + ZERO_NULL, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + ZERO_NULL, /* perform_pollset */ rtmp_disconnect, /* disconnect */ ZERO_NULL, /* write_resp */ ZERO_NULL, /* write_resp_hd */ @@ -206,10 +206,10 @@ const struct Curl_handler Curl_handler_rtmpts = { rtmp_connect, /* connect_it */ ZERO_NULL, /* connecting */ ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_getsock */ - ZERO_NULL, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ + ZERO_NULL, /* proto_pollset */ + ZERO_NULL, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + ZERO_NULL, /* perform_pollset */ rtmp_disconnect, /* disconnect */ ZERO_NULL, /* write_resp */ ZERO_NULL, /* write_resp_hd */ diff --git a/lib/dict.c b/lib/dict.c index 7f13e6f7ea..30e13b4547 100644 --- a/lib/dict.c +++ b/lib/dict.c @@ -92,10 +92,10 @@ const struct Curl_handler Curl_handler_dict = { ZERO_NULL, /* connect_it */ ZERO_NULL, /* connecting */ ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_getsock */ - ZERO_NULL, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ + ZERO_NULL, /* proto_pollset */ + ZERO_NULL, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + ZERO_NULL, /* perform_pollset */ ZERO_NULL, /* disconnect */ ZERO_NULL, /* write_resp */ ZERO_NULL, /* write_resp_hd */ diff --git a/lib/file.c b/lib/file.c index d59d1478f6..0b44d2257d 100644 --- a/lib/file.c +++ b/lib/file.c @@ -120,10 +120,10 @@ const struct Curl_handler Curl_handler_file = { file_connect, /* connect_it */ ZERO_NULL, /* connecting */ ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_getsock */ - ZERO_NULL, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ + ZERO_NULL, /* proto_pollset */ + ZERO_NULL, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + ZERO_NULL, /* perform_pollset */ file_disconnect, /* disconnect */ ZERO_NULL, /* write_resp */ ZERO_NULL, /* write_resp_hd */ diff --git a/lib/ftp.c b/lib/ftp.c index 18613ae80e..3eec2f14ee 100644 --- a/lib/ftp.c +++ b/lib/ftp.c @@ -214,10 +214,10 @@ static CURLcode ftp_disconnect(struct Curl_easy *data, struct connectdata *conn, bool dead_connection); static CURLcode ftp_do_more(struct Curl_easy *data, int *completed); static CURLcode ftp_multi_statemach(struct Curl_easy *data, bool *done); -static int ftp_getsock(struct Curl_easy *data, struct connectdata *conn, - curl_socket_t *socks); -static int ftp_domore_getsock(struct Curl_easy *data, - struct connectdata *conn, curl_socket_t *socks); +static CURLcode ftp_pollset(struct Curl_easy *data, + struct easy_pollset *ps); +static CURLcode ftp_domore_pollset(struct Curl_easy *data, + struct easy_pollset *ps); static CURLcode ftp_doing(struct Curl_easy *data, bool *dophase_done); static CURLcode ftp_setup_connection(struct Curl_easy *data, @@ -257,10 +257,10 @@ const struct Curl_handler Curl_handler_ftp = { ftp_connect, /* connect_it */ ftp_multi_statemach, /* connecting */ ftp_doing, /* doing */ - ftp_getsock, /* proto_getsock */ - ftp_getsock, /* doing_getsock */ - ftp_domore_getsock, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ + ftp_pollset, /* proto_pollset */ + ftp_pollset, /* doing_pollset */ + ftp_domore_pollset, /* domore_pollset */ + ZERO_NULL, /* perform_pollset */ ftp_disconnect, /* disconnect */ ZERO_NULL, /* write_resp */ ZERO_NULL, /* write_resp_hd */ @@ -290,10 +290,10 @@ const struct Curl_handler Curl_handler_ftps = { ftp_connect, /* connect_it */ ftp_multi_statemach, /* connecting */ ftp_doing, /* doing */ - ftp_getsock, /* proto_getsock */ - ftp_getsock, /* doing_getsock */ - ftp_domore_getsock, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ + ftp_pollset, /* proto_pollset */ + ftp_pollset, /* doing_pollset */ + ftp_domore_pollset, /* domore_pollset */ + ZERO_NULL, /* perform_pollset */ ftp_disconnect, /* disconnect */ ZERO_NULL, /* write_resp */ ZERO_NULL, /* write_resp_hd */ @@ -781,42 +781,39 @@ static CURLcode ftp_state_pwd(struct Curl_easy *data, } /* For the FTP "protocol connect" and "doing" phases only */ -static int ftp_getsock(struct Curl_easy *data, - struct connectdata *conn, - curl_socket_t *socks) +static CURLcode ftp_pollset(struct Curl_easy *data, + struct easy_pollset *ps) { - struct ftp_conn *ftpc = Curl_conn_meta_get(conn, CURL_META_FTP_CONN); - return ftpc ? Curl_pp_getsock(data, &ftpc->pp, socks) : GETSOCK_BLANK; + struct ftp_conn *ftpc = Curl_conn_meta_get(data->conn, CURL_META_FTP_CONN); + return ftpc ? Curl_pp_pollset(data, &ftpc->pp, ps) : CURLE_OK; } /* For the FTP "DO_MORE" phase only */ -static int ftp_domore_getsock(struct Curl_easy *data, - struct connectdata *conn, curl_socket_t *socks) +static CURLcode ftp_domore_pollset(struct Curl_easy *data, + struct easy_pollset *ps) { - struct ftp_conn *ftpc = Curl_conn_meta_get(conn, CURL_META_FTP_CONN); - (void)data; + struct ftp_conn *ftpc = Curl_conn_meta_get(data->conn, CURL_META_FTP_CONN); if(!ftpc) - return GETSOCK_BLANK; + return CURLE_OK; /* When in DO_MORE state, we could be either waiting for us to connect to a * remote site, or we could wait for that site to connect to us. Or just * handle ordinary commands. */ - CURL_TRC_FTP(data, "[%s] ftp_domore_getsock()", FTP_CSTATE(ftpc)); + CURL_TRC_FTP(data, "[%s] ftp_domore_pollset()", FTP_CSTATE(ftpc)); if(FTP_STOP == ftpc->state) { /* if stopped and still in this state, then we are also waiting for a connect on the secondary connection */ - DEBUGASSERT(conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD || - (conn->cfilter[SECONDARYSOCKET] && - !Curl_conn_is_connected(conn, SECONDARYSOCKET))); - socks[0] = conn->sock[FIRSTSOCKET]; + DEBUGASSERT(data->conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD || + (data->conn->cfilter[SECONDARYSOCKET] && + !Curl_conn_is_connected(data->conn, SECONDARYSOCKET))); /* An unconnected SECONDARY will add its socket by itself * via its adjust_pollset() */ - return GETSOCK_READSOCK(0); + return Curl_pollset_add_in(data, ps, data->conn->sock[FIRSTSOCKET]); } - return Curl_pp_getsock(data, &ftpc->pp, socks); + return Curl_pp_pollset(data, &ftpc->pp, ps); } /* This is called after the FTP_QUOTE state is passed. diff --git a/lib/gopher.c b/lib/gopher.c index 68ccb59e40..82e02ed170 100644 --- a/lib/gopher.c +++ b/lib/gopher.c @@ -70,10 +70,10 @@ const struct Curl_handler Curl_handler_gopher = { ZERO_NULL, /* connect_it */ ZERO_NULL, /* connecting */ ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_getsock */ - ZERO_NULL, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ + ZERO_NULL, /* proto_pollset */ + ZERO_NULL, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + ZERO_NULL, /* perform_pollset */ ZERO_NULL, /* disconnect */ ZERO_NULL, /* write_resp */ ZERO_NULL, /* write_resp_hd */ @@ -96,10 +96,10 @@ const struct Curl_handler Curl_handler_gophers = { gopher_connect, /* connect_it */ gopher_connecting, /* connecting */ ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_getsock */ - ZERO_NULL, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ + ZERO_NULL, /* proto_pollset */ + ZERO_NULL, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + ZERO_NULL, /* perform_pollset */ ZERO_NULL, /* disconnect */ ZERO_NULL, /* write_resp */ ZERO_NULL, /* write_resp_hd */ diff --git a/lib/hostip.c b/lib/hostip.c index e01f111a64..06fa3bc47e 100644 --- a/lib/hostip.c +++ b/lib/hostip.c @@ -1527,21 +1527,21 @@ CURLcode Curl_resolv_check(struct Curl_easy *data, } #endif -int Curl_resolv_getsock(struct Curl_easy *data, - curl_socket_t *socks) +CURLcode Curl_resolv_pollset(struct Curl_easy *data, + struct easy_pollset *ps) { #ifdef CURLRES_ASYNCH #ifndef CURL_DISABLE_DOH if(data->conn->bits.doh) /* nothing to wait for during DoH resolve, those handles have their own sockets */ - return GETSOCK_BLANK; + return CURLE_OK; #endif - return Curl_async_getsock(data, socks); + return Curl_async_pollset(data, ps); #else (void)data; - (void)socks; - return GETSOCK_BLANK; + (void)ps; + return CURLE_OK; #endif } diff --git a/lib/hostip.h b/lib/hostip.h index 5548a6f125..3743b73961 100644 --- a/lib/hostip.h +++ b/lib/hostip.h @@ -53,6 +53,7 @@ struct addrinfo; struct hostent; struct Curl_easy; struct connectdata; +struct easy_pollset; enum alpnid { ALPN_none = 0, @@ -199,8 +200,8 @@ CURLcode Curl_resolv_check(struct Curl_easy *data, #else #define Curl_resolv_check(x,y) CURLE_NOT_BUILT_IN #endif -int Curl_resolv_getsock(struct Curl_easy *data, - curl_socket_t *socks); +CURLcode Curl_resolv_pollset(struct Curl_easy *data, + struct easy_pollset *ps); CURLcode Curl_resolver_error(struct Curl_easy *data); diff --git a/lib/http.c b/lib/http.c index aa48887723..08daab6dc9 100644 --- a/lib/http.c +++ b/lib/http.c @@ -137,10 +137,10 @@ const struct Curl_handler Curl_handler_http = { Curl_http_connect, /* connect_it */ ZERO_NULL, /* connecting */ ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_getsock */ - Curl_http_getsock_do, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ + ZERO_NULL, /* proto_pollset */ + Curl_http_do_pollset, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + ZERO_NULL, /* perform_pollset */ ZERO_NULL, /* disconnect */ Curl_http_write_resp, /* write_resp */ Curl_http_write_resp_hd, /* write_resp_hd */ @@ -167,10 +167,10 @@ const struct Curl_handler Curl_handler_https = { Curl_http_connect, /* connect_it */ NULL, /* connecting */ ZERO_NULL, /* doing */ - NULL, /* proto_getsock */ - Curl_http_getsock_do, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ + NULL, /* proto_pollset */ + Curl_http_do_pollset, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + ZERO_NULL, /* perform_pollset */ ZERO_NULL, /* disconnect */ Curl_http_write_resp, /* write_resp */ Curl_http_write_resp_hd, /* write_resp_hd */ @@ -1524,14 +1524,12 @@ CURLcode Curl_http_connect(struct Curl_easy *data, bool *done) /* this returns the socket to wait for in the DO and DOING state for the multi interface and then we are always _sending_ a request and thus we wait for the single socket to become writable only */ -int Curl_http_getsock_do(struct Curl_easy *data, - struct connectdata *conn, - curl_socket_t *socks) +CURLcode Curl_http_do_pollset(struct Curl_easy *data, + struct easy_pollset *ps) { /* write mode */ - (void)conn; - socks[0] = Curl_conn_get_socket(data, FIRSTSOCKET); - return GETSOCK_WRITESOCK(0); + curl_socket_t sock = Curl_conn_get_socket(data, FIRSTSOCKET); + return Curl_pollset_add_out(data, ps, sock); } /* diff --git a/lib/http.h b/lib/http.h index a7bc2c6537..50279aedfa 100644 --- a/lib/http.h +++ b/lib/http.h @@ -115,8 +115,8 @@ CURLcode Curl_http_setup_conn(struct Curl_easy *data, CURLcode Curl_http(struct Curl_easy *data, bool *done); CURLcode Curl_http_done(struct Curl_easy *data, CURLcode, bool premature); CURLcode Curl_http_connect(struct Curl_easy *data, bool *done); -int Curl_http_getsock_do(struct Curl_easy *data, struct connectdata *conn, - curl_socket_t *socks); +CURLcode Curl_http_do_pollset(struct Curl_easy *data, + struct easy_pollset *ps); CURLcode Curl_http_write_resp(struct Curl_easy *data, const char *buf, size_t blen, bool is_eos); diff --git a/lib/http2.c b/lib/http2.c index c6c7c21474..e550abc8f8 100644 --- a/lib/http2.c +++ b/lib/http2.c @@ -2465,17 +2465,18 @@ out: return result; } -static void cf_h2_adjust_pollset(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct easy_pollset *ps) +static CURLcode cf_h2_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps) { struct cf_h2_ctx *ctx = cf->ctx; struct cf_call_data save; curl_socket_t sock; bool want_recv, want_send; + CURLcode result = CURLE_OK; if(!ctx->h2) - return; + return CURLE_OK; sock = Curl_conn_cf_get_socket(cf, data); Curl_pollset_check(data, ps, sock, &want_recv, &want_send); @@ -2493,7 +2494,7 @@ static void cf_h2_adjust_pollset(struct Curl_cfilter *cf, (!c_exhaust && nghttp2_session_want_write(ctx->h2)) || !Curl_bufq_is_empty(&ctx->outbufq); - Curl_pollset_set(data, ps, sock, want_recv, want_send); + result = Curl_pollset_set(data, ps, sock, want_recv, want_send); CF_DATA_RESTORE(cf, save); } else if(ctx->sent_goaway && !cf->shutdown) { @@ -2502,9 +2503,10 @@ static void cf_h2_adjust_pollset(struct Curl_cfilter *cf, want_send = nghttp2_session_want_write(ctx->h2) || !Curl_bufq_is_empty(&ctx->outbufq); want_recv = nghttp2_session_want_read(ctx->h2); - Curl_pollset_set(data, ps, sock, want_recv, want_send); + result = Curl_pollset_set(data, ps, sock, want_recv, want_send); CF_DATA_RESTORE(cf, save); } + return result; } static CURLcode cf_h2_connect(struct Curl_cfilter *cf, diff --git a/lib/imap.c b/lib/imap.c index f25176bb9e..5949b667e9 100644 --- a/lib/imap.c +++ b/lib/imap.c @@ -157,8 +157,8 @@ static CURLcode imap_connect(struct Curl_easy *data, bool *done); static CURLcode imap_disconnect(struct Curl_easy *data, struct connectdata *conn, bool dead); static CURLcode imap_multi_statemach(struct Curl_easy *data, bool *done); -static int imap_getsock(struct Curl_easy *data, struct connectdata *conn, - curl_socket_t *socks); +static CURLcode imap_pollset(struct Curl_easy *data, + struct easy_pollset *ps); static CURLcode imap_doing(struct Curl_easy *data, bool *dophase_done); static CURLcode imap_setup_connection(struct Curl_easy *data, struct connectdata *conn); @@ -196,10 +196,10 @@ const struct Curl_handler Curl_handler_imap = { imap_connect, /* connect_it */ imap_multi_statemach, /* connecting */ imap_doing, /* doing */ - imap_getsock, /* proto_getsock */ - imap_getsock, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ + imap_pollset, /* proto_pollset */ + imap_pollset, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + ZERO_NULL, /* perform_pollset */ imap_disconnect, /* disconnect */ ZERO_NULL, /* write_resp */ ZERO_NULL, /* write_resp_hd */ @@ -227,10 +227,10 @@ const struct Curl_handler Curl_handler_imaps = { imap_connect, /* connect_it */ imap_multi_statemach, /* connecting */ imap_doing, /* doing */ - imap_getsock, /* proto_getsock */ - imap_getsock, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ + imap_pollset, /* proto_pollset */ + imap_pollset, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + ZERO_NULL, /* perform_pollset */ imap_disconnect, /* disconnect */ ZERO_NULL, /* write_resp */ ZERO_NULL, /* write_resp_hd */ @@ -1560,13 +1560,12 @@ static CURLcode imap_block_statemach(struct Curl_easy *data, } /* For the IMAP "protocol connect" and "doing" phases only */ -static int imap_getsock(struct Curl_easy *data, - struct connectdata *conn, - curl_socket_t *socks) +static CURLcode imap_pollset(struct Curl_easy *data, + struct easy_pollset *ps) { - struct imap_conn *imapc = Curl_conn_meta_get(conn, CURL_META_IMAP_CONN); - return imapc ? - Curl_pp_getsock(data, &imapc->pp, socks) : GETSOCK_BLANK; + struct imap_conn *imapc = + Curl_conn_meta_get(data->conn, CURL_META_IMAP_CONN); + return imapc ? Curl_pp_pollset(data, &imapc->pp, ps) : CURLE_OK; } /*********************************************************************** diff --git a/lib/ldap.c b/lib/ldap.c index 3880d35d60..c66a56d7bb 100644 --- a/lib/ldap.c +++ b/lib/ldap.c @@ -177,10 +177,10 @@ const struct Curl_handler Curl_handler_ldap = { ZERO_NULL, /* connect_it */ ZERO_NULL, /* connecting */ ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_getsock */ - ZERO_NULL, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ + ZERO_NULL, /* proto_pollset */ + ZERO_NULL, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + ZERO_NULL, /* perform_pollset */ ZERO_NULL, /* disconnect */ ZERO_NULL, /* write_resp */ ZERO_NULL, /* write_resp_hd */ @@ -207,10 +207,10 @@ const struct Curl_handler Curl_handler_ldaps = { ZERO_NULL, /* connect_it */ ZERO_NULL, /* connecting */ ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_getsock */ - ZERO_NULL, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ + ZERO_NULL, /* proto_pollset */ + ZERO_NULL, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + ZERO_NULL, /* perform_pollset */ ZERO_NULL, /* disconnect */ ZERO_NULL, /* write_resp */ ZERO_NULL, /* write_resp_hd */ diff --git a/lib/mqtt.c b/lib/mqtt.c index 90f857dca5..35afe01207 100644 --- a/lib/mqtt.c +++ b/lib/mqtt.c @@ -107,8 +107,8 @@ static CURLcode mqtt_do(struct Curl_easy *data, bool *done); static CURLcode mqtt_done(struct Curl_easy *data, CURLcode status, bool premature); static CURLcode mqtt_doing(struct Curl_easy *data, bool *done); -static int mqtt_getsock(struct Curl_easy *data, struct connectdata *conn, - curl_socket_t *sock); +static CURLcode mqtt_pollset(struct Curl_easy *data, + struct easy_pollset *ps); static CURLcode mqtt_setup_conn(struct Curl_easy *data, struct connectdata *conn); @@ -125,10 +125,10 @@ const struct Curl_handler Curl_handler_mqtt = { ZERO_NULL, /* connect_it */ ZERO_NULL, /* connecting */ mqtt_doing, /* doing */ - ZERO_NULL, /* proto_getsock */ - mqtt_getsock, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ + ZERO_NULL, /* proto_pollset */ + mqtt_pollset, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + ZERO_NULL, /* perform_pollset */ ZERO_NULL, /* disconnect */ ZERO_NULL, /* write_resp */ ZERO_NULL, /* write_resp_hd */ @@ -213,13 +213,10 @@ static CURLcode mqtt_send(struct Curl_easy *data, /* Generic function called by the multi interface to figure out what socket(s) to wait for and for what actions during the DOING and PROTOCONNECT states */ -static int mqtt_getsock(struct Curl_easy *data, - struct connectdata *conn, - curl_socket_t *sock) +static CURLcode mqtt_pollset(struct Curl_easy *data, + struct easy_pollset *ps) { - (void)data; - sock[0] = conn->sock[FIRSTSOCKET]; - return GETSOCK_READSOCK(FIRSTSOCKET); + return Curl_pollset_add_in(data, ps, data->conn->sock[FIRSTSOCKET]); } static int mqtt_encode_len(char *buf, size_t len) diff --git a/lib/multi.c b/lib/multi.c index 194cf75d40..b5a6970eee 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -907,118 +907,103 @@ void Curl_attach_connection(struct Curl_easy *data, conn->handler->attach(data, conn); } -static int connecting_getsock(struct Curl_easy *data, curl_socket_t *socks) +static CURLcode mstate_connecting_pollset(struct Curl_easy *data, + struct easy_pollset *ps) { - struct connectdata *conn = data->conn; - curl_socket_t sockfd; - - if(!conn) - return GETSOCK_BLANK; - sockfd = Curl_conn_get_socket(data, FIRSTSOCKET); - if(sockfd != CURL_SOCKET_BAD) { - /* Default is to wait to something from the server */ - socks[0] = sockfd; - return GETSOCK_READSOCK(0); + if(data->conn) { + curl_socket_t sockfd = Curl_conn_get_socket(data, FIRSTSOCKET); + if(sockfd != CURL_SOCKET_BAD) { + /* Default is to wait to something from the server */ + return Curl_pollset_change(data, ps, sockfd, CURL_POLL_IN, 0); + } } - return GETSOCK_BLANK; + return CURLE_OK; } -static int protocol_getsock(struct Curl_easy *data, curl_socket_t *socks) +static CURLcode mstate_protocol_pollset(struct Curl_easy *data, + struct easy_pollset *ps) { - struct connectdata *conn = data->conn; - curl_socket_t sockfd; - - if(!conn) - return GETSOCK_BLANK; - if(conn->handler->proto_getsock) - return conn->handler->proto_getsock(data, conn, socks); - sockfd = Curl_conn_get_socket(data, FIRSTSOCKET); - if(sockfd != CURL_SOCKET_BAD) { - /* Default is to wait to something from the server */ - socks[0] = sockfd; - return GETSOCK_READSOCK(0); + if(data->conn) { + curl_socket_t sockfd; + if(data->conn->handler->proto_pollset) + return data->conn->handler->proto_pollset(data, ps); + sockfd = Curl_conn_get_socket(data, FIRSTSOCKET); + if(sockfd != CURL_SOCKET_BAD) { + /* Default is to wait to something from the server */ + return Curl_pollset_change(data, ps, sockfd, CURL_POLL_IN, 0); + } } - return GETSOCK_BLANK; + return CURLE_OK; } -static int domore_getsock(struct Curl_easy *data, curl_socket_t *socks) +static CURLcode mstate_do_pollset(struct Curl_easy *data, + struct easy_pollset *ps) { struct connectdata *conn = data->conn; - if(!conn) - return GETSOCK_BLANK; - if(conn->handler->domore_getsock) - return conn->handler->domore_getsock(data, conn, socks); - else if(conn->sockfd != CURL_SOCKET_BAD) { - /* Default is that we want to send something to the server */ - socks[0] = conn->sockfd; - return GETSOCK_WRITESOCK(0); + if(data->conn) { + if(data->conn->handler->doing_pollset) + return data->conn->handler->doing_pollset(data, ps); + else if(conn->sockfd != CURL_SOCKET_BAD) { + /* Default is that we want to send something to the server */ + return Curl_pollset_add_out(data, ps, conn->sockfd); + } } - return GETSOCK_BLANK; + return CURLE_OK; } -static int doing_getsock(struct Curl_easy *data, curl_socket_t *socks) +static CURLcode mstate_domore_pollset(struct Curl_easy *data, + struct easy_pollset *ps) { - struct connectdata *conn = data->conn; - if(!conn) - return GETSOCK_BLANK; - if(conn->handler->doing_getsock) - return conn->handler->doing_getsock(data, conn, socks); - else if(conn->sockfd != CURL_SOCKET_BAD) { - /* Default is that we want to send something to the server */ - socks[0] = conn->sockfd; - return GETSOCK_WRITESOCK(0); + if(data->conn) { + if(data->conn->handler->domore_pollset) + return data->conn->handler->domore_pollset(data, ps); + else if(data->conn->sockfd != CURL_SOCKET_BAD) { + /* Default is that we want to send something to the server */ + return Curl_pollset_add_out(data, ps, data->conn->sockfd); + } } - return GETSOCK_BLANK; + return CURLE_OK; } -static int perform_getsock(struct Curl_easy *data, curl_socket_t *sock) +static CURLcode mstate_perform_pollset(struct Curl_easy *data, + struct easy_pollset *ps) { - struct connectdata *conn = data->conn; - if(!conn) - return GETSOCK_BLANK; - else if(conn->handler->perform_getsock) - return conn->handler->perform_getsock(data, conn, sock); + if(!data->conn) + return CURLE_OK; + else if(data->conn->handler->perform_pollset) + return data->conn->handler->perform_pollset(data, ps); else { /* Default is to obey the data->req.keepon flags for send/recv */ - int bitmap = GETSOCK_BLANK; - unsigned sockindex = 0; + CURLcode result = CURLE_OK; if(CURL_WANT_RECV(data)) { - DEBUGASSERT(conn->sockfd != CURL_SOCKET_BAD); - bitmap |= GETSOCK_READSOCK(sockindex); - sock[sockindex] = conn->sockfd; + DEBUGASSERT(data->conn->sockfd != CURL_SOCKET_BAD); + result = Curl_pollset_add_in(data, ps, data->conn->sockfd); } - if(Curl_req_want_send(data)) { - if((conn->sockfd != conn->writesockfd) || - bitmap == GETSOCK_BLANK) { - /* only if they are not the same socket and we have a readable - one, we increase index */ - if(bitmap != GETSOCK_BLANK) - sockindex++; /* increase index if we need two entries */ - - DEBUGASSERT(conn->writesockfd != CURL_SOCKET_BAD); - sock[sockindex] = conn->writesockfd; - } - bitmap |= GETSOCK_WRITESOCK(sockindex); + if(!result && Curl_req_want_send(data)) { + DEBUGASSERT(data->conn->writesockfd != CURL_SOCKET_BAD); + result = Curl_pollset_add_out(data, ps, data->conn->writesockfd); } - return bitmap; + return result; } } /* Initializes `poll_set` with the current socket poll actions needed * for transfer `data`. */ -void Curl_multi_getsock(struct Curl_easy *data, - struct easy_pollset *ps, - const char *caller) +CURLMcode Curl_multi_pollset(struct Curl_easy *data, + struct easy_pollset *ps, + const char *caller) { + CURLMcode mresult = CURLM_OK; + CURLcode result = CURLE_OK; bool expect_sockets = TRUE; /* If the transfer has no connection, this is fine. Happens when called via curl_multi_remove_handle() => Curl_multi_ev_assess() => - Curl_multi_getsock(). */ - Curl_pollset_reset(data, ps); + Curl_multi_pollset(). */ + Curl_pollset_reset(ps); if(!data->conn) - return; + return CURLM_OK; switch(data->mstate) { case MSTATE_INIT: @@ -1030,7 +1015,7 @@ void Curl_multi_getsock(struct Curl_easy *data, break; case MSTATE_RESOLVING: - Curl_pollset_add_socks(data, ps, Curl_resolv_getsock); + result = Curl_resolv_pollset(data, ps); /* connection filters are not involved in this phase. It's ok if we get no * sockets to wait for. Resolving can wake up from other sources. */ expect_sockets = FALSE; @@ -1038,31 +1023,36 @@ void Curl_multi_getsock(struct Curl_easy *data, case MSTATE_CONNECTING: case MSTATE_TUNNELING: - Curl_pollset_add_socks(data, ps, connecting_getsock); - Curl_conn_adjust_pollset(data, data->conn, ps); + result = mstate_connecting_pollset(data, ps); + if(!result) + result = Curl_conn_adjust_pollset(data, data->conn, ps); break; case MSTATE_PROTOCONNECT: case MSTATE_PROTOCONNECTING: - Curl_pollset_add_socks(data, ps, protocol_getsock); - Curl_conn_adjust_pollset(data, data->conn, ps); + result = mstate_protocol_pollset(data, ps); + if(!result) + result = Curl_conn_adjust_pollset(data, data->conn, ps); break; case MSTATE_DO: case MSTATE_DOING: - Curl_pollset_add_socks(data, ps, doing_getsock); - Curl_conn_adjust_pollset(data, data->conn, ps); + result = mstate_do_pollset(data, ps); + if(!result) + result = Curl_conn_adjust_pollset(data, data->conn, ps); break; case MSTATE_DOING_MORE: - Curl_pollset_add_socks(data, ps, domore_getsock); - Curl_conn_adjust_pollset(data, data->conn, ps); + result = mstate_domore_pollset(data, ps); + if(!result) + result = Curl_conn_adjust_pollset(data, data->conn, ps); break; case MSTATE_DID: /* same as PERFORMING in regard to polling */ case MSTATE_PERFORMING: - Curl_pollset_add_socks(data, ps, perform_getsock); - Curl_conn_adjust_pollset(data, data->conn, ps); + result = mstate_perform_pollset(data, ps); + if(!result) + result = Curl_conn_adjust_pollset(data, data->conn, ps); break; case MSTATE_RATELIMITING: @@ -1084,6 +1074,15 @@ void Curl_multi_getsock(struct Curl_easy *data, break; } + if(result) { + if(result == CURLE_OUT_OF_MEMORY) + mresult = CURLM_OUT_OF_MEMORY; + else { + failf(data, "error determining pollset: %d", result); + mresult = CURLM_INTERNAL_ERROR; + } + goto out; + } /* Unblocked and waiting to receive with buffered input. * Make transfer run again at next opportunity. */ @@ -1097,7 +1096,7 @@ void Curl_multi_getsock(struct Curl_easy *data, Curl_multi_mark_dirty(data); } - switch(ps->num) { + switch(ps->n) { case 0: CURL_TRC_M(data, "%s pollset[], timeouts=%zu, paused %d/%d (r/w)", caller, Curl_llist_count(&data->state.timeoutlist), @@ -1124,10 +1123,10 @@ void Curl_multi_getsock(struct Curl_easy *data, break; default: CURL_TRC_M(data, "%s pollset[fds=%u], timeouts=%zu", - caller, ps->num, Curl_llist_count(&data->state.timeoutlist)); + caller, ps->n, Curl_llist_count(&data->state.timeoutlist)); break; } - if(expect_sockets && !ps->num && data->multi && + if(expect_sockets && !ps->n && data->multi && !Curl_uint_bset_contains(&data->multi->dirty, data->mid) && !Curl_llist_count(&data->state.timeoutlist) && !Curl_cwriter_is_paused(data) && !Curl_creader_is_paused(data) && @@ -1140,6 +1139,8 @@ void Curl_multi_getsock(struct Curl_easy *data, infof(data, "WARNING: no socket in pollset or timer, transfer may stall!"); DEBUGASSERT(0); } +out: + return mresult; } CURLMcode curl_multi_fdset(CURLM *m, @@ -1151,6 +1152,7 @@ CURLMcode curl_multi_fdset(CURLM *m, and then we must make sure that is done. */ int this_max_fd = -1; struct Curl_multi *multi = m; + struct easy_pollset ps; unsigned int i, mid; (void)exc_fd_set; /* not used */ @@ -1160,18 +1162,18 @@ CURLMcode curl_multi_fdset(CURLM *m, if(multi->in_callback) return CURLM_RECURSIVE_API_CALL; + Curl_pollset_init(&ps); if(Curl_uint_bset_first(&multi->process, &mid)) { do { struct Curl_easy *data = Curl_multi_get_easy(multi, mid); - struct easy_pollset ps; if(!data) { DEBUGASSERT(0); continue; } - Curl_multi_getsock(data, &ps, "curl_multi_fdset"); - for(i = 0; i < ps.num; i++) { + Curl_multi_pollset(data, &ps, "curl_multi_fdset"); + for(i = 0; i < ps.n; i++) { if(!FDSET_SOCK(ps.sockets[i])) /* pretend it does not exist */ continue; @@ -1197,6 +1199,7 @@ CURLMcode curl_multi_fdset(CURLM *m, read_fd_set, write_fd_set, &this_max_fd); *max_fd = this_max_fd; + Curl_pollset_cleanup(&ps); return CURLM_OK; } @@ -1209,6 +1212,7 @@ CURLMcode curl_multi_waitfds(CURLM *m, struct Curl_waitfds cwfds; CURLMcode result = CURLM_OK; struct Curl_multi *multi = m; + struct easy_pollset ps; unsigned int need = 0, mid; if(!ufds && (size || !fd_count)) @@ -1220,18 +1224,18 @@ CURLMcode curl_multi_waitfds(CURLM *m, if(multi->in_callback) return CURLM_RECURSIVE_API_CALL; + Curl_pollset_init(&ps); Curl_waitfds_init(&cwfds, ufds, size); if(Curl_uint_bset_first(&multi->process, &mid)) { do { struct Curl_easy *data = Curl_multi_get_easy(multi, mid); - struct easy_pollset ps; if(!data) { DEBUGASSERT(0); Curl_uint_bset_remove(&multi->process, mid); Curl_uint_bset_remove(&multi->dirty, mid); continue; } - Curl_multi_getsock(data, &ps, "curl_multi_waitfds"); + Curl_multi_pollset(data, &ps, "curl_multi_waitfds"); need += Curl_waitfds_add_ps(&cwfds, &ps); } while(Curl_uint_bset_next(&multi->process, mid, &mid)); @@ -1245,6 +1249,7 @@ CURLMcode curl_multi_waitfds(CURLM *m, if(fd_count) *fd_count = need; + Curl_pollset_cleanup(&ps); return result; } @@ -1278,6 +1283,7 @@ static CURLMcode multi_wait(struct Curl_multi *multi, struct curltime expire_time; long timeout_internal; int retcode = 0; + struct easy_pollset ps; struct pollfd a_few_on_stack[NUM_POLLS_ON_STACK]; struct curl_pollfds cpfds; unsigned int curl_nfds = 0; /* how many pfds are for curl transfers */ @@ -1302,12 +1308,12 @@ static CURLMcode multi_wait(struct Curl_multi *multi, if(timeout_ms < 0) return CURLM_BAD_FUNCTION_ARGUMENT; + Curl_pollset_init(&ps); Curl_pollfds_init(&cpfds, a_few_on_stack, NUM_POLLS_ON_STACK); /* Add the curl handles to our pollfds first */ if(Curl_uint_bset_first(&multi->process, &mid)) { do { - struct easy_pollset ps; data = Curl_multi_get_easy(multi, mid); if(!data) { DEBUGASSERT(0); @@ -1315,7 +1321,7 @@ static CURLMcode multi_wait(struct Curl_multi *multi, Curl_uint_bset_remove(&multi->dirty, mid); continue; } - Curl_multi_getsock(data, &ps, "multi_wait"); + Curl_multi_pollset(data, &ps, "multi_wait"); if(Curl_pollfds_add_ps(&cpfds, &ps)) { result = CURLM_OUT_OF_MEMORY; goto out; @@ -1514,6 +1520,7 @@ static CURLMcode multi_wait(struct Curl_multi *multi, } out: + Curl_pollset_cleanup(&ps); Curl_pollfds_cleanup(&cpfds); return result; } diff --git a/lib/multi_ev.c b/lib/multi_ev.c index 4cde603805..61c639d9e4 100644 --- a/lib/multi_ev.c +++ b/lib/multi_ev.c @@ -317,7 +317,7 @@ static CURLMcode mev_pollset_diff(struct Curl_multi *multi, DEBUGASSERT(prev_ps); /* Handle changes to sockets the transfer is interested in. */ - for(i = 0; i < ps->num; i++) { + for(i = 0; i < ps->n; i++) { unsigned char last_action; bool first_time = FALSE; /* data/conn appears first time on socket */ @@ -362,7 +362,7 @@ static CURLMcode mev_pollset_diff(struct Curl_multi *multi, entry->conn ? 1 : 0); } else { - for(j = 0; j < prev_ps->num; j++) { + for(j = 0; j < prev_ps->n; j++) { if(s == prev_ps->sockets[j]) { last_action = prev_ps->actions[j]; break; @@ -377,11 +377,11 @@ static CURLMcode mev_pollset_diff(struct Curl_multi *multi, } /* Handle changes to sockets the transfer is NO LONGER interested in. */ - for(i = 0; i < prev_ps->num; i++) { + for(i = 0; i < prev_ps->n; i++) { bool stillused = FALSE; s = prev_ps->sockets[i]; - for(j = 0; j < ps->num; j++) { + for(j = 0; j < ps->n; j++) { if(s == ps->sockets[j]) { /* socket is still supervised */ stillused = TRUE; @@ -437,15 +437,19 @@ static CURLMcode mev_pollset_diff(struct Curl_multi *multi, } /* for loop over num */ /* Remember for next time */ - memcpy(prev_ps, ps, sizeof(*prev_ps)); + Curl_pollset_move(prev_ps, ps); return CURLM_OK; } static void mev_pollset_dtor(void *key, size_t klen, void *entry) { + struct easy_pollset *ps = entry; (void)key; (void)klen; - free(entry); + if(ps) { + Curl_pollset_cleanup(ps); + free(ps); + } } static struct easy_pollset* @@ -453,7 +457,7 @@ mev_add_new_conn_pollset(struct connectdata *conn) { struct easy_pollset *ps; - ps = calloc(1, sizeof(*ps)); + ps = Curl_pollset_create(); if(!ps) return NULL; if(Curl_conn_meta_set(conn, CURL_META_MEV_POLLSET, ps, mev_pollset_dtor)) @@ -466,7 +470,7 @@ mev_add_new_xfer_pollset(struct Curl_easy *data) { struct easy_pollset *ps; - ps = calloc(1, sizeof(*ps)); + ps = Curl_pollset_create(); if(!ps) return NULL; if(Curl_meta_set(data, CURL_META_MEV_POLLSET, ps, mev_pollset_dtor)) @@ -486,42 +490,47 @@ mev_get_last_pollset(struct Curl_easy *data, return NULL; } -static void mev_init_cur_pollset(struct easy_pollset *ps, - struct Curl_easy *data, - struct connectdata *conn) -{ - memset(ps, 0, sizeof(*ps)); - if(conn) - Curl_conn_adjust_pollset(data, conn, ps); - else if(data) - Curl_multi_getsock(data, ps, "ev assess"); -} - static CURLMcode mev_assess(struct Curl_multi *multi, struct Curl_easy *data, struct connectdata *conn) { - if(multi && multi->socket_cb) { - struct easy_pollset ps, *last_ps; + struct easy_pollset ps, *last_ps; + CURLMcode res = CURLM_OK; - mev_init_cur_pollset(&ps, data, conn); - last_ps = mev_get_last_pollset(data, conn); + if(!multi || !multi->socket_cb) + return CURLM_OK; - if(!last_ps && ps.num) { - if(conn) - last_ps = mev_add_new_conn_pollset(conn); - else - last_ps = mev_add_new_xfer_pollset(data); - if(!last_ps) - return CURLM_OUT_OF_MEMORY; + Curl_pollset_init(&ps); + if(conn) { + CURLcode r = Curl_conn_adjust_pollset(data, conn, &ps); + if(r) { + res = (r == CURLE_OUT_OF_MEMORY) ? + CURLM_OUT_OF_MEMORY : CURLM_INTERNAL_ERROR; + goto out; } + } + else if(data) + Curl_multi_pollset(data, &ps, "ev assess"); + last_ps = mev_get_last_pollset(data, conn); - if(last_ps) - return mev_pollset_diff(multi, data, conn, &ps, last_ps); + if(!last_ps && ps.n) { + if(conn) + last_ps = mev_add_new_conn_pollset(conn); else - DEBUGASSERT(!ps.num); + last_ps = mev_add_new_xfer_pollset(data); + if(!last_ps) { + res = CURLM_OUT_OF_MEMORY; + goto out; + } } - return CURLM_OK; + + if(last_ps) + res = mev_pollset_diff(multi, data, conn, &ps, last_ps); + else + DEBUGASSERT(!ps.n); +out: + Curl_pollset_cleanup(&ps); + return res; } CURLMcode Curl_multi_ev_assess_xfer(struct Curl_multi *multi, diff --git a/lib/multihandle.h b/lib/multihandle.h index dbad164bc5..cdedfb08ab 100644 --- a/lib/multihandle.h +++ b/lib/multihandle.h @@ -72,12 +72,6 @@ typedef enum { MSTATE_LAST /* 18 - not a true state, never use this */ } CURLMstate; -/* we support N sockets per easy handle. Set the corresponding bit to what - action we should wait for */ -#define MAX_SOCKSPEREASYHANDLE 5 -#define GETSOCK_READABLE (0x00ff) -#define GETSOCK_WRITABLE (0xff00) - #define CURLPIPE_ANY (CURLPIPE_MULTIPLEX) #ifndef CURL_DISABLE_SOCKETPAIR diff --git a/lib/multiif.h b/lib/multiif.h index 1ba0d0f838..5cf22bc046 100644 --- a/lib/multiif.h +++ b/lib/multiif.h @@ -53,20 +53,6 @@ struct Curl_multi *Curl_multi_handle(unsigned int xfer_table_size, size_t dnssize, size_t sesssize); -/* the write bits start at bit 16 for the *getsock() bitmap */ -#define GETSOCK_WRITEBITSTART 16 - -#define GETSOCK_BLANK 0 /* no bits set */ - -/* set the bit for the given sock number to make the bitmap for writable */ -#define GETSOCK_WRITESOCK(x) (1 << (GETSOCK_WRITEBITSTART + (x))) - -/* set the bit for the given sock number to make the bitmap for readable */ -#define GETSOCK_READSOCK(x) (1 << (x)) - -/* mask for checking if read and/or write is set for index x */ -#define GETSOCK_MASK_RW(x) (GETSOCK_READSOCK(x)|GETSOCK_WRITESOCK(x)) - /** * Let the multi handle know that the socket is about to be closed. * The multi will then remove anything it knows about the socket, so @@ -86,9 +72,9 @@ CURLMcode Curl_multi_add_perform(struct Curl_multi *multi, /* Return the value of the CURLMOPT_MAX_CONCURRENT_STREAMS option */ unsigned int Curl_multi_max_concurrent_streams(struct Curl_multi *multi); -void Curl_multi_getsock(struct Curl_easy *data, - struct easy_pollset *ps, - const char *caller); +CURLMcode Curl_multi_pollset(struct Curl_easy *data, + struct easy_pollset *ps, + const char *caller); /** * Borrow the transfer buffer from the multi, suitable diff --git a/lib/openldap.c b/lib/openldap.c index b0cb26c7ff..c532be3f32 100644 --- a/lib/openldap.c +++ b/lib/openldap.c @@ -126,10 +126,10 @@ const struct Curl_handler Curl_handler_ldap = { oldap_connect, /* connect_it */ oldap_connecting, /* connecting */ ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_getsock */ - ZERO_NULL, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ + ZERO_NULL, /* proto_pollset */ + ZERO_NULL, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + ZERO_NULL, /* perform_pollset */ oldap_disconnect, /* disconnect */ ZERO_NULL, /* write_resp */ ZERO_NULL, /* write_resp_hd */ @@ -156,10 +156,10 @@ const struct Curl_handler Curl_handler_ldaps = { oldap_connect, /* connect_it */ oldap_connecting, /* connecting */ ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_getsock */ - ZERO_NULL, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ + ZERO_NULL, /* proto_pollset */ + ZERO_NULL, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + ZERO_NULL, /* perform_pollset */ oldap_disconnect, /* disconnect */ ZERO_NULL, /* write_resp */ ZERO_NULL, /* write_resp_hd */ diff --git a/lib/pingpong.c b/lib/pingpong.c index a661a0285b..003ad58441 100644 --- a/lib/pingpong.c +++ b/lib/pingpong.c @@ -397,19 +397,13 @@ CURLcode Curl_pp_readresp(struct Curl_easy *data, return result; } -int Curl_pp_getsock(struct Curl_easy *data, - struct pingpong *pp, curl_socket_t *socks) +CURLcode Curl_pp_pollset(struct Curl_easy *data, + struct pingpong *pp, + struct easy_pollset *ps) { - struct connectdata *conn = data->conn; - socks[0] = conn->sock[FIRSTSOCKET]; - - if(pp->sendleft) { - /* write mode */ - return GETSOCK_WRITESOCK(0); - } - - /* read mode */ - return GETSOCK_READSOCK(0); + int flags = pp->sendleft ? CURL_POLL_OUT : CURL_POLL_IN; + return Curl_pollset_change(data, ps, data->conn->sock[FIRSTSOCKET], + flags, 0); } bool Curl_pp_needs_flush(struct Curl_easy *data, diff --git a/lib/pingpong.h b/lib/pingpong.h index c6d0a56f0d..5db96c4345 100644 --- a/lib/pingpong.h +++ b/lib/pingpong.h @@ -147,8 +147,9 @@ CURLcode Curl_pp_flushsend(struct Curl_easy *data, /* call this when a pingpong connection is disconnected */ CURLcode Curl_pp_disconnect(struct pingpong *pp); -int Curl_pp_getsock(struct Curl_easy *data, struct pingpong *pp, - curl_socket_t *socks); +CURLcode Curl_pp_pollset(struct Curl_easy *data, + struct pingpong *pp, + struct easy_pollset *ps); /*********************************************************************** diff --git a/lib/pop3.c b/lib/pop3.c index 514c4b8fa5..493b36f45f 100644 --- a/lib/pop3.c +++ b/lib/pop3.c @@ -153,8 +153,8 @@ static CURLcode pop3_connect(struct Curl_easy *data, bool *done); static CURLcode pop3_disconnect(struct Curl_easy *data, struct connectdata *conn, bool dead); static CURLcode pop3_multi_statemach(struct Curl_easy *data, bool *done); -static int pop3_getsock(struct Curl_easy *data, - struct connectdata *conn, curl_socket_t *socks); +static CURLcode pop3_pollset(struct Curl_easy *data, + struct easy_pollset *ps); static CURLcode pop3_doing(struct Curl_easy *data, bool *dophase_done); static CURLcode pop3_setup_connection(struct Curl_easy *data, struct connectdata *conn); @@ -186,10 +186,10 @@ const struct Curl_handler Curl_handler_pop3 = { pop3_connect, /* connect_it */ pop3_multi_statemach, /* connecting */ pop3_doing, /* doing */ - pop3_getsock, /* proto_getsock */ - pop3_getsock, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ + pop3_pollset, /* proto_pollset */ + pop3_pollset, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + ZERO_NULL, /* perform_pollset */ pop3_disconnect, /* disconnect */ pop3_write, /* write_resp */ ZERO_NULL, /* write_resp_hd */ @@ -217,10 +217,10 @@ const struct Curl_handler Curl_handler_pop3s = { pop3_connect, /* connect_it */ pop3_multi_statemach, /* connecting */ pop3_doing, /* doing */ - pop3_getsock, /* proto_getsock */ - pop3_getsock, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ + pop3_pollset, /* proto_pollset */ + pop3_pollset, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + ZERO_NULL, /* perform_pollset */ pop3_disconnect, /* disconnect */ pop3_write, /* write_resp */ ZERO_NULL, /* write_resp_hd */ @@ -1269,13 +1269,12 @@ static CURLcode pop3_block_statemach(struct Curl_easy *data, } /* For the POP3 "protocol connect" and "doing" phases only */ -static int pop3_getsock(struct Curl_easy *data, - struct connectdata *conn, curl_socket_t *socks) +static CURLcode pop3_pollset(struct Curl_easy *data, + struct easy_pollset *ps) { - struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN); - if(pop3c) - return Curl_pp_getsock(data, &pop3c->pp, socks); - return GETSOCK_BLANK; + struct pop3_conn *pop3c = + Curl_conn_meta_get(data->conn, CURL_META_POP3_CONN); + return pop3c ? Curl_pp_pollset(data, &pop3c->pp, ps) : CURLE_OK; } /*********************************************************************** diff --git a/lib/rtsp.c b/lib/rtsp.c index d50624158e..46ff660aed 100644 --- a/lib/rtsp.c +++ b/lib/rtsp.c @@ -83,8 +83,8 @@ struct RTSP { static CURLcode rtsp_do(struct Curl_easy *data, bool *done); static CURLcode rtsp_done(struct Curl_easy *data, CURLcode, bool premature); static CURLcode rtsp_connect(struct Curl_easy *data, bool *done); -static int rtsp_getsock_do(struct Curl_easy *data, - struct connectdata *conn, curl_socket_t *socks); +static CURLcode rtsp_do_pollset(struct Curl_easy *data, + struct easy_pollset *ps); /* * Parse and write out an RTSP response. @@ -110,13 +110,11 @@ static unsigned int rtsp_conncheck(struct Curl_easy *data, /* this returns the socket to wait for in the DO and DOING state for the multi interface and then we are always _sending_ a request and thus we wait for the single socket to become writable only */ -static int rtsp_getsock_do(struct Curl_easy *data, struct connectdata *conn, - curl_socket_t *socks) +static CURLcode rtsp_do_pollset(struct Curl_easy *data, + struct easy_pollset *ps) { /* write mode */ - (void)data; - socks[0] = conn->sock[FIRSTSOCKET]; - return GETSOCK_WRITESOCK(0); + return Curl_pollset_add_out(data, ps, data->conn->sock[FIRSTSOCKET]); } static @@ -137,10 +135,10 @@ const struct Curl_handler Curl_handler_rtsp = { rtsp_connect, /* connect_it */ ZERO_NULL, /* connecting */ ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_getsock */ - rtsp_getsock_do, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ + ZERO_NULL, /* proto_pollset */ + rtsp_do_pollset, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + ZERO_NULL, /* perform_pollset */ ZERO_NULL, /* disconnect */ rtsp_rtp_write_resp, /* write_resp */ ZERO_NULL, /* write_resp_hd */ diff --git a/lib/select.c b/lib/select.c index 2353c474df..af78cb836b 100644 --- a/lib/select.c +++ b/lib/select.c @@ -41,6 +41,7 @@ #include "urldata.h" #include "connect.h" #include "select.h" +#include "curl_trc.h" #include "curlx/timediff.h" #include "curlx/wait.h" #include "curlx/warnless.h" @@ -423,7 +424,7 @@ CURLcode Curl_pollfds_add_ps(struct curl_pollfds *cpfds, DEBUGASSERT(cpfds); DEBUGASSERT(ps); - for(i = 0; i < ps->num; i++) { + for(i = 0; i < ps->n; i++) { short events = 0; if(ps->actions[i] & CURL_POLL_IN) events |= POLLIN; @@ -481,7 +482,7 @@ unsigned int Curl_waitfds_add_ps(struct Curl_waitfds *cwfds, DEBUGASSERT(cwfds); DEBUGASSERT(ps); - for(i = 0; i < ps->num; i++) { + for(i = 0; i < ps->n; i++) { short events = 0; if(ps->actions[i] & CURL_POLL_IN) events |= CURL_WAIT_POLLIN; @@ -492,3 +493,233 @@ unsigned int Curl_waitfds_add_ps(struct Curl_waitfds *cwfds, } return need; } + +void Curl_pollset_reset(struct easy_pollset *ps) +{ + unsigned int i; + ps->n = 0; +#ifdef DEBUGBUILD + DEBUGASSERT(ps->init == CURL_EASY_POLLSET_MAGIC); +#endif + DEBUGASSERT(ps->count); + for(i = 0; i < ps->count; i++) + ps->sockets[i] = CURL_SOCKET_BAD; + memset(ps->actions, 0, ps->count * sizeof(ps->actions[0])); +} + +void Curl_pollset_init(struct easy_pollset *ps) +{ +#ifdef DEBUGBUILD + ps->init = CURL_EASY_POLLSET_MAGIC; +#endif + ps->sockets = ps->def_sockets; + ps->actions = ps->def_actions; + ps->count = CURL_ARRAYSIZE(ps->def_sockets); + ps->n = 0; + Curl_pollset_reset(ps); +} + +struct easy_pollset *Curl_pollset_create(void) +{ + struct easy_pollset *ps = calloc(1, sizeof(*ps)); + if(ps) + Curl_pollset_init(ps); + return ps; +} + +void Curl_pollset_cleanup(struct easy_pollset *ps) +{ +#ifdef DEBUGBUILD + DEBUGASSERT(ps->init == CURL_EASY_POLLSET_MAGIC); +#endif + if(ps->sockets != ps->def_sockets) { + free(ps->sockets); + ps->sockets = ps->def_sockets; + } + if(ps->actions != ps->def_actions) { + free(ps->actions); + ps->actions = ps->def_actions; + } + ps->count = CURL_ARRAYSIZE(ps->def_sockets); + Curl_pollset_reset(ps); +} + +void Curl_pollset_move(struct easy_pollset *to, struct easy_pollset *from) +{ + Curl_pollset_cleanup(to); /* deallocate anything in to */ + if(from->sockets != from->def_sockets) { + DEBUGASSERT(from->actions != from->def_actions); + to->sockets = from->sockets; + to->actions = from->actions; + to->count = from->count; + to->n = from->n; + Curl_pollset_init(from); + } + else { + DEBUGASSERT(to->sockets == to->def_sockets); + DEBUGASSERT(to->actions == to->def_actions); + memcpy(to->sockets, from->sockets, to->count * sizeof(to->sockets[0])); + memcpy(to->actions, from->actions, to->count * sizeof(to->actions[0])); + to->n = from->n; + Curl_pollset_init(from); + } +} + +/** + * + */ +CURLcode Curl_pollset_change(struct Curl_easy *data, + struct easy_pollset *ps, curl_socket_t sock, + int add_flags, int remove_flags) +{ + unsigned int i; + +#ifdef DEBUGBUILD + DEBUGASSERT(ps->init == CURL_EASY_POLLSET_MAGIC); +#endif + + (void)data; + DEBUGASSERT(VALID_SOCK(sock)); + if(!VALID_SOCK(sock)) + return CURLE_BAD_FUNCTION_ARGUMENT; + + DEBUGASSERT(add_flags <= (CURL_POLL_IN|CURL_POLL_OUT)); + DEBUGASSERT(remove_flags <= (CURL_POLL_IN|CURL_POLL_OUT)); + DEBUGASSERT((add_flags&remove_flags) == 0); /* no overlap */ + for(i = 0; i < ps->n; ++i) { + if(ps->sockets[i] == sock) { + ps->actions[i] &= (unsigned char)(~remove_flags); + ps->actions[i] |= (unsigned char)add_flags; + /* all gone? remove socket */ + if(!ps->actions[i]) { + if((i + 1) < ps->n) { + memmove(&ps->sockets[i], &ps->sockets[i + 1], + (ps->n - (i + 1)) * sizeof(ps->sockets[0])); + memmove(&ps->actions[i], &ps->actions[i + 1], + (ps->n - (i + 1)) * sizeof(ps->actions[0])); + } + --ps->n; + } + return CURLE_OK; + } + } + /* not present */ + if(add_flags) { + if(i >= ps->count) { /* need to grow */ + unsigned int new_count = CURLMAX(ps->count * 2, 8); + curl_socket_t *nsockets; + unsigned char *nactions; + + CURL_TRC_M(data, "growing pollset capacity from %u to %u", + ps->count, new_count); + if(new_count <= ps->count) + return CURLE_OUT_OF_MEMORY; + nsockets = calloc(new_count, sizeof(nsockets[0])); + if(!nsockets) + return CURLE_OUT_OF_MEMORY; + nactions = calloc(new_count, sizeof(nactions[0])); + if(!nactions) { + free(nsockets); + return CURLE_OUT_OF_MEMORY; + } + memcpy(nsockets, ps->sockets, ps->count * sizeof(ps->sockets[0])); + memcpy(nactions, ps->actions, ps->count * sizeof(ps->actions[0])); + if(ps->sockets != ps->def_sockets) + free(ps->sockets); + ps->sockets = nsockets; + if(ps->actions != ps->def_actions) + free(ps->actions); + ps->actions = nactions; + ps->count = new_count; + } + DEBUGASSERT(i < ps->count); + if(i < ps->count) { + ps->sockets[i] = sock; + ps->actions[i] = (unsigned char)add_flags; + ps->n = i + 1; + } + } + return CURLE_OK; +} + +CURLcode Curl_pollset_set(struct Curl_easy *data, + struct easy_pollset *ps, curl_socket_t sock, + bool do_in, bool do_out) +{ + return Curl_pollset_change(data, ps, sock, + (do_in ? CURL_POLL_IN : 0)| + (do_out ? CURL_POLL_OUT : 0), + (!do_in ? CURL_POLL_IN : 0)| + (!do_out ? CURL_POLL_OUT : 0)); +} + +int Curl_pollset_poll(struct Curl_easy *data, + struct easy_pollset *ps, + timediff_t timeout_ms) +{ + struct pollfd *pfds; + unsigned int i, npfds; + int result; + + (void)data; + DEBUGASSERT(data); + DEBUGASSERT(data->conn); + + if(!ps->n) + return curlx_wait_ms(timeout_ms); + + pfds = calloc(ps->n, sizeof(*pfds)); + if(!pfds) + return -1; + + npfds = 0; + for(i = 0; i < ps->n; ++i) { + short events = 0; + if(ps->actions[i] & CURL_POLL_IN) { + events |= POLLIN; + } + if(ps->actions[i] & CURL_POLL_OUT) { + events |= POLLOUT; + } + if(events) { + pfds[npfds].fd = ps->sockets[i]; + pfds[npfds].events = events; + ++npfds; + } + } + + result = Curl_poll(pfds, npfds, timeout_ms); + free(pfds); + return result; +} + +void Curl_pollset_check(struct Curl_easy *data, + struct easy_pollset *ps, curl_socket_t sock, + bool *pwant_read, bool *pwant_write) +{ + unsigned int i; + + (void)data; + DEBUGASSERT(VALID_SOCK(sock)); + for(i = 0; i < ps->n; ++i) { + if(ps->sockets[i] == sock) { + *pwant_read = !!(ps->actions[i] & CURL_POLL_IN); + *pwant_write = !!(ps->actions[i] & CURL_POLL_OUT); + return; + } + } + *pwant_read = *pwant_write = FALSE; +} + +bool Curl_pollset_want_read(struct Curl_easy *data, + struct easy_pollset *ps, + curl_socket_t sock) +{ + unsigned int i; + (void)data; + for(i = 0; i < ps->n; ++i) { + if((ps->sockets[i] == sock) && (ps->actions[i] & CURL_POLL_IN)) + return TRUE; + } + return FALSE; +} diff --git a/lib/select.h b/lib/select.h index e9cec600f3..47cdd31267 100644 --- a/lib/select.h +++ b/lib/select.h @@ -110,6 +110,90 @@ int Curl_poll(struct pollfd ufds[], unsigned int nfds, timediff_t timeout_ms); } while(0) #endif + +/* Keep the sockets to poll for an easy handle. + * `actions` are bitmaps of CURL_POLL_IN and CURL_POLL_OUT. + * Starts with small capacity, grows on demand. + */ +#define EZ_POLLSET_DEF_COUNT 2 + +struct easy_pollset { + curl_socket_t *sockets; + unsigned char *actions; + unsigned int n; + unsigned int count; +#ifdef DEBUGBUILD + int init; +#endif + curl_socket_t def_sockets[EZ_POLLSET_DEF_COUNT]; + unsigned char def_actions[EZ_POLLSET_DEF_COUNT]; +}; + +#ifdef DEBUGBUILD +#define CURL_EASY_POLLSET_MAGIC 0x7a657370 +#endif + + +/* allocate and initialise */ +struct easy_pollset *Curl_pollset_create(void); + +/* Initialize before first use */ +void Curl_pollset_init(struct easy_pollset *ps); +/* Free any allocated resources */ +void Curl_pollset_cleanup(struct easy_pollset *ps); +/* Reset to an empty pollset */ +void Curl_pollset_reset(struct easy_pollset *ps); +/* Move pollset from to pollset to, replacing all in to, + * leaving from empty. */ +void Curl_pollset_move(struct easy_pollset *to, struct easy_pollset *from); + +/* Change the poll flags (CURL_POLL_IN/CURL_POLL_OUT) to the poll set for + * socket `sock`. If the socket is not already part of the poll set, it + * will be added. + * If the socket is present and all poll flags are cleared, it will be removed. + */ +CURLcode Curl_pollset_change(struct Curl_easy *data, + struct easy_pollset *ps, curl_socket_t sock, + int add_flags, int remove_flags); + +CURLcode Curl_pollset_set(struct Curl_easy *data, + struct easy_pollset *ps, curl_socket_t sock, + bool do_in, bool do_out); + +#define Curl_pollset_add_in(data, ps, sock) \ + Curl_pollset_change((data), (ps), (sock), CURL_POLL_IN, 0) +#define Curl_pollset_add_out(data, ps, sock) \ + Curl_pollset_change((data), (ps), (sock), CURL_POLL_OUT, 0) +#define Curl_pollset_add_inout(data, ps, sock) \ + Curl_pollset_change((data), (ps), (sock), \ + CURL_POLL_IN|CURL_POLL_OUT, 0) +#define Curl_pollset_set_in_only(data, ps, sock) \ + Curl_pollset_change((data), (ps), (sock), \ + CURL_POLL_IN, CURL_POLL_OUT) +#define Curl_pollset_set_out_only(data, ps, sock) \ + Curl_pollset_change((data), (ps), (sock), \ + CURL_POLL_OUT, CURL_POLL_IN) + +/* return < = on error, 0 on timeout or how many sockets are ready */ +int Curl_pollset_poll(struct Curl_easy *data, + struct easy_pollset *ps, + timediff_t timeout_ms); + +/** + * Check if the pollset, as is, wants to read and/or write regarding + * the given socket. + */ +void Curl_pollset_check(struct Curl_easy *data, + struct easy_pollset *ps, curl_socket_t sock, + bool *pwant_read, bool *pwant_write); + +/** + * Return TRUE if the pollset contains socket with CURL_POLL_IN. + */ +bool Curl_pollset_want_read(struct Curl_easy *data, + struct easy_pollset *ps, + curl_socket_t sock); + struct curl_pollfds { struct pollfd *pfds; unsigned int n; diff --git a/lib/smb.c b/lib/smb.c index d6ef8f6f1d..81cf6e7cc1 100644 --- a/lib/smb.c +++ b/lib/smb.c @@ -36,6 +36,7 @@ #include "connect.h" #include "progress.h" #include "transfer.h" +#include "select.h" #include "vtls/vtls.h" #include "curl_ntlm_core.h" #include "escape.h" @@ -298,8 +299,8 @@ static CURLcode smb_connect(struct Curl_easy *data, bool *done); static CURLcode smb_connection_state(struct Curl_easy *data, bool *done); static CURLcode smb_do(struct Curl_easy *data, bool *done); static CURLcode smb_request_state(struct Curl_easy *data, bool *done); -static int smb_getsock(struct Curl_easy *data, struct connectdata *conn, - curl_socket_t *socks); +static CURLcode smb_pollset(struct Curl_easy *data, + struct easy_pollset *ps); static CURLcode smb_parse_url_path(struct Curl_easy *data, struct smb_conn *smbc, struct smb_request *req); @@ -316,10 +317,10 @@ const struct Curl_handler Curl_handler_smb = { smb_connect, /* connect_it */ smb_connection_state, /* connecting */ smb_request_state, /* doing */ - smb_getsock, /* proto_getsock */ - smb_getsock, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ + smb_pollset, /* proto_pollset */ + smb_pollset, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + ZERO_NULL, /* perform_pollset */ ZERO_NULL, /* disconnect */ ZERO_NULL, /* write_resp */ ZERO_NULL, /* write_resp_hd */ @@ -345,10 +346,10 @@ const struct Curl_handler Curl_handler_smbs = { smb_connect, /* connect_it */ smb_connection_state, /* connecting */ smb_request_state, /* doing */ - smb_getsock, /* proto_getsock */ - smb_getsock, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ + smb_pollset, /* proto_pollset */ + smb_pollset, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + ZERO_NULL, /* perform_pollset */ ZERO_NULL, /* disconnect */ ZERO_NULL, /* write_resp */ ZERO_NULL, /* write_resp_hd */ @@ -1204,12 +1205,10 @@ static CURLcode smb_request_state(struct Curl_easy *data, bool *done) return CURLE_OK; } -static int smb_getsock(struct Curl_easy *data, - struct connectdata *conn, curl_socket_t *socks) +static CURLcode smb_pollset(struct Curl_easy *data, + struct easy_pollset *ps) { - (void)data; - socks[0] = conn->sock[FIRSTSOCKET]; - return GETSOCK_READSOCK(0) | GETSOCK_WRITESOCK(0); + return Curl_pollset_add_inout(data, ps, data->conn->sock[FIRSTSOCKET]); } static CURLcode smb_do(struct Curl_easy *data, bool *done) diff --git a/lib/smtp.c b/lib/smtp.c index 13b89b6ecc..4bb3bde4af 100644 --- a/lib/smtp.c +++ b/lib/smtp.c @@ -157,8 +157,8 @@ static CURLcode smtp_connect(struct Curl_easy *data, bool *done); static CURLcode smtp_disconnect(struct Curl_easy *data, struct connectdata *conn, bool dead); static CURLcode smtp_multi_statemach(struct Curl_easy *data, bool *done); -static int smtp_getsock(struct Curl_easy *data, - struct connectdata *conn, curl_socket_t *socks); +static CURLcode smtp_pollset(struct Curl_easy *data, + struct easy_pollset *ps); static CURLcode smtp_doing(struct Curl_easy *data, bool *dophase_done); static CURLcode smtp_setup_connection(struct Curl_easy *data, struct connectdata *conn); @@ -192,10 +192,10 @@ const struct Curl_handler Curl_handler_smtp = { smtp_connect, /* connect_it */ smtp_multi_statemach, /* connecting */ smtp_doing, /* doing */ - smtp_getsock, /* proto_getsock */ - smtp_getsock, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ + smtp_pollset, /* proto_pollset */ + smtp_pollset, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + ZERO_NULL, /* perform_pollset */ smtp_disconnect, /* disconnect */ ZERO_NULL, /* write_resp */ ZERO_NULL, /* write_resp_hd */ @@ -223,10 +223,10 @@ const struct Curl_handler Curl_handler_smtps = { smtp_connect, /* connect_it */ smtp_multi_statemach, /* connecting */ smtp_doing, /* doing */ - smtp_getsock, /* proto_getsock */ - smtp_getsock, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ + smtp_pollset, /* proto_pollset */ + smtp_pollset, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + ZERO_NULL, /* perform_pollset */ smtp_disconnect, /* disconnect */ ZERO_NULL, /* write_resp */ ZERO_NULL, /* write_resp_hd */ @@ -1413,12 +1413,12 @@ static CURLcode smtp_block_statemach(struct Curl_easy *data, } /* For the SMTP "protocol connect" and "doing" phases only */ -static int smtp_getsock(struct Curl_easy *data, - struct connectdata *conn, curl_socket_t *socks) +static CURLcode smtp_pollset(struct Curl_easy *data, + struct easy_pollset *ps) { - struct smtp_conn *smtpc = Curl_conn_meta_get(conn, CURL_META_SMTP_CONN); - return smtpc ? - Curl_pp_getsock(data, &smtpc->pp, socks) : GETSOCK_BLANK; + struct smtp_conn *smtpc = + Curl_conn_meta_get(data->conn, CURL_META_SMTP_CONN); + return smtpc ? Curl_pp_pollset(data, &smtpc->pp, ps) : CURLE_OK; } /*********************************************************************** diff --git a/lib/socks.c b/lib/socks.c index 64795d69ee..4f9c98dbaa 100644 --- a/lib/socks.c +++ b/lib/socks.c @@ -1161,11 +1161,12 @@ static CURLcode socks_proxy_cf_connect(struct Curl_cfilter *cf, return result; } -static void socks_cf_adjust_pollset(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct easy_pollset *ps) +static CURLcode socks_cf_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps) { struct socks_state *sx = cf->ctx; + CURLcode result = CURLE_OK; if(!cf->connected && sx) { /* If we are not connected, the filter below is and has nothing @@ -1177,13 +1178,14 @@ static void socks_cf_adjust_pollset(struct Curl_cfilter *cf, case CONNECT_AUTH_READ: case CONNECT_REQ_READ: case CONNECT_REQ_READ_MORE: - Curl_pollset_set_in_only(data, ps, sock); + result = Curl_pollset_set_in_only(data, ps, sock); break; default: - Curl_pollset_set_out_only(data, ps, sock); + result = Curl_pollset_set_out_only(data, ps, sock); break; } } + return result; } static void socks_proxy_cf_close(struct Curl_cfilter *cf, diff --git a/lib/telnet.c b/lib/telnet.c index bd599dece2..731789e463 100644 --- a/lib/telnet.c +++ b/lib/telnet.c @@ -188,10 +188,10 @@ const struct Curl_handler Curl_handler_telnet = { ZERO_NULL, /* connect_it */ ZERO_NULL, /* connecting */ ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_getsock */ - ZERO_NULL, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ + ZERO_NULL, /* proto_pollset */ + ZERO_NULL, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + ZERO_NULL, /* perform_pollset */ ZERO_NULL, /* disconnect */ ZERO_NULL, /* write_resp */ ZERO_NULL, /* write_resp_hd */ diff --git a/lib/tftp.c b/lib/tftp.c index 5ea986dbb4..6a9f964a07 100644 --- a/lib/tftp.c +++ b/lib/tftp.c @@ -160,8 +160,8 @@ static CURLcode tftp_setup_connection(struct Curl_easy *data, struct connectdata *conn); static CURLcode tftp_multi_statemach(struct Curl_easy *data, bool *done); static CURLcode tftp_doing(struct Curl_easy *data, bool *dophase_done); -static int tftp_getsock(struct Curl_easy *data, struct connectdata *conn, - curl_socket_t *socks); +static CURLcode tftp_pollset(struct Curl_easy *data, + struct easy_pollset *ps); static CURLcode tftp_translate_code(tftp_error_t error); @@ -178,10 +178,10 @@ const struct Curl_handler Curl_handler_tftp = { tftp_connect, /* connect_it */ tftp_multi_statemach, /* connecting */ tftp_doing, /* doing */ - tftp_getsock, /* proto_getsock */ - tftp_getsock, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ + tftp_pollset, /* proto_pollset */ + tftp_pollset, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + ZERO_NULL, /* perform_pollset */ ZERO_NULL, /* disconnect */ ZERO_NULL, /* write_resp */ ZERO_NULL, /* write_resp_hd */ @@ -1074,19 +1074,10 @@ static CURLcode tftp_done(struct Curl_easy *data, CURLcode status, return result; } -/********************************************************** - * - * tftp_getsock - * - * The getsock callback - * - **********************************************************/ -static int tftp_getsock(struct Curl_easy *data, - struct connectdata *conn, curl_socket_t *socks) +static CURLcode tftp_pollset(struct Curl_easy *data, + struct easy_pollset *ps) { - (void)data; - socks[0] = conn->sock[FIRSTSOCKET]; - return GETSOCK_READSOCK(0); + return Curl_pollset_add_in(data, ps, data->conn->sock[FIRSTSOCKET]); } /********************************************************** diff --git a/lib/transfer.h b/lib/transfer.h index 92278226be..157c6b991e 100644 --- a/lib/transfer.h +++ b/lib/transfer.h @@ -34,8 +34,6 @@ void Curl_init_CONNECT(struct Curl_easy *data); CURLcode Curl_pretransfer(struct Curl_easy *data); CURLcode Curl_sendrecv(struct Curl_easy *data, struct curltime *nowp); -int Curl_single_getsock(struct Curl_easy *data, - struct connectdata *conn, curl_socket_t *socks); CURLcode Curl_retry_request(struct Curl_easy *data, char **url); bool Curl_meets_timecondition(struct Curl_easy *data, time_t timeofdoc); diff --git a/lib/urldata.h b/lib/urldata.h index 83d7b50e44..ea31d9f5c8 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -471,15 +471,6 @@ struct hostname { #define FIRSTSOCKET 0 #define SECONDARYSOCKET 1 -/* Polling requested by an easy handle. - * `action` is CURL_POLL_IN, CURL_POLL_OUT or CURL_POLL_INOUT. - */ -struct easy_pollset { - curl_socket_t sockets[MAX_SOCKSPEREASYHANDLE]; - unsigned int num; - unsigned char actions[MAX_SOCKSPEREASYHANDLE]; -}; - /* * Specific protocol handler. */ @@ -516,24 +507,24 @@ struct Curl_handler { /* Called from the multi interface during the PROTOCONNECT phase, and it should then return a proper fd set */ - int (*proto_getsock)(struct Curl_easy *data, - struct connectdata *conn, curl_socket_t *socks); + CURLcode (*proto_pollset)(struct Curl_easy *data, + struct easy_pollset *ps); /* Called from the multi interface during the DOING phase, and it should then return a proper fd set */ - int (*doing_getsock)(struct Curl_easy *data, - struct connectdata *conn, curl_socket_t *socks); + CURLcode (*doing_pollset)(struct Curl_easy *data, + struct easy_pollset *ps); /* Called from the multi interface during the DO_MORE phase, and it should then return a proper fd set */ - int (*domore_getsock)(struct Curl_easy *data, - struct connectdata *conn, curl_socket_t *socks); + CURLcode (*domore_pollset)(struct Curl_easy *data, + struct easy_pollset *ps); /* Called from the multi interface during the DO_DONE, PERFORM and WAITPERFORM phases, and it should then return a proper fd set. Not setting this will make libcurl use the generic default one. */ - int (*perform_getsock)(struct Curl_easy *data, - struct connectdata *conn, curl_socket_t *socks); + CURLcode (*perform_pollset)(struct Curl_easy *data, + struct easy_pollset *ps); /* This function *MAY* be set to a protocol-dependent function that is run * by the curl_disconnect(), as a step in the disconnection. If the handler diff --git a/lib/vquic/curl_ngtcp2.c b/lib/vquic/curl_ngtcp2.c index 8d6f94bc83..1cb99aaea8 100644 --- a/lib/vquic/curl_ngtcp2.c +++ b/lib/vquic/curl_ngtcp2.c @@ -898,15 +898,16 @@ static CURLcode check_and_set_expiry(struct Curl_cfilter *cf, return CURLE_OK; } -static void cf_ngtcp2_adjust_pollset(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct easy_pollset *ps) +static CURLcode cf_ngtcp2_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps) { struct cf_ngtcp2_ctx *ctx = cf->ctx; bool want_recv, want_send; + CURLcode result = CURLE_OK; if(!ctx->qconn) - return; + return CURLE_OK; Curl_pollset_check(data, ps, ctx->q.sockfd, &want_recv, &want_send); if(!want_send && !Curl_bufq_is_empty(&ctx->q.sendbuf)) @@ -926,9 +927,10 @@ static void cf_ngtcp2_adjust_pollset(struct Curl_cfilter *cf, want_send = (!s_exhaust && want_send) || !Curl_bufq_is_empty(&ctx->q.sendbuf); - Curl_pollset_set(data, ps, ctx->q.sockfd, want_recv, want_send); + result = Curl_pollset_set(data, ps, ctx->q.sockfd, want_recv, want_send); CF_DATA_RESTORE(cf, save); } + return result; } static int cb_h3_stream_close(nghttp3_conn *conn, int64_t sid, diff --git a/lib/vquic/curl_osslq.c b/lib/vquic/curl_osslq.c index 05362e2396..c18801d811 100644 --- a/lib/vquic/curl_osslq.c +++ b/lib/vquic/curl_osslq.c @@ -2266,11 +2266,12 @@ out: return alive; } -static void cf_osslq_adjust_pollset(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct easy_pollset *ps) +static CURLcode cf_osslq_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps) { struct cf_osslq_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; if(!ctx->tls.ossl.ssl) { /* NOP */ @@ -2278,9 +2279,9 @@ static void cf_osslq_adjust_pollset(struct Curl_cfilter *cf, else if(!cf->connected) { /* during handshake, transfer has not started yet. we always * add our socket for polling if SSL wants to send/recv */ - Curl_pollset_set(data, ps, ctx->q.sockfd, - SSL_net_read_desired(ctx->tls.ossl.ssl), - SSL_net_write_desired(ctx->tls.ossl.ssl)); + result = Curl_pollset_set(data, ps, ctx->q.sockfd, + SSL_net_read_desired(ctx->tls.ossl.ssl), + SSL_net_write_desired(ctx->tls.ossl.ssl)); } else { /* once connected, we only modify the socket if it is present. @@ -2288,15 +2289,16 @@ static void cf_osslq_adjust_pollset(struct Curl_cfilter *cf, bool want_recv, want_send; Curl_pollset_check(data, ps, ctx->q.sockfd, &want_recv, &want_send); if(want_recv || want_send) { - Curl_pollset_set(data, ps, ctx->q.sockfd, - SSL_net_read_desired(ctx->tls.ossl.ssl), - SSL_net_write_desired(ctx->tls.ossl.ssl)); + result = Curl_pollset_set(data, ps, ctx->q.sockfd, + SSL_net_read_desired(ctx->tls.ossl.ssl), + SSL_net_write_desired(ctx->tls.ossl.ssl)); } else if(ctx->need_recv || ctx->need_send) { - Curl_pollset_set(data, ps, ctx->q.sockfd, - ctx->need_recv, ctx->need_send); + result = Curl_pollset_set(data, ps, ctx->q.sockfd, + ctx->need_recv, ctx->need_send); } } + return result; } static CURLcode cf_osslq_query(struct Curl_cfilter *cf, diff --git a/lib/vquic/curl_quiche.c b/lib/vquic/curl_quiche.c index 9bb72511e9..b981f4d780 100644 --- a/lib/vquic/curl_quiche.c +++ b/lib/vquic/curl_quiche.c @@ -1142,15 +1142,16 @@ static bool stream_is_writeable(struct Curl_cfilter *cf, ctx->qconn, (curl_uint64_t)stream->id, 1) > 0); } -static void cf_quiche_adjust_pollset(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct easy_pollset *ps) +static CURLcode cf_quiche_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps) { struct cf_quiche_ctx *ctx = cf->ctx; bool want_recv, want_send; + CURLcode result = CURLE_OK; if(!ctx->qconn) - return; + return CURLE_OK; Curl_pollset_check(data, ps, ctx->q.sockfd, &want_recv, &want_send); if(want_recv || want_send) { @@ -1165,8 +1166,9 @@ static void cf_quiche_adjust_pollset(struct Curl_cfilter *cf, want_send = (!s_exhaust && want_send) || !Curl_bufq_is_empty(&ctx->q.sendbuf); - Curl_pollset_set(data, ps, ctx->q.sockfd, want_recv, want_send); + result = Curl_pollset_set(data, ps, ctx->q.sockfd, want_recv, want_send); } + return result; } /* diff --git a/lib/vssh/libssh.c b/lib/vssh/libssh.c index d9c37f9db5..d6ce1a65f4 100644 --- a/lib/vssh/libssh.c +++ b/lib/vssh/libssh.c @@ -125,8 +125,8 @@ CURLcode sftp_perform(struct Curl_easy *data, bool *connected, bool *dophase_done); -static int myssh_getsock(struct Curl_easy *data, - struct connectdata *conn, curl_socket_t *sock); +static CURLcode myssh_pollset(struct Curl_easy *data, + struct easy_pollset *ps); static void myssh_block2waitfor(struct connectdata *conn, struct ssh_conn *sshc, bool block); @@ -148,10 +148,10 @@ const struct Curl_handler Curl_handler_scp = { myssh_connect, /* connect_it */ myssh_multi_statemach, /* connecting */ scp_doing, /* doing */ - myssh_getsock, /* proto_getsock */ - myssh_getsock, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - myssh_getsock, /* perform_getsock */ + myssh_pollset, /* proto_pollset */ + myssh_pollset, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + myssh_pollset, /* perform_pollset */ scp_disconnect, /* disconnect */ ZERO_NULL, /* write_resp */ ZERO_NULL, /* write_resp_hd */ @@ -177,10 +177,10 @@ const struct Curl_handler Curl_handler_sftp = { myssh_connect, /* connect_it */ myssh_multi_statemach, /* connecting */ sftp_doing, /* doing */ - myssh_getsock, /* proto_getsock */ - myssh_getsock, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - myssh_getsock, /* perform_getsock */ + myssh_pollset, /* proto_pollset */ + myssh_pollset, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + myssh_pollset, /* perform_pollset */ sftp_disconnect, /* disconnect */ ZERO_NULL, /* write_resp */ ZERO_NULL, /* write_resp_hd */ @@ -2390,25 +2390,19 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, /* called by the multi interface to figure out what socket(s) to wait for and for what actions in the DO_DONE, PERFORM and WAITPERFORM states */ -static int myssh_getsock(struct Curl_easy *data, - struct connectdata *conn, - curl_socket_t *sock) +static CURLcode myssh_pollset(struct Curl_easy *data, + struct easy_pollset *ps) { - int bitmap = GETSOCK_BLANK; - (void)data; - sock[0] = conn->sock[FIRSTSOCKET]; - - if(conn->waitfor & KEEP_RECV) - bitmap |= GETSOCK_READSOCK(FIRSTSOCKET); - - if(conn->waitfor & KEEP_SEND) - bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET); - - if(!conn->waitfor) - bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET); - - DEBUGF(infof(data, "ssh_getsock -> %x", bitmap)); - return bitmap; + int flags = 0; + if(data->conn->waitfor & KEEP_RECV) + flags |= CURL_POLL_IN; + if(data->conn->waitfor & KEEP_SEND) + flags |= CURL_POLL_OUT; + if(!data->conn->waitfor) + flags |= CURL_POLL_OUT; + return flags ? + Curl_pollset_change(data, ps, data->conn->sock[FIRSTSOCKET], flags, 0) : + CURLE_OK; } static void myssh_block2waitfor(struct connectdata *conn, @@ -2437,7 +2431,7 @@ static CURLcode myssh_multi_statemach(struct Curl_easy *data, struct connectdata *conn = data->conn; struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN); struct SSHPROTO *sshp = Curl_meta_get(data, CURL_META_SSH_EASY); - bool block; /* we store the status and use that to provide a ssh_getsock() + bool block; /* we store the status and use that to provide a ssh_pollset() implementation */ CURLcode result; diff --git a/lib/vssh/libssh2.c b/lib/vssh/libssh2.c index ab57dd87f5..bac362dd5c 100644 --- a/lib/vssh/libssh2.c +++ b/lib/vssh/libssh2.c @@ -99,8 +99,8 @@ static CURLcode sftp_disconnect(struct Curl_easy *data, struct connectdata *conn, bool dead); static CURLcode sftp_perform(struct Curl_easy *data, bool *connected, bool *dophase_done); -static int ssh_getsock(struct Curl_easy *data, struct connectdata *conn, - curl_socket_t *sock); +static CURLcode ssh_pollset(struct Curl_easy *data, + struct easy_pollset *ps); static CURLcode ssh_setup_connection(struct Curl_easy *data, struct connectdata *conn); static void ssh_attach(struct Curl_easy *data, struct connectdata *conn); @@ -119,10 +119,10 @@ const struct Curl_handler Curl_handler_scp = { ssh_connect, /* connect_it */ ssh_multi_statemach, /* connecting */ scp_doing, /* doing */ - ssh_getsock, /* proto_getsock */ - ssh_getsock, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ssh_getsock, /* perform_getsock */ + ssh_pollset, /* proto_pollset */ + ssh_pollset, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + ssh_pollset, /* perform_pollset */ scp_disconnect, /* disconnect */ ZERO_NULL, /* write_resp */ ZERO_NULL, /* write_resp_hd */ @@ -150,10 +150,10 @@ const struct Curl_handler Curl_handler_sftp = { ssh_connect, /* connect_it */ ssh_multi_statemach, /* connecting */ sftp_doing, /* doing */ - ssh_getsock, /* proto_getsock */ - ssh_getsock, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ssh_getsock, /* perform_getsock */ + ssh_pollset, /* proto_pollset */ + ssh_pollset, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + ssh_pollset, /* perform_pollset */ sftp_disconnect, /* disconnect */ ZERO_NULL, /* write_resp */ ZERO_NULL, /* write_resp_hd */ @@ -3087,22 +3087,17 @@ static CURLcode ssh_statemachine(struct Curl_easy *data, /* called by the multi interface to figure out what socket(s) to wait for and for what actions in the DO_DONE, PERFORM and WAITPERFORM states */ -static int ssh_getsock(struct Curl_easy *data, - struct connectdata *conn, - curl_socket_t *sock) +static CURLcode ssh_pollset(struct Curl_easy *data, + struct easy_pollset *ps) { - int bitmap = GETSOCK_BLANK; - (void)data; - - sock[0] = conn->sock[FIRSTSOCKET]; - - if(conn->waitfor & KEEP_RECV) - bitmap |= GETSOCK_READSOCK(FIRSTSOCKET); - - if(conn->waitfor & KEEP_SEND) - bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET); - - return bitmap; + int flags = 0; + if(data->conn->waitfor & KEEP_RECV) + flags |= CURL_POLL_IN; + if(data->conn->waitfor & KEEP_SEND) + flags |= CURL_POLL_OUT; + return flags ? + Curl_pollset_change(data, ps, data->conn->sock[FIRSTSOCKET], flags, 0) : + CURLE_OK; } /* @@ -3139,7 +3134,7 @@ static CURLcode ssh_multi_statemach(struct Curl_easy *data, bool *done) struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN); struct SSHPROTO *sshp = Curl_meta_get(data, CURL_META_SSH_EASY); CURLcode result = CURLE_OK; - bool block; /* we store the status and use that to provide a ssh_getsock() + bool block; /* we store the status and use that to provide a ssh_pollset() implementation */ if(!sshc || !sshp) return CURLE_FAILED_INIT; diff --git a/lib/vssh/wolfssh.c b/lib/vssh/wolfssh.c index eed73fd919..15f504a01c 100644 --- a/lib/vssh/wolfssh.c +++ b/lib/vssh/wolfssh.c @@ -66,9 +66,8 @@ static CURLcode wsftp_doing(struct Curl_easy *data, static CURLcode wsftp_disconnect(struct Curl_easy *data, struct connectdata *conn, bool dead); -static int wssh_getsock(struct Curl_easy *data, - struct connectdata *conn, - curl_socket_t *sock); +static CURLcode wssh_pollset(struct Curl_easy *data, + struct easy_pollset *ps); static CURLcode wssh_setup_connection(struct Curl_easy *data, struct connectdata *conn); static void wssh_sshc_cleanup(struct ssh_conn *sshc); @@ -87,10 +86,10 @@ const struct Curl_handler Curl_handler_scp = { wssh_connect, /* connect_it */ wssh_multi_statemach, /* connecting */ wscp_doing, /* doing */ - wssh_getsock, /* proto_getsock */ - wssh_getsock, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - wssh_getsock, /* perform_getsock */ + wssh_pollset, /* proto_pollset */ + wssh_pollset, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + wssh_pollset, /* perform_pollset */ wscp_disconnect, /* disconnect */ ZERO_NULL, /* write_resp */ ZERO_NULL, /* write_resp_hd */ @@ -118,10 +117,10 @@ const struct Curl_handler Curl_handler_sftp = { wssh_connect, /* connect_it */ wssh_multi_statemach, /* connecting */ wsftp_doing, /* doing */ - wssh_getsock, /* proto_getsock */ - wssh_getsock, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - wssh_getsock, /* perform_getsock */ + wssh_pollset, /* proto_pollset */ + wssh_pollset, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + wssh_pollset, /* perform_pollset */ wsftp_disconnect, /* disconnect */ ZERO_NULL, /* write_resp */ ZERO_NULL, /* write_resp_hd */ @@ -932,7 +931,7 @@ static CURLcode wssh_multi_statemach(struct Curl_easy *data, bool *done) struct connectdata *conn = data->conn; struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN); CURLcode result = CURLE_OK; - bool block; /* we store the status and use that to provide a ssh_getsock() + bool block; /* we store the status and use that to provide a ssh_pollset() implementation */ if(!sshc) return CURLE_FAILED_INIT; @@ -1185,21 +1184,17 @@ static CURLcode wsftp_disconnect(struct Curl_easy *data, return result; } -static int wssh_getsock(struct Curl_easy *data, - struct connectdata *conn, - curl_socket_t *sock) +static CURLcode wssh_pollset(struct Curl_easy *data, + struct easy_pollset *ps) { - int bitmap = GETSOCK_BLANK; - int dir = conn->waitfor; - (void)data; - sock[0] = conn->sock[FIRSTSOCKET]; - - if(dir == KEEP_RECV) - bitmap |= GETSOCK_READSOCK(FIRSTSOCKET); - else if(dir == KEEP_SEND) - bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET); - - return bitmap; + int flags = 0; + if(data->conn->waitfor & KEEP_RECV) + flags |= CURL_POLL_IN; + if(data->conn->waitfor & KEEP_SEND) + flags |= CURL_POLL_OUT; + return flags ? + Curl_pollset_change(data, ps, data->conn->sock[FIRSTSOCKET], flags, 0) : + CURLE_OK; } void Curl_ssh_version(char *buffer, size_t buflen) diff --git a/lib/vtls/vtls.c b/lib/vtls/vtls.c index 0ff79ea70b..96d64dd606 100644 --- a/lib/vtls/vtls.c +++ b/lib/vtls/vtls.c @@ -503,26 +503,30 @@ void Curl_ssl_close_all(struct Curl_easy *data) Curl_ssl->close_all(data); } -void Curl_ssl_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data, - struct easy_pollset *ps) +CURLcode Curl_ssl_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps) { struct ssl_connect_data *connssl = cf->ctx; if(connssl->io_need) { curl_socket_t sock = Curl_conn_cf_get_socket(cf->next, data); + CURLcode result = CURLE_OK; if(sock != CURL_SOCKET_BAD) { if(connssl->io_need & CURL_SSL_IO_NEED_SEND) { - Curl_pollset_set_out_only(data, ps, sock); + result = Curl_pollset_set_out_only(data, ps, sock); CURL_TRC_CF(data, cf, "adjust_pollset, POLLOUT fd=%" FMT_SOCKET_T, sock); } else { - Curl_pollset_set_in_only(data, ps, sock); + result = Curl_pollset_set_in_only(data, ps, sock); CURL_TRC_CF(data, cf, "adjust_pollset, POLLIN fd=%" FMT_SOCKET_T, sock); } } + return result; } + return CURLE_OK; } /* Selects an SSL crypto engine @@ -902,13 +906,13 @@ static CURLcode multissl_connect(struct Curl_cfilter *cf, return Curl_ssl->do_connect(cf, data, done); } -static void multissl_adjust_pollset(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct easy_pollset *ps) +static CURLcode multissl_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps) { if(multissl_setup(NULL)) - return; - Curl_ssl->adjust_pollset(cf, data, ps); + return CURLE_OK; + return Curl_ssl->adjust_pollset(cf, data, ps); } static void *multissl_get_internals(struct ssl_connect_data *connssl, @@ -1545,16 +1549,18 @@ static CURLcode ssl_cf_shutdown(struct Curl_cfilter *cf, return result; } -static void ssl_cf_adjust_pollset(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct easy_pollset *ps) +static CURLcode ssl_cf_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps) { struct ssl_connect_data *connssl = cf->ctx; struct cf_call_data save; + CURLcode result; CF_DATA_SAVE(save, cf, data); - connssl->ssl_impl->adjust_pollset(cf, data, ps); + result = connssl->ssl_impl->adjust_pollset(cf, data, ps); CF_DATA_RESTORE(cf, save); + return result; } static CURLcode ssl_cf_query(struct Curl_cfilter *cf, diff --git a/lib/vtls/vtls_int.h b/lib/vtls/vtls_int.h index 11987c382a..de0b735e22 100644 --- a/lib/vtls/vtls_int.h +++ b/lib/vtls/vtls_int.h @@ -166,8 +166,8 @@ struct Curl_ssl { /* During handshake/shutdown, adjust the pollset to include the socket * for POLLOUT or POLLIN as needed. Mandatory. */ - void (*adjust_pollset)(struct Curl_cfilter *cf, struct Curl_easy *data, - struct easy_pollset *ps); + CURLcode (*adjust_pollset)(struct Curl_cfilter *cf, struct Curl_easy *data, + struct easy_pollset *ps); void *(*get_internals)(struct ssl_connect_data *connssl, CURLINFO info); void (*close)(struct Curl_cfilter *cf, struct Curl_easy *data); void (*close_all)(struct Curl_easy *data); @@ -190,8 +190,9 @@ struct Curl_ssl { extern const struct Curl_ssl *Curl_ssl; -void Curl_ssl_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data, - struct easy_pollset *ps); +CURLcode Curl_ssl_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps); /** * Get the SSL filter below the given one or NULL if there is none. diff --git a/lib/ws.c b/lib/ws.c index ac542fbc14..7d7bf48157 100644 --- a/lib/ws.c +++ b/lib/ws.c @@ -1552,10 +1552,10 @@ const struct Curl_handler Curl_handler_ws = { Curl_http_connect, /* connect_it */ ZERO_NULL, /* connecting */ ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_getsock */ - Curl_http_getsock_do, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ + ZERO_NULL, /* proto_pollset */ + Curl_http_do_pollset, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + ZERO_NULL, /* perform_pollset */ ZERO_NULL, /* disconnect */ Curl_http_write_resp, /* write_resp */ Curl_http_write_resp_hd, /* write_resp_hd */ @@ -1579,10 +1579,10 @@ const struct Curl_handler Curl_handler_wss = { Curl_http_connect, /* connect_it */ NULL, /* connecting */ ZERO_NULL, /* doing */ - NULL, /* proto_getsock */ - Curl_http_getsock_do, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ + NULL, /* proto_pollset */ + Curl_http_do_pollset, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + ZERO_NULL, /* perform_pollset */ ZERO_NULL, /* disconnect */ Curl_http_write_resp, /* write_resp */ Curl_http_write_resp_hd, /* write_resp_hd */ diff --git a/tests/unit/unit2600.c b/tests/unit/unit2600.c index 820176b2d9..08b2ca6fbd 100644 --- a/tests/unit/unit2600.c +++ b/tests/unit/unit2600.c @@ -105,8 +105,10 @@ struct test_result { static const struct test_case *current_tc; static struct test_result *current_tr; +static int test_idx; struct cf_test_ctx { + int idx; int ai_family; int transport; char id[16]; @@ -151,13 +153,13 @@ static CURLcode cf_test_connect(struct Curl_cfilter *cf, return CURLE_OK; } -static void cf_test_adjust_pollset(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct easy_pollset *ps) +static CURLcode cf_test_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps) { + struct cf_test_ctx *ctx = cf->ctx; /* just for testing, give one socket with events back */ - (void)cf; - Curl_pollset_set(data, ps, 1, TRUE, TRUE); + return Curl_pollset_set(data, ps, ctx->idx, TRUE, TRUE); } static CURLcode cf_test_create(struct Curl_cfilter **pcf, @@ -196,6 +198,7 @@ static CURLcode cf_test_create(struct Curl_cfilter **pcf, result = CURLE_OUT_OF_MEMORY; goto out; } + ctx->idx = test_idx++; ctx->ai_family = ai->ai_family; ctx->transport = transport; ctx->started = curlx_now();