From 3dcc5a60f273a8149611b22a6957d6162c543743 Mon Sep 17 00:00:00 2001 From: Dmitry Podgorny Date: Wed, 3 Jun 2020 02:11:40 +0300 Subject: [PATCH] Introduce global timed handlers There are situations when applications need more predictable timed handlers that don't depend on connection status. Other usecase of the global handlers is to manage offline connections, for example, reconnect after a disconnection. --- ChangeLog | 5 ++ src/common.h | 63 +++++++++++++------------ src/ctx.c | 1 + src/handler.c | 127 +++++++++++++++++++++++++++++++------------------- strophe.h | 13 +++++- 5 files changed, 131 insertions(+), 78 deletions(-) diff --git a/ChangeLog b/ChangeLog index baff0df..4694dee 100644 --- a/ChangeLog +++ b/ChangeLog @@ -3,6 +3,9 @@ - SCRAM-SHA-256 and SCRAM-SHA-512 support - c-ares support - LibreSSL support + - Introduced global timed handlers that fire periodically regardless of + connections status, such a handler can be used to implement deferred + re-connection - examples/register implements XEP-0077 - Fixed issue with IPv6 on Windows (#153) - Improved portability across systems such as Haiku, Windows @@ -13,6 +16,8 @@ - xmpp_conn_is_disconnected() - xmpp_stanza_add_child_ex() - xmpp_stanza_get_context() + - xmpp_global_timed_handler_add() + - xmpp_global_timed_handler_delete() 0.9.3 - PLAIN mechanism is used only when no other mechanisms are supported diff --git a/src/common.h b/src/common.h index 51247db..ddc30fd 100644 --- a/src/common.h +++ b/src/common.h @@ -29,6 +29,38 @@ #include "rand.h" #include "snprintf.h" +/** handlers **/ + +typedef struct _xmpp_handlist_t xmpp_handlist_t; +struct _xmpp_handlist_t { + /* common members */ + int user_handler; + int (*handler)(); + void *userdata; + int enabled; /* handlers are added disabled and enabled after the + * handler chain is processed to prevent stanzas from + * getting processed by newly added handlers */ + xmpp_handlist_t *next; + + union { + /* timed handlers */ + struct { + unsigned long period; + uint64_t last_stamp; + }; + /* id handlers */ + struct { + char *id; + }; + /* normal handlers */ + struct { + char *ns; + char *name; + char *type; + }; + } u; +}; + /** run-time context **/ typedef enum { @@ -49,6 +81,7 @@ struct _xmpp_ctx_t { xmpp_rand_t *rand; xmpp_loop_status_t loop_status; xmpp_connlist_t *connlist; + xmpp_handlist_t *timed_handlers; unsigned long timeout; }; @@ -100,36 +133,6 @@ struct _xmpp_send_queue_t { xmpp_send_queue_t *next; }; -typedef struct _xmpp_handlist_t xmpp_handlist_t; -struct _xmpp_handlist_t { - /* common members */ - int user_handler; - int (*handler)(); - void *userdata; - int enabled; /* handlers are added disabled and enabled after the - * handler chain is processed to prevent stanzas from - * getting processed by newly added handlers */ - xmpp_handlist_t *next; - - union { - /* timed handlers */ - struct { - unsigned long period; - uint64_t last_stamp; - }; - /* id handlers */ - struct { - char *id; - }; - /* normal handlers */ - struct { - char *ns; - char *name; - char *type; - }; - } u; -}; - #define UNUSED(x) ((void)(x)) #define MAX_DOMAIN_LEN 256 diff --git a/src/ctx.c b/src/ctx.c index f1b9784..3491616 100644 --- a/src/ctx.c +++ b/src/ctx.c @@ -419,6 +419,7 @@ xmpp_ctx_t *xmpp_ctx_new(const xmpp_mem_t *const mem, ctx->log = log; ctx->connlist = NULL; + ctx->timed_handlers = NULL; ctx->loop_status = XMPP_LOOP_NOTSTARTED; ctx->rand = xmpp_rand_new(ctx); ctx->timeout = EVENT_LOOP_DEFAULT_TIMEOUT; diff --git a/src/handler.c b/src/handler.c index 74f30b7..2d8aa1d 100644 --- a/src/handler.c +++ b/src/handler.c @@ -24,6 +24,8 @@ #include "common.h" #include "ostypes.h" +typedef int (*xmpp_void_handler)(); + /* Remove item from the list pointed by head, but don't free it. * There can be a situation when user's handler deletes another handler which * is the previous in the list. handler_fire_stanza() and handler_fire_timed() @@ -35,16 +37,12 @@ */ static void _handler_item_remove(xmpp_handlist_t **head, xmpp_handlist_t *item) { - xmpp_handlist_t *i = *head; - - if (i == item) - *head = item->next; - else if (i != NULL) { - while (i->next != NULL && i->next != item) - i = i->next; - if (i->next == item) { - i->next = item->next; + while (*head) { + if (*head == item) { + *head = item->next; + break; } + head = &(*head)->next; } } @@ -191,7 +189,7 @@ uint64_t handler_fire_timed(xmpp_ctx_t *const ctx) if (!ret) { /* delete handler if it returned false */ _handler_item_remove(&conn->timed_handlers, item); - xmpp_free(conn->ctx, item); + xmpp_free(ctx, item); } } else if (min > (item->u.period - elapsed)) min = item->u.period - elapsed; @@ -202,6 +200,34 @@ uint64_t handler_fire_timed(xmpp_ctx_t *const ctx) connitem = connitem->next; } + /* + * Check timed handlers in context. These handlers fire periodically + * regardless of connections state. + * TODO Reduce copy-paste. + */ + item = ctx->timed_handlers; + while (item) { + next = item->next; + timestamp = time_stamp(); + elapsed = time_elapsed(item->u.last_stamp, timestamp); + if (elapsed >= item->u.period) { + /* fire! */ + item->u.last_stamp = timestamp; + ret = + ((xmpp_global_timed_handler)item->handler)(ctx, item->userdata); + /* list may be changed during execution of a handler */ + next = item->next; + if (!ret) { + /* delete handler if it returned false */ + _handler_item_remove(&ctx->timed_handlers, item); + xmpp_free(ctx, item); + } + } else if (min > (item->u.period - elapsed)) + min = item->u.period - elapsed; + + item = next; + } + return min; } @@ -224,18 +250,19 @@ void handler_reset_timed(xmpp_conn_t *conn, int user_only) } } -static void _timed_handler_add(xmpp_conn_t *const conn, - xmpp_timed_handler handler, +static void _timed_handler_add(xmpp_ctx_t *ctx, + xmpp_handlist_t **handlers_list, + xmpp_void_handler handler, const unsigned long period, void *const userdata, const int user_handler) { - xmpp_handlist_t *item, *tail; + xmpp_handlist_t *item; /* check if handler is already in the list */ - for (item = conn->timed_handlers; item; item = item->next) { + for (item = *handlers_list; item; item = item->next) { if (item->handler == handler && item->userdata == userdata) { - xmpp_warn(conn->ctx, "xmpp", "Timed handler already exists."); + xmpp_warn(ctx, "xmpp", "Timed handler already exists."); break; } } @@ -243,7 +270,7 @@ static void _timed_handler_add(xmpp_conn_t *const conn, return; /* build new item */ - item = xmpp_alloc(conn->ctx, sizeof(xmpp_handlist_t)); + item = xmpp_alloc(ctx, sizeof(xmpp_handlist_t)); if (!item) return; @@ -251,19 +278,29 @@ static void _timed_handler_add(xmpp_conn_t *const conn, item->handler = handler; item->userdata = userdata; item->enabled = 0; - item->next = NULL; item->u.period = period; item->u.last_stamp = time_stamp(); /* append item to list */ - if (!conn->timed_handlers) - conn->timed_handlers = item; - else { - tail = conn->timed_handlers; - while (tail->next) - tail = tail->next; - tail->next = item; + item->next = *handlers_list; + *handlers_list = item; +} + +static void _timed_handler_delete(xmpp_ctx_t *ctx, + xmpp_handlist_t **handlers_list, + xmpp_void_handler handler) +{ + xmpp_handlist_t *item; + + while (*handlers_list) { + item = *handlers_list; + if (item->handler == handler) { + *handlers_list = item->next; + xmpp_free(ctx, item); + } else { + handlers_list = &item->next; + } } } @@ -277,27 +314,7 @@ static void _timed_handler_add(xmpp_conn_t *const conn, void xmpp_timed_handler_delete(xmpp_conn_t *const conn, xmpp_timed_handler handler) { - xmpp_handlist_t *item, *prev; - - if (!conn->timed_handlers) - return; - - prev = NULL; - item = conn->timed_handlers; - while (item) { - if (item->handler == handler) { - if (prev) - prev->next = item->next; - else - conn->timed_handlers = item->next; - - xmpp_free(conn->ctx, item); - item = prev ? prev->next : conn->timed_handlers; - } else { - prev = item; - item = item->next; - } - } + _timed_handler_delete(conn->ctx, &conn->timed_handlers, handler); } static void _id_handler_add(xmpp_conn_t *const conn, @@ -524,7 +541,8 @@ void xmpp_timed_handler_add(xmpp_conn_t *const conn, const unsigned long period, void *const userdata) { - _timed_handler_add(conn, handler, period, userdata, 1); + _timed_handler_add(conn->ctx, &conn->timed_handlers, handler, period, + userdata, 1); } /** Add a timed system handler. @@ -541,7 +559,8 @@ void handler_add_timed(xmpp_conn_t *const conn, const unsigned long period, void *const userdata) { - _timed_handler_add(conn, handler, period, userdata, 0); + _timed_handler_add(conn->ctx, &conn->timed_handlers, handler, period, + userdata, 0); } /** Add an id based stanza handler. @@ -713,3 +732,17 @@ void handler_system_delete_all(xmpp_conn_t *conn) if (iter) hash_iter_release(iter); } + +void xmpp_global_timed_handler_add(xmpp_ctx_t *const ctx, + xmpp_global_timed_handler handler, + const unsigned long period, + void *const userdata) +{ + _timed_handler_add(ctx, &ctx->timed_handlers, handler, period, userdata, 1); +} + +void xmpp_global_timed_handler_delete(xmpp_ctx_t *const ctx, + xmpp_global_timed_handler handler) +{ + _timed_handler_delete(ctx, &ctx->timed_handlers, handler); +} diff --git a/strophe.h b/strophe.h index acf22a5..19898bb 100644 --- a/strophe.h +++ b/strophe.h @@ -279,7 +279,7 @@ void xmpp_send_raw(xmpp_conn_t *const conn, /* handlers */ -/* if the handle returns false it is removed */ +/* if the handler returns false it is removed */ typedef int (*xmpp_timed_handler)(xmpp_conn_t *const conn, void *const userdata); @@ -290,6 +290,17 @@ void xmpp_timed_handler_add(xmpp_conn_t *const conn, void xmpp_timed_handler_delete(xmpp_conn_t *const conn, xmpp_timed_handler handler); +/* if the handler returns false it is removed */ +typedef int (*xmpp_global_timed_handler)(xmpp_ctx_t *const ctx, + void *const userdata); + +void xmpp_global_timed_handler_add(xmpp_ctx_t *const ctx, + xmpp_global_timed_handler handler, + const unsigned long period, + void *const userdata); +void xmpp_global_timed_handler_delete(xmpp_ctx_t *const ctx, + xmpp_global_timed_handler handler); + /* if the handler returns false it is removed */ typedef int (*xmpp_handler)(xmpp_conn_t *const conn, xmpp_stanza_t *const stanza,