mirror of
https://github.com/cesanta/mongoose.git
synced 2024-11-27 20:59:00 +08:00
Fix #1520 - move state to struct mg_mgr
This commit is contained in:
parent
66376885e9
commit
ef44f90491
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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");
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
|
137
mongoose.c
137
mongoose.c
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
16
mongoose.h
16
mongoose.h
@ -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);
|
||||
|
||||
|
||||
|
||||
|
33
src/dns.c
33
src/dns.c
@ -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;
|
||||
|
52
src/http.c
52
src/http.c
@ -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); "
|
||||
"};"
|
||||
|
10
src/mqtt.c
10
src/mqtt.c
@ -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_));
|
||||
|
10
src/net.c
10
src/net.c
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
27
src/timer.c
27
src/timer.c
@ -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);
|
||||
}
|
||||
}
|
||||
|
10
src/timer.h
10
src/timer.h
@ -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);
|
||||
|
@ -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, ...) {
|
||||
|
Loading…
Reference in New Issue
Block a user