Newer
Older
Daniel Stenberg
committed
Daniel Stenberg
committed
CURLMcode curl_multi_socket_all(CURLM *multi_handle, int *running_handles)
Daniel Stenberg
committed
{
CURLMcode result = multi_socket((struct Curl_multi *)multi_handle,
Daniel Stenberg
committed
TRUE, CURL_SOCKET_BAD, 0, running_handles);
Daniel Stenberg
committed
if(CURLM_OK >= result)
update_timer((struct Curl_multi *)multi_handle);
return result;
Daniel Stenberg
committed
}
static CURLMcode multi_timeout(struct Curl_multi *multi,
long *timeout_ms)
Daniel Stenberg
committed
{
static struct timeval tv_zero = {0,0};
Daniel Stenberg
committed
if(multi->timetree) {
/* we have a tree of expire times */
struct timeval now = Curl_tvnow();
/* splay the lowest to the bottom */
multi->timetree = Curl_splay(tv_zero, multi->timetree);
Daniel Stenberg
committed
Daniel Stenberg
committed
if(Curl_splaycomparekeys(multi->timetree->key, now) > 0) {
/* some time left before expiration */
*timeout_ms = curlx_tvdiff(multi->timetree->key, now);
Daniel Stenberg
committed
if(!*timeout_ms)
/*
* Since we only provide millisecond resolution on the returned value
* and the diff might be less than one millisecond here, we don't
* return zero as that may cause short bursts of busyloops on fast
* processors while the diff is still present but less than one
* millisecond! instead we return 1 until the time is ripe.
*/
*timeout_ms=1;
}
Daniel Stenberg
committed
/* 0 means immediately */
*timeout_ms = 0;
}
else
*timeout_ms = -1;
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;
Daniel Stenberg
committed
if(!multi->timer_cb)
return 0;
Daniel Stenberg
committed
if( multi_timeout(multi, &timeout_ms) != CURLM_OK )
return -1;
Daniel Stenberg
committed
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(Curl_splaycomparekeys(multi->timetree->key, multi->timer_lastcall) == 0)
return 0;
multi->timer_lastcall = multi->timetree->key;
return multi->timer_cb((CURLM*)multi, timeout_ms, multi->timer_userp);
}
Daniel Stenberg
committed
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
static CURLcode addHandleToSendOrPendPipeline(struct SessionHandle *handle,
struct connectdata *conn)
{
size_t pipeLen = conn->send_pipe->size + conn->recv_pipe->size;
struct curl_llist *pipeline;
if(!Curl_isPipeliningEnabled(handle) ||
pipeLen == 0)
pipeline = conn->send_pipe;
else {
if(conn->server_supports_pipelining &&
pipeLen < MAX_PIPELINE_LENGTH)
pipeline = conn->send_pipe;
else
pipeline = conn->pend_pipe;
}
return Curl_addHandleToPipeline(handle, pipeline);
}
static int checkPendPipeline(struct connectdata *conn)
{
int result = 0;
Daniel Stenberg
committed
struct curl_llist_element *sendhead = conn->send_pipe->head;
Daniel Stenberg
committed
size_t pipeLen = conn->send_pipe->size + conn->recv_pipe->size;
if (conn->server_supports_pipelining || pipeLen == 0) {
Daniel Stenberg
committed
struct curl_llist_element *curr = conn->pend_pipe->head;
const size_t maxPipeLen =
conn->server_supports_pipelining ? MAX_PIPELINE_LENGTH : 1;
Daniel Stenberg
committed
while(pipeLen < maxPipeLen && curr) {
Daniel Stenberg
committed
Curl_llist_move(conn->pend_pipe, curr,
conn->send_pipe, conn->send_pipe->tail);
Daniel Stenberg
committed
Curl_pgrsTime(curr->ptr, TIMER_PRETRANSFER);
Daniel Stenberg
committed
++result; /* count how many handles we moved */
curr = conn->pend_pipe->head;
++pipeLen;
}
}
if (result) {
conn->now = Curl_tvnow();
Daniel Stenberg
committed
/* something moved, check for a new send pipeline leader */
if(sendhead != conn->send_pipe->head) {
/* this is a new one as head, expire it */
conn->writechannel_inuse = FALSE; /* not in use yet */
infof(conn->data, "%p is at send pipe head!\n",
conn->send_pipe->head->ptr);
Curl_expire(conn->send_pipe->head->ptr, 1);
}
}
Daniel Stenberg
committed
return result;
}
Daniel Stenberg
committed
/* Move this transfer from the sending list to the receiving list.
Pay special attention to the new sending list "leader" as it needs to get
checked to update what sockets it acts on.
*/
static void moveHandleFromSendToRecvPipeline(struct SessionHandle *handle,
Daniel Stenberg
committed
struct connectdata *conn)
{
struct curl_llist_element *curr;
curr = conn->send_pipe->head;
while(curr) {
if(curr->ptr == handle) {
Curl_llist_move(conn->send_pipe, curr,
conn->recv_pipe, conn->recv_pipe->tail);
Daniel Stenberg
committed
if(conn->send_pipe->head) {
/* Since there's a new easy handle at the start of the send pipeline,
set its timeout value to 1ms to make it trigger instantly */
conn->writechannel_inuse = FALSE; /* not used now */
infof(conn->data, "%p is at send pipe head B!\n",
conn->send_pipe->head->ptr);
Curl_expire(conn->send_pipe->head->ptr, 1);
}
/* The receiver's list is not really interesting here since either this
handle is now first in the list and we'll deal with it soon, or
another handle is already first and thus is already taken care of */
break; /* we're done! */
Daniel Stenberg
committed
}
curr = curr->next;
}
}
static bool isHandleAtHead(struct SessionHandle *handle,
struct curl_llist *pipeline)
{
struct curl_llist_element *curr = pipeline->head;
if(curr)
return (bool)(curr->ptr == handle);
return FALSE;
}
Daniel Stenberg
committed
/* given a number of milliseconds from now to use to set the 'act before
Daniel Stenberg
committed
this'-time for the transfer, to be extracted by curl_multi_timeout()
Pass zero to clear the timeout value for this handle.
*/
Daniel Stenberg
committed
void Curl_expire(struct SessionHandle *data, long milli)
{
struct Curl_multi *multi = data->multi;
struct timeval *nowp = &data->state.expiretime;
Daniel Stenberg
committed
/* this is only interesting for multi-interface using libcurl, and only
while there is still a multi interface struct remaining! */
if(!multi)
return;
if(!milli) {
/* No timeout, clear the time data. */
if(nowp->tv_sec || nowp->tv_usec) {
Daniel Stenberg
committed
/* Since this is an cleared time, we must remove the previous entry from
the splay tree */
rc = Curl_splayremovebyaddr(multi->timetree,
&data->state.timenode,
&multi->timetree);
if(rc)
infof(data, "Internal error clearing splay node = %d\n", rc);
Daniel Stenberg
committed
infof(data, "Expire cleared\n");
Daniel Stenberg
committed
}
}
else {
struct timeval set;
int rest;
set = Curl_tvnow();
set.tv_sec += milli/1000;
set.tv_usec += (milli%1000)*1000;
rest = (int)(set.tv_usec - 1000000);
if(rest > 0) {
/* bigger than a full microsec */
set.tv_sec++;
set.tv_usec -= 1000000;
}
if(nowp->tv_sec || nowp->tv_usec) {
/* This means that the struct is added as a node in the splay tree.
Compare if the new time is earlier, and only remove-old/add-new if it
is. */
Daniel Stenberg
committed
long diff = curlx_tvdiff(set, *nowp);
if(diff > 0)
/* the new expire time was later so we don't change this */
return;
/* Since this is an updated time, we must remove the previous entry from
the splay tree first and then re-add the new value */
rc = Curl_splayremovebyaddr(multi->timetree,
&data->state.timenode,
&multi->timetree);
if(rc)
infof(data, "Internal error removing splay node = %d\n", rc);
Daniel Stenberg
committed
}
*nowp = set;
Daniel Stenberg
committed
infof(data, "Expire at %ld / %ld (%ldms) %p\n",
(long)nowp->tv_sec, (long)nowp->tv_usec, milli, data);
#endif
Daniel Stenberg
committed
data->state.timenode.payload = data;
multi->timetree = Curl_splayinsert(*nowp,
Daniel Stenberg
committed
multi->timetree,
&data->state.timenode);
}
#if 0
Curl_splayprint(multi->timetree, 0, TRUE);
#endif
}
Daniel Stenberg
committed
CURLMcode curl_multi_assign(CURLM *multi_handle,
curl_socket_t s, void *hashp)
{
struct Curl_sh_entry *there = NULL;
struct Curl_multi *multi = (struct Curl_multi *)multi_handle;
if(s != CURL_SOCKET_BAD)
there = Curl_hash_pick(multi->sockhash, (char *)&s, sizeof(curl_socket_t));
if(!there)
return CURLM_BAD_SOCKET;
there->socketp = hashp;
return CURLM_OK;
}
static bool multi_conn_using(struct Curl_multi *multi,
struct SessionHandle *data)
{
/* any live CLOSEACTION-connections pointing to the give 'data' ? */
int i;
for(i=0; i< multi->connc->num; i++) {
if(multi->connc->connects[i] &&
(multi->connc->connects[i]->data == data) &&
multi->connc->connects[i]->protocol & PROT_CLOSEACTION)
return TRUE;
}
return FALSE;
}
/* Add the given data pointer to the list of 'closure handles' that are kept
around only to be able to close some connections nicely - just make sure
that this handle isn't already added, like for the cases when an easy
handle is removed, added and removed again... */
Daniel Stenberg
committed
static void add_closure(struct Curl_multi *multi,
struct SessionHandle *data)
{
int i;
struct closure *cl = calloc(sizeof(struct closure), 1);
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
struct closure *p=NULL;
struct closure *n;
if(cl) {
cl->easy_handle = data;
cl->next = multi->closure;
multi->closure = cl;
}
p = multi->closure;
cl = p->next; /* start immediately on the second since the first is the one
we just added and it is _very_ likely to actually exist
used in the cache since that's the whole purpose of adding
it to this list! */
/* When adding, scan through all the other currently kept handles and see if
there are any connections still referring to them and kill them if not. */
while(cl) {
bool inuse = FALSE;
for(i=0; i< multi->connc->num; i++) {
if(multi->connc->connects[i] &&
(multi->connc->connects[i]->data == cl->easy_handle)) {
inuse = TRUE;
break;
}
}
n = cl->next;
if(!inuse) {
/* cl->easy_handle is now killable */
infof(data, "Delayed kill of easy handle %p\n", cl->easy_handle);
/* unmark it as not having a connection around that uses it anymore */
cl->easy_handle->state.shared_conn= NULL;
Curl_close(cl->easy_handle);
if(p)
p->next = n;
else
multi->closure = n;
free(cl);
}
else
p = cl;
cl = n;
}
}
Daniel Stenberg
committed
#ifdef CURLDEBUG
void Curl_multi_dump(const struct Curl_multi *multi_handle)
Daniel Stenberg
committed
{
struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
struct Curl_one_easy *easy;
int i;
fprintf(stderr, "* Multi status: %d handles, %d alive\n",
multi->num_easy, multi->num_alive);
Daniel Stenberg
committed
for(easy=multi->easy.next; easy != &multi->easy; easy = easy->next) {
Daniel Stenberg
committed
if(easy->state != CURLM_STATE_COMPLETED) {
/* only display handles that are not completed */
fprintf(stderr, "handle %p, state %s, %d sockets\n",
Daniel Stenberg
committed
(void *)easy->easy_handle,
statename[easy->state], easy->numsocks);
Daniel Stenberg
committed
for(i=0; i < easy->numsocks; i++) {
curl_socket_t s = easy->sockets[i];
struct Curl_sh_entry *entry =
Curl_hash_pick(multi->sockhash, (char *)&s, sizeof(s));
fprintf(stderr, "%d ", (int)s);
if(!entry) {
fprintf(stderr, "INTERNAL CONFUSION\n");
continue;
}
fprintf(stderr, "[%s %s] ",
entry->action&CURL_POLL_IN?"RECVING":"",
entry->action&CURL_POLL_OUT?"SENDING":"");
}
if(easy->numsocks)
fprintf(stderr, "\n");
}
}
}
#endif