diff options
Diffstat (limited to 'src/downloadpool.c')
| -rw-r--r-- | src/downloadpool.c | 177 |
1 files changed, 177 insertions, 0 deletions
diff --git a/src/downloadpool.c b/src/downloadpool.c new file mode 100644 index 0000000..5da2905 --- /dev/null +++ b/src/downloadpool.c @@ -0,0 +1,177 @@ +#include "downloadpool.h" +#include "macros.h" +#include <curl/curl.h> +#include <curl/multi.h> +#include <stdbool.h> +#include <stdlib.h> + +struct l2_dlpool__handle { + CURL *easy; + void *user; + + struct l2_dlpool__handle *next; + struct l2_dlpool__handle *prev; +}; + +struct l2_dlpool__pool_tag { + CURLM *multi; + + struct l2_dlpool__handle *head, *tail; + + l2_dlpool_download_init_proc_t *initproc; + l2_dlpool_one_downloaded_proc_t *dlproc; +}; + +l2_dlpool_t *l2_dlpool_init(l2_dlpool_download_init_proc_t *init, l2_dlpool_one_downloaded_proc_t *dlproc) +{ + l2_dlpool_t *ret = calloc(1, sizeof(l2_dlpool_t)); + if (!ret) return NULL; + + ret->multi = curl_multi_init(); + if (!ret->multi) { + goto cleanup; + } + + ret->initproc = init; + ret->dlproc = dlproc; + + return ret; + +cleanup: + if (ret) { + if (ret->multi) curl_multi_cleanup(ret->multi); + } + free(ret); + return NULL; +} + +int l2_dlpool_add(l2_dlpool_t *pool, void *user) +{ + int res = -1; + struct l2_dlpool__handle *newhandle = calloc(1, sizeof(struct l2_dlpool__handle)); + if (!newhandle) return -1; + + newhandle->easy = curl_easy_init(); + if (!newhandle->easy) goto cleanup; + + curl_easy_setopt(newhandle->easy, CURLOPT_USERAGENT, L2_USER_AGENT); + + if ((*pool->initproc)(newhandle->easy, user) < 0) { + res = 0; + goto cleanup; + } + + newhandle->user = user; + + CURLMcode mc = curl_multi_add_handle(pool->multi, newhandle->easy); + if (mc != CURLM_OK) { + if (mc == CURLM_OUT_OF_MEMORY) { + fputs("CURL out of memory. (You are doomed.)\n", stderr); + abort(); + } + goto cleanup; + } + + if (pool->tail) { + pool->tail->next = newhandle; + newhandle->prev = pool->tail; + pool->tail = newhandle; + } else { + pool->head = pool->tail = newhandle; + } + + return 1; + +cleanup: + if (newhandle) { + if (newhandle->easy) curl_easy_cleanup(newhandle->easy); + } + free(newhandle); + return res; +} + +int l2_dlpool__download_complete(l2_dlpool_t *pool, CURL *easy, CURLcode code) +{ + struct l2_dlpool__handle *handle = NULL; + int uret = 0; + for (struct l2_dlpool__handle *cur = pool->head; cur; cur = cur->next) { + if (cur->easy == easy) { + handle = cur; + continue; + } + } + + if (handle) { + uret = (*pool->dlproc)(easy, code, handle->user); + + if (handle->prev) { + handle->prev->next = handle->next; + } else { + pool->head = handle->next; + } + + if (handle->next) { + handle->next->prev = handle->prev; + } else { + pool->tail = handle->prev; + } + + free(handle); + handle = NULL; + } + + curl_multi_remove_handle(pool->multi, easy); + curl_easy_cleanup(easy); + + if (uret < 0) return -1; + return 0; +} + +int l2_dlpool_perform(l2_dlpool_t *pool) +{ + CURLMcode code; + int running = 0; + int queuelen = 0; + CURLMsg *msg; + + do { + code = curl_multi_perform(pool->multi, &running); + if (code != CURLM_OK) { + return -1; + } + + while ((msg = curl_multi_info_read(pool->multi, &queuelen))) { + if (msg->msg == CURLMSG_DONE) { + if (l2_dlpool__download_complete(pool, msg->easy_handle, msg->data.result) < 0) return -1; + } + } + + code = curl_multi_poll(pool->multi, NULL, 0, 1000, NULL); + if (code != CURLM_OK) { + return -1; + } + } while (running); + + return 0; +} + +void l2_dlpool_cleanup(l2_dlpool_t *pool) +{ + if (!pool) return; + + /* assumes pool->multi is valid (API must not return a handle where this isn't the case) */ + + /* curl docs say to remove handles BEFORE ANY HANDLES ARE CLEANED UP */ + for (struct l2_dlpool__handle *cur = pool->head; cur; cur = cur->next) { + curl_multi_remove_handle(pool->multi, cur->easy); /* assume it succeeds (if it fails, too bad we tried LOL) */ + } + + for (struct l2_dlpool__handle *cur = pool->head, *temp; cur; cur = temp) { + temp = cur->next; + curl_easy_cleanup(cur->easy); + free(cur); + } + + curl_multi_cleanup(pool->multi); + free(pool); +} |
