diff --git a/docs/README.md b/docs/README.md index 60582f66..2670eb42 100644 --- a/docs/README.md +++ b/docs/README.md @@ -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. + +NOTE: 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 diff --git a/examples/complete/main.c b/examples/complete/main.c index a1a89741..c875dd12 100644 --- a/examples/complete/main.c +++ b/examples/complete/main.c @@ -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; diff --git a/examples/form-upload/main.c b/examples/form-upload/main.c index 98c02cdf..0f8153a8 100644 --- a/examples/form-upload/main.c +++ b/examples/form-upload/main.c @@ -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"); diff --git a/examples/http-client/main.c b/examples/http-client/main.c index 535c699e..2ff3ca8a 100644 --- a/examples/http-client/main.c +++ b/examples/http-client/main.c @@ -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"); } diff --git a/examples/huge-response/main.c b/examples/huge-response/main.c index 01fcea96..3e1c1f14 100644 --- a/examples/huge-response/main.c +++ b/examples/huge-response/main.c @@ -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; } diff --git a/examples/json-rpc-over-websocket/main.c b/examples/json-rpc-over-websocket/main.c index 7ad1be1b..24afa2ac 100644 --- a/examples/json-rpc-over-websocket/main.c +++ b/examples/json-rpc-over-websocket/main.c @@ -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; } diff --git a/examples/live-log/main.c b/examples/live-log/main.c index b8ab2aaf..235a5c53 100644 --- a/examples/live-log/main.c +++ b/examples/live-log/main.c @@ -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; diff --git a/examples/mqtt-client/main.c b/examples/mqtt-client/main.c index 6e22bd79..38978bc8 100644 --- a/examples/mqtt-client/main.c +++ b/examples/mqtt-client/main.c @@ -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; } diff --git a/examples/timers/main.c b/examples/timers/main.c index 88a21f0f..686d9611 100644 --- a/examples/timers/main.c +++ b/examples/timers/main.c @@ -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; } diff --git a/examples/udp-ssdp-search/main.c b/examples/udp-ssdp-search/main.c index 90a0291f..35bb2b09 100644 --- a/examples/udp-ssdp-search/main.c +++ b/examples/udp-ssdp-search/main.c @@ -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; diff --git a/examples/video-stream/main.c b/examples/video-stream/main.c index 9c6be6fa..ba076cd8 100644 --- a/examples/video-stream/main.c +++ b/examples/video-stream/main.c @@ -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; diff --git a/examples/zephyr/http-server/src/main.c b/examples/zephyr/http-server/src/main.c index 2d0f620c..b4c9a199 100644 --- a/examples/zephyr/http-server/src/main.c +++ b/examples/zephyr/http-server/src/main.c @@ -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)); diff --git a/mongoose.c b/mongoose.c index 870fc3ef..0e228959 100644 --- a/mongoose.c +++ b/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 = "