diff --git a/CHANGES b/CHANGES index f572e7ef5..7eabba8d4 100644 --- a/CHANGES +++ b/CHANGES @@ -6,6 +6,20 @@ Changelog +Daniel (12 October 2006) +- 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. + + Jeff has also updated the hiperfifo.c example code to use this news. + Daniel (9 October 2006) - Bogdan Nicula's second test case (posted Sun, 08 Oct 2006) converted to test case 535 and it now runs fine. Again a problem with the pipelining code not diff --git a/RELEASE-NOTES b/RELEASE-NOTES index 950784f23..5b70767b9 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -11,6 +11,7 @@ Curl and libcurl 7.16.0 This release includes the following changes: + o Added CURLMOPT_TIMERFUNCTION o The CURLOPT_SOURCE_* options are removed and so are the --3p* command line options o curl_multi_socket() and family are suitable to start using diff --git a/TODO-RELEASE b/TODO-RELEASE index 03ffa88be..7d0a1e38f 100644 --- a/TODO-RELEASE +++ b/TODO-RELEASE @@ -1,8 +1,6 @@ To get fixed in 7.16.0 (planned release: October 2006) ====================== -67 - Jeff Pohlmeyer's test case +67 - Jeff Pohlmeyer's crashing pipelining test case -68 - Dmitriy Sergeyev's test02.c and test03.c - -69 - \ No newline at end of file +69 - diff --git a/docs/libcurl/curl_multi_setopt.3 b/docs/libcurl/curl_multi_setopt.3 index 5279055ce..5ba81b952 100644 --- a/docs/libcurl/curl_multi_setopt.3 +++ b/docs/libcurl/curl_multi_setopt.3 @@ -1,6 +1,6 @@ .\" $Id$ .\" -.TH curl_multi_setopt 3 "8 Jan 2006" "libcurl 7.16.0" "libcurl Manual" +.TH curl_multi_setopt 3 "10 Oct 2006" "libcurl 7.16.0" "libcurl Manual" .SH NAME curl_multi_setopt \- set options for a curl multi handle .SH SYNOPSIS @@ -9,7 +9,7 @@ curl_multi_setopt \- set options for a curl multi handle CURLMcode curl_multi_setopt(CURLM * multi_handle, CURLMoption option, param); .SH DESCRIPTION curl_multi_setopt() is used to tell a libcurl multi handle how to behave. By -using the appropriate options to \fIcurl_multi_setopt\fP, you can change +using the appropriate options to \fIcurl_multi_setopt(3)\fP, you can change libcurl's behaviour when using that multi handle. All options are set with the \fIoption\fP followed by the parameter \fIparam\fP. That parameter can be a \fBlong\fP, a \fBfunction pointer\fP, an \fBobject pointer\fP or a @@ -19,19 +19,20 @@ You can only set one option in each function call. .SH OPTIONS .IP CURLMOPT_SOCKETFUNCTION -Pass a pointer to a function matching the curl_socket_callback prototype. The -\fIcurl_multi_socket(3)\fP functions inform the application about updates in -the socket (file descriptor) status by doing none, one or multiple calls to -the curl_socket_callback given in the \fBparam\fP argument. They update the -status with changes since the previous time a \fIcurl_multi_socket(3)\fP -function was called. If the given callback pointer is NULL, no callback will -be called. Set the callback's \fBuserp\fP argument with -\fICURLMOPT_SOCKETDATA\fP. See \fIcurl_multi_socket(3)\fP for more callback -details. +Pass a pointer to a function matching the \fBcurl_socket_callback\fP +prototype. The \fIcurl_multi_socket(3)\fP functions inform the application +about updates in the socket (file descriptor) status by doing none, one or +multiple calls to the curl_socket_callback given in the \fBparam\fP +argument. They update the status with changes since the previous time a +\fIcurl_multi_socket(3)\fP function was called. If the given callback pointer +is NULL, no callback will be called. Set the callback's \fBuserp\fP argument +with \fICURLMOPT_SOCKETDATA\fP. See \fIcurl_multi_socket(3)\fP for more +callback details. .IP CURLMOPT_SOCKETDATA -Pass a pointer to whatever you want passed to the curl_socket_callback's forth -argument, the userp pointer. This is not used by libcurl but only passed-thru -as-is. Set the callback pointer with \fICURLMOPT_SOCKETFUNCTION\fP. +Pass a pointer to whatever you want passed to the \fBcurl_socket_callback\fP's +forth argument, the userp pointer. This is not used by libcurl but only +passed-thru as-is. Set the callback pointer with +\fICURLMOPT_SOCKETFUNCTION\fP. .IP CURLMOPT_PIPELINING Pass a long set to 1 to enable or 0 to disable. Enabling pipelining on a multi handle will make it attempt to perform HTTP Pipelining as far as possible for @@ -39,6 +40,22 @@ transfers using this handle. This means that if you add a second request that can use an already existing connection, the second request will be \&"piped" on the same connection rather than being executed in parallell. (Added in 7.16.0) +.IP CURLMOPT_TIMERFUNCTION +Pass a pointer to a function matching the \fBcurl_multi_timer_callback\fP +prototype. This function will then be called when the timeout value +changes. The timeout value is at what latest time the application should call +one of the \&"performing" functions of the multi interface +(\fIcurl_multi_socket(3)\fP, \fIcurl_multi_socket_all(3)\fP and +\fIcurl_multi_perform(3)\fP) - to allow libcurl to keep timeouts and retries +etc to work. Libcurl attempts to limit calling this only when the fixed future +timeout time actually change. See also \fICURLMOPT_TIMERDATA\fP. This callback +can be used instead of, or in addition to, \fIcurl_multi_timeout(3)\fP. (Added +in 7.16.0) +.IP CURLMOPT_TIMERDATA +Pass a pointer to whatever you want passed to the +\fBcurl_multi_timer_callback\fP's third argument, the userp pointer. This is +not used by libcurl but only passed-thru as-is. Set the callback pointer with +\fICURLMOPT_TIMERFUNCTION\fP. (Added in 7.16.0) .SH RETURNS The standard CURLMcode for multi interface error codes. Note that it returns a CURLM_UNKNOWN_OPTION if you try setting an option that this version of libcurl diff --git a/include/curl/multi.h b/include/curl/multi.h index 23fe180d3..d2533728d 100644 --- a/include/curl/multi.h +++ b/include/curl/multi.h @@ -231,6 +231,20 @@ typedef int (*curl_socket_callback)(CURL *easy, /* easy handle */ pointer */ void *socketp); /* private socket pointer */ +/* + * Name: curl_multi_timer_callback + * + * Desc: Called by libcurl whenever the library detects a change in the + * maximum number of milliseconds the app is allowed to wait before + * curl_multi_socket() or curl_multi_perform() must be called + * (to allow libcurl's timed events to take place). + * + * Returns: The callback should return zero. + */ +typedef int (*curl_multi_timer_callback)(CURLM *multi, /* multi handle */ + long timeout_ms, /* see above */ + void *userp); /* private callback + pointer */ CURL_EXTERN CURLMcode curl_multi_socket(CURLM *multi_handle, curl_socket_t s, int *running_handles); @@ -273,6 +287,12 @@ typedef enum { /* set to 1 to enable pipelining for this multi handle */ CINIT(PIPELINING, LONG, 3), + /* This is the timer callback function pointer */ + CINIT(TIMERFUNCTION, FUNCTIONPOINT, 4), + + /* This is the argument passed to the timer callback */ + CINIT(TIMERDATA, OBJECTPOINT, 5), + CURLMOPT_LASTENTRY /* the last unused */ } CURLMoption; 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)