diff --git a/Makefile b/Makefile index c6c9b4366..f56fb78dd 100644 --- a/Makefile +++ b/Makefile @@ -30,25 +30,26 @@ THIRDP_OBJS = $(THIRDP_DIR)/sha1.o \ $(THIRDP_DIR)/curl-websocket.o \ $(THIRDP_DIR)/threadpool.o \ $(THIRDP_DIR)/priority_queue.o -DISCORD_OBJS = $(SRC_DIR)/concord-once.o \ - $(SRC_DIR)/discord-adapter.o \ - $(SRC_DIR)/discord-ratelimit.o \ - $(SRC_DIR)/discord-client.o \ - $(SRC_DIR)/discord-gateway.o \ - $(SRC_DIR)/discord-timer.o \ - $(SRC_DIR)/discord-misc.o \ - $(SRC_DIR)/application_command.o \ - $(SRC_DIR)/interaction.o \ - $(SRC_DIR)/audit_log.o \ - $(SRC_DIR)/channel.o \ - $(SRC_DIR)/emoji.o \ - $(SRC_DIR)/gateway.o \ - $(SRC_DIR)/guild.o \ - $(SRC_DIR)/guild_template.o \ - $(SRC_DIR)/invite.o \ - $(SRC_DIR)/user.o \ - $(SRC_DIR)/voice.o \ - $(SRC_DIR)/webhook.o \ +DISCORD_OBJS = $(SRC_DIR)/concord-once.o \ + $(SRC_DIR)/discord-adapter.o \ + $(SRC_DIR)/discord-adapter_ratelimit.o \ + $(SRC_DIR)/discord-adapter_refcount.o \ + $(SRC_DIR)/discord-client.o \ + $(SRC_DIR)/discord-gateway.o \ + $(SRC_DIR)/discord-timer.o \ + $(SRC_DIR)/discord-misc.o \ + $(SRC_DIR)/application_command.o \ + $(SRC_DIR)/interaction.o \ + $(SRC_DIR)/audit_log.o \ + $(SRC_DIR)/channel.o \ + $(SRC_DIR)/emoji.o \ + $(SRC_DIR)/gateway.o \ + $(SRC_DIR)/guild.o \ + $(SRC_DIR)/guild_template.o \ + $(SRC_DIR)/invite.o \ + $(SRC_DIR)/user.o \ + $(SRC_DIR)/voice.o \ + $(SRC_DIR)/webhook.o \ $(XOBJ) OBJS := $(COGUTILS_OBJS) $(CORE_OBJS) $(THIRDP_OBJS) $(DISCORD_OBJS) \ diff --git a/core/third-party/heap-inl.h b/core/third-party/heap-inl.h deleted file mode 100644 index 1e2ed60e0..000000000 --- a/core/third-party/heap-inl.h +++ /dev/null @@ -1,245 +0,0 @@ -/* Copyright (c) 2013, Ben Noordhuis - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef UV_SRC_HEAP_H_ -#define UV_SRC_HEAP_H_ - -#include /* NULL */ - -#if defined(__GNUC__) -# define HEAP_EXPORT(declaration) __attribute__((unused)) static declaration -#else -# define HEAP_EXPORT(declaration) static declaration -#endif - -struct heap_node { - struct heap_node* left; - struct heap_node* right; - struct heap_node* parent; -}; - -/* A binary min heap. The usual properties hold: the root is the lowest - * element in the set, the height of the tree is at most log2(nodes) and - * it's always a complete binary tree. - * - * The heap function try hard to detect corrupted tree nodes at the cost - * of a minor reduction in performance. Compile with -DNDEBUG to disable. - */ -struct heap { - struct heap_node* min; - unsigned int nelts; -}; - -/* Return non-zero if a < b. */ -typedef int (*heap_compare_fn)(const struct heap_node* a, - const struct heap_node* b); - -/* Public functions. */ -HEAP_EXPORT(void heap_init(struct heap* heap)); -HEAP_EXPORT(struct heap_node* heap_min(const struct heap* heap)); -HEAP_EXPORT(void heap_insert(struct heap* heap, - struct heap_node* newnode, - heap_compare_fn less_than)); -HEAP_EXPORT(void heap_remove(struct heap* heap, - struct heap_node* node, - heap_compare_fn less_than)); -HEAP_EXPORT(void heap_dequeue(struct heap* heap, heap_compare_fn less_than)); - -/* Implementation follows. */ - -HEAP_EXPORT(void heap_init(struct heap* heap)) { - heap->min = NULL; - heap->nelts = 0; -} - -HEAP_EXPORT(struct heap_node* heap_min(const struct heap* heap)) { - return heap->min; -} - -/* Swap parent with child. Child moves closer to the root, parent moves away. */ -static void heap_node_swap(struct heap* heap, - struct heap_node* parent, - struct heap_node* child) { - struct heap_node* sibling; - struct heap_node t; - - t = *parent; - *parent = *child; - *child = t; - - parent->parent = child; - if (child->left == child) { - child->left = parent; - sibling = child->right; - } else { - child->right = parent; - sibling = child->left; - } - if (sibling != NULL) - sibling->parent = child; - - if (parent->left != NULL) - parent->left->parent = parent; - if (parent->right != NULL) - parent->right->parent = parent; - - if (child->parent == NULL) - heap->min = child; - else if (child->parent->left == parent) - child->parent->left = child; - else - child->parent->right = child; -} - -HEAP_EXPORT(void heap_insert(struct heap* heap, - struct heap_node* newnode, - heap_compare_fn less_than)) { - struct heap_node** parent; - struct heap_node** child; - unsigned int path; - unsigned int n; - unsigned int k; - - newnode->left = NULL; - newnode->right = NULL; - newnode->parent = NULL; - - /* Calculate the path from the root to the insertion point. This is a min - * heap so we always insert at the left-most free node of the bottom row. - */ - path = 0; - for (k = 0, n = 1 + heap->nelts; n >= 2; k += 1, n /= 2) - path = (path << 1) | (n & 1); - - /* Now traverse the heap using the path we calculated in the previous step. */ - parent = child = &heap->min; - while (k > 0) { - parent = child; - if (path & 1) - child = &(*child)->right; - else - child = &(*child)->left; - path >>= 1; - k -= 1; - } - - /* Insert the new node. */ - newnode->parent = *parent; - *child = newnode; - heap->nelts += 1; - - /* Walk up the tree and check at each node if the heap property holds. - * It's a min heap so parent < child must be true. - */ - while (newnode->parent != NULL && less_than(newnode, newnode->parent)) - heap_node_swap(heap, newnode->parent, newnode); -} - -HEAP_EXPORT(void heap_remove(struct heap* heap, - struct heap_node* node, - heap_compare_fn less_than)) { - struct heap_node* smallest; - struct heap_node** max; - struct heap_node* child; - unsigned int path; - unsigned int k; - unsigned int n; - - if (heap->nelts == 0) - return; - - /* Calculate the path from the min (the root) to the max, the left-most node - * of the bottom row. - */ - path = 0; - for (k = 0, n = heap->nelts; n >= 2; k += 1, n /= 2) - path = (path << 1) | (n & 1); - - /* Now traverse the heap using the path we calculated in the previous step. */ - max = &heap->min; - while (k > 0) { - if (path & 1) - max = &(*max)->right; - else - max = &(*max)->left; - path >>= 1; - k -= 1; - } - - heap->nelts -= 1; - - /* Unlink the max node. */ - child = *max; - *max = NULL; - - if (child == node) { - /* We're removing either the max or the last node in the tree. */ - if (child == heap->min) { - heap->min = NULL; - } - return; - } - - /* Replace the to be deleted node with the max node. */ - child->left = node->left; - child->right = node->right; - child->parent = node->parent; - - if (child->left != NULL) { - child->left->parent = child; - } - - if (child->right != NULL) { - child->right->parent = child; - } - - if (node->parent == NULL) { - heap->min = child; - } else if (node->parent->left == node) { - node->parent->left = child; - } else { - node->parent->right = child; - } - - /* Walk down the subtree and check at each node if the heap property holds. - * It's a min heap so parent < child must be true. If the parent is bigger, - * swap it with the smallest child. - */ - for (;;) { - smallest = child; - if (child->left != NULL && less_than(child->left, smallest)) - smallest = child->left; - if (child->right != NULL && less_than(child->right, smallest)) - smallest = child->right; - if (smallest == child) - break; - heap_node_swap(heap, child, smallest); - } - - /* Walk up the subtree and check that each parent is less than the node - * this is required, because `max` node is not guaranteed to be the - * actual maximum in tree - */ - while (child->parent != NULL && less_than(child, child->parent)) - heap_node_swap(heap, child->parent, child); -} - -HEAP_EXPORT(void heap_dequeue(struct heap* heap, heap_compare_fn less_than)) { - heap_remove(heap, heap->min, less_than); -} - -#undef HEAP_EXPORT - -#endif /* UV_SRC_HEAP_H_ */ diff --git a/core/third-party/queue.h b/core/third-party/queue.h index ff3540a0a..697a9f685 100644 --- a/core/third-party/queue.h +++ b/core/third-party/queue.h @@ -20,6 +20,9 @@ typedef void *QUEUE[2]; +/* Improve readability by letting user specify underlying type. */ +#define QUEUE(type) QUEUE + /* Private macros. */ #define QUEUE_NEXT(q) (*(QUEUE **) &((*(q))[0])) #define QUEUE_PREV(q) (*(QUEUE **) &((*(q))[1])) diff --git a/core/user-agent.c b/core/user-agent.c index 098d00658..4de2a6f74 100644 --- a/core/user-agent.c +++ b/core/user-agent.c @@ -37,9 +37,9 @@ struct user_agent { struct ua_conn_queue { /** idle connections */ - QUEUE idle; + QUEUE(struct ua_conn) idle; /* busy connections */ - QUEUE busy; + QUEUE(struct ua_conn) busy; /** total amount of created connection handles */ int total; /** lock for blocking queue operations */ @@ -408,8 +408,8 @@ _ua_conn_cleanup(struct ua_conn *conn) struct ua_conn * ua_conn_start(struct user_agent *ua) { + QUEUE(struct ua_conn) *qelem = NULL; struct ua_conn *conn = NULL; - QUEUE *q; pthread_mutex_lock(&ua->connq->lock); @@ -419,10 +419,10 @@ ua_conn_start(struct user_agent *ua) } else { /* remove from idle queue */ - q = QUEUE_HEAD(&ua->connq->idle); - QUEUE_REMOVE(q); + qelem = QUEUE_HEAD(&ua->connq->idle); + QUEUE_REMOVE(qelem); - conn = QUEUE_DATA(q, struct ua_conn, entry); + conn = QUEUE_DATA(qelem, struct ua_conn, entry); } QUEUE_INSERT_TAIL(&ua->connq->busy, &conn->entry); @@ -513,21 +513,21 @@ ua_init(struct ua_attr *attr) void ua_cleanup(struct user_agent *ua) { - QUEUE *ua_queues[] = { &ua->connq->idle, &ua->connq->busy }; + QUEUE(struct ua_conn) + * ua_queues[] = { &ua->connq->idle, &ua->connq->busy }; size_t i; /* cleanup connection queues */ for (i = 0; i < sizeof(ua_queues) / sizeof(QUEUE *); ++i) { + QUEUE(struct ua_conn) queue, *qelem; struct ua_conn *conn; - QUEUE queue; - QUEUE *q; QUEUE_MOVE(ua_queues[i], &queue); while (!QUEUE_EMPTY(&queue)) { - q = QUEUE_HEAD(&queue); - QUEUE_REMOVE(q); + qelem = QUEUE_HEAD(&queue); + QUEUE_REMOVE(qelem); - conn = QUEUE_DATA(q, struct ua_conn, entry); + conn = QUEUE_DATA(qelem, struct ua_conn, entry); _ua_conn_cleanup(conn); } } diff --git a/include/discord-internal.h b/include/discord-internal.h index 72e23abf2..a222e9b74 100644 --- a/include/discord-internal.h +++ b/include/discord-internal.h @@ -27,7 +27,6 @@ #include "uthash.h" #include "queue.h" -#include "heap-inl.h" #include "priority_queue.h" /** @brief Return 1 if string isn't considered empty */ @@ -156,10 +155,6 @@ struct discord_context { struct ua_conn *conn; /** the request bucket's queue entry */ QUEUE entry; - /** the min-heap node (for selecting timeouts) */ - struct heap_node node; - /** the timeout timestamp */ - u64unix_ms timeout_ms; /** current retry attempt (stop at adapter->retry_limit) */ int retry_attempt; @@ -194,10 +189,8 @@ struct discord_adapter { pthread_mutex_t lock; } * global; - /** idle request handles of type 'struct discord_context' */ - QUEUE *idleq; - /* request timeouts */ - struct heap timeouts; + /** idle request handles */ + QUEUE(struct discord_context) *idleq; /** max amount of retries before a failed request gives up */ int retry_limit; @@ -321,12 +314,10 @@ struct discord_bucket { u64unix_ms reset_tstamp; /** synchronize ratelimiting between threads */ pthread_mutex_t lock; - /** pending requests of type 'struct discord_context' */ - QUEUE waitq; - /** busy requests of type 'struct discord_context' */ - QUEUE busyq; - /** avoid excessive timeouts */ - bool freeze; + /** pending requests */ + QUEUE(struct discord_context) waitq; + /** busy requests */ + QUEUE(struct discord_context) busyq; /** makes this structure hashable */ UT_hash_handle hh; }; @@ -721,7 +712,7 @@ void discord_timers_run(struct discord *client, struct discord_timers *timers); * @param client the client created with discord_init() * @param timers the timer group to perform this operation on * @param timer the timer that should be modified - * @return unsigned the id of the timer + * @return the id of the timer */ unsigned _discord_timer_ctl( struct discord *client, diff --git a/include/discord.h b/include/discord.h index cce2307c5..6e051143e 100644 --- a/include/discord.h +++ b/include/discord.h @@ -290,15 +290,11 @@ struct io_poller *discord_get_io_poller(struct discord *client); struct discord_timer; /**/ -/** - * @brief callback to be used with struct discord_timer - */ +/** @brief callback to be used with struct discord_timer */ typedef void (*discord_ev_timer) (struct discord *client, struct discord_timer *ev); -/** - * @brief flags used to change behaviour of timer - */ +/** @brief flags used to change behaviour of timer */ enum discord_timer_flags { /** use milliseconds for interval and start_time */ DISCORD_TIMER_MILLISECONDS = 0, @@ -312,9 +308,7 @@ enum discord_timer_flags { DISCORD_TIMER_CANCELED = 1 << 3, }; -/** - * @brief struct used for modifying, and getting info about a timer - */ +/** @brief struct used for modifying, and getting info about a timer */ struct discord_timer { /** the identifier used for the timer. 0 creates a new timer */ unsigned id; @@ -337,7 +331,7 @@ struct discord_timer { * * @param client the client created with discord_init() * @param timer the timer that should be modified - * @return unsigned the id of the timer + * @return the id of the timer */ unsigned discord_timer_ctl(struct discord *client, struct discord_timer *timer); @@ -349,7 +343,7 @@ unsigned discord_timer_ctl(struct discord *client, struct discord_timer *timer); * @param cb the callback that should be called when timer triggers * @param data user data * @param delay delay before timer should start in milliseconds - * @return unsigned + * @return the id of the timer */ unsigned discord_timer(struct discord *client, discord_ev_timer cb, void *data, int64_t delay); diff --git a/src/discord-adapter.c b/src/discord-adapter.c index 412d3eff5..a81cfc4da 100644 --- a/src/discord-adapter.c +++ b/src/discord-adapter.c @@ -79,8 +79,6 @@ discord_adapter_init(struct discord_adapter *adapter, * share the same queue with the original */ adapter->idleq = malloc(sizeof(QUEUE)); QUEUE_INIT(adapter->idleq); - /* initialize min-heap for handling request timeouts */ - heap_init(&adapter->timeouts); adapter->retry_limit = 3; /**< hard limit for now */ } @@ -95,9 +93,8 @@ _discord_context_cleanup(struct discord_context *cxt) void discord_adapter_cleanup(struct discord_adapter *adapter) { + QUEUE(struct discord_context) queue, *qelem; struct discord_context *cxt; - QUEUE queue; - QUEUE *qelem; /* cleanup User-Agent handle */ ua_cleanup(adapter->ua); @@ -458,7 +455,6 @@ _discord_context_reset(struct discord_context *cxt) *cxt->endpoint = '\0'; *cxt->route = '\0'; cxt->conn = NULL; - cxt->timeout_ms = 0; cxt->retry_attempt = 0; discord_attachments_cleanup(&cxt->req.attachments); @@ -504,81 +500,6 @@ _discord_context_populate(struct discord_context *cxt, cxt->bucket = discord_bucket_get(adapter, route); } -static int -timer_less_than(const struct heap_node *ha, const struct heap_node *hb) -{ - const struct discord_context *a = - CONTAINEROF(ha, struct discord_context, node); - const struct discord_context *b = - CONTAINEROF(hb, struct discord_context, node); - - return a->timeout_ms <= b->timeout_ms; -} - -static void -_discord_context_set_timeout(struct discord_adapter *adapter, - u64unix_ms timeout, - struct discord_context *cxt) -{ - cxt->bucket->freeze = true; - - cxt->timeout_ms = timeout; - - heap_insert(&adapter->timeouts, &cxt->node, &timer_less_than); -} - -/* true if a timeout has been set, false otherwise */ -static bool -_discord_context_timeout(struct discord_adapter *adapter, - struct discord_context *cxt) -{ - u64unix_ms now = NOW(adapter); - u64unix_ms timeout = discord_bucket_get_timeout(adapter, cxt->bucket); - - if (now > timeout) return false; - - logconf_info(&adapter->conf, - "[%.4s] RATELIMITING (timeout %" PRId64 " ms)", - cxt->bucket->hash, (int64_t)(timeout - now)); - - _discord_context_set_timeout(adapter, timeout, cxt); - - return true; -} - -void -discord_refcount_incr(struct discord_adapter *adapter, - void *data, - void (*cleanup)(void *data)) -{ - struct discord_refcount *ref = NULL; - - HASH_FIND_PTR(adapter->refcounts, &data, ref); - if (NULL == ref) { - ref = calloc(1, sizeof *ref); - ref->data = data; - ref->cleanup = cleanup; - - HASH_ADD_PTR(adapter->refcounts, data, ref); - } - - ++ref->visits; -} - -void -discord_refcount_decr(struct discord_adapter *adapter, void *data) -{ - struct discord_refcount *ref = NULL; - - HASH_FIND_PTR(adapter->refcounts, &data, ref); - if (ref && --ref->visits <= 0) { - if (ref->cleanup) ref->cleanup(ref->data); - - HASH_DEL(adapter->refcounts, ref); - free(ref); - } -} - /* enqueue a request to be executed asynchronously */ static CCORDcode _discord_adapter_run_async(struct discord_adapter *adapter, @@ -596,7 +517,7 @@ _discord_adapter_run_async(struct discord_adapter *adapter, } else { /* get from idle requests queue */ - QUEUE *qelem = QUEUE_HEAD(adapter->idleq); + QUEUE(struct discord_context) *qelem = QUEUE_HEAD(adapter->idleq); QUEUE_REMOVE(qelem); cxt = QUEUE_DATA(qelem, struct discord_context, entry); @@ -623,12 +544,18 @@ _discord_adapter_run_async(struct discord_adapter *adapter, /* add a request to libcurl's multi handle */ static CCORDcode _discord_adapter_send(struct discord_adapter *adapter, - struct discord_context *cxt) + struct discord_bucket *b) { struct ua_conn_attr conn_attr = { 0 }; + struct discord_context *cxt; CURLMcode mcode; CURL *ehandle; + QUEUE(struct discord_context) *qelem = QUEUE_HEAD(&b->waitq); + QUEUE_REMOVE(qelem); + QUEUE_INIT(qelem); + + cxt = QUEUE_DATA(qelem, struct discord_context, entry); cxt->conn = ua_conn_start(adapter->ua); conn_attr.method = cxt->method; @@ -660,72 +587,18 @@ _discord_adapter_send(struct discord_adapter *adapter, return mcode ? CCORD_CURLM_INTERNAL : CCORD_OK; } -/* check and enqueue requests that have been timed out */ -static CCORDcode -_discord_adapter_check_timeouts(struct discord_adapter *adapter) -{ - struct discord_context *cxt; - struct heap_node *hmin; - - while (1) { - hmin = heap_min(&adapter->timeouts); - if (!hmin) break; - - cxt = CONTAINEROF(hmin, struct discord_context, node); - if (cxt->timeout_ms > NOW(adapter)) { - /* current timestamp is lesser than lowest timeout */ - break; - } - - heap_remove(&adapter->timeouts, hmin, &timer_less_than); - - cxt->bucket->freeze = false; - QUEUE_INSERT_HEAD(&cxt->bucket->waitq, &cxt->entry); - } - - return CCORD_OK; -} - -/* send a standalone request to update stale bucket values */ -static CCORDcode -_discord_adapter_send_single(struct discord_adapter *adapter, - struct discord_bucket *b) -{ - struct discord_context *cxt; - QUEUE *qelem; - - qelem = QUEUE_HEAD(&b->waitq); - QUEUE_REMOVE(qelem); - QUEUE_INIT(qelem); - - cxt = QUEUE_DATA(qelem, struct discord_context, entry); - - return _discord_adapter_send(adapter, cxt); -} - /* send a batch of requests */ static CCORDcode _discord_adapter_send_batch(struct discord_adapter *adapter, struct discord_bucket *b) { - struct discord_context *cxt; CCORDcode code = CCORD_OK; - QUEUE *qelem; long i; for (i = b->remaining; i > 0; --i) { if (QUEUE_EMPTY(&b->waitq)) break; - qelem = QUEUE_HEAD(&b->waitq); - QUEUE_REMOVE(qelem); - QUEUE_INIT(qelem); - - cxt = QUEUE_DATA(qelem, struct discord_context, entry); - - /* timeout request if ratelimiting is necessary */ - if (_discord_context_timeout(adapter, cxt)) break; - - code = _discord_adapter_send(adapter, cxt); + code = _discord_adapter_send(adapter, b); if (code != CCORD_OK) break; } @@ -739,18 +612,17 @@ _discord_adapter_check_pending(struct discord_adapter *adapter) /* iterate over buckets in search of pending requests */ for (b = adapter->buckets; b != NULL; b = b->hh.next) { - /* skip timed-out, busy and non-pending buckets */ - if (b->freeze || !QUEUE_EMPTY(&b->busyq) || QUEUE_EMPTY(&b->waitq)) { + /* skip busy and non-pending buckets */ + if (!QUEUE_EMPTY(&b->busyq) || QUEUE_EMPTY(&b->waitq)) { continue; } /* if bucket is outdated then its necessary to send a single * request to fetch updated values */ if (b->reset_tstamp < NOW(adapter)) { - _discord_adapter_send_single(adapter, b); + _discord_adapter_send(adapter, b); continue; } - /* send remainder or trigger timeout */ _discord_adapter_send_batch(adapter, b); } @@ -835,11 +707,7 @@ _discord_adapter_check_action(struct discord_adapter *adapter, if (retry && cxt->retry_attempt++ < adapter->retry_limit) { ua_conn_reset(cxt->conn); - if (wait_ms > 0) { - u64unix_ms timeout = NOW(adapter) + (u64unix_ms)wait_ms; - _discord_context_set_timeout(adapter, timeout, cxt); - } - else { + if (wait_ms <= 0) { QUEUE_INSERT_HEAD(&cxt->bucket->waitq, &cxt->entry); } } @@ -859,9 +727,6 @@ discord_adapter_perform(struct discord_adapter *adapter) CCORDcode code; int alive = 0; - if (CCORD_OK != (code = _discord_adapter_check_timeouts(adapter))) - return code; - if (CCORD_OK != (code = _discord_adapter_check_pending(adapter))) return code; @@ -888,21 +753,9 @@ discord_adapter_perform(struct discord_adapter *adapter) void discord_adapter_stop_all(struct discord_adapter *adapter) { + QUEUE(struct discord_context) *qelem = NULL; struct discord_context *cxt; struct discord_bucket *b; - struct heap_node *hmin; - QUEUE *qelem; - - /* cancel pending timeouts */ - while ((hmin = heap_min(&adapter->timeouts)) != NULL) { - cxt = CONTAINEROF(hmin, struct discord_context, node); - - heap_remove(&adapter->timeouts, hmin, &timer_less_than); - - cxt->bucket->freeze = false; - - QUEUE_INSERT_TAIL(adapter->idleq, &cxt->entry); - } /* cancel bucket's on-going transfers */ for (b = adapter->buckets; b != NULL; b = b->hh.next) { diff --git a/src/discord-ratelimit.c b/src/discord-adapter_ratelimit.c similarity index 99% rename from src/discord-ratelimit.c rename to src/discord-adapter_ratelimit.c index cf3cbc660..5c553be21 100644 --- a/src/discord-ratelimit.c +++ b/src/discord-adapter_ratelimit.c @@ -337,9 +337,8 @@ _discord_bucket_null_filter(struct discord_adapter *adapter, struct discord_bucket *b, const char route[DISCORD_ROUTE_LEN]) { + QUEUE(struct discord_context) queue, *qelem; struct discord_context *cxt; - QUEUE queue; - QUEUE *qelem; QUEUE_MOVE(&adapter->b_null->waitq, &queue); QUEUE_INIT(&adapter->b_null->waitq); diff --git a/src/discord-adapter_refcount.c b/src/discord-adapter_refcount.c new file mode 100644 index 000000000..7cff6a1df --- /dev/null +++ b/src/discord-adapter_refcount.c @@ -0,0 +1,38 @@ +#include +#include + +#include "discord.h" +#include "discord-internal.h" + +void +discord_refcount_incr(struct discord_adapter *adapter, + void *data, + void (*cleanup)(void *data)) +{ + struct discord_refcount *ref = NULL; + + HASH_FIND_PTR(adapter->refcounts, &data, ref); + if (NULL == ref) { + ref = calloc(1, sizeof *ref); + ref->data = data; + ref->cleanup = cleanup; + + HASH_ADD_PTR(adapter->refcounts, data, ref); + } + + ++ref->visits; +} + +void +discord_refcount_decr(struct discord_adapter *adapter, void *data) +{ + struct discord_refcount *ref = NULL; + + HASH_FIND_PTR(adapter->refcounts, &data, ref); + if (ref && --ref->visits <= 0) { + if (ref->cleanup) ref->cleanup(ref->data); + + HASH_DEL(adapter->refcounts, ref); + free(ref); + } +}