Fix #1520 - move state to struct mg_mgr

This commit is contained in:
Sergey Lyubka 2022-04-12 14:14:55 +01:00
parent 66376885e9
commit ef44f90491
24 changed files with 218 additions and 276 deletions

View File

@ -1835,6 +1835,40 @@ mg_tls_init(c, &opts);
## Timer
### mg\_timer\_add()
```c
struct timer *mg_timer_add(struct mg_mgr *mgr,
uint64_t period_ms, unsigned flags,
void (*fn)(void *), void *fn_data);
```
Setup a timer. This is a high-level timer API that allows to add a software
timer to the event manager. This function `calloc()`s a new timer and
adds it to the `mgr->timers` list. All added timers are polled when
`mg_mgr_poll()` is called, and called if expired.
<span class="badge bg-danger">NOTE: </span> Make sure that the timer
interval is equal or more to the `mg_mgr_poll()` timeout.
Parameters:
- `mgr` - Pointer to `mg_mgr` event manager structure
- `ms` - An interval in milliseconds
- `flags` - Timer flags bitmask, `MG_TIMER_REPEAT` and `MG_TIMER_RUN_NOW`
- `fn` - Function to invoke
- `fn_data` - Function argument
Return value: None
Usage example:
```c
void timer_fn(void *data) {
// ...
}
mg_timer_add(mgr, 1000, MG_TIMER_REPEAT, timer_fn, NULL);
```
### struct mg\_timer
```c
@ -1856,13 +1890,15 @@ as the `mg_mgr_poll()` timeout argument in the main event loop.
### mg\_timer\_init()
```c
void mg_timer_init(struct mg_timer *t, uint64_t period_ms, unsigned flags,
void mg_timer_init(struct mg_timer **head,
struct mg_timer *t, uint64_t period_ms, unsigned flags,
void (*fn)(void *), void *fn_data);
```
Setup a timer.
Parameters:
- `head` - Pointer to `mg_timer` list head
- `t` - Pointer to `mg_timer` that should be initialized
- `ms` - An interval in milliseconds
- `flags` - Timer flags bitmask, `MG_TIMER_REPEAT` and `MG_TIMER_RUN_NOW`
@ -1877,19 +1913,19 @@ void timer_fn(void *data) {
// ...
}
struct mg_timer timer;
mg_timer_init(&timer, 1000 /* 1sec */, MG_TIMER_REPEAT, timer_fn, NULL);
// A timer gets initialized and linked into the internal timers list
struct mg_timer timer, *head = NULL;
mg_timer_init(&head, &timer, 1000, MG_TIMER_REPEAT, timer_fn, NULL);
```
### mg\_timer\_free()
```c
void mg_timer_free(struct mg_timer *t);
void mg_timer_free(struct mg_timer **head, struct mg_timer *t);
```
Free timer, remove it from the internal timers list.
Parameters:
- `head` - Pointer to `mg_timer` list head
- `t` - Timer to free
Return value: None
@ -1904,7 +1940,7 @@ mg_timer_free(&timer);
### mg\_timer\_poll()
```c
void mg_timer_poll(uint64_t uptime_ms);
void mg_timer_poll(struct mg_timer **head, uint64_t uptime_ms);
```
Traverse list of timers and call them if current timestamp `uptime_ms` is
@ -1914,6 +1950,7 @@ Note, that `mg_mgr_poll` function internally calls `mg_timer_poll`; therefore,
in most cases it is unnecessary to call it explicitly.
Parameters:
- `head` - Pointer to `mg_timer` list head
- `uptime_ms` - current timestamp
Return value: None

View File

@ -192,15 +192,12 @@ static void log_cb(void *arg) {
int main(void) {
struct mg_mgr mgr;
struct mg_timer t1, t2;
mg_mgr_init(&mgr);
mg_http_listen(&mgr, "http://localhost:8000", cb, &mgr);
mg_timer_init(&t1, 500, MG_TIMER_REPEAT, mjpeg_cb, &mgr);
mg_timer_init(&t2, 1000, MG_TIMER_REPEAT, log_cb, &mgr);
mg_timer_add(&mgr, 500, MG_TIMER_REPEAT, mjpeg_cb, &mgr);
mg_timer_add(&mgr, 1000, MG_TIMER_REPEAT, log_cb, &mgr);
for (;;) mg_mgr_poll(&mgr, 50);
mg_timer_free(&t1);
mg_timer_free(&t2);
mg_mgr_free(&mgr);
return 0;

View File

@ -39,7 +39,6 @@ static void cb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
int main(void) {
struct mg_mgr mgr;
struct mg_timer t1;
mg_mgr_init(&mgr);
mg_log_set("3");

View File

@ -13,16 +13,16 @@
// The very first web page in history. You can replace it from command line
static const char *s_url = "http://info.cern.ch/";
static const char *s_post_data = NULL; // POST data
static const int64_t s_timeout_ms = 1500; // Connect timeout in milliseconds
static const char *s_post_data = NULL; // POST data
static const uint64_t s_timeout_ms = 1500; // Connect timeout in milliseconds
// Print HTTP response and signal that we're done
static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
if (ev == MG_EV_OPEN) {
// Connection created. Store connect expiration time in c->label
*(int64_t *) c->label = mg_millis() + s_timeout_ms;
*(uint64_t *) c->label = mg_millis() + s_timeout_ms;
} else if (ev == MG_EV_POLL) {
if (mg_millis() > *(int64_t *) c->label &&
if (mg_millis() > *(uint64_t *) c->label &&
(c->is_connecting || c->is_resolving)) {
mg_error(c, "Connect timeout");
}

View File

@ -76,14 +76,12 @@ static void timer_fn(void *arg) {
int main(void) {
struct mg_mgr mgr;
struct mg_timer t1;
mg_mgr_init(&mgr);
srand(time(NULL));
mg_timer_init(&t1, 1000, MG_TIMER_REPEAT | MG_TIMER_RUN_NOW, timer_fn, NULL);
mg_timer_add(&mgr, 1000, MG_TIMER_REPEAT | MG_TIMER_RUN_NOW, timer_fn, NULL);
mg_http_listen(&mgr, s_listen_on, fn, NULL);
MG_INFO(("Listening on %s", s_listen_on));
for (;;) mg_mgr_poll(&mgr, 1000);
mg_timer_free(&t1);
mg_mgr_free(&mgr);
return 0;
}

View File

@ -67,11 +67,10 @@ static void timer_fn(void *arg) {
}
int main(void) {
struct mg_mgr mgr; // Event manager
struct mg_timer t1; // Timer
struct mg_mgr mgr; // Event manager
mg_mgr_init(&mgr); // Init event manager
mg_timer_init(&t1, 5000, MG_TIMER_REPEAT, timer_fn, &mgr); // Init timer
mg_timer_add(&mgr, 5000, MG_TIMER_REPEAT, timer_fn, &mgr); // Init timer
jsonrpc_init(NULL, NULL); // Init JSON-RPC instance
jsonrpc_export("sum", sum); // And export a couple
@ -80,7 +79,6 @@ int main(void) {
printf("Starting WS listener on %s/websocket\n", s_listen_on);
mg_http_listen(&mgr, s_listen_on, fn, NULL); // Create HTTP listener
for (;;) mg_mgr_poll(&mgr, 1000); // Infinite event loop
mg_timer_free(&t1); // Free timer resources
mg_mgr_free(&mgr); // Deallocate event manager
return 0;
}

View File

@ -49,14 +49,12 @@ static void timer_fn(void *arg) {
int main(void) {
struct mg_mgr mgr;
struct mg_timer t1;
mg_mgr_init(&mgr);
mg_http_listen(&mgr, "http://localhost:8000", cb, NULL);
mg_timer_init(&t1, 1000, MG_TIMER_REPEAT, timer_fn, &mgr);
mg_timer_add(&mgr, 1000, MG_TIMER_REPEAT, timer_fn, &mgr);
for (;;) mg_mgr_poll(&mgr, 50);
mg_timer_free(&t1);
mg_mgr_free(&mgr);
return 0;

View File

@ -74,17 +74,15 @@ static void timer_fn(void *arg) {
int main(void) {
struct mg_mgr mgr;
struct mg_timer timer;
int topts = MG_TIMER_REPEAT | MG_TIMER_RUN_NOW;
signal(SIGINT, signal_handler); // Setup signal handlers - exist event
signal(SIGTERM, signal_handler); // manager loop on SIGINT and SIGTERM
mg_mgr_init(&mgr); // Init event manager
mg_timer_init(&timer, 3000, topts, timer_fn, &mgr); // Init timer
while (s_signo == 0) mg_mgr_poll(&mgr, 1000); // Event loop, 1s timeout
mg_mgr_free(&mgr); // Finished, cleanup
mg_timer_free(&timer); // Free timer resources
mg_mgr_init(&mgr); // Init event manager
mg_timer_add(&mgr, 3000, topts, timer_fn, &mgr); // Init timer
while (s_signo == 0) mg_mgr_poll(&mgr, 1000); // Event loop, 1s timeout
mg_mgr_free(&mgr); // Finished, cleanup
return 0;
}

View File

@ -40,13 +40,11 @@ static void timer_fn(void *arg) {
}
int main(void) {
struct mg_mgr mgr; // Event manager
struct mg_timer t1; // Timer
mg_mgr_init(&mgr); // Initialise event manager
mg_timer_init(&t1, 300, MG_TIMER_REPEAT, timer_fn, &mgr); // Init timer
struct mg_mgr mgr; // Event manager
mg_mgr_init(&mgr); // Initialise event manager
mg_timer_add(&mgr, 300, MG_TIMER_REPEAT, timer_fn, &mgr); // Init timer
mg_http_listen(&mgr, s_listen_on, fn, NULL); // Create HTTP listener
for (;;) mg_mgr_poll(&mgr, 1000); // Infinite event loop
mg_timer_free(&t1); // Free timer resources
mg_mgr_free(&mgr); // Free manager resources
return 0;
}

View File

@ -36,11 +36,10 @@ static void tfn(void *param) {
int main(void) {
struct mg_mgr mgr;
struct mg_timer timer;
static struct mg_connection *c;
mg_mgr_init(&mgr);
c = mg_connect(&mgr, s_ssdp_url, fn, NULL);
mg_timer_init(&timer, 2000, MG_TIMER_REPEAT | MG_TIMER_RUN_NOW, tfn, c);
mg_timer_add(&mgr, 2000, MG_TIMER_REPEAT | MG_TIMER_RUN_NOW, tfn, c);
while (true) mg_mgr_poll(&mgr, 200);
mg_mgr_free(&mgr);
return 0;

View File

@ -54,13 +54,11 @@ static void timer_callback(void *arg) {
int main(void) {
struct mg_mgr mgr;
struct mg_timer t1;
mg_mgr_init(&mgr);
mg_http_listen(&mgr, "http://localhost:8000", cb, NULL);
mg_timer_init(&t1, 500, MG_TIMER_REPEAT, timer_callback, &mgr);
mg_timer_add(&mgr, 500, MG_TIMER_REPEAT, timer_callback, &mgr);
for (;;) mg_mgr_poll(&mgr, 50);
mg_timer_free(&t1);
mg_mgr_free(&mgr);
return 0;

View File

@ -72,8 +72,7 @@ int main(int argc, char *argv[]) {
mg_http_listen(&mgr, s_http_addr, wcb, NULL);
mg_http_listen(&mgr, s_https_addr, wcb, &mgr);
struct mg_timer t;
mg_timer_init(&t, 5000, MG_TIMER_REPEAT | MG_TIMER_RUN_NOW, timer_fn, &mgr);
mg_timer_add(&mgr, 5000, MG_TIMER_REPEAT | MG_TIMER_RUN_NOW, timer_fn, &mgr);
// Start infinite event loop
MG_INFO(("Mongoose version : v%s", MG_VERSION));

View File

@ -124,21 +124,20 @@ struct dns_data {
uint16_t txnid;
};
static struct dns_data *s_reqs; // Active DNS requests
static void mg_sendnsreq(struct mg_connection *, struct mg_str *, int,
struct mg_dns *, bool);
static void mg_dns_free(struct dns_data *d) {
LIST_DELETE(struct dns_data, &s_reqs, d);
static void mg_dns_free(struct mg_connection *c, struct dns_data *d) {
LIST_DELETE(struct dns_data,
(struct dns_data **) &c->mgr->active_dns_requests, d);
free(d);
}
void mg_resolve_cancel(struct mg_connection *c) {
struct dns_data *tmp, *d;
for (d = s_reqs; d != NULL; d = tmp) {
struct dns_data *tmp, *d = (struct dns_data *) c->mgr->active_dns_requests;
for (; d != NULL; d = tmp) {
tmp = d->next;
if (d->c == c) mg_dns_free(d);
if (d->c == c) mg_dns_free(c, d);
}
}
@ -251,7 +250,8 @@ static void dns_cb(struct mg_connection *c, int ev, void *ev_data,
struct dns_data *d, *tmp;
if (ev == MG_EV_POLL) {
uint64_t now = *(uint64_t *) ev_data;
for (d = s_reqs; d != NULL; d = tmp) {
for (d = (struct dns_data *) c->mgr->active_dns_requests; d != NULL;
d = tmp) {
tmp = d->next;
// MG_DEBUG ("%lu %lu dns poll", d->expire, now));
if (now > d->expire) mg_error(d->c, "DNS timeout");
@ -265,7 +265,8 @@ static void dns_cb(struct mg_connection *c, int ev, void *ev_data,
free(s);
} else {
MG_VERBOSE(("%s %d", dm.name, dm.resolved));
for (d = s_reqs; d != NULL; d = tmp) {
for (d = (struct dns_data *) c->mgr->active_dns_requests; d != NULL;
d = tmp) {
tmp = d->next;
// MG_INFO(("d %p %hu %hu", d, d->txnid, dm.txnid));
if (dm.txnid != d->txnid) continue;
@ -288,17 +289,18 @@ static void dns_cb(struct mg_connection *c, int ev, void *ev_data,
} else {
MG_ERROR(("%lu already resolved", d->c->id));
}
mg_dns_free(d);
mg_dns_free(c, d);
resolved = 1;
}
}
if (!resolved) MG_ERROR(("stray DNS reply"));
c->recv.len = 0;
} else if (ev == MG_EV_CLOSE) {
for (d = s_reqs; d != NULL; d = tmp) {
for (d = (struct dns_data *) c->mgr->active_dns_requests; d != NULL;
d = tmp) {
tmp = d->next;
mg_error(d->c, "DNS error");
mg_dns_free(d);
mg_dns_free(c, d);
}
}
(void) fn_data;
@ -348,10 +350,11 @@ static void mg_sendnsreq(struct mg_connection *c, struct mg_str *name, int ms,
} else if ((d = (struct dns_data *) calloc(1, sizeof(*d))) == NULL) {
mg_error(c, "resolve OOM");
} else {
struct dns_data *reqs = (struct dns_data *) c->mgr->active_dns_requests;
char buf[100];
d->txnid = s_reqs ? (uint16_t) (s_reqs->txnid + 1) : 1;
d->next = s_reqs;
s_reqs = d;
d->txnid = reqs ? (uint16_t) (reqs->txnid + 1) : 1;
d->next = (struct dns_data *) c->mgr->active_dns_requests;
c->mgr->active_dns_requests = d;
d->expire = mg_millis() + (uint64_t) ms;
d->c = c;
c->is_resolving = 1;
@ -1279,68 +1282,20 @@ void mg_http_write_chunk(struct mg_connection *c, const char *buf, size_t len) {
static const char *mg_http_status_code_str(int status_code) {
switch (status_code) {
case 100: return "Continue";
case 101: return "Switching Protocols";
case 102: return "Processing";
case 200: return "OK";
case 201: return "Created";
case 202: return "Accepted";
case 203: return "Non-authoritative Information";
case 204: return "No Content";
case 205: return "Reset Content";
case 206: return "Partial Content";
case 207: return "Multi-Status";
case 208: return "Already Reported";
case 226: return "IM Used";
case 300: return "Multiple Choices";
case 301: return "Moved Permanently";
case 302: return "Found";
case 303: return "See Other";
case 304: return "Not Modified";
case 305: return "Use Proxy";
case 307: return "Temporary Redirect";
case 308: return "Permanent Redirect";
case 400: return "Bad Request";
case 401: return "Unauthorized";
case 402: return "Payment Required";
case 403: return "Forbidden";
case 404: return "Not Found";
case 405: return "Method Not Allowed";
case 406: return "Not Acceptable";
case 407: return "Proxy Authentication Required";
case 408: return "Request Timeout";
case 409: return "Conflict";
case 410: return "Gone";
case 411: return "Length Required";
case 412: return "Precondition Failed";
case 413: return "Payload Too Large";
case 414: return "Request-URI Too Long";
case 415: return "Unsupported Media Type";
case 416: return "Requested Range Not Satisfiable";
case 417: return "Expectation Failed";
case 418: return "I'm a teapot";
case 421: return "Misdirected Request";
case 422: return "Unprocessable Entity";
case 423: return "Locked";
case 424: return "Failed Dependency";
case 426: return "Upgrade Required";
case 428: return "Precondition Required";
case 429: return "Too Many Requests";
case 431: return "Request Header Fields Too Large";
case 444: return "Connection Closed Without Response";
case 451: return "Unavailable For Legal Reasons";
case 499: return "Client Closed Request";
case 500: return "Internal Server Error";
case 501: return "Not Implemented";
case 502: return "Bad Gateway";
case 503: return "Service Unavailable";
case 504: return "Gateway Timeout";
case 505: return "HTTP Version Not Supported";
case 506: return "Variant Also Negotiates";
case 507: return "Insufficient Storage";
case 508: return "Loop Detected";
case 510: return "Not Extended";
case 511: return "Network Authentication Required";
case 599: return "Network Connect Timeout Error";
default: return "OK";
}
}
@ -1572,7 +1527,7 @@ static void printdirentry(const char *name, void *userdata) {
static void listdir(struct mg_connection *c, struct mg_http_message *hm,
const struct mg_http_serve_opts *opts, char *dir) {
static const char *sort_js_code =
const char *sort_js_code =
"<script>function srt(tb, sc, so, d) {"
"var tr = Array.prototype.slice.call(tb.rows, 0),"
"tr = tr.sort(function (a, b) { var c1 = a.cells[sc], c2 = b.cells[sc],"
@ -1582,7 +1537,7 @@ static void listdir(struct mg_connection *c, struct mg_http_message *hm,
"return so * (t1 < 0 && t2 >= 0 ? -1 : t2 < 0 && t1 >= 0 ? 1 : "
"n1 ? parseInt(n2) - parseInt(n1) : "
"c1.textContent.trim().localeCompare(c2.textContent.trim())); });";
static const char *sort_js_code2 =
const char *sort_js_code2 =
"for (var i = 0; i < tr.length; i++) tb.appendChild(tr[i]); "
"if (!d) window.location.hash = ('sc=' + sc + '&so=' + so); "
"};"
@ -2406,20 +2361,18 @@ void mg_mqtt_pub(struct mg_connection *c, struct mg_str topic,
mg_send_u16(c, mg_htons((uint16_t) topic.len));
mg_send(c, topic.ptr, topic.len);
if (qos > 0) {
static uint16_t s_id;
if (++s_id == 0) s_id++;
mg_send_u16(c, mg_htons(s_id));
if (++c->mgr->mqtt_id == 0) ++c->mgr->mqtt_id;
mg_send_u16(c, mg_htons(c->mgr->mqtt_id));
}
mg_send(c, data.ptr, data.len);
}
void mg_mqtt_sub(struct mg_connection *c, struct mg_str topic, int qos) {
static uint16_t s_id;
uint8_t qos_ = qos & 3;
uint32_t total_len = 2 + (uint32_t) topic.len + 2 + 1;
mg_mqtt_send_header(c, MQTT_CMD_SUBSCRIBE, 2, total_len);
if (++s_id == 0) ++s_id;
mg_send_u16(c, mg_htons(s_id));
if (++c->mgr->mqtt_id == 0) ++c->mgr->mqtt_id;
mg_send_u16(c, mg_htons(c->mgr->mqtt_id));
mg_send_u16(c, mg_htons((uint16_t) topic.len));
mg_send(c, topic.ptr, topic.len);
mg_send(c, &qos_, sizeof(qos_));
@ -2597,6 +2550,7 @@ struct mg_connection *mg_mqtt_listen(struct mg_mgr *mgr, const char *url,
size_t mg_vprintf(struct mg_connection *c, const char *fmt, va_list ap) {
char mem[256], *buf = mem;
size_t len = mg_vasprintf(&buf, sizeof(mem), fmt, ap);
@ -2797,8 +2751,17 @@ struct mg_connection *mg_listen(struct mg_mgr *mgr, const char *url,
return c;
}
struct mg_timer *mg_timer_add(struct mg_mgr *mgr, uint64_t milliseconds,
unsigned flags, void (*fn)(void *), void *arg) {
struct mg_timer *t = (struct mg_timer *) calloc(1, sizeof(*t));
mg_timer_init(&mgr->timers, t, milliseconds, flags, fn, arg);
return t;
}
void mg_mgr_free(struct mg_mgr *mgr) {
struct mg_connection *c;
struct mg_timer *tmp, *t = mgr->timers;
while (t != NULL) tmp = t->next, free(t), t = tmp;
for (c = mgr->conns; c != NULL; c = c->next) c->is_closing = 1;
mg_mgr_poll(mgr, 0);
#if MG_ARCH == MG_ARCH_FREERTOS_TCP
@ -3662,7 +3625,7 @@ void mg_mgr_poll(struct mg_mgr *mgr, int ms) {
mg_iotest(mgr, ms);
now = mg_millis();
mg_timer_poll(now);
mg_timer_poll(&mgr->timers, now);
for (c = mgr->conns; c != NULL; c = tmp) {
tmp = c->next;
@ -3989,7 +3952,7 @@ char *mg_hexdump(const void *buf, size_t len) {
char *mg_hex(const void *buf, size_t len, char *to) {
const unsigned char *p = (const unsigned char *) buf;
static const char *hex = "0123456789abcdef";
const char *hex = "0123456789abcdef";
size_t i = 0;
for (; len--; p++) {
to[i++] = hex[p[0] >> 4];
@ -4056,7 +4019,6 @@ uint64_t mg_tou64(struct mg_str str) {
while (i < str.len && str.ptr[i] >= '0' && str.ptr[i] <= '9') {
result *= 10;
result += (unsigned) (str.ptr[i] - '0');
MG_INFO(("[%.*s] %llu", (int) str.len, str.ptr, result));
i++;
}
return result;
@ -4193,33 +4155,26 @@ size_t mg_vsnprintf(char *buf, size_t len, const char *fmt, va_list ap) {
struct mg_timer *g_timers;
void mg_timer_init(struct mg_timer *t, uint64_t ms, unsigned flags,
void (*fn)(void *), void *arg) {
struct mg_timer tmp = {ms, 0UL, flags, fn, arg, g_timers};
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;
g_timers = t;
*head = t;
if (flags & MG_TIMER_RUN_NOW) fn(arg);
}
void mg_timer_free(struct mg_timer *t) {
struct mg_timer **head = &g_timers;
void mg_timer_free(struct mg_timer **head, struct mg_timer *t) {
while (*head && *head != t) head = &(*head)->next;
if (*head) *head = t->next;
}
void mg_timer_poll(uint64_t now_ms) {
void mg_timer_poll(struct mg_timer **head, uint64_t now_ms) {
// If time goes back (wrapped around), reset timers
struct mg_timer *t, *tmp;
static uint64_t oldnow; // Timestamp in a previous invocation
if (oldnow > now_ms) { // If it is wrapped, reset timers
for (t = g_timers; t != NULL; t = t->next) t->expire = 0;
}
oldnow = now_ms;
for (t = g_timers; t != NULL; t = tmp) {
for (t = *head; t != NULL; t = tmp) {
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 > now_ms) continue;
t->fn(t->arg);
@ -4227,7 +4182,7 @@ void mg_timer_poll(uint64_t now_ms) {
// 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(t);
if (!(t->flags & MG_TIMER_REPEAT)) mg_timer_free(head, t);
}
}

View File

@ -675,6 +675,7 @@ void mg_log_set_callback(void (*fn)(const void *, size_t, void *), void *param);
struct mg_timer {
uint64_t period_ms; // Timer period in milliseconds
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
@ -686,10 +687,11 @@ struct mg_timer {
extern struct mg_timer *g_timers; // Global list of timers
void mg_timer_init(struct mg_timer *, uint64_t, unsigned, void (*)(void *),
void *);
void mg_timer_free(struct mg_timer *);
void mg_timer_poll(uint64_t current_time_ms);
void mg_timer_init(struct mg_timer **head, struct mg_timer *timer,
uint64_t milliseconds, unsigned flags, void (*fn)(void *),
void *arg);
void mg_timer_free(struct mg_timer **head, struct mg_timer *);
void mg_timer_poll(struct mg_timer **head, uint64_t new_ms);
@ -878,6 +880,7 @@ enum {
struct mg_dns {
const char *url; // DNS server URL
struct mg_connection *c; // DNS server connection
@ -897,6 +900,9 @@ struct mg_mgr {
int dnstimeout; // DNS resolve timeout in milliseconds
unsigned long nextid; // Next connection ID
void *userdata; // Arbitrary user data pointer
uint16_t mqtt_id; // MQTT IDs for pub/sub
void *active_dns_requests; // DNS requests in progress
struct mg_timer *timers; // Active timers
#if MG_ARCH == MG_ARCH_FREERTOS_TCP
SocketSet_t ss; // NOTE(lsm): referenced from socket struct
#endif
@ -956,6 +962,8 @@ bool mg_mgr_wakeup(struct mg_connection *pipe, const void *buf, size_t len);
struct mg_connection *mg_alloc_conn(struct mg_mgr *);
void mg_close_conn(struct mg_connection *c);
bool mg_open_listener(struct mg_connection *c, const char *url);
struct mg_timer *mg_timer_add(struct mg_mgr *mgr, uint64_t milliseconds,
unsigned flags, void (*fn)(void *), void *arg);

View File

@ -12,21 +12,20 @@ struct dns_data {
uint16_t txnid;
};
static struct dns_data *s_reqs; // Active DNS requests
static void mg_sendnsreq(struct mg_connection *, struct mg_str *, int,
struct mg_dns *, bool);
static void mg_dns_free(struct dns_data *d) {
LIST_DELETE(struct dns_data, &s_reqs, d);
static void mg_dns_free(struct mg_connection *c, struct dns_data *d) {
LIST_DELETE(struct dns_data,
(struct dns_data **) &c->mgr->active_dns_requests, d);
free(d);
}
void mg_resolve_cancel(struct mg_connection *c) {
struct dns_data *tmp, *d;
for (d = s_reqs; d != NULL; d = tmp) {
struct dns_data *tmp, *d = (struct dns_data *) c->mgr->active_dns_requests;
for (; d != NULL; d = tmp) {
tmp = d->next;
if (d->c == c) mg_dns_free(d);
if (d->c == c) mg_dns_free(c, d);
}
}
@ -139,7 +138,8 @@ static void dns_cb(struct mg_connection *c, int ev, void *ev_data,
struct dns_data *d, *tmp;
if (ev == MG_EV_POLL) {
uint64_t now = *(uint64_t *) ev_data;
for (d = s_reqs; d != NULL; d = tmp) {
for (d = (struct dns_data *) c->mgr->active_dns_requests; d != NULL;
d = tmp) {
tmp = d->next;
// MG_DEBUG ("%lu %lu dns poll", d->expire, now));
if (now > d->expire) mg_error(d->c, "DNS timeout");
@ -153,7 +153,8 @@ static void dns_cb(struct mg_connection *c, int ev, void *ev_data,
free(s);
} else {
MG_VERBOSE(("%s %d", dm.name, dm.resolved));
for (d = s_reqs; d != NULL; d = tmp) {
for (d = (struct dns_data *) c->mgr->active_dns_requests; d != NULL;
d = tmp) {
tmp = d->next;
// MG_INFO(("d %p %hu %hu", d, d->txnid, dm.txnid));
if (dm.txnid != d->txnid) continue;
@ -176,17 +177,18 @@ static void dns_cb(struct mg_connection *c, int ev, void *ev_data,
} else {
MG_ERROR(("%lu already resolved", d->c->id));
}
mg_dns_free(d);
mg_dns_free(c, d);
resolved = 1;
}
}
if (!resolved) MG_ERROR(("stray DNS reply"));
c->recv.len = 0;
} else if (ev == MG_EV_CLOSE) {
for (d = s_reqs; d != NULL; d = tmp) {
for (d = (struct dns_data *) c->mgr->active_dns_requests; d != NULL;
d = tmp) {
tmp = d->next;
mg_error(d->c, "DNS error");
mg_dns_free(d);
mg_dns_free(c, d);
}
}
(void) fn_data;
@ -236,10 +238,11 @@ static void mg_sendnsreq(struct mg_connection *c, struct mg_str *name, int ms,
} else if ((d = (struct dns_data *) calloc(1, sizeof(*d))) == NULL) {
mg_error(c, "resolve OOM");
} else {
struct dns_data *reqs = (struct dns_data *) c->mgr->active_dns_requests;
char buf[100];
d->txnid = s_reqs ? (uint16_t) (s_reqs->txnid + 1) : 1;
d->next = s_reqs;
s_reqs = d;
d->txnid = reqs ? (uint16_t) (reqs->txnid + 1) : 1;
d->next = (struct dns_data *) c->mgr->active_dns_requests;
c->mgr->active_dns_requests = d;
d->expire = mg_millis() + (uint64_t) ms;
d->c = c;
c->is_resolving = 1;

View File

@ -289,68 +289,20 @@ void mg_http_write_chunk(struct mg_connection *c, const char *buf, size_t len) {
static const char *mg_http_status_code_str(int status_code) {
switch (status_code) {
case 100: return "Continue";
case 101: return "Switching Protocols";
case 102: return "Processing";
case 200: return "OK";
case 201: return "Created";
case 202: return "Accepted";
case 203: return "Non-authoritative Information";
case 204: return "No Content";
case 205: return "Reset Content";
case 206: return "Partial Content";
case 207: return "Multi-Status";
case 208: return "Already Reported";
case 226: return "IM Used";
case 300: return "Multiple Choices";
case 301: return "Moved Permanently";
case 302: return "Found";
case 303: return "See Other";
case 304: return "Not Modified";
case 305: return "Use Proxy";
case 307: return "Temporary Redirect";
case 308: return "Permanent Redirect";
case 400: return "Bad Request";
case 401: return "Unauthorized";
case 402: return "Payment Required";
case 403: return "Forbidden";
case 404: return "Not Found";
case 405: return "Method Not Allowed";
case 406: return "Not Acceptable";
case 407: return "Proxy Authentication Required";
case 408: return "Request Timeout";
case 409: return "Conflict";
case 410: return "Gone";
case 411: return "Length Required";
case 412: return "Precondition Failed";
case 413: return "Payload Too Large";
case 414: return "Request-URI Too Long";
case 415: return "Unsupported Media Type";
case 416: return "Requested Range Not Satisfiable";
case 417: return "Expectation Failed";
case 418: return "I'm a teapot";
case 421: return "Misdirected Request";
case 422: return "Unprocessable Entity";
case 423: return "Locked";
case 424: return "Failed Dependency";
case 426: return "Upgrade Required";
case 428: return "Precondition Required";
case 429: return "Too Many Requests";
case 431: return "Request Header Fields Too Large";
case 444: return "Connection Closed Without Response";
case 451: return "Unavailable For Legal Reasons";
case 499: return "Client Closed Request";
case 500: return "Internal Server Error";
case 501: return "Not Implemented";
case 502: return "Bad Gateway";
case 503: return "Service Unavailable";
case 504: return "Gateway Timeout";
case 505: return "HTTP Version Not Supported";
case 506: return "Variant Also Negotiates";
case 507: return "Insufficient Storage";
case 508: return "Loop Detected";
case 510: return "Not Extended";
case 511: return "Network Authentication Required";
case 599: return "Network Connect Timeout Error";
default: return "OK";
}
}
@ -582,7 +534,7 @@ static void printdirentry(const char *name, void *userdata) {
static void listdir(struct mg_connection *c, struct mg_http_message *hm,
const struct mg_http_serve_opts *opts, char *dir) {
static const char *sort_js_code =
const char *sort_js_code =
"<script>function srt(tb, sc, so, d) {"
"var tr = Array.prototype.slice.call(tb.rows, 0),"
"tr = tr.sort(function (a, b) { var c1 = a.cells[sc], c2 = b.cells[sc],"
@ -592,7 +544,7 @@ static void listdir(struct mg_connection *c, struct mg_http_message *hm,
"return so * (t1 < 0 && t2 >= 0 ? -1 : t2 < 0 && t1 >= 0 ? 1 : "
"n1 ? parseInt(n2) - parseInt(n1) : "
"c1.textContent.trim().localeCompare(c2.textContent.trim())); });";
static const char *sort_js_code2 =
const char *sort_js_code2 =
"for (var i = 0; i < tr.length; i++) tb.appendChild(tr[i]); "
"if (!d) window.location.hash = ('sc=' + sc + '&so=' + so); "
"};"

View File

@ -95,20 +95,18 @@ void mg_mqtt_pub(struct mg_connection *c, struct mg_str topic,
mg_send_u16(c, mg_htons((uint16_t) topic.len));
mg_send(c, topic.ptr, topic.len);
if (qos > 0) {
static uint16_t s_id;
if (++s_id == 0) s_id++;
mg_send_u16(c, mg_htons(s_id));
if (++c->mgr->mqtt_id == 0) ++c->mgr->mqtt_id;
mg_send_u16(c, mg_htons(c->mgr->mqtt_id));
}
mg_send(c, data.ptr, data.len);
}
void mg_mqtt_sub(struct mg_connection *c, struct mg_str topic, int qos) {
static uint16_t s_id;
uint8_t qos_ = qos & 3;
uint32_t total_len = 2 + (uint32_t) topic.len + 2 + 1;
mg_mqtt_send_header(c, MQTT_CMD_SUBSCRIBE, 2, total_len);
if (++s_id == 0) ++s_id;
mg_send_u16(c, mg_htons(s_id));
if (++c->mgr->mqtt_id == 0) ++c->mgr->mqtt_id;
mg_send_u16(c, mg_htons(c->mgr->mqtt_id));
mg_send_u16(c, mg_htons((uint16_t) topic.len));
mg_send(c, topic.ptr, topic.len);
mg_send(c, &qos_, sizeof(qos_));

View File

@ -1,6 +1,7 @@
#include "net.h"
#include "dns.h"
#include "log.h"
#include "timer.h"
#include "tls.h"
#include "util.h"
@ -204,8 +205,17 @@ struct mg_connection *mg_listen(struct mg_mgr *mgr, const char *url,
return c;
}
struct mg_timer *mg_timer_add(struct mg_mgr *mgr, uint64_t milliseconds,
unsigned flags, void (*fn)(void *), void *arg) {
struct mg_timer *t = (struct mg_timer *) calloc(1, sizeof(*t));
mg_timer_init(&mgr->timers, t, milliseconds, flags, fn, arg);
return t;
}
void mg_mgr_free(struct mg_mgr *mgr) {
struct mg_connection *c;
struct mg_timer *tmp, *t = mgr->timers;
while (t != NULL) tmp = t->next, free(t), t = tmp;
for (c = mgr->conns; c != NULL; c = c->next) c->is_closing = 1;
mg_mgr_poll(mgr, 0);
#if MG_ARCH == MG_ARCH_FREERTOS_TCP

View File

@ -4,6 +4,7 @@
#include "event.h"
#include "iobuf.h"
#include "str.h"
#include "timer.h"
struct mg_dns {
const char *url; // DNS server URL
@ -24,6 +25,9 @@ struct mg_mgr {
int dnstimeout; // DNS resolve timeout in milliseconds
unsigned long nextid; // Next connection ID
void *userdata; // Arbitrary user data pointer
uint16_t mqtt_id; // MQTT IDs for pub/sub
void *active_dns_requests; // DNS requests in progress
struct mg_timer *timers; // Active timers
#if MG_ARCH == MG_ARCH_FREERTOS_TCP
SocketSet_t ss; // NOTE(lsm): referenced from socket struct
#endif
@ -83,3 +87,5 @@ bool mg_mgr_wakeup(struct mg_connection *pipe, const void *buf, size_t len);
struct mg_connection *mg_alloc_conn(struct mg_mgr *);
void mg_close_conn(struct mg_connection *c);
bool mg_open_listener(struct mg_connection *c, const char *url);
struct mg_timer *mg_timer_add(struct mg_mgr *mgr, uint64_t milliseconds,
unsigned flags, void (*fn)(void *), void *arg);

View File

@ -536,7 +536,7 @@ void mg_mgr_poll(struct mg_mgr *mgr, int ms) {
mg_iotest(mgr, ms);
now = mg_millis();
mg_timer_poll(now);
mg_timer_poll(&mgr->timers, now);
for (c = mgr->conns; c != NULL; c = tmp) {
tmp = c->next;

View File

@ -194,7 +194,7 @@ char *mg_hexdump(const void *buf, size_t len) {
char *mg_hex(const void *buf, size_t len, char *to) {
const unsigned char *p = (const unsigned char *) buf;
static const char *hex = "0123456789abcdef";
const char *hex = "0123456789abcdef";
size_t i = 0;
for (; len--; p++) {
to[i++] = hex[p[0] >> 4];
@ -261,7 +261,6 @@ uint64_t mg_tou64(struct mg_str str) {
while (i < str.len && str.ptr[i] >= '0' && str.ptr[i] <= '9') {
result *= 10;
result += (unsigned) (str.ptr[i] - '0');
MG_INFO(("[%.*s] %llu", (int) str.len, str.ptr, result));
i++;
}
return result;

View File

@ -4,33 +4,26 @@
#include "timer.h"
#include "arch.h"
struct mg_timer *g_timers;
void mg_timer_init(struct mg_timer *t, uint64_t ms, unsigned flags,
void (*fn)(void *), void *arg) {
struct mg_timer tmp = {ms, 0UL, flags, fn, arg, g_timers};
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;
g_timers = t;
*head = t;
if (flags & MG_TIMER_RUN_NOW) fn(arg);
}
void mg_timer_free(struct mg_timer *t) {
struct mg_timer **head = &g_timers;
void mg_timer_free(struct mg_timer **head, struct mg_timer *t) {
while (*head && *head != t) head = &(*head)->next;
if (*head) *head = t->next;
}
void mg_timer_poll(uint64_t now_ms) {
void mg_timer_poll(struct mg_timer **head, uint64_t now_ms) {
// If time goes back (wrapped around), reset timers
struct mg_timer *t, *tmp;
static uint64_t oldnow; // Timestamp in a previous invocation
if (oldnow > now_ms) { // If it is wrapped, reset timers
for (t = g_timers; t != NULL; t = t->next) t->expire = 0;
}
oldnow = now_ms;
for (t = g_timers; t != NULL; t = tmp) {
for (t = *head; t != NULL; t = tmp) {
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 > now_ms) continue;
t->fn(t->arg);
@ -38,6 +31,6 @@ void mg_timer_poll(uint64_t now_ms) {
// 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(t);
if (!(t->flags & MG_TIMER_REPEAT)) mg_timer_free(head, t);
}
}

View File

@ -4,6 +4,7 @@
struct mg_timer {
uint64_t period_ms; // Timer period in milliseconds
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
@ -15,7 +16,8 @@ struct mg_timer {
extern struct mg_timer *g_timers; // Global list of timers
void mg_timer_init(struct mg_timer *, uint64_t, unsigned, void (*)(void *),
void *);
void mg_timer_free(struct mg_timer *);
void mg_timer_poll(uint64_t current_time_ms);
void mg_timer_init(struct mg_timer **head, struct mg_timer *timer,
uint64_t milliseconds, unsigned flags, void (*fn)(void *),
void *arg);
void mg_timer_free(struct mg_timer **head, struct mg_timer *);
void mg_timer_poll(struct mg_timer **head, uint64_t new_ms);

View File

@ -1140,103 +1140,100 @@ static void f1(void *arg) {
static void test_timer(void) {
int v1 = 0, v2 = 0, v3 = 0;
struct mg_timer t1, t2, t3;
struct mg_timer t1, t2, t3, *head = NULL;
MG_INFO(("g_timers: %p", g_timers));
ASSERT(g_timers == 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, &t3, 10, MG_TIMER_REPEAT | MG_TIMER_RUN_NOW, f1, &v3);
mg_timer_init(&t1, 5, MG_TIMER_REPEAT, f1, &v1);
mg_timer_init(&t2, 15, 0, f1, &v2);
mg_timer_init(&t3, 10, MG_TIMER_REPEAT | MG_TIMER_RUN_NOW, f1, &v3);
ASSERT(head == &t3);
ASSERT(head->next == &t2);
ASSERT(g_timers == &t3);
ASSERT(g_timers->next == &t2);
mg_timer_poll(0);
mg_timer_poll(1);
mg_timer_poll(&head, 0);
mg_timer_poll(&head, 1);
ASSERT(v1 == 0);
ASSERT(v2 == 0);
ASSERT(v3 == 1);
mg_timer_poll(5);
mg_timer_poll(&head, 5);
ASSERT(v1 == 1);
ASSERT(v2 == 0);
ASSERT(v3 == 1);
ASSERT(g_timers == &t3);
ASSERT(g_timers->next == &t2);
ASSERT(head == &t3);
ASSERT(head->next == &t2);
// Simulate long delay - timers must invalidate expiration times
mg_timer_poll(100);
mg_timer_poll(&head, 100);
ASSERT(v1 == 2);
ASSERT(v2 == 1);
ASSERT(v3 == 2);
ASSERT(g_timers == &t3);
ASSERT(g_timers->next == &t1); // t2 should be removed
ASSERT(g_timers->next->next == NULL);
ASSERT(head == &t3);
ASSERT(head->next == &t1); // t2 should be removed
ASSERT(head->next->next == NULL);
mg_timer_poll(107);
mg_timer_poll(&head, 107);
ASSERT(v1 == 3);
ASSERT(v2 == 1);
ASSERT(v3 == 2);
mg_timer_poll(114);
mg_timer_poll(&head, 114);
ASSERT(v1 == 4);
ASSERT(v2 == 1);
ASSERT(v3 == 3);
mg_timer_poll(115);
mg_timer_poll(&head, 115);
ASSERT(v1 == 5);
ASSERT(v2 == 1);
ASSERT(v3 == 3);
mg_timer_init(&t2, 3, 0, f1, &v2);
ASSERT(g_timers == &t2);
ASSERT(g_timers->next == &t3);
ASSERT(g_timers->next->next == &t1);
ASSERT(g_timers->next->next->next == NULL);
mg_timer_init(&head, &t2, 3, 0, f1, &v2);
ASSERT(head == &t2);
ASSERT(head->next == &t3);
ASSERT(head->next->next == &t1);
ASSERT(head->next->next->next == NULL);
mg_timer_poll(120);
mg_timer_poll(&head, 120);
ASSERT(v1 == 6);
ASSERT(v2 == 1);
ASSERT(v3 == 4);
mg_timer_poll(125);
mg_timer_poll(&head, 125);
ASSERT(v1 == 7);
ASSERT(v2 == 2);
ASSERT(v3 == 4);
// Test millisecond counter wrap - when time goes back.
mg_timer_poll(0);
mg_timer_poll(&head, 0);
ASSERT(v1 == 7);
ASSERT(v2 == 2);
ASSERT(v3 == 4);
ASSERT(g_timers == &t3);
ASSERT(g_timers->next == &t1);
ASSERT(g_timers->next->next == NULL);
ASSERT(head == &t3);
ASSERT(head->next == &t1);
ASSERT(head->next->next == NULL);
mg_timer_poll(7);
mg_timer_poll(&head, 7);
ASSERT(v1 == 8);
ASSERT(v2 == 2);
ASSERT(v3 == 4);
mg_timer_poll(11);
mg_timer_poll(&head, 11);
ASSERT(v1 == 9);
ASSERT(v2 == 2);
ASSERT(v3 == 5);
mg_timer_free(&t1);
ASSERT(g_timers == &t3);
ASSERT(g_timers->next == NULL);
mg_timer_free(&head, &t1);
ASSERT(head == &t3);
ASSERT(head->next == NULL);
mg_timer_free(&t2);
ASSERT(g_timers == &t3);
ASSERT(g_timers->next == NULL);
mg_timer_free(&head, &t2);
ASSERT(head == &t3);
ASSERT(head->next == NULL);
mg_timer_free(&t3);
ASSERT(g_timers == NULL);
mg_timer_free(&head, &t3);
ASSERT(head == NULL);
}
static bool sn(const char *fmt, ...) {