Newer
Older
Daniel Stenberg
committed
easy->result = Curl_readwrite_init(easy->easy_conn);
if(CURLE_OK == easy->result) {
multistate(easy, CURLM_STATE_DO_DONE);
Daniel Stenberg
committed
result = CURLM_CALL_MULTI_PERFORM;
Daniel Stenberg
committed
}
break;
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
case CURLM_STATE_DO_DONE:
/* Remove ourselves from the send pipeline */
Curl_removeHandleFromPipeline(easy->easy_handle,
easy->easy_conn->send_pipe);
/* Add ourselves to the recv pipeline */
Curl_addHandleToPipeline(easy->easy_handle,
easy->easy_conn->recv_pipe);
multistate(easy, CURLM_STATE_WAITPERFORM);
result = CURLM_CALL_MULTI_PERFORM;
break;
case CURLM_STATE_WAITPERFORM:
infof(easy->easy_handle, "Connection #%d: recv pipe size = %d\n",
easy->easy_conn->connectindex,
easy->easy_conn->recv_pipe->size);
/* Wait for our turn to PERFORM */
if (!easy->easy_conn->readchannel_inuse &&
Curl_isHandleAtHead(easy->easy_handle,
easy->easy_conn->recv_pipe)) {
/* Grab the channel */
easy->easy_conn->readchannel_inuse = TRUE;
multistate(easy, CURLM_STATE_PERFORM);
result = CURLM_CALL_MULTI_PERFORM;
}
break;
case CURLM_STATE_TOOFAST: /* limit-rate exceeded in either direction */
/* if both rates are within spec, resume transfer */
Curl_pgrsUpdate(easy->easy_conn);
if ( ( ( easy->easy_handle->set.max_send_speed == 0 ) ||
( easy->easy_handle->progress.ulspeed <
easy->easy_handle->set.max_send_speed ) ) &&
( ( easy->easy_handle->set.max_recv_speed == 0 ) ||
( easy->easy_handle->progress.dlspeed <
easy->easy_handle->set.max_recv_speed ) )
)
multistate(easy, CURLM_STATE_PERFORM);
Daniel Stenberg
committed
case CURLM_STATE_PERFORM:
/* check if over speed */
if ( ( ( easy->easy_handle->set.max_send_speed > 0 ) &&
( easy->easy_handle->progress.ulspeed >
easy->easy_handle->set.max_send_speed ) ) ||
( ( easy->easy_handle->set.max_recv_speed > 0 ) &&
( easy->easy_handle->progress.dlspeed >
easy->easy_handle->set.max_recv_speed ) )
) {
/* Transfer is over the speed limit. Change state. TODO: Call
* Curl_expire() with the time left until we're targeted to be below
* the speed limit again. */
multistate(easy, CURLM_STATE_TOOFAST );
break;
}
Daniel Stenberg
committed
/* read/write data if it is ready to do so */
easy->result = Curl_readwrite(easy->easy_conn, &done);
k = &easy->easy_handle->reqdata.keep;
if (!(k->keepon & KEEP_READ)) {
/* We're done reading */
easy->easy_conn->readchannel_inuse = FALSE;
}
if (!(k->keepon & KEEP_WRITE)) {
/* We're done writing */
easy->easy_conn->writechannel_inuse = FALSE;
}
Daniel Stenberg
committed
if(easy->result) {
/* The transfer phase returned error, we mark the connection to get
* closed to prevent being re-used. This is becasue we can't
* possibly know if the connection is in a good shape or not now. */
easy->easy_conn->bits.close = TRUE;
if(CURL_SOCKET_BAD != easy->easy_conn->sock[SECONDARYSOCKET]) {
/* if we failed anywhere, we must clean up the secondary socket if
it was used */
sclose(easy->easy_conn->sock[SECONDARYSOCKET]);
easy->easy_conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
Daniel Stenberg
committed
Curl_posttransfer(easy->easy_handle);
Curl_done(&easy->easy_conn, easy->result);
}
else if(TRUE == done) {
char *newurl;
bool retry = Curl_retry_request(easy->easy_conn, &newurl);
Daniel Stenberg
committed
Daniel Stenberg
committed
/* call this even if the readwrite function returned error */
Curl_posttransfer(easy->easy_handle);
Daniel Stenberg
committed
if (retry) {
Curl_removeHandleFromPipeline(easy->easy_handle,
easy->easy_conn->recv_pipe);
}
Daniel Stenberg
committed
/* When we follow redirects, must to go back to the CONNECT state */
if(easy->easy_handle->reqdata.newurl || retry) {
Daniel Stenberg
committed
if(!retry) {
/* if the URL is a follow-location and not just a retried request
then figure out the URL here */
newurl = easy->easy_handle->reqdata.newurl;
easy->easy_handle->reqdata.newurl = NULL;
Daniel Stenberg
committed
}
Daniel Stenberg
committed
easy->result = Curl_done(&easy->easy_conn, CURLE_OK);
if(easy->result == CURLE_OK)
easy->result = Curl_follow(easy->easy_handle, newurl, retry);
if(CURLE_OK == easy->result) {
multistate(easy, CURLM_STATE_CONNECT);
result = CURLM_CALL_MULTI_PERFORM;
Daniel Stenberg
committed
else
/* Since we "took it", we are in charge of freeing this on
failure */
free(newurl);
Daniel Stenberg
committed
else {
/* after the transfer is done, go DONE */
multistate(easy, CURLM_STATE_DONE);
result = CURLM_CALL_MULTI_PERFORM;
}
}
Daniel Stenberg
committed
break;
Daniel Stenberg
committed
case CURLM_STATE_DONE:
/* Remove ourselves from the receive pipeline */
Curl_removeHandleFromPipeline(easy->easy_handle,
easy->easy_conn->recv_pipe);
easy->easy_handle->state.is_in_pipeline = FALSE;
if (easy->easy_conn->bits.stream_was_rewound) {
/* This request read past its response boundary so we quickly
let the other requests consume those bytes since there is no
guarantee that the socket will become active again */
result = CURLM_CALL_MULTI_PERFORM;
}
if (!easy->easy_handle->state.cancelled) {
/* post-transfer command */
easy->result = Curl_done(&easy->easy_conn, CURLE_OK);
/* after we have DONE what we're supposed to do, go COMPLETED, and
it doesn't matter what the Curl_done() returned! */
multistate(easy, CURLM_STATE_COMPLETED);
}
Daniel Stenberg
committed
break;
Daniel Stenberg
committed
case CURLM_STATE_COMPLETED:
if (easy->easy_handle->state.cancelled) {
/* Go into the CANCELLED state if we were cancelled */
multistate(easy, CURLM_STATE_CANCELLED);
}
Daniel Stenberg
committed
/* this is a completed transfer, it is likely to still be connected */
Daniel Stenberg
committed
/* This node should be delinked from the list now and we should post
an information message that we are complete. */
break;
case CURLM_STATE_CANCELLED:
/* Cancelled transfer, wait to be cleaned up */
break;
Daniel Stenberg
committed
default:
return CURLM_INTERNAL_ERROR;
}
Daniel Stenberg
committed
if(CURLM_STATE_COMPLETED != easy->state) {
if(CURLE_OK != easy->result) {
/*
* If an error was returned, and we aren't in completed state now,
* then we go to completed and consider this transfer aborted.
*/
Daniel Stenberg
committed
multistate(easy, CURLM_STATE_COMPLETED);
Daniel Stenberg
committed
}
Daniel Stenberg
committed
}
Daniel Stenberg
committed
Daniel Stenberg
committed
} while (easy->easy_handle->change.url_changed);
Daniel Stenberg
committed
if ((CURLM_STATE_COMPLETED == easy->state) && !easy->msg) {
Daniel Stenberg
committed
if(easy->easy_handle->dns.hostcachetype == HCACHE_MULTI) {
/* clear out the usage of the shared DNS cache */
easy->easy_handle->dns.hostcache = NULL;
easy->easy_handle->dns.hostcachetype = HCACHE_NONE;
}
Daniel Stenberg
committed
/* now add a node to the Curl_message linked list with this info */
msg = (struct Curl_message *)malloc(sizeof(struct Curl_message));
Daniel Stenberg
committed
if(!msg)
return CURLM_OUT_OF_MEMORY;
Daniel Stenberg
committed
msg->extmsg.msg = CURLMSG_DONE;
msg->extmsg.easy_handle = easy->easy_handle;
msg->extmsg.data.result = easy->result;
msg->next = NULL;
Daniel Stenberg
committed
easy->msg = msg;
easy->msg_num = 1; /* there is one unread message here */
Daniel Stenberg
committed
multi->num_msgs++; /* increase message counter */
}
}
Daniel Stenberg
committed
CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles)
{
struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
struct Curl_one_easy *easy;
CURLMcode returncode=CURLM_OK;
struct Curl_tree *t;
if(!GOOD_MULTI_HANDLE(multi))
return CURLM_BAD_HANDLE;
easy=multi->easy.next;
while(easy) {
CURLMcode result;
if (easy->easy_handle->state.cancelled &&
easy->state == CURLM_STATE_CANCELLED) {
/* Remove cancelled handles once it's safe to do so */
Curl_multi_rmeasy(multi_handle, easy->easy_handle);
Daniel Stenberg
committed
easy->easy_handle = NULL;
easy = easy->next;
continue;
}
result = multi_runsingle(multi, easy);
Daniel Stenberg
committed
if(result)
returncode = result;
easy = easy->next; /* operate on next handle */
}
/*
* Simply remove all expired timers from the splay since handles are dealt
* with unconditionally by this function and curl_multi_timeout() requires
* that already passed/handled expire times are removed from the splay.
*/
do {
struct timeval now = Curl_tvnow();
int key = now.tv_sec; /* drop the usec part */
multi->timetree = Curl_splaygetbest(key, multi->timetree, &t);
Daniel Stenberg
committed
if (t) {
struct SessionHandle *d = t->payload;
struct timeval* tv = &d->state.expiretime;
/* clear the expire times within the handles that we remove from the
splay tree */
tv->tv_sec = 0;
tv->tv_usec = 0;
}
Daniel Stenberg
committed
} while(t);
Daniel Stenberg
committed
*running_handles = multi->num_alive;
Daniel Stenberg
committed
return returncode;
}
Daniel Stenberg
committed
/* This is called when an easy handle is cleanup'ed that is part of a multi
handle */
void Curl_multi_rmeasy(void *multi_handle, CURL *easy_handle)
{
curl_multi_remove_handle(multi_handle, easy_handle);
}
CURLMcode curl_multi_cleanup(CURLM *multi_handle)
{
struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
struct Curl_one_easy *easy;
struct Curl_one_easy *nexteasy;
int i;
struct closure *cl;
struct closure *n;
if(GOOD_MULTI_HANDLE(multi)) {
multi->type = 0; /* not good anymore */
Curl_hash_destroy(multi->hostcache);
Daniel Stenberg
committed
Curl_hash_destroy(multi->sockhash);
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
#if 1
/* go over all connections that have close actions */
for(i=0; i< multi->connc->num; i++) {
if(multi->connc->connects[i] &&
multi->connc->connects[i]->protocol & PROT_CLOSEACTION)
Curl_disconnect(multi->connc->connects[i]);
}
/* now walk through the list of handles we kept around only to be
able to close connections "properly" */
cl = multi->closure;
while(cl) {
cl->easy_handle->state.shared_conn = NULL; /* no more shared */
Curl_close(cl->easy_handle); /* close handle */
n = cl->next;
free(cl);
cl= n;
}
#endif
Curl_rm_connc(multi->connc);
easy = multi->easy.next;
while(easy) {
nexteasy=easy->next;
Daniel Stenberg
committed
if(easy->easy_handle->dns.hostcachetype == HCACHE_MULTI) {
/* clear out the usage of the shared DNS cache */
easy->easy_handle->dns.hostcache = NULL;
easy->easy_handle->dns.hostcachetype = HCACHE_NONE;
}
/* Clear the pointer to the connection cache */
easy->easy_handle->state.connc = NULL;
Curl_easy_addmulti(easy->easy_handle, NULL); /* clear the association */
if (easy->msg)
free(easy->msg);
free(easy);
easy = nexteasy;
}
free(multi);
return CURLM_OK;
}
else
return CURLM_BAD_HANDLE;
}
CURLMsg *curl_multi_info_read(CURLM *multi_handle, int *msgs_in_queue)
{
struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
*msgs_in_queue = 0; /* default to none */
if(GOOD_MULTI_HANDLE(multi)) {
struct Curl_one_easy *easy;
if(!multi->num_msgs)
return NULL; /* no messages left to return */
easy=multi->easy.next;
while(easy) {
if(easy->msg_num) {
easy->msg_num--;
break;
}
easy = easy->next;
}
if(!easy)
return NULL; /* this means internal count confusion really */
multi->num_msgs--;
*msgs_in_queue = multi->num_msgs;
return &easy->msg->extmsg;
}
else
Daniel Stenberg
committed
/*
Daniel Stenberg
committed
* singlesocket() checks what sockets we deal with and their "action state"
* and if we have a different state in any of those sockets from last time we
* call the callback accordingly.
Daniel Stenberg
committed
*/
static void singlesocket(struct Curl_multi *multi,
struct Curl_one_easy *easy)
{
struct socketstate current;
int i;
struct Curl_sh_entry *entry;
curl_socket_t s;
int num;
Daniel Stenberg
committed
memset(¤t, 0, sizeof(current));
for(i=0; i< MAX_SOCKSPEREASYHANDLE; i++)
current.socks[i] = CURL_SOCKET_BAD;
/* Fill in the 'current' struct with the state as it is now: what sockets to
supervise and for what actions */
Daniel Stenberg
committed
current.action = multi_getsock(easy, current.socks, MAX_SOCKSPEREASYHANDLE);
/* We have 0 .. N sockets already and we get to know about the 0 .. M
sockets we should have from now on. Detect the differences, remove no
longer supervised ones and add new ones */
Daniel Stenberg
committed
/* walk over the sockets we got right now */
for(i=0; (i< MAX_SOCKSPEREASYHANDLE) &&
(current.action & (GETSOCK_READSOCK(i) | GETSOCK_WRITESOCK(i)));
i++) {
int action = CURL_POLL_NONE;
Daniel Stenberg
committed
s = current.socks[i];
Daniel Stenberg
committed
/* get it from the hash */
entry = Curl_hash_pick(multi->sockhash, (char *)&s, sizeof(s));
Daniel Stenberg
committed
if(current.action & GETSOCK_READSOCK(i))
action |= CURL_POLL_IN;
if(current.action & GETSOCK_WRITESOCK(i))
action |= CURL_POLL_OUT;
Daniel Stenberg
committed
if(entry) {
/* yeps, already present so check if it has the same action set */
if(entry->action == action)
/* same, continue */
continue;
}
else {
/* this is a socket we didn't have before, add it! */
entry = sh_addentry(multi->sockhash, s, easy->easy_handle);
if(!entry)
/* fatal */
return;
}
Daniel Stenberg
committed
multi->socket_cb(easy->easy_handle,
s,
action,
multi->socket_userp,
entry ? entry->socketp : NULL);
Daniel Stenberg
committed
entry->action = action; /* store the current action state */
}
Daniel Stenberg
committed
num = i; /* number of sockets */
/* when we've walked over all the sockets we should have right now, we must
make sure to detect sockets that are removed */
for(i=0; i< easy->numsocks; i++) {
int j;
s = easy->sockets[i];
for(j=0; j<num; j++) {
if(s == current.socks[j]) {
/* this is still supervised */
s = CURL_SOCKET_BAD;
break;
Daniel Stenberg
committed
}
}
if(s != CURL_SOCKET_BAD) {
/* this socket has been removed. Remove it */
entry = Curl_hash_pick(multi->sockhash, (char *)&s, sizeof(s));
if(entry) {
/* just a precaution, this socket really SHOULD be in the hash already
but in case it isn't, we don't have to tell the app to remove it
either since it never got to know about it */
Daniel Stenberg
committed
multi->socket_cb(easy->easy_handle,
s,
CURL_POLL_REMOVE,
Daniel Stenberg
committed
multi->socket_userp,
Daniel Stenberg
committed
entry ? entry->socketp : NULL);
Daniel Stenberg
committed
Daniel Stenberg
committed
sh_delentry(multi->sockhash, s);
}
Daniel Stenberg
committed
}
}
memcpy(easy->sockets, current.socks, num*sizeof(curl_socket_t));
easy->numsocks = num;
Daniel Stenberg
committed
}
static CURLMcode multi_socket(struct Curl_multi *multi,
bool checkall,
Daniel Stenberg
committed
curl_socket_t s,
int *running_handles)
Daniel Stenberg
committed
{
CURLMcode result = CURLM_OK;
struct SessionHandle *data = NULL;
struct Curl_tree *t;
if(checkall) {
struct Curl_one_easy *easyp;
Daniel Stenberg
committed
/* *perform() deals with running_handles on its own */
Daniel Stenberg
committed
result = curl_multi_perform(multi, running_handles);
Daniel Stenberg
committed
/* walk through each easy handle and do the socket state change magic
and callbacks */
easyp=multi->easy.next;
while(easyp) {
singlesocket(multi, easyp);
easyp = easyp->next;
}
Daniel Stenberg
committed
/* or should we fall-through and do the timer-based stuff? */
Daniel Stenberg
committed
return result;
}
else if (s != CURL_SOCKET_TIMEOUT) {
struct Curl_sh_entry *entry =
Curl_hash_pick(multi->sockhash, (char *)&s, sizeof(s));
if(!entry)
/* unmatched socket, major problemo! */
return CURLM_BAD_SOCKET; /* better return code? */
Daniel Stenberg
committed
data = entry->easy;
Daniel Stenberg
committed
if(data->magic != CURLEASY_MAGIC_NUMBER)
/* bad bad bad bad bad bad bad */
return CURLM_INTERNAL_ERROR;
Daniel Stenberg
committed
result = multi_runsingle(multi, data->set.one_easy);
Daniel Stenberg
committed
Daniel Stenberg
committed
if(result == CURLM_OK)
/* get the socket(s) and check if the state has been changed since
last */
singlesocket(multi, data->set.one_easy);
Daniel Stenberg
committed
Daniel Stenberg
committed
/* Now we fall-through and do the timer-based stuff, since we don't want
to force the user to have to deal with timeouts as long as at least one
connection in fact has traffic. */
data = NULL; /* set data to NULL again to avoid calling multi_runsingle()
in case there's no need to */
Daniel Stenberg
committed
}
/*
* The loop following here will go on as long as there are expire-times left
* to process in the splay and 'data' will be re-assigned for every expired
* handle we deal with.
*/
do {
int key;
struct timeval now;
/* the first loop lap 'data' can be NULL */
if(data) {
Daniel Stenberg
committed
result = multi_runsingle(multi, data->set.one_easy);
Daniel Stenberg
committed
if(result == CURLM_OK)
/* get the socket(s) and check if the state has been changed since
last */
singlesocket(multi, data->set.one_easy);
}
/* Check if there's one (more) expired timer to deal with! This function
extracts a matching node if there is one */
now = Curl_tvnow();
key = now.tv_sec; /* drop the usec part */
multi->timetree = Curl_splaygetbest(key, multi->timetree, &t);
Daniel Stenberg
committed
if(t) {
/* assign 'data' to be the easy handle we just removed from the splay
tree */
Daniel Stenberg
committed
data = t->payload;
Daniel Stenberg
committed
/* clear the expire time within the handle we removed from the
splay tree */
data->state.expiretime.tv_sec = 0;
data->state.expiretime.tv_usec = 0;
}
Daniel Stenberg
committed
} while(t);
Daniel Stenberg
committed
*running_handles = multi->num_alive;
Daniel Stenberg
committed
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
return result;
}
CURLMcode curl_multi_setopt(CURLM *multi_handle,
CURLMoption option, ...)
{
struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
CURLMcode res = CURLM_OK;
va_list param;
if(!GOOD_MULTI_HANDLE(multi))
return CURLM_BAD_HANDLE;
va_start(param, option);
switch(option) {
case CURLMOPT_SOCKETFUNCTION:
multi->socket_cb = va_arg(param, curl_socket_callback);
break;
case CURLMOPT_SOCKETDATA:
multi->socket_userp = va_arg(param, void *);
break;
case CURLMOPT_PIPELINING:
multi->pipelining_enabled = (bool)(0 != va_arg(param, long));
break;
Daniel Stenberg
committed
default:
res = CURLM_UNKNOWN_OPTION;
}
va_end(param);
return res;
}
Daniel Stenberg
committed
CURLMcode curl_multi_socket(CURLM *multi_handle, curl_socket_t s,
int *running_handles)
Daniel Stenberg
committed
{
Daniel Stenberg
committed
return multi_socket((struct Curl_multi *)multi_handle, FALSE, s,
running_handles);
Daniel Stenberg
committed
}
Daniel Stenberg
committed
CURLMcode curl_multi_socket_all(CURLM *multi_handle, int *running_handles)
Daniel Stenberg
committed
{
return multi_socket((struct Curl_multi *)multi_handle,
Daniel Stenberg
committed
TRUE, CURL_SOCKET_BAD, running_handles);
Daniel Stenberg
committed
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
}
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;
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(0, multi->timetree);
/* At least currently, the splay key is a time_t for the expire time */
*timeout_ms = (multi->timetree->key - now.tv_sec) * 1000 -
now.tv_usec/1000;
if(*timeout_ms < 0)
/* 0 means immediately */
*timeout_ms = 0;
}
else
*timeout_ms = -1;
return CURLM_OK;
}
/* 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) {
/* 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) {
/* 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;
infof(data, "Expire at %ld / %ld (%ldms)\n",
(long)nowp->tv_sec, (long)nowp->tv_usec, milli);
data->state.timenode.payload = data;
multi->timetree = Curl_splayinsert((int)nowp->tv_sec,
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;
}
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
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 */
void Curl_multi_add_closure(struct Curl_multi *multi,
struct SessionHandle *data)
{
int i;
struct closure *cl = (struct closure *)calloc(sizeof(struct closure), 1);
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;
}
}