mirror of
https://github.com/warmcat/libwebsockets.git
synced 2025-03-30 00:00:16 +01:00
112 lines
4.8 KiB
Markdown
112 lines
4.8 KiB
Markdown
# `lws_sequencer_t` introduction
|
|
|
|
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
|
|
happening on the network with a particular connection on the event
|
|
loop thread.
|
|
|
|

|
|
|
|
`lws_sequencer_t` provides a generic way to stage multi-step
|
|
operations from inside the event loop. Because it participates
|
|
in the event loop similar to a wsi, it always operates from the
|
|
service thread context and can access structures that share the
|
|
service thread without locking. It can also provide its own
|
|
higher-level timeout handling.
|
|
|
|
Naturally you can have many of them running in the same event
|
|
loop operating independently.
|
|
|
|
Sequencers themselves bind to a pt (per-thread) service thread,
|
|
by default there's only one of these and it's the same as saying
|
|
they bind to an `lws_context`. The sequencer callback may create
|
|
wsi which in turn are bound to a vhost, but the sequencer itself
|
|
is above all that.
|
|
|
|
## Sequencer timeouts
|
|
|
|
The sequencer additionally maintains its own second-resolution timeout
|
|
checked by lws for the step being sequenced... this is independent of
|
|
any lws wsi timeouts which tend to be set and reset for very short-term
|
|
timeout protection inside one transaction.
|
|
|
|
The sequencer timeout operates separately and above any wsi timeout, and
|
|
is typically only reset by the sequencer callback when it receives an
|
|
event indicating a step completed or failed, or it sets up the next sequence
|
|
step.
|
|
|
|
If the sequencer timeout expires, then the sequencer receives a queued
|
|
`LWSSEQ_TIMED_OUT` message informing it, and it can take corrective action
|
|
or schedule a retry of the step. This message is queued and sent normally
|
|
under the service thread context and in order of receipt.
|
|
|
|
Unlike lws timeouts which force the wsi to close, the sequencer timeout
|
|
only sends the message. This allows the timeout to be used to, eg, wait
|
|
out a retry cooloff period and then start the retry when the
|
|
`LWSSEQ_TIMED_OUT` is received, according to the state of the sequencer.
|
|
|
|
## Creating an `lws_sequencer_t`
|
|
|
|
```
|
|
lws_sequencer_t *
|
|
lws_sequencer_create(struct lws_context *context, int tsi, void *user_data,
|
|
lws_seq_event_cb cb);
|
|
```
|
|
|
|
When created, in lws the sequencer objects are bound to a 'per-thread',
|
|
which is by default the same as to say bound to the `lws_context`. You
|
|
can tag them with an opaque user data pointer, and they are also bound to
|
|
a user-specified callback which handles sequencer events
|
|
|
|
```
|
|
typedef int (*lws_seq_event_cb)(struct lws_sequencer *seq, void *user_data,
|
|
lws_seq_events_t event, void *data);
|
|
```
|
|
|
|
`lws_sequencer_t` objects are private to lws and opaque to the user. A small
|
|
set of apis lets you perform operations on the pointer returned by the
|
|
create api.
|
|
|
|
## Queueing events on a sequencer
|
|
|
|
Each sequencer object can be passed "events", which are held on a per-sequencer
|
|
queue and handled strictly in the order they arrived on subsequent event loops.
|
|
`LWSSEQ_CREATED` and `LWSSEQ_DESTROYED` events are produced by lws reflecting
|
|
the sequencer's lifecycle, but otherwise the event indexes have a user-defined
|
|
meaning and are queued on the sequencer by user code for eventual consumption
|
|
by user code in the sequencer callback.
|
|
|
|
Pending events are removed from the sequencer queues and sent to the sequencer
|
|
callback from inside the event loop at a rate of one per event loop wait.
|
|
|
|
## Destroying sequencers
|
|
|
|
`lws_sequencer_t` objects are cleaned up during context destruction if they are
|
|
still around.
|
|
|
|
Normally the sequencer callback receives a queued message that
|
|
informs it that it's either failed at the current step, or succeeded and that
|
|
was the last step, and requests that it should be destroyed by returning
|
|
`LWSSEQ_RET_DESTROY` from the sequencer callback.
|
|
|
|
## Lifecycle considerations
|
|
|
|
Sequencers may spawn additional assets like client wsi as part of the sequenced
|
|
actions... the lifecycle of the sequencer and the assets overlap but do not
|
|
necessarily depend on each other... that is a wsi created by the sequencer may
|
|
outlive the sequencer.
|
|
|
|
It's important therefore to detach assets from the sequencer and the sequencer
|
|
from the assets when each step is over and the asset is "out of scope" for the
|
|
sequencer. It doesn't necessarily mean closing the assets, just making sure
|
|
pointers are invalidated. For example, if a client wsi held a pointer to the
|
|
sequencer as its `.user_data`, when the wsi is out of scope for the sequencer
|
|
it can set it to NULL, eg, `lws_set_wsi_user(wsi, NULL);`.
|
|
|
|
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.
|
|
|