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:
parent
8d79c06f49
commit
f12e116188
9 changed files with 161 additions and 33 deletions
|
@ -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',
|
||||
|
|
|
@ -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>
|
||||
|
|
60
include/libwebsockets/lws-retry.h
Normal file
60
include/libwebsockets/lws-retry.h
Normal 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);
|
||||
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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__);
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Reference in a new issue