From d0e7c5a4056deb50f50d619d6b4252fcfcb6268c Mon Sep 17 00:00:00 2001 From: Anotra Date: Fri, 2 Feb 2024 21:35:45 -0500 Subject: [PATCH 01/14] fix(anomap): update->latest(breaking function changes), and slight change to cache garbage collection --- core/anomap.c | 350 ++++++++++++++++++++++++++++++++++++-------- core/anomap.h | 66 +++++++-- src/discord-cache.c | 52 ++++--- 3 files changed, 380 insertions(+), 88 deletions(-) diff --git a/core/anomap.c b/core/anomap.c index 7f692ceb..2a3ff4f0 100644 --- a/core/anomap.c +++ b/core/anomap.c @@ -7,48 +7,91 @@ #include "anomap.h" +#define ANOMAP_ALLOWED_OPTIONS ( anomap_reverse_order \ + | anomap_direct_access \ + | anomap_preserve_order \ + ) + struct anomap { int (*cmp)(const void *, const void *); + anomap_options options; + bool free_on_cleanup; struct { anomap_on_item_changed *cb; void *data; } on_changed; struct { unsigned *arr; - size_t len, cap; + size_t len : 32, cap : 32; size_t highest; } map; struct { char *arr; - size_t cap, size; + size_t cap : 32, size : 32; } keys; struct { char *arr; - size_t cap, size; + size_t cap : 32, size : 32; } vals; + struct { + struct { + unsigned prev, next; + } *arr; + size_t cap : 32; + unsigned tail; + } order; }; +anomap_options +anomap_supported_options(void) { + return ANOMAP_ALLOWED_OPTIONS; +} + +size_t +anomap_struct_size(void) { + return sizeof(struct anomap); +} + +bool +anomap_init(struct anomap *map, + size_t key_size, size_t val_size, + int (*cmp)(const void *, const void *), + anomap_options options) +{ + if (!key_size || !cmp || (options & ~ANOMAP_ALLOWED_OPTIONS)) + return false; + memset(map, 0, sizeof *map); + map->free_on_cleanup = false; + map->options = options; + map->cmp = cmp; + map->keys.size = key_size; + map->vals.size = val_size; + return true; +} + struct anomap * anomap_create(size_t key_size, size_t val_size, - int (*cmp)(const void *, const void *)) { + int (*cmp)(const void *, const void *), + anomap_options options) +{ struct anomap *map = calloc(1, sizeof *map); - if (map) { - map->cmp = cmp; - map->keys.size = key_size; - map->vals.size = val_size; - return map; - } - return NULL; + if (!map) return NULL; + if (anomap_init(map, key_size, val_size, cmp, options)) + return map->free_on_cleanup = true, map; + return free(map), NULL; } void anomap_destroy(struct anomap *map) { anomap_clear(map); + free(map->map.arr); free(map->keys.arr); free(map->vals.arr); - free(map->map.arr); + free(map->order.arr); + const bool free_on_cleanup = map->free_on_cleanup; memset(map, 0, sizeof *map); - free(map); + if (free_on_cleanup) + free(map); } void @@ -64,12 +107,25 @@ anomap_length(struct anomap *map) { return map->map.len; } -void anomap_clear(struct anomap *map) { +static inline void +_anomap_on_empty(struct anomap *map) { + if (map->map.len) return; + if (map->options & anomap_preserve_order) + map->order.tail = map->order.arr[0].next = map->order.arr[0].prev = 0; + map->map.highest = 0; +} + +void +anomap_clear(struct anomap *map) { + if (0 == map->map.len) + return; + for (size_t i = 0; i < map->map.len; i++) { if (!map->on_changed.cb) break; unsigned pos = map->map.arr[i]; - map->on_changed.cb(map, + map->on_changed.cb( &(struct anomap_item_changed) { + .map = map, .data = map->on_changed.data, .op = anomap_delete, .key = map->keys.arr + map->keys.size * pos, @@ -77,7 +133,13 @@ void anomap_clear(struct anomap *map) { }); } map->map.len = 0; - map->map.highest = 0; + _anomap_on_empty(map); +} + +bool +anomap_contains(struct anomap *map, void *key) { + size_t position; + return anomap_index_of(map, key, &position); } bool @@ -85,18 +147,32 @@ anomap_index_of(struct anomap *map, void *key, size_t *position) { size_t lo = 0, mid, hi = map->map.len; const char *const keys = map->keys.arr; const size_t key_size = map->keys.size; + int result; - if (map->map.len >= 0x10 // support fast appending - && map->cmp(key, keys + key_size * map->map.arr[map->map.len - 1]) > 0) - return *position = map->map.len, false; + if (0 == map->map.len) goto on_empty; + result = map->cmp(key, keys + key_size * map->map.arr[map->map.len - 1]); + if (0 == result) return *position = map->map.len - 1, true; - while (lo < hi) { - mid = lo + (hi - lo) / 2; - int r = map->cmp(key, keys + key_size * map->map.arr[mid]); - if (r == 0) return *position = mid, true; - if (r > 0) lo = mid + 1; - else hi = mid; +# define BINARY_SEARCH(cmp_operator) \ + if (result cmp_operator 0) \ + return *position = map->map.len, false; \ + while (lo < hi) { \ + mid = lo + (hi - lo) / 2; \ + result = map->cmp(key, keys + key_size * map->map.arr[mid]); \ + if (result == 0) return *position = mid, true; \ + if (result cmp_operator 0) lo = mid + 1; \ + else hi = mid; \ + } + + if (map->options & anomap_reverse_order) { + BINARY_SEARCH(<); + } else { + BINARY_SEARCH(>); } + +# undef BINARY_SEARCH + + on_empty: return *position = lo, false; } @@ -110,42 +186,81 @@ anomap_at_index(struct anomap *map, size_t index, void *key, void *val) { return true; } +const void * +anomap_direct_key_at_index(struct anomap *map, size_t index) { + if (!(map->options & anomap_direct_access)) + return NULL; + if (index >= map->map.len) + return NULL; + return map->keys.arr + map->keys.size * map->map.arr[index]; +} + +void * +anomap_direct_val_at_index(struct anomap *map, size_t index) { + if (!(map->options & anomap_direct_access)) + return NULL; + if (index >= map->map.len) + return NULL; + if (!map->vals.size) + return NULL; + return map->vals.arr + map->vals.size * map->map.arr[index]; +} + static bool _anomap_ensure_capacity(struct anomap *map, size_t capacity) { - if (capacity > ~(unsigned)0) return false; + if (capacity > (size_t)1 << 31) return false; if (capacity <= map->map.cap) return true; size_t cap = map->map.cap ? map->map.cap << 1 : 8; - while (cap < capacity) cap <<= 1; - if (map->keys.cap < cap) { - void *tmp = realloc(map->keys.arr, map->keys.size * cap); - if (!tmp) return false; - map->keys.arr = tmp; - map->keys.cap = cap; - } - if (map->vals.size && map->vals.cap < cap) { - void *tmp = realloc(map->vals.arr, map->vals.size * cap); - if (!tmp) return false; - map->vals.arr = tmp; - map->vals.cap = cap; - } - if (map->map.cap < cap) { - unsigned *tmp = realloc(map->map.arr, sizeof *map->map.arr * cap); - if (!tmp) return false; - map->map.arr = tmp; - map->map.cap = cap; - } + while (cap < capacity) if (0 == (cap <<= 1)) return false; + +#define RESIZE_ARRAY(ARRAY, ELEMENT_SIZE, CAPACITY, NEW_CAPACITY) \ + do { \ + if (CAPACITY < NEW_CAPACITY) { \ + void *tmp = /* use calloc initially to be safe */ \ + 0 == CAPACITY ? calloc((NEW_CAPACITY), (ELEMENT_SIZE)) \ + : realloc(ARRAY, (NEW_CAPACITY) *(ELEMENT_SIZE)); \ + if (!tmp) return false; \ + ARRAY = tmp; \ + CAPACITY = NEW_CAPACITY; \ + } \ + } while (0) + + RESIZE_ARRAY(map->map.arr, sizeof *map->map.arr, map->map.cap, cap); + + RESIZE_ARRAY(map->keys.arr, map->keys.size, map->keys.cap, cap); + + if (map->vals.size) + RESIZE_ARRAY(map->vals.arr, map->vals.size, map->vals.cap, cap); + + if (map->options & anomap_preserve_order) + RESIZE_ARRAY(map->order.arr, sizeof *map->order.arr, map->order.cap, cap); + return true; } -enum anomap_operation -anomap_do(struct anomap *map, enum anomap_operation operation, +static void +_unlink_element(struct anomap *map, unsigned pos) { + if (0 == map->map.len) { + map->order.tail = map->order.arr[0].next = map->order.arr[0].prev = 0; + } else { + const unsigned prev = map->order.arr[pos].prev; + const unsigned next = map->order.arr[pos].next; + map->order.arr[next].prev = prev; + map->order.arr[prev].next = next; + if (map->order.tail == pos) + map->order.tail = prev; + } +} + +anomap_operation +anomap_do(struct anomap *map, anomap_operation operation, void *key, void *val) { const size_t key_size = map->keys.size, val_size = map->vals.size; - enum anomap_operation result = 0; + anomap_operation result = 0; size_t mpos = 0; if (!anomap_index_of(map, key, &mpos)) { - if (~operation & anomap_insert) + if (!(operation & anomap_insert)) return 0; if (!_anomap_ensure_capacity(map, map->map.len + 1)) return 0; @@ -162,10 +277,20 @@ anomap_do(struct anomap *map, enum anomap_operation operation, sizeof *map->map.arr * (map->map.len - mpos)); map->map.arr[mpos] = pos; map->map.len++; + + if (map->options & anomap_preserve_order) { + unsigned tail = map->order.tail; + unsigned head = map->order.arr[tail].next; + map->order.arr[map->order.arr[pos].prev = tail].next = pos; + map->order.arr[map->order.arr[pos].next = head].prev = pos; + map->order.tail = pos; + } + result |= anomap_insert; if (map->on_changed.cb) - map->on_changed.cb(map, + map->on_changed.cb( &(struct anomap_item_changed) { + .map = map, .data = map->on_changed.data, .op = anomap_insert, .key = key, @@ -178,8 +303,9 @@ anomap_do(struct anomap *map, enum anomap_operation operation, result |= anomap_update; void *const val_to_update = map->vals.arr + val_size * pos; if (map->on_changed.cb) - map->on_changed.cb(map, + map->on_changed.cb( &(struct anomap_item_changed) { + .map = map, .data = map->on_changed.data, .op = anomap_update, .key = key, @@ -191,8 +317,7 @@ anomap_do(struct anomap *map, enum anomap_operation operation, char tmp[0x1000]; char *a = val_to_update; char *b = val; - size_t amount_left = map->vals.size; - for (size_t i = 0; amount_left; i += sizeof tmp) { + for (size_t amount_left = map->vals.size; amount_left;) { size_t current_block = amount_left; if (current_block > sizeof tmp) current_block = sizeof tmp; memcpy(tmp, a, current_block); @@ -217,8 +342,9 @@ anomap_do(struct anomap *map, enum anomap_operation operation, result |= anomap_delete; void *const deleted_item = map->vals.arr + val_size * pos; if (map->on_changed.cb) - map->on_changed.cb(map, + map->on_changed.cb( &(struct anomap_item_changed) { + .map = map, .data = map->on_changed.data, .op = anomap_delete, .key = key, @@ -228,6 +354,9 @@ anomap_do(struct anomap *map, enum anomap_operation operation, memmove(map->map.arr + mpos, map->map.arr + mpos + 1, sizeof *map->map.arr * (map->map.len - mpos)); map->map.arr[map->map.len] = pos; + if (map->options & anomap_preserve_order) + _unlink_element(map, pos); + _anomap_on_empty(map); } return result; } @@ -242,8 +371,8 @@ anomap_copy_range(struct anomap *map, size_t from_index, size_t to_index, if (keys || vals) { const size_t key_size = map->keys.size; const size_t val_size = map->vals.size; - bool going_up = from_index <= to_index; - for (size_t i = 0;; i++, going_up ? from_index++ : from_index--) { + const int next = from_index <= to_index ? 1 : -1; + for (size_t i = 0;; i++, from_index += next) { unsigned pos = map->map.arr[from_index]; if (keys) memcpy(((char *)keys) + key_size * i, map->keys.arr + key_size * pos, @@ -263,12 +392,18 @@ anomap_delete_range(struct anomap *map, size_t from_index, size_t to_index, { size_t count = anomap_copy_range(map, from_index, to_index, keys, vals); if (!count) return 0; - bool going_up = from_index <= to_index; - for (size_t i = from_index;; going_up ? i++ : i--) { + const int next = from_index <= to_index ? 1 : -1; + if (map->options & anomap_preserve_order) + for (size_t i = from_index;; i += next) { + _unlink_element(map, map->map.arr[i]); + if (i == to_index) break; + } + for (size_t i = from_index;; i += next) { if (!map->on_changed.cb) break; unsigned pos = map->map.arr[i]; - map->on_changed.cb(map, + map->on_changed.cb( &(struct anomap_item_changed) { + .map = map, .data = map->on_changed.data, .op = anomap_delete, .key = map->keys.arr + map->keys.size * pos, @@ -290,5 +425,102 @@ anomap_delete_range(struct anomap *map, size_t from_index, size_t to_index, memcpy(map->map.arr + map->map.len, tmp, copy_size); remaining -= block; } + _anomap_on_empty(map); return count; } + +void +anomap_foreach(struct anomap *map, anomap_foreach_cb *cb, void *data) { + const size_t key_size = map->keys.size, val_size = map->vals.size; + if (0 == map->map.len) + return; + if (map->options & anomap_preserve_order) { + const unsigned tail = map->order.tail; + unsigned pos = map->order.arr[tail].next; + switch (val_size ? 1 : 0) { + while (tail != pos) { + pos = map->order.arr[pos].next; + case 1: + cb(map, data, map->keys.arr + key_size * pos, + map->vals.arr + val_size * pos); + } break; + + while (tail != pos) { + pos = map->order.arr[pos].next; + case 0: + cb(map, data, map->keys.arr + key_size * pos, NULL); + } break; + } + } else { + if (val_size) { + for (size_t i=0; imap.len; i++) + cb(map, data, map->keys.arr + key_size * map->map.arr[i], + map->vals.arr + val_size * map->map.arr[i]); + } else { + for (size_t i=0; imap.len; i++) + cb(map, data, map->keys.arr + key_size * map->map.arr[i], NULL); + } + } +} + +bool +anomap_advance(struct anomap *map, size_t *index, anomap_position *position) { + unsigned pos; + if (0 == map->map.len) + return false; + if (map->options & anomap_preserve_order) { + switch (*position) { + case anomap_head: + pos = map->order.arr[map->order.tail].next; + anomap_index_of(map, map->keys.arr + map->keys.size * pos, index); + *position = anomap_next; + return true; + case anomap_tail: + pos = map->order.tail; + anomap_index_of(map, map->keys.arr + map->keys.size * pos, index); + *position = anomap_prev; + return true; + case anomap_next: + pos = map->map.arr[*index]; + if (pos == map->order.tail) + return false; + pos = map->order.arr[pos].next; + anomap_index_of(map, map->keys.arr + map->keys.size * pos, index); + return true; + case anomap_prev: + pos = map->map.arr[*index]; + if (pos == map->order.arr[map->order.tail].next) + return false; + pos = map->order.arr[pos].prev; + anomap_index_of(map, map->keys.arr + map->keys.size * pos, index); + return true; + } + } else { + switch (*position) { + case anomap_head: + *index = 0; + *position = anomap_next; + return true; + case anomap_tail: + *index = map->map.len - 1; + *position = anomap_prev; + return true; + case anomap_next: + if (*index >= map->map.len - 1) + return false; + (*index)++; + return true; + case anomap_prev: + if (0 == *index) + return false; + (*index)--; + return true; + } + } + return false; +} + +int +anomap_cmp_str(const void *a, const void *b) { + return strcmp(*(char **)a, *(char **)b); +} diff --git a/core/anomap.h b/core/anomap.h index 93cb7f41..697b4f94 100644 --- a/core/anomap.h +++ b/core/anomap.h @@ -17,23 +17,48 @@ return *(data_type *)a > *(data_type *)b ? 1 : -1; \ } -enum anomap_operation { +typedef enum { +#define ANOMAP_REVERSE_ORDER anomap_reverse_order + anomap_reverse_order = 1 << 0, +#define ANOMAP_DIRECT_ACCESS anomap_direct_access + anomap_direct_access = 1 << 1, +#define ANOMAP_PRESERVE_ORDER anomap_preserve_order + anomap_preserve_order = 1 << 2, +} anomap_options; + +typedef enum { +#define ANOMAP_INSERT anomap_insert anomap_insert = 1 << 0, +#define ANOMAP_UPDATE anomap_update anomap_update = 1 << 1, +#define ANOMAP_UPSERT anomap_upsert anomap_upsert = anomap_insert | anomap_update, +#define ANOMAP_DELETE anomap_delete anomap_delete = 1 << 2, +#define ANOMAP_GETVAL anomap_getval anomap_getval = 1 << 3, -}; +} anomap_operation; struct anomap; +anomap_options anomap_supported_options(void); + +size_t anomap_struct_size(void); + +bool anomap_init(struct anomap *map, + size_t key_size, size_t val_size, + int (*cmp)(const void *, const void *), + anomap_options options); + struct anomap *anomap_create(size_t key_size, size_t val_size, - int (*cmp)(const void *, const void *)); + int (*cmp)(const void *, const void *), + anomap_options options); void anomap_destroy(struct anomap *map); struct anomap_item_changed { + struct anomap *map; void *data; - enum anomap_operation op; + anomap_operation op; void *key; struct { void *prev; @@ -41,8 +66,7 @@ struct anomap_item_changed { } val; }; -typedef void anomap_on_item_changed( - struct anomap *map, struct anomap_item_changed *item_changed); +typedef void anomap_on_item_changed(const struct anomap_item_changed *ev); void anomap_set_on_item_changed( struct anomap *map, anomap_on_item_changed *on_changed, void *data); @@ -50,12 +74,15 @@ void anomap_set_on_item_changed( size_t anomap_length(struct anomap *map); void anomap_clear(struct anomap *map); +bool anomap_contains(struct anomap *map, void *key); bool anomap_index_of(struct anomap *map, void *key, size_t *index); bool anomap_at_index(struct anomap *map, size_t index, void *key, void *val); +const void *anomap_direct_key_at_index(struct anomap *map, size_t index); +void *anomap_direct_val_at_index(struct anomap *map, size_t index); -enum anomap_operation anomap_do(struct anomap *map, - enum anomap_operation operation, - void *key, void *val); +anomap_operation anomap_do(struct anomap *map, + anomap_operation operation, + void *key, void *val); size_t anomap_copy_range(struct anomap *map, size_t from_index, size_t to_index, @@ -64,4 +91,25 @@ size_t anomap_delete_range(struct anomap *map, size_t from_index, size_t to_index, void *keys, void *vals); +typedef void anomap_foreach_cb(struct anomap *map, void *data, + const void *key, const void *val); +void anomap_foreach(struct anomap *map, anomap_foreach_cb *cb, void *data); + +typedef enum { +#define ANOMAP_HEAD anomap_head + anomap_head, +#define ANOMAP_TAIL anomap_tail + anomap_tail, +#define ANOMAP_PREV anomap_prev + anomap_prev, +#define ANOMAP_NEXT anomap_next + anomap_next, +} anomap_position; + +bool anomap_advance(struct anomap *map, + size_t *index, + anomap_position *position); + +int anomap_cmp_str(const void *a, const void *b); + #endif // !ANOMAP_H diff --git a/src/discord-cache.c b/src/discord-cache.c index 5661af4d..39aa0027 100644 --- a/src/discord-cache.c +++ b/src/discord-cache.c @@ -119,7 +119,7 @@ EV_CB(guild_create, discord_guild) { CACHE_BEGIN(data, cache, shard, ev->id); GUILD_BEGIN(guild); - enum anomap_operation op = anomap_insert; + anomap_operation op = anomap_insert; anomap_do(cache->guild_map, op, (u64snowflake *)&ev->id, &guild); CACHE_END(cache); } @@ -129,7 +129,7 @@ EV_CB(guild_update, discord_guild) CACHE_BEGIN(data, cache, shard, ev->id); GUILD_BEGIN(guild); struct anomap *map = cache->guild_map; - enum anomap_operation op = anomap_upsert; + anomap_operation op = anomap_upsert; anomap_do(map, op, (u64snowflake *)&ev->id, &guild); CACHE_END(cache); } @@ -139,7 +139,7 @@ EV_CB(guild_delete, discord_guild) CACHE_BEGIN(data, cache, shard, ev->id); struct discord_guild *guild = NULL; struct anomap *map = cache->guild_map; - enum anomap_operation op = anomap_delete; + anomap_operation op = anomap_delete; anomap_do(map, op, (u64snowflake *)&ev->id, &guild); CACHE_END(cache); } @@ -173,25 +173,38 @@ EV_CB(message_delete, discord_message_delete) CACHE_END(cache); } +static void +_delete_old_messages_from_cache(struct _discord_shard_cache *cache, + unsigned minutes_old) +{ + u64snowflake delete_before = + ((cog_timestamp_ms() - DISCORD_EPOCH) - minutes_old * 60 * 1000) << 22; + size_t idx; + anomap_index_of(cache->msg_map, &delete_before, &idx); + if (idx--) anomap_delete_range(cache->msg_map, 0, idx, NULL, NULL); +} + static void _on_garbage_collection(struct discord *client, struct discord_timer *timer) { (void)client; struct _discord_cache_data *data = timer->data; + timer->repeat = 1; + timer->interval = 1000 * 60; + for (int i = 0; i < data->total_shards; i++) { struct _discord_shard_cache *const cache = &data->caches[i]; - pthread_mutex_lock(&cache->lock); - { // DELETE MESSAGES - u64snowflake delete_before = - ((cog_timestamp_ms() - DISCORD_EPOCH) - 10 * 60 * 1000) << 22; - size_t idx; - anomap_index_of(cache->msg_map, &delete_before, &idx); - if (idx--) anomap_delete_range(cache->msg_map, 0, idx, NULL, NULL); - } // !DELETE MESSAGES - pthread_mutex_unlock(&cache->lock); + if (0 == pthread_mutex_trylock(&cache->lock)) { + _delete_old_messages_from_cache(cache, 15 /* minutes */); + pthread_mutex_unlock(&cache->lock); + } + else { + // couldn't get lock, don't block, + // just try again in 10 seconds instead of 60 seconds + timer->interval = 1000 * 10; + continue; + } } - timer->repeat = 1; - timer->interval = 1000 * 60; } static void @@ -215,19 +228,17 @@ _discord_cache_cleanup(struct discord *client) } static void -_on_guild_map_changed(struct anomap *map, struct anomap_item_changed *ev) +_on_guild_map_changed(const struct anomap_item_changed *ev) { struct discord_refcounter *rc = &((struct discord *)ev->data)->refcounter; - (void)map; if (ev->op & (anomap_update | anomap_delete)) discord_refcounter_decr(rc, *(void **)ev->val.prev); } static void -_on_map_changed(struct anomap *map, struct anomap_item_changed *ev) +_on_map_changed(const struct anomap_item_changed *ev) { struct discord_refcounter *rc = &((struct discord *)ev->data)->refcounter; - (void)map; if (ev->op & (anomap_delete | anomap_update)) discord_refcounter_decr(rc, *(void **)ev->val.prev); if (ev->op & anomap_upsert) @@ -251,10 +262,11 @@ discord_cache_enable(struct discord *client, struct _discord_shard_cache *cache = &data->caches[i]; pthread_mutex_init(&cache->lock, NULL); const size_t sf_sz = sizeof(u64snowflake); - cache->guild_map = anomap_create(sf_sz, sizeof(void *), _cmp_sf); + cache->guild_map = + anomap_create(sf_sz, sizeof(void *), _cmp_sf, 0); anomap_set_on_item_changed(cache->guild_map, _on_guild_map_changed, client); - cache->msg_map = anomap_create(sf_sz, sizeof(void *), _cmp_sf); + cache->msg_map = anomap_create(sf_sz, sizeof(void *), _cmp_sf, 0); anomap_set_on_item_changed(cache->msg_map, _on_map_changed, client); } From e7111aab5d1a896a4545b5e74b70ed1c778b273d Mon Sep 17 00:00:00 2001 From: Anotra Date: Thu, 15 Feb 2024 10:23:07 -0500 Subject: [PATCH 02/14] refactor(discord-refcount.c): complete rewrite using anomap --- include/discord-internal.h | 50 +++--- src/discord-refcount.c | 315 +++++++++++++++---------------------- src/discord-rest_request.c | 3 +- 3 files changed, 151 insertions(+), 217 deletions(-) diff --git a/include/discord-internal.h b/include/discord-internal.h index 17eb2a59..252b131d 100644 --- a/include/discord-internal.h +++ b/include/discord-internal.h @@ -29,6 +29,7 @@ extern "C" { #include "queue.h" #include "priority_queue.h" #include "attributes.h" +#include "anomap.h" /** @brief Return 1 if string isn't considered empty */ #define NOT_EMPTY_STR(str) ((str) && *(str)) @@ -493,7 +494,7 @@ struct discord_requestor { * their callbacks to be called from the main thread */ QUEUE(struct discord_request) finished; - } * queues; + } *queues; /** queue locks */ struct { @@ -503,7 +504,7 @@ struct discord_requestor { pthread_mutex_t pending; /** finished queue lock */ pthread_mutex_t finished; - } * qlocks; + } *qlocks; }; /** @@ -780,7 +781,7 @@ struct discord_gateway { int ping_ms; /** ping rwlock */ pthread_rwlock_t rwlock; - } * timer; + } *timer; /** the identify structure for client authentication */ struct discord_identify id; @@ -938,17 +939,9 @@ void discord_gateway_dispatch(struct discord_gateway *gw); struct discord_refcounter { /** `DISCORD_REFCOUNT` logging module */ struct logconf conf; - /** amount of individual user's data held for automatic cleanup */ - int length; - /** cap before increase */ - int capacity; - /** - * individual user's data held for automatic cleanup - * @note datatype declared at discord-refcount.c - */ - struct _discord_ref *refs; - /** global lock */ - pthread_mutex_t *g_lock; + + struct anomap *maps[16]; + pthread_mutex_t locks[16]; }; /** @@ -970,10 +963,15 @@ void discord_refcounter_init(struct discord_refcounter *rc, * no longer referenced * @param should_free whether `data` cleanup should be followed by a free() */ -void discord_refcounter_add_internal(struct discord_refcounter *rc, - void *data, - void (*cleanup)(void *data), - bool should_free); +#define discord_refcounter_add_internal(refcounter, data, cleanup, \ + should_free) \ + __discord_refcounter_add_internal(refcounter, __func__, data, cleanup, \ + should_free) +void __discord_refcounter_add_internal(struct discord_refcounter *rc, + const char *name, + void *data, + void (*cleanup)(void *data), + bool should_free); /** * @brief Add a new client reference to the reference counter @@ -984,11 +982,15 @@ void discord_refcounter_add_internal(struct discord_refcounter *rc, * no longer referenced * @param should_free whether `data` cleanup should be followed by a free() */ -void discord_refcounter_add_client(struct discord_refcounter *rc, - void *data, - void (*cleanup)(struct discord *client, - void *data), - bool should_free); +#define discord_refcounter_add_client(refcounter, data, cleanup, should_free) \ + __discord_refcounter_add_client(refcounter, __func__, data, cleanup, \ + should_free) +void __discord_refcounter_add_client(struct discord_refcounter *rc, + const char *name, + void *data, + void (*cleanup)(struct discord *client, + void *data), + bool should_free); /** * @brief Cleanup refcounter and all user data currently held @@ -1239,7 +1241,7 @@ struct discord { pthread_mutex_t lock; /** notify of `count` decrement */ pthread_cond_t cond; - } * workers; + } *workers; #ifdef CCORD_VOICE struct discord_voice *vcs; diff --git a/src/discord-refcount.c b/src/discord-refcount.c index 4e3dd66b..8d5a1ff5 100644 --- a/src/discord-refcount.c +++ b/src/discord-refcount.c @@ -5,24 +5,10 @@ #include "discord.h" #include "discord-internal.h" -#define CHASH_BUCKETS_FIELD refs -#include "chash.h" - -/* chash heap-mode (auto-increase hashtable) */ -#define REFCOUNTER_TABLE_HEAP 1 -#define REFCOUNTER_TABLE_BUCKET struct _discord_ref -#define REFCOUNTER_TABLE_FREE_KEY(_key) -#define REFCOUNTER_TABLE_HASH(_key, _hash) ((intptr_t)(_key)) -#define REFCOUNTER_TABLE_FREE_VALUE(_value) \ - _discord_refvalue_cleanup(rc, &_value) -#define REFCOUNTER_TABLE_COMPARE(_cmp_a, _cmp_b) (_cmp_a == _cmp_b) -#define REFCOUNTER_TABLE_INIT(ref, _key, _value) \ - memset(&ref, 0, sizeof(ref)); \ - chash_default_init(ref, _key, _value) +ANOMAP_DECLARE_COMPARE_FUNCTION(_cmp_ptr, void *) struct _discord_refvalue { - /** user arbitrary data to be retrieved at `done` or `fail` callbacks */ - void *data; + const char *const name; /** * cleanup for when `data` is no longer needed * @note this only has to be assigned once, it is automatically called once @@ -31,244 +17,189 @@ struct _discord_refvalue { void (*client)(struct discord *client, void *data); void (*internal)(void *data); } cleanup; + /** how many times this resource has been discord_claim() 'd */ + int claims; /** - * `data` references count - * @note if `-1` then `data` has been claimed with - * discord_refcounter_claim() and will be cleaned up once - * discord_refcount_unclaim() is called + * how many references the internal client is holding */ - int visits; + short visits; /** whether `data` cleanup should also be followed by a free() */ bool should_free; /** whether cleanup expects a client parameter */ bool expects_client; - /** how many times this resource has been discord_claim() 'd */ - int claims; -}; - -struct _discord_ref { - /** key is the user data's address */ - intptr_t key; - /** holds the user data and information for automatic cleanup */ - struct _discord_refvalue value; - /** the route state in the hashtable (see chash.h 'State enums') */ - int state; }; static void -_discord_refvalue_cleanup(struct discord_refcounter *rc, - struct _discord_refvalue *value) +_on_ref_map_changed(const struct anomap_item_changed *ev) { - if (value->cleanup.client) { - if (value->expects_client) - value->cleanup.client(CLIENT(rc, refcounter), value->data); - else - value->cleanup.internal(value->data); + struct discord_refcounter *rc = ev->data; + if (ev->op & anomap_delete) { + struct _discord_refvalue *val = ev->val.prev; + logconf_trace(&rc->conf, "deleting %s %p", val->name, + *(void **)ev->key); + if (val->cleanup.client) { + if (val->expects_client) + val->cleanup.client(CLIENT(ev->data, refcounter), + *(void **)ev->key); + else + val->cleanup.internal(*(void **)ev->key); + } + if (val->should_free) free(*(void **)ev->key); } - if (value->should_free) free(value->data); -} - -static struct _discord_refvalue * -_discord_refvalue_find(struct discord_refcounter *rc, const void *data) -{ - struct _discord_ref *ref = - chash_lookup_bucket(rc, (intptr_t)data, ref, REFCOUNTER_TABLE); - return &ref->value; -} - -static void -_discord_refvalue_init(struct discord_refcounter *rc, - void *data, - struct _discord_refvalue *init_fields) -{ - init_fields->data = data; - init_fields->visits = 1; - chash_assign(rc, (intptr_t)data, *init_fields, REFCOUNTER_TABLE); -} - -static void -_discord_refvalue_delete(struct discord_refcounter *rc, void *data) -{ - chash_delete(rc, (intptr_t)data, REFCOUNTER_TABLE); } void discord_refcounter_init(struct discord_refcounter *rc, struct logconf *conf) { logconf_branch(&rc->conf, conf, "DISCORD_REFCOUNT"); - - __chash_init(rc, REFCOUNTER_TABLE); - - rc->g_lock = malloc(sizeof *rc->g_lock); - ASSERT_S(!pthread_mutex_init(rc->g_lock, NULL), - "Couldn't initialize refcounter mutex"); + for (int i = 0; i < 16; i++) { + rc->maps[i] = + anomap_create(sizeof(void *), sizeof(struct _discord_refvalue), + _cmp_ptr, anomap_direct_access); + anomap_set_on_item_changed(rc->maps[i], _on_ref_map_changed, rc); + pthread_mutex_init(&rc->locks[i], NULL); + } } void discord_refcounter_cleanup(struct discord_refcounter *rc) { - __chash_free(rc, REFCOUNTER_TABLE); - pthread_mutex_destroy(rc->g_lock); - free(rc->g_lock); + for (int i = 0; i < 16; i++) { + anomap_destroy(rc->maps[i]); + pthread_mutex_destroy(&rc->locks[i]); + } } -static bool -_discord_refcounter_contains(struct discord_refcounter *rc, const void *data) +static size_t +_get_map_index(const void *data) { - bool ret = chash_contains(rc, (intptr_t)data, ret, REFCOUNTER_TABLE); - return ret; + return (((uintptr_t)data) >> 6) & 0xF; } static CCORDcode -_discord_refcounter_incr_no_lock(struct discord_refcounter *rc, void *data) +_discord_refcounter_mod_no_lock(struct discord_refcounter *rc, + const void *data, + short visits, + int claims) { - CCORDcode code = CCORD_RESOURCE_UNAVAILABLE; - if (_discord_refcounter_contains(rc, data)) { - struct _discord_refvalue *value = _discord_refvalue_find(rc, data); + struct anomap *map = rc->maps[_get_map_index(data)]; + size_t index; + bool found = anomap_index_of(map, &data, &index); + struct _discord_refvalue *val; + if (!found) { + logconf_trace(&rc->conf, "%p not found", data); + return CCORD_RESOURCE_UNAVAILABLE; + } - if (value->visits == INT_MAX) { - logconf_error(&rc->conf, - "Can't increment %p any further: Overflow", data); - } - else { - ++value->visits; - logconf_trace(&rc->conf, "Increment %p (%d visits)", data, - value->visits); - code = CCORD_OK; - } + val = anomap_direct_val_at_index(map, index); + static const char *operation_name[] = { "decrementing", "incrementing" }; + + if (0 != visits) + logconf_trace(&rc->conf, "visits %p %s by %i to %i", data, + operation_name[visits > 0], + visits > 0 ? visits : -visits, val->visits += visits); + + if (0 != claims) + logconf_trace(&rc->conf, "claims %p %s by %i to %i", data, + operation_name[claims > 0], + claims > 0 ? claims : -claims, val->claims += claims); + + if (0 <= val->visits && 0 <= val->claims) { + if (0 == val->visits && 0 == val->claims) + anomap_do(map, anomap_delete, &data, NULL); + return CCORD_OK; } - return code; + + if (0 > val->visits) + logconf_error(&rc->conf, "visits for %p below 0", data); + + if (0 > val->claims) + logconf_error(&rc->conf, "claims for %p below 0", data); + + return CCORD_RESOURCE_UNAVAILABLE; } static CCORDcode -_discord_refcounter_decr_no_lock(struct discord_refcounter *rc, void *data) +_discord_refcounter_mod(struct discord_refcounter *rc, + const void *data, + short visits, + int claims) { - CCORDcode code = CCORD_RESOURCE_UNAVAILABLE; - if (_discord_refcounter_contains(rc, data)) { - struct _discord_refvalue *value = _discord_refvalue_find(rc, data); - - if (value->visits < value->claims) { - logconf_error(&rc->conf, - "(Internal Error) There shouldn't be more visits " - "than claims!"); - } - else if (--value->visits > 0) { - code = CCORD_OK; - logconf_trace(&rc->conf, "Decrement %p (%d visits)", data, - value->visits); - } - else { - if (value->claims != 0) { - logconf_error(&rc->conf, "(Internal Error) Caught attempt to " - "cleanup claimed resource!"); - ++value->visits; - code = CCORD_RESOURCE_OWNERSHIP; - } - else { - _discord_refvalue_delete(rc, data); - logconf_info(&rc->conf, "Fully decremented and free'd %p", - data); - code = CCORD_OK; - } - } - } + size_t arrayi = _get_map_index(data); + pthread_mutex_lock(&rc->locks[arrayi]); + CCORDcode code = _discord_refcounter_mod_no_lock(rc, data, visits, claims); + pthread_mutex_unlock(&rc->locks[arrayi]); return code; } CCORDcode discord_refcounter_claim(struct discord_refcounter *rc, const void *data) { - CCORDcode code = CCORD_RESOURCE_UNAVAILABLE; - - pthread_mutex_lock(rc->g_lock); - if (_discord_refcounter_contains(rc, data)) { - struct _discord_refvalue *value = _discord_refvalue_find(rc, data); - - ++value->claims; - code = _discord_refcounter_incr_no_lock(rc, (void *)data); - logconf_trace(&rc->conf, "Claiming %p (claims: %d)", data, - value->claims); - } - pthread_mutex_unlock(rc->g_lock); - return code; + return _discord_refcounter_mod(rc, data, 0, 1); } CCORDcode discord_refcounter_unclaim(struct discord_refcounter *rc, void *data) { - CCORDcode code = CCORD_RESOURCE_UNAVAILABLE; - - pthread_mutex_lock(rc->g_lock); - if (_discord_refcounter_contains(rc, data)) { - struct _discord_refvalue *value = _discord_refvalue_find(rc, data); - - if (0 == value->claims) { - logconf_error(&rc->conf, "Resource hasn't been claimed before, or " - "it has already been unclaimed"); - } - else { - --value->claims; - logconf_trace(&rc->conf, "Unclaiming %p (claims: %d)", data, - value->claims); - code = _discord_refcounter_decr_no_lock(rc, data); - } - } - pthread_mutex_unlock(rc->g_lock); - - return code; + return _discord_refcounter_mod(rc, data, 0, -1); } void -discord_refcounter_add_internal(struct discord_refcounter *rc, - void *data, - void (*cleanup)(void *data), - bool should_free) +__discord_refcounter_add_internal(struct discord_refcounter *rc, + const char *name, + void *data, + void (*cleanup)(void *data), + bool should_free) { - pthread_mutex_lock(rc->g_lock); - _discord_refvalue_init(rc, data, - &(struct _discord_refvalue){ - .expects_client = false, - .cleanup.internal = cleanup, - .should_free = should_free, - }); - logconf_info(&rc->conf, "Adding concord's internal resource %p", data); - pthread_mutex_unlock(rc->g_lock); + size_t arrayi = _get_map_index(data); + struct anomap *map = rc->maps[arrayi]; + pthread_mutex_lock(&rc->locks[arrayi]); + struct _discord_refvalue val = { + .name = name, + .expects_client = false, + .cleanup.internal = cleanup, + .should_free = should_free, + .visits = 1, + }; + anomap_operation result = anomap_do(map, anomap_insert, &data, &val); + logconf_trace(&rc->conf, "Adding concord's internal resource %s %p %s", + name, data, + result & anomap_insert ? "successful" : "failed"); + pthread_mutex_unlock(&rc->locks[arrayi]); } void -discord_refcounter_add_client(struct discord_refcounter *rc, - void *data, - void (*cleanup)(struct discord *client, - void *data), - bool should_free) +__discord_refcounter_add_client(struct discord_refcounter *rc, + const char *name, + void *data, + void (*cleanup)(struct discord *client, + void *data), + bool should_free) { - pthread_mutex_lock(rc->g_lock); - _discord_refvalue_init(rc, data, - &(struct _discord_refvalue){ - .expects_client = true, - .cleanup.client = cleanup, - .should_free = should_free, - }); - logconf_info(&rc->conf, "Adding user's custom resource %p", data); - pthread_mutex_unlock(rc->g_lock); + size_t arrayi = _get_map_index(data); + struct anomap *map = rc->maps[arrayi]; + pthread_mutex_lock(&rc->locks[arrayi]); + struct _discord_refvalue val = { + .name = name, + .expects_client = true, + .cleanup.client = cleanup, + .should_free = should_free, + .visits = 1, + }; + anomap_operation result = anomap_do(map, anomap_insert, &data, &val); + logconf_trace(&rc->conf, "Adding user's custom resource %s %p %s", name, + data, result & anomap_insert ? "successful" : "failed"); + pthread_mutex_unlock(&rc->locks[arrayi]); } CCORDcode discord_refcounter_incr(struct discord_refcounter *rc, void *data) { - CCORDcode code; - pthread_mutex_lock(rc->g_lock); - code = _discord_refcounter_incr_no_lock(rc, data); - pthread_mutex_unlock(rc->g_lock); - return code; + return _discord_refcounter_mod(rc, data, 1, 0); } CCORDcode discord_refcounter_decr(struct discord_refcounter *rc, void *data) { - CCORDcode code; - pthread_mutex_lock(rc->g_lock); - code = _discord_refcounter_decr_no_lock(rc, data); - pthread_mutex_unlock(rc->g_lock); - return code; + return _discord_refcounter_mod(rc, data, -1, 0); } diff --git a/src/discord-rest_request.c b/src/discord-rest_request.c index 48850136..9e84ef21 100644 --- a/src/discord-rest_request.c +++ b/src/discord-rest_request.c @@ -391,7 +391,8 @@ discord_requestor_info_read(struct discord_requestor *rqtor) body.start); } else if (req->dispatch.has_type - && req->dispatch.sync != DISCORD_SYNC_FLAG) { + && req->dispatch.sync != DISCORD_SYNC_FLAG) + { if (req->dispatch.sync) { req->response.data = req->dispatch.sync; } From 31588a7b04abf0070670037aabadfc3cb88e312e Mon Sep 17 00:00:00 2001 From: Anotra Date: Sat, 17 Feb 2024 08:08:54 -0500 Subject: [PATCH 03/14] fix(concord-once.c): fix fail gotos --- src/concord-once.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/concord-once.c b/src/concord-once.c index f9621c11..80e9ec36 100644 --- a/src/concord-once.c +++ b/src/concord-once.c @@ -75,28 +75,29 @@ ccord_global_init() if (0 != ioctl(shutdown_fds[i], FIOCLEX, NULL)) { fputs("Failed to make shutdown pipe close on execute\n", stderr); - goto fail_pipe_init; + goto fail_pipe_flags; } if (0 != ioctl(shutdown_fds[i], FIONBIO, &on)) { fputs("Failed to make shutdown pipe nonblocking\n", stderr); - goto fail_pipe_init; + goto fail_pipe_flags; } } } pthread_mutex_unlock(&lock); return CCORD_OK; -fail_pipe_init: +fail_pipe_flags: for (int i = 0; i < 2; i++) { if (-1 != shutdown_fds[i]) { close(shutdown_fds[i]); shutdown_fds[i] = -1; } } -fail_discord_worker_init: +fail_pipe_init: discord_worker_global_cleanup(); -fail_curl_init: +fail_discord_worker_init: curl_global_cleanup(); +fail_curl_init: init_counter = 0; pthread_mutex_unlock(&lock); From 5f16128103e3ce8ddbef36128af0c56fd3126abb Mon Sep 17 00:00:00 2001 From: Anotra Date: Sat, 17 Feb 2024 08:16:14 -0500 Subject: [PATCH 04/14] fix(io_poller.c): make pipe fds CLOEXEC --- core/io_poller.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/core/io_poller.c b/core/io_poller.c index b2447818..1d6ee1ce 100644 --- a/core/io_poller.c +++ b/core/io_poller.c @@ -5,6 +5,7 @@ #include #include #include +#include #ifndef __MINGW32__ #include @@ -49,7 +50,7 @@ on_io_poller_wakeup(struct io_poller *io, enum io_poller_events events, void *user_data) { - char buf[0x10000]; + char buf[0x1000]; (void)!read(io->wakeup_fds[0], buf, sizeof buf); } @@ -63,14 +64,19 @@ io_poller_create(void) io->pollfds = calloc(io->cap, sizeof *io->pollfds); if (io->elements && io->pollfds) { if (0 == pipe(io->wakeup_fds)) { - int flags = fcntl(io->wakeup_fds[0], F_GETFL); - fcntl(io->wakeup_fds[0], F_SETFL, flags | O_NONBLOCK); - flags = fcntl(io->wakeup_fds[1], F_GETFL); - fcntl(io->wakeup_fds[1], F_SETFL, flags | O_NONBLOCK); - - io_poller_socket_add(io, io->wakeup_fds[0], IO_POLLER_IN, - on_io_poller_wakeup, NULL); - return io; + const int on = 1; + bool success = true; + for (int i = 0; i < 2; i++) + if (0 != ioctl(io->wakeup_fds[i], FIOCLEX, NULL) + && 0 != ioctl(io->wakeup_fds[i], FIONBIO, &on)) + success = false; + if (success) { + io_poller_socket_add(io, io->wakeup_fds[0], IO_POLLER_IN, + on_io_poller_wakeup, NULL); + return io; + } + close(io->wakeup_fds[1]); + close(io->wakeup_fds[0]); } } free(io->elements); @@ -138,7 +144,8 @@ io_poller_perform(struct io_poller *io) for (int i = 0; i < io->curlm_cnt; i++) { struct io_curlm *curlm = io->curlm[i]; if (curlm->should_perform - || (-1 != curlm->timeout && now >= curlm->timeout)) { + || (-1 != curlm->timeout && now >= curlm->timeout)) + { curlm->should_perform = false; int result = curlm->cb From a411973793b473b3577e319b39979cb1c1556c11 Mon Sep 17 00:00:00 2001 From: Anotra Date: Sat, 17 Feb 2024 08:26:35 -0500 Subject: [PATCH 05/14] feat(io_poller.c):added IO_POLLER_(PRI/ERR) --- core/io_poller.c | 3 +++ core/io_poller.h | 2 ++ 2 files changed, 5 insertions(+) diff --git a/core/io_poller.c b/core/io_poller.c index 1d6ee1ce..b2843a6d 100644 --- a/core/io_poller.c +++ b/core/io_poller.c @@ -136,6 +136,8 @@ io_poller_perform(struct io_poller *io) int events = 0; if (io->pollfds[i].revents & POLLIN) events |= IO_POLLER_IN; if (io->pollfds[i].revents & POLLOUT) events |= IO_POLLER_OUT; + if (io->pollfds[i].revents & POLLPRI) events |= IO_POLLER_PRI; + if (io->pollfds[i].revents & POLLERR) events |= IO_POLLER_ERR; io->pollfds[i].revents = 0; struct io_poller_element *element = &io->elements[i]; element->cb(io, events, element->user_data); @@ -195,6 +197,7 @@ io_poller_socket_add(struct io_poller *io, io->pollfds[index].events = 0; if (events & IO_POLLER_IN) io->pollfds[index].events |= POLLIN; if (events & IO_POLLER_OUT) io->pollfds[index].events |= POLLOUT; + if (events & IO_POLLER_PRI) io->pollfds[index].events |= POLLPRI; io->elements[index].cb = cb; io->elements[index].user_data = user_data; return true; diff --git a/core/io_poller.h b/core/io_poller.h index bc3ba5cb..b53a6759 100644 --- a/core/io_poller.h +++ b/core/io_poller.h @@ -10,6 +10,8 @@ enum io_poller_events { IO_POLLER_IN = 1 << 0, IO_POLLER_OUT = 1 << 1, + IO_POLLER_PRI = 1 << 2, + IO_POLLER_ERR = 1 << 3, }; /** From 427ef0c4290709769d7435baff488218abd3e95d Mon Sep 17 00:00:00 2001 From: Anotra Date: Sat, 17 Feb 2024 18:16:15 -0500 Subject: [PATCH 06/14] feat(anomap): add foreach_reverse --- core/anomap.c | 98 ++++++++++++++++++++++++++++++++++++--------------- core/anomap.h | 2 ++ 2 files changed, 72 insertions(+), 28 deletions(-) diff --git a/core/anomap.c b/core/anomap.c index 2a3ff4f0..b8c53817 100644 --- a/core/anomap.c +++ b/core/anomap.c @@ -115,22 +115,29 @@ _anomap_on_empty(struct anomap *map) { map->map.highest = 0; } +static void +_anomap_clear_foreach(struct anomap *map, void *data, + const void *key, const void *val) +{ + if (!map->on_changed.cb) return; + map->on_changed.cb( + &(struct anomap_item_changed) { + .map = map, + .data = map->on_changed.data, + .op = anomap_delete, + .key = (void *)key, + .val.prev = (void *)val, + }); +} + void anomap_clear(struct anomap *map) { if (0 == map->map.len) return; - - for (size_t i = 0; i < map->map.len; i++) { - if (!map->on_changed.cb) break; - unsigned pos = map->map.arr[i]; - map->on_changed.cb( - &(struct anomap_item_changed) { - .map = map, - .data = map->on_changed.data, - .op = anomap_delete, - .key = map->keys.arr + map->keys.size * pos, - .val.prev = map->vals.arr + map->vals.size * pos, - }); + if (map->on_changed.cb) { + if (map->options & anomap_preserve_order) + anomap_foreach_reverse(map, _anomap_clear_foreach, NULL); + else anomap_foreach(map, _anomap_clear_foreach, NULL); } map->map.len = 0; _anomap_on_empty(map); @@ -430,39 +437,74 @@ anomap_delete_range(struct anomap *map, size_t from_index, size_t to_index, } void -anomap_foreach(struct anomap *map, anomap_foreach_cb *cb, void *data) { +_anomap_foreach(struct anomap *map, anomap_foreach_cb *cb, void *data, + anomap_position start_position) +{ const size_t key_size = map->keys.size, val_size = map->vals.size; if (0 == map->map.len) return; if (map->options & anomap_preserve_order) { - const unsigned tail = map->order.tail; - unsigned pos = map->order.arr[tail].next; - switch (val_size ? 1 : 0) { - while (tail != pos) { + unsigned tail = map->order.tail; + unsigned head = map->order.arr[tail].next; + unsigned start = start_position == anomap_head ? head : tail; + unsigned pos = start; + switch ((start_position == anomap_tail ? 2 : 0) | (val_size ? 1 : 0)) { + while (pos != tail) { pos = map->order.arr[pos].next; - case 1: + case 0 | 0: + cb(map, data, map->keys.arr + key_size * pos, NULL); + } break; + while (pos != tail) { + pos = map->order.arr[pos].next; + case 0 | 1: cb(map, data, map->keys.arr + key_size * pos, map->vals.arr + val_size * pos); } break; - - while (tail != pos) { - pos = map->order.arr[pos].next; - case 0: + while (pos != head) { + pos = map->order.arr[pos].prev; + case 2 | 0: cb(map, data, map->keys.arr + key_size * pos, NULL); } break; + while (pos != head) { + pos = map->order.arr[pos].prev; + case 2 | 1: + cb(map, data, map->keys.arr + key_size * pos, + map->vals.arr + val_size * pos); + } break; } } else { - if (val_size) { - for (size_t i=0; imap.len; i++) - cb(map, data, map->keys.arr + key_size * map->map.arr[i], - map->vals.arr + val_size * map->map.arr[i]); + if (start_position == anomap_head) { + if (val_size) { + for (size_t i=0; imap.len; i++) + cb(map, data, map->keys.arr + key_size * map->map.arr[i], + map->vals.arr + val_size * map->map.arr[i]); + } else { + for (size_t i=0; imap.len; i++) + cb(map, data, map->keys.arr + key_size * map->map.arr[i], NULL); + } } else { - for (size_t i=0; imap.len; i++) - cb(map, data, map->keys.arr + key_size * map->map.arr[i], NULL); + if (val_size) { + for (size_t i = map->map.len; i--;) + cb(map, data, map->keys.arr + key_size * map->map.arr[i], + map->vals.arr + val_size * map->map.arr[i]); + } else { + for (size_t i = map->map.len; i--;) + cb(map, data, map->keys.arr + key_size * map->map.arr[i], NULL); + } } } } +void +anomap_foreach(struct anomap *map, anomap_foreach_cb *cb, void *data) { + _anomap_foreach(map, cb, data, anomap_head); +} + +void +anomap_foreach_reverse(struct anomap *map, anomap_foreach_cb *cb, void *data) { + _anomap_foreach(map, cb, data, anomap_tail); +} + bool anomap_advance(struct anomap *map, size_t *index, anomap_position *position) { unsigned pos; diff --git a/core/anomap.h b/core/anomap.h index 697b4f94..33022fe5 100644 --- a/core/anomap.h +++ b/core/anomap.h @@ -94,6 +94,8 @@ size_t anomap_delete_range(struct anomap *map, typedef void anomap_foreach_cb(struct anomap *map, void *data, const void *key, const void *val); void anomap_foreach(struct anomap *map, anomap_foreach_cb *cb, void *data); +void anomap_foreach_reverse(struct anomap *map, + anomap_foreach_cb *cb, void *data); typedef enum { #define ANOMAP_HEAD anomap_head From 5ae114d85a32a68189f7e1367ecb97571a32b909 Mon Sep 17 00:00:00 2001 From: Anotra Date: Mon, 19 Feb 2024 15:57:18 -0500 Subject: [PATCH 07/14] feat(anomap): add locking support --- core/anomap.c | 326 +++++++++++++++++++++++++++++++++++++++++++------- core/anomap.h | 6 + 2 files changed, 288 insertions(+), 44 deletions(-) diff --git a/core/anomap.c b/core/anomap.c index b8c53817..9f48becf 100644 --- a/core/anomap.c +++ b/core/anomap.c @@ -7,15 +7,172 @@ #include "anomap.h" +#define NATIVE_LOCK_NONE 0 +#define NATIVE_LOCK_PTHREADS 1 +#define NATIVE_LOCK_WINDOWS 2 + +#if !defined ANOMAP_NATIVE_LOCKS + #if defined __has_include + #if __has_include() + #define ANOMAP_NATIVE_LOCKS NATIVE_LOCK_PTHREADS + #endif + #endif +#endif + +struct anomap_lock { + void *(*create)(void); + void (*destroy)(void *lock); + struct { + bool (*attempt)(void *lock); + void (*acquire)(void *lock); + void (*release)(void *lock); + } r; + struct { + bool (*attempt)(void *lock); + void (*acquire)(void *lock); + void (*release)(void *lock); + } w; +}; + +static void * +_null_lock_create(void) { + static int dummy = 0; + return &dummy; +} + +static void +_null_lock_destroy(void *lock) { + (void)lock; +} + +static bool +_null_lock_attempt(void *lock) { + (void)lock; + return true; +} + +static void +_null_lock_acquire(void *lock) { + (void)lock; +} + +static void +_null_lock_release(void *lock) { + (void)lock; +} + +static const struct anomap_lock null_lock = { + .create = _null_lock_create, + .destroy = _null_lock_destroy, + .r = { + .attempt = _null_lock_attempt, + .acquire = _null_lock_acquire, + .release = _null_lock_release, + }, + .w = { + .attempt = _null_lock_attempt, + .acquire = _null_lock_acquire, + .release = _null_lock_release, + } +}; + +static unsigned anomap_has_locks = +#if !defined ANOMAP_NATIVE_LOCKS || ANOMAP_NATIVE_LOCKS == NATIVE_LOCK_NONE + 0; + static const struct anomap_lock lock_functions = null_lock; +#else + anomap_use_lock; + + #if ANOMAP_NATIVE_LOCKS == NATIVE_LOCK_PTHREADS + #ifndef _GNU_SOURCE + #define _GNU_SOURCE + #endif + #include + + static void * + _pthread_lock_create(void) { + pthread_rwlock_t *lock = calloc(1, sizeof *lock); + if (!lock) return NULL; + pthread_rwlock_init(lock, NULL); + return lock; + } + + static void + _pthread_lock_destroy(void *lock) { + pthread_rwlock_destroy(lock); + free(lock); + } + + static bool + _pthread_rd_lock_attempt(void *lock) { + return 0 == pthread_rwlock_tryrdlock(lock); + } + + static void + _pthread_rd_lock(void *lock) { + pthread_rwlock_rdlock(lock); + } + + static void + _pthread_rd_unlock(void *lock) { + pthread_rwlock_unlock(lock); + } + + static bool + _pthread_wr_lock_attempt(void *lock) { + return 0 == pthread_rwlock_trywrlock(lock); + } + + static void + _pthread_wr_lock(void *lock) { + pthread_rwlock_wrlock(lock); + } + + static void + _pthread_wr_unlock(void *lock) { + pthread_rwlock_unlock(lock); + } + + static struct anomap_lock lock_functions = { + .create = _pthread_lock_create, + .destroy = _pthread_lock_destroy, + .r = { + .attempt = _pthread_rd_lock_attempt, + .acquire = _pthread_rd_lock, + .release = _pthread_rd_unlock, + }, + .w = { + .attempt = _pthread_wr_lock_attempt, + .acquire = _pthread_wr_lock, + .release = _pthread_wr_unlock, + }, + }; + #else + #error native lock not yet implemented + #endif +#endif + +#define LOCK_W_ATTEMPT map->lock.functions->w.attempt(map->lock.lock) +#define LOCK_W_ACQUIRE map->lock.functions->w.acquire(map->lock.lock) +#define LOCK_W_RELEASE map->lock.functions->w.release(map->lock.lock) +#define LOCK_R_ATTEMPT map->lock.functions->r.attempt(map->lock.lock) +#define LOCK_R_ACQUIRE map->lock.functions->r.acquire(map->lock.lock) +#define LOCK_R_RELEASE map->lock.functions->r.release(map->lock.lock) + #define ANOMAP_ALLOWED_OPTIONS ( anomap_reverse_order \ | anomap_direct_access \ | anomap_preserve_order \ + | anomap_has_locks \ ) struct anomap { int (*cmp)(const void *, const void *); anomap_options options; bool free_on_cleanup; + struct { + void *lock; + const struct anomap_lock *functions; + } lock; struct { anomap_on_item_changed *cb; void *data; @@ -42,6 +199,9 @@ struct anomap { } order; }; +static void _anomap_foreach(struct anomap *map, anomap_foreach_cb *cb, + void *data, anomap_position start_position); + anomap_options anomap_supported_options(void) { return ANOMAP_ALLOWED_OPTIONS; @@ -58,7 +218,7 @@ anomap_init(struct anomap *map, int (*cmp)(const void *, const void *), anomap_options options) { - if (!key_size || !cmp || (options & ~ANOMAP_ALLOWED_OPTIONS)) + if (!map || !key_size || !cmp || (options & ~ANOMAP_ALLOWED_OPTIONS)) return false; memset(map, 0, sizeof *map); map->free_on_cleanup = false; @@ -66,7 +226,14 @@ anomap_init(struct anomap *map, map->cmp = cmp; map->keys.size = key_size; map->vals.size = val_size; - return true; + map->lock.functions = options & anomap_use_lock + ? &lock_functions + : &null_lock; + if ((map->lock.lock = map->lock.functions->create())) + return true; + + memset(map, 0, sizeof *map); + return false; } struct anomap * @@ -88,6 +255,7 @@ anomap_destroy(struct anomap *map) { free(map->keys.arr); free(map->vals.arr); free(map->order.arr); + map->lock.functions->destroy(map->lock.lock); const bool free_on_cleanup = map->free_on_cleanup; memset(map, 0, sizeof *map); if (free_on_cleanup) @@ -98,13 +266,18 @@ void anomap_set_on_item_changed( struct anomap *map, anomap_on_item_changed *on_changed, void *data) { + LOCK_W_ACQUIRE; map->on_changed.cb = on_changed; map->on_changed.data = data; + LOCK_W_RELEASE; } size_t anomap_length(struct anomap *map) { - return map->map.len; + LOCK_R_ACQUIRE; + size_t len = map->map.len; + LOCK_R_RELEASE; + return len; } static inline void @@ -132,15 +305,17 @@ _anomap_clear_foreach(struct anomap *map, void *data, void anomap_clear(struct anomap *map) { - if (0 == map->map.len) - return; - if (map->on_changed.cb) { - if (map->options & anomap_preserve_order) - anomap_foreach_reverse(map, _anomap_clear_foreach, NULL); - else anomap_foreach(map, _anomap_clear_foreach, NULL); + LOCK_W_ACQUIRE; + if (map->map.len) { + if (map->on_changed.cb) { + if (map->options & anomap_preserve_order) + _anomap_foreach(map, _anomap_clear_foreach, NULL, anomap_tail); + else _anomap_foreach(map, _anomap_clear_foreach, NULL, anomap_head); + } + map->map.len = 0; + _anomap_on_empty(map); } - map->map.len = 0; - _anomap_on_empty(map); + LOCK_W_RELEASE; } bool @@ -149,8 +324,8 @@ anomap_contains(struct anomap *map, void *key) { return anomap_index_of(map, key, &position); } -bool -anomap_index_of(struct anomap *map, void *key, size_t *position) { +static bool +anomap_index_of_no_lock(struct anomap *map, void *key, size_t *position) { size_t lo = 0, mid, hi = map->map.len; const char *const keys = map->keys.arr; const size_t key_size = map->keys.size; @@ -183,34 +358,61 @@ anomap_index_of(struct anomap *map, void *key, size_t *position) { return *position = lo, false; } +bool +anomap_index_of(struct anomap *map, void *key, size_t *position) { + LOCK_R_ACQUIRE; + bool found = anomap_index_of_no_lock(map, key, position); + LOCK_R_RELEASE; + return found; +} + bool anomap_at_index(struct anomap *map, size_t index, void *key, void *val) { - if (index >= map->map.len) return false; + LOCK_R_ACQUIRE; + if (index >= map->map.len) { + LOCK_R_RELEASE; + return false; + } const size_t key_size = map->keys.size, val_size = map->vals.size; const unsigned pos = map->map.arr[index]; if (key) memcpy(key, map->keys.arr + key_size * pos, key_size); if (val) memcpy(val, map->vals.arr + val_size * pos, val_size); + LOCK_R_RELEASE; return true; } const void * anomap_direct_key_at_index(struct anomap *map, size_t index) { + void *result = NULL; + LOCK_R_ACQUIRE; if (!(map->options & anomap_direct_access)) - return NULL; + goto fail; if (index >= map->map.len) - return NULL; - return map->keys.arr + map->keys.size * map->map.arr[index]; + goto fail; + + result = map->keys.arr + map->keys.size * map->map.arr[index]; + + fail: + LOCK_R_RELEASE; + return result; } void * anomap_direct_val_at_index(struct anomap *map, size_t index) { + void *result = NULL; + LOCK_R_ACQUIRE; if (!(map->options & anomap_direct_access)) - return NULL; + goto fail; if (index >= map->map.len) - return NULL; + goto fail; if (!map->vals.size) - return NULL; - return map->vals.arr + map->vals.size * map->map.arr[index]; + goto fail; + + result = map->vals.arr + map->vals.size * map->map.arr[index]; + + fail: + LOCK_R_RELEASE; + return result; } static bool @@ -263,14 +465,17 @@ anomap_operation anomap_do(struct anomap *map, anomap_operation operation, void *key, void *val) { + if (operation == anomap_getval) + LOCK_R_ACQUIRE; + else LOCK_W_ACQUIRE; const size_t key_size = map->keys.size, val_size = map->vals.size; anomap_operation result = 0; size_t mpos = 0; - if (!anomap_index_of(map, key, &mpos)) { + if (!anomap_index_of_no_lock(map, key, &mpos)) { if (!(operation & anomap_insert)) - return 0; + goto finish; if (!_anomap_ensure_capacity(map, map->map.len + 1)) - return 0; + goto finish; unsigned pos = map->map.highest == map->map.len ? map->map.highest++ : map->map.arr[map->map.len]; @@ -303,7 +508,7 @@ anomap_do(struct anomap *map, anomap_operation operation, .key = key, .val.now = val, }); - return result; + goto finish; } unsigned pos = map->map.arr[mpos]; if (operation & anomap_update) { @@ -365,12 +570,17 @@ anomap_do(struct anomap *map, anomap_operation operation, _unlink_element(map, pos); _anomap_on_empty(map); } + finish: + if (operation == anomap_getval) + LOCK_R_RELEASE; + else LOCK_W_RELEASE; return result; } -size_t -anomap_copy_range(struct anomap *map, size_t from_index, size_t to_index, - void *keys, void *vals) +static size_t +anomap_copy_range_no_lock(struct anomap *map, + size_t from_index, size_t to_index, + void *keys, void *vals) { size_t count = 1 + (from_index < to_index ? to_index - from_index : from_index - to_index); @@ -393,12 +603,25 @@ anomap_copy_range(struct anomap *map, size_t from_index, size_t to_index, return count; } +size_t +anomap_copy_range(struct anomap *map, size_t from_index, size_t to_index, + void *keys, void *vals) +{ + LOCK_R_ACQUIRE; + size_t count = anomap_copy_range_no_lock(map, from_index, to_index, + keys, vals); + LOCK_R_RELEASE; + return count; +} + size_t anomap_delete_range(struct anomap *map, size_t from_index, size_t to_index, void *keys, void *vals) { - size_t count = anomap_copy_range(map, from_index, to_index, keys, vals); - if (!count) return 0; + LOCK_W_ACQUIRE; + size_t count = anomap_copy_range_no_lock(map, from_index, to_index, + keys, vals); + if (!count) goto finish; const int next = from_index <= to_index ? 1 : -1; if (map->options & anomap_preserve_order) for (size_t i = from_index;; i += next) { @@ -433,10 +656,12 @@ anomap_delete_range(struct anomap *map, size_t from_index, size_t to_index, remaining -= block; } _anomap_on_empty(map); + finish: + LOCK_W_RELEASE; return count; } -void +static void _anomap_foreach(struct anomap *map, anomap_foreach_cb *cb, void *data, anomap_position start_position) { @@ -497,69 +722,82 @@ _anomap_foreach(struct anomap *map, anomap_foreach_cb *cb, void *data, void anomap_foreach(struct anomap *map, anomap_foreach_cb *cb, void *data) { + LOCK_R_ACQUIRE; _anomap_foreach(map, cb, data, anomap_head); + LOCK_R_RELEASE; } void anomap_foreach_reverse(struct anomap *map, anomap_foreach_cb *cb, void *data) { + LOCK_R_ACQUIRE; _anomap_foreach(map, cb, data, anomap_tail); + LOCK_R_RELEASE; } + bool anomap_advance(struct anomap *map, size_t *index, anomap_position *position) { unsigned pos; + LOCK_R_ACQUIRE; if (0 == map->map.len) - return false; + goto return_false; if (map->options & anomap_preserve_order) { switch (*position) { case anomap_head: pos = map->order.arr[map->order.tail].next; anomap_index_of(map, map->keys.arr + map->keys.size * pos, index); *position = anomap_next; - return true; + goto return_true; case anomap_tail: pos = map->order.tail; anomap_index_of(map, map->keys.arr + map->keys.size * pos, index); *position = anomap_prev; - return true; + goto return_true; case anomap_next: pos = map->map.arr[*index]; if (pos == map->order.tail) - return false; + goto return_false; pos = map->order.arr[pos].next; anomap_index_of(map, map->keys.arr + map->keys.size * pos, index); - return true; + goto return_true; case anomap_prev: pos = map->map.arr[*index]; if (pos == map->order.arr[map->order.tail].next) - return false; + goto return_false; pos = map->order.arr[pos].prev; anomap_index_of(map, map->keys.arr + map->keys.size * pos, index); - return true; + goto return_true; } } else { switch (*position) { case anomap_head: *index = 0; *position = anomap_next; - return true; + goto return_true; case anomap_tail: *index = map->map.len - 1; *position = anomap_prev; - return true; + goto return_true; case anomap_next: if (*index >= map->map.len - 1) - return false; + goto return_false; (*index)++; - return true; + goto return_true; case anomap_prev: if (0 == *index) - return false; + goto return_false; (*index)--; - return true; + goto return_true; } } + // false should be first as it's the default return value + return_false: + LOCK_R_RELEASE; return false; + + return_true: + LOCK_R_RELEASE; + return true; } int diff --git a/core/anomap.h b/core/anomap.h index 33022fe5..149c7cb0 100644 --- a/core/anomap.h +++ b/core/anomap.h @@ -10,6 +10,8 @@ #include #include +__BEGIN_DECLS + #define ANOMAP_DECLARE_COMPARE_FUNCTION(function_name, data_type) \ static int \ function_name(const void *a, const void *b) { \ @@ -24,6 +26,8 @@ typedef enum { anomap_direct_access = 1 << 1, #define ANOMAP_PRESERVE_ORDER anomap_preserve_order anomap_preserve_order = 1 << 2, +#define ANOMAP_USE_LOCK anomap_use_lock + anomap_use_lock = 1 << 3, } anomap_options; typedef enum { @@ -114,4 +118,6 @@ bool anomap_advance(struct anomap *map, int anomap_cmp_str(const void *a, const void *b); +__END_DECLS + #endif // !ANOMAP_H From 59c5eb360579c28dcf1315dba584a81826ff7b02 Mon Sep 17 00:00:00 2001 From: Anotra Date: Mon, 19 Feb 2024 18:06:52 -0500 Subject: [PATCH 08/14] fix: ignore close on execute for platforms that don't support it --- core/io_poller.c | 10 +++++++--- src/concord-once.c | 12 ++++++++++-- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/core/io_poller.c b/core/io_poller.c index b2843a6d..dd69caa0 100644 --- a/core/io_poller.c +++ b/core/io_poller.c @@ -66,10 +66,14 @@ io_poller_create(void) if (0 == pipe(io->wakeup_fds)) { const int on = 1; bool success = true; - for (int i = 0; i < 2; i++) - if (0 != ioctl(io->wakeup_fds[i], FIOCLEX, NULL) - && 0 != ioctl(io->wakeup_fds[i], FIONBIO, &on)) + for (int i = 0; i < 2; i++) { + if (0 != ioctl(io->wakeup_fds[i], FIONBIO, &on)) success = false; +#ifdef FIOCLEX + if (0 != ioctl(io->wakeup_fds[i], FIOCLEX, NULL)) + success = false; +#endif + } if (success) { io_poller_socket_add(io, io->wakeup_fds[0], IO_POLLER_IN, on_io_poller_wakeup, NULL); diff --git a/src/concord-once.c b/src/concord-once.c index 80e9ec36..4cce5eb3 100644 --- a/src/concord-once.c +++ b/src/concord-once.c @@ -72,11 +72,13 @@ ccord_global_init() } for (int i = 0; i < 2; i++) { const int on = 1; +#ifdef FIOCLEX if (0 != ioctl(shutdown_fds[i], FIOCLEX, NULL)) { fputs("Failed to make shutdown pipe close on execute\n", stderr); goto fail_pipe_flags; } +#endif if (0 != ioctl(shutdown_fds[i], FIONBIO, &on)) { fputs("Failed to make shutdown pipe nonblocking\n", stderr); goto fail_pipe_flags; @@ -126,9 +128,15 @@ discord_dup_shutdown_fd(void) if (-1 == shutdown_fds[0]) return -1; if (-1 != (fd = dup(shutdown_fds[0]))) { const int on = 1; - if (0 != ioctl(fd, FIOCLEX, NULL) && 0 != ioctl(fd, FIONBIO, &on)) { +#ifdef FIOCLEX + if (0 != ioctl(fd, FIOCLEX, NULL)) { + close(fd); + return -1; + } +#endif + if (0 != ioctl(fd, FIONBIO, &on)) { close(fd); - fd = -1; + return -1; } } return fd; From 29c4aa0e8dfdf9ccbe4f137419d84a3beff5bdc0 Mon Sep 17 00:00:00 2001 From: Anotra Date: Wed, 21 Feb 2024 15:57:05 -0500 Subject: [PATCH 09/14] feat(anomap): add windows SRW locks --- core/anomap.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++ core/anomap.h | 8 +++++-- 2 files changed, 68 insertions(+), 2 deletions(-) diff --git a/core/anomap.c b/core/anomap.c index 9f48becf..760c8ad0 100644 --- a/core/anomap.c +++ b/core/anomap.c @@ -15,6 +15,8 @@ #if defined __has_include #if __has_include() #define ANOMAP_NATIVE_LOCKS NATIVE_LOCK_PTHREADS + #elif __has_include() + #define ANOMAP_NATIVE_LOCKS NATIVE_LOCK_WINDOWS #endif #endif #endif @@ -147,6 +149,66 @@ static unsigned anomap_has_locks = .release = _pthread_wr_unlock, }, }; + #elif ANOMAP_NATIVE_LOCKS == NATIVE_LOCK_WINDOWS + #include + #include + static void * + _srw_lock_create(void) { + SRWLOCK *lock = calloc(1, sizeof *lock); + if (!lock) return NULL; + InitializeSRWLock(lock); + return lock; + } + + static void + _srw_lock_destroy(void *lock) { + free(lock); + } + + static bool + _srw_rd_lock_attempt(void *lock) { + return TryAcquireSRWLockShared(lock); + } + + static void + _srw_rd_lock(void *lock) { + AcquireSRWLockShared(lock); + } + + static void + _srw_rd_unlock(void *lock) { + ReleaseSRWLockShared(lock); + } + + static bool + _srw_wr_lock_attempt(void *lock) { + return TryAcquireSRWLockExclusive(lock); + } + + static void + _srw_wr_lock(void *lock) { + AcquireSRWLockExclusive(lock); + } + + static void + _srw_wr_unlock(void *lock) { + ReleaseSRWLockExclusive(lock); + } + + static struct anomap_lock lock_functions = { + .create = _srw_lock_create, + .destroy = _srw_lock_destroy, + .r = { + .attempt = _srw_rd_lock_attempt, + .acquire = _srw_rd_lock, + .release = _srw_rd_unlock, + }, + .w = { + .attempt = _srw_wr_lock_attempt, + .acquire = _srw_wr_lock, + .release = _srw_wr_unlock, + }, + }; #else #error native lock not yet implemented #endif diff --git a/core/anomap.h b/core/anomap.h index 149c7cb0..073147a2 100644 --- a/core/anomap.h +++ b/core/anomap.h @@ -10,7 +10,9 @@ #include #include -__BEGIN_DECLS +#ifdef __cplusplus +extern "C" { +#endif #define ANOMAP_DECLARE_COMPARE_FUNCTION(function_name, data_type) \ static int \ @@ -118,6 +120,8 @@ bool anomap_advance(struct anomap *map, int anomap_cmp_str(const void *a, const void *b); -__END_DECLS +#ifdef __cplusplus +} +#endif #endif // !ANOMAP_H From 8bbbd4cfa2b9bb33f59dd146baefe8c09d28a0cd Mon Sep 17 00:00:00 2001 From: Anotra Date: Wed, 21 Feb 2024 16:28:04 -0500 Subject: [PATCH 10/14] fix: use fcntl instead of ioctl to set nonblock windoze --- core/io_poller.c | 4 ++-- src/concord-once.c | 9 +++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/core/io_poller.c b/core/io_poller.c index dd69caa0..b8b365e7 100644 --- a/core/io_poller.c +++ b/core/io_poller.c @@ -64,10 +64,10 @@ io_poller_create(void) io->pollfds = calloc(io->cap, sizeof *io->pollfds); if (io->elements && io->pollfds) { if (0 == pipe(io->wakeup_fds)) { - const int on = 1; bool success = true; for (int i = 0; i < 2; i++) { - if (0 != ioctl(io->wakeup_fds[i], FIONBIO, &on)) + int flags = fcntl(io->wakeup_fds[i], F_GETFL) | O_NONBLOCK; + if (0 != fcntl(io->wakeup_fds[i], F_SETFL, flags)) success = false; #ifdef FIOCLEX if (0 != ioctl(io->wakeup_fds[i], FIOCLEX, NULL)) diff --git a/src/concord-once.c b/src/concord-once.c index 4cce5eb3..24b26896 100644 --- a/src/concord-once.c +++ b/src/concord-once.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -71,7 +72,6 @@ ccord_global_init() goto fail_pipe_init; } for (int i = 0; i < 2; i++) { - const int on = 1; #ifdef FIOCLEX if (0 != ioctl(shutdown_fds[i], FIOCLEX, NULL)) { fputs("Failed to make shutdown pipe close on execute\n", @@ -79,7 +79,8 @@ ccord_global_init() goto fail_pipe_flags; } #endif - if (0 != ioctl(shutdown_fds[i], FIONBIO, &on)) { + int flags = fcntl(shutdown_fds[i], F_GETFL) | O_NONBLOCK; + if (0 != fcntl(shutdown_fds[i], F_SETFL, flags)) { fputs("Failed to make shutdown pipe nonblocking\n", stderr); goto fail_pipe_flags; } @@ -127,14 +128,14 @@ discord_dup_shutdown_fd(void) int fd = -1; if (-1 == shutdown_fds[0]) return -1; if (-1 != (fd = dup(shutdown_fds[0]))) { - const int on = 1; #ifdef FIOCLEX if (0 != ioctl(fd, FIOCLEX, NULL)) { close(fd); return -1; } #endif - if (0 != ioctl(fd, FIONBIO, &on)) { + int flags = fcntl(fd, F_GETFL) | O_NONBLOCK; + if (0 != fcntl(fd, F_SETFL, flags)) { close(fd); return -1; } From 435dee1fa56ebc22f8db88693e77c9bb5d240088 Mon Sep 17 00:00:00 2001 From: Anotra Date: Sun, 25 Feb 2024 14:04:11 -0500 Subject: [PATCH 11/14] feat(anomap): add anomap_clone and anomap_move, .. refactor --- core/anomap.c | 159 +++++++++++++++++++++++++++++++++++--------------- core/anomap.h | 8 +++ 2 files changed, 121 insertions(+), 46 deletions(-) diff --git a/core/anomap.c b/core/anomap.c index 760c8ad0..1b304a13 100644 --- a/core/anomap.c +++ b/core/anomap.c @@ -28,12 +28,7 @@ struct anomap_lock { bool (*attempt)(void *lock); void (*acquire)(void *lock); void (*release)(void *lock); - } r; - struct { - bool (*attempt)(void *lock); - void (*acquire)(void *lock); - void (*release)(void *lock); - } w; + } r, w; }; static void * @@ -81,7 +76,6 @@ static const struct anomap_lock null_lock = { static unsigned anomap_has_locks = #if !defined ANOMAP_NATIVE_LOCKS || ANOMAP_NATIVE_LOCKS == NATIVE_LOCK_NONE 0; - static const struct anomap_lock lock_functions = null_lock; #else anomap_use_lock; @@ -106,47 +100,47 @@ static unsigned anomap_has_locks = } static bool - _pthread_rd_lock_attempt(void *lock) { + _pthread_rd_attempt(void *lock) { return 0 == pthread_rwlock_tryrdlock(lock); } static void - _pthread_rd_lock(void *lock) { + _pthread_rd_acquire(void *lock) { pthread_rwlock_rdlock(lock); } static void - _pthread_rd_unlock(void *lock) { + _pthread_rd_release(void *lock) { pthread_rwlock_unlock(lock); } static bool - _pthread_wr_lock_attempt(void *lock) { + _pthread_wr_attempt(void *lock) { return 0 == pthread_rwlock_trywrlock(lock); } static void - _pthread_wr_lock(void *lock) { + _pthread_wr_acquire(void *lock) { pthread_rwlock_wrlock(lock); } static void - _pthread_wr_unlock(void *lock) { + _pthread_wr_release(void *lock) { pthread_rwlock_unlock(lock); } - static struct anomap_lock lock_functions = { + static const struct anomap_lock lock_functions = { .create = _pthread_lock_create, .destroy = _pthread_lock_destroy, .r = { - .attempt = _pthread_rd_lock_attempt, - .acquire = _pthread_rd_lock, - .release = _pthread_rd_unlock, + .attempt = _pthread_rd_attempt, + .acquire = _pthread_rd_acquire, + .release = _pthread_rd_release, }, .w = { - .attempt = _pthread_wr_lock_attempt, - .acquire = _pthread_wr_lock, - .release = _pthread_wr_unlock, + .attempt = _pthread_wr_attempt, + .acquire = _pthread_wr_acquire, + .release = _pthread_wr_release, }, }; #elif ANOMAP_NATIVE_LOCKS == NATIVE_LOCK_WINDOWS @@ -166,47 +160,47 @@ static unsigned anomap_has_locks = } static bool - _srw_rd_lock_attempt(void *lock) { + _srw_rd_attempt(void *lock) { return TryAcquireSRWLockShared(lock); } static void - _srw_rd_lock(void *lock) { + _srw_rd_acquire(void *lock) { AcquireSRWLockShared(lock); } static void - _srw_rd_unlock(void *lock) { + _srw_rd_release(void *lock) { ReleaseSRWLockShared(lock); } static bool - _srw_wr_lock_attempt(void *lock) { + _srw_wr_attempt(void *lock) { return TryAcquireSRWLockExclusive(lock); } static void - _srw_wr_lock(void *lock) { + _srw_wr_acquire(void *lock) { AcquireSRWLockExclusive(lock); } static void - _srw_wr_unlock(void *lock) { + _srw_wr_release(void *lock) { ReleaseSRWLockExclusive(lock); } - static struct anomap_lock lock_functions = { + static const struct anomap_lock lock_functions = { .create = _srw_lock_create, .destroy = _srw_lock_destroy, .r = { - .attempt = _srw_rd_lock_attempt, - .acquire = _srw_rd_lock, - .release = _srw_rd_unlock, + .attempt = _srw_rd_attempt, + .acquire = _srw_rd_acquire, + .release = _srw_rd_release, }, .w = { - .attempt = _srw_wr_lock_attempt, - .acquire = _srw_wr_lock, - .release = _srw_wr_unlock, + .attempt = _srw_wr_attempt, + .acquire = _srw_wr_acquire, + .release = _srw_wr_release, }, }; #else @@ -214,12 +208,14 @@ static unsigned anomap_has_locks = #endif #endif -#define LOCK_W_ATTEMPT map->lock.functions->w.attempt(map->lock.lock) -#define LOCK_W_ACQUIRE map->lock.functions->w.acquire(map->lock.lock) -#define LOCK_W_RELEASE map->lock.functions->w.release(map->lock.lock) -#define LOCK_R_ATTEMPT map->lock.functions->r.attempt(map->lock.lock) -#define LOCK_R_ACQUIRE map->lock.functions->r.acquire(map->lock.lock) -#define LOCK_R_RELEASE map->lock.functions->r.release(map->lock.lock) +#define LOCK_DO(MODE, FUNC) \ + map->lock.functions->MODE.FUNC(map->lock.lock) +#define LOCK_W_ATTEMPT LOCK_DO(w, attempt) +#define LOCK_W_ACQUIRE LOCK_DO(w, acquire) +#define LOCK_W_RELEASE LOCK_DO(w, release) +#define LOCK_R_ATTEMPT LOCK_DO(r, attempt) +#define LOCK_R_ACQUIRE LOCK_DO(r, acquire) +#define LOCK_R_RELEASE LOCK_DO(r, release) #define ANOMAP_ALLOWED_OPTIONS ( anomap_reverse_order \ | anomap_direct_access \ @@ -242,7 +238,7 @@ struct anomap { struct { unsigned *arr; size_t len : 32, cap : 32; - size_t highest; + unsigned highest; } map; struct { char *arr; @@ -288,9 +284,13 @@ anomap_init(struct anomap *map, map->cmp = cmp; map->keys.size = key_size; map->vals.size = val_size; +#if !defined ANOMAP_NATIVE_LOCKS || ANOMAP_NATIVE_LOCKS == NATIVE_LOCK_NONE + map->lock.functions = &null_lock; +#else map->lock.functions = options & anomap_use_lock ? &lock_functions : &null_lock; +#endif if ((map->lock.lock = map->lock.functions->create())) return true; @@ -324,6 +324,72 @@ anomap_destroy(struct anomap *map) { free(map); } +struct anomap * +anomap_clone(struct anomap *map, anomap_clone_options options) { + if (options) return NULL; + struct anomap *clone = malloc(sizeof *clone); + if (!clone) return NULL; + struct { + void *ptrs[16]; + size_t len; + } cleanup = { .len = 0 }; + + LOCK_R_ACQUIRE; + memcpy(clone, map, sizeof *clone); + clone->free_on_cleanup = true; + + if (!(clone->lock.lock = clone->lock.functions->create())) + goto lock_create_fail; + +#define CLONE_ARRAY(ARRAY, SIZE) \ + do { \ + if ((clone->ARRAY.arr = malloc(SIZE))) { \ + memcpy(clone->ARRAY.arr, map->ARRAY.arr, SIZE); \ + cleanup.ptrs[cleanup.len++] = clone->ARRAY.arr; \ + } else goto array_copy_fail; \ + } while (0) + + if (clone->map.len) { + CLONE_ARRAY(map, clone->map.cap * sizeof *clone->map.arr); + CLONE_ARRAY(keys, clone->keys.cap * clone->keys.size); + if (clone->vals.size) + CLONE_ARRAY(vals, clone->vals.cap * clone->vals.size); + if (clone->options & anomap_preserve_order) + CLONE_ARRAY(order, clone->order.cap * sizeof *clone->order.arr); + } else { + memset(&clone->map, 0, sizeof clone->map); + memset(&clone->keys, 0, sizeof clone->keys); + clone->keys.size = map->keys.size; + memset(&clone->vals, 0, sizeof clone->vals); + clone->vals.size = map->vals.size; + memset(&clone->order, 0, sizeof clone->order); + } + + LOCK_R_RELEASE; + return clone; + + array_copy_fail: + while (cleanup.len) + free(cleanup.ptrs[--cleanup.len]); + clone->lock.functions->destroy(clone->lock.lock); + lock_create_fail: + LOCK_R_RELEASE; + free(clone); + return NULL; +} + +void +anomap_move(struct anomap *dest, bool free_on_destroy, struct anomap *map) { + LOCK_W_ACQUIRE; + memcpy(dest, map, sizeof *dest); + const bool free_map = map->free_on_cleanup; + dest->free_on_cleanup = free_on_destroy; + memset(map, 0, sizeof *map); + if (free_map) free(map); + map = dest; + LOCK_W_RELEASE; +} + void anomap_set_on_item_changed( struct anomap *map, anomap_on_item_changed *on_changed, void *data) @@ -527,9 +593,10 @@ anomap_operation anomap_do(struct anomap *map, anomap_operation operation, void *key, void *val) { - if (operation == anomap_getval) - LOCK_R_ACQUIRE; - else LOCK_W_ACQUIRE; + const bool use_write_lock = operation != anomap_getval; + if (use_write_lock) + LOCK_W_ACQUIRE; + else LOCK_R_ACQUIRE; const size_t key_size = map->keys.size, val_size = map->vals.size; anomap_operation result = 0; size_t mpos = 0; @@ -633,9 +700,9 @@ anomap_do(struct anomap *map, anomap_operation operation, _anomap_on_empty(map); } finish: - if (operation == anomap_getval) - LOCK_R_RELEASE; - else LOCK_W_RELEASE; + if (use_write_lock) + LOCK_W_RELEASE; + else LOCK_R_RELEASE; return result; } diff --git a/core/anomap.h b/core/anomap.h index 073147a2..a56422e0 100644 --- a/core/anomap.h +++ b/core/anomap.h @@ -61,6 +61,14 @@ struct anomap *anomap_create(size_t key_size, size_t val_size, anomap_options options); void anomap_destroy(struct anomap *map); +typedef enum { +#define ANOMAP_CLONE_OPTIONS_NONE anomap_clone_options_none + anomap_clone_options_none, +} anomap_clone_options; + +struct anomap *anomap_clone(struct anomap *map, anomap_clone_options options); +void anomap_move(struct anomap *dest, bool free_on_destroy, struct anomap *map); + struct anomap_item_changed { struct anomap *map; void *data; From 20c2e1fc7eaaced65bcef856745a8a664a59a59c Mon Sep 17 00:00:00 2001 From: Anotra Date: Sun, 3 Mar 2024 08:21:34 -0500 Subject: [PATCH 12/14] refactor(discord-loop): slightly more efficient --- src/discord-loop.c | 34 ++++++++++------------------------ 1 file changed, 10 insertions(+), 24 deletions(-) diff --git a/src/discord-loop.c b/src/discord-loop.c index 7eed336e..43408fcd 100644 --- a/src/discord-loop.c +++ b/src/discord-loop.c @@ -56,12 +56,6 @@ discord_set_on_cycle(struct discord *client, #define BREAK_ON_FAIL(code, function) \ if (CCORD_OK != (code = function)) break -#define CALL_IO_POLLER_POLL(poll_errno, poll_result, io_poller, delay) \ - do { \ - if (-1 == (poll_result = io_poller_poll(io_poller, (int)(delay)))) \ - poll_errno = errno; \ - } while (0) - CCORDcode discord_run(struct discord *client) { @@ -74,7 +68,6 @@ discord_run(struct discord *client) BREAK_ON_FAIL(code, discord_gateway_start(&client->gw)); while (1) { - int poll_result, poll_errno = 0; int64_t poll_time = 0; now = (int64_t)discord_timestamp_us(client); @@ -82,24 +75,27 @@ discord_run(struct discord *client) if (!client->on_idle) { poll_time = discord_timers_get_next_trigger( timers, sizeof timers / sizeof *timers, now, 60000000); + if (poll_time % 1000 > 850) + poll_time += 1000 - (poll_time % 1000); } - CALL_IO_POLLER_POLL(poll_errno, poll_result, client->io_poller, - poll_time / 1000); - - now = (int64_t)discord_timestamp_us(client); - - if (0 == poll_result) { - + switch (io_poller_poll(client->io_poller, (int)(poll_time / 1000))) + { + case -1: + // TODO: handle poll errno + break; + case 0: if (client->on_idle) { client->on_idle(client); } else { + now = (int64_t)discord_timestamp_us(client); int64_t sleep_time = discord_timers_get_next_trigger( timers, sizeof timers / sizeof *timers, now, 1000); if (sleep_time > 0 && sleep_time < 1000) cog_sleep_us(sleep_time); } + break; } if (client->on_cycle) client->on_cycle(client); @@ -107,16 +103,6 @@ discord_run(struct discord *client) for (unsigned i = 0; i < sizeof timers / sizeof *timers; i++) discord_timers_run(client, timers[i]); - if (poll_result >= 0 && !client->on_idle) - CALL_IO_POLLER_POLL(poll_errno, poll_result, client->io_poller, - 0); - - if (-1 == poll_result) { - /* TODO: handle poll error here */ - /* use poll_errno instead of errno */ - (void)poll_errno; - } - if (client->gw.session->status & DISCORD_SESSION_SHUTDOWN) break; BREAK_ON_FAIL(code, io_poller_perform(client->io_poller)); From 8532b4c9849ca566bbc2488bc9589dd40978c67e Mon Sep 17 00:00:00 2001 From: Anotra Date: Fri, 22 Mar 2024 20:21:25 -0400 Subject: [PATCH 13/14] refactor(anomap): change anomap_foreach_cb --- core/anomap.c | 60 ++++++++++++++++++--------------------------------- core/anomap.h | 3 +-- 2 files changed, 22 insertions(+), 41 deletions(-) diff --git a/core/anomap.c b/core/anomap.c index 1b304a13..de84ae2e 100644 --- a/core/anomap.c +++ b/core/anomap.c @@ -417,10 +417,8 @@ _anomap_on_empty(struct anomap *map) { } static void -_anomap_clear_foreach(struct anomap *map, void *data, - const void *key, const void *val) -{ - if (!map->on_changed.cb) return; +_anomap_clear_foreach(const void *key, const void *val, void *data) { + struct anomap *map = data; map->on_changed.cb( &(struct anomap_item_changed) { .map = map, @@ -437,8 +435,8 @@ anomap_clear(struct anomap *map) { if (map->map.len) { if (map->on_changed.cb) { if (map->options & anomap_preserve_order) - _anomap_foreach(map, _anomap_clear_foreach, NULL, anomap_tail); - else _anomap_foreach(map, _anomap_clear_foreach, NULL, anomap_head); + _anomap_foreach(map, _anomap_clear_foreach, map, anomap_tail); + else _anomap_foreach(map, _anomap_clear_foreach, map, anomap_head); } map->map.len = 0; _anomap_on_empty(map); @@ -802,49 +800,33 @@ _anomap_foreach(struct anomap *map, anomap_foreach_cb *cb, void *data, unsigned head = map->order.arr[tail].next; unsigned start = start_position == anomap_head ? head : tail; unsigned pos = start; - switch ((start_position == anomap_tail ? 2 : 0) | (val_size ? 1 : 0)) { - while (pos != tail) { - pos = map->order.arr[pos].next; - case 0 | 0: - cb(map, data, map->keys.arr + key_size * pos, NULL); - } break; + switch ((start_position == anomap_tail ? 1 : 0)) { while (pos != tail) { pos = map->order.arr[pos].next; - case 0 | 1: - cb(map, data, map->keys.arr + key_size * pos, - map->vals.arr + val_size * pos); + case 0: + cb(map->keys.arr + key_size * pos, + map->vals.arr + val_size * pos, + data); } break; while (pos != head) { pos = map->order.arr[pos].prev; - case 2 | 0: - cb(map, data, map->keys.arr + key_size * pos, NULL); - } break; - while (pos != head) { - pos = map->order.arr[pos].prev; - case 2 | 1: - cb(map, data, map->keys.arr + key_size * pos, - map->vals.arr + val_size * pos); + case 1: + cb(map->keys.arr + key_size * pos, + map->vals.arr + val_size * pos, + data); } break; } } else { if (start_position == anomap_head) { - if (val_size) { - for (size_t i=0; imap.len; i++) - cb(map, data, map->keys.arr + key_size * map->map.arr[i], - map->vals.arr + val_size * map->map.arr[i]); - } else { - for (size_t i=0; imap.len; i++) - cb(map, data, map->keys.arr + key_size * map->map.arr[i], NULL); - } + for (size_t i=0; imap.len; i++) + cb(map->keys.arr + key_size * map->map.arr[i], + map->vals.arr + val_size * map->map.arr[i], + data); } else { - if (val_size) { - for (size_t i = map->map.len; i--;) - cb(map, data, map->keys.arr + key_size * map->map.arr[i], - map->vals.arr + val_size * map->map.arr[i]); - } else { - for (size_t i = map->map.len; i--;) - cb(map, data, map->keys.arr + key_size * map->map.arr[i], NULL); - } + for (size_t i = map->map.len; i--;) + cb(map->keys.arr + key_size * map->map.arr[i], + map->vals.arr + val_size * map->map.arr[i], + data); } } } diff --git a/core/anomap.h b/core/anomap.h index a56422e0..f56d7b01 100644 --- a/core/anomap.h +++ b/core/anomap.h @@ -105,8 +105,7 @@ size_t anomap_delete_range(struct anomap *map, size_t from_index, size_t to_index, void *keys, void *vals); -typedef void anomap_foreach_cb(struct anomap *map, void *data, - const void *key, const void *val); +typedef void anomap_foreach_cb(const void *key, const void *val, void *data); void anomap_foreach(struct anomap *map, anomap_foreach_cb *cb, void *data); void anomap_foreach_reverse(struct anomap *map, anomap_foreach_cb *cb, void *data); From a48553cbc70919efbde05858f82f8408dc866839 Mon Sep 17 00:00:00 2001 From: Anotra Date: Sun, 24 Mar 2024 16:48:54 -0400 Subject: [PATCH 14/14] refactor(anomap): make struct anomap a type, and reorganize header --- core/anomap.c | 64 ++++++++++++++++---------------- core/anomap.h | 101 +++++++++++++++++++++++++++----------------------- 2 files changed, 86 insertions(+), 79 deletions(-) diff --git a/core/anomap.c b/core/anomap.c index de84ae2e..78b1ce54 100644 --- a/core/anomap.c +++ b/core/anomap.c @@ -257,7 +257,7 @@ struct anomap { } order; }; -static void _anomap_foreach(struct anomap *map, anomap_foreach_cb *cb, +static void _anomap_foreach(anomap *map, anomap_foreach_cb *cb, void *data, anomap_position start_position); anomap_options @@ -267,11 +267,11 @@ anomap_supported_options(void) { size_t anomap_struct_size(void) { - return sizeof(struct anomap); + return sizeof(anomap); } bool -anomap_init(struct anomap *map, +anomap_init(anomap *map, size_t key_size, size_t val_size, int (*cmp)(const void *, const void *), anomap_options options) @@ -298,12 +298,12 @@ anomap_init(struct anomap *map, return false; } -struct anomap * +anomap * anomap_create(size_t key_size, size_t val_size, int (*cmp)(const void *, const void *), anomap_options options) { - struct anomap *map = calloc(1, sizeof *map); + anomap *map = calloc(1, sizeof *map); if (!map) return NULL; if (anomap_init(map, key_size, val_size, cmp, options)) return map->free_on_cleanup = true, map; @@ -311,7 +311,7 @@ anomap_create(size_t key_size, size_t val_size, } void -anomap_destroy(struct anomap *map) { +anomap_destroy(anomap *map) { anomap_clear(map); free(map->map.arr); free(map->keys.arr); @@ -324,10 +324,10 @@ anomap_destroy(struct anomap *map) { free(map); } -struct anomap * -anomap_clone(struct anomap *map, anomap_clone_options options) { +anomap * +anomap_clone(anomap *map, anomap_clone_options options) { if (options) return NULL; - struct anomap *clone = malloc(sizeof *clone); + anomap *clone = malloc(sizeof *clone); if (!clone) return NULL; struct { void *ptrs[16]; @@ -379,7 +379,7 @@ anomap_clone(struct anomap *map, anomap_clone_options options) { } void -anomap_move(struct anomap *dest, bool free_on_destroy, struct anomap *map) { +anomap_move(anomap *dest, bool free_on_destroy, anomap *map) { LOCK_W_ACQUIRE; memcpy(dest, map, sizeof *dest); const bool free_map = map->free_on_cleanup; @@ -392,7 +392,7 @@ anomap_move(struct anomap *dest, bool free_on_destroy, struct anomap *map) { void anomap_set_on_item_changed( - struct anomap *map, anomap_on_item_changed *on_changed, void *data) + anomap *map, anomap_on_item_changed *on_changed, void *data) { LOCK_W_ACQUIRE; map->on_changed.cb = on_changed; @@ -401,7 +401,7 @@ anomap_set_on_item_changed( } size_t -anomap_length(struct anomap *map) { +anomap_length(anomap *map) { LOCK_R_ACQUIRE; size_t len = map->map.len; LOCK_R_RELEASE; @@ -409,7 +409,7 @@ anomap_length(struct anomap *map) { } static inline void -_anomap_on_empty(struct anomap *map) { +_anomap_on_empty(anomap *map) { if (map->map.len) return; if (map->options & anomap_preserve_order) map->order.tail = map->order.arr[0].next = map->order.arr[0].prev = 0; @@ -418,7 +418,7 @@ _anomap_on_empty(struct anomap *map) { static void _anomap_clear_foreach(const void *key, const void *val, void *data) { - struct anomap *map = data; + anomap *map = data; map->on_changed.cb( &(struct anomap_item_changed) { .map = map, @@ -430,7 +430,7 @@ _anomap_clear_foreach(const void *key, const void *val, void *data) { } void -anomap_clear(struct anomap *map) { +anomap_clear(anomap *map) { LOCK_W_ACQUIRE; if (map->map.len) { if (map->on_changed.cb) { @@ -445,13 +445,13 @@ anomap_clear(struct anomap *map) { } bool -anomap_contains(struct anomap *map, void *key) { +anomap_contains(anomap *map, void *key) { size_t position; return anomap_index_of(map, key, &position); } static bool -anomap_index_of_no_lock(struct anomap *map, void *key, size_t *position) { +anomap_index_of_no_lock(anomap *map, void *key, size_t *position) { size_t lo = 0, mid, hi = map->map.len; const char *const keys = map->keys.arr; const size_t key_size = map->keys.size; @@ -485,7 +485,7 @@ anomap_index_of_no_lock(struct anomap *map, void *key, size_t *position) { } bool -anomap_index_of(struct anomap *map, void *key, size_t *position) { +anomap_index_of(anomap *map, void *key, size_t *position) { LOCK_R_ACQUIRE; bool found = anomap_index_of_no_lock(map, key, position); LOCK_R_RELEASE; @@ -493,7 +493,7 @@ anomap_index_of(struct anomap *map, void *key, size_t *position) { } bool -anomap_at_index(struct anomap *map, size_t index, void *key, void *val) { +anomap_at_index(anomap *map, size_t index, void *key, void *val) { LOCK_R_ACQUIRE; if (index >= map->map.len) { LOCK_R_RELEASE; @@ -508,7 +508,7 @@ anomap_at_index(struct anomap *map, size_t index, void *key, void *val) { } const void * -anomap_direct_key_at_index(struct anomap *map, size_t index) { +anomap_direct_key_at_index(anomap *map, size_t index) { void *result = NULL; LOCK_R_ACQUIRE; if (!(map->options & anomap_direct_access)) @@ -524,7 +524,7 @@ anomap_direct_key_at_index(struct anomap *map, size_t index) { } void * -anomap_direct_val_at_index(struct anomap *map, size_t index) { +anomap_direct_val_at_index(anomap *map, size_t index) { void *result = NULL; LOCK_R_ACQUIRE; if (!(map->options & anomap_direct_access)) @@ -542,7 +542,7 @@ anomap_direct_val_at_index(struct anomap *map, size_t index) { } static bool -_anomap_ensure_capacity(struct anomap *map, size_t capacity) { +_anomap_ensure_capacity(anomap *map, size_t capacity) { if (capacity > (size_t)1 << 31) return false; if (capacity <= map->map.cap) return true; size_t cap = map->map.cap ? map->map.cap << 1 : 8; @@ -574,7 +574,7 @@ _anomap_ensure_capacity(struct anomap *map, size_t capacity) { } static void -_unlink_element(struct anomap *map, unsigned pos) { +_unlink_element(anomap *map, unsigned pos) { if (0 == map->map.len) { map->order.tail = map->order.arr[0].next = map->order.arr[0].prev = 0; } else { @@ -588,9 +588,7 @@ _unlink_element(struct anomap *map, unsigned pos) { } anomap_operation -anomap_do(struct anomap *map, anomap_operation operation, - void *key, void *val) -{ +anomap_do(anomap *map, anomap_operation operation, void *key, void *val) { const bool use_write_lock = operation != anomap_getval; if (use_write_lock) LOCK_W_ACQUIRE; @@ -705,7 +703,7 @@ anomap_do(struct anomap *map, anomap_operation operation, } static size_t -anomap_copy_range_no_lock(struct anomap *map, +anomap_copy_range_no_lock(anomap *map, size_t from_index, size_t to_index, void *keys, void *vals) { @@ -731,7 +729,7 @@ anomap_copy_range_no_lock(struct anomap *map, } size_t -anomap_copy_range(struct anomap *map, size_t from_index, size_t to_index, +anomap_copy_range(anomap *map, size_t from_index, size_t to_index, void *keys, void *vals) { LOCK_R_ACQUIRE; @@ -742,7 +740,7 @@ anomap_copy_range(struct anomap *map, size_t from_index, size_t to_index, } size_t -anomap_delete_range(struct anomap *map, size_t from_index, size_t to_index, +anomap_delete_range(anomap *map, size_t from_index, size_t to_index, void *keys, void *vals) { LOCK_W_ACQUIRE; @@ -789,7 +787,7 @@ anomap_delete_range(struct anomap *map, size_t from_index, size_t to_index, } static void -_anomap_foreach(struct anomap *map, anomap_foreach_cb *cb, void *data, +_anomap_foreach(anomap *map, anomap_foreach_cb *cb, void *data, anomap_position start_position) { const size_t key_size = map->keys.size, val_size = map->vals.size; @@ -832,14 +830,14 @@ _anomap_foreach(struct anomap *map, anomap_foreach_cb *cb, void *data, } void -anomap_foreach(struct anomap *map, anomap_foreach_cb *cb, void *data) { +anomap_foreach(anomap *map, anomap_foreach_cb *cb, void *data) { LOCK_R_ACQUIRE; _anomap_foreach(map, cb, data, anomap_head); LOCK_R_RELEASE; } void -anomap_foreach_reverse(struct anomap *map, anomap_foreach_cb *cb, void *data) { +anomap_foreach_reverse(anomap *map, anomap_foreach_cb *cb, void *data) { LOCK_R_ACQUIRE; _anomap_foreach(map, cb, data, anomap_tail); LOCK_R_RELEASE; @@ -847,7 +845,7 @@ anomap_foreach_reverse(struct anomap *map, anomap_foreach_cb *cb, void *data) { bool -anomap_advance(struct anomap *map, size_t *index, anomap_position *position) { +anomap_advance(anomap *map, size_t *index, anomap_position *position) { unsigned pos; LOCK_R_ACQUIRE; if (0 == map->map.len) diff --git a/core/anomap.h b/core/anomap.h index f56d7b01..351485c4 100644 --- a/core/anomap.h +++ b/core/anomap.h @@ -22,52 +22,59 @@ extern "C" { } typedef enum { -#define ANOMAP_REVERSE_ORDER anomap_reverse_order - anomap_reverse_order = 1 << 0, -#define ANOMAP_DIRECT_ACCESS anomap_direct_access - anomap_direct_access = 1 << 1, -#define ANOMAP_PRESERVE_ORDER anomap_preserve_order - anomap_preserve_order = 1 << 2, -#define ANOMAP_USE_LOCK anomap_use_lock - anomap_use_lock = 1 << 3, + #define ANOMAP_REVERSE_ORDER anomap_reverse_order + anomap_reverse_order = 1 << 0, + + #define ANOMAP_DIRECT_ACCESS anomap_direct_access + anomap_direct_access = 1 << 1, + + #define ANOMAP_PRESERVE_ORDER anomap_preserve_order + anomap_preserve_order = 1 << 2, + + #define ANOMAP_USE_LOCK anomap_use_lock + anomap_use_lock = 1 << 3, } anomap_options; typedef enum { -#define ANOMAP_INSERT anomap_insert - anomap_insert = 1 << 0, -#define ANOMAP_UPDATE anomap_update - anomap_update = 1 << 1, -#define ANOMAP_UPSERT anomap_upsert - anomap_upsert = anomap_insert | anomap_update, -#define ANOMAP_DELETE anomap_delete - anomap_delete = 1 << 2, -#define ANOMAP_GETVAL anomap_getval - anomap_getval = 1 << 3, + #define ANOMAP_INSERT anomap_insert + anomap_insert = 1 << 0, + + #define ANOMAP_UPDATE anomap_update + anomap_update = 1 << 1, + + #define ANOMAP_UPSERT anomap_upsert + anomap_upsert = anomap_insert | anomap_update, + + #define ANOMAP_DELETE anomap_delete + anomap_delete = 1 << 2, + + #define ANOMAP_GETVAL anomap_getval + anomap_getval = 1 << 3, } anomap_operation; -struct anomap; +typedef struct anomap anomap; anomap_options anomap_supported_options(void); size_t anomap_struct_size(void); -bool anomap_init(struct anomap *map, +bool anomap_init(anomap *map, size_t key_size, size_t val_size, int (*cmp)(const void *, const void *), anomap_options options); -struct anomap *anomap_create(size_t key_size, size_t val_size, - int (*cmp)(const void *, const void *), - anomap_options options); -void anomap_destroy(struct anomap *map); +anomap *anomap_create(size_t key_size, size_t val_size, + int (*cmp)(const void *, const void *), + anomap_options options); +void anomap_destroy(anomap *map); typedef enum { -#define ANOMAP_CLONE_OPTIONS_NONE anomap_clone_options_none - anomap_clone_options_none, + #define ANOMAP_CLONE_OPTIONS_NONE anomap_clone_options_none + anomap_clone_options_none, } anomap_clone_options; -struct anomap *anomap_clone(struct anomap *map, anomap_clone_options options); -void anomap_move(struct anomap *dest, bool free_on_destroy, struct anomap *map); +struct anomap *anomap_clone(anomap *map, anomap_clone_options options); +void anomap_move(anomap *dest, bool free_on_destroy, anomap *map); struct anomap_item_changed { struct anomap *map; @@ -85,43 +92,45 @@ typedef void anomap_on_item_changed(const struct anomap_item_changed *ev); void anomap_set_on_item_changed( struct anomap *map, anomap_on_item_changed *on_changed, void *data); -size_t anomap_length(struct anomap *map); -void anomap_clear(struct anomap *map); +size_t anomap_length(anomap *map); +void anomap_clear(anomap *map); -bool anomap_contains(struct anomap *map, void *key); -bool anomap_index_of(struct anomap *map, void *key, size_t *index); -bool anomap_at_index(struct anomap *map, size_t index, void *key, void *val); -const void *anomap_direct_key_at_index(struct anomap *map, size_t index); -void *anomap_direct_val_at_index(struct anomap *map, size_t index); +bool anomap_contains(anomap *map, void *key); +bool anomap_index_of(anomap *map, void *key, size_t *index); +bool anomap_at_index(anomap *map, size_t index, void *key, void *val); +const void *anomap_direct_key_at_index(anomap *map, size_t index); +void *anomap_direct_val_at_index(anomap *map, size_t index); -anomap_operation anomap_do(struct anomap *map, +anomap_operation anomap_do(anomap *map, anomap_operation operation, void *key, void *val); -size_t anomap_copy_range(struct anomap *map, +size_t anomap_copy_range(anomap *map, size_t from_index, size_t to_index, void *keys, void *vals); -size_t anomap_delete_range(struct anomap *map, +size_t anomap_delete_range(anomap *map, size_t from_index, size_t to_index, void *keys, void *vals); typedef void anomap_foreach_cb(const void *key, const void *val, void *data); -void anomap_foreach(struct anomap *map, anomap_foreach_cb *cb, void *data); -void anomap_foreach_reverse(struct anomap *map, - anomap_foreach_cb *cb, void *data); +void anomap_foreach(anomap *map, anomap_foreach_cb *cb, void *data); +void anomap_foreach_reverse(anomap *map, anomap_foreach_cb *cb, void *data); typedef enum { #define ANOMAP_HEAD anomap_head - anomap_head, + anomap_head, + #define ANOMAP_TAIL anomap_tail - anomap_tail, + anomap_tail, + #define ANOMAP_PREV anomap_prev - anomap_prev, + anomap_prev, + #define ANOMAP_NEXT anomap_next - anomap_next, + anomap_next, } anomap_position; -bool anomap_advance(struct anomap *map, +bool anomap_advance(anomap *map, size_t *index, anomap_position *position);