Newer
Older
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
this'-time for the transfer, to be extracted by curl_multi_timeout() */
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);
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
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