From b61c06384ab88baf4b3231e84386c4a70126d888 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 12 Oct 2006 08:36:47 +0000 Subject: Jeff Pohlmeyer has been working with the hiperfifo.c example source code, and while doing so it became apparent that the current timeout system for the socket API really was a bit awkward since it become quite some work to be sure we have the correct timeout set. Jeff then provided the new CURLMOPT_TIMERFUNCTION that is yet another callback the app can set to get to know when the general timeout time changes and thus for an application like hiperfifo.c it makes everything a lot easier and nicer. There's a CURLMOPT_TIMERDATA option too of course in good old libcurl tradition. --- lib/multi.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 68 insertions(+), 12 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index e901bddfd..aaa80b228 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -166,6 +166,12 @@ struct Curl_multi { /* list of easy handles kept around for doing nice connection closures */ struct closure *closure; + + /* timer callback and user data pointer for the *socket() API */ + curl_multi_timer_callback timer_cb; + void *timer_userp; + time_t timer_lastcall; /* the fixed time for the timeout for the previous + callback */ }; static bool multi_conn_using(struct Curl_multi *multi, @@ -174,6 +180,7 @@ static void singlesocket(struct Curl_multi *multi, struct Curl_one_easy *easy); static void add_closure(struct Curl_multi *multi, struct SessionHandle *data); +static int update_timer(struct Curl_multi *multi); /* always use this function to change state, to make debugging easier */ static void multistate(struct Curl_one_easy *easy, CURLMstate state) @@ -460,6 +467,7 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle, /* increase the alive-counter */ multi->num_alive++; + update_timer(multi); return CURLM_OK; } @@ -610,6 +618,7 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, multi->num_easy--; /* one less to care about now */ + update_timer(multi); return CURLM_OK; } else @@ -1342,6 +1351,8 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) *running_handles = multi->num_alive; + if ( CURLM_OK == returncode ) + update_timer(multi); return returncode; } @@ -1673,8 +1684,15 @@ CURLMcode curl_multi_setopt(CURLM *multi_handle, case CURLMOPT_PIPELINING: multi->pipelining_enabled = (bool)(0 != va_arg(param, long)); break; + case CURLMOPT_TIMERFUNCTION: + multi->timer_cb = va_arg(param, curl_multi_timer_callback); + break; + case CURLMOPT_TIMERDATA: + multi->timer_userp = va_arg(param, void *); + break; default: res = CURLM_UNKNOWN_OPTION; + break; } va_end(param); return res; @@ -1684,26 +1702,26 @@ CURLMcode curl_multi_setopt(CURLM *multi_handle, CURLMcode curl_multi_socket(CURLM *multi_handle, curl_socket_t s, int *running_handles) { - return multi_socket((struct Curl_multi *)multi_handle, FALSE, s, - running_handles); + CURLMcode result = multi_socket((struct Curl_multi *)multi_handle, FALSE, s, + running_handles); + if (CURLM_OK == result) + update_timer((struct Curl_multi *)multi_handle); + return result; } CURLMcode curl_multi_socket_all(CURLM *multi_handle, int *running_handles) { - return multi_socket((struct Curl_multi *)multi_handle, - TRUE, CURL_SOCKET_BAD, running_handles); + CURLMcode result = multi_socket((struct Curl_multi *)multi_handle, + TRUE, CURL_SOCKET_BAD, running_handles); + if (CURLM_OK == result) + update_timer((struct Curl_multi *)multi_handle); + return result; } -CURLMcode curl_multi_timeout(CURLM *multi_handle, - long *timeout_ms) +static CURLMcode multi_timeout(struct Curl_multi *multi, + long *timeout_ms) { - struct Curl_multi *multi=(struct Curl_multi *)multi_handle; - - /* First, make some basic checks that the CURLM handle is a good handle */ - if(!GOOD_MULTI_HANDLE(multi)) - return CURLM_BAD_HANDLE; - if(multi->timetree) { /* we have a tree of expire times */ struct timeval now = Curl_tvnow(); @@ -1724,6 +1742,44 @@ CURLMcode curl_multi_timeout(CURLM *multi_handle, return CURLM_OK; } +CURLMcode curl_multi_timeout(CURLM *multi_handle, + long *timeout_ms) +{ + struct Curl_multi *multi=(struct Curl_multi *)multi_handle; + + /* First, make some basic checks that the CURLM handle is a good handle */ + if(!GOOD_MULTI_HANDLE(multi)) + return CURLM_BAD_HANDLE; + + return multi_timeout(multi, timeout_ms); +} + +/* + * Tell the application it should update its timers, if it subscribes to the + * update timer callback. + */ +static int update_timer(struct Curl_multi *multi) +{ + long timeout_ms; + if (!multi->timer_cb) + return 0; + if ( multi_timeout(multi, &timeout_ms) != CURLM_OK ) + return -1; + if ( timeout_ms < 0 ) + return 0; + + /* When multi_timeout() is done, multi->timetree points to the node with the + * timeout we got the (relative) time-out time for. We can thus easily check + * if this is the same (fixed) time as we got in a previous call and then + * avoid calling the callback again. */ + if(multi->timetree->key == multi->timer_lastcall) + return 0; + + multi->timer_lastcall = multi->timetree->key; + + return multi->timer_cb((CURLM*)multi, timeout_ms, multi->timer_userp); +} + /* given a number of milliseconds from now to use to set the 'act before this'-time for the transfer, to be extracted by curl_multi_timeout() */ void Curl_expire(struct SessionHandle *data, long milli) -- cgit v1.2.3