From 1af7947d4c4cce2b7476202c5e020cd9f7b45150 Mon Sep 17 00:00:00 2001 From: Anotra Date: Wed, 13 Apr 2022 07:44:41 -0400 Subject: [PATCH 01/24] fix(discord-timer.c): make timer delay -1 disable a timer --- src/discord-timer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/discord-timer.c b/src/discord-timer.c index 7ee80d5dc..830131f16 100644 --- a/src/discord-timer.c +++ b/src/discord-timer.c @@ -105,7 +105,7 @@ discord_timers_run(struct discord *client, struct discord_timers *timers) TIMER_TRY_DELETE int64_t next = -1; - if (timer.repeat != 0) { + if (timer.repeat != 0 && timer.delay != -1) { if (timer.interval > 0) next = now + ((timer.flags & DISCORD_TIMER_MICROSECONDS) ? timer.interval : timer.interval * 1000); From 7f49653f00b9ce18ee3597743ea7c52fc8ab529a Mon Sep 17 00:00:00 2001 From: Anotra Date: Wed, 13 Apr 2022 09:53:48 -0400 Subject: [PATCH 02/24] refactor(discord-timer.c): rework delete and cancel logic. add DISCORD_TIMER_GET flag --- include/discord.h | 2 ++ src/discord-timer.c | 76 ++++++++++++++++++++++++++++----------------- 2 files changed, 50 insertions(+), 28 deletions(-) diff --git a/include/discord.h b/include/discord.h index d45a4e1f3..1fd437863 100644 --- a/include/discord.h +++ b/include/discord.h @@ -308,6 +308,8 @@ enum discord_timer_flags { DISCORD_TIMER_CANCELED = 1 << 3, /** used in the timer callback to skip update phase */ DISCORD_TIMER_DONT_UPDATE = 1 << 4, + /** used in discord_timer_ctl to get the timer's data */ + DISCORD_TIMER_GET = 1 << 5, }; /** @brief struct used for modifying, and getting info about a timer */ diff --git a/src/discord-timer.c b/src/discord-timer.c index 830131f16..7523bdd18 100644 --- a/src/discord-timer.c +++ b/src/discord-timer.c @@ -2,6 +2,12 @@ #include "discord.h" #include "discord-internal.h" +#define DISCORD_TIMER_ALLOWED_FLAGS \ + ( DISCORD_TIMER_MILLISECONDS \ + | DISCORD_TIMER_MICROSECONDS \ + | DISCORD_TIMER_DELETE \ + | DISCORD_TIMER_DELETE_AUTO ) + static int cmp_timers(const void *a, const void *b) { @@ -51,38 +57,50 @@ unsigned _discord_timer_ctl( struct discord *client, struct discord_timers *timers, - struct discord_timer *timer) + struct discord_timer *timer_ret) { - int64_t now = -1; - if (timer->flags & DISCORD_TIMER_DELETE) { - unsigned id; - if (timer->id) { - id = priority_queue_get(timers->q, timer->id, NULL, timer); - if (id) return priority_queue_del(timers->q, id) ? id : 0; + struct discord_timer timer; + memcpy(&timer, timer_ret, sizeof timer); + + int64_t key = -1; + if (timer.id) { + if (!priority_queue_get(timers->q, timer.id, &key, NULL)) + return 0; + + if (timer.flags & DISCORD_TIMER_GET) { + priority_queue_get(timers->q, timer.id, NULL, timer_ret); + if (timer.flags == DISCORD_TIMER_GET) + return timer.id; } - return 0; } - if (timer->delay >= 0) + + int64_t now = -1; + if (timer.delay >= 0) now = (int64_t)discord_timestamp_us(client) + - ((timer->flags & DISCORD_TIMER_MICROSECONDS) - ? timer->delay : timer->delay * 1000); - if (!timer->id) { - return priority_queue_push(timers->q, &now, timer); + ((timer.flags & DISCORD_TIMER_MICROSECONDS) + ? timer.delay : timer.delay * 1000); + if (timer.flags & (DISCORD_TIMER_DELETE | DISCORD_TIMER_CANCELED)) + now = 0; + + timer.flags &= DISCORD_TIMER_ALLOWED_FLAGS | DISCORD_TIMER_CANCELED; + + if (!timer.id) { + return priority_queue_push(timers->q, &now, &timer); } else { if (timers->currently_being_run - && timers->currently_being_run->id == timer->id) + && timers->currently_being_run->id == timer.id) timers->currently_being_run->flags |= DISCORD_TIMER_DONT_UPDATE; - if (priority_queue_update(timers->q, timer->id, &now, timer)) - return timer->id; + if (priority_queue_update(timers->q, timer.id, &now, &timer)) + return timer.id; return 0; } } #define TIMER_TRY_DELETE \ - if (timer.flags & DISCORD_TIMER_DELETE) { \ - priority_queue_pop(timers->q, NULL, NULL); \ + if (timer.flags & DISCORD_TIMER_DELETE) { \ + priority_queue_del(timers->q, timer.id); \ continue; \ - } + } void discord_timers_run(struct discord *client, struct discord_timers *timers) @@ -95,25 +113,28 @@ discord_timers_run(struct discord *client, struct discord_timers *timers) { if (trigger > now || trigger == -1) break; - TIMER_TRY_DELETE + if (~timer.flags & DISCORD_TIMER_CANCELED) + TIMER_TRY_DELETE - if (timer.repeat > 0) + if (timer.repeat > 0 && ~timer.flags & DISCORD_TIMER_CANCELED) timer.repeat--; if (timer.cb) timer.cb(client, &timer); - if (timer.repeat == 0 && (timer.flags & DISCORD_TIMER_DELETE_AUTO)) + if ((timer.repeat == 0 || timer.flags & DISCORD_TIMER_CANCELED) + && (timer.flags & DISCORD_TIMER_DELETE_AUTO)) timer.flags |= DISCORD_TIMER_DELETE; TIMER_TRY_DELETE + if (timer.flags & DISCORD_TIMER_DONT_UPDATE) + continue; + int64_t next = -1; - if (timer.repeat != 0 && timer.delay != -1) { + if (timer.repeat != 0 && timer.delay != -1 + && ~timer.flags & DISCORD_TIMER_CANCELED) { if (timer.interval > 0) next = now + ((timer.flags & DISCORD_TIMER_MICROSECONDS) ? timer.interval : timer.interval * 1000); } - if (priority_queue_peek(timers->q, NULL, NULL) != timer.id) - continue; - if (timer.flags & DISCORD_TIMER_DONT_UPDATE) - continue; + timer.flags &= DISCORD_TIMER_ALLOWED_FLAGS; priority_queue_update(timers->q, timer.id, &next, &timer); } timers->currently_being_run = NULL; @@ -152,7 +173,6 @@ discord_timer(struct discord *client, discord_ev_timer cb, return _discord_timer(client, &client->timers.user, cb, data, delay); } - unsigned discord_internal_timer(struct discord *client, discord_ev_timer cb, void *data, int64_t delay) From d3a6e0134026ff3285c21020f8429cadb8d2823f Mon Sep 17 00:00:00 2001 From: Anotra Date: Wed, 13 Apr 2022 10:52:25 -0400 Subject: [PATCH 03/24] feat(discord-timer): added DISCORD_TIMER_INTERVAL_FIXED --- include/discord.h | 2 ++ src/discord-timer.c | 9 ++++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/include/discord.h b/include/discord.h index 1fd437863..70a778429 100644 --- a/include/discord.h +++ b/include/discord.h @@ -310,6 +310,8 @@ enum discord_timer_flags { DISCORD_TIMER_DONT_UPDATE = 1 << 4, /** used in discord_timer_ctl to get the timer's data */ DISCORD_TIMER_GET = 1 << 5, + /** timer should run using a fixed interval based on start time */ + DISCORD_TIMER_INTERVAL_FIXED = 1 << 6, }; /** @brief struct used for modifying, and getting info about a timer */ diff --git a/src/discord-timer.c b/src/discord-timer.c index 7523bdd18..6194c7c0e 100644 --- a/src/discord-timer.c +++ b/src/discord-timer.c @@ -6,7 +6,8 @@ ( DISCORD_TIMER_MILLISECONDS \ | DISCORD_TIMER_MICROSECONDS \ | DISCORD_TIMER_DELETE \ - | DISCORD_TIMER_DELETE_AUTO ) + | DISCORD_TIMER_DELETE_AUTO \ + | DISCORD_TIMER_INTERVAL_FIXED ) static int cmp_timers(const void *a, const void *b) @@ -131,8 +132,10 @@ discord_timers_run(struct discord *client, struct discord_timers *timers) if (timer.repeat != 0 && timer.delay != -1 && ~timer.flags & DISCORD_TIMER_CANCELED) { if (timer.interval > 0) - next = now + ((timer.flags & DISCORD_TIMER_MICROSECONDS) - ? timer.interval : timer.interval * 1000); + next = ((timer.flags & DISCORD_TIMER_INTERVAL_FIXED) + ? trigger : now) + + ((timer.flags & DISCORD_TIMER_MICROSECONDS) + ? timer.interval : timer.interval * 1000); } timer.flags &= DISCORD_TIMER_ALLOWED_FLAGS; priority_queue_update(timers->q, timer.id, &next, &timer); From 5ec231b4beb955021a244b35a002f88bb4f62bae Mon Sep 17 00:00:00 2001 From: Anotra Date: Wed, 13 Apr 2022 11:06:57 -0400 Subject: [PATCH 04/24] refactor(discord-timer.c): remove flag DISCORD_TIMER_DONT_UPDATE --- include/discord-internal.h | 5 ++++- include/discord.h | 4 ++-- src/discord-timer.c | 15 ++++++++------- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/include/discord-internal.h b/include/discord-internal.h index 109103a8a..def97ca63 100644 --- a/include/discord-internal.h +++ b/include/discord-internal.h @@ -682,7 +682,10 @@ void discord_gateway_send_presence_update(struct discord_gateway *gw); struct discord_timers { priority_queue *q; - struct discord_timer *currently_being_run; + struct { + struct discord_timer *timer; + bool skip_update_phase; + } active; }; /** diff --git a/include/discord.h b/include/discord.h index 70a778429..7a30c4ae3 100644 --- a/include/discord.h +++ b/include/discord.h @@ -306,8 +306,8 @@ enum discord_timer_flags { DISCORD_TIMER_DELETE_AUTO = 1 << 2, /** timer has been canceled. user should cleanup only */ DISCORD_TIMER_CANCELED = 1 << 3, - /** used in the timer callback to skip update phase */ - DISCORD_TIMER_DONT_UPDATE = 1 << 4, + + /** used in discord_timer_ctl to get the timer's data */ DISCORD_TIMER_GET = 1 << 5, /** timer should run using a fixed interval based on start time */ diff --git a/src/discord-timer.c b/src/discord-timer.c index 6194c7c0e..a72320908 100644 --- a/src/discord-timer.c +++ b/src/discord-timer.c @@ -88,9 +88,8 @@ _discord_timer_ctl( if (!timer.id) { return priority_queue_push(timers->q, &now, &timer); } else { - if (timers->currently_being_run - && timers->currently_being_run->id == timer.id) - timers->currently_being_run->flags |= DISCORD_TIMER_DONT_UPDATE; + if (timers->active.timer && timers->active.timer->id == timer.id) + timers->active.skip_update_phase = true; if (priority_queue_update(timers->q, timer.id, &now, &timer)) return timer.id; return 0; @@ -108,7 +107,7 @@ discord_timers_run(struct discord *client, struct discord_timers *timers) { int64_t now = (int64_t)discord_timestamp_us(client); struct discord_timer timer; - timers->currently_being_run = &timer; + timers->active.timer = &timer; for (int64_t trigger; (timer.id = priority_queue_peek(timers->q, &trigger, &timer));) { @@ -119,14 +118,16 @@ discord_timers_run(struct discord *client, struct discord_timers *timers) if (timer.repeat > 0 && ~timer.flags & DISCORD_TIMER_CANCELED) timer.repeat--; + timers->active.skip_update_phase = false; if (timer.cb) timer.cb(client, &timer); + if (timers->active.skip_update_phase) + continue; if ((timer.repeat == 0 || timer.flags & DISCORD_TIMER_CANCELED) && (timer.flags & DISCORD_TIMER_DELETE_AUTO)) timer.flags |= DISCORD_TIMER_DELETE; TIMER_TRY_DELETE - if (timer.flags & DISCORD_TIMER_DONT_UPDATE) - continue; + int64_t next = -1; if (timer.repeat != 0 && timer.delay != -1 @@ -140,7 +141,7 @@ discord_timers_run(struct discord *client, struct discord_timers *timers) timer.flags &= DISCORD_TIMER_ALLOWED_FLAGS; priority_queue_update(timers->q, timer.id, &next, &timer); } - timers->currently_being_run = NULL; + timers->active.timer = NULL; } unsigned From 6a0c32b88cbe9096bd1097a1e6e82a37a5fc758a Mon Sep 17 00:00:00 2001 From: Anotra Date: Wed, 13 Apr 2022 11:48:53 -0400 Subject: [PATCH 05/24] feat(discord-timer.c): added wrapper functions for common methods associated with timers --- include/discord.h | 57 +++++++++++++++++++++++++++++++++ src/discord-timer.c | 78 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 135 insertions(+) diff --git a/include/discord.h b/include/discord.h index 7a30c4ae3..cf6a9c5b2 100644 --- a/include/discord.h +++ b/include/discord.h @@ -354,6 +354,63 @@ unsigned discord_timer_ctl(struct discord *client, struct discord_timer *timer); unsigned discord_timer(struct discord *client, discord_ev_timer cb, void *data, int64_t delay); +/** + * @brief get the data associated with the timer + * + * @param client the client created with discord_init() + * @param id id of the timer + * @param timer where to copy the timer data to + * @return true on success + */ +bool discord_timer_get(struct discord *client, unsigned id, + struct discord_timer *timer); + +/** + * @brief starts a timer + * + * @param client the client created with discord_init() + * @param id id of the timer + * @return true on success + */ +bool discord_timer_start(struct discord *client, unsigned id); + +/** + * @brief stops a timer + * + * @param client the client created with discord_init() + * @param id id of the timer + * @return true on success + */ +bool discord_timer_stop(struct discord *client, unsigned id); + +/** + * @brief cancels a timer, + * this will delete the timer if DISCORD_TIMER_DELETE_AUTO is enabled + * + * @param client the client created with discord_init() + * @param id id of the timer + * @return true on success + */ +bool discord_timer_cancel(struct discord *client, unsigned id); + +/** + * @brief deletes a timer + * + * @param client the client created with discord_init() + * @param id id of the timer + * @return true on success + */ +bool discord_timer_delete(struct discord *client, unsigned id); + +/** + * @brief cancels, and deletes a timer + * + * @param client the client created with discord_init() + * @param id id of the timer + * @return true on success + */ +bool discord_timer_cancel_and_delete(struct discord *client, unsigned id); + /** @example timers.c * Demonstrates the Timer API for callback scheduling */ diff --git a/src/discord-timer.c b/src/discord-timer.c index a72320908..f635edfea 100644 --- a/src/discord-timer.c +++ b/src/discord-timer.c @@ -183,3 +183,81 @@ discord_internal_timer(struct discord *client, discord_ev_timer cb, { return _discord_timer(client, &client->timers.internal, cb, data, delay); } + +bool +discord_timer_get(struct discord *client, unsigned id, + struct discord_timer *timer) +{ + if (!id) return 0; + return priority_queue_get(client->timers.user.q, id, NULL, timer); +} + +static void +discord_timer_disable_update_if_active(struct discord_timers *timers, + unsigned id) +{ + if (!timers->active.timer) + return; + if (timers->active.timer->id == id) + timers->active.skip_update_phase = true; +} + +bool +discord_timer_start(struct discord *client, unsigned id) +{ + struct discord_timer timer; + discord_timer_disable_update_if_active(&client->timers.user, id); + if (discord_timer_get(client, id, &timer)) { + if (timer.delay < 0) + timer.delay = 0; + return discord_timer_ctl(client, &timer); + } + return false; +} + +bool +discord_timer_stop(struct discord *client, unsigned id) +{ + struct discord_timer timer; + discord_timer_disable_update_if_active(&client->timers.user, id); + if (discord_timer_get(client, id, &timer)) { + int64_t disabled = -1; + return priority_queue_update(client->timers.user.q, + id, &disabled, &timer); + } + return false; +} + +static bool +discord_timer_add_flags(struct discord *client, unsigned id, + enum discord_timer_flags flags) +{ + struct discord_timer timer; + discord_timer_disable_update_if_active(&client->timers.user, id); + if (discord_timer_get(client, id, &timer)) { + timer.flags |= flags; + int64_t run_now = 0; + return priority_queue_update(client->timers.user.q, + id, &run_now, &timer); + } + return false; +} + +bool +discord_timer_cancel(struct discord *client, unsigned id) +{ + return discord_timer_add_flags(client, id, DISCORD_TIMER_CANCELED); +} + +bool +discord_timer_delete(struct discord *client, unsigned id) +{ + return discord_timer_add_flags(client, id, DISCORD_TIMER_DELETE); +} + +bool +discord_timer_cancel_and_delete(struct discord *client, unsigned id) +{ + return discord_timer_add_flags(client, id, DISCORD_TIMER_DELETE + | DISCORD_TIMER_CANCELED); +} From 4ac8816223cf53b0c06ea6d799eff3356fb4f0bf Mon Sep 17 00:00:00 2001 From: Anotra Date: Wed, 13 Apr 2022 12:28:34 -0400 Subject: [PATCH 06/24] feat(discord-timers.c): added discord_timer_interval() function --- include/discord.h | 19 +++++++++++++++++++ src/discord-timer.c | 15 +++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/include/discord.h b/include/discord.h index cf6a9c5b2..590f48630 100644 --- a/include/discord.h +++ b/include/discord.h @@ -354,6 +354,25 @@ unsigned discord_timer_ctl(struct discord *client, struct discord_timer *timer); unsigned discord_timer(struct discord *client, discord_ev_timer cb, void *data, int64_t delay); +/** + * @brief creates a repeating timer that automatically + * deletes itself upon completion + * + * @param client the client created with discord_init() + * @param cb the callback that should be called when timer triggers + * @param data user data + * @param delay delay before timer should start in milliseconds + * @param interval interval between runs + * @param repeat repetitions (-1 == infinity) + * @return the id of the timer + */ +unsigned discord_timer_interval(struct discord *client, + discord_ev_timer cb, + void *data, + int64_t delay, + int64_t interval, + int64_t repeat); + /** * @brief get the data associated with the timer * diff --git a/src/discord-timer.c b/src/discord-timer.c index f635edfea..7ebaca321 100644 --- a/src/discord-timer.c +++ b/src/discord-timer.c @@ -170,6 +170,21 @@ _discord_timer(struct discord *client, struct discord_timers *timers, return _discord_timer_ctl(client, timers, &timer); } +unsigned +discord_timer_interval(struct discord *client, discord_ev_timer cb, void *data, + int64_t delay, int64_t interval, int64_t repeat) +{ + struct discord_timer timer = { + .cb = cb, + .data = data, + .delay = delay, + .interval = interval, + .repeat = repeat, + .flags = DISCORD_TIMER_DELETE_AUTO, + }; + return discord_timer_ctl(client, &timer); +} + unsigned discord_timer(struct discord *client, discord_ev_timer cb, void *data, int64_t delay) From 2c6afed789ee5dd5b6064af2839295b9ff7420ab Mon Sep 17 00:00:00 2001 From: Anotra Date: Fri, 15 Apr 2022 05:58:48 -0400 Subject: [PATCH 07/24] =?UTF-8?q?refactor(discord-client.c):=20use=20?= =?UTF-8?q?=C2=B5seconds=20in=20event=20loop=20instead=20of=20ms?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/discord-client.c | 44 ++++++++++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/src/discord-client.c b/src/discord-client.c index 14b519426..0f41c0b0b 100644 --- a/src/discord-client.c +++ b/src/discord-client.c @@ -348,32 +348,40 @@ discord_run(struct discord *client) while (1) { if (CCORD_OK != (code = discord_gateway_start(&client->gw))) break; - next_run = (int64_t)cog_timestamp_ms(); + next_run = (int64_t)discord_timestamp_us(client); while (1) { - int poll_time = 0, poll_result; + int64_t poll_time = 0, poll_result; - now = (int64_t)cog_timestamp_ms(); - - if (!client->on_idle) - poll_time = now < next_run ? (int)(next_run - now) : 0; + now = (int64_t)discord_timestamp_us(client); struct discord_timers *const timers[] = { &client->timers.internal, &client->timers.user }; - for (unsigned i = 0; i < sizeof timers / sizeof *timers; i++) { - int64_t trigger_us, trigger_ms; - if (priority_queue_peek(timers[i]->q, &trigger_us, NULL)) { - trigger_ms = trigger_us / 1000; - if (trigger_us >= 0) { - if (trigger_ms <= now) { + + if (!client->on_idle) { + poll_time = now < next_run ? (int)((next_run - now)) : 0; + + for (unsigned i = 0; i < sizeof timers / sizeof *timers; i++) { + int64_t trigger; + if (priority_queue_peek(timers[i]->q, &trigger, NULL)) { + if (trigger < 0) + continue; + if (trigger <= now) poll_time = 0; - } else if (trigger_ms - now < poll_time) { - poll_time = (int)(trigger_ms - now); - } + else if (poll_time > trigger - now) + poll_time = trigger - now; } } } - - poll_result = io_poller_poll(client->io_poller, poll_time); + + if (poll_time && poll_time < 3000 /* 3 milliseconds */) { + poll_time = 1000; // FIXME: with below + + // TODO: cog_sleep_us(poll_time); + // poll_time = 0; + } + + poll_result = io_poller_poll(client->io_poller, + (int)(poll_time / 1000)); if (-1 == poll_result) { /* TODO: handle poll error here */ } @@ -398,7 +406,7 @@ discord_run(struct discord *client) break; /* enforce a min 1 sec delay between runs */ - next_run = now + 1000; + next_run = now + 1000000; } } From babb2e74988cae59afce34225272beeff1d7b568 Mon Sep 17 00:00:00 2001 From: Anotra Date: Fri, 15 Apr 2022 09:48:19 -0400 Subject: [PATCH 08/24] fix(discord-timers.c): allow 0 interval timers --- include/discord.h | 4 ++-- src/discord-timer.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/discord.h b/include/discord.h index 590f48630..7ce45dfb1 100644 --- a/include/discord.h +++ b/include/discord.h @@ -326,7 +326,7 @@ struct discord_timer { void *data; /** delay before timer should start */ int64_t delay; - /** interval that the timer should repeat at. must be > 1 */ + /** interval that the timer should repeat at. must be >= 0 */ int64_t interval; /** how many times a timer should repeat (-1 == infinity) */ int64_t repeat; @@ -362,7 +362,7 @@ unsigned discord_timer(struct discord *client, discord_ev_timer cb, * @param cb the callback that should be called when timer triggers * @param data user data * @param delay delay before timer should start in milliseconds - * @param interval interval between runs + * @param interval interval between runs. (-1 == disable repeat) * @param repeat repetitions (-1 == infinity) * @return the id of the timer */ diff --git a/src/discord-timer.c b/src/discord-timer.c index 7ebaca321..d871cbb6d 100644 --- a/src/discord-timer.c +++ b/src/discord-timer.c @@ -132,7 +132,7 @@ discord_timers_run(struct discord *client, struct discord_timers *timers) int64_t next = -1; if (timer.repeat != 0 && timer.delay != -1 && ~timer.flags & DISCORD_TIMER_CANCELED) { - if (timer.interval > 0) + if (timer.interval >= 0) next = ((timer.flags & DISCORD_TIMER_INTERVAL_FIXED) ? trigger : now) + ((timer.flags & DISCORD_TIMER_MICROSECONDS) From e923b6db1a1254ed4e7e382c023c38d1c2379f84 Mon Sep 17 00:00:00 2001 From: Anotra Date: Fri, 15 Apr 2022 10:27:08 -0400 Subject: [PATCH 09/24] docs(timers.c): update discord timer example --- examples/timers.c | 73 +++++++++++++++++++++-------------------------- 1 file changed, 33 insertions(+), 40 deletions(-) diff --git a/examples/timers.c b/examples/timers.c index bff5a03b5..fbad3e439 100644 --- a/examples/timers.c +++ b/examples/timers.c @@ -2,70 +2,63 @@ #include #include #include -#include #include "discord.h" -static int interrupted = 0; - static void -on_sigint(int sig) { - interrupted = 1; +print_timer_info(struct discord_timer *timer) { + printf("Timer id:%u flags:%i " + "delay:%"PRIi64" interval:%"PRIi64" repeat:%"PRIi64"\n", + timer->id, timer->flags, + timer->delay, timer->interval, timer->repeat); } static void one_shot_timer_cb(struct discord *client, struct discord_timer *timer) { - printf("one_shot_timer_cb %u triggered with flags %i\n", - timer->id, timer->flags); - - //DO NOT IGNORE CANCELATION - if (timer->flags & DISCORD_TIMER_CANCELED) { - puts("Timer has been canceled"); - return; - } - if (interrupted) { - puts("Shutdown Canceled"); - return; - } - puts(timer->data); - discord_shutdown(client); + print_timer_info(timer); + if (~timer->flags & DISCORD_TIMER_CANCELED) { + //if timer is not canceled + puts(timer->data); + } else { + puts("ONE SHOT TIMER CANCELED"); + } + free(timer->data); } static void repeating_timer_cb(struct discord *client, struct discord_timer *timer) { - printf("repeating_timer_cb %u triggered with flags %i\n", - timer->id, timer->flags); - printf("%"PRIi64", %"PRIi64"\n", timer->interval, timer->repeat); - if (timer->repeat == 0) - puts("Shutting down soon, press ctrl + c to cancel"); + print_timer_info(timer); + if (timer->flags & DISCORD_TIMER_CANCELED) { + printf("TIMER WITH ID %u CANCELED\n", timer->id); + return; + } + printf("SHUTTING DOWN IN %"PRIi64" SECONDS\n", timer->repeat); + if (!timer->repeat) { + discord_shutdown(client); + } } int main(int argc, char *argv[]) { const char *config_file = argc > 1 ? argv[1] : "../config.json"; - - signal(SIGINT, on_sigint); ccord_global_init(); - struct discord *client = discord_config_init(config_file); - //create one shot auto deleting timer - unsigned one_shot_timer_id = - discord_timer(client, one_shot_timer_cb, "Shutting Down", 5000); - - discord_timer_ctl(client, &(struct discord_timer) { - .id = 0, /* 0 to create a new timer */ - .cb = repeating_timer_cb, - .data = &one_shot_timer_id, - .delay = 0, /* start right away */ - .interval = 100, - .repeat = 10, /* -1 for infinity, 0 for never */ - .flags = DISCORD_TIMER_DELETE_AUTO, - }); + + discord_timer(client, one_shot_timer_cb, strdup("Hello World"), 1000); + discord_timer(client, one_shot_timer_cb, strdup("Hello World!!!!!!"), 5000); + discord_timer(client, one_shot_timer_cb, strdup("Hello World"), 15000); + discord_timer_interval(client, repeating_timer_cb, NULL, 0, 1000, 10); + + //start 3 timers that will never get a chance to run + for (int i=0; i<3; i++) + discord_timer(client, repeating_timer_cb, NULL, 20 * 1000); + discord_run(client); + //discord_cleanup will cancel all timers that are still active discord_cleanup(client); ccord_global_cleanup(); } \ No newline at end of file From a094f475c8351fff3bbb0bc33452129bc60dded6 Mon Sep 17 00:00:00 2001 From: Anotra Date: Sat, 16 Apr 2022 02:14:45 -0400 Subject: [PATCH 10/24] fix(discord-client.c): fix logic for determining how long to sleep --- src/discord-client.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/discord-client.c b/src/discord-client.c index 0f41c0b0b..587cd95cb 100644 --- a/src/discord-client.c +++ b/src/discord-client.c @@ -371,13 +371,13 @@ discord_run(struct discord *client) poll_time = trigger - now; } } - } - if (poll_time && poll_time < 3000 /* 3 milliseconds */) { - poll_time = 1000; // FIXME: with below + if (poll_time > 0) { + if (poll_time < 1000) poll_time = 1000; // FIXME: with below - // TODO: cog_sleep_us(poll_time); - // poll_time = 0; + // const int64_t sleep_time = poll_time % 1000; + // TODO: if (sleep_time) cog_sleep_us(sleep_time); + } } poll_result = io_poller_poll(client->io_poller, From bc3420e55eaf89f10d0405f38579a5af7069001c Mon Sep 17 00:00:00 2001 From: Anotra Date: Sat, 16 Apr 2022 02:20:51 -0400 Subject: [PATCH 11/24] docs(timers.c): update timer example --- examples/timers.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/examples/timers.c b/examples/timers.c index fbad3e439..907e3ace1 100644 --- a/examples/timers.c +++ b/examples/timers.c @@ -6,7 +6,8 @@ #include "discord.h" static void -print_timer_info(struct discord_timer *timer) { +print_timer_info(struct discord_timer *timer) +{ printf("Timer id:%u flags:%i " "delay:%"PRIi64" interval:%"PRIi64" repeat:%"PRIi64"\n", timer->id, timer->flags, @@ -14,19 +15,27 @@ print_timer_info(struct discord_timer *timer) { } static void -one_shot_timer_cb(struct discord *client, struct discord_timer *timer) { +one_shot_timer_cb(struct discord *client, struct discord_timer *timer) +{ print_timer_info(timer); if (~timer->flags & DISCORD_TIMER_CANCELED) { //if timer is not canceled puts(timer->data); - } else { + + //timers can be updated in the callback (see below) + //timer->interval += 100; + //timer->repeat = 1; + //return; //skip free(timer->data); + } + else { puts("ONE SHOT TIMER CANCELED"); } free(timer->data); } static void -repeating_timer_cb(struct discord *client, struct discord_timer *timer) { +repeating_timer_cb(struct discord *client, struct discord_timer *timer) +{ print_timer_info(timer); if (timer->flags & DISCORD_TIMER_CANCELED) { printf("TIMER WITH ID %u CANCELED\n", timer->id); @@ -48,7 +57,10 @@ main(int argc, char *argv[]) discord_timer(client, one_shot_timer_cb, strdup("Hello World"), 1000); discord_timer(client, one_shot_timer_cb, strdup("Hello World!!!!!!"), 5000); + + //start a one shot timer that will never get a chance to run discord_timer(client, one_shot_timer_cb, strdup("Hello World"), 15000); + discord_timer_interval(client, repeating_timer_cb, NULL, 0, 1000, 10); //start 3 timers that will never get a chance to run @@ -61,4 +73,4 @@ main(int argc, char *argv[]) //discord_cleanup will cancel all timers that are still active discord_cleanup(client); ccord_global_cleanup(); -} \ No newline at end of file +} From 07ec9dd20e697d779258fb4c55494eab18c65489 Mon Sep 17 00:00:00 2001 From: Anotra Date: Sat, 16 Apr 2022 04:18:32 -0400 Subject: [PATCH 12/24] refactor(discord-timer.c): make code easier to read --- src/discord-timer.c | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/src/discord-timer.c b/src/discord-timer.c index d871cbb6d..fa736f0fd 100644 --- a/src/discord-timer.c +++ b/src/discord-timer.c @@ -76,14 +76,16 @@ _discord_timer_ctl( } int64_t now = -1; - if (timer.delay >= 0) - now = (int64_t)discord_timestamp_us(client) + + if (timer.delay >= 0) { + now = (int64_t)discord_timestamp_us(client) + + ((timer.flags & DISCORD_TIMER_MICROSECONDS) ? timer.delay : timer.delay * 1000); + } if (timer.flags & (DISCORD_TIMER_DELETE | DISCORD_TIMER_CANCELED)) now = 0; - timer.flags &= DISCORD_TIMER_ALLOWED_FLAGS | DISCORD_TIMER_CANCELED; + timer.flags &= (DISCORD_TIMER_ALLOWED_FLAGS | DISCORD_TIMER_CANCELED); if (!timer.id) { return priority_queue_push(timers->q, &now, &timer); @@ -96,11 +98,13 @@ _discord_timer_ctl( } } -#define TIMER_TRY_DELETE \ - if (timer.flags & DISCORD_TIMER_DELETE) { \ - priority_queue_del(timers->q, timer.id); \ - continue; \ - } +#define TIMER_TRY_DELETE \ + do { \ + if (timer.flags & DISCORD_TIMER_DELETE) { \ + priority_queue_del(timers->q, timer.id); \ + continue; \ + } \ + } while (0) void discord_timers_run(struct discord *client, struct discord_timers *timers) @@ -114,29 +118,35 @@ discord_timers_run(struct discord *client, struct discord_timers *timers) if (trigger > now || trigger == -1) break; if (~timer.flags & DISCORD_TIMER_CANCELED) - TIMER_TRY_DELETE + TIMER_TRY_DELETE; if (timer.repeat > 0 && ~timer.flags & DISCORD_TIMER_CANCELED) timer.repeat--; + timers->active.skip_update_phase = false; if (timer.cb) timer.cb(client, &timer); if (timers->active.skip_update_phase) continue; + if ((timer.repeat == 0 || timer.flags & DISCORD_TIMER_CANCELED) && (timer.flags & DISCORD_TIMER_DELETE_AUTO)) + { timer.flags |= DISCORD_TIMER_DELETE; - TIMER_TRY_DELETE - - + } + + TIMER_TRY_DELETE; int64_t next = -1; if (timer.repeat != 0 && timer.delay != -1 - && ~timer.flags & DISCORD_TIMER_CANCELED) { - if (timer.interval >= 0) + && ~timer.flags & DISCORD_TIMER_CANCELED) + { + if (timer.interval >= 0) { next = ((timer.flags & DISCORD_TIMER_INTERVAL_FIXED) - ? trigger : now) + + ? trigger : now) + + ((timer.flags & DISCORD_TIMER_MICROSECONDS) ? timer.interval : timer.interval * 1000); + } } timer.flags &= DISCORD_TIMER_ALLOWED_FLAGS; priority_queue_update(timers->q, timer.id, &next, &timer); From 55f54aad5916c8982090291c87c62111e2ed1fda Mon Sep 17 00:00:00 2001 From: Anotra Date: Sat, 16 Apr 2022 04:36:59 -0400 Subject: [PATCH 13/24] fix(discord-timer.c): add a hard limit on the number of timers that can run per cycle --- src/discord-timer.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/discord-timer.c b/src/discord-timer.c index fa736f0fd..768de5978 100644 --- a/src/discord-timer.c +++ b/src/discord-timer.c @@ -112,8 +112,9 @@ discord_timers_run(struct discord *client, struct discord_timers *timers) int64_t now = (int64_t)discord_timestamp_us(client); struct discord_timer timer; timers->active.timer = &timer; - for (int64_t trigger; - (timer.id = priority_queue_peek(timers->q, &trigger, &timer));) + for (int64_t trigger, max_iterations = 10000; + (timer.id = priority_queue_peek(timers->q, &trigger, &timer)) + && max_iterations > 0; max_iterations--) { if (trigger > now || trigger == -1) break; From d1b932569794319a9c1fac64cf3161679ab05582 Mon Sep 17 00:00:00 2001 From: Anotra Date: Sat, 16 Apr 2022 07:29:01 -0400 Subject: [PATCH 14/24] fix(discord-timer.c): limit amount of time discord_timers_run will be allowed to use --- src/discord-timer.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/discord-timer.c b/src/discord-timer.c index 768de5978..c6d25e016 100644 --- a/src/discord-timer.c +++ b/src/discord-timer.c @@ -110,12 +110,24 @@ void discord_timers_run(struct discord *client, struct discord_timers *timers) { int64_t now = (int64_t)discord_timestamp_us(client); + const int64_t start_time = now; + struct discord_timer timer; timers->active.timer = &timer; - for (int64_t trigger, max_iterations = 10000; + + for (int64_t trigger, max_iterations = 100000; (timer.id = priority_queue_peek(timers->q, &trigger, &timer)) && max_iterations > 0; max_iterations--) { + //update now timestamp every so often + if ((max_iterations & 0x1F) == 0) { + now = (int64_t)discord_timestamp_us(client); + //break if we've spent too much time running timers + if (now - start_time > 3000) + break; + } + + //no timers to run if (trigger > now || trigger == -1) break; if (~timer.flags & DISCORD_TIMER_CANCELED) From 1d672adcb77441ff51185011e4ea525fb1a20bb2 Mon Sep 17 00:00:00 2001 From: Anotra Date: Sat, 16 Apr 2022 10:40:56 -0400 Subject: [PATCH 15/24] feat(cog-utils): implement cog_sleep_us --- cog-utils/cog-utils.c | 34 ++++++++++++++++++++++++++++++++++ cog-utils/cog-utils.h | 13 +++++++++++-- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/cog-utils/cog-utils.c b/cog-utils/cog-utils.c index 2c0ce902a..7c8db3bfe 100644 --- a/cog-utils/cog-utils.c +++ b/cog-utils/cog-utils.c @@ -215,6 +215,40 @@ cog_sleep_ms(const long tms) return ret; } +int +cog_sleep_us(const long tms) +{ + int ret; + +#if _POSIX_C_SOURCE >= 199309L + struct timespec ts; + + if (tms < 0) { + errno = EINVAL; + return -1; + } + + ts.tv_sec = tms / 1000000; + ts.tv_nsec = (tms % 1000000) * 1000; + + do { + ret = nanosleep(&ts, &ts); + } while (ret && errno == EINTR); +#else + struct timeval timeout; + long _tms = tms; + + timeout.tv_sec = _tms / 1000000L; + _tms = tms % 1000000L; + timeout.tv_usec = (int)_tms; + select(0, NULL, NULL, NULL, &timeout); + + ret = 0; +#endif + + return ret; +} + /* returns current timestamp in milliseconds */ uint64_t cog_timestamp_ms(void) diff --git a/cog-utils/cog-utils.h b/cog-utils/cog-utils.h index 0eed6f06a..5ddc84c0a 100644 --- a/cog-utils/cog-utils.h +++ b/cog-utils/cog-utils.h @@ -141,13 +141,22 @@ size_t cog_strndup(const char src[], size_t len, char **p_dest); size_t cog_asprintf(char **strp, const char fmt[], ...); /** - * @brief Sleep for milliseconds amount + * @brief Sleep for amount of milliseconds * - * @param tms milliseconds amount to sleep for + * @param tms amount of milliseconds to sleep for * @return 0 on success, -1 on error with an `errno` set to indicate the error */ int cog_sleep_ms(const long tms); + +/** + * @brief Sleep for amount of microseconds + * + * @param tms amount of microseconds to sleep for + * @return 0 on success, -1 on error with an `errno` set to indicate the error + */ +int cog_sleep_us(const long tms); + /** * @brief Get the current timestamp in milliseconds * From ad52d80361f72c9b21cf4f048935b4e5d47d9dea Mon Sep 17 00:00:00 2001 From: Anotra Date: Sat, 16 Apr 2022 11:13:47 -0400 Subject: [PATCH 16/24] chore(discord-client.c): integrate cog_sleep_us into event loop, and reorder calls --- src/discord-client.c | 67 ++++++++++++++++++++++++++------------------ 1 file changed, 40 insertions(+), 27 deletions(-) diff --git a/src/discord-client.c b/src/discord-client.c index 587cd95cb..83aed890a 100644 --- a/src/discord-client.c +++ b/src/discord-client.c @@ -339,11 +339,34 @@ discord_set_on_ready(struct discord *client, discord_ev_idle callback) client->gw.cmds.cbs.on_ready = callback; } +static inline int64_t +discord_timer_get_next_trigger(struct discord_timers *const timers[], size_t n, + int64_t now, int64_t max_time) +{ + if (max_time == 0) return 0; + + for (unsigned i = 0; i < n; i++) { + int64_t trigger; + if (priority_queue_peek(timers[i]->q, &trigger, NULL)) { + if (trigger < 0) + continue; + + if (trigger <= now) + max_time = 0; + else if (max_time > trigger - now) + max_time = trigger - now; + } + } + return max_time; +} + CCORDcode discord_run(struct discord *client) { int64_t next_run, now; CCORDcode code; + struct discord_timers *const timers[] = + { &client->timers.internal, &client->timers.user }; while (1) { if (CCORD_OK != (code = discord_gateway_start(&client->gw))) break; @@ -354,50 +377,40 @@ discord_run(struct discord *client) now = (int64_t)discord_timestamp_us(client); - struct discord_timers *const timers[] = - { &client->timers.internal, &client->timers.user }; - if (!client->on_idle) { - poll_time = now < next_run ? (int)((next_run - now)) : 0; - - for (unsigned i = 0; i < sizeof timers / sizeof *timers; i++) { - int64_t trigger; - if (priority_queue_peek(timers[i]->q, &trigger, NULL)) { - if (trigger < 0) - continue; - if (trigger <= now) - poll_time = 0; - else if (poll_time > trigger - now) - poll_time = trigger - now; - } - } - - if (poll_time > 0) { - if (poll_time < 1000) poll_time = 1000; // FIXME: with below - - // const int64_t sleep_time = poll_time % 1000; - // TODO: if (sleep_time) cog_sleep_us(sleep_time); - } + poll_time = discord_timer_get_next_trigger( + timers, sizeof timers / sizeof *timers, now, + now < next_run ? ((next_run - now)) : 0); } poll_result = io_poller_poll(client->io_poller, (int)(poll_time / 1000)); + + now = (int64_t)discord_timestamp_us(client); + if (-1 == poll_result) { /* TODO: handle poll error here */ } else if (0 == poll_result) { if (ccord_has_sigint != 0) discord_shutdown(client); - if (client->on_idle) client->on_idle(client); + if (client->on_idle) { + client->on_idle(client); + } + else { + poll_time = discord_timer_get_next_trigger( + timers, sizeof timers / sizeof *timers, now, 999); + if (poll_time) cog_sleep_us(poll_time); + } } if (client->on_cycle) client->on_cycle(client); - if (CCORD_OK != (code = io_poller_perform(client->io_poller))) - break; - for (unsigned i = 0; i < sizeof timers / sizeof *timers; i++) discord_timers_run(client, timers[i]); + if (CCORD_OK != (code = io_poller_perform(client->io_poller))) + break; + if (next_run <= now) { if (CCORD_OK != (code = discord_gateway_perform(&client->gw))) break; From 628ed543b0b3707309e329f7a43136692a47a554 Mon Sep 17 00:00:00 2001 From: Anotra Date: Sat, 16 Apr 2022 11:33:43 -0400 Subject: [PATCH 17/24] chore: apply clang formatting --- examples/timers.c | 33 +++---- include/discord-internal.h | 34 +++---- include/discord.h | 59 ++++++------ src/discord-client.c | 44 ++++----- src/discord-timer.c | 182 ++++++++++++++++++------------------- 5 files changed, 178 insertions(+), 174 deletions(-) diff --git a/examples/timers.c b/examples/timers.c index 907e3ace1..c1c25d6f8 100644 --- a/examples/timers.c +++ b/examples/timers.c @@ -9,9 +9,9 @@ static void print_timer_info(struct discord_timer *timer) { printf("Timer id:%u flags:%i " - "delay:%"PRIi64" interval:%"PRIi64" repeat:%"PRIi64"\n", - timer->id, timer->flags, - timer->delay, timer->interval, timer->repeat); + "delay:%" PRIi64 " interval:%" PRIi64 " repeat:%" PRIi64 "\n", + timer->id, timer->flags, timer->delay, timer->interval, + timer->repeat); } static void @@ -19,13 +19,15 @@ one_shot_timer_cb(struct discord *client, struct discord_timer *timer) { print_timer_info(timer); if (~timer->flags & DISCORD_TIMER_CANCELED) { - //if timer is not canceled + // if timer is not canceled puts(timer->data); - //timers can be updated in the callback (see below) - //timer->interval += 100; - //timer->repeat = 1; - //return; //skip free(timer->data); + // timers can be updated in the callback (see below) + if (0) { + timer->interval += 100; + timer->repeat = 1; + return; // skip free(timer->data); + } } else { puts("ONE SHOT TIMER CANCELED"); @@ -41,7 +43,7 @@ repeating_timer_cb(struct discord *client, struct discord_timer *timer) printf("TIMER WITH ID %u CANCELED\n", timer->id); return; } - printf("SHUTTING DOWN IN %"PRIi64" SECONDS\n", timer->repeat); + printf("SHUTTING DOWN IN %" PRIi64 " SECONDS\n", timer->repeat); if (!timer->repeat) { discord_shutdown(client); } @@ -54,23 +56,22 @@ main(int argc, char *argv[]) ccord_global_init(); struct discord *client = discord_config_init(config_file); - discord_timer(client, one_shot_timer_cb, strdup("Hello World"), 1000); - discord_timer(client, one_shot_timer_cb, strdup("Hello World!!!!!!"), 5000); + discord_timer(client, one_shot_timer_cb, strdup("Hello World!!!!!!"), + 5000); - //start a one shot timer that will never get a chance to run + // start a one shot timer that will never get a chance to run discord_timer(client, one_shot_timer_cb, strdup("Hello World"), 15000); discord_timer_interval(client, repeating_timer_cb, NULL, 0, 1000, 10); - //start 3 timers that will never get a chance to run - for (int i=0; i<3; i++) + // start 3 timers that will never get a chance to run + for (int i = 0; i < 3; i++) discord_timer(client, repeating_timer_cb, NULL, 20 * 1000); - discord_run(client); - //discord_cleanup will cancel all timers that are still active + // discord_cleanup will cancel all timers that are still active discord_cleanup(client); ccord_global_cleanup(); } diff --git a/include/discord-internal.h b/include/discord-internal.h index def97ca63..4f7022a67 100644 --- a/include/discord-internal.h +++ b/include/discord-internal.h @@ -190,7 +190,7 @@ struct discord_adapter { } * global; /** idle request handles */ - QUEUE(struct discord_context) *idleq; + QUEUE(struct discord_context) * idleq; /** max amount of retries before a failed request gives up */ int retry_limit; @@ -690,21 +690,21 @@ struct discord_timers { /** * @brief prepare timers for usage - * + * * @param client the client created with discord_init() */ void discord_timers_init(struct discord *client); /** * @brief cleanup timers and call cancel any running ones - * + * * @param client the client created with discord_init() */ void discord_timers_cleanup(struct discord *client); /** * @brief run all timers that are due - * + * * @param client the client created with discord_init() * @param timers the timers to run */ @@ -712,40 +712,40 @@ void discord_timers_run(struct discord *client, struct discord_timers *timers); /** * @brief modifies or creates a timer - * + * * @param client the client created with discord_init() * @param timers the timer group to perform this operation on * @param timer the timer that should be modified * @return the id of the timer */ -unsigned _discord_timer_ctl( - struct discord *client, - struct discord_timers *timers, - struct discord_timer *timer); +unsigned _discord_timer_ctl(struct discord *client, + struct discord_timers *timers, + struct discord_timer *timer); /** * @brief modifies or creates a timer - * + * * @param client the client created with discord_init() * @param timer the timer that should be modified * @return unsigned the id of the timer */ -unsigned discord_internal_timer_ctl( - struct discord *client, - struct discord_timer *timer); +unsigned discord_internal_timer_ctl(struct discord *client, + struct discord_timer *timer); /** * @brief creates a one shot timer that automatically * deletes itself upon completion - * + * * @param client the client created with discord_init() * @param cb the callback that should be called when timer triggers * @param data user data * @param delay delay before timer should start in milliseconds - * @return unsigned + * @return unsigned */ -unsigned discord_internal_timer(struct discord *client, discord_ev_timer cb, - void *data, int64_t delay); +unsigned discord_internal_timer(struct discord *client, + discord_ev_timer cb, + void *data, + int64_t delay); /** @} DiscordInternalTimer */ /** diff --git a/include/discord.h b/include/discord.h index 7ce45dfb1..1fd1dd7fa 100644 --- a/include/discord.h +++ b/include/discord.h @@ -276,9 +276,9 @@ struct logconf *discord_get_logconf(struct discord *client); /** * @brief get the io_poller used by the discord client - * + * * @param client the client created with discord_init() - * @return struct io_poller* + * @return struct io_poller* */ struct io_poller *discord_get_io_poller(struct discord *client); @@ -291,27 +291,26 @@ struct discord_timer; /**/ /** @brief callback to be used with struct discord_timer */ -typedef void (*discord_ev_timer) - (struct discord *client, struct discord_timer *ev); +typedef void (*discord_ev_timer)(struct discord *client, + struct discord_timer *ev); /** @brief flags used to change behaviour of timer */ enum discord_timer_flags { /** use milliseconds for interval and start_time */ - DISCORD_TIMER_MILLISECONDS = 0, + DISCORD_TIMER_MILLISECONDS = 0, /** use microseconds for interval and start_time */ - DISCORD_TIMER_MICROSECONDS = 1 << 0, + DISCORD_TIMER_MICROSECONDS = 1 << 0, /** whether or not timer is marked for deletion */ - DISCORD_TIMER_DELETE = 1 << 1, + DISCORD_TIMER_DELETE = 1 << 1, /** automatically delete a timer once its repeat counter runs out */ - DISCORD_TIMER_DELETE_AUTO = 1 << 2, + DISCORD_TIMER_DELETE_AUTO = 1 << 2, /** timer has been canceled. user should cleanup only */ - DISCORD_TIMER_CANCELED = 1 << 3, + DISCORD_TIMER_CANCELED = 1 << 3, - /** used in discord_timer_ctl to get the timer's data */ - DISCORD_TIMER_GET = 1 << 5, + DISCORD_TIMER_GET = 1 << 5, /** timer should run using a fixed interval based on start time */ - DISCORD_TIMER_INTERVAL_FIXED = 1 << 6, + DISCORD_TIMER_INTERVAL_FIXED = 1 << 6, }; /** @brief struct used for modifying, and getting info about a timer */ @@ -334,37 +333,40 @@ struct discord_timer { /** * @brief modifies or creates a timer - * + * * @param client the client created with discord_init() * @param timer the timer that should be modified * @return the id of the timer */ -unsigned discord_timer_ctl(struct discord *client, struct discord_timer *timer); +unsigned discord_timer_ctl(struct discord *client, + struct discord_timer *timer); /** * @brief creates a one shot timer that automatically * deletes itself upon completion - * + * * @param client the client created with discord_init() * @param cb the callback that should be called when timer triggers * @param data user data * @param delay delay before timer should start in milliseconds - * @return the id of the timer + * @return the id of the timer */ -unsigned discord_timer(struct discord *client, discord_ev_timer cb, - void *data, int64_t delay); +unsigned discord_timer(struct discord *client, + discord_ev_timer cb, + void *data, + int64_t delay); /** * @brief creates a repeating timer that automatically * deletes itself upon completion - * + * * @param client the client created with discord_init() * @param cb the callback that should be called when timer triggers * @param data user data * @param delay delay before timer should start in milliseconds * @param interval interval between runs. (-1 == disable repeat) * @param repeat repetitions (-1 == infinity) - * @return the id of the timer + * @return the id of the timer */ unsigned discord_timer_interval(struct discord *client, discord_ev_timer cb, @@ -375,18 +377,19 @@ unsigned discord_timer_interval(struct discord *client, /** * @brief get the data associated with the timer - * + * * @param client the client created with discord_init() * @param id id of the timer * @param timer where to copy the timer data to * @return true on success */ -bool discord_timer_get(struct discord *client, unsigned id, +bool discord_timer_get(struct discord *client, + unsigned id, struct discord_timer *timer); /** * @brief starts a timer - * + * * @param client the client created with discord_init() * @param id id of the timer * @return true on success @@ -395,7 +398,7 @@ bool discord_timer_start(struct discord *client, unsigned id); /** * @brief stops a timer - * + * * @param client the client created with discord_init() * @param id id of the timer * @return true on success @@ -405,7 +408,7 @@ bool discord_timer_stop(struct discord *client, unsigned id); /** * @brief cancels a timer, * this will delete the timer if DISCORD_TIMER_DELETE_AUTO is enabled - * + * * @param client the client created with discord_init() * @param id id of the timer * @return true on success @@ -414,7 +417,7 @@ bool discord_timer_cancel(struct discord *client, unsigned id); /** * @brief deletes a timer - * + * * @param client the client created with discord_init() * @param id id of the timer * @return true on success @@ -423,7 +426,7 @@ bool discord_timer_delete(struct discord *client, unsigned id); /** * @brief cancels, and deletes a timer - * + * * @param client the client created with discord_init() * @param id id of the timer * @return true on success @@ -432,7 +435,7 @@ bool discord_timer_cancel_and_delete(struct discord *client, unsigned id); /** @example timers.c * Demonstrates the Timer API for callback scheduling */ - + /** @} DiscordTimer */ /** @} Discord */ diff --git a/src/discord-client.c b/src/discord-client.c index 83aed890a..79996e650 100644 --- a/src/discord-client.c +++ b/src/discord-client.c @@ -288,9 +288,9 @@ discord_set_event_scheduler(struct discord *client, client->gw.cmds.scheduler = callback; } - static void -discord_wake_timer_cb(struct discord *client, struct discord_timer *timer) { +discord_wake_timer_cb(struct discord *client, struct discord_timer *timer) +{ if (~timer->flags & DISCORD_TIMER_CANCELED && client->wakeup_timer.cb) client->wakeup_timer.cb(client); } @@ -298,12 +298,12 @@ discord_wake_timer_cb(struct discord *client, struct discord_timer *timer) { void discord_set_next_wakeup(struct discord *client, int64_t delay) { - unsigned id = discord_internal_timer_ctl(client, - &(struct discord_timer) { - .id = client->wakeup_timer.id, - .cb = discord_wake_timer_cb, - .delay = delay, - }); + unsigned id = + discord_internal_timer_ctl(client, &(struct discord_timer){ + .id = client->wakeup_timer.id, + .cb = discord_wake_timer_cb, + .delay = delay, + }); client->wakeup_timer.id = id; } @@ -312,12 +312,11 @@ discord_set_on_wakeup(struct discord *client, discord_ev_idle callback) { client->wakeup_timer.cb = callback; if (client->wakeup_timer.id) { - discord_internal_timer_ctl(client, - &(struct discord_timer) { - .id = client->wakeup_timer.id, - .cb = discord_wake_timer_cb, - .delay = -1, - }); + discord_internal_timer_ctl(client, &(struct discord_timer){ + .id = client->wakeup_timer.id, + .cb = discord_wake_timer_cb, + .delay = -1, + }); } } @@ -340,16 +339,17 @@ discord_set_on_ready(struct discord *client, discord_ev_idle callback) } static inline int64_t -discord_timer_get_next_trigger(struct discord_timers *const timers[], size_t n, - int64_t now, int64_t max_time) +discord_timer_get_next_trigger(struct discord_timers *const timers[], + size_t n, + int64_t now, + int64_t max_time) { if (max_time == 0) return 0; for (unsigned i = 0; i < n; i++) { int64_t trigger; if (priority_queue_peek(timers[i]->q, &trigger, NULL)) { - if (trigger < 0) - continue; + if (trigger < 0) continue; if (trigger <= now) max_time = 0; @@ -365,8 +365,8 @@ discord_run(struct discord *client) { int64_t next_run, now; CCORDcode code; - struct discord_timers *const timers[] = - { &client->timers.internal, &client->timers.user }; + struct discord_timers *const timers[] = { &client->timers.internal, + &client->timers.user }; while (1) { if (CCORD_OK != (code = discord_gateway_start(&client->gw))) break; @@ -383,8 +383,8 @@ discord_run(struct discord *client) now < next_run ? ((next_run - now)) : 0); } - poll_result = io_poller_poll(client->io_poller, - (int)(poll_time / 1000)); + poll_result = + io_poller_poll(client->io_poller, (int)(poll_time / 1000)); now = (int64_t)discord_timestamp_us(client); diff --git a/src/discord-timer.c b/src/discord-timer.c index c6d25e016..e3c5fd026 100644 --- a/src/discord-timer.c +++ b/src/discord-timer.c @@ -2,34 +2,29 @@ #include "discord.h" #include "discord-internal.h" -#define DISCORD_TIMER_ALLOWED_FLAGS \ - ( DISCORD_TIMER_MILLISECONDS \ - | DISCORD_TIMER_MICROSECONDS \ - | DISCORD_TIMER_DELETE \ - | DISCORD_TIMER_DELETE_AUTO \ - | DISCORD_TIMER_INTERVAL_FIXED ) +#define DISCORD_TIMER_ALLOWED_FLAGS \ + (DISCORD_TIMER_MILLISECONDS | DISCORD_TIMER_MICROSECONDS \ + | DISCORD_TIMER_DELETE | DISCORD_TIMER_DELETE_AUTO \ + | DISCORD_TIMER_INTERVAL_FIXED) static int cmp_timers(const void *a, const void *b) { const int64_t l = *(int64_t *)a; const int64_t r = *(int64_t *)b; - if (l == r || (l < 0 && r < 0)) - return 0; + if (l == r || (l < 0 && r < 0)) return 0; if (l < 0) return 1; if (r < 0) return -1; return l > r ? 1 : -1; } void -discord_timers_init(struct discord *client) +discord_timers_init(struct discord *client) { - client->timers.internal.q = - priority_queue_create(sizeof(int64_t), sizeof(struct discord_timer), - cmp_timers, 0); - client->timers.user.q = - priority_queue_create(sizeof(int64_t), sizeof(struct discord_timer), - cmp_timers, 0); + client->timers.internal.q = priority_queue_create( + sizeof(int64_t), sizeof(struct discord_timer), cmp_timers, 0); + client->timers.user.q = priority_queue_create( + sizeof(int64_t), sizeof(struct discord_timer), cmp_timers, 0); } static void @@ -37,8 +32,8 @@ discord_timers_cancel_all(struct discord *client, priority_queue *q) { struct discord_timer timer; while ((timer.id = priority_queue_pop(q, NULL, &timer))) { - timer.flags |= DISCORD_TIMER_CANCELED; - if (timer.cb) timer.cb(client, &timer); + timer.flags |= DISCORD_TIMER_CANCELED; + if (timer.cb) timer.cb(client, &timer); } } @@ -55,41 +50,38 @@ discord_timers_cleanup(struct discord *client) } unsigned -_discord_timer_ctl( - struct discord *client, - struct discord_timers *timers, - struct discord_timer *timer_ret) +_discord_timer_ctl(struct discord *client, + struct discord_timers *timers, + struct discord_timer *timer_ret) { struct discord_timer timer; memcpy(&timer, timer_ret, sizeof timer); int64_t key = -1; if (timer.id) { - if (!priority_queue_get(timers->q, timer.id, &key, NULL)) - return 0; + if (!priority_queue_get(timers->q, timer.id, &key, NULL)) return 0; if (timer.flags & DISCORD_TIMER_GET) { priority_queue_get(timers->q, timer.id, NULL, timer_ret); - if (timer.flags == DISCORD_TIMER_GET) - return timer.id; + if (timer.flags == DISCORD_TIMER_GET) return timer.id; } } - + int64_t now = -1; if (timer.delay >= 0) { now = (int64_t)discord_timestamp_us(client) - + - ((timer.flags & DISCORD_TIMER_MICROSECONDS) - ? timer.delay : timer.delay * 1000); + + ((timer.flags & DISCORD_TIMER_MICROSECONDS) + ? timer.delay + : timer.delay * 1000); } - if (timer.flags & (DISCORD_TIMER_DELETE | DISCORD_TIMER_CANCELED)) - now = 0; - + if (timer.flags & (DISCORD_TIMER_DELETE | DISCORD_TIMER_CANCELED)) now = 0; + timer.flags &= (DISCORD_TIMER_ALLOWED_FLAGS | DISCORD_TIMER_CANCELED); if (!timer.id) { return priority_queue_push(timers->q, &now, &timer); - } else { + } + else { if (timers->active.timer && timers->active.timer->id == timer.id) timers->active.skip_update_phase = true; if (priority_queue_update(timers->q, timer.id, &now, &timer)) @@ -98,12 +90,12 @@ _discord_timer_ctl( } } -#define TIMER_TRY_DELETE \ - do { \ - if (timer.flags & DISCORD_TIMER_DELETE) { \ - priority_queue_del(timers->q, timer.id); \ - continue; \ - } \ +#define TIMER_TRY_DELETE \ + do { \ + if (timer.flags & DISCORD_TIMER_DELETE) { \ + priority_queue_del(timers->q, timer.id); \ + continue; \ + } \ } while (0) void @@ -115,31 +107,29 @@ discord_timers_run(struct discord *client, struct discord_timers *timers) struct discord_timer timer; timers->active.timer = &timer; - for (int64_t trigger, max_iterations = 100000; - (timer.id = priority_queue_peek(timers->q, &trigger, &timer)) - && max_iterations > 0; max_iterations--) + for (int64_t trigger, max_iterations = 100000; + (timer.id = priority_queue_peek(timers->q, &trigger, &timer)) + && max_iterations > 0; + max_iterations--) { - //update now timestamp every so often + // update now timestamp every so often if ((max_iterations & 0x1F) == 0) { now = (int64_t)discord_timestamp_us(client); - //break if we've spent too much time running timers - if (now - start_time > 3000) - break; + // break if we've spent too much time running timers + if (now - start_time > 3000) break; } - //no timers to run + // no timers to run if (trigger > now || trigger == -1) break; - if (~timer.flags & DISCORD_TIMER_CANCELED) - TIMER_TRY_DELETE; + if (~timer.flags & DISCORD_TIMER_CANCELED) TIMER_TRY_DELETE; if (timer.repeat > 0 && ~timer.flags & DISCORD_TIMER_CANCELED) timer.repeat--; timers->active.skip_update_phase = false; if (timer.cb) timer.cb(client, &timer); - if (timers->active.skip_update_phase) - continue; + if (timers->active.skip_update_phase) continue; if ((timer.repeat == 0 || timer.flags & DISCORD_TIMER_CANCELED) && (timer.flags & DISCORD_TIMER_DELETE_AUTO)) @@ -148,17 +138,17 @@ discord_timers_run(struct discord *client, struct discord_timers *timers) } TIMER_TRY_DELETE; - + int64_t next = -1; if (timer.repeat != 0 && timer.delay != -1 && ~timer.flags & DISCORD_TIMER_CANCELED) { if (timer.interval >= 0) { - next = ((timer.flags & DISCORD_TIMER_INTERVAL_FIXED) - ? trigger : now) - + - ((timer.flags & DISCORD_TIMER_MICROSECONDS) - ? timer.interval : timer.interval * 1000); + next = ((timer.flags & DISCORD_TIMER_INTERVAL_FIXED) ? trigger + : now) + + ((timer.flags & DISCORD_TIMER_MICROSECONDS) + ? timer.interval + : timer.interval * 1000); } } timer.flags &= DISCORD_TIMER_ALLOWED_FLAGS; @@ -173,57 +163,68 @@ discord_timer_ctl(struct discord *client, struct discord_timer *timer) return _discord_timer_ctl(client, &client->timers.user, timer); } -unsigned -discord_internal_timer_ctl(struct discord *client, - struct discord_timer *timer) +unsigned +discord_internal_timer_ctl(struct discord *client, struct discord_timer *timer) { return _discord_timer_ctl(client, &client->timers.internal, timer); } static unsigned -_discord_timer(struct discord *client, struct discord_timers *timers, - discord_ev_timer cb, void *data, int64_t delay) +_discord_timer(struct discord *client, + struct discord_timers *timers, + discord_ev_timer cb, + void *data, + int64_t delay) { struct discord_timer timer = { - .cb = cb, - .data = data, - .delay = delay, - .flags = DISCORD_TIMER_DELETE_AUTO, + .cb = cb, + .data = data, + .delay = delay, + .flags = DISCORD_TIMER_DELETE_AUTO, }; return _discord_timer_ctl(client, timers, &timer); } unsigned -discord_timer_interval(struct discord *client, discord_ev_timer cb, void *data, - int64_t delay, int64_t interval, int64_t repeat) +discord_timer_interval(struct discord *client, + discord_ev_timer cb, + void *data, + int64_t delay, + int64_t interval, + int64_t repeat) { struct discord_timer timer = { - .cb = cb, - .data = data, - .delay = delay, - .interval = interval, - .repeat = repeat, - .flags = DISCORD_TIMER_DELETE_AUTO, + .cb = cb, + .data = data, + .delay = delay, + .interval = interval, + .repeat = repeat, + .flags = DISCORD_TIMER_DELETE_AUTO, }; return discord_timer_ctl(client, &timer); } unsigned -discord_timer(struct discord *client, discord_ev_timer cb, - void *data, int64_t delay) +discord_timer(struct discord *client, + discord_ev_timer cb, + void *data, + int64_t delay) { return _discord_timer(client, &client->timers.user, cb, data, delay); } unsigned -discord_internal_timer(struct discord *client, discord_ev_timer cb, - void *data, int64_t delay) +discord_internal_timer(struct discord *client, + discord_ev_timer cb, + void *data, + int64_t delay) { return _discord_timer(client, &client->timers.internal, cb, data, delay); } bool -discord_timer_get(struct discord *client, unsigned id, +discord_timer_get(struct discord *client, + unsigned id, struct discord_timer *timer) { if (!id) return 0; @@ -234,8 +235,7 @@ static void discord_timer_disable_update_if_active(struct discord_timers *timers, unsigned id) { - if (!timers->active.timer) - return; + if (!timers->active.timer) return; if (timers->active.timer->id == id) timers->active.skip_update_phase = true; } @@ -246,8 +246,7 @@ discord_timer_start(struct discord *client, unsigned id) struct discord_timer timer; discord_timer_disable_update_if_active(&client->timers.user, id); if (discord_timer_get(client, id, &timer)) { - if (timer.delay < 0) - timer.delay = 0; + if (timer.delay < 0) timer.delay = 0; return discord_timer_ctl(client, &timer); } return false; @@ -260,23 +259,24 @@ discord_timer_stop(struct discord *client, unsigned id) discord_timer_disable_update_if_active(&client->timers.user, id); if (discord_timer_get(client, id, &timer)) { int64_t disabled = -1; - return priority_queue_update(client->timers.user.q, - id, &disabled, &timer); + return priority_queue_update(client->timers.user.q, id, &disabled, + &timer); } return false; } static bool -discord_timer_add_flags(struct discord *client, unsigned id, - enum discord_timer_flags flags) +discord_timer_add_flags(struct discord *client, + unsigned id, + enum discord_timer_flags flags) { struct discord_timer timer; discord_timer_disable_update_if_active(&client->timers.user, id); if (discord_timer_get(client, id, &timer)) { timer.flags |= flags; int64_t run_now = 0; - return priority_queue_update(client->timers.user.q, - id, &run_now, &timer); + return priority_queue_update(client->timers.user.q, id, &run_now, + &timer); } return false; } @@ -296,6 +296,6 @@ discord_timer_delete(struct discord *client, unsigned id) bool discord_timer_cancel_and_delete(struct discord *client, unsigned id) { - return discord_timer_add_flags(client, id, DISCORD_TIMER_DELETE - | DISCORD_TIMER_CANCELED); + return discord_timer_add_flags( + client, id, DISCORD_TIMER_DELETE | DISCORD_TIMER_CANCELED); } From fa2b4a7391b62030970b0493f5ffb3d8d58a5938 Mon Sep 17 00:00:00 2001 From: Anotra Date: Sat, 16 Apr 2022 14:40:47 -0400 Subject: [PATCH 18/24] refactor(discord-client.c): add BREAK_ON_FAIL macro to simplify event loop --- src/discord-client.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/discord-client.c b/src/discord-client.c index 79996e650..8446f6450 100644 --- a/src/discord-client.c +++ b/src/discord-client.c @@ -360,6 +360,11 @@ discord_timer_get_next_trigger(struct discord_timers *const timers[], return max_time; } +#define BREAK_ON_FAIL(function) \ + do { \ + if (CCORD_OK != (code = function)) break; \ + } while (0) + CCORDcode discord_run(struct discord *client) { @@ -369,7 +374,7 @@ discord_run(struct discord *client) &client->timers.user }; while (1) { - if (CCORD_OK != (code = discord_gateway_start(&client->gw))) break; + BREAK_ON_FAIL(discord_gateway_start(&client->gw)); next_run = (int64_t)discord_timestamp_us(client); while (1) { @@ -408,15 +413,11 @@ discord_run(struct discord *client) for (unsigned i = 0; i < sizeof timers / sizeof *timers; i++) discord_timers_run(client, timers[i]); - if (CCORD_OK != (code = io_poller_perform(client->io_poller))) - break; + BREAK_ON_FAIL(io_poller_perform(client->io_poller)); if (next_run <= now) { - if (CCORD_OK != (code = discord_gateway_perform(&client->gw))) - break; - if (CCORD_OK - != (code = discord_adapter_perform(&client->adapter))) - break; + BREAK_ON_FAIL(discord_gateway_perform(&client->gw)); + BREAK_ON_FAIL(discord_adapter_perform(&client->adapter)); /* enforce a min 1 sec delay between runs */ next_run = now + 1000000; @@ -433,6 +434,8 @@ discord_run(struct discord *client) return code; } +#undef BREAK_ON_FAIL + void discord_shutdown(struct discord *client) { From 9fb463a906dbbb5f63d8e34062f004362daff524 Mon Sep 17 00:00:00 2001 From: Anotra Date: Sat, 16 Apr 2022 14:49:38 -0400 Subject: [PATCH 19/24] chore(discord-client.c): move discord_set_on_ready to its related functions --- src/discord-client.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/discord-client.c b/src/discord-client.c index 8446f6450..9a29f8b73 100644 --- a/src/discord-client.c +++ b/src/discord-client.c @@ -332,12 +332,6 @@ discord_set_on_cycle(struct discord *client, discord_ev_idle callback) client->on_cycle = callback; } -void -discord_set_on_ready(struct discord *client, discord_ev_idle callback) -{ - client->gw.cmds.cbs.on_ready = callback; -} - static inline int64_t discord_timer_get_next_trigger(struct discord_timers *const timers[], size_t n, @@ -449,6 +443,12 @@ discord_reconnect(struct discord *client, bool resume) discord_gateway_reconnect(&client->gw, resume); } +void +discord_set_on_ready(struct discord *client, discord_ev_idle callback) +{ + client->gw.cmds.cbs.on_ready = callback; +} + void discord_set_on_guild_role_create(struct discord *client, discord_ev_guild_role callback) From 25a28a3094e3a218291ca7569b2d6c337634505d Mon Sep 17 00:00:00 2001 From: Anotra Date: Sat, 16 Apr 2022 15:03:31 -0400 Subject: [PATCH 20/24] refactor(discord-client): move event loop to a new file discord-loop.c --- Makefile | 1 + src/discord-client.c | 142 ----------------------------------------- src/discord-loop.c | 148 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 149 insertions(+), 142 deletions(-) create mode 100644 src/discord-loop.c diff --git a/Makefile b/Makefile index f56fb78dd..735d7034a 100644 --- a/Makefile +++ b/Makefile @@ -35,6 +35,7 @@ DISCORD_OBJS = $(SRC_DIR)/concord-once.o \ $(SRC_DIR)/discord-adapter_ratelimit.o \ $(SRC_DIR)/discord-adapter_refcount.o \ $(SRC_DIR)/discord-client.o \ + $(SRC_DIR)/discord-loop.o \ $(SRC_DIR)/discord-gateway.o \ $(SRC_DIR)/discord-timer.o \ $(SRC_DIR)/discord-misc.o \ diff --git a/src/discord-client.c b/src/discord-client.c index 9a29f8b73..f50f6b57c 100644 --- a/src/discord-client.c +++ b/src/discord-client.c @@ -288,148 +288,6 @@ discord_set_event_scheduler(struct discord *client, client->gw.cmds.scheduler = callback; } -static void -discord_wake_timer_cb(struct discord *client, struct discord_timer *timer) -{ - if (~timer->flags & DISCORD_TIMER_CANCELED && client->wakeup_timer.cb) - client->wakeup_timer.cb(client); -} - -void -discord_set_next_wakeup(struct discord *client, int64_t delay) -{ - unsigned id = - discord_internal_timer_ctl(client, &(struct discord_timer){ - .id = client->wakeup_timer.id, - .cb = discord_wake_timer_cb, - .delay = delay, - }); - client->wakeup_timer.id = id; -} - -void -discord_set_on_wakeup(struct discord *client, discord_ev_idle callback) -{ - client->wakeup_timer.cb = callback; - if (client->wakeup_timer.id) { - discord_internal_timer_ctl(client, &(struct discord_timer){ - .id = client->wakeup_timer.id, - .cb = discord_wake_timer_cb, - .delay = -1, - }); - } -} - -void -discord_set_on_idle(struct discord *client, discord_ev_idle callback) -{ - client->on_idle = callback; -} - -void -discord_set_on_cycle(struct discord *client, discord_ev_idle callback) -{ - client->on_cycle = callback; -} - -static inline int64_t -discord_timer_get_next_trigger(struct discord_timers *const timers[], - size_t n, - int64_t now, - int64_t max_time) -{ - if (max_time == 0) return 0; - - for (unsigned i = 0; i < n; i++) { - int64_t trigger; - if (priority_queue_peek(timers[i]->q, &trigger, NULL)) { - if (trigger < 0) continue; - - if (trigger <= now) - max_time = 0; - else if (max_time > trigger - now) - max_time = trigger - now; - } - } - return max_time; -} - -#define BREAK_ON_FAIL(function) \ - do { \ - if (CCORD_OK != (code = function)) break; \ - } while (0) - -CCORDcode -discord_run(struct discord *client) -{ - int64_t next_run, now; - CCORDcode code; - struct discord_timers *const timers[] = { &client->timers.internal, - &client->timers.user }; - - while (1) { - BREAK_ON_FAIL(discord_gateway_start(&client->gw)); - - next_run = (int64_t)discord_timestamp_us(client); - while (1) { - int64_t poll_time = 0, poll_result; - - now = (int64_t)discord_timestamp_us(client); - - if (!client->on_idle) { - poll_time = discord_timer_get_next_trigger( - timers, sizeof timers / sizeof *timers, now, - now < next_run ? ((next_run - now)) : 0); - } - - poll_result = - io_poller_poll(client->io_poller, (int)(poll_time / 1000)); - - now = (int64_t)discord_timestamp_us(client); - - if (-1 == poll_result) { - /* TODO: handle poll error here */ - } - else if (0 == poll_result) { - if (ccord_has_sigint != 0) discord_shutdown(client); - if (client->on_idle) { - client->on_idle(client); - } - else { - poll_time = discord_timer_get_next_trigger( - timers, sizeof timers / sizeof *timers, now, 999); - if (poll_time) cog_sleep_us(poll_time); - } - } - - if (client->on_cycle) client->on_cycle(client); - - for (unsigned i = 0; i < sizeof timers / sizeof *timers; i++) - discord_timers_run(client, timers[i]); - - BREAK_ON_FAIL(io_poller_perform(client->io_poller)); - - if (next_run <= now) { - BREAK_ON_FAIL(discord_gateway_perform(&client->gw)); - BREAK_ON_FAIL(discord_adapter_perform(&client->adapter)); - - /* enforce a min 1 sec delay between runs */ - next_run = now + 1000000; - } - } - - /* stop all pending requests in case of connection shutdown */ - if (true == discord_gateway_end(&client->gw)) { - discord_adapter_stop_all(&client->adapter); - break; - } - } - - return code; -} - -#undef BREAK_ON_FAIL - void discord_shutdown(struct discord *client) { diff --git a/src/discord-loop.c b/src/discord-loop.c new file mode 100644 index 000000000..b25748a62 --- /dev/null +++ b/src/discord-loop.c @@ -0,0 +1,148 @@ +#include +#include + +#include "discord.h" +#include "discord-internal.h" +#include "cog-utils.h" + +static void +discord_wake_timer_cb(struct discord *client, struct discord_timer *timer) +{ + if (~timer->flags & DISCORD_TIMER_CANCELED && client->wakeup_timer.cb) + client->wakeup_timer.cb(client); +} + +void +discord_set_next_wakeup(struct discord *client, int64_t delay) +{ + unsigned id = + discord_internal_timer_ctl(client, &(struct discord_timer){ + .id = client->wakeup_timer.id, + .cb = discord_wake_timer_cb, + .delay = delay, + }); + client->wakeup_timer.id = id; +} + +void +discord_set_on_wakeup(struct discord *client, discord_ev_idle callback) +{ + client->wakeup_timer.cb = callback; + if (client->wakeup_timer.id) { + discord_internal_timer_ctl(client, &(struct discord_timer){ + .id = client->wakeup_timer.id, + .cb = discord_wake_timer_cb, + .delay = -1, + }); + } +} + +void +discord_set_on_idle(struct discord *client, discord_ev_idle callback) +{ + client->on_idle = callback; +} + +void +discord_set_on_cycle(struct discord *client, discord_ev_idle callback) +{ + client->on_cycle = callback; +} + +static inline int64_t +discord_timer_get_next_trigger(struct discord_timers *const timers[], + size_t n, + int64_t now, + int64_t max_time) +{ + if (max_time == 0) return 0; + + for (unsigned i = 0; i < n; i++) { + int64_t trigger; + if (priority_queue_peek(timers[i]->q, &trigger, NULL)) { + if (trigger < 0) continue; + + if (trigger <= now) + max_time = 0; + else if (max_time > trigger - now) + max_time = trigger - now; + } + } + return max_time; +} + +#define BREAK_ON_FAIL(function) \ + do { \ + if (CCORD_OK != (code = function)) break; \ + } while (0) + +CCORDcode +discord_run(struct discord *client) +{ + int64_t next_run, now; + CCORDcode code; + struct discord_timers *const timers[] = { &client->timers.internal, + &client->timers.user }; + + while (1) { + BREAK_ON_FAIL(discord_gateway_start(&client->gw)); + + next_run = (int64_t)discord_timestamp_us(client); + while (1) { + int64_t poll_time = 0, poll_result; + + now = (int64_t)discord_timestamp_us(client); + + if (!client->on_idle) { + poll_time = discord_timer_get_next_trigger( + timers, sizeof timers / sizeof *timers, now, + now < next_run ? ((next_run - now)) : 0); + } + + poll_result = + io_poller_poll(client->io_poller, (int)(poll_time / 1000)); + + now = (int64_t)discord_timestamp_us(client); + + if (-1 == poll_result) { + /* TODO: handle poll error here */ + } + else if (0 == poll_result) { + if (ccord_has_sigint != 0) discord_shutdown(client); + if (client->on_idle) { + client->on_idle(client); + } + else { + poll_time = discord_timer_get_next_trigger( + timers, sizeof timers / sizeof *timers, now, 999); + if (poll_time) cog_sleep_us(poll_time); + } + } + + if (client->on_cycle) client->on_cycle(client); + + for (unsigned i = 0; i < sizeof timers / sizeof *timers; i++) + discord_timers_run(client, timers[i]); + + BREAK_ON_FAIL(io_poller_perform(client->io_poller)); + + if (next_run <= now) { + BREAK_ON_FAIL(discord_gateway_perform(&client->gw)); + BREAK_ON_FAIL(discord_adapter_perform(&client->adapter)); + + /* enforce a min 1 sec delay between runs */ + next_run = now + 1000000; + } + } + + /* stop all pending requests in case of connection shutdown */ + if (true == discord_gateway_end(&client->gw)) { + discord_adapter_stop_all(&client->adapter); + break; + } + } + + return code; +} + +#undef BREAK_ON_FAIL From 3660e13b39a2a48dfccdb507d724ee49b055070f Mon Sep 17 00:00:00 2001 From: Anotra Date: Sat, 16 Apr 2022 15:20:43 -0400 Subject: [PATCH 21/24] refactor(discord-loop.c): poll again after running timers --- src/discord-loop.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/discord-loop.c b/src/discord-loop.c index b25748a62..49db60923 100644 --- a/src/discord-loop.c +++ b/src/discord-loop.c @@ -104,10 +104,7 @@ discord_run(struct discord *client) now = (int64_t)discord_timestamp_us(client); - if (-1 == poll_result) { - /* TODO: handle poll error here */ - } - else if (0 == poll_result) { + if (0 == poll_result) { if (ccord_has_sigint != 0) discord_shutdown(client); if (client->on_idle) { client->on_idle(client); @@ -124,6 +121,13 @@ 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) + poll_result = io_poller_poll(client->io_poller, 0); + + if (-1 == poll_result) { + /* TODO: handle poll error here */ + } + BREAK_ON_FAIL(io_poller_perform(client->io_poller)); if (next_run <= now) { From 59636a2b2c3b1807f98a8faad605e3b98f79e7b0 Mon Sep 17 00:00:00 2001 From: Anotra Date: Sun, 17 Apr 2022 00:22:23 -0400 Subject: [PATCH 22/24] fix(discord-loop.c): fix BREAK_ON_FAIL - how did this happen? --- src/discord-loop.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/discord-loop.c b/src/discord-loop.c index 49db60923..37386076a 100644 --- a/src/discord-loop.c +++ b/src/discord-loop.c @@ -72,9 +72,7 @@ discord_timer_get_next_trigger(struct discord_timers *const timers[], } #define BREAK_ON_FAIL(function) \ - do { \ - if (CCORD_OK != (code = function)) break; \ - } while (0) + if (CCORD_OK != (code = function)) break CCORDcode discord_run(struct discord *client) From ac27693dbb4fe3819b66f20099857c83173ffe04 Mon Sep 17 00:00:00 2001 From: Anotra Date: Sun, 17 Apr 2022 00:55:25 -0400 Subject: [PATCH 23/24] refactor(discord-loop.c): add poll_errno to capture poll errors --- src/discord-loop.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/discord-loop.c b/src/discord-loop.c index 37386076a..3256bdb2b 100644 --- a/src/discord-loop.c +++ b/src/discord-loop.c @@ -72,7 +72,13 @@ discord_timer_get_next_trigger(struct discord_timers *const timers[], } #define BREAK_ON_FAIL(function) \ - if (CCORD_OK != (code = function)) break + if (CCORD_OK != (code = function)) break + +#define CALL_IO_POLLER_POLL(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) @@ -87,7 +93,8 @@ discord_run(struct discord *client) next_run = (int64_t)discord_timestamp_us(client); while (1) { - int64_t poll_time = 0, poll_result; + int64_t poll_time = 0; + int poll_result, poll_errno = 0; now = (int64_t)discord_timestamp_us(client); @@ -97,8 +104,7 @@ discord_run(struct discord *client) now < next_run ? ((next_run - now)) : 0); } - poll_result = - io_poller_poll(client->io_poller, (int)(poll_time / 1000)); + CALL_IO_POLLER_POLL(client->io_poller, poll_time / 1000); now = (int64_t)discord_timestamp_us(client); @@ -120,10 +126,12 @@ discord_run(struct discord *client) discord_timers_run(client, timers[i]); if (poll_result >= 0 && !client->on_idle) - poll_result = io_poller_poll(client->io_poller, 0); + CALL_IO_POLLER_POLL(client->io_poller, 0); if (-1 == poll_result) { /* TODO: handle poll error here */ + // use poll_errno instead of errno + (void)poll_errno; } BREAK_ON_FAIL(io_poller_perform(client->io_poller)); @@ -148,3 +156,4 @@ discord_run(struct discord *client) } #undef BREAK_ON_FAIL +#undef CALL_IO_POLLER_POLL From fc46ecce3a474e3f53cc8c030c7e2ed5f8da892d Mon Sep 17 00:00:00 2001 From: Anotra Date: Sun, 17 Apr 2022 13:56:04 -0400 Subject: [PATCH 24/24] refactor(discord-loop.c): add extra params to macros CALL_IO_POLLER_POLL and BREAK_ON_FAIL --- src/discord-loop.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/discord-loop.c b/src/discord-loop.c index 3256bdb2b..74207b435 100644 --- a/src/discord-loop.c +++ b/src/discord-loop.c @@ -71,10 +71,10 @@ discord_timer_get_next_trigger(struct discord_timers *const timers[], return max_time; } -#define BREAK_ON_FAIL(function) \ +#define BREAK_ON_FAIL(code, function) \ if (CCORD_OK != (code = function)) break -#define CALL_IO_POLLER_POLL(io_poller, delay) \ +#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; \ @@ -89,7 +89,7 @@ discord_run(struct discord *client) &client->timers.user }; while (1) { - BREAK_ON_FAIL(discord_gateway_start(&client->gw)); + BREAK_ON_FAIL(code, discord_gateway_start(&client->gw)); next_run = (int64_t)discord_timestamp_us(client); while (1) { @@ -104,7 +104,8 @@ discord_run(struct discord *client) now < next_run ? ((next_run - now)) : 0); } - CALL_IO_POLLER_POLL(client->io_poller, poll_time / 1000); + CALL_IO_POLLER_POLL(poll_errno, poll_result, client->io_poller, + poll_time / 1000); now = (int64_t)discord_timestamp_us(client); @@ -126,7 +127,8 @@ discord_run(struct discord *client) discord_timers_run(client, timers[i]); if (poll_result >= 0 && !client->on_idle) - CALL_IO_POLLER_POLL(client->io_poller, 0); + CALL_IO_POLLER_POLL(poll_errno, poll_result, client->io_poller, + 0); if (-1 == poll_result) { /* TODO: handle poll error here */ @@ -134,11 +136,11 @@ discord_run(struct discord *client) (void)poll_errno; } - BREAK_ON_FAIL(io_poller_perform(client->io_poller)); + BREAK_ON_FAIL(code, io_poller_perform(client->io_poller)); if (next_run <= now) { - BREAK_ON_FAIL(discord_gateway_perform(&client->gw)); - BREAK_ON_FAIL(discord_adapter_perform(&client->adapter)); + BREAK_ON_FAIL(code, discord_gateway_perform(&client->gw)); + BREAK_ON_FAIL(code, discord_adapter_perform(&client->adapter)); /* enforce a min 1 sec delay between runs */ next_run = now + 1000000;