Update Files
This commit is contained in:
8
Kinc/Sources/kinc/libs/system/smd/CMakeLists.txt
Normal file
8
Kinc/Sources/kinc/libs/system/smd/CMakeLists.txt
Normal file
@ -0,0 +1,8 @@
|
||||
include_directories(.)
|
||||
|
||||
list(APPEND SOURCES
|
||||
system/smd/smd.c
|
||||
)
|
||||
|
||||
exports_to_parent_scope()
|
||||
|
282
Kinc/Sources/kinc/libs/system/smd/README.md
Normal file
282
Kinc/Sources/kinc/libs/system/smd/README.md
Normal file
@ -0,0 +1,282 @@
|
||||
# LWS System Message Distribution
|
||||
|
||||
## Overview
|
||||
|
||||
Independent pieces of a system may need to become aware of events and state
|
||||
changes in the other pieces quickly, along with the new state if it is small.
|
||||
These messages are local to inside a system, although they may be triggered by
|
||||
events outside of it. Examples include keypresses, or networking state changes.
|
||||
Individual OSes and frameworks typically have their own fragmented apis for
|
||||
message-passing, but the lws apis operate the same across any platforms
|
||||
including, eg, Windows and RTOS and allow crossplatform code to be written once.
|
||||
|
||||
Message payloads are short, less than 384 bytes, below system limits for atomic
|
||||
pipe or UDS datagrams and consistent with heap usage on smaller systems, but
|
||||
large enough to carry JSON usefully. Messages are typically low duty cycle.
|
||||
|
||||

|
||||
|
||||
Messages may be sent by any registered participant, they are allocated on heap
|
||||
in a linked-list, and delivered to all other registered participants for that
|
||||
message class no sooner than next time around the event loop. This retains the
|
||||
ability to handle multiple event queuing in one event loop trip while
|
||||
guaranteeing message handling is nonrecursive and so with modest stack usage.
|
||||
Messages are passed to all other registered participants before being destroyed.
|
||||
|
||||
Messages are delivered to all particpants on the same lws_context by default.
|
||||
|
||||

|
||||
|
||||
`lws_smd` apis allow publication and subscription of message objects between
|
||||
participants that are in a single process and are informed by callback from lws
|
||||
service thread context.
|
||||
|
||||
SMD messages can also broadcast between particpants in different lws_contexts in
|
||||
different processes, using existing Secure Streams proxying. In this way
|
||||
different application processes can intercommunicate and all observe any system
|
||||
smd messages they are interested in.
|
||||
|
||||

|
||||
|
||||
Registering as a participant and sending messages are threadsafe APIs.
|
||||
|
||||
## Message Class
|
||||
|
||||
Message class is a bitfield messages use to indicate their general type, eg,
|
||||
network status, or UI event like a keypress. Participants set a bitmask to
|
||||
filter what kind of messages they care about, classes that are 0 in the peer's
|
||||
filter are never delivered to the peer. A message usually indicates it is a
|
||||
single class, but it's possible to set multiple class bits and match on any. If
|
||||
so, care must be taken the payload can be parsed by readers expecting any of the
|
||||
indicated classes, eg, by using JSON.
|
||||
|
||||
`lws_smd` tracks a global union mask for all participants' class mask. Requests
|
||||
to allocate a message of a class that no participant listens for are rejected,
|
||||
not at distribution-time but at message allocation-time, so no heap or cpu is
|
||||
wasted on things that are not currently interesting; but such messages start to
|
||||
appear as soon as a participant appears that wants them. The message generation
|
||||
action should be bypassed without error in the case lws_smd_msg_alloc()
|
||||
returns NULL.
|
||||
|
||||
Various well-known high level classes are defined but also a bit index
|
||||
`LWSSMDCL_USER_BASE_BITNUM`, which can be used by user code to define up to 8
|
||||
private classes, with class bit values `(1 << LWSSMDCL_USER_BASE_BITNUM)` thru
|
||||
`(1 << (LWSSMDCL_USER_BASE_BITNUM + 7))`
|
||||
|
||||
## Messaging guarantees
|
||||
|
||||
Sent messages are delivered to all registered participants whose class mask
|
||||
indicates they want it, including the sender. The send apis are threadsafe.
|
||||
|
||||
Locally-delivered message delivery callbacks occur from lws event loop thread
|
||||
context 0 (the only one in the default case `LWS_MAX_SMP` = 1). Clients in
|
||||
different processes receive callbacks from the thread context of their UDS
|
||||
networking thread.
|
||||
|
||||
The message payload may be destroyed immediately when you return from the
|
||||
callback, you can't store references to it or expect it to be there later.
|
||||
|
||||
Messages are timestamped with a systemwide monotonic timestamp. When
|
||||
participants are on the lws event loop, messages are delivered in-order. When
|
||||
participants are on different threads, delivery order depends on platform lock
|
||||
acquisition. External process participants are connected by the Unix Domain
|
||||
Socket capability of Secure Streams, and may be delivered out-of-order;
|
||||
receivers that care must consult the message creation timestamps.
|
||||
|
||||
## Message Refcounting
|
||||
|
||||
To avoid keeping a list of the length of the number of participants for each
|
||||
message, a refcount is used in the message, computed at the time the message
|
||||
arrived considering the number of active participants that indicated a desire to
|
||||
receive messages of that class.
|
||||
|
||||
Since peers may detach / close their link asynchronously, the logical peer
|
||||
objects at the distributor defer destroying themselves until there is no more
|
||||
possibility of messages arriving timestamped with the period they were active.
|
||||
A grace period (default 2s) is used to ensure departing peers correctly account
|
||||
for message refcounts before being destroyed.
|
||||
|
||||
## Message creation
|
||||
|
||||
Messages may contain arbitrary text or binary data depending on the class. JSON
|
||||
is recommended since lws_smd messages are small and low duty cycle but have
|
||||
open-ended content: JSON is maintainable, extensible, debuggable and self-
|
||||
documenting and avoids, eg, fragile dependencies on header versions shared
|
||||
between teams. To simplify issuing JSON, a threadsafe api to create and send
|
||||
messages in one step using format strings is provided:
|
||||
|
||||
```
|
||||
int
|
||||
lws_smd_msg_printf(struct lws_context *ctx, lws_smd_class_t _class,
|
||||
const char *format, ...);
|
||||
```
|
||||
|
||||
## Secure Streams `lws_smd` streamtype
|
||||
|
||||
When built with LWS_WITH_SECURE_STREAMS, lws_smd exposes a built-in streamtype
|
||||
`_lws_smd` which user Secure Streams may use to interoperate with lws_smd using
|
||||
SS payload semantics.
|
||||
|
||||
When using `_lws_smd`, the SS info struct member `manual_initial_tx_credit`
|
||||
provided by the user when creating the Secure Stream is overloaded to be used as
|
||||
the RX class mask for the SMD connection associated with the Secure Stream.
|
||||
|
||||
Both RX and TX payloads have a 16-byte binary header before the actual payload.
|
||||
For TX, although the header is 16-bytes, only the first 64-bit class bitfield
|
||||
needs setting, the timestamp is fetched and added by lws.
|
||||
|
||||
- MSB-first 64-bit class bitfield (currently only 32 least-sig in use)
|
||||
- MSB-First Order 64-bit us-resolution timestamp
|
||||
|
||||
A helper `lws_smd_ss_msg_printf()` is provided to format and create and smd
|
||||
message from the SS tx() callback in one step, using the same api layout as
|
||||
for direct messages via `lws_smd_msg_printf()`
|
||||
|
||||
```
|
||||
int
|
||||
lws_smd_ss_msg_printf(const char *tag, uint8_t *buf, size_t *len,
|
||||
lws_smd_class_t _class, const char *format, ...);
|
||||
```
|
||||
|
||||
## Well-known message schema
|
||||
|
||||
Class|Schema
|
||||
---|---
|
||||
LWSSMDCL_INTERACTION|lws_button events
|
||||
LWSSMDCL_NETWORK|captive portal detection requests and results
|
||||
LWSSMDCL_SYSTEM_STATE|lws_system state progression
|
||||
|
||||
### User interaction Button events
|
||||
|
||||
Class: `LWSSMDCL_INTERACTION`
|
||||
|
||||
Produced by lws_button when a user interacts with a defined button.
|
||||
|
||||
Click-related events are produced alongside up and down related events, the
|
||||
participant can choose which to attend to according to the meaning of the
|
||||
interaction.
|
||||
|
||||
Both kinds of event go through sophisticated filtering before being issued, see
|
||||
`./lib/drivers/button/README.md` for details.
|
||||
|
||||
#### SMD Button interaction event
|
||||
|
||||
Schema:
|
||||
```
|
||||
{
|
||||
"type": "button",
|
||||
"src": "<controller-name>/<button-name>",
|
||||
"event": "<event-name>"
|
||||
}
|
||||
```
|
||||
|
||||
For example, `{"type":"button","src":"bc/user","event":"doubleclick"}`
|
||||
|
||||
Event name|Meaning
|
||||
---|---
|
||||
down|The button passes a filter for being down, useful for duration-based response
|
||||
up|The button has come up, useful for duration-based response
|
||||
click|The button activity resulted in a classification as a single-click
|
||||
longclick|The button activity resulted in a classification as a long-click
|
||||
doubleclick|The button activity resulted in a classification as a double-click
|
||||
|
||||
### Routing Table Change
|
||||
|
||||
Class: `LWSSMDCL_NETWORK`
|
||||
|
||||
If able to subscribe to OS routing table changes (eg, by rtnetlink on Linux
|
||||
which is supported), lws announces there have been changes using SMD.
|
||||
|
||||
If Captive Portal Detect is enabled, and routing tables changes can be seen,
|
||||
then a new CPD is requested automatically and the results will be seen over SMD
|
||||
when that completes.
|
||||
|
||||
Schema:
|
||||
|
||||
```
|
||||
{
|
||||
"rt": "add|del", "add" if being added
|
||||
}
|
||||
```
|
||||
|
||||
When the context / pts are created, if linux then lws attempts to get the
|
||||
routing table sent, which requires root. This is done before the permissions
|
||||
are dropped after protocols init.
|
||||
|
||||
Lws maintains a cache of the routing table in each pt. Upon changes, existing
|
||||
connections are reassessed to see if their peer can still be routed to, if not
|
||||
the connection is closed.
|
||||
|
||||
If a gateway route changes, `{"trigger":"cpdcheck","src":"gw-change"}` is
|
||||
issued on SMD as well.
|
||||
|
||||
### Captive Portal Detection
|
||||
|
||||
Class: `LWSSMDCL_NETWORK`
|
||||
|
||||
Actively detects if the network can reach the internet or if it is
|
||||
intercepted by a captive portal. The detection steps are programmable
|
||||
via the Secure Streams Policy for a streamtype `captive_portal_detect`, eg
|
||||
|
||||
```
|
||||
"captive_portal_detect": {
|
||||
"endpoint": "connectivitycheck.android.com",
|
||||
"http_url": "generate_204",
|
||||
"port": 80,
|
||||
"protocol": "h1",
|
||||
"http_method": "GET",
|
||||
"opportunistic": true,
|
||||
"http_expect": 204,
|
||||
"http_fail_redirect": true
|
||||
}
|
||||
```
|
||||
|
||||
#### SMD Report Result
|
||||
|
||||
Schema: `{"type": "cpd", "result":"<result>"}`
|
||||
|
||||
result|meaning
|
||||
---|---
|
||||
OK|Internet is reachable
|
||||
Captive|Internet is behind a captive portal
|
||||
No internet|There is no connectivity
|
||||
|
||||
#### SMD Request re-detection
|
||||
|
||||
Schema: `{"trigger": "cpdcheck"}`
|
||||
|
||||
### lws_system state progression
|
||||
|
||||
Class: `LWSSMDCL_SYSTEM_STATE`
|
||||
|
||||
Lws system state changes are forwarded to lws_smd messages so participants not
|
||||
on the lws event loop directly can be aware of progress. Code registering a
|
||||
lws_system notifier callback, on the main lws loop, can synchronously veto state
|
||||
changes and hook proposed state changes, lws_smd events are asynchronous
|
||||
notifications of state changes after they were decided only... however they are
|
||||
available over the whole system.
|
||||
|
||||
It's not possible to make validated TLS connections until the system has
|
||||
acquired the date as well as acquired an IP on a non-captive portal connection,
|
||||
for that reason user code will usually be dependent on the system reaching
|
||||
"OPERATIONAL" state if lws is responsible for managing the boot process.
|
||||
|
||||
#### System state event
|
||||
|
||||
Schema: `{"state":"<state>"}"`
|
||||
|
||||
State|Meaning
|
||||
---|---
|
||||
CONTEXT_CREATED|We're creating the lws_context
|
||||
INITIALIZED|Initial vhosts and protocols initialized
|
||||
IFACE_COLDPLUG|Network interfaces discovered
|
||||
DHCP|DHCP acquired
|
||||
CPD_PRE_TIME|Captive portal detect hook before we have system time
|
||||
TIME_VALID|Ntpclient has run
|
||||
CPD_POST_TIME|Captive portal detect hook after system time (tls-based check)
|
||||
POLICY_VALID|The system policy has been acquired and parsed
|
||||
REGISTERED|This device is registered with an authority
|
||||
AUTH1|We acquired auth1 from the authority using our registration info
|
||||
AUTH2|We acquired auth2 from the authority using our registration info
|
||||
OPERATIONAL|We are active and able to make authenticated tls connections
|
||||
POLICY_INVALID|The policy is being changed
|
94
Kinc/Sources/kinc/libs/system/smd/private-lib-system-smd.h
Normal file
94
Kinc/Sources/kinc/libs/system/smd/private-lib-system-smd.h
Normal file
@ -0,0 +1,94 @@
|
||||
/*
|
||||
* lws System Message Distribution
|
||||
*
|
||||
* Copyright (C) 2019 - 2020 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#if defined(LWS_WITH_SECURE_STREAMS)
|
||||
#define LWS_SMD_SS_RX_HEADER_LEN_EFF (LWS_SMD_SS_RX_HEADER_LEN)
|
||||
#else
|
||||
#define LWS_SMD_SS_RX_HEADER_LEN_EFF (0)
|
||||
#endif
|
||||
|
||||
struct lws_smd_peer;
|
||||
|
||||
typedef struct lws_smd_msg {
|
||||
lws_dll2_t list;
|
||||
|
||||
struct lws_smd_peer *exc;
|
||||
|
||||
lws_usec_t timestamp;
|
||||
lws_smd_class_t _class;
|
||||
|
||||
uint16_t length;
|
||||
uint16_t refcount;
|
||||
|
||||
/* message itself is over-allocated after this */
|
||||
} lws_smd_msg_t;
|
||||
|
||||
typedef struct lws_smd_peer {
|
||||
lws_dll2_t list;
|
||||
|
||||
#if defined(LWS_WITH_SECURE_STREAMS)
|
||||
lws_ss_handle_t *ss_handle; /* LSMDT_SECURE_STREAMS */
|
||||
#endif
|
||||
|
||||
lws_smd_notification_cb_t cb; /* LSMDT_<other> */
|
||||
struct lws_context *ctx;
|
||||
void *opaque;
|
||||
|
||||
/* NULL, or next message we will handle */
|
||||
lws_smd_msg_t *tail;
|
||||
|
||||
lws_smd_class_t _class_filter;
|
||||
} lws_smd_peer_t;
|
||||
|
||||
/*
|
||||
* Manages message distribution
|
||||
*
|
||||
* There is one of these in the lws_context, but the distribution action also
|
||||
* gets involved in delivering to pt event loops individually for SMP case
|
||||
*/
|
||||
|
||||
typedef struct lws_smd {
|
||||
lws_dll2_owner_t owner_messages; /* lws_smd_msg_t */
|
||||
lws_mutex_t lock_messages;
|
||||
lws_dll2_owner_t owner_peers; /* lws_smd_peer_t */
|
||||
lws_mutex_t lock_peers;
|
||||
|
||||
/* union of peer class filters, suppress creation of msg classes not set */
|
||||
lws_smd_class_t _class_filter;
|
||||
|
||||
char delivering;
|
||||
} lws_smd_t;
|
||||
|
||||
/* check if this tsi has pending messages to deliver */
|
||||
|
||||
int
|
||||
lws_smd_message_pending(struct lws_context *ctx);
|
||||
|
||||
int
|
||||
lws_smd_msg_distribute(struct lws_context *ctx);
|
||||
|
||||
int
|
||||
_lws_smd_destroy(struct lws_context *ctx);
|
||||
|
803
Kinc/Sources/kinc/libs/system/smd/smd.c
Normal file
803
Kinc/Sources/kinc/libs/system/smd/smd.c
Normal file
@ -0,0 +1,803 @@
|
||||
/*
|
||||
* lws System Message Distribution
|
||||
*
|
||||
* Copyright (C) 2019 - 2021 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "private-lib-core.h"
|
||||
#include <assert.h>
|
||||
|
||||
/* comment me to remove extra debug and sanity checks */
|
||||
// #define LWS_SMD_DEBUG
|
||||
|
||||
|
||||
#if defined(LWS_SMD_DEBUG)
|
||||
#define lwsl_smd lwsl_notice
|
||||
#else
|
||||
#define lwsl_smd(_s, ...)
|
||||
#endif
|
||||
|
||||
void *
|
||||
lws_smd_msg_alloc(struct lws_context *ctx, lws_smd_class_t _class, size_t len)
|
||||
{
|
||||
lws_smd_msg_t *msg;
|
||||
|
||||
/* only allow it if someone wants to consume this class of event */
|
||||
|
||||
if (!(ctx->smd._class_filter & _class)) {
|
||||
lwsl_cx_info(ctx, "rejecting class 0x%x as no participant wants",
|
||||
(unsigned int)_class);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
assert(len <= LWS_SMD_MAX_PAYLOAD);
|
||||
|
||||
|
||||
/*
|
||||
* If SS configured, over-allocate LWS_SMD_SS_RX_HEADER_LEN behind
|
||||
* payload, ie, msg_t (gap LWS_SMD_SS_RX_HEADER_LEN) payload
|
||||
*/
|
||||
msg = lws_malloc(sizeof(*msg) + LWS_SMD_SS_RX_HEADER_LEN_EFF + len,
|
||||
__func__);
|
||||
if (!msg)
|
||||
return NULL;
|
||||
|
||||
memset(msg, 0, sizeof(*msg));
|
||||
msg->timestamp = lws_now_usecs();
|
||||
msg->length = (uint16_t)len;
|
||||
msg->_class = _class;
|
||||
|
||||
return ((uint8_t *)&msg[1]) + LWS_SMD_SS_RX_HEADER_LEN_EFF;
|
||||
}
|
||||
|
||||
void
|
||||
lws_smd_msg_free(void **ppay)
|
||||
{
|
||||
lws_smd_msg_t *msg = (lws_smd_msg_t *)(((uint8_t *)*ppay) -
|
||||
LWS_SMD_SS_RX_HEADER_LEN_EFF - sizeof(*msg));
|
||||
|
||||
/* if SS configured, actual alloc is LWS_SMD_SS_RX_HEADER_LEN behind */
|
||||
lws_free(msg);
|
||||
*ppay = NULL;
|
||||
}
|
||||
|
||||
#if defined(LWS_SMD_DEBUG)
|
||||
static void
|
||||
lws_smd_dump(lws_smd_t *smd)
|
||||
{
|
||||
int n = 1;
|
||||
|
||||
lws_start_foreach_dll_safe(struct lws_dll2 *, p, p1,
|
||||
smd->owner_messages.head) {
|
||||
lws_smd_msg_t *msg = lws_container_of(p, lws_smd_msg_t, list);
|
||||
|
||||
lwsl_info(" msg %d: %p: ref %d, lat %dms, cls: 0x%x, len %u: '%s'\n",
|
||||
n++, msg, msg->refcount,
|
||||
(unsigned int)((lws_now_usecs() - msg->timestamp) / 1000),
|
||||
msg->length, msg->_class,
|
||||
(const char *)&msg[1] + LWS_SMD_SS_RX_HEADER_LEN_EFF);
|
||||
|
||||
} lws_end_foreach_dll_safe(p, p1);
|
||||
|
||||
n = 1;
|
||||
lws_start_foreach_dll(struct lws_dll2 *, p, smd->owner_peers.head) {
|
||||
lws_smd_peer_t *pr = lws_container_of(p, lws_smd_peer_t, list);
|
||||
|
||||
lwsl_info(" peer %d: %p: tail: %p, filt 0x%x\n",
|
||||
n++, pr, pr->tail, pr->_class_filter);
|
||||
} lws_end_foreach_dll(p);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int
|
||||
_lws_smd_msg_peer_interested_in_msg(lws_smd_peer_t *pr, lws_smd_msg_t *msg)
|
||||
{
|
||||
return !!(msg->_class & pr->_class_filter);
|
||||
}
|
||||
|
||||
/*
|
||||
* Figure out what to set the initial refcount for the message to
|
||||
*/
|
||||
|
||||
static int
|
||||
_lws_smd_msg_assess_peers_interested(lws_smd_t *smd, lws_smd_msg_t *msg,
|
||||
struct lws_smd_peer *exc)
|
||||
{
|
||||
struct lws_context *ctx = lws_container_of(smd, struct lws_context, smd);
|
||||
int interested = 0;
|
||||
|
||||
lws_start_foreach_dll(struct lws_dll2 *, p, ctx->smd.owner_peers.head) {
|
||||
lws_smd_peer_t *pr = lws_container_of(p, lws_smd_peer_t, list);
|
||||
|
||||
if (pr != exc && _lws_smd_msg_peer_interested_in_msg(pr, msg))
|
||||
/*
|
||||
* This peer wants to consume it
|
||||
*/
|
||||
interested++;
|
||||
|
||||
} lws_end_foreach_dll(p);
|
||||
|
||||
return interested;
|
||||
}
|
||||
|
||||
static int
|
||||
_lws_smd_class_mask_union(lws_smd_t *smd)
|
||||
{
|
||||
uint32_t mask = 0;
|
||||
|
||||
lws_start_foreach_dll_safe(struct lws_dll2 *, p, p1,
|
||||
smd->owner_peers.head) {
|
||||
lws_smd_peer_t *pr = lws_container_of(p, lws_smd_peer_t, list);
|
||||
|
||||
mask |= pr->_class_filter;
|
||||
|
||||
} lws_end_foreach_dll_safe(p, p1);
|
||||
|
||||
smd->_class_filter = mask;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Call with message lock held */
|
||||
|
||||
static void
|
||||
_lws_smd_msg_destroy(struct lws_context *cx, lws_smd_t *smd, lws_smd_msg_t *msg)
|
||||
{
|
||||
/*
|
||||
* We think we gave the message to everyone and can destroy it.
|
||||
* Sanity check that no peer holds a pointer to this guy
|
||||
*/
|
||||
|
||||
lws_start_foreach_dll_safe(struct lws_dll2 *, p, p1,
|
||||
smd->owner_peers.head) {
|
||||
lws_smd_peer_t *xpr = lws_container_of(p, lws_smd_peer_t, list);
|
||||
|
||||
if (xpr->tail == msg) {
|
||||
lwsl_cx_err(cx, "peer %p has msg %p "
|
||||
"we are about to destroy as tail", xpr, msg);
|
||||
#if !defined(LWS_PLAT_FREERTOS)
|
||||
assert(0);
|
||||
#endif
|
||||
}
|
||||
|
||||
} lws_end_foreach_dll_safe(p, p1);
|
||||
|
||||
/*
|
||||
* We have fully delivered the message now, it
|
||||
* can be unlinked and destroyed
|
||||
*/
|
||||
lwsl_cx_info(cx, "destroy msg %p", msg);
|
||||
lws_dll2_remove(&msg->list);
|
||||
lws_free(msg);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is wanting to be threadsafe, limiting the apis we can call
|
||||
*/
|
||||
|
||||
int
|
||||
_lws_smd_msg_send(struct lws_context *ctx, void *pay, struct lws_smd_peer *exc)
|
||||
{
|
||||
lws_smd_msg_t *msg = (lws_smd_msg_t *)(((uint8_t *)pay) -
|
||||
LWS_SMD_SS_RX_HEADER_LEN_EFF - sizeof(*msg));
|
||||
|
||||
if (ctx->smd.owner_messages.count >= ctx->smd_queue_depth) {
|
||||
lwsl_cx_warn(ctx, "rejecting message on queue depth %d",
|
||||
(int)ctx->smd.owner_messages.count);
|
||||
/* reject the message due to max queue depth reached */
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!ctx->smd.delivering &&
|
||||
lws_mutex_lock(ctx->smd.lock_peers)) /* +++++++++++++++ peers */
|
||||
return 1; /* For Coverity */
|
||||
|
||||
if (lws_mutex_lock(ctx->smd.lock_messages)) /* +++++++++++++++++ messages */
|
||||
goto bail;
|
||||
|
||||
msg->refcount = (uint16_t)_lws_smd_msg_assess_peers_interested(
|
||||
&ctx->smd, msg, exc);
|
||||
if (!msg->refcount) {
|
||||
/* possible, condsidering exc and no other participants */
|
||||
lws_mutex_unlock(ctx->smd.lock_messages); /* --------------- messages */
|
||||
|
||||
lws_free(msg);
|
||||
if (!ctx->smd.delivering)
|
||||
lws_mutex_unlock(ctx->smd.lock_peers); /* ------------- peers */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
msg->exc = exc;
|
||||
|
||||
/* let's add him on the queue... */
|
||||
|
||||
lws_dll2_add_tail(&msg->list, &ctx->smd.owner_messages);
|
||||
|
||||
/*
|
||||
* Any peer with no active tail needs to check our class to see if we
|
||||
* should become his tail
|
||||
*/
|
||||
|
||||
lws_start_foreach_dll(struct lws_dll2 *, p, ctx->smd.owner_peers.head) {
|
||||
lws_smd_peer_t *pr = lws_container_of(p, lws_smd_peer_t, list);
|
||||
|
||||
if (pr != exc &&
|
||||
!pr->tail && _lws_smd_msg_peer_interested_in_msg(pr, msg)) {
|
||||
pr->tail = msg;
|
||||
/* tail message has to actually be of interest to the peer */
|
||||
assert(!pr->tail || (pr->tail->_class & pr->_class_filter));
|
||||
}
|
||||
|
||||
} lws_end_foreach_dll(p);
|
||||
|
||||
#if defined(LWS_SMD_DEBUG)
|
||||
lwsl_smd("%s: added %p (refc %u) depth now %d\n", __func__,
|
||||
msg, msg->refcount, ctx->smd.owner_messages.count);
|
||||
lws_smd_dump(&ctx->smd);
|
||||
#endif
|
||||
|
||||
lws_mutex_unlock(ctx->smd.lock_messages); /* --------------- messages */
|
||||
|
||||
bail:
|
||||
if (!ctx->smd.delivering)
|
||||
lws_mutex_unlock(ctx->smd.lock_peers); /* ------------- peers */
|
||||
|
||||
/* we may be happening from another thread context */
|
||||
lws_cancel_service(ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is wanting to be threadsafe, limiting the apis we can call
|
||||
*/
|
||||
|
||||
int
|
||||
lws_smd_msg_send(struct lws_context *ctx, void *pay)
|
||||
{
|
||||
return _lws_smd_msg_send(ctx, pay, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is wanting to be threadsafe, limiting the apis we can call
|
||||
*/
|
||||
|
||||
int
|
||||
lws_smd_msg_printf(struct lws_context *ctx, lws_smd_class_t _class,
|
||||
const char *format, ...)
|
||||
{
|
||||
lws_smd_msg_t *msg;
|
||||
va_list ap;
|
||||
void *p;
|
||||
int n;
|
||||
|
||||
if (!(ctx->smd._class_filter & _class))
|
||||
/*
|
||||
* There's nobody interested in messages of this class atm.
|
||||
* Don't bother generating it, and act like all is well.
|
||||
*/
|
||||
return 0;
|
||||
|
||||
va_start(ap, format);
|
||||
n = vsnprintf(NULL, 0, format, ap);
|
||||
va_end(ap);
|
||||
if (n > LWS_SMD_MAX_PAYLOAD)
|
||||
/* too large to send */
|
||||
return 1;
|
||||
|
||||
p = lws_smd_msg_alloc(ctx, _class, (size_t)n + 2);
|
||||
if (!p)
|
||||
return 1;
|
||||
msg = (lws_smd_msg_t *)(((uint8_t *)p) - LWS_SMD_SS_RX_HEADER_LEN_EFF -
|
||||
sizeof(*msg));
|
||||
msg->length = (uint16_t)n;
|
||||
va_start(ap, format);
|
||||
vsnprintf((char *)p, (unsigned int)n + 2, format, ap);
|
||||
va_end(ap);
|
||||
|
||||
/*
|
||||
* locks taken and released in here
|
||||
*/
|
||||
|
||||
if (lws_smd_msg_send(ctx, p)) {
|
||||
lws_smd_msg_free(&p);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(LWS_WITH_SECURE_STREAMS)
|
||||
int
|
||||
lws_smd_ss_msg_printf(const char *tag, uint8_t *buf, size_t *len,
|
||||
lws_smd_class_t _class, const char *format, ...)
|
||||
{
|
||||
char *content = (char *)buf + LWS_SMD_SS_RX_HEADER_LEN;
|
||||
va_list ap;
|
||||
int n;
|
||||
|
||||
if (*len < LWS_SMD_SS_RX_HEADER_LEN)
|
||||
return 1;
|
||||
|
||||
lws_ser_wu64be(buf, _class);
|
||||
lws_ser_wu64be(buf + 8, 0); /* valgrind notices uninitialized if left */
|
||||
|
||||
va_start(ap, format);
|
||||
n = vsnprintf(content, (*len) - LWS_SMD_SS_RX_HEADER_LEN, format, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (n > LWS_SMD_MAX_PAYLOAD ||
|
||||
(unsigned int)n > (*len) - LWS_SMD_SS_RX_HEADER_LEN)
|
||||
/* too large to send */
|
||||
return 1;
|
||||
|
||||
*len = LWS_SMD_SS_RX_HEADER_LEN + (unsigned int)n;
|
||||
|
||||
lwsl_info("%s: %s send cl 0x%x, len %u\n", __func__, tag, (unsigned int)_class,
|
||||
(unsigned int)n);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is a helper that user rx handler for LWS_SMD_STREAMTYPENAME SS can
|
||||
* call through to with the payload it received from the proxy. It will then
|
||||
* forward the recieved SMD message to all local (same-context) participants
|
||||
* that are interested in that class (except ones with callback skip_cb, so
|
||||
* we don't loop).
|
||||
*/
|
||||
|
||||
static int
|
||||
_lws_smd_ss_rx_forward(struct lws_context *ctx, const char *tag,
|
||||
struct lws_smd_peer *pr, const uint8_t *buf, size_t len)
|
||||
{
|
||||
lws_smd_class_t _class;
|
||||
lws_smd_msg_t *msg;
|
||||
void *p;
|
||||
|
||||
if (len < LWS_SMD_SS_RX_HEADER_LEN_EFF)
|
||||
return 1;
|
||||
|
||||
if (len >= LWS_SMD_MAX_PAYLOAD + LWS_SMD_SS_RX_HEADER_LEN_EFF)
|
||||
return 1;
|
||||
|
||||
_class = (lws_smd_class_t)lws_ser_ru64be(buf);
|
||||
|
||||
//if (_class == LWSSMDCL_METRICS) {
|
||||
|
||||
//}
|
||||
|
||||
/* only locally forward messages that we care about in this process */
|
||||
|
||||
if (!(ctx->smd._class_filter & _class))
|
||||
/*
|
||||
* There's nobody interested in messages of this class atm.
|
||||
* Don't bother generating it, and act like all is well.
|
||||
*/
|
||||
return 0;
|
||||
|
||||
p = lws_smd_msg_alloc(ctx, _class, len);
|
||||
if (!p)
|
||||
return 1;
|
||||
|
||||
msg = (lws_smd_msg_t *)(((uint8_t *)p) - LWS_SMD_SS_RX_HEADER_LEN_EFF -
|
||||
sizeof(*msg));
|
||||
msg->length = (uint16_t)(len - LWS_SMD_SS_RX_HEADER_LEN_EFF);
|
||||
/* adopt the original source timestamp, not time we forwarded it */
|
||||
msg->timestamp = (lws_usec_t)lws_ser_ru64be(buf + 8);
|
||||
|
||||
/* copy the message payload in */
|
||||
memcpy(p, buf + LWS_SMD_SS_RX_HEADER_LEN_EFF, msg->length);
|
||||
|
||||
/*
|
||||
* locks taken and released in here
|
||||
*/
|
||||
|
||||
if (_lws_smd_msg_send(ctx, p, pr)) {
|
||||
/* we couldn't send it after all that... */
|
||||
lws_smd_msg_free(&p);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
lwsl_info("%s: %s send cl 0x%x, len %u, ts %llu\n", __func__,
|
||||
tag, (unsigned int)_class, msg->length,
|
||||
(unsigned long long)msg->timestamp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
lws_smd_ss_rx_forward(void *ss_user, const uint8_t *buf, size_t len)
|
||||
{
|
||||
struct lws_ss_handle *h = (struct lws_ss_handle *)
|
||||
(((char *)ss_user) - sizeof(*h));
|
||||
struct lws_context *ctx = lws_ss_get_context(h);
|
||||
|
||||
return _lws_smd_ss_rx_forward(ctx, lws_ss_tag(h), h->u.smd.smd_peer, buf, len);
|
||||
}
|
||||
|
||||
#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API)
|
||||
int
|
||||
lws_smd_sspc_rx_forward(void *ss_user, const uint8_t *buf, size_t len)
|
||||
{
|
||||
struct lws_sspc_handle *h = (struct lws_sspc_handle *)
|
||||
(((char *)ss_user) - sizeof(*h));
|
||||
struct lws_context *ctx = lws_sspc_get_context(h);
|
||||
|
||||
return _lws_smd_ss_rx_forward(ctx, lws_sspc_tag(h), NULL, buf, len);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Peers that deregister need to adjust the refcount of messages they would
|
||||
* have been interested in, but didn't take delivery of yet
|
||||
*/
|
||||
|
||||
static void
|
||||
_lws_smd_peer_destroy(lws_smd_peer_t *pr)
|
||||
{
|
||||
lws_smd_t *smd = lws_container_of(pr->list.owner, lws_smd_t,
|
||||
owner_peers);
|
||||
|
||||
if (lws_mutex_lock(smd->lock_messages)) /* +++++++++ messages */
|
||||
return; /* For Coverity */
|
||||
|
||||
lws_dll2_remove(&pr->list);
|
||||
|
||||
/*
|
||||
* We take the approach to adjust the refcount of every would-have-been
|
||||
* delivered message we were interested in
|
||||
*/
|
||||
|
||||
while (pr->tail) {
|
||||
|
||||
lws_smd_msg_t *m1 = lws_container_of(pr->tail->list.next,
|
||||
lws_smd_msg_t, list);
|
||||
|
||||
if (_lws_smd_msg_peer_interested_in_msg(pr, pr->tail)) {
|
||||
if (!--pr->tail->refcount)
|
||||
_lws_smd_msg_destroy(pr->ctx, smd, pr->tail);
|
||||
}
|
||||
|
||||
pr->tail = m1;
|
||||
}
|
||||
|
||||
lws_free(pr);
|
||||
|
||||
lws_mutex_unlock(smd->lock_messages); /* messages ------- */
|
||||
}
|
||||
|
||||
static lws_smd_msg_t *
|
||||
_lws_smd_msg_next_matching_filter(lws_smd_peer_t *pr)
|
||||
{
|
||||
lws_dll2_t *tail = &pr->tail->list;
|
||||
lws_smd_msg_t *msg;
|
||||
|
||||
do {
|
||||
tail = tail->next;
|
||||
if (!tail)
|
||||
return NULL;
|
||||
|
||||
msg = lws_container_of(tail, lws_smd_msg_t, list);
|
||||
if (msg->exc != pr &&
|
||||
_lws_smd_msg_peer_interested_in_msg(pr, msg))
|
||||
return msg;
|
||||
} while (1);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Delivers only one message to the peer and advances the tail, or sets to NULL
|
||||
* if no more filtered queued messages. Returns nonzero if tail non-NULL.
|
||||
*
|
||||
* For Proxied SS, only asks for writeable and does not advance or change the
|
||||
* tail.
|
||||
*
|
||||
* This is done so if multiple messages queued, we don't get a situation where
|
||||
* one participant gets them all spammed, then the next etc. Instead they are
|
||||
* delivered round-robin.
|
||||
*
|
||||
* Requires peer lock, may take message lock
|
||||
*/
|
||||
|
||||
static int
|
||||
_lws_smd_msg_deliver_peer(struct lws_context *ctx, lws_smd_peer_t *pr)
|
||||
{
|
||||
lws_smd_msg_t *msg;
|
||||
|
||||
if (!pr->tail)
|
||||
return 0;
|
||||
|
||||
msg = lws_container_of(pr->tail, lws_smd_msg_t, list);
|
||||
|
||||
lwsl_cx_info(ctx, "deliver cl 0x%x, len %d, to peer %p",
|
||||
(unsigned int)msg->_class, (int)msg->length,
|
||||
pr);
|
||||
|
||||
pr->cb(pr->opaque, msg->_class, msg->timestamp,
|
||||
((uint8_t *)&msg[1]) + LWS_SMD_SS_RX_HEADER_LEN_EFF,
|
||||
(size_t)msg->length);
|
||||
#if !defined(__COVERITY__)
|
||||
assert(msg->refcount);
|
||||
#endif
|
||||
/*
|
||||
* If there is one, move forward to the next queued
|
||||
* message that meets the filters of this peer
|
||||
*/
|
||||
pr->tail = _lws_smd_msg_next_matching_filter(pr);
|
||||
|
||||
/* tail message has to actually be of interest to the peer */
|
||||
assert(!pr->tail || (pr->tail->_class & pr->_class_filter));
|
||||
|
||||
if (lws_mutex_lock(ctx->smd.lock_messages)) /* +++++++++ messages */
|
||||
return 1; /* For Coverity */
|
||||
|
||||
if (!--msg->refcount)
|
||||
_lws_smd_msg_destroy(ctx, &ctx->smd, msg);
|
||||
lws_mutex_unlock(ctx->smd.lock_messages); /* messages ------- */
|
||||
|
||||
return !!pr->tail;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called when the event loop could deliver messages synchronously, eg, on
|
||||
* entry to idle
|
||||
*/
|
||||
|
||||
int
|
||||
lws_smd_msg_distribute(struct lws_context *ctx)
|
||||
{
|
||||
char more;
|
||||
|
||||
/* commonly, no messages and nothing to do... */
|
||||
|
||||
if (!ctx->smd.owner_messages.count)
|
||||
return 0;
|
||||
|
||||
ctx->smd.delivering = 1;
|
||||
|
||||
do {
|
||||
more = 0;
|
||||
if (lws_mutex_lock(ctx->smd.lock_peers)) /* +++++++++++++++ peers */
|
||||
return 1; /* For Coverity */
|
||||
|
||||
lws_start_foreach_dll_safe(struct lws_dll2 *, p, p1,
|
||||
ctx->smd.owner_peers.head) {
|
||||
lws_smd_peer_t *pr = lws_container_of(p, lws_smd_peer_t, list);
|
||||
|
||||
more = (char)(more | !!_lws_smd_msg_deliver_peer(ctx, pr));
|
||||
|
||||
} lws_end_foreach_dll_safe(p, p1);
|
||||
|
||||
lws_mutex_unlock(ctx->smd.lock_peers); /* ------------- peers */
|
||||
} while (more);
|
||||
|
||||
ctx->smd.delivering = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct lws_smd_peer *
|
||||
lws_smd_register(struct lws_context *ctx, void *opaque, int flags,
|
||||
lws_smd_class_t _class_filter, lws_smd_notification_cb_t cb)
|
||||
{
|
||||
lws_smd_peer_t *pr = lws_zalloc(sizeof(*pr), __func__);
|
||||
|
||||
if (!pr)
|
||||
return NULL;
|
||||
|
||||
pr->cb = cb;
|
||||
pr->opaque = opaque;
|
||||
pr->_class_filter = _class_filter;
|
||||
pr->ctx = ctx;
|
||||
|
||||
if (!ctx->smd.delivering &&
|
||||
lws_mutex_lock(ctx->smd.lock_peers)) { /* +++++++++++++++ peers */
|
||||
lws_free(pr);
|
||||
return NULL; /* For Coverity */
|
||||
}
|
||||
|
||||
/*
|
||||
* Let's lock the message list before adding this peer... because...
|
||||
*/
|
||||
|
||||
if (lws_mutex_lock(ctx->smd.lock_messages)) { /* +++++++++ messages */
|
||||
lws_free(pr);
|
||||
pr = NULL;
|
||||
goto bail1; /* For Coverity */
|
||||
}
|
||||
|
||||
lws_dll2_add_tail(&pr->list, &ctx->smd.owner_peers);
|
||||
|
||||
/* update the global class mask union to account for new peer mask */
|
||||
_lws_smd_class_mask_union(&ctx->smd);
|
||||
|
||||
/*
|
||||
* Now there's a new peer added, any messages we have stashed will try
|
||||
* to deliver to this guy too, if he's interested in that class. So we
|
||||
* have to update the message refcounts for queued messages-he's-
|
||||
* interested-in accordingly.
|
||||
*/
|
||||
|
||||
lws_start_foreach_dll_safe(struct lws_dll2 *, p, p1,
|
||||
ctx->smd.owner_messages.head) {
|
||||
lws_smd_msg_t *msg = lws_container_of(p, lws_smd_msg_t, list);
|
||||
|
||||
if (_lws_smd_msg_peer_interested_in_msg(pr, msg))
|
||||
msg->refcount++;
|
||||
|
||||
} lws_end_foreach_dll_safe(p, p1);
|
||||
|
||||
/* ... ok we are done adding the peer */
|
||||
|
||||
lws_mutex_unlock(ctx->smd.lock_messages); /* messages ------- */
|
||||
|
||||
lwsl_cx_info(ctx, "peer %p (count %u) registered", pr,
|
||||
(unsigned int)ctx->smd.owner_peers.count);
|
||||
|
||||
bail1:
|
||||
if (!ctx->smd.delivering)
|
||||
lws_mutex_unlock(ctx->smd.lock_peers); /* ------------- peers */
|
||||
|
||||
return pr;
|
||||
}
|
||||
|
||||
void
|
||||
lws_smd_unregister(struct lws_smd_peer *pr)
|
||||
{
|
||||
lws_smd_t *smd = lws_container_of(pr->list.owner, lws_smd_t, owner_peers);
|
||||
|
||||
if (!smd->delivering &&
|
||||
lws_mutex_lock(smd->lock_peers)) /* +++++++++++++++++++ peers */
|
||||
return; /* For Coverity */
|
||||
lwsl_cx_notice(pr->ctx, "destroying peer %p", pr);
|
||||
_lws_smd_peer_destroy(pr);
|
||||
if (!smd->delivering)
|
||||
lws_mutex_unlock(smd->lock_peers); /* ----------------- peers */
|
||||
}
|
||||
|
||||
int
|
||||
lws_smd_message_pending(struct lws_context *ctx)
|
||||
{
|
||||
int ret = 1;
|
||||
|
||||
/*
|
||||
* First cheaply check the common case no messages pending, so there's
|
||||
* definitely nothing for this tsi or anything else
|
||||
*/
|
||||
|
||||
if (!ctx->smd.owner_messages.count)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* If there are any messages, check their age and expire ones that
|
||||
* have been hanging around too long
|
||||
*/
|
||||
|
||||
if (lws_mutex_lock(ctx->smd.lock_peers)) /* +++++++++++++++++++++++ peers */
|
||||
return 1; /* For Coverity */
|
||||
if (lws_mutex_lock(ctx->smd.lock_messages)) /* +++++++++++++++++ messages */
|
||||
goto bail; /* For Coverity */
|
||||
|
||||
lws_start_foreach_dll_safe(struct lws_dll2 *, p, p1,
|
||||
ctx->smd.owner_messages.head) {
|
||||
lws_smd_msg_t *msg = lws_container_of(p, lws_smd_msg_t, list);
|
||||
|
||||
if ((lws_now_usecs() - msg->timestamp) > ctx->smd_ttl_us) {
|
||||
lwsl_cx_warn(ctx, "timing out queued message %p",
|
||||
msg);
|
||||
|
||||
/*
|
||||
* We're forcibly yanking this guy, we can expect that
|
||||
* there might be peers that point to it as their tail.
|
||||
*
|
||||
* In that case, move their tails on to the next guy
|
||||
* they are interested in, if any.
|
||||
*/
|
||||
|
||||
lws_start_foreach_dll_safe(struct lws_dll2 *, pp, pp1,
|
||||
ctx->smd.owner_peers.head) {
|
||||
lws_smd_peer_t *pr = lws_container_of(pp,
|
||||
lws_smd_peer_t, list);
|
||||
|
||||
if (pr->tail == msg)
|
||||
pr->tail = _lws_smd_msg_next_matching_filter(pr);
|
||||
|
||||
} lws_end_foreach_dll_safe(pp, pp1);
|
||||
|
||||
/*
|
||||
* No peer should fall foul of the peer tail checks
|
||||
* when destroying the message now.
|
||||
*/
|
||||
|
||||
_lws_smd_msg_destroy(ctx, &ctx->smd, msg);
|
||||
}
|
||||
} lws_end_foreach_dll_safe(p, p1);
|
||||
|
||||
lws_mutex_unlock(ctx->smd.lock_messages); /* --------------- messages */
|
||||
|
||||
/*
|
||||
* Walk the peer list
|
||||
*/
|
||||
|
||||
lws_start_foreach_dll(struct lws_dll2 *, p, ctx->smd.owner_peers.head) {
|
||||
lws_smd_peer_t *pr = lws_container_of(p, lws_smd_peer_t, list);
|
||||
|
||||
if (pr->tail)
|
||||
goto bail;
|
||||
|
||||
} lws_end_foreach_dll(p);
|
||||
|
||||
/*
|
||||
* There's no message pending that we need to handle
|
||||
*/
|
||||
|
||||
ret = 0;
|
||||
|
||||
bail:
|
||||
lws_mutex_unlock(ctx->smd.lock_peers); /* --------------------- peers */
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
_lws_smd_destroy(struct lws_context *ctx)
|
||||
{
|
||||
/* stop any message creation */
|
||||
|
||||
ctx->smd._class_filter = 0;
|
||||
|
||||
/*
|
||||
* Walk the message list, destroying them
|
||||
*/
|
||||
|
||||
lws_start_foreach_dll_safe(struct lws_dll2 *, p, p1,
|
||||
ctx->smd.owner_messages.head) {
|
||||
lws_smd_msg_t *msg = lws_container_of(p, lws_smd_msg_t, list);
|
||||
|
||||
lws_dll2_remove(&msg->list);
|
||||
lws_free(msg);
|
||||
|
||||
} lws_end_foreach_dll_safe(p, p1);
|
||||
|
||||
/*
|
||||
* Walk the peer list, destroying them
|
||||
*/
|
||||
|
||||
lws_start_foreach_dll_safe(struct lws_dll2 *, p, p1,
|
||||
ctx->smd.owner_peers.head) {
|
||||
lws_smd_peer_t *pr = lws_container_of(p, lws_smd_peer_t, list);
|
||||
|
||||
pr->tail = NULL; /* we just nuked all the messages, ignore */
|
||||
_lws_smd_peer_destroy(pr);
|
||||
|
||||
} lws_end_foreach_dll_safe(p, p1);
|
||||
|
||||
lws_mutex_destroy(ctx->smd.lock_messages);
|
||||
lws_mutex_destroy(ctx->smd.lock_peers);
|
||||
|
||||
return 0;
|
||||
}
|
Reference in New Issue
Block a user