mirror of
https://github.com/warmcat/libwebsockets.git
synced 2025-03-09 00:00:04 +01:00
lws_sequencer_t: allow wsi binding
This commit is contained in:
parent
557d51f1f4
commit
b6b6915837
8 changed files with 134 additions and 2 deletions
|
@ -4,7 +4,7 @@ Often a single network action like a client GET is just part of a
|
|||
larger series of actions, perhaps involving different connections.
|
||||
|
||||
Since lws operates inside an event loop, if the outer sequencing
|
||||
does not, it can be awkward to synchronize these steps with what's
|
||||
doesn't, it can be awkward to synchronize these steps with what's
|
||||
happening on the network with a particular connection on the event
|
||||
loop thread.
|
||||
|
||||
|
@ -110,3 +110,29 @@ Under some conditions wsi may want to hang around a bit to see if there is a
|
|||
subsequent client wsi transaction they can be reused on. They will clean
|
||||
themselves up when they time out.
|
||||
|
||||
## Watching wsi lifecycle from a sequencer
|
||||
|
||||
When a sequencer is creating a wsi as part of its sequence, it will be very
|
||||
interested in lifecycle events. At client wsi creation time, the sequencer
|
||||
callback can set info->seq to itself in order to receive lifecycle messages
|
||||
about its wsi.
|
||||
|
||||
|message|meaning|
|
||||
|---|---|
|
||||
|`LWSSEQ_WSI_CONNECTED`|The wsi has become connected|
|
||||
|`LWSSEQ_WSI_CONN_FAIL`|The wsi has failed to connect|
|
||||
|`LWSSEQ_WSI_CONN_CLOSE`|The wsi had been connected, but has now closed|
|
||||
|
||||
By receiving these, the sequencer can understand when it should attempt
|
||||
reconnections or that it cannot progress the sequence.
|
||||
|
||||
When dealing with wsi that were created by the sequencer, they may close at
|
||||
any time, eg, be closed by the remote peer or an intermediary. The
|
||||
`LWSSEQ_WSI_CONN_CLOSE` message may have been queued but since they are
|
||||
strictly handled in the order they arrived, before it was
|
||||
handled an earlier message may want to cause some api to be called on
|
||||
the now-free()-d wsi. To detect this situation safely, there is a
|
||||
sequencer api `lws_sequencer_check_wsi()` which peeks the message
|
||||
buffer and returns nonzero if it later contains an `LWSSEQ_WSI_CONN_CLOSE`
|
||||
already.
|
||||
|
||||
|
|
|
@ -73,6 +73,9 @@ typedef struct lws_abs {
|
|||
const struct lws_abs_transport *at;
|
||||
const lws_token_map_t *at_tokens;
|
||||
|
||||
lws_sequencer_t *seq;
|
||||
void *opaque_user_data;
|
||||
|
||||
/*
|
||||
* These are filled in by lws_abs_bind_and_create_instance() in the
|
||||
* instance copy. They do not need to be set when creating the struct
|
||||
|
|
|
@ -49,6 +49,8 @@ enum lws_client_connect_ssl_connection_flags {
|
|||
* */
|
||||
};
|
||||
|
||||
typedef struct lws_sequencer lws_sequencer_t;
|
||||
|
||||
/** struct lws_client_connect_info - parameters to connect with when using
|
||||
* lws_client_connect_via_info() */
|
||||
|
||||
|
@ -119,9 +121,16 @@ struct lws_client_connect_info {
|
|||
* tokens
|
||||
*/
|
||||
|
||||
lws_sequencer_t *seq;
|
||||
/**< NULL, or an lws_sequencer_t that wants to be given messages about
|
||||
* this wsi's lifecycle as it connects, errors or closes.
|
||||
*/
|
||||
|
||||
void *opaque_user_data;
|
||||
/**< This data has no meaning to lws but is applied to the client wsi
|
||||
* and can be retrieved by user code with lws_get_opaque_user_data()
|
||||
* and can be retrieved by user code with lws_get_opaque_user_data().
|
||||
* It's also provided with sequencer messages if the wsi is bound to
|
||||
* an lws_sequencer_t.
|
||||
*/
|
||||
|
||||
/* Add new things just above here ---^
|
||||
|
|
|
@ -37,6 +37,10 @@ typedef enum {
|
|||
LWSSEQ_TIMED_OUT, /* sequencer timeout */
|
||||
LWSSEQ_HEARTBEAT, /* 1Hz callback */
|
||||
|
||||
LWSSEQ_WSI_CONNECTED, /* wsi we bound to us has connected */
|
||||
LWSSEQ_WSI_CONN_FAIL, /* wsi we bound to us has failed to connect */
|
||||
LWSSEQ_WSI_CONN_CLOSE, /* wsi we bound to us has closed */
|
||||
|
||||
LWSSEQ_USER_BASE = 100 /* define your events from here */
|
||||
} lws_seq_events_t;
|
||||
|
||||
|
@ -61,6 +65,7 @@ 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);
|
||||
|
||||
|
||||
/**
|
||||
* lws_sequencer_create() - create and bind sequencer to a pt
|
||||
*
|
||||
|
@ -119,6 +124,25 @@ lws_sequencer_destroy(lws_sequencer_t **seq);
|
|||
LWS_VISIBLE LWS_EXTERN int
|
||||
lws_sequencer_event(lws_sequencer_t *seq, lws_seq_events_t e, void *data);
|
||||
|
||||
/**
|
||||
* lws_sequencer_check_wsi() - check if wsi still extant
|
||||
*
|
||||
* \param seq: the sequencer interested in the wsi
|
||||
* \param wsi: the wsi we want to confirm hasn't closed yet
|
||||
*
|
||||
* Check if wsi still extant, by peeking in the message queue for a
|
||||
* LWSSEQ_WSI_CONN_CLOSE message about wsi. (Doesn't need to do the same for
|
||||
* CONN_FAIL since that will never have produced any messages prior to that).
|
||||
*
|
||||
* Use this to avoid trying to perform operations on wsi that have already
|
||||
* closed but we didn't get to that message yet.
|
||||
*
|
||||
* Returns 0 if not closed yet or 1 if it has closed but we didn't process the
|
||||
* close message yet.
|
||||
*/
|
||||
LWS_VISIBLE LWS_EXTERN int
|
||||
lws_sequencer_check_wsi(lws_sequencer_t *seq, struct lws *wsi);
|
||||
|
||||
/**
|
||||
* lws_sequencer_timeout() - set a timeout by which the sequence must have
|
||||
* completed by a different event or inform the
|
||||
|
|
|
@ -91,6 +91,13 @@ callback_abs_client_raw_skt(struct lws *wsi, enum lws_callback_reasons reason,
|
|||
priv->established = 1;
|
||||
if (priv->abs->ap->accept)
|
||||
priv->abs->ap->accept(priv->abs->api);
|
||||
if (wsi->seq)
|
||||
/*
|
||||
* we are bound to a sequencer who wants to know about
|
||||
* our lifecycle events
|
||||
*/
|
||||
|
||||
lws_sequencer_event(wsi->seq, LWSSEQ_WSI_CONNECTED, wsi);
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
|
||||
|
@ -98,10 +105,32 @@ callback_abs_client_raw_skt(struct lws *wsi, enum lws_callback_reasons reason,
|
|||
if (in)
|
||||
lwsl_user(" %s\n", (const char *)in);
|
||||
|
||||
if (wsi->seq)
|
||||
/*
|
||||
* we are bound to a sequencer who wants to know about
|
||||
* our lifecycle events
|
||||
*/
|
||||
|
||||
lws_sequencer_event(wsi->seq, LWSSEQ_WSI_CONN_FAIL,
|
||||
wsi);
|
||||
|
||||
goto close_path;
|
||||
|
||||
/* fallthru */
|
||||
case LWS_CALLBACK_RAW_CLOSE:
|
||||
if (!user)
|
||||
break;
|
||||
|
||||
if (wsi->seq)
|
||||
/*
|
||||
* we are bound to a sequencer who wants to know about
|
||||
* our lifecycle events
|
||||
*/
|
||||
|
||||
lws_sequencer_event(wsi->seq, LWSSEQ_WSI_CONN_CLOSE,
|
||||
wsi);
|
||||
|
||||
close_path:
|
||||
lwsl_debug("LWS_CALLBACK_RAW_CLOSE\n");
|
||||
priv->established = 0;
|
||||
priv->connecting = 0;
|
||||
|
@ -237,6 +266,8 @@ lws_atcrs_client_conn(const lws_abs_t *abs)
|
|||
i.origin = i.address;
|
||||
i.context = abs->vh->context;
|
||||
i.local_protocol_name = "lws-abs-cli-raw-skt";
|
||||
i.seq = abs->seq;
|
||||
i.opaque_user_data = abs->opaque_user_data;
|
||||
|
||||
priv->wsi = lws_client_connect_via_info(&i);
|
||||
if (!priv->wsi)
|
||||
|
|
|
@ -70,6 +70,7 @@ lws_client_connect_via_info(const struct lws_client_connect_info *i)
|
|||
|
||||
wsi->context = i->context;
|
||||
wsi->desc.sockfd = LWS_SOCK_INVALID;
|
||||
wsi->seq = i->seq;
|
||||
|
||||
wsi->vhost = NULL;
|
||||
if (!i->vhost)
|
||||
|
|
|
@ -512,6 +512,8 @@ struct lws {
|
|||
const struct lws_protocols *protocol;
|
||||
struct lws_dll same_vh_protocol;
|
||||
|
||||
lws_sequencer_t *seq; /* associated sequencer if any */
|
||||
|
||||
struct lws_dll dll_timeout;
|
||||
struct lws_dll dll_hrtimer;
|
||||
struct lws_dll2 dll_buflist; /* guys with pending rxflow */
|
||||
|
|
|
@ -161,6 +161,42 @@ lws_sequencer_event(lws_sequencer_t *seq, lws_seq_events_t e, void *data)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if wsi still extant, by peeking in the message queue for a
|
||||
* LWSSEQ_WSI_CONN_CLOSE message about wsi. (Doesn't need to do the same for
|
||||
* CONN_FAIL since that will never have produced any messages prior to that).
|
||||
*
|
||||
* Use this to avoid trying to perform operations on wsi that have already
|
||||
* closed but we didn't get to that message yet.
|
||||
*
|
||||
* Returns 0 if not closed yet or 1 if it has closed but we didn't process the
|
||||
* close message yet.
|
||||
*/
|
||||
|
||||
int
|
||||
lws_sequencer_check_wsi(lws_sequencer_t *seq, struct lws *wsi)
|
||||
{
|
||||
lws_seq_event_t *seqe;
|
||||
struct lws_dll2 *dh;
|
||||
|
||||
lws_pt_lock(seq->pt, __func__); /* ----------------------------- pt { */
|
||||
|
||||
dh = lws_dll2_get_head(&seq->seq_event_owner);
|
||||
while (dh) {
|
||||
seqe = lws_container_of(dh, lws_seq_event_t, seq_event_list);
|
||||
|
||||
if (seqe->e == LWSSEQ_WSI_CONN_CLOSE && seqe->data == wsi)
|
||||
break;
|
||||
|
||||
dh = dh->next;
|
||||
}
|
||||
|
||||
lws_pt_unlock(seq->pt); /* } pt ------------------------------------- */
|
||||
|
||||
return !!dh;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* seq should have at least one pending event (he was on the pt's list of
|
||||
* sequencers with pending events). Send the top event in the queue.
|
||||
|
|
Loading…
Add table
Reference in a new issue