mirror of
https://github.com/cesanta/mongoose.git
synced 2024-12-27 15:01:03 +08:00
Address #1551 - better timers handling
This commit is contained in:
parent
443d0f0c6c
commit
cf7d1d8041
@ -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
|
||||
};
|
||||
```
|
||||
|
||||
|
16
mongoose.c
16
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
16
src/timer.c
16
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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user