aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/multi.c134
1 files changed, 94 insertions, 40 deletions
diff --git a/lib/multi.c b/lib/multi.c
index 03fddb458..c449542d6 100644
--- a/lib/multi.c
+++ b/lib/multi.c
@@ -110,7 +110,6 @@ struct Curl_one_easy {
CURLcode result; /* previous result */
struct Curl_message msg; /* A single posted message. */
- int msg_stored; /* a message is stored in 'msg' to return */
/* Array with the plain socket numbers this handle takes care of, in no
particular order. Note that all sockets are added to the sockhash, where
@@ -137,10 +136,11 @@ struct Curl_multi {
struct Curl_one_easy easy;
int num_easy; /* amount of entries in the linked list above. */
- int num_msgs; /* amount of messages in the easy handles */
int num_alive; /* amount of easy handles that are added but have not yet
reached COMPLETE state */
+ struct curl_llist *msglist; /* a list of messages from completed transfers */
+
/* callback function and user data pointer for the *socket() API */
curl_socket_callback socket_cb;
void *socket_userp;
@@ -355,6 +355,33 @@ static struct curl_hash *sh_init(void)
sh_freeentry);
}
+/*
+ * multi_addmsg()
+ *
+ * Called when a transfer is completed. Adds the given msg pointer to
+ * the list kept in the multi handle.
+ */
+static CURLMcode multi_addmsg(struct Curl_multi *multi,
+ struct Curl_message *msg)
+{
+ if(!Curl_llist_insert_next(multi->msglist, multi->msglist->tail, msg))
+ return CURLM_OUT_OF_MEMORY;
+
+ return CURLM_OK;
+}
+
+/*
+ * multi_freeamsg()
+ *
+ * Callback used by the llist system when a single list entry is destroyed.
+ */
+static void multi_freeamsg(void *a, void *b)
+{
+ (void)a;
+ (void)b;
+}
+
+
CURLM *curl_multi_init(void)
{
struct Curl_multi *multi = calloc(1, sizeof(struct Curl_multi));
@@ -365,27 +392,20 @@ CURLM *curl_multi_init(void)
multi->type = CURL_MULTI_HANDLE;
multi->hostcache = Curl_mk_dnscache();
- if(!multi->hostcache) {
- /* failure, free mem and bail out */
- free(multi);
- return NULL;
- }
+ if(!multi->hostcache)
+ goto error;
multi->sockhash = sh_init();
- if(!multi->sockhash) {
- /* failure, free mem and bail out */
- Curl_hash_destroy(multi->hostcache);
- free(multi);
- return NULL;
- }
+ if(!multi->sockhash)
+ goto error;
multi->connc = Curl_mk_connc(CONNCACHE_MULTI, -1L);
- if(!multi->connc) {
- Curl_hash_destroy(multi->sockhash);
- Curl_hash_destroy(multi->hostcache);
- free(multi);
- return NULL;
- }
+ if(!multi->connc)
+ goto error;
+
+ multi->msglist = Curl_llist_alloc(multi_freeamsg);
+ if(!multi->msglist)
+ goto error;
/* Let's make the doubly-linked list a circular list. This makes
the linked list code simpler and allows inserting at the end
@@ -394,6 +414,17 @@ CURLM *curl_multi_init(void)
multi->easy.prev = &multi->easy;
return (CURLM *) multi;
+
+ error:
+ if(multi->sockhash)
+ Curl_hash_destroy(multi->sockhash);
+ if(multi->hostcache)
+ Curl_hash_destroy(multi->hostcache);
+ if(multi->connc)
+ Curl_rm_connc(multi->connc);
+
+ free(multi);
+ return NULL;
}
CURLMcode curl_multi_add_handle(CURLM *multi_handle,
@@ -678,6 +709,22 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle,
Curl_easy_addmulti(easy->easy_handle, NULL); /* clear the association
to this multi handle */
+ {
+ /* make sure there's no pending message in the queue sent from this easy
+ handle */
+ struct curl_llist_element *e;
+
+ for(e = multi->msglist->head; e; e = e->next) {
+ struct Curl_message *msg = e->ptr;
+
+ if(msg->extmsg.easy_handle == easy->easy_handle) {
+ Curl_llist_remove(multi->msglist, e, NULL);
+ /* there can only be one from this specific handle */
+ break;
+ }
+ }
+ }
+
/* make the previous node point to our next */
if(easy->prev)
easy->prev->next = easy->next;
@@ -1529,7 +1576,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
easy->result = CURLE_ABORTED_BY_CALLBACK;
}
} while(0);
- if((CURLM_STATE_COMPLETED == easy->state) && !easy->msg_stored) {
+
+ if(CURLM_STATE_COMPLETED == easy->state) {
if(easy->easy_handle->dns.hostcachetype == HCACHE_MULTI) {
/* clear out the usage of the shared DNS cache */
easy->easy_handle->dns.hostcache = NULL;
@@ -1543,9 +1591,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
msg->extmsg.easy_handle = easy->easy_handle;
msg->extmsg.data.result = easy->result;
- easy->msg_stored = 1; /* there is an unread message here */
-
- multi->num_msgs++; /* increase message counter */
+ result = multi_addmsg(multi, msg);
}
return result;
@@ -1672,6 +1718,9 @@ CURLMcode curl_multi_cleanup(CURLM *multi_handle)
Curl_rm_connc(multi->connc);
+ /* remove the pending list of messages */
+ Curl_llist_destroy(multi->msglist, NULL);
+
/* remove all easy handles */
easy = multi->easy.next;
while(easy != &multi->easy) {
@@ -1699,33 +1748,38 @@ CURLMcode curl_multi_cleanup(CURLM *multi_handle)
return CURLM_BAD_HANDLE;
}
+/*
+ * curl_multi_info_read()
+ *
+ * This function is the primary way for a multi/multi_socket application to
+ * figure out if a transfer has ended. We MUST make this function as fast as
+ * possible as it will be polled frequently and we MUST NOT scan any lists in
+ * here to figure out things. We must scale fine to thousands of handles and
+ * beyond. The current design is fully O(1).
+ */
+
CURLMsg *curl_multi_info_read(CURLM *multi_handle, int *msgs_in_queue)
{
struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
+ struct Curl_message *msg;
*msgs_in_queue = 0; /* default to none */
- if(GOOD_MULTI_HANDLE(multi)) {
- struct Curl_one_easy *easy;
+ if(GOOD_MULTI_HANDLE(multi) && Curl_llist_count(multi->msglist)) {
+ /* there is one or more messages in the list */
+ struct curl_llist_element *e;
- if(!multi->num_msgs)
- return NULL; /* no messages left to return */
+ /* extract the head of the list to return */
+ e = multi->msglist->head;
- easy=multi->easy.next;
- while(easy != &multi->easy) {
- if(easy->msg_stored) {
- easy->msg_stored = 0;
- break;
- }
- easy = easy->next;
- }
- if(!easy)
- return NULL; /* this means internal count confusion really */
+ msg = e->ptr;
+
+ /* remove the extracted entry */
+ Curl_llist_remove(multi->msglist, e, NULL);
- multi->num_msgs--;
- *msgs_in_queue = multi->num_msgs;
+ *msgs_in_queue = Curl_llist_count(multi->msglist);
- return &easy->msg.extmsg;
+ return &msg->extmsg;
}
else
return NULL;