diff --git a/libdc/CMakeLists.txt b/libdc/CMakeLists.txt index 6601c79..180e1f5 100644 --- a/libdc/CMakeLists.txt +++ b/libdc/CMakeLists.txt @@ -11,11 +11,13 @@ SET(SOURCES "include/dc/account.h" "include/dc/api.h" "include/dc/apisync.h" + "include/dc/loop.h" "include/dc/refable.h" "include/dc/util.h" "src/account.c" "src/api.c" "src/apisync.c" + "src/loop.c" "src/refable.c" "src/util.c" ) diff --git a/libdc/include/dc/loop.h b/libdc/include/dc/loop.h new file mode 100644 index 0000000..6588dc7 --- /dev/null +++ b/libdc/include/dc/loop.h @@ -0,0 +1,48 @@ +#ifndef DC_LOOP_H +#define DC_LOOP_H + +#include + +#include +#include + +#include + +struct dc_loop_; +typedef struct dc_loop_ *dc_loop_t; + +/** + * A simple CURLM <--> libevent2 loop and handler if you don't want + * to bother rolling your own. + */ +dc_loop_t dc_loop_new(void); + +/** + * If you already have either the CURL multi handle, or the event + * base handle, use this function. Either can be NULL. If both are + * NULL both are allocated for you. + */ +dc_loop_t dc_loop_new_full(struct event_base *base, CURLM *multi); + +/** + * Returns the CURL multi handle in use by this loop. + */ +CURLM *dc_loop_curl(dc_loop_t l); + +/** + * Returns the event base used by this loop. + */ +struct event_base *dc_loop_event_base(dc_loop_t l); + +/** + * Add an API handle that this loop should feed. + */ +void dc_loop_add_api(dc_loop_t loop, dc_api_t api); + +/** + * Loop once, and process one message in the queues of the event + * base, and one message from the queue of the CURL multi events. + */ +bool dc_loop_once(dc_loop_t l); + +#endif diff --git a/libdc/src/loop.c b/libdc/src/loop.c index ac4d5dc..cef5c6b 100644 --- a/libdc/src/loop.c +++ b/libdc/src/loop.c @@ -1,6 +1,223 @@ #include +#include -struct nc_loop_ +#include "internal.h" + +struct dc_loop_ { - nc_refable_t ref; + dc_refable_t ref; + + struct event_base *base; + struct event *timer; + CURLM *multi; + + bool base_owner; + bool multi_owner; + + GPtrArray *apis; }; + +static void dc_loop_free(dc_loop_t p) +{ + return_if_true(p == NULL,); + + if (p->timer != NULL) { + evtimer_del(p->timer); + event_free(p->timer); + p->timer = NULL; + } + + if (p->multi_owner && p->multi != NULL) { + curl_multi_cleanup(p->multi); + p->multi = NULL; + } + + if (p->base_owner && p->base != NULL) { + event_base_free(p->base); + p->base = NULL; + } + + if (p->apis != NULL) { + g_ptr_array_unref(p->apis); + p->apis = NULL; + } + + free(p); +} + +static void socket_handler(int sock, short what, void *data) +{ + int unused = 0; + dc_loop_t loop = (dc_loop_t)data; + + if ((what & EV_READ) == EV_READ) { + curl_multi_socket_action(loop->multi, sock, CURL_CSELECT_IN, &unused); + } else if ((what & EV_WRITE) == EV_WRITE) { + curl_multi_socket_action(loop->multi, sock, CURL_CSELECT_OUT, &unused); + } +} + +static int +mcurl_handler(CURL *easy, curl_socket_t s, int what, void *userp, void *socketp) +{ + struct event *event = (struct event *)socketp; + dc_loop_t loop = (dc_loop_t)userp; + + if (what == CURL_POLL_REMOVE) { + if (event != NULL) { + event_del(event); + event_free(event); + curl_multi_assign(loop->multi, s, NULL); + } + } else { + int stat = + ((what & CURL_POLL_IN) ? EV_READ : 0) | + ((what & CURL_POLL_OUT) ? EV_WRITE : 0) | + EV_PERSIST + ; + + if (event == NULL) { + event = event_new(loop->base, s, stat, socket_handler, loop); + if (event == NULL) { + return 0; + } + curl_multi_assign(loop->multi, s, event); + } else { + event_del(event); + event_assign(event, loop->base, s, stat, socket_handler,loop); + event_add(event, NULL); + } + } + + return 0; +} + +static void timer_handler(int sock, short what, void *data) +{ + int running = 0; + dc_loop_t loop = (dc_loop_t)data; + curl_multi_socket_action(loop->multi, CURL_SOCKET_TIMEOUT, 0, &running); +} + +static int mcurl_timer(CURLM *curl, long timeout, void *ptr) +{ + int running = 0; + struct timeval tm; + dc_loop_t loop = (dc_loop_t)ptr; + + if (timeout == -1) { + evtimer_del(loop->timer); + } else if (timeout == 0) { + curl_multi_socket_action(loop->multi, CURL_SOCKET_TIMEOUT, 0, &running); + } else if (timeout > 0) { + tm.tv_sec = timeout / 1000; + tm.tv_usec = (timeout % 1000) * 1000; + evtimer_add(loop->timer, &tm); + } + + return 0; +} + +dc_loop_t dc_loop_new(void) +{ + return dc_loop_new_full(NULL, NULL); +} + +dc_loop_t dc_loop_new_full(struct event_base *base, CURLM *multi) +{ + dc_loop_t ptr = calloc(1, sizeof(struct dc_loop_)); + return_if_true(ptr == NULL, NULL); + + ptr->ref.cleanup = (dc_cleanup_t)dc_loop_free; + + if (base != NULL) { + ptr->base = base; + ptr->base_owner = false; + } else { + ptr->base = event_base_new(); + goto_if_true(ptr->base == NULL, fail); + ptr->base_owner = true; + } + + if (multi != NULL) { + ptr->multi = multi; + ptr->multi_owner = false; + } else { + ptr->multi = curl_multi_init(); + goto_if_true(ptr->multi == NULL, fail); + ptr->multi_owner = true; + } + + ptr->apis = g_ptr_array_new(); + goto_if_true(ptr->apis == NULL, fail); + + ptr->timer = evtimer_new(ptr->base, timer_handler, ptr); + goto_if_true(ptr->timer == NULL, fail); + + curl_multi_setopt(ptr->multi, CURLMOPT_SOCKETDATA, ptr); + curl_multi_setopt(ptr->multi, CURLMOPT_SOCKETFUNCTION, mcurl_handler); + + curl_multi_setopt(ptr->multi, CURLMOPT_TIMERDATA, ptr); + curl_multi_setopt(ptr->multi, CURLMOPT_TIMERFUNCTION, mcurl_timer); + + return ptr; + +fail: + + dc_loop_free(ptr); + return NULL; +} + +CURLM *dc_loop_curl(dc_loop_t l) +{ + return_if_true(l == NULL, NULL); + return l->multi; +} + +struct event_base *dc_loop_event_base(dc_loop_t l) +{ + return_if_true(l == NULL, NULL); + return l->base; +} + +void dc_loop_add_api(dc_loop_t l, dc_api_t a) +{ + return_if_true(l == NULL || a == NULL,); + dc_api_t p = dc_ref(a); + + dc_api_set_event_base(p, l->base); + dc_api_set_curl_multi(p, l->multi); + + g_ptr_array_add(l->apis, p); +} + +bool dc_loop_once(dc_loop_t l) +{ + return_if_true(l == NULL, false); + + int ret = 0, remain = 0; + struct CURLMsg *msg = NULL; + size_t i = 0; + + ret = event_base_loop(l->base, EVLOOP_ONCE|EVLOOP_NONBLOCK); + if (ret < 0) { + return false; + } + + msg = curl_multi_info_read(l->multi, &remain); + if (msg != NULL) { + if (remain <= 0) { + if (evtimer_pending(l->timer, NULL)) { + evtimer_del(l->timer); + } + } + if (msg->msg == CURLMSG_DONE) { + for (i = 0; i < l->apis->len; i++) { + dc_api_t api = g_ptr_array_index(l->apis, i); + dc_api_signal(api, msg->easy_handle, msg->data.result); + } + } + } + + return true; +} diff --git a/ncdc/src/ncdc.c b/ncdc/src/ncdc.c index 571e18a..256ffb0 100644 --- a/ncdc/src/ncdc.c +++ b/ncdc/src/ncdc.c @@ -1,14 +1,15 @@ #include + +#include #include +#include #include #include /* event base for libevent */ -struct event_base *base = NULL; struct event *stdin_ev = NULL; -struct event *timer = NULL; /* we loop in a different thread */ @@ -20,14 +21,9 @@ char *dc_config_file = NULL; static GKeyFile *config = NULL; -/* global curl multi for API access - */ -CURLM *curl = NULL; - +dc_loop_t loop = NULL; dc_api_t api = NULL; -static void handle_multi_info(void); - static void sighandler(int sig) { exit(3); @@ -41,128 +37,39 @@ static void cleanup(void) stdin_ev = NULL; } - if (timer != NULL) { - evtimer_del(timer); - event_free(timer); - timer = NULL; - } - done = true; pthread_join(looper, NULL); - curl_multi_cleanup(curl); - curl = NULL; - - event_base_free(base); - base = NULL; + dc_unref(api); + dc_unref(loop); } static void stdin_handler(int sock, short what, void *data) { } -static void mcurl_socket_handler(int sock, short what, void *data) -{ - int unused = 0; - - if ((what & EV_READ) == EV_READ) { - curl_multi_socket_action(curl, sock, CURL_CSELECT_IN, &unused); - } else if ((what & EV_WRITE) == EV_WRITE) { - curl_multi_socket_action(curl, sock, CURL_CSELECT_OUT, &unused); - } -} - -static void timer_handler(int sock, short what, void *data) -{ - int running = 0; - curl_multi_socket_action(curl, CURL_SOCKET_TIMEOUT, 0, &running); -} - -static int mcurl_timer(CURLM *curl, long timeout, void *ptr) -{ - int running = 0; - struct timeval tm; - - if (timeout == -1) { - evtimer_del(timer); - } else if (timeout == 0) { - curl_multi_socket_action(curl, CURL_SOCKET_TIMEOUT, 0, &running); - } else if (timeout > 0) { - tm.tv_sec = timeout / 1000; - tm.tv_usec = (timeout % 1000) * 1000; - evtimer_add(timer, &tm); - } - - return 0; -} - -static int -mcurl_handler(CURL *easy, curl_socket_t s, int what, void *userp, void *socketp) -{ - struct event *event = (struct event *)socketp; - - if (what == CURL_POLL_REMOVE) { - if (event != NULL) { - event_del(event); - event_free(event); - curl_multi_assign(curl, s, NULL); - } - } else { - int stat = - ((what & CURL_POLL_IN) ? EV_READ : 0) | - ((what & CURL_POLL_OUT) ? EV_WRITE : 0) | - EV_PERSIST - ; - - if (event == NULL) { - event = event_new(base, s, stat, mcurl_socket_handler, NULL); - if (event == NULL) { - return 0; - } - curl_multi_assign(curl, s, event); - } else { - event_del(event); - event_assign(event, base, s, stat, mcurl_socket_handler, NULL); - event_add(event, NULL); - } - } - - return 0; -} - static bool init_everything(void) { evthread_use_pthreads(); - base = event_base_new(); - return_if_true(base == NULL, false); + loop = dc_loop_new(); + return_if_true(loop == NULL, false); /* add handle for STDIN, this info will then be fed to the UI */ - stdin_ev = event_new(base, 0 /* stdin */, EV_READ|EV_PERSIST, + stdin_ev = event_new(dc_loop_event_base(loop), 0 /* stdin */, + EV_READ|EV_PERSIST, stdin_handler, NULL ); return_if_true(stdin_ev == NULL, false); event_add(stdin_ev, NULL); - timer = evtimer_new(base, timer_handler, NULL); - return_if_true(timer == NULL, false); - - /* create curl multi and feed that to the API too - */ - curl = curl_multi_init(); - return_if_true(curl == NULL, false); - - curl_multi_setopt(curl, CURLMOPT_SOCKETFUNCTION, mcurl_handler); - curl_multi_setopt(curl, CURLMOPT_TIMERFUNCTION, mcurl_timer); - /* initialise event */ api = dc_api_new(); return_if_true(api == NULL, false); - dc_api_set_event_base(api, base); - dc_api_set_curl_multi(api, curl); + dc_loop_add_api(loop, api); config = g_key_file_new(); return_if_true(config == NULL, false); @@ -172,39 +79,14 @@ static bool init_everything(void) return true; } -static void handle_multi_info(void) -{ - struct CURLMsg *msg = NULL; - int remain = 0; - - /* check for finished multi curls - */ - msg = curl_multi_info_read(curl, &remain); - if (msg != NULL) { - if (remain <= 0) { - if (evtimer_pending(timer, NULL)) { - evtimer_del(timer); - } - } - if (msg->msg == CURLMSG_DONE) { - dc_api_signal(api, msg->easy_handle, msg->data.result); - } - } else { - usleep(10 * 1000); - } -} - static void *loop_thread(void *arg) { - int ret = 0; - while (!done) { - ret = event_base_loop(base, EVLOOP_ONCE|EVLOOP_NONBLOCK); - if (ret < 0) { + if (!dc_loop_once(loop)) { break; } - handle_multi_info(); + usleep(10 * 1000); } return NULL;