1
0
Fork 0
mirror of https://github.com/warmcat/libwebsockets.git synced 2025-03-09 00:00:04 +01:00

lws_retry_bo_t: generic retry backoff

Add a generic table-based backoff scheme and a helper to track the
try count and calculate the next delay in ms.

Allow lws_sequencer_t to be given one of these at creation time...
since the number of creation args is getting a bit too much
convert that to an info struct at the same time.
This commit is contained in:
Andy Green 2019-08-05 14:47:51 +01:00
parent 8d79c06f49
commit f12e116188
9 changed files with 161 additions and 33 deletions

View file

@ -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',

View file

@ -533,6 +533,7 @@ struct lws;
#include <libwebsockets/lws-lwsac.h>
#include <libwebsockets/lws-fts.h>
#include <libwebsockets/lws-diskcache.h>
#include <libwebsockets/lws-retry.h>
#include <libwebsockets/lws-sequencer.h>
#include <libwebsockets/abstract/abstract.h>

View file

@ -0,0 +1,60 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-2019 Andy Green <andy@warmcat.com>
*
* 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);

View file

@ -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

View file

@ -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;

View file

@ -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__);

View file

@ -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)

View file

@ -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 */

View file

@ -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;