From 01b2cf82ec9495f36976710af0015d4cf7f529cd Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sun, 30 Jul 2006 22:44:07 +0000 Subject: [PATCH] curl_multi_socket() and curl_multi_socket_all() got modified prototypes: they both now provide the number of running handles back to the calling function. --- CHANGES | 11 +++++ hiper/STATUS | 9 ++++ hiper/hipev.c | 131 ++++++++++++++++++++++++--------------------------- include/curl/multi.h | 6 ++- lib/multi.c | 40 +++++++++------- 5 files changed, 107 insertions(+), 90 deletions(-) diff --git a/CHANGES b/CHANGES index 3fcac9b..66a810b 100644 --- a/CHANGES +++ b/CHANGES @@ -6,6 +6,17 @@ Changelog +Daniel (31 July 2006) +- *ARLERT* curl_multi_socket() and curl_multi_socket_all() got modified + prototypes: they both now provide the number of running handles back to the + calling function. It makes the functions resemble the good old + curl_multi_perform() more and provides a nice way to know when the multi + handle goes empty. + + ALERT2: don't use the curl_multi_socket*() functionality in anything + production-like until I say it's somewhat settled, as I suspect there might + be some further API changes before I'm done... + Daniel (28 July 2006) - Yves Lejeune fixed so that replacing Content-Type: when doing multipart formposts work exactly the way you want it (and the way you'd assume it diff --git a/hiper/STATUS b/hiper/STATUS index 6b3e511..918611b 100644 --- a/hiper/STATUS +++ b/hiper/STATUS @@ -289,3 +289,12 @@ July 9, 2006 The set hashp pointer will then be passed on to the callback in upcoming calls when this same socket is used (in the brand new 'socketp' argument). + +--------------------------------------------------------------------------- + +July 30, 2006 + + Shockingly stupid (of me not having realized this before), but we really need + to add a 'running_handles' argument to the curl_multi_socket() and + curl_multi_socket_all() prototypes so that the caller can get to know when + all the transfers are actually done! diff --git a/hiper/hipev.c b/hiper/hipev.c index b5a0159..e99f908 100644 --- a/hiper/hipev.c +++ b/hiper/hipev.c @@ -71,9 +71,6 @@ struct connection { size_t dlcounter; struct globalinfo *global; char error[CURL_ERROR_SIZE]; - struct event ev[3]; /* maximum 3 events per handle NOTE: should this rather - be a define in a public curl header file or possibly - just documented somewhere or... ? */ }; struct fdinfo { @@ -84,10 +81,27 @@ struct fdinfo { CURL *easy; int action; /* as set by libcurl */ long timeout; /* as set by libcurl */ + struct event ev; /* */ + int evset; /* true if the 'ev' struct has been used in a event_set() call */ + CURLMcode *multi; /* pointer to the multi handle */ + int *running_handles; /* pointer to the running_handles counter */ }; static struct fdinfo *allsocks; +static int running_handles; + +/* called from libevent on action on a particular socket ("event") */ +static void eventcallback(int fd, short type, void *userp) +{ + struct fdinfo *fdp = (struct fdinfo *)userp; + + fprintf(stderr, "EVENT callback\n"); + + /* tell libcurl to deal with the transfer associated with this socket */ + curl_multi_socket(fdp->multi, fd, fdp->running_handles); +} + static void remsock(struct fdinfo *f) { if(!f) @@ -109,12 +123,29 @@ static void setsock(struct fdinfo *fdp, curl_socket_t s, CURL *easy, fdp->sockfd = s; fdp->action = action; fdp->easy = easy; + + if(fdp->evset) + /* first remove the existing event if the old setup was used */ + event_del(&fdp->ev); + + /* now use and add the current socket setup */ + event_set(&fdp->ev, fdp->sockfd, + (action&CURL_POLL_IN?EV_READ:0)| + (action&CURL_POLL_OUT?EV_WRITE:0), + eventcallback, fdp); + + fdp->evset=1; + + fprintf(stderr, "event_add() for fd %d\n", s); + event_add(&fdp->ev, NULL); /* no timeout */ } static void addsock(curl_socket_t s, CURL *easy, int action, CURLM *multi) { struct fdinfo *fdp = calloc(sizeof(struct fdinfo), 1); + fdp->multi = multi; + fdp->running_handles = &running_handles; setsock(fdp, s, easy, action); if(allsocks) { @@ -189,7 +220,7 @@ static int socket_callback(CURL *easy, /* easy handle */ { struct fdinfo *fdp = (struct fdinfo *)socketp; - printf("socket %d easy %p what %d\n", s, easy, what); + fprintf(stderr, "socket %d easy %p what %d\n", s, easy, what); if(what == CURL_POLL_REMOVE) remsock(fdp); @@ -219,7 +250,7 @@ writecallback(void *ptr, size_t size, size_t nmemb, void *data) c->dlcounter += realsize; c->global->dlcounter += realsize; -#if 0 +#if 1 printf("%02d: %d, total %d\n", c->id, c->dlcounter, c->global->dlcounter); #endif @@ -360,6 +391,7 @@ int main(int argc, char **argv) int selectmaxamount; struct fdinfo *fdp; char act; + long timeout_ms; memset(&info, 0, sizeof(struct globalinfo)); @@ -431,81 +463,40 @@ int main(int argc, char **argv) curl_multi_setopt(multi_handle, CURLMOPT_SOCKETDATA, multi_handle); /* we start the action by calling *socket() right away */ - while(CURLM_CALL_MULTI_PERFORM == curl_multi_socket_all(multi_handle)); - - printf("Starting timer, expects to run for %ldus\n", RUN_FOR_THIS_LONG); - timer_start(); - timer_pause(); + while(CURLM_CALL_MULTI_PERFORM == curl_multi_socket_all(multi_handle, + &running_handles)); - while(1) { - struct timeval timeout; - int rc; /* select() return code */ - long timeout_ms; + /* event_dispatch() isn't good enough for us, since we need a global timeout + to occur after a given time of inactivity + */ - fd2_set fdread; - fd2_set fdwrite; - int maxfd; + /* get the timeout value from libcurl */ + curl_multi_timeout(multi_handle, &timeout_ms); - curl_multi_timeout(multi_handle, &timeout_ms); + while(running_handles) { + struct timeval timeout; - /* set timeout to wait */ + /* convert ms to timeval */ timeout.tv_sec = timeout_ms/1000; timeout.tv_usec = (timeout_ms%1000)*1000; - /* convert file descriptors from the transfers to fd_sets */ - fdinfo2fdset(&fdread, &fdwrite, &maxfd); - - selects++; - rc = select(maxfd+1, - (fd_set *)&fdread, - (fd_set *)&fdwrite, - NULL, &timeout); - switch(rc) { - case -1: - /* select error */ - break; - case 0: - timeouts++; - curl_multi_socket(multi_handle, CURL_SOCKET_TIMEOUT); - break; - - default: - /* timeout or readable/writable sockets */ - - for(i=0, fdp = allsocks; fdp; fdp = fdp->next) { - act = 0; - if((fdp->action & CURL_POLL_IN) && - FD_ISSET(fdp->sockfd, &fdread)) { - act |= CURL_POLL_IN; - i++; - } - if((fdp->action & CURL_POLL_OUT) && - FD_ISSET(fdp->sockfd, &fdwrite)) { - act |= CURL_POLL_OUT; - i++; - } - - if(act) { - multi_socket++; - timer_continue(); - if(act & CURL_POLL_OUT) - act--; - curl_multi_socket(multi_handle, fdp->sockfd); - timer_pause(); - } - } + event_loopexit(&timeout); - performselect += rc; - if(rc > topselect) - topselect = rc; - break; - } + /* The event_loopexit() function may have taken a while and it may or may + not have invoked libcurl calls during that time. During those calls, + the timeout situation might very well have changed, so we check the + timeout time again to see if we really need to call curl_multi_socket() + at this point! */ + + /* get the timeout value from libcurl */ + curl_multi_timeout(multi_handle, &timeout_ms); - timer_total(); /* calculate the total time spent so far */ + if(timeout_ms <= 0) { + /* no time left */ + curl_multi_socket(multi_handle, CURL_SOCKET_TIMEOUT, &running_handles); - if(total > RUN_FOR_THIS_LONG) { - printf("Stopped after %ldus\n", total); - break; + /* and get the new timeout value again */ + curl_multi_timeout(multi_handle, &timeout_ms); } } diff --git a/include/curl/multi.h b/include/curl/multi.h index eb62446..3c8bb9e 100644 --- a/include/curl/multi.h +++ b/include/curl/multi.h @@ -266,9 +266,11 @@ typedef int (*curl_socket_callback)(CURL *easy, /* easy handle */ void *socketp); /* private socket 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); -CURL_EXTERN CURLMcode curl_multi_socket_all(CURLM *multi_handle); +CURL_EXTERN CURLMcode curl_multi_socket_all(CURLM *multi_handle, + int *running_handles); /* * Name: curl_multi_timeout() diff --git a/lib/multi.c b/lib/multi.c index 3296f09..7f553bd 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1123,6 +1123,15 @@ static void singlesocket(struct Curl_multi *multi, action |= CURL_POLL_OUT; } + /* Update the sockhash accordingly BEFORE the callback of not a removal, + in case the callback wants to use curl_multi_assign(), but do the + removal AFTER the callback for the very same reason (but then to be + able to pass the correct entry->socketp) */ + + if(action != CURL_POLL_REMOVE) + /* make sure this socket is present in the hash for this handle */ + sh_addentry(multi->sockhash, s, easy->easy_handle); + /* call the callback with this new info */ if(multi->socket_cb) { struct Curl_sh_entry *entry = @@ -1132,16 +1141,13 @@ static void singlesocket(struct Curl_multi *multi, s, action, multi->socket_userp, - entry->socketp); + entry ? entry->socketp : NULL); } - /* Update the sockhash accordingly */ if(action == CURL_POLL_REMOVE) /* remove from hash for this easy handle */ sh_delentry(multi->sockhash, s); - else - /* make sure this socket is present in the hash for this handle */ - sh_addentry(multi->sockhash, s, easy->easy_handle); + } /* copy the current state to the storage area */ memcpy(&easy->sockstate, ¤t, sizeof(struct socketstate)); @@ -1154,16 +1160,16 @@ static void singlesocket(struct Curl_multi *multi, static CURLMcode multi_socket(struct Curl_multi *multi, bool checkall, - curl_socket_t s) + curl_socket_t s, + int *running_handles) { CURLMcode result = CURLM_OK; - int running_handles; struct SessionHandle *data = NULL; struct Curl_tree *t; if(checkall) { struct Curl_one_easy *easyp; - result = curl_multi_perform(multi, &running_handles); + result = curl_multi_perform(multi, running_handles); /* walk through each easy handle and do the socket state change magic and callbacks */ @@ -1190,7 +1196,7 @@ static CURLMcode multi_socket(struct Curl_multi *multi, data = entry->easy; - result = multi_runsingle(multi, data->set.one_easy, &running_handles); + result = multi_runsingle(multi, data->set.one_easy, running_handles); if(result == CURLM_OK) /* get the socket(s) and check if the state has been changed since @@ -1212,7 +1218,7 @@ static CURLMcode multi_socket(struct Curl_multi *multi, /* the first loop lap 'data' can be NULL */ if(data) { - result = multi_runsingle(multi, data->set.one_easy, &running_handles); + result = multi_runsingle(multi, data->set.one_easy, running_handles); if(result == CURLM_OK) /* get the socket(s) and check if the state has been changed since @@ -1269,20 +1275,18 @@ 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) { -#if 0 - printf("multi_socket(%d)\n", (int)s); -#endif - - return multi_socket((struct Curl_multi *)multi_handle, FALSE, s); + return multi_socket((struct Curl_multi *)multi_handle, FALSE, s, + running_handles); } -CURLMcode curl_multi_socket_all(CURLM *multi_handle) +CURLMcode curl_multi_socket_all(CURLM *multi_handle, int *running_handles) { return multi_socket((struct Curl_multi *)multi_handle, - TRUE, CURL_SOCKET_BAD); + TRUE, CURL_SOCKET_BAD, running_handles); } CURLMcode curl_multi_timeout(CURLM *multi_handle, -- 2.7.4