diff --git a/lib/asyn-ares.c b/lib/asyn-ares.c index 451964ec1c..c1368e690a 100644 --- a/lib/asyn-ares.c +++ b/lib/asyn-ares.c @@ -277,11 +277,11 @@ static void async_ares_cleanup(struct Curl_easy *data) * (using curl_multi_fdset()) wants to get our fd_set setup. */ -unsigned int Curl_async_getsock(struct Curl_easy *data, curl_socket_t *socks) +CURLcode Curl_async_getsock(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_getsock(data, ares->channel, ps); } /* diff --git a/lib/asyn-base.c b/lib/asyn-base.c index bd4cf99a7d..57ac591c6b 100644 --- a/lib/asyn-base.c +++ b/lib/asyn-base.c @@ -78,19 +78,38 @@ * Returns: sockets-in-use-bitmap */ -unsigned int Curl_ares_getsock(struct Curl_easy *data, - ares_channel channel, - curl_socket_t *socks) + +CURLcode Curl_ares_getsock(struct Curl_easy *data, + ares_channel channel, + struct easy_pollset *ps) { struct timeval maxtime = { CURL_TIMEOUT_RESOLVE, 0 }; struct timeval timebuf; - unsigned 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[5]; + 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 e70893ddc5..d4f53a09a0 100644 --- a/lib/asyn-thrdd.c +++ b/lib/asyn-thrdd.c @@ -630,33 +630,25 @@ CURLcode Curl_async_is_resolved(struct Curl_easy *data, } } -unsigned int Curl_async_getsock(struct Curl_easy *data, curl_socket_t *socks) +CURLcode Curl_async_getsock(struct Curl_easy *data, struct easy_pollset *ps) { struct async_thrdd_ctx *thrdd = &data->state.async.thrdd; - unsigned 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_getsock(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 @@ -674,7 +666,7 @@ unsigned 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 ae9b670ecf..08d35c77fc 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 @@ -78,7 +79,7 @@ CURLcode Curl_async_get_impl(struct Curl_easy *easy, void **impl); * return bitmask indicating what file descriptors (referring to array indexes * in the 'sock' array) to wait for, read/write. */ -unsigned int Curl_async_getsock(struct Curl_easy *data, curl_socket_t *sock); +CURLcode Curl_async_getsock(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 -unsigned int Curl_ares_getsock(struct Curl_easy *data, - ares_channel channel, - curl_socket_t *socks); +CURLcode Curl_ares_getsock(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-https-connect.c b/lib/cf-https-connect.c index c62429a142..81f303d0ba 100644 --- a/lib/cf-https-connect.c +++ b/lib/cf-https-connect.c @@ -441,7 +441,7 @@ static void cf_hc_adjust_pollset(struct Curl_cfilter *cf, continue; 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 socks", ps->n); } } diff --git a/lib/cf-ip-happy.c b/lib/cf-ip-happy.c index f60f5bf611..b90cb26f64 100644 --- a/lib/cf-ip-happy.c +++ b/lib/cf-ip-happy.c @@ -722,7 +722,7 @@ static void cf_ip_happy_adjust_pollset(struct Curl_cfilter *cf, if(!cf->connected) { cf_ip_ballers_pollset(&ctx->ballers, data, ps); - CURL_TRC_CF(data, cf, "adjust_pollset -> %d socks", ps->num); + CURL_TRC_CF(data, cf, "adjust_pollset -> %d socks", ps->n); } } diff --git a/lib/cfilters.c b/lib/cfilters.c index fda0367bb0..3860200718 100644 --- a/lib/cfilters.c +++ b/lib/cfilters.c @@ -469,6 +469,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 +487,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 +525,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,8 +535,8 @@ 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); @@ -557,6 +558,7 @@ CURLcode Curl_conn_connect(struct Curl_easy *data, } out: + Curl_pollset_cleanup(&ps); Curl_pollfds_cleanup(&cpfds); return result; } @@ -746,35 +748,17 @@ 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_pollset_init(&ps); 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; - } - } - - if(!npfds) - DEBUGF(infof(data, "no sockets to poll!")); - return Curl_poll(pfds, npfds, timeout_ms); + 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, diff --git a/lib/cshutdn.c b/lib/cshutdn.c index aeea9bdc3e..a35313fd74 100644 --- a/lib/cshutdn.c +++ b/lib/cshutdn.c @@ -486,18 +486,19 @@ 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)); + Curl_pollset_reset(&ps); Curl_attach_connection(data, conn); Curl_conn_adjust_pollset(data, conn, &ps); Curl_detach_connection(data); - for(i = 0; i < ps.num; i++) { + for(i = 0; i < ps.n; i++) { #ifdef __DJGPP__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Warith-conversion" @@ -514,6 +515,7 @@ void Curl_cshutdn_setfds(struct cshutdn *cshutdn, *maxfd = (int)ps.sockets[i]; } } + Curl_pollset_cleanup(&ps); } } @@ -529,16 +531,18 @@ unsigned int Curl_cshutdn_add_waitfds(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); Curl_detach_connection(data); need += Curl_waitfds_add_ps(cwfds, &ps); } + Curl_pollset_cleanup(&ps); } return need; } @@ -554,20 +558,23 @@ 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); Curl_detach_connection(data); 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/hostip.c b/lib/hostip.c index ae63ab3293..e98f84ce4d 100644 --- a/lib/hostip.c +++ b/lib/hostip.c @@ -1523,21 +1523,21 @@ CURLcode Curl_resolv_check(struct Curl_easy *data, } #endif -unsigned int Curl_resolv_getsock(struct Curl_easy *data, - curl_socket_t *socks) +CURLcode Curl_resolv_getsock(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_getsock(data, ps); #else (void)data; (void)socks; - return GETSOCK_BLANK; + return CURLE_OK; #endif } diff --git a/lib/hostip.h b/lib/hostip.h index 978cd83909..9c0c570f7c 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 -unsigned int Curl_resolv_getsock(struct Curl_easy *data, - curl_socket_t *socks); +CURLcode Curl_resolv_getsock(struct Curl_easy *data, + struct easy_pollset *ps); CURLcode Curl_resolver_error(struct Curl_easy *data); diff --git a/lib/multi.c b/lib/multi.c index 8fb9410124..fa20f4b0f3 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1012,18 +1012,20 @@ static unsigned int perform_getsock(struct Curl_easy *data, /* 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_getsock(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_pollset_reset(ps); if(!data->conn) - return; + return CURLM_OK; switch(data->mstate) { case MSTATE_INIT: @@ -1035,7 +1037,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_getsock(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; @@ -1089,6 +1091,13 @@ void Curl_multi_getsock(struct Curl_easy *data, break; } + if(result) { + if(result == CURLE_OUT_OF_MEMORY) + mresult = CURLM_OUT_OF_MEMORY; + else + mresult = CURLM_INTERNAL_ERROR; + goto out; + } /* Unblocked and waiting to receive with buffered input. * Make transfer run again at next opportunity. */ @@ -1102,7 +1111,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), @@ -1129,10 +1138,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) && @@ -1145,6 +1154,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, @@ -1156,6 +1167,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 */ @@ -1165,10 +1177,10 @@ 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); @@ -1176,7 +1188,7 @@ CURLMcode curl_multi_fdset(CURLM *m, } Curl_multi_getsock(data, &ps, "curl_multi_fdset"); - for(i = 0; i < ps.num; i++) { + for(i = 0; i < ps.n; i++) { if(!FDSET_SOCK(ps.sockets[i])) /* pretend it does not exist */ continue; @@ -1202,6 +1214,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; } @@ -1214,6 +1227,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)) @@ -1225,11 +1239,11 @@ 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); @@ -1250,6 +1264,7 @@ CURLMcode curl_multi_waitfds(CURLM *m, if(fd_count) *fd_count = need; + Curl_pollset_cleanup(&ps); return result; } @@ -1283,6 +1298,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 */ @@ -1307,12 +1323,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); @@ -1519,6 +1535,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..13bf747d3b 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; @@ -443,9 +443,11 @@ static CURLMcode mev_pollset_diff(struct Curl_multi *multi, static void mev_pollset_dtor(void *key, size_t klen, void *entry) { + struct easy_pollset *ps = entry; (void)key; (void)klen; - free(entry); + Curl_pollset_cleanup(ps); + free(ps); } static struct easy_pollset* @@ -456,6 +458,7 @@ mev_add_new_conn_pollset(struct connectdata *conn) ps = calloc(1, sizeof(*ps)); if(!ps) return NULL; + Curl_pollset_init(ps); if(Curl_conn_meta_set(conn, CURL_META_MEV_POLLSET, ps, mev_pollset_dtor)) return NULL; return ps; @@ -469,6 +472,7 @@ mev_add_new_xfer_pollset(struct Curl_easy *data) ps = calloc(1, sizeof(*ps)); if(!ps) return NULL; + Curl_pollset_init(ps); if(Curl_meta_set(data, CURL_META_MEV_POLLSET, ps, mev_pollset_dtor)) return NULL; return ps; @@ -486,42 +490,41 @@ 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) + Curl_conn_adjust_pollset(data, conn, &ps); + else if(data) + Curl_multi_getsock(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/multiif.h b/lib/multiif.h index 221de04721..54ceb1c089 100644 --- a/lib/multiif.h +++ b/lib/multiif.h @@ -72,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_getsock(struct Curl_easy *data, + struct easy_pollset *ps, + const char *caller); /** * Borrow the transfer buffer from the multi, suitable diff --git a/lib/select.c b/lib/select.c index db1933e223..53cff5db30 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; @@ -493,48 +494,70 @@ unsigned int Curl_waitfds_add_ps(struct Curl_waitfds *cwfds, return need; } -void Curl_pollset_reset(struct Curl_easy *data, - struct easy_pollset *ps) +void Curl_pollset_reset(struct easy_pollset *ps) { - size_t i; - (void)data; - memset(ps, 0, sizeof(*ps)); - for(i = 0; i < MAX_SOCKSPEREASYHANDLE; i++) + 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) +{ + memset(ps, 0, sizeof(*ps)); +#ifdef DEBUGBUILD + ps->init = CURL_EASY_POLLSET_MAGIC; +#endif + ps->count = MAX_SOCKSPEREASYHANDLE; + Curl_pollset_reset(ps); +} + +void Curl_pollset_cleanup(struct easy_pollset *ps) +{ + Curl_pollset_reset(ps); } /** * */ -void Curl_pollset_change(struct Curl_easy *data, - struct easy_pollset *ps, curl_socket_t sock, - int add_flags, int remove_flags) +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; + 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->num; ++i) { + 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->num) { + if((i + 1) < ps->n) { memmove(&ps->sockets[i], &ps->sockets[i + 1], - (ps->num - (i + 1)) * sizeof(ps->sockets[0])); + (ps->n - (i + 1)) * sizeof(ps->sockets[0])); memmove(&ps->actions[i], &ps->actions[i + 1], - (ps->num - (i + 1)) * sizeof(ps->actions[0])); + (ps->n - (i + 1)) * sizeof(ps->actions[0])); } - --ps->num; + --ps->n; } - return; + return CURLE_OK; } } /* not present */ @@ -547,24 +570,65 @@ void Curl_pollset_change(struct Curl_easy *data, * 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) { + DEBUGASSERT(i < ps->count); + if(i < ps->count) { ps->sockets[i] = sock; ps->actions[i] = (unsigned char)add_flags; - ps->num = i + 1; + ps->n = i + 1; } } + return CURLE_OK; } -void Curl_pollset_set(struct Curl_easy *data, - struct easy_pollset *ps, curl_socket_t sock, - bool do_in, bool do_out) +CURLcode 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)); + 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; } static void ps_add(struct Curl_easy *data, struct easy_pollset *ps, @@ -609,7 +673,7 @@ void Curl_pollset_check(struct Curl_easy *data, (void)data; DEBUGASSERT(VALID_SOCK(sock)); - for(i = 0; i < ps->num; ++i) { + 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); @@ -625,7 +689,7 @@ bool Curl_pollset_want_read(struct Curl_easy *data, { unsigned int i; (void)data; - for(i = 0; i < ps->num; ++i) { + for(i = 0; i < ps->n; ++i) { if((ps->sockets[i] == sock) && (ps->actions[i] & CURL_POLL_IN)) return TRUE; } diff --git a/lib/select.h b/lib/select.h index 02257dcbea..3d562a5edb 100644 --- a/lib/select.h +++ b/lib/select.h @@ -133,27 +133,39 @@ int Curl_poll(struct pollfd ufds[], unsigned int nfds, timediff_t timeout_ms); /* Polling requested by an easy handle. * `action` is CURL_POLL_IN, CURL_POLL_OUT or CURL_POLL_INOUT. */ +#ifdef DEBUGBUILD +#define CURL_EASY_POLLSET_MAGIC 0x7a657370 +#endif + struct easy_pollset { curl_socket_t sockets[MAX_SOCKSPEREASYHANDLE]; - unsigned int num; + unsigned int n; + unsigned int count; +#ifdef DEBUGBUILD + int init; +#endif unsigned char actions[MAX_SOCKSPEREASYHANDLE]; }; -void Curl_pollset_reset(struct Curl_easy *data, - struct easy_pollset *ps); +/* 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); /* 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); +CURLcode 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); +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) @@ -169,6 +181,11 @@ void Curl_pollset_set(struct Curl_easy *data, 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); + void Curl_pollset_add_socks(struct Curl_easy *data, struct easy_pollset *ps, unsigned int (*socks_cb)(struct Curl_easy *data,