cURL / Mailing Lists / curl-library / Single Mail

curl-library

curl_multi_perform will never return CURLM_CALL_MULTI_PERFORM (and suggested fix)

From: Miguel Parra <miguelvp_at_msn.com>
Date: Fri, 3 Aug 2012 11:08:46 -0500

Since 7.20 a do-while loop has been added when processing each easy
handle held by the multi handle.

For real time applications it's important to be able to just do a single
multi_runsingle per easy handle and allow the calling application to
decide if to call curl_multi_perform again if it has cycles left for
that frame.

I did check on 7.27 and the problem is still in there but this code
snippet is from 7.21.7

If you look at the inner do-while loop as it is now, you can see
curl_multi_performwill never return CURLM_CALL_MULTI_PERFORM, since each
easy handle that returns that will cause the inner do_while loop to
execute again.

     CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles)
     {
       struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
       struct Curl_one_easy *easy;
       CURLMcode returncode=CURLM_OK;
     ...
       struct timeval now = Curl_tvnow();
     ...
       easy=multi->easy.next;
       while(easy != &multi->easy) {
     ...
         do
           result = multi_runsingle(multi, now, easy);
         while(CURLM_CALL_MULTI_PERFORM == result);
     ...
         if(result)
           returncode = result;

         easy = easy->next; /* operate on next handle */
       }
     ...
       return returncode;
     }

Pre 7.20, the do_while loop was not present, but there was a bug since
the returned value would have been the last non CURLM_OK on the multi
handle list of easy handles.

My suggestion will be to change the code so it does a single
multi_runsingle call per easy_handle and returning
CURLM_CALL_MULTI_PERFORM if any of the easy handles needs to be called back.

I also added code to store the last non CURLM_OK nor
CURLM_CALL_MULTI_PERFORM, so if you want to prioritize returning errors
over CURLM_OK or CURLM_CALL_MULTI_PERFORM the code will look like this:

     CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles)
     {
       struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
       struct Curl_one_easy *easy;
       CURLMcode returncode=CURLM_OK;
       CURLMcode lasterrorcode = CURLM_OK;
     ...
       struct timeval now = Curl_tvnow();
     ...
       easy=multi->easy.next;
       while(easy != &multi->easy) {
     ...
         result = multi_runsingle(multi, now, easy); /* removed the
do_while loop */
     ...
         if(CURLM_CALL_MULTI_PERFORM == result)
           returncode = result;
         else if(CURLM_OK != result)
           lasterrorcode = result;

         easy = easy->next; /* operate on next handle */
       }
     ...
       return (CURLM_OK!=lasterrorcode)?lasterrorcode:returncode;
     }

if you want to allow other easy handles to complete first before
reporting the error thenchange the return to:

return (CURLM_OK != lasterrorcode && CURLM_CALL_MULTI_PERFORM !=
returncode)?lasterrorcode:returncode;

I think these changes will make the original intent of
CURLM_CALL_MULTI_PERFORM be of relevance once more, specially for
real-time applications that want a tighter resource control.

BTW I have another code change suggestion regarding
CURLOPT_MAX_RECV_SPEED_LARGE unintended behavior but i'll send a
separate report.

Thanks,

Miguel

-------------------------------------------------------------------
List admin: http://cool.haxx.se/list/listinfo/curl-library
Etiquette: http://curl.haxx.se/mail/etiquette.html
Received on 2012-08-03