| @@ -294,7 +294,8 @@ struct Curl_multi *Curl_multi_handle(uint32_t xfer_table_size, |
| multi->wsa_event = WSACreateEvent(); |
| if(multi->wsa_event == WSA_INVALID_EVENT) |
| goto error; |
| - | #elif defined(ENABLE_WAKEUP) |
| + | #endif |
| + | #ifdef ENABLE_WAKEUP |
| if(Curl_wakeup_init(multi->wakeup_pair, TRUE) < 0) { |
| multi->wakeup_pair[0] = CURL_SOCKET_BAD; |
| multi->wakeup_pair[1] = CURL_SOCKET_BAD; |
| @@ -319,6 +320,7 @@ error: |
| Curl_ssl_scache_destroy(multi->ssl_scache); |
| #endif |
| if(multi->admin) { |
| + | Curl_multi_ev_xfer_done(multi, multi->admin); |
| multi->admin->multi = NULL; |
| Curl_close(&multi->admin); |
| } |
| @@ -362,6 +364,17 @@ bool Curl_is_connecting(struct Curl_easy *data) |
| return data->mstate < MSTATE_DO; |
| } |
| |
| + | static CURLMcode multi_assess_wakeup(struct Curl_multi *multi) |
| + | { |
| + | #ifdef ENABLE_WAKEUP |
| + | if(multi->socket_cb) |
| + | return Curl_multi_ev_assess_xfer(multi, multi->admin); |
| + | #else |
| + | (void)multi; |
| + | #endif |
| + | return CURLM_OK; |
| + | } |
| + | |
| static CURLMcode multi_xfers_add(struct Curl_multi *multi, |
| struct Curl_easy *data) |
| { |
| @@ -528,6 +541,12 @@ CURLMcode curl_multi_add_handle(CURLM *m, CURL *d) |
| data->set.server_response_timeout; |
| multi->admin->set.no_signal = data->set.no_signal; |
| |
| + | mresult = multi_assess_wakeup(multi); |
| + | if(mresult) { |
| + | failf(data, "error enabling wakeup listening: %d", mresult); |
| + | return mresult; |
| + | } |
| + | |
| CURL_TRC_M(data, "added to multi, mid=%u, running=%u, total=%u", |
| data->mid, Curl_multi_xfers_running(multi), |
| Curl_uint32_tbl_count(&multi->xfers)); |
| @@ -656,9 +675,6 @@ static CURLcode multi_done(struct Curl_easy *data, |
| { |
| CURLcode result; |
| struct connectdata *conn = data->conn; |
| - | struct multi_done_ctx mdctx; |
| - | |
| - | memset(&mdctx, 0, sizeof(mdctx)); |
| |
| CURL_TRC_M(data, "multi_done: status: %d prem: %d done: %d", |
| (int)status, (int)premature, data->state.done); |
| @@ -689,7 +705,7 @@ static CURLcode multi_done(struct Curl_easy *data, |
| } |
| |
| /* this calls the protocol-specific function pointer previously set */ |
| - | if(conn->scheme->run->done && (data->mstate >= MSTATE_PROTOCONNECT)) |
| + | if(conn && conn->scheme->run->done && (data->mstate >= MSTATE_PROTOCONNECT)) |
| result = conn->scheme->run->done(data, status, premature); |
| else |
| result = status; |
| @@ -706,17 +722,23 @@ static CURLcode multi_done(struct Curl_easy *data, |
| result = Curl_1st_fatal(result, Curl_xfer_write_done(data, premature)); |
| |
| /* Inform connection filters that this transfer is done */ |
| - | Curl_conn_ev_data_done(data, premature); |
| + | if(conn) |
| + | Curl_conn_ev_data_done(data, premature); |
| |
| process_pending_handles(data->multi); /* connection / multiplex */ |
| |
| if(!result) |
| result = Curl_req_done(&data->req, data, premature); |
| |
| - | /* Under the potential connection pool's share lock, decide what to |
| - | * do with the transfer's connection. */ |
| - | mdctx.premature = premature; |
| - | Curl_cpool_do_locked(data, data->conn, multi_done_locked, &mdctx); |
| + | if(conn) { |
| + | /* Under the potential connection pool's share lock, decide what to |
| + | * do with the transfer's connection. */ |
| + | struct multi_done_ctx mdctx; |
| + | |
| + | memset(&mdctx, 0, sizeof(mdctx)); |
| + | mdctx.premature = premature; |
| + | Curl_cpool_do_locked(data, data->conn, multi_done_locked, &mdctx); |
| + | } |
| |
| /* flush the netrc cache */ |
| Curl_netrc_cleanup(&data->state.netrc); |
| @@ -871,6 +893,12 @@ CURLMcode curl_multi_remove_handle(CURLM *m, CURL *d) |
| if(mresult) |
| return mresult; |
| |
| + | mresult = multi_assess_wakeup(multi); |
| + | if(mresult) { |
| + | failf(data, "error enabling wakeup listening: %d", mresult); |
| + | return mresult; |
| + | } |
| + | |
| CURL_TRC_M(data, "removed from multi, mid=%u, running=%u, total=%u", |
| mid, Curl_multi_xfers_running(multi), |
| Curl_uint32_tbl_count(&multi->xfers)); |
| @@ -1086,75 +1114,79 @@ static CURLcode mstate_perform_pollset(struct Curl_easy *data, |
| CURLMcode Curl_multi_pollset(struct Curl_easy *data, |
| struct easy_pollset *ps) |
| { |
| - | CURLMcode mresult = CURLM_OK; |
| CURLcode result = CURLE_OK; |
| |
| + | Curl_pollset_reset(ps); |
| + | #ifdef ENABLE_WAKEUP |
| + | /* The admin handle always listens on the wakeup socket when there |
| + | * are transfers alive. */ |
| + | if(data->multi && (data == data->multi->admin) && |
| + | data->multi->xfers_alive && |
| + | (data->multi->wakeup_pair[0] != CURL_SOCKET_BAD)) { |
| + | result = Curl_pollset_add_in(data, ps, data->multi->wakeup_pair[0]); |
| + | } |
| + | #endif |
| /* If the transfer has no connection, this is fine. Happens when |
| called via curl_multi_remove_handle() => Curl_multi_ev_assess() => |
| Curl_multi_pollset(). */ |
| - | Curl_pollset_reset(ps); |
| - | if(!data->conn) |
| - | return CURLM_OK; |
| - | |
| - | switch(data->mstate) { |
| - | case MSTATE_INIT: |
| - | case MSTATE_PENDING: |
| - | case MSTATE_SETUP: |
| - | case MSTATE_CONNECT: |
| - | /* nothing to poll for yet */ |
| - | break; |
| + | if(!result && data->conn) { |
| + | switch(data->mstate) { |
| + | case MSTATE_INIT: |
| + | case MSTATE_PENDING: |
| + | case MSTATE_SETUP: |
| + | case MSTATE_CONNECT: |
| + | /* nothing to poll for yet */ |
| + | break; |
| |
| - | case MSTATE_RESOLVING: |
| - | result = Curl_resolv_pollset(data, ps); |
| - | break; |
| + | case MSTATE_RESOLVING: |
| + | result = Curl_resolv_pollset(data, ps); |
| + | break; |
| |
| - | case MSTATE_CONNECTING: |
| - | result = mstate_connecting_pollset(data, ps); |
| - | break; |
| + | case MSTATE_CONNECTING: |
| + | result = mstate_connecting_pollset(data, ps); |
| + | break; |
| |
| - | case MSTATE_PROTOCONNECT: |
| - | case MSTATE_PROTOCONNECTING: |
| - | result = mstate_protocol_pollset(data, ps); |
| - | break; |
| + | case MSTATE_PROTOCONNECT: |
| + | case MSTATE_PROTOCONNECTING: |
| + | result = mstate_protocol_pollset(data, ps); |
| + | break; |
| |
| - | case MSTATE_DO: |
| - | case MSTATE_DOING: |
| - | result = mstate_do_pollset(data, ps); |
| - | break; |
| + | case MSTATE_DO: |
| + | case MSTATE_DOING: |
| + | result = mstate_do_pollset(data, ps); |
| + | break; |
| |
| - | case MSTATE_DOING_MORE: |
| - | result = mstate_domore_pollset(data, ps); |
| - | break; |
| + | case MSTATE_DOING_MORE: |
| + | result = mstate_domore_pollset(data, ps); |
| + | break; |
| |
| - | case MSTATE_DID: /* same as PERFORMING in regard to polling */ |
| - | case MSTATE_PERFORMING: |
| - | result = mstate_perform_pollset(data, ps); |
| - | break; |
| + | case MSTATE_DID: /* same as PERFORMING in regard to polling */ |
| + | case MSTATE_PERFORMING: |
| + | result = mstate_perform_pollset(data, ps); |
| + | break; |
| |
| - | case MSTATE_RATELIMITING: |
| - | /* we need to let time pass, ignore socket(s) */ |
| - | break; |
| + | case MSTATE_RATELIMITING: |
| + | /* we need to let time pass, ignore socket(s) */ |
| + | break; |
| |
| - | case MSTATE_DONE: |
| - | case MSTATE_COMPLETED: |
| - | case MSTATE_MSGSENT: |
| - | /* nothing more to poll for */ |
| - | break; |
| + | case MSTATE_DONE: |
| + | case MSTATE_COMPLETED: |
| + | case MSTATE_MSGSENT: |
| + | /* nothing more to poll for */ |
| + | break; |
| |
| - | default: |
| - | failf(data, "multi_getsock: unexpected multi state %d", data->mstate); |
| - | DEBUGASSERT(0); |
| - | break; |
| + | default: |
| + | failf(data, "multi_getsock: unexpected multi state %d", data->mstate); |
| + | DEBUGASSERT(0); |
| + | 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; |
| + | return CURLM_OUT_OF_MEMORY; |
| + | failf(data, "error determining pollset: %d", result); |
| + | return CURLM_INTERNAL_ERROR; |
| } |
| |
| #ifdef CURLVERBOSE |
| @@ -1193,8 +1225,7 @@ CURLMcode Curl_multi_pollset(struct Curl_easy *data, |
| } |
| #endif |
| |
| - | out: |
| - | return mresult; |
| + | return CURLM_OK; |
| } |
| |
| CURLMcode curl_multi_fdset(CURLM *m, |
| @@ -1313,7 +1344,176 @@ static void reset_socket_fdwrite(curl_socket_t s) |
| if(!getsockopt(s, SOL_SOCKET, SO_TYPE, (char *)&t, &l) && t == SOCK_STREAM) |
| swrite(s, NULL, 0); |
| } |
| - | #endif |
| + | |
| + | static CURLMcode multi_winsock_select(struct Curl_multi *multi, |
| + | struct curl_pollfds *cpfds, |
| + | unsigned int curl_nfds, |
| + | struct curl_waitfd extra_fds[], |
| + | unsigned int extra_nfds, |
| + | int timeout_ms, |
| + | bool wait_on_nop, |
| + | int *pnevents) |
| + | { |
| + | CURLMcode mresult = CURLM_OK; |
| + | WSANETWORKEVENTS wsa_events; |
| + | int nevents = 0; |
| + | size_t i; |
| + | |
| + | DEBUGASSERT(multi->wsa_event != WSA_INVALID_EVENT); |
| + | |
| + | /* Set the WSA events based on the collected pollds */ |
| + | for(i = 0; i < cpfds->n; i++) { |
| + | long mask = 0; |
| + | if(cpfds->pfds[i].events & POLLIN) |
| + | mask |= FD_READ | FD_ACCEPT | FD_CLOSE; |
| + | if(cpfds->pfds[i].events & POLLPRI) |
| + | mask |= FD_OOB; |
| + | if(cpfds->pfds[i].events & POLLOUT) { |
| + | mask |= FD_WRITE | FD_CONNECT | FD_CLOSE; |
| + | reset_socket_fdwrite(cpfds->pfds[i].fd); |
| + | } |
| + | if(mask) { |
| + | if(WSAEventSelect(cpfds->pfds[i].fd, multi->wsa_event, mask) != 0) { |
| + | mresult = CURLM_OUT_OF_MEMORY; |
| + | goto out; |
| + | } |
| + | } |
| + | } |
| + | |
| + | if(cpfds->n || wait_on_nop) { |
| + | int pollrc = 0; |
| + | |
| + | if(cpfds->n) { /* pre-check with Winsock */ |
| + | pollrc = Curl_poll(cpfds->pfds, cpfds->n, 0); |
| + | if(pollrc < 0) { |
| + | mresult = CURLM_UNRECOVERABLE_POLL; |
| + | goto out; |
| + | } |
| + | nevents = pollrc; |
| + | } |
| + | |
| + | if(!nevents) { |
| + | /* now wait... if not ready during the pre-check (pollrc == 0) */ |
| + | WSAWaitForMultipleEvents(1, &multi->wsa_event, FALSE, (DWORD)timeout_ms, |
| + | FALSE); |
| + | } |
| + | |
| + | /* With Winsock, we have to run the following section unconditionally |
| + | to call WSAEventSelect(fd, event, 0) on all the sockets */ |
| + | /* copy revents results from the poll to the curl_multi_wait poll |
| + | struct, the bit values of the actual underlying poll() implementation |
| + | may not be the same as the ones in the public libcurl API! */ |
| + | for(i = 0; i < extra_nfds; i++) { |
| + | unsigned short mask = 0; |
| + | curl_socket_t s = extra_fds[i].fd; |
| + | |
| + | wsa_events.lNetworkEvents = 0; |
| + | if(WSAEnumNetworkEvents(s, NULL, &wsa_events) == 0) { |
| + | if(wsa_events.lNetworkEvents & (FD_READ | FD_ACCEPT | FD_CLOSE)) |
| + | mask |= CURL_WAIT_POLLIN; |
| + | if(wsa_events.lNetworkEvents & (FD_WRITE | FD_CONNECT | FD_CLOSE)) |
| + | mask |= CURL_WAIT_POLLOUT; |
| + | if(wsa_events.lNetworkEvents & FD_OOB) |
| + | mask |= CURL_WAIT_POLLPRI; |
| + | if(!pollrc && wsa_events.lNetworkEvents) |
| + | nevents++; |
| + | } |
| + | WSAEventSelect(s, multi->wsa_event, 0); |
| + | if(!pollrc) { |
| + | extra_fds[i].revents = (short)mask; |
| + | continue; |
| + | } |
| + | else { |
| + | unsigned r = (unsigned)cpfds->pfds[curl_nfds + i].revents; |
| + | if(r & POLLIN) |
| + | mask |= CURL_WAIT_POLLIN; |
| + | if(r & POLLOUT) |
| + | mask |= CURL_WAIT_POLLOUT; |
| + | if(r & POLLPRI) |
| + | mask |= CURL_WAIT_POLLPRI; |
| + | extra_fds[i].revents = (short)mask; |
| + | } |
| + | } |
| + | |
| + | /* Count up all our own sockets that had activity, |
| + | and remove them from the event. */ |
| + | for(i = 0; i < curl_nfds; ++i) { |
| + | wsa_events.lNetworkEvents = 0; |
| + | if(WSAEnumNetworkEvents(cpfds->pfds[i].fd, NULL, &wsa_events) == 0) { |
| + | if(!pollrc && wsa_events.lNetworkEvents) |
| + | nevents++; |
| + | } |
| + | WSAEventSelect(cpfds->pfds[i].fd, multi->wsa_event, 0); |
| + | } |
| + | WSAResetEvent(multi->wsa_event); |
| + | } |
| + | |
| + | out: |
| + | *pnevents = nevents; |
| + | return mresult; |
| + | } |
| + | |
| + | #else /* USE_WINSOCK */ |
| + | |
| + | static CURLMcode multi_posix_poll(struct Curl_multi *multi, |
| + | struct curl_pollfds *cpfds, |
| + | unsigned int curl_nfds, |
| + | struct curl_waitfd extra_fds[], |
| + | unsigned int extra_nfds, |
| + | int timeout_ms, |
| + | bool wait_on_nop, |
| + | int *pnevents) |
| + | { |
| + | CURLMcode mresult = CURLM_OK; |
| + | int nevents = 0; |
| + | size_t i; |
| + | |
| + | if(cpfds->n) { |
| + | int pollrc = Curl_poll(cpfds->pfds, cpfds->n, timeout_ms); /* wait... */ |
| + | if(pollrc < 0) { |
| + | mresult = CURLM_UNRECOVERABLE_POLL; |
| + | goto out; |
| + | } |
| + | nevents = pollrc; |
| + | |
| + | /* copy revents results from the poll to the curl_multi_wait poll |
| + | struct, the bit values of the actual underlying poll() implementation |
| + | may not be the same as the ones in the public libcurl API! */ |
| + | for(i = 0; i < extra_nfds; i++) { |
| + | unsigned r = (unsigned)cpfds->pfds[curl_nfds + i].revents; |
| + | unsigned short mask = 0; |
| + | if(r & POLLIN) |
| + | mask |= CURL_WAIT_POLLIN; |
| + | if(r & POLLOUT) |
| + | mask |= CURL_WAIT_POLLOUT; |
| + | if(r & POLLPRI) |
| + | mask |= CURL_WAIT_POLLPRI; |
| + | extra_fds[i].revents = (short)mask; |
| + | } |
| + | } |
| + | else if(wait_on_nop) { |
| + | struct curltime expire_time; |
| + | long sleep_ms = 0; |
| + | |
| + | /* Avoid busy-looping when there is nothing particular to wait for */ |
| + | multi_timeout(multi, &expire_time, &sleep_ms); |
| + | if(sleep_ms) { |
| + | if(sleep_ms > timeout_ms) |
| + | sleep_ms = timeout_ms; |
| + | /* when there are no easy handles in the multi, this holds a -1 |
| + | timeout */ |
| + | else if(sleep_ms < 0) |
| + | sleep_ms = timeout_ms; |
| + | curlx_wait_ms(sleep_ms); |
| + | } |
| + | } |
| + | |
| + | out: |
| + | *pnevents = nevents; |
| + | return mresult; |
| + | } |
| + | |
| + | #endif /* !USE_WINSOCK */ |
| |
| #define NUM_POLLS_ON_STACK 10 |
| |
| @@ -1322,13 +1522,13 @@ static CURLMcode multi_wait(struct Curl_multi *multi, |
| unsigned int extra_nfds, |
| int timeout_ms, |
| int *ret, |
| - | bool extrawait, /* when no socket, wait */ |
| - | bool use_wakeup) |
| + | bool wait_on_nop) /* spend time, even if there |
| + | * is nothing to monitor */ |
| { |
| size_t i; |
| struct curltime expire_time; |
| long timeout_internal; |
| - | int retcode = 0; |
| + | int nevents = 0; |
| struct easy_pollset ps; |
| struct pollfd a_few_on_stack[NUM_POLLS_ON_STACK]; |
| struct curl_pollfds cpfds; |
| @@ -1336,13 +1536,8 @@ static CURLMcode multi_wait(struct Curl_multi *multi, |
| struct Curl_easy *data = NULL; |
| CURLMcode mresult = CURLM_OK; |
| uint32_t mid; |
| - | |
| - | #ifdef USE_WINSOCK |
| - | WSANETWORKEVENTS wsa_events; |
| - | DEBUGASSERT(multi->wsa_event != WSA_INVALID_EVENT); |
| - | #endif |
| - | #ifndef ENABLE_WAKEUP |
| - | (void)use_wakeup; |
| + | #ifdef ENABLE_WAKEUP |
| + | int wakeup_idx = -1; |
| #endif |
| |
| if(!GOOD_MULTI_HANDLE(multi)) |
| @@ -1380,6 +1575,16 @@ static CURLMcode multi_wait(struct Curl_multi *multi, |
| goto out; |
| } |
| |
| + | #ifdef ENABLE_WAKEUP |
| + | if(wait_on_nop && multi->wakeup_pair[0] != CURL_SOCKET_BAD) { |
| + | wakeup_idx = cpfds.n; |
| + | if(Curl_pollfds_add_sock(&cpfds, multi->wakeup_pair[0], POLLIN)) { |
| + | mresult = CURLM_OUT_OF_MEMORY; |
| + | goto out; |
| + | } |
| + | } |
| + | #endif |
| + | |
| curl_nfds = cpfds.n; /* what curl internally uses in cpfds */ |
| /* Add external file descriptions from poll-like struct curl_waitfd */ |
| for(i = 0; i < extra_nfds; i++) { |
| @@ -1396,38 +1601,6 @@ static CURLMcode multi_wait(struct Curl_multi *multi, |
| } |
| } |
| |
| - | #ifdef USE_WINSOCK |
| - | /* Set the WSA events based on the collected pollds */ |
| - | for(i = 0; i < cpfds.n; i++) { |
| - | long mask = 0; |
| - | if(cpfds.pfds[i].events & POLLIN) |
| - | mask |= FD_READ | FD_ACCEPT | FD_CLOSE; |
| - | if(cpfds.pfds[i].events & POLLPRI) |
| - | mask |= FD_OOB; |
| - | if(cpfds.pfds[i].events & POLLOUT) { |
| - | mask |= FD_WRITE | FD_CONNECT | FD_CLOSE; |
| - | reset_socket_fdwrite(cpfds.pfds[i].fd); |
| - | } |
| - | if(mask) { |
| - | if(WSAEventSelect(cpfds.pfds[i].fd, multi->wsa_event, mask) != 0) { |
| - | mresult = CURLM_OUT_OF_MEMORY; |
| - | goto out; |
| - | } |
| - | } |
| - | } |
| - | #endif |
| - | |
| - | #ifdef ENABLE_WAKEUP |
| - | #ifndef USE_WINSOCK |
| - | if(use_wakeup && multi->wakeup_pair[0] != CURL_SOCKET_BAD) { |
| - | if(Curl_pollfds_add_sock(&cpfds, multi->wakeup_pair[0], POLLIN)) { |
| - | mresult = CURLM_OUT_OF_MEMORY; |
| - | goto out; |
| - | } |
| - | } |
| - | #endif |
| - | #endif |
| - | |
| /* We check the internal timeout *AFTER* we collected all sockets to |
| * poll. Collecting the sockets may install new timers by protocols |
| * and connection filters. |
| @@ -1439,122 +1612,32 @@ static CURLMcode multi_wait(struct Curl_multi *multi, |
| if(data) |
| CURL_TRC_M(data, "multi_wait(fds=%d, timeout=%d) tinternal=%ld", |
| cpfds.n, timeout_ms, timeout_internal); |
| - | #if defined(ENABLE_WAKEUP) && defined(USE_WINSOCK) |
| - | if(cpfds.n || use_wakeup) { |
| - | #else |
| - | if(cpfds.n) { |
| - | #endif |
| - | int pollrc; |
| - | #ifdef USE_WINSOCK |
| - | if(cpfds.n) /* pre-check with Winsock */ |
| - | pollrc = Curl_poll(cpfds.pfds, cpfds.n, 0); |
| - | else |
| - | pollrc = 0; |
| - | #else |
| - | pollrc = Curl_poll(cpfds.pfds, cpfds.n, timeout_ms); /* wait... */ |
| - | #endif |
| - | if(pollrc < 0) { |
| - | mresult = CURLM_UNRECOVERABLE_POLL; |
| - | goto out; |
| - | } |
| |
| - | if(pollrc > 0) { |
| - | retcode = pollrc; |
| - | #ifdef USE_WINSOCK |
| - | } |
| - | else { /* now wait... if not ready during the pre-check (pollrc == 0) */ |
| - | WSAWaitForMultipleEvents(1, &multi->wsa_event, FALSE, (DWORD)timeout_ms, |
| - | FALSE); |
| - | } |
| - | /* With Winsock, we have to run the following section unconditionally |
| - | to call WSAEventSelect(fd, event, 0) on all the sockets */ |
| - | { |
| - | #endif |
| - | /* copy revents results from the poll to the curl_multi_wait poll |
| - | struct, the bit values of the actual underlying poll() implementation |
| - | may not be the same as the ones in the public libcurl API! */ |
| - | for(i = 0; i < extra_nfds; i++) { |
| - | unsigned r = (unsigned)cpfds.pfds[curl_nfds + i].revents; |
| - | unsigned short mask = 0; |
| #ifdef USE_WINSOCK |
| - | curl_socket_t s = extra_fds[i].fd; |
| - | wsa_events.lNetworkEvents = 0; |
| - | if(WSAEnumNetworkEvents(s, NULL, &wsa_events) == 0) { |
| - | if(wsa_events.lNetworkEvents & (FD_READ | FD_ACCEPT | FD_CLOSE)) |
| - | mask |= CURL_WAIT_POLLIN; |
| - | if(wsa_events.lNetworkEvents & (FD_WRITE | FD_CONNECT | FD_CLOSE)) |
| - | mask |= CURL_WAIT_POLLOUT; |
| - | if(wsa_events.lNetworkEvents & FD_OOB) |
| - | mask |= CURL_WAIT_POLLPRI; |
| - | if(ret && !pollrc && wsa_events.lNetworkEvents) |
| - | retcode++; |
| - | } |
| - | WSAEventSelect(s, multi->wsa_event, 0); |
| - | if(!pollrc) { |
| - | extra_fds[i].revents = (short)mask; |
| - | continue; |
| - | } |
| + | mresult = multi_winsock_select(multi, &cpfds, curl_nfds, |
| + | extra_fds, extra_nfds, |
| + | timeout_ms, wait_on_nop, &nevents); |
| + | #else |
| + | mresult = multi_posix_poll(multi, &cpfds, curl_nfds, |
| + | extra_fds, extra_nfds, |
| + | timeout_ms, wait_on_nop, &nevents); |
| #endif |
| - | if(r & POLLIN) |
| - | mask |= CURL_WAIT_POLLIN; |
| - | if(r & POLLOUT) |
| - | mask |= CURL_WAIT_POLLOUT; |
| - | if(r & POLLPRI) |
| - | mask |= CURL_WAIT_POLLPRI; |
| - | extra_fds[i].revents = (short)mask; |
| - | } |
| |
| - | #ifdef USE_WINSOCK |
| - | /* Count up all our own sockets that had activity, |
| - | and remove them from the event. */ |
| - | for(i = 0; i < curl_nfds; ++i) { |
| - | wsa_events.lNetworkEvents = 0; |
| - | if(WSAEnumNetworkEvents(cpfds.pfds[i].fd, NULL, &wsa_events) == 0) { |
| - | if(ret && !pollrc && wsa_events.lNetworkEvents) |
| - | retcode++; |
| - | } |
| - | WSAEventSelect(cpfds.pfds[i].fd, multi->wsa_event, 0); |
| - | } |
| - | WSAResetEvent(multi->wsa_event); |
| - | #else |
| #ifdef ENABLE_WAKEUP |
| - | if(use_wakeup && multi->wakeup_pair[0] != CURL_SOCKET_BAD) { |
| - | if(cpfds.pfds[curl_nfds + extra_nfds].revents & POLLIN) { |
| - | (void)Curl_wakeup_consume(multi->wakeup_pair, TRUE); |
| - | /* do not count the wakeup socket into the returned value */ |
| - | retcode--; |
| - | } |
| + | if(nevents && (wakeup_idx >= 0)) { |
| + | if(cpfds.pfds[wakeup_idx].revents & POLLIN) { |
| + | (void)Curl_wakeup_consume(multi->wakeup_pair, TRUE); |
| + | /* do not count the wakeup socket into the returned value */ |
| + | nevents--; |
| } |
| - | #endif |
| - | #endif |
| } |
| - | } |
| - | |
| - | if(ret) |
| - | *ret = retcode; |
| - | #if defined(ENABLE_WAKEUP) && defined(USE_WINSOCK) |
| - | if(extrawait && !cpfds.n && !use_wakeup) { |
| - | #else |
| - | if(extrawait && !cpfds.n) { |
| #endif |
| - | long sleep_ms = 0; |
| - | |
| - | /* Avoid busy-looping when there is nothing particular to wait for */ |
| - | multi_timeout(multi, &expire_time, &sleep_ms); |
| - | if(sleep_ms) { |
| - | if(sleep_ms > timeout_ms) |
| - | sleep_ms = timeout_ms; |
| - | /* when there are no easy handles in the multi, this holds a -1 |
| - | timeout */ |
| - | else if(sleep_ms < 0) |
| - | sleep_ms = timeout_ms; |
| - | curlx_wait_ms(sleep_ms); |
| - | } |
| - | } |
| |
| out: |
| Curl_pollset_cleanup(&ps); |
| Curl_pollfds_cleanup(&cpfds); |
| + | if(ret) |
| + | *ret = nevents; |
| return mresult; |
| } |
| |
| @@ -1564,8 +1647,7 @@ CURLMcode curl_multi_wait(CURLM *multi, |
| int timeout_ms, |
| int *ret) |
| { |
| - | return multi_wait(multi, extra_fds, extra_nfds, timeout_ms, ret, FALSE, |
| - | FALSE); |
| + | return multi_wait(multi, extra_fds, extra_nfds, timeout_ms, ret, FALSE); |
| } |
| |
| CURLMcode curl_multi_poll(CURLM *multi, |
| @@ -1574,7 +1656,7 @@ CURLMcode curl_multi_poll(CURLM *multi, |
| int timeout_ms, |
| int *ret) |
| { |
| - | return multi_wait(multi, extra_fds, extra_nfds, timeout_ms, ret, TRUE, TRUE); |
| + | return multi_wait(multi, extra_fds, extra_nfds, timeout_ms, ret, TRUE); |
| } |
| |
| CURLMcode curl_multi_wakeup(CURLM *m) |
| @@ -1588,11 +1670,11 @@ CURLMcode curl_multi_wakeup(CURLM *m) |
| if(!GOOD_MULTI_HANDLE(multi)) |
| return CURLM_BAD_HANDLE; |
| |
| - | #ifdef ENABLE_WAKEUP |
| #ifdef USE_WINSOCK |
| if(WSASetEvent(multi->wsa_event)) |
| return CURLM_OK; |
| - | #else |
| + | #endif |
| + | #ifdef ENABLE_WAKEUP |
| /* the wakeup_pair variable is only written during init and cleanup, |
| making it safe to access from another thread after the init part |
| and before cleanup */ |
| @@ -1601,7 +1683,6 @@ CURLMcode curl_multi_wakeup(CURLM *m) |
| return CURLM_WAKEUP_FAILURE; |
| return CURLM_OK; |
| } |
| - | #endif |
| #endif |
| return CURLM_WAKEUP_FAILURE; |
| } |
| @@ -2408,6 +2489,8 @@ static void handle_completed(struct Curl_multi *multi, |
| Curl_uint32_bset_remove(&multi->pending, data->mid); |
| Curl_uint32_bset_add(&multi->msgsent, data->mid); |
| --multi->xfers_alive; |
| + | if(!multi->xfers_alive) |
| + | multi_assess_wakeup(multi); |
| } |
| |
| static CURLMcode multi_runsingle(struct Curl_multi *multi, |
| @@ -2890,10 +2973,9 @@ CURLMcode curl_multi_cleanup(CURLM *m) |
| |
| #ifdef USE_WINSOCK |
| WSACloseEvent(multi->wsa_event); |
| - | #else |
| + | #endif |
| #ifdef ENABLE_WAKEUP |
| Curl_wakeup_destroy(multi->wakeup_pair); |
| - | #endif |
| #endif |
| |
| multi_xfer_bufs_free(multi); |
| @@ -3132,6 +3214,14 @@ static CURLMcode multi_socket(struct Curl_multi *multi, |
| handles the case when the application asks libcurl to run the timeout |
| prematurely. */ |
| memset(&multi->last_expire_ts, 0, sizeof(multi->last_expire_ts)); |
| + | |
| + | /* Applications may set `socket_cb` *after* having added transfers |
| + | * first. *Then* kick off processing with a |
| + | * curl_multi_socket_action(TIMEOUT) afterwards. Make sure our |
| + | * admin handle registers its pollset with the callbacks present. */ |
| + | mresult = multi_assess_wakeup(multi); |
| + | if(mresult) |
| + | goto out; |
| } |
| |
| multi_mark_expired_as_dirty(multi, multi_now(multi)); |