diff --git a/READMEs/README.lws_sequencer.md b/READMEs/README.lws_sequencer.md index 155631455..fab057352 100644 --- a/READMEs/README.lws_sequencer.md +++ b/READMEs/README.lws_sequencer.md @@ -50,10 +50,21 @@ out a retry cooloff period and then start the retry when the ## Creating an `lws_sequencer_t` +``` +typedef struct lws_seq_info { + struct lws_context *context; /* lws_context for seq */ + int tsi; /* thread service idx */ + size_t user_size; /* size of user alloc */ + void **puser; /* place ptr to user */ + lws_seq_event_cb cb; /* seq callback */ + const char *name; /* seq name */ + const lws_retry_bo_t *retry; /* retry policy */ +} lws_seq_info_t; +``` + ``` lws_sequencer_t * -lws_sequencer_create(struct lws_context *context, int tsi, void *user_data, - lws_seq_event_cb cb); +lws_sequencer_create(lws_seq_info_t *info); ``` When created, in lws the sequencer objects are bound to a 'per-thread', diff --git a/include/libwebsockets.h b/include/libwebsockets.h index 7db5321cc..3343f50a0 100644 --- a/include/libwebsockets.h +++ b/include/libwebsockets.h @@ -533,6 +533,7 @@ struct lws; #include #include #include +#include #include #include diff --git a/include/libwebsockets/lws-retry.h b/include/libwebsockets/lws-retry.h new file mode 100644 index 000000000..fe058c5c2 --- /dev/null +++ b/include/libwebsockets/lws-retry.h @@ -0,0 +1,60 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2019 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * included from libwebsockets.h + */ + +/* + * Specifies backoff ranges using a pair of uint32_t in ms for the min, max. + * + * The actual backoff timing is picked randomly within the range. + */ + +typedef struct lws_retry_range { + uint32_t min_ms; + uint32_t max_ms; +} lws_retry_range_t; + +typedef struct lws_retry_bo { + const lws_retry_range_t *retry_ms_table; /* backoff range pair */ + uint16_t retry_ms_table_count; /* ranges in table */ + uint16_t conceal_count; /* max retries to conceal */ +} lws_retry_bo_t; + +/** + * lws_retry_get_delay_ms() - get next delay from backoff table + * + * \param lws_context: the lws context (used for getting random) + * \param retry: the retry backoff table we are using + * \param ctry: pointer to the try counter + * \param conceal: pointer to flag set to nonzero if the try should be concealed + * in terms of creating an error + * + * Increments *\p try and retruns the number of ms that should elapse before the + * next connection retry, according to the backoff table \p retry. *\p conceal is + * set if the number of tries is less than the backoff table conceal_count, or + * is zero if it exceeded it. This lets you conceal a certain number of retries + * before alerting the caller there is a problem. + */ + +LWS_VISIBLE LWS_EXTERN unsigned int +lws_retry_get_delay_ms(struct lws_context *context, const lws_retry_bo_t *retry, + uint16_t *ctry, char *conceal); + diff --git a/include/libwebsockets/lws-sequencer.h b/include/libwebsockets/lws-sequencer.h index c31ce7509..923ce2254 100644 --- a/include/libwebsockets/lws-sequencer.h +++ b/include/libwebsockets/lws-sequencer.h @@ -30,6 +30,13 @@ * lws_sequencer-s are bound to a pt (per-thread) which for the default case of * one service thread is the same as binding to an lws_context. */ +/* + * retry backoff table... retry n happens after .retry_ms_table[n] ms, with + * the last entry used if n is greater than the number of entries. + * + * The first .conceal_count retries are concealed, but after that the failures + * are reported. + */ typedef enum { LWSSEQ_CREATED, /* sequencer created */ @@ -64,24 +71,20 @@ typedef struct lws_sequencer lws_sequencer_t; /* opaque */ */ typedef lws_seq_cb_return_t (*lws_seq_event_cb)(struct lws_sequencer *seq, void *user, int event, void *data); - +typedef struct lws_seq_info { + struct lws_context *context; /* lws_context for seq */ + int tsi; /* thread service idx */ + size_t user_size; /* size of user alloc */ + void **puser; /* place ptr to user */ + lws_seq_event_cb cb; /* seq callback */ + const char *name; /* seq name */ + const lws_retry_bo_t *retry; /* retry policy */ +} lws_seq_info_t; /** * lws_sequencer_create() - create and bind sequencer to a pt * - * \param context: lws_context - * \param tsi: thread service index, 0 is safe anything else depends - * multiple service threads being set up - * \param user_size: size of the additional heap allocation to allocate after - * the lws sequencer object to hold user data associated - * with the sequence. The start of this extra allocation - * is passed to the sequencer callback and in \p *puser - * \param puser: pointer to a void * that will be set to the start of the - * extra user heap allocation whose size was set by - * user_size. The user area pointed to here is all zeroed - * after successful sequencer creation. - * \param cb: callback for events on this sequencer - * \param name: Used in sequencer logging + * \param info: information about sequencer to create * * This binds an abstract sequencer to a per-thread (by default, the single * event loop of an lws_context). After the event loop starts, the sequencer @@ -94,8 +97,7 @@ typedef lws_seq_cb_return_t (*lws_seq_event_cb)(struct lws_sequencer *seq, * pt locking is used to protect the related data structures. */ LWS_VISIBLE LWS_EXTERN lws_sequencer_t * -lws_sequencer_create(struct lws_context *context, int tsi, size_t user_size, - void **puser, lws_seq_event_cb cb, const char *name); +lws_sequencer_create(lws_seq_info_t *info); /** * lws_sequencer_destroy() - destroy the sequencer diff --git a/lib/abstract/test-sequencer.c b/lib/abstract/test-sequencer.c index b38a65fe0..92387054c 100644 --- a/lib/abstract/test-sequencer.c +++ b/lib/abstract/test-sequencer.c @@ -233,14 +233,20 @@ lws_abs_unit_test_sequencer(const lws_test_sequencer_args_t *args) { struct lws_seq_test_sequencer *s; lws_sequencer_t *seq; + lws_seq_info_t i; + + memset(&i, 0, sizeof(i)); + i.context = args->abs->vh->context; + i.user_size = sizeof(struct lws_seq_test_sequencer); + i.puser = (void **)&s; + i.cb = test_sequencer_cb; + i.name = "test-seq"; /* * Create a sequencer in the event loop to manage the tests */ - seq = lws_sequencer_create(args->abs->vh->context, 0, - sizeof(struct lws_seq_test_sequencer), - (void **)&s, test_sequencer_cb, "test-seq"); + seq = lws_sequencer_create(&i); if (!seq) { lwsl_err("%s: unable to create sequencer\n", __func__); return 1; diff --git a/lib/abstract/transports/unit-test.c b/lib/abstract/transports/unit-test.c index ccee684f3..c8a8860f5 100644 --- a/lib/abstract/transports/unit-test.c +++ b/lib/abstract/transports/unit-test.c @@ -415,15 +415,21 @@ lws_atcut_create(lws_abs_t *ai) { abs_unit_test_priv_t *priv; lws_sequencer_t *seq; + lws_seq_info_t i; seq_priv_t *s; + memset(&i, 0, sizeof(i)); + i.context = ai->vh->context; + i.user_size = sizeof(*s); + i.puser = (void **)&s; + i.cb = unit_test_sequencer_cb; + i.name = "unit-test-seq"; + /* * Create the sequencer for the steps in a single unit test */ - seq = lws_sequencer_create(ai->vh->context, 0, sizeof(*s), - (void **)&s, unit_test_sequencer_cb, - "unit-test-seq"); + seq = lws_sequencer_create(&i); if (!seq) { lwsl_err("%s: unable to create sequencer\n", __func__); diff --git a/lib/core-net/network.c b/lib/core-net/network.c index 1ae77fbb5..8c3fe1a13 100644 --- a/lib/core-net/network.c +++ b/lib/core-net/network.c @@ -380,6 +380,40 @@ lws_socket_bind(struct lws_vhost *vhost, lws_sockfd_type sockfd, int port, return port; } +static const lws_retry_range_t default_bo = { 3000, 7000 }; + +unsigned int +lws_retry_get_delay_ms(struct lws_context *context, + const lws_retry_bo_t *retry, uint16_t *ctry, char *conceal) +{ + const lws_retry_range_t *r = &default_bo; + unsigned int ms; + uint16_t ra; + + if (conceal) + *conceal = 0; + + if (retry) { + if (*ctry < retry->retry_ms_table_count) + r = &retry->retry_ms_table[*ctry]; + else + r = &retry->retry_ms_table[ + retry->retry_ms_table_count - 1]; + } + + ms = r->min_ms; + if (lws_get_random(context, &ra, sizeof(ra)) == sizeof(ra)) + ms += ((r->max_ms - ms) * ra) / 65535; + + if (*ctry < 0xffff) + (*ctry)++; + + if (retry && conceal) + *conceal = (int)*ctry <= retry->conceal_count; + + return ms; +} + #if defined(LWS_WITH_IPV6) LWS_EXTERN unsigned long lws_get_addr_scope(const char *ipaddr) diff --git a/lib/core-net/sequencer.c b/lib/core-net/sequencer.c index 72c123081..b277ce877 100644 --- a/lib/core-net/sequencer.c +++ b/lib/core-net/sequencer.c @@ -43,6 +43,7 @@ typedef struct lws_sequencer { struct lws_context_per_thread *pt; lws_seq_event_cb cb; const char *name; + const lws_retry_bo_t *retry; time_t time_created; time_t timeout; /* 0 or time we timeout */ @@ -53,20 +54,20 @@ typedef struct lws_sequencer { #define QUEUE_SANITY_LIMIT 10 lws_sequencer_t * -lws_sequencer_create(struct lws_context *context, int tsi, size_t user_size, - void **puser, lws_seq_event_cb cb, const char *name) +lws_sequencer_create(lws_seq_info_t *i) { - struct lws_context_per_thread *pt = &context->pt[tsi]; - lws_sequencer_t *seq = lws_zalloc(sizeof(*seq) + user_size, __func__); + struct lws_context_per_thread *pt = &i->context->pt[i->tsi]; + lws_sequencer_t *seq = lws_zalloc(sizeof(*seq) + i->user_size, __func__); if (!seq) return NULL; - seq->cb = cb; + seq->cb = i->cb; seq->pt = pt; - seq->name = name; + seq->name = i->name; + seq->retry = i->retry; - *puser = (void *)&seq[1]; + *i->puser = (void *)&seq[1]; /* add the sequencer to the pt */ diff --git a/minimal-examples/api-tests/api-test-lws_sequencer/main.c b/minimal-examples/api-tests/api-test-lws_sequencer/main.c index 2ec07e8b2..af058f667 100644 --- a/minimal-examples/api-tests/api-test-lws_sequencer/main.c +++ b/minimal-examples/api-tests/api-test-lws_sequencer/main.c @@ -325,6 +325,7 @@ main(int argc, const char **argv) struct lws_context *context; lws_sequencer_t *seq; struct lws_vhost *vh; + lws_seq_info_t i; struct myseq *s; const char *p; @@ -369,8 +370,14 @@ main(int argc, const char **argv) * receive the LWSSEQ_CREATED callback */ - seq = lws_sequencer_create(context, 0, sizeof(struct myseq), - (void **)&s, sequencer_cb, "seq"); + memset(&i, 0, sizeof(i)); + i.context = context; + i.user_size = sizeof(struct myseq); + i.puser = (void **)&s; + i.cb = sequencer_cb; + i.name = "seq"; + + seq = lws_sequencer_create(&i); if (!seq) { lwsl_err("%s: unable to create sequencer\n", __func__); goto bail1;