From df6f3ae60ab915e0993d435f416ac763c5095255 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 12 Feb 2026 16:12:06 +0100 Subject: [PATCH] multi: split multi_runsingle into sub functions To reduce complexity. - is_finished() checks if the individual transfer is done - handle_completed() is the logic that runs for a completed transfer Closes #20573 --- lib/multi.c | 192 ++++++++++++++++++++++++++++------------------------ 1 file changed, 105 insertions(+), 87 deletions(-) diff --git a/lib/multi.c b/lib/multi.c index 9420e3f940..66456fe928 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -2323,11 +2323,111 @@ static CURLMcode state_connect(struct Curl_multi *multi, return mresult; } +/* returns the possibly updated result */ +static CURLcode is_finished(struct Curl_multi *multi, + struct Curl_easy *data, + bool stream_error, + CURLcode result) +{ + if(data->mstate < MSTATE_COMPLETED) { + if(result) { + /* + * If an error was returned, and we are not in completed state now, + * then we go to completed and consider this transfer aborted. + */ + + /* No attempt to disconnect connections must be made before this - + connection detach and termination happens only here */ + + /* Check if we can move pending requests to send pipe */ + process_pending_handles(multi); /* connection */ + + if(data->conn) { + if(stream_error) { + /* Do not attempt to send data over a connection that timed out */ + bool dead_connection = result == CURLE_OPERATION_TIMEDOUT; + struct connectdata *conn = data->conn; + + /* This is where we make sure that the conn pointer is reset. + We do not have to do this in every case block above where a + failure is detected */ + Curl_detach_connection(data); + Curl_conn_terminate(data, conn, dead_connection); + } + } + else if(data->mstate == MSTATE_CONNECT) { + /* Curl_connect() failed */ + multi_posttransfer(data); + Curl_pgrsUpdate_nometer(data); + } + + multistate(data, MSTATE_COMPLETED); + return result; + } + /* if there is still a connection to use, call the progress function */ + else if(data->conn) { + result = Curl_pgrsUpdate(data); + if(result) { + /* aborted due to progress callback return code must close the + connection */ + streamclose(data->conn, "Aborted by callback"); + + /* if not yet in DONE state, go there, otherwise COMPLETED */ + multistate(data, (data->mstate < MSTATE_DONE) ? + MSTATE_DONE : MSTATE_COMPLETED); + return result; + } + } + } + return result; +} + +static void handle_completed(struct Curl_multi *multi, + struct Curl_easy *data, + CURLcode result) +{ + if(data->master_mid != UINT32_MAX) { + /* A sub transfer, not for msgsent to application */ + struct Curl_easy *mdata; + + CURL_TRC_M(data, "sub xfer done for master %u", data->master_mid); + mdata = Curl_multi_get_easy(multi, data->master_mid); + if(mdata) { + if(mdata->sub_xfer_done) + mdata->sub_xfer_done(mdata, data, result); + else + CURL_TRC_M(data, "master easy %u without sub_xfer_done callback.", + data->master_mid); + } + else { + CURL_TRC_M(data, "master easy %u already gone.", data->master_mid); + } + } + else { + /* now fill in the Curl_message with this info */ + struct Curl_message *msg = &data->msg; + + msg->extmsg.msg = CURLMSG_DONE; + msg->extmsg.easy_handle = data; + msg->extmsg.data.result = result; + + multi_addmsg(multi, msg); + DEBUGASSERT(!data->conn); + } + multistate(data, MSTATE_MSGSENT); + + /* remove from the other sets, add to msgsent */ + Curl_uint32_bset_remove(&multi->process, data->mid); + Curl_uint32_bset_remove(&multi->dirty, data->mid); + Curl_uint32_bset_remove(&multi->pending, data->mid); + Curl_uint32_bset_add(&multi->msgsent, data->mid); + --multi->xfers_alive; +} + static CURLMcode multi_runsingle(struct Curl_multi *multi, struct Curl_easy *data, struct Curl_sigpipe_ctx *sigpipe_ctx) { - struct Curl_message *msg = NULL; bool connected; bool protocol_connected = FALSE; bool dophase_done = FALSE; @@ -2629,94 +2729,12 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, statemachine_end: - if(data->mstate < MSTATE_COMPLETED) { - if(result) { - /* - * If an error was returned, and we are not in completed state now, - * then we go to completed and consider this transfer aborted. - */ - - /* NOTE: no attempt to disconnect connections must be made - in the case blocks above - cleanup happens only here */ - - /* Check if we can move pending requests to send pipe */ - process_pending_handles(multi); /* connection */ - - if(data->conn) { - if(stream_error) { - /* Do not attempt to send data over a connection that timed out */ - bool dead_connection = result == CURLE_OPERATION_TIMEDOUT; - struct connectdata *conn = data->conn; - - /* This is where we make sure that the conn pointer is reset. - We do not have to do this in every case block above where a - failure is detected */ - Curl_detach_connection(data); - Curl_conn_terminate(data, conn, dead_connection); - } - } - else if(data->mstate == MSTATE_CONNECT) { - /* Curl_connect() failed */ - multi_posttransfer(data); - Curl_pgrsUpdate_nometer(data); - } - - multistate(data, MSTATE_COMPLETED); - mresult = CURLM_CALL_MULTI_PERFORM; - } - /* if there is still a connection to use, call the progress function */ - else if(data->conn) { - result = Curl_pgrsUpdate(data); - if(result) { - /* aborted due to progress callback return code must close the - connection */ - streamclose(data->conn, "Aborted by callback"); - - /* if not yet in DONE state, go there, otherwise COMPLETED */ - multistate(data, (data->mstate < MSTATE_DONE) ? - MSTATE_DONE : MSTATE_COMPLETED); - mresult = CURLM_CALL_MULTI_PERFORM; - } - } - } + result = is_finished(multi, data, stream_error, result); + if(result) + mresult = CURLM_CALL_MULTI_PERFORM; if(MSTATE_COMPLETED == data->mstate) { - if(data->master_mid != UINT32_MAX) { - /* A sub transfer, not for msgsent to application */ - struct Curl_easy *mdata; - - CURL_TRC_M(data, "sub xfer done for master %u", data->master_mid); - mdata = Curl_multi_get_easy(multi, data->master_mid); - if(mdata) { - if(mdata->sub_xfer_done) - mdata->sub_xfer_done(mdata, data, result); - else - CURL_TRC_M(data, "master easy %u without sub_xfer_done callback.", - data->master_mid); - } - else { - CURL_TRC_M(data, "master easy %u already gone.", data->master_mid); - } - } - else { - /* now fill in the Curl_message with this info */ - msg = &data->msg; - - msg->extmsg.msg = CURLMSG_DONE; - msg->extmsg.easy_handle = data; - msg->extmsg.data.result = result; - - multi_addmsg(multi, msg); - DEBUGASSERT(!data->conn); - } - multistate(data, MSTATE_MSGSENT); - - /* remove from the other sets, add to msgsent */ - Curl_uint32_bset_remove(&multi->process, data->mid); - Curl_uint32_bset_remove(&multi->dirty, data->mid); - Curl_uint32_bset_remove(&multi->pending, data->mid); - Curl_uint32_bset_add(&multi->msgsent, data->mid); - --multi->xfers_alive; + handle_completed(multi, data, result); return CURLM_OK; } } while((mresult == CURLM_CALL_MULTI_PERFORM) ||