From cf7d1d8041b5f744e023dd1b2aebc4e039b4c363 Mon Sep 17 00:00:00 2001 From: Sergey Lyubka Date: Sun, 15 May 2022 14:29:34 +0100 Subject: [PATCH] Address #1551 - better timers handling --- docs/README.md | 5 +++-- mongoose.c | 16 ++++++++++++---- mongoose.h | 7 +++---- src/timer.c | 16 ++++++++++++---- src/timer.h | 7 +++---- test/unit_test.c | 35 +++++++++++++++-------------------- 6 files changed, 48 insertions(+), 38 deletions(-) diff --git a/docs/README.md b/docs/README.md index 7334f68c..c4f21460 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1951,11 +1951,12 @@ struct mg_timer { uint64_t period_ms; // Timer period in milliseconds uint64_t expire; // Expiration timestamp in milliseconds unsigned flags; // Possible flags values below -#define MG_TIMER_REPEAT 1 // Call function periodically, otherwise run once +#define MG_TIMER_ONCE 0 // Call function once +#define MG_TIMER_REPEAT 1 // Call function periodically #define MG_TIMER_RUN_NOW 2 // Call immediately when timer is set void (*fn)(void *); // Function to call void *arg; // Function argument - struct mg_timer *next; // Linkage in g_timers list + struct mg_timer *next; // Linkage }; ``` diff --git a/mongoose.c b/mongoose.c index 6927bc55..1288a8f3 100644 --- a/mongoose.c +++ b/mongoose.c @@ -4158,12 +4158,13 @@ size_t mg_vsnprintf(char *buf, size_t len, const char *fmt, va_list ap) { +#define MG_TIMER_CALLED 4 + void mg_timer_init(struct mg_timer **head, struct mg_timer *t, uint64_t ms, unsigned flags, void (*fn)(void *), void *arg) { struct mg_timer tmp = {ms, 0U, 0U, flags, fn, arg, *head}; *t = tmp; *head = t; - if (flags & MG_TIMER_RUN_NOW) fn(arg); } void mg_timer_free(struct mg_timer **head, struct mg_timer *t) { @@ -4178,14 +4179,21 @@ void mg_timer_poll(struct mg_timer **head, uint64_t now_ms) { tmp = t->next; if (t->prev_ms > now_ms) t->expire = 0; // Handle time wrap t->prev_ms = now_ms; - if (t->expire == 0) t->expire = now_ms + t->period_ms; + if (t->expire == 0 && (t->flags & MG_TIMER_RUN_NOW) && + !(t->flags & MG_TIMER_CALLED)) { + // Handle MG_TIMER_NOW only once + } else if (t->expire == 0) { + t->expire = now_ms + t->period_ms; + } if (t->expire > now_ms) continue; - t->fn(t->arg); + if ((t->flags & MG_TIMER_REPEAT) || !(t->flags & MG_TIMER_CALLED)) { + t->fn(t->arg); + } + t->flags |= MG_TIMER_CALLED; // Try to tick timers with the given period as accurate as possible, // even if this polling function is called with some random period. t->expire = now_ms - t->expire > t->period_ms ? now_ms + t->period_ms : t->expire + t->period_ms; - if (!(t->flags & MG_TIMER_REPEAT)) mg_timer_free(head, t); } } diff --git a/mongoose.h b/mongoose.h index 4a3ab101..7b36bd62 100644 --- a/mongoose.h +++ b/mongoose.h @@ -719,15 +719,14 @@ struct mg_timer { uint64_t prev_ms; // Timestamp of a previous poll uint64_t expire; // Expiration timestamp in milliseconds unsigned flags; // Possible flags values below -#define MG_TIMER_REPEAT 1 // Call function periodically, otherwise run once +#define MG_TIMER_ONCE 0 // Call function once +#define MG_TIMER_REPEAT 1 // Call function periodically #define MG_TIMER_RUN_NOW 2 // Call immediately when timer is set void (*fn)(void *); // Function to call void *arg; // Function argument - struct mg_timer *next; // Linkage in g_timers list + struct mg_timer *next; // Linkage }; -extern struct mg_timer *g_timers; // Global list of timers - void mg_timer_init(struct mg_timer **head, struct mg_timer *timer, uint64_t milliseconds, unsigned flags, void (*fn)(void *), void *arg); diff --git a/src/timer.c b/src/timer.c index 474437a1..10567aca 100644 --- a/src/timer.c +++ b/src/timer.c @@ -4,12 +4,13 @@ #include "timer.h" #include "arch.h" +#define MG_TIMER_CALLED 4 + void mg_timer_init(struct mg_timer **head, struct mg_timer *t, uint64_t ms, unsigned flags, void (*fn)(void *), void *arg) { struct mg_timer tmp = {ms, 0U, 0U, flags, fn, arg, *head}; *t = tmp; *head = t; - if (flags & MG_TIMER_RUN_NOW) fn(arg); } void mg_timer_free(struct mg_timer **head, struct mg_timer *t) { @@ -24,13 +25,20 @@ void mg_timer_poll(struct mg_timer **head, uint64_t now_ms) { tmp = t->next; if (t->prev_ms > now_ms) t->expire = 0; // Handle time wrap t->prev_ms = now_ms; - if (t->expire == 0) t->expire = now_ms + t->period_ms; + if (t->expire == 0 && (t->flags & MG_TIMER_RUN_NOW) && + !(t->flags & MG_TIMER_CALLED)) { + // Handle MG_TIMER_NOW only once + } else if (t->expire == 0) { + t->expire = now_ms + t->period_ms; + } if (t->expire > now_ms) continue; - t->fn(t->arg); + if ((t->flags & MG_TIMER_REPEAT) || !(t->flags & MG_TIMER_CALLED)) { + t->fn(t->arg); + } + t->flags |= MG_TIMER_CALLED; // Try to tick timers with the given period as accurate as possible, // even if this polling function is called with some random period. t->expire = now_ms - t->expire > t->period_ms ? now_ms + t->period_ms : t->expire + t->period_ms; - if (!(t->flags & MG_TIMER_REPEAT)) mg_timer_free(head, t); } } diff --git a/src/timer.h b/src/timer.h index a6959a07..a72955fd 100644 --- a/src/timer.h +++ b/src/timer.h @@ -7,15 +7,14 @@ struct mg_timer { uint64_t prev_ms; // Timestamp of a previous poll uint64_t expire; // Expiration timestamp in milliseconds unsigned flags; // Possible flags values below -#define MG_TIMER_REPEAT 1 // Call function periodically, otherwise run once +#define MG_TIMER_ONCE 0 // Call function once +#define MG_TIMER_REPEAT 1 // Call function periodically #define MG_TIMER_RUN_NOW 2 // Call immediately when timer is set void (*fn)(void *); // Function to call void *arg; // Function argument - struct mg_timer *next; // Linkage in g_timers list + struct mg_timer *next; // Linkage }; -extern struct mg_timer *g_timers; // Global list of timers - void mg_timer_init(struct mg_timer **head, struct mg_timer *timer, uint64_t milliseconds, unsigned flags, void (*fn)(void *), void *arg); diff --git a/test/unit_test.c b/test/unit_test.c index 8bb76875..17903fba 100644 --- a/test/unit_test.c +++ b/test/unit_test.c @@ -1171,13 +1171,17 @@ static void test_timer(void) { struct mg_timer t1, t2, t3, *head = NULL; mg_timer_init(&head, &t1, 5, MG_TIMER_REPEAT, f1, &v1); - mg_timer_init(&head, &t2, 15, 0, f1, &v2); + mg_timer_init(&head, &t2, 15, MG_TIMER_ONCE, f1, &v2); mg_timer_init(&head, &t3, 10, MG_TIMER_REPEAT | MG_TIMER_RUN_NOW, f1, &v3); ASSERT(head == &t3); ASSERT(head->next == &t2); mg_timer_poll(&head, 0); + ASSERT(v1 == 0); + ASSERT(v2 == 0); + ASSERT(v3 == 1); + mg_timer_poll(&head, 1); ASSERT(v1 == 0); ASSERT(v2 == 0); @@ -1188,19 +1192,12 @@ static void test_timer(void) { ASSERT(v2 == 0); ASSERT(v3 == 1); - ASSERT(head == &t3); - ASSERT(head->next == &t2); - // Simulate long delay - timers must invalidate expiration times mg_timer_poll(&head, 100); ASSERT(v1 == 2); ASSERT(v2 == 1); ASSERT(v3 == 2); - ASSERT(head == &t3); - ASSERT(head->next == &t1); // t2 should be removed - ASSERT(head->next->next == NULL); - mg_timer_poll(&head, 107); ASSERT(v1 == 3); ASSERT(v2 == 1); @@ -1216,6 +1213,7 @@ static void test_timer(void) { ASSERT(v2 == 1); ASSERT(v3 == 3); + mg_timer_free(&head, &t2); mg_timer_init(&head, &t2, 3, 0, f1, &v2); ASSERT(head == &t2); ASSERT(head->next == &t3); @@ -1238,10 +1236,6 @@ static void test_timer(void) { ASSERT(v2 == 2); ASSERT(v3 == 4); - ASSERT(head == &t3); - ASSERT(head->next == &t1); - ASSERT(head->next->next == NULL); - mg_timer_poll(&head, 7); ASSERT(v1 == 8); ASSERT(v2 == 2); @@ -1253,8 +1247,9 @@ static void test_timer(void) { ASSERT(v3 == 5); mg_timer_free(&head, &t1); - ASSERT(head == &t3); - ASSERT(head->next == NULL); + ASSERT(head == &t2); + ASSERT(head->next == &t3); + ASSERT(head->next->next == NULL); mg_timer_free(&head, &t2); ASSERT(head == &t3); @@ -1366,7 +1361,7 @@ static void fn1(struct mg_connection *c, int ev, void *ev_data, void *fn_data) { (void) c; } -static void test_dns_timeout(const char *dns_server_url, const char *errstr) { +static void test_dns_error(const char *dns_server_url, const char *errstr) { // Test timeout struct mg_mgr mgr; char buf[100] = ""; @@ -1374,12 +1369,12 @@ static void test_dns_timeout(const char *dns_server_url, const char *errstr) { mg_mgr_init(&mgr); mgr.dns4.url = dns_server_url; mgr.dnstimeout = 10; - MG_DEBUG(("opening dummy DNS listener...")); + MG_DEBUG(("opening dummy DNS listener @ [%s]...", dns_server_url)); mg_listen(&mgr, mgr.dns4.url, NULL, NULL); // Just discard our queries mg_http_connect(&mgr, "http://google.com", fn1, buf); for (i = 0; i < 50 && buf[0] == '\0'; i++) mg_mgr_poll(&mgr, 1); mg_mgr_free(&mgr); - MG_DEBUG(("buf: [%s]", buf)); + // MG_DEBUG(("buf: [%s] [%s]", buf, errstr)); ASSERT(strcmp(buf, errstr) == 0); } @@ -1429,9 +1424,9 @@ static void test_dns(void) { ASSERT(strcmp(dm.name, "new-fp-shed.wg1.b.yahoo.com") == 0); } - test_dns_timeout("udp://127.0.0.1:12345", "DNS timeout"); - test_dns_timeout("", "resolver"); - test_dns_timeout("tcp://0.0.0.0:0", "DNS error"); + test_dns_error("udp://127.0.0.1:12345", "DNS timeout"); + test_dns_error("", "resolver"); + test_dns_error("tcp://0.0.0.0:0", "DNS error"); } static void test_util(void) {