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.
This commit is contained in:
Daniel Stenberg 2006-10-12 08:36:47 +00:00
parent e7742bfb7c
commit b61c06384a
6 changed files with 136 additions and 30 deletions

14
CHANGES
View File

@ -6,6 +6,20 @@
Changelog 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) Daniel (9 October 2006)
- Bogdan Nicula's second test case (posted Sun, 08 Oct 2006) converted to test - 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 case 535 and it now runs fine. Again a problem with the pipelining code not

View File

@ -11,6 +11,7 @@ Curl and libcurl 7.16.0
This release includes the following changes: This release includes the following changes:
o Added CURLMOPT_TIMERFUNCTION
o The CURLOPT_SOURCE_* options are removed and so are the --3p* command line o The CURLOPT_SOURCE_* options are removed and so are the --3p* command line
options options
o curl_multi_socket() and family are suitable to start using o curl_multi_socket() and family are suitable to start using

View File

@ -1,8 +1,6 @@
To get fixed in 7.16.0 (planned release: October 2006) 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 -
69 -

View File

@ -1,6 +1,6 @@
.\" $Id$ .\" $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 .SH NAME
curl_multi_setopt \- set options for a curl multi handle curl_multi_setopt \- set options for a curl multi handle
.SH SYNOPSIS .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); CURLMcode curl_multi_setopt(CURLM * multi_handle, CURLMoption option, param);
.SH DESCRIPTION .SH DESCRIPTION
curl_multi_setopt() is used to tell a libcurl multi handle how to behave. By 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 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 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 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 .SH OPTIONS
.IP CURLMOPT_SOCKETFUNCTION .IP CURLMOPT_SOCKETFUNCTION
Pass a pointer to a function matching the curl_socket_callback prototype. The Pass a pointer to a function matching the \fBcurl_socket_callback\fP
\fIcurl_multi_socket(3)\fP functions inform the application about updates in prototype. The \fIcurl_multi_socket(3)\fP functions inform the application
the socket (file descriptor) status by doing none, one or multiple calls to about updates in the socket (file descriptor) status by doing none, one or
the curl_socket_callback given in the \fBparam\fP argument. They update the multiple calls to the curl_socket_callback given in the \fBparam\fP
status with changes since the previous time a \fIcurl_multi_socket(3)\fP argument. They update the status with changes since the previous time a
function was called. If the given callback pointer is NULL, no callback will \fIcurl_multi_socket(3)\fP function was called. If the given callback pointer
be called. Set the callback's \fBuserp\fP argument with is NULL, no callback will be called. Set the callback's \fBuserp\fP argument
\fICURLMOPT_SOCKETDATA\fP. See \fIcurl_multi_socket(3)\fP for more callback with \fICURLMOPT_SOCKETDATA\fP. See \fIcurl_multi_socket(3)\fP for more
details. callback details.
.IP CURLMOPT_SOCKETDATA .IP CURLMOPT_SOCKETDATA
Pass a pointer to whatever you want passed to the curl_socket_callback's forth Pass a pointer to whatever you want passed to the \fBcurl_socket_callback\fP's
argument, the userp pointer. This is not used by libcurl but only passed-thru forth argument, the userp pointer. This is not used by libcurl but only
as-is. Set the callback pointer with \fICURLMOPT_SOCKETFUNCTION\fP. passed-thru as-is. Set the callback pointer with
\fICURLMOPT_SOCKETFUNCTION\fP.
.IP CURLMOPT_PIPELINING .IP CURLMOPT_PIPELINING
Pass a long set to 1 to enable or 0 to disable. Enabling pipelining on a multi 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 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" can use an already existing connection, the second request will be \&"piped"
on the same connection rather than being executed in parallell. (Added in on the same connection rather than being executed in parallell. (Added in
7.16.0) 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 .SH RETURNS
The standard CURLMcode for multi interface error codes. Note that it returns a 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 CURLM_UNKNOWN_OPTION if you try setting an option that this version of libcurl

View File

@ -231,6 +231,20 @@ typedef int (*curl_socket_callback)(CURL *easy, /* easy handle */
pointer */ pointer */
void *socketp); /* private socket void *socketp); /* private socket
pointer */ 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, CURL_EXTERN CURLMcode curl_multi_socket(CURLM *multi_handle, curl_socket_t s,
int *running_handles); int *running_handles);
@ -273,6 +287,12 @@ typedef enum {
/* set to 1 to enable pipelining for this multi handle */ /* set to 1 to enable pipelining for this multi handle */
CINIT(PIPELINING, LONG, 3), 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 */ CURLMOPT_LASTENTRY /* the last unused */
} CURLMoption; } CURLMoption;

View File

@ -166,6 +166,12 @@ struct Curl_multi {
/* list of easy handles kept around for doing nice connection closures */ /* list of easy handles kept around for doing nice connection closures */
struct closure *closure; 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, 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); struct Curl_one_easy *easy);
static void add_closure(struct Curl_multi *multi, static void add_closure(struct Curl_multi *multi,
struct SessionHandle *data); struct SessionHandle *data);
static int update_timer(struct Curl_multi *multi);
/* always use this function to change state, to make debugging easier */ /* always use this function to change state, to make debugging easier */
static void multistate(struct Curl_one_easy *easy, CURLMstate state) 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 */ /* increase the alive-counter */
multi->num_alive++; multi->num_alive++;
update_timer(multi);
return CURLM_OK; return CURLM_OK;
} }
@ -610,6 +618,7 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle,
multi->num_easy--; /* one less to care about now */ multi->num_easy--; /* one less to care about now */
update_timer(multi);
return CURLM_OK; return CURLM_OK;
} }
else else
@ -1342,6 +1351,8 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles)
*running_handles = multi->num_alive; *running_handles = multi->num_alive;
if ( CURLM_OK == returncode )
update_timer(multi);
return returncode; return returncode;
} }
@ -1673,8 +1684,15 @@ CURLMcode curl_multi_setopt(CURLM *multi_handle,
case CURLMOPT_PIPELINING: case CURLMOPT_PIPELINING:
multi->pipelining_enabled = (bool)(0 != va_arg(param, long)); multi->pipelining_enabled = (bool)(0 != va_arg(param, long));
break; 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: default:
res = CURLM_UNKNOWN_OPTION; res = CURLM_UNKNOWN_OPTION;
break;
} }
va_end(param); va_end(param);
return res; return res;
@ -1684,26 +1702,26 @@ CURLMcode curl_multi_setopt(CURLM *multi_handle,
CURLMcode curl_multi_socket(CURLM *multi_handle, curl_socket_t s, CURLMcode curl_multi_socket(CURLM *multi_handle, curl_socket_t s,
int *running_handles) int *running_handles)
{ {
return multi_socket((struct Curl_multi *)multi_handle, FALSE, s, CURLMcode result = multi_socket((struct Curl_multi *)multi_handle, FALSE, s,
running_handles); 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) CURLMcode curl_multi_socket_all(CURLM *multi_handle, int *running_handles)
{ {
return multi_socket((struct Curl_multi *)multi_handle, CURLMcode result = multi_socket((struct Curl_multi *)multi_handle,
TRUE, CURL_SOCKET_BAD, running_handles); 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, static CURLMcode multi_timeout(struct Curl_multi *multi,
long *timeout_ms) 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) { if(multi->timetree) {
/* we have a tree of expire times */ /* we have a tree of expire times */
struct timeval now = Curl_tvnow(); struct timeval now = Curl_tvnow();
@ -1724,6 +1742,44 @@ CURLMcode curl_multi_timeout(CURLM *multi_handle,
return CURLM_OK; 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 /* 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() */ this'-time for the transfer, to be extracted by curl_multi_timeout() */
void Curl_expire(struct SessionHandle *data, long milli) void Curl_expire(struct SessionHandle *data, long milli)