From 4b84a1b35cad3b8b70b91dca60c84e5b8d5a0c90 Mon Sep 17 00:00:00 2001 From: Erik Boasson Date: Tue, 21 Mar 2023 11:15:21 +0100 Subject: [PATCH 01/11] Replace special-cased timed events by callbacks This necessitates some changes: * Extending the callbacks with an "xpack" parameter that was available to the special-cased functions for sending data out from a handler. * Making synchronisation between execution of the callback and deleting the event optional, to not introduce (unnecessary) blocking where it wasn't there before. * Changing the closure argument to a blob that is copied into the event, rather than an arbitrary pointer. The problem is a need to avoid races with the garbage collector which makes it impossible to store the pointer to an entity without introducing additional stages in the deletion process. This in turn forces an additional indirection in the "old" callbacks. * The internal functions heavily relied on the availability of a "gv" pointer, this could easily be emulated via the closure argument, but it seems to make more sense to pass it as an argument. This allows moving a lot of handler code to more reasonable places, which in turn made "ddsi_discovery" explode too messy to survive without splitting it. Signed-off-by: Erik Boasson --- src/core/ddsc/tests/deadline.c | 10 +- src/core/ddsc/tests/nwpart.c | 1 + src/core/ddsi/CMakeLists.txt | 16 + src/core/ddsi/include/dds/ddsi/ddsi_xevent.h | 18 +- src/core/ddsi/src/ddsi__discovery.h | 35 +- src/core/ddsi/src/ddsi__discovery_addrset.h | 75 + src/core/ddsi/src/ddsi__discovery_endpoint.h | 62 + src/core/ddsi/src/ddsi__discovery_sedp.h | 57 + src/core/ddsi/src/ddsi__discovery_spdp.h | 65 + src/core/ddsi/src/ddsi__discovery_topic.h | 49 + src/core/ddsi/src/ddsi__pmd.h | 8 + src/core/ddsi/src/ddsi__transmit.h | 2 + src/core/ddsi/src/ddsi__xevent.h | 23 +- src/core/ddsi/src/ddsi_deadline.c | 16 +- src/core/ddsi/src/ddsi_discovery.c | 1886 +---------------- src/core/ddsi/src/ddsi_discovery_addrset.c | 252 +++ src/core/ddsi/src/ddsi_discovery_endpoint.c | 593 ++++++ src/core/ddsi/src/ddsi_discovery_sedp.c | 212 ++ src/core/ddsi/src/ddsi_discovery_spdp.c | 858 ++++++++ src/core/ddsi/src/ddsi_discovery_topic.c | 177 ++ src/core/ddsi/src/ddsi_endpoint.c | 216 +- src/core/ddsi/src/ddsi_endpoint_match.c | 130 +- src/core/ddsi/src/ddsi_init.c | 23 +- src/core/ddsi/src/ddsi_lifespan.c | 16 +- src/core/ddsi/src/ddsi_participant.c | 12 +- src/core/ddsi/src/ddsi_pmd.c | 38 +- src/core/ddsi/src/ddsi_security_exchange.c | 2 +- src/core/ddsi/src/ddsi_security_omg.c | 18 +- src/core/ddsi/src/ddsi_topic.c | 3 + src/core/ddsi/src/ddsi_transmit.c | 9 + src/core/ddsi/src/ddsi_xevent.c | 784 +------ src/security/core/src/dds_security_timed_cb.c | 16 +- 32 files changed, 3011 insertions(+), 2671 deletions(-) create mode 100644 src/core/ddsi/src/ddsi__discovery_addrset.h create mode 100644 src/core/ddsi/src/ddsi__discovery_endpoint.h create mode 100644 src/core/ddsi/src/ddsi__discovery_sedp.h create mode 100644 src/core/ddsi/src/ddsi__discovery_spdp.h create mode 100644 src/core/ddsi/src/ddsi__discovery_topic.h create mode 100644 src/core/ddsi/src/ddsi_discovery_addrset.c create mode 100644 src/core/ddsi/src/ddsi_discovery_endpoint.c create mode 100644 src/core/ddsi/src/ddsi_discovery_sedp.c create mode 100644 src/core/ddsi/src/ddsi_discovery_spdp.c create mode 100644 src/core/ddsi/src/ddsi_discovery_topic.c diff --git a/src/core/ddsc/tests/deadline.c b/src/core/ddsc/tests/deadline.c index 8da9a90972..3321437830 100644 --- a/src/core/ddsc/tests/deadline.c +++ b/src/core/ddsc/tests/deadline.c @@ -446,9 +446,11 @@ CU_Theory((int32_t n_inst, uint8_t unreg_nth, uint8_t dispose_nth), ddsc_deadlin //deadline callback function, this function's purpose is to delay the monitor thread such that while instance's //deadline may expire, the event thread is blocked by this function, and updates to instances are "queued" if they happen //during this block. these queued updates happen the next time the expiration callbacks fire -static void cb (struct ddsi_xevent *xev, void *ptr, ddsrt_mtime_t tm) +static void cb (struct ddsi_domaingv *gv, struct ddsi_xevent *xev, struct ddsi_xpack *xp, void *ptr, ddsrt_mtime_t tm) { + (void) gv; (void) xev; + (void) xp; (void) ptr; (void) tm; dds_sleepfor(DEADLINE); @@ -544,11 +546,11 @@ CU_Test(ddsc_deadline, update) } struct ddsi_domaingv *gvptr = get_domaingv (wr); - struct ddsi_xevent *xev = ddsi_qxev_callback( + struct ddsi_xevent *xev = ddsi_qxev_callback ( gvptr->xevents, ddsrt_mtime_add_duration(ddsrt_time_monotonic(), (dds_duration_t)(0.5*DEADLINE)), cb, - NULL); //this should sleep the thread that updates the statuses from 0.5*DEADLINE to 1.5*DEADLINE + NULL, 0, true); //this should sleep the thread that updates the statuses from 0.5*DEADLINE to 1.5*DEADLINE CU_ASSERT_FATAL(xev != NULL); Space_Type1 msg1 = { 1, 0, 0 }, @@ -599,7 +601,7 @@ CU_Test(ddsc_deadline, update) //msg1 should have expired 3 times, msg2 should have expired 2 times check_statuses(wr, rd, expired_1, expired_2, tw1, tw2, ih1, ih2); - ddsi_delete_xevent_callback(xev); + ddsi_delete_xevent(xev); dds_delete(pp); } diff --git a/src/core/ddsc/tests/nwpart.c b/src/core/ddsc/tests/nwpart.c index 64e6255a0d..b7897a7e47 100644 --- a/src/core/ddsc/tests/nwpart.c +++ b/src/core/ddsc/tests/nwpart.c @@ -27,6 +27,7 @@ #include "ddsi__misc.h" #include "ddsi__addrset.h" #include "ddsi__discovery.h" +#include "ddsi__discovery_endpoint.h" #include "ddsi__plist.h" #include "ddsi__tran.h" diff --git a/src/core/ddsi/CMakeLists.txt b/src/core/ddsi/CMakeLists.txt index 07e6f65b17..eda89d3194 100644 --- a/src/core/ddsi/CMakeLists.txt +++ b/src/core/ddsi/CMakeLists.txt @@ -64,6 +64,10 @@ set(srcs_ddsi ddsi_guid.c ddsi_bswap.c ddsi_discovery.c + ddsi_discovery_addrset.c + ddsi_discovery_spdp.c + ddsi_discovery_sedp.c + ddsi_discovery_endpoint.c ddsi_debmon.c ddsi_init.c ddsi_lat_estim.c @@ -188,6 +192,10 @@ set(hdrs_private_ddsi ddsi__bitset.h ddsi__bswap.h ddsi__discovery.h + ddsi__discovery_addrset.h + ddsi__discovery_spdp.h + ddsi__discovery_sedp.h + ddsi__discovery_endpoint.h ddsi__debmon.h ddsi__hbcontrol.h ddsi__inverse_uint32_set.h @@ -222,6 +230,14 @@ if(ENABLE_DEADLINE_MISSED) ddsi_deadline.h ) endif() +if(ENABLE_TOPIC_DISCOVERY) + list(APPEND srcs_ddsi + ddsi_discovery_topic.c + ) + list(APPEND hdrs_private_ddsi + ddsi__discovery_topic.h + ) +endif() if(ENABLE_TYPE_DISCOVERY) list(APPEND srcs_ddsi ddsi_xt_typeinfo.c diff --git a/src/core/ddsi/include/dds/ddsi/ddsi_xevent.h b/src/core/ddsi/include/dds/ddsi/ddsi_xevent.h index 0605cee657..4a122dc53c 100644 --- a/src/core/ddsi/include/dds/ddsi/ddsi_xevent.h +++ b/src/core/ddsi/include/dds/ddsi/ddsi_xevent.h @@ -22,19 +22,17 @@ extern "C" { struct ddsi_xevent; struct ddsi_xeventq; -/** - * @component timed_events - * @remark: locks EVQ for the duration of the operation - * @param ev the event - */ -void ddsi_delete_xevent (struct ddsi_xevent *ev); +struct ddsi_domaingv; +struct ddsi_xpack; + +typedef void (*ddsi_xevent_cb_t) (struct ddsi_domaingv *gv, struct ddsi_xevent *ev, struct ddsi_xpack *xp, void *arg, ddsrt_mtime_t tnow); /** * @component timed_events * @remark: locks EVQ for the duration of the operation * @param ev the event */ -void ddsi_delete_xevent_callback (struct ddsi_xevent *ev); +void ddsi_delete_xevent (struct ddsi_xevent *ev); /** * @component timed_events @@ -46,15 +44,17 @@ int ddsi_resched_xevent_if_earlier (struct ddsi_xevent *ev, ddsrt_mtime_t tsched /** * @component timed_events * + * @remark: delete does not block until an ongoing callback finishes * @remark: cb will be called with now = NEVER if the event is still enqueued when when ddsi_xeventq_free starts cleaning up * * @param evq event queue * @param tsched timestamp scheduled * @param cb callback function pointer - * @param arg arguments for the callback + * @param arg argument (copied into ddsi_xevent) + * @param arg_size size of argument * @return struct ddsi_xevent* */ -struct ddsi_xevent *ddsi_qxev_callback (struct ddsi_xeventq *evq, ddsrt_mtime_t tsched, void (*cb) (struct ddsi_xevent *xev, void *arg, ddsrt_mtime_t now), void *arg); +struct ddsi_xevent *ddsi_qxev_callback (struct ddsi_xeventq *evq, ddsrt_mtime_t tsched, ddsi_xevent_cb_t cb, const void *arg, size_t arg_size, bool sync_on_delete); #if defined (__cplusplus) } diff --git a/src/core/ddsi/src/ddsi__discovery.h b/src/core/ddsi/src/ddsi__discovery.h index 121cadbd5a..2ab98f18af 100644 --- a/src/core/ddsi/src/ddsi__discovery.h +++ b/src/core/ddsi/src/ddsi__discovery.h @@ -26,38 +26,9 @@ struct ddsi_reader; struct ddsi_rsample_info; struct ddsi_rdata; struct ddsi_plist; - -struct ddsi_participant_builtin_topic_data_locators { - struct ddsi_locators_one def_uni[MAX_XMIT_CONNS], meta_uni[MAX_XMIT_CONNS]; - struct ddsi_locators_one def_multi, meta_multi; -}; - -/** @component discovery */ -void ddsi_get_participant_builtin_topic_data (const struct ddsi_participant *pp, ddsi_plist_t *dst, struct ddsi_participant_builtin_topic_data_locators *locs); - -/** @component discovery */ -struct ddsi_addrset *ddsi_get_endpoint_addrset (const struct ddsi_domaingv *gv, const ddsi_plist_t *datap, struct ddsi_addrset *proxypp_as_default, const ddsi_locator_t *rst_srcloc); - -/** @component discovery */ -int ddsi_spdp_write (struct ddsi_participant *pp); - -/** @component discovery */ -int ddsi_spdp_dispose_unregister (struct ddsi_participant *pp); - -/** @component discovery */ -int ddsi_sedp_write_topic (struct ddsi_topic *tp, bool alive); - -/** @component discovery */ -int ddsi_sedp_write_writer (struct ddsi_writer *wr); - -/** @component discovery */ -int ddsi_sedp_write_reader (struct ddsi_reader *rd); - -/** @component discovery */ -int ddsi_sedp_dispose_unregister_writer (struct ddsi_writer *wr); - -/** @component discovery */ -int ddsi_sedp_dispose_unregister_reader (struct ddsi_reader *rd); +struct ddsi_xevent; +struct ddsi_xpack; +struct ddsi_domaingv; /** @component discovery */ int ddsi_builtins_dqueue_handler (const struct ddsi_rsample_info *sampleinfo, const struct ddsi_rdata *fragchain, const ddsi_guid_t *rdguid, void *qarg); diff --git a/src/core/ddsi/src/ddsi__discovery_addrset.h b/src/core/ddsi/src/ddsi__discovery_addrset.h new file mode 100644 index 0000000000..ced4f80932 --- /dev/null +++ b/src/core/ddsi/src/ddsi__discovery_addrset.h @@ -0,0 +1,75 @@ +/* + * Copyright(c) 2023 ZettaScale Technology and others + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License + * v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ +#ifndef DDSI__DISCOVERY_ADDRSET_H +#define DDSI__DISCOVERY_ADDRSET_H + +#include "dds/ddsi/ddsi_domaingv.h" // FIXME: MAX_XMIT_CONNS + +#if defined (__cplusplus) +extern "C" { +#endif + +typedef struct ddsi_interface_set { + bool xs[MAX_XMIT_CONNS]; +} ddsi_interface_set_t; + +/** @brief Initializes an interface set to all-false + * @component discovery + * + * @param[out] intfs interface set to initialize */ +void ddsi_interface_set_init (ddsi_interface_set_t *intfs) + ddsrt_nonnull_all; + +/** @brief Whether multicast locators are to be included in discovery information for this domain + * @component discovery + * + * @param[in] gv domain + * @return true iff multicast locators are to be included */ +bool ddsi_include_multicast_locator_in_discovery (const struct ddsi_domaingv *gv) + ddsrt_nonnull_all; + +/** @brief Constructs a new address set from uni- and multicast locators received in SPDP or SEDP + * @component discovery + * + * The construction process uses heuristics for determining which interfaces appear to be applicable for and uses + * this information to set (1) the transmit sockets and (2) choose the interfaces with which to associate multicast + * addresses. + * + * Loopback addresses are accepted if it can be determined that they originate on the same machine: + * - if all enabled interfaces are loopback interfaces, the peer must be on the same host (this ought to be cached) + * - if all advertised addresses are loopback addresses + * - if there is a non-unicast address that matches one of the (enabled) addresses of the host + * + * Unicast addresses are matched against interface addresses to determine whether the address is likely to be + * reachable without any routing. If so, the address is assigned to the interface and the interface is marked as + * "enabled" for the purposes of multicast handling. If not, it is associated with the first enabled non-loopback + * interface on the assumption that unicast routing works fine (but the interface is not "enabled" for multicast + * handling). + * + * Multicast addresses are added only for interfaces that are "enabled" based on unicast processing. If none are + * and the source locator matches an interface, it will enable that interface. + * + * @param[in] gv domain state, needed for interfaces, transports, tracing + * @param[in] uc list of advertised unicast locators + * @param[in] mc list of advertised multicast locators + * @param[in] srcloc source address for discovery packet, or "invalid" + * @param[in,out] inherited_intfs set of applicable interfaces, may be NULL + * + * @return new addrset, possibly empty */ +struct ddsi_addrset *ddsi_addrset_from_locatorlists (const struct ddsi_domaingv *gv, const ddsi_locators_t *uc, const ddsi_locators_t *mc, const ddsi_locator_t *srcloc, const ddsi_interface_set_t *inherited_intfs) + ddsrt_attribute_warn_unused_result ddsrt_nonnull((1,2,3,4)); + +#if defined (__cplusplus) +} +#endif + +#endif /* DDSI__DISCOVERY_ADDRSET_H */ diff --git a/src/core/ddsi/src/ddsi__discovery_endpoint.h b/src/core/ddsi/src/ddsi__discovery_endpoint.h new file mode 100644 index 0000000000..e4e295740f --- /dev/null +++ b/src/core/ddsi/src/ddsi__discovery_endpoint.h @@ -0,0 +1,62 @@ +/* + * Copyright(c) 2006 to 2021 ZettaScale Technology and others + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License + * v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ +#ifndef DDSI__DISCOVERY_ENDPOINT_H +#define DDSI__DISCOVERY_ENDPOINT_H + +#include "dds/ddsi/ddsi_unused.h" +#include "dds/ddsi/ddsi_domaingv.h" +#include "ddsi__discovery_sedp.h" + +#if defined (__cplusplus) +extern "C" { +#endif + +struct ddsi_participant; +struct ddsi_topic; +struct ddsi_writer; +struct ddsi_reader; +struct ddsi_rsample_info; +struct ddsi_rdata; +struct ddsi_plist; +struct ddsi_xevent; +struct ddsi_xpack; +struct ddsi_domaingv; + +/** @component discovery */ +struct ddsi_addrset *ddsi_get_endpoint_addrset (const struct ddsi_domaingv *gv, const ddsi_plist_t *datap, struct ddsi_addrset *proxypp_as_default, const ddsi_locator_t *rst_srcloc) + ddsrt_attribute_warn_unused_result ddsrt_nonnull((1,2,3)); + +/** @component discovery */ +int ddsi_sedp_write_writer (struct ddsi_writer *wr) ddsrt_nonnull_all; + +/** @component discovery */ +int ddsi_sedp_write_reader (struct ddsi_reader *rd) ddsrt_nonnull_all; + +/** @component discovery */ +int ddsi_sedp_dispose_unregister_writer (struct ddsi_writer *wr) ddsrt_nonnull_all; + +/** @component discovery */ +int ddsi_sedp_dispose_unregister_reader (struct ddsi_reader *rd) ddsrt_nonnull_all; + +/** @component discovery */ +void ddsi_handle_sedp_alive_endpoint (const struct ddsi_receiver_state *rst, ddsi_seqno_t seq, ddsi_plist_t *datap /* note: potentially modifies datap */, ddsi_sedp_kind_t sedp_kind, const ddsi_guid_prefix_t *src_guid_prefix, ddsi_vendorid_t vendorid, ddsrt_wctime_t timestamp) + ddsrt_nonnull_all; + +/** @component discovery */ +void ddsi_handle_sedp_dead_endpoint (const struct ddsi_receiver_state *rst, ddsi_plist_t *datap, ddsi_sedp_kind_t sedp_kind, ddsrt_wctime_t timestamp) + ddsrt_nonnull_all; + +#if defined (__cplusplus) +} +#endif + +#endif /* DDSI__DISCOVERY_ENDPOINT_H */ diff --git a/src/core/ddsi/src/ddsi__discovery_sedp.h b/src/core/ddsi/src/ddsi__discovery_sedp.h new file mode 100644 index 0000000000..911ef64b5a --- /dev/null +++ b/src/core/ddsi/src/ddsi__discovery_sedp.h @@ -0,0 +1,57 @@ +/* + * Copyright(c) 2006 to 2021 ZettaScale Technology and others + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License + * v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ +#ifndef DDSI__DISCOVERY_SEDP_H +#define DDSI__DISCOVERY_SEDP_H + +#include "dds/ddsi/ddsi_domaingv.h" + +#if defined (__cplusplus) +extern "C" { +#endif + +struct ddsi_receiver_state; +struct ddsi_serdata; +struct ddsi_proxy_participant; + +typedef enum ddsi_sedp_kind { + SEDP_KIND_READER, + SEDP_KIND_WRITER, + SEDP_KIND_TOPIC +} ddsi_sedp_kind_t; + +/** @component discovery */ +struct ddsi_writer *ddsi_get_sedp_writer (const struct ddsi_participant *pp, unsigned entityid) + ddsrt_nonnull_all; + +/** @component discovery */ +struct ddsi_proxy_participant *ddsi_implicitly_create_proxypp (struct ddsi_domaingv *gv, const ddsi_guid_t *ppguid, ddsi_plist_t *datap /* note: potentially modifies datap */, const ddsi_guid_prefix_t *src_guid_prefix, ddsi_vendorid_t vendorid, ddsrt_wctime_t timestamp, ddsi_seqno_t seq) + ddsrt_nonnull_all; + +/** @component discovery */ +bool ddsi_check_sedp_kind_and_guid (ddsi_sedp_kind_t sedp_kind, const ddsi_guid_t *entity_guid) + ddsrt_nonnull_all; + +/** @component discovery */ +bool ddsi_handle_sedp_checks (struct ddsi_domaingv * const gv, ddsi_sedp_kind_t sedp_kind, ddsi_guid_t *entity_guid, ddsi_plist_t *datap, + const ddsi_guid_prefix_t *src_guid_prefix, ddsi_vendorid_t vendorid, ddsrt_wctime_t timestamp, + struct ddsi_proxy_participant **proxypp, ddsi_guid_t *ppguid) + ddsrt_nonnull_all; + +/** @component discovery */ +void ddsi_handle_sedp (const struct ddsi_receiver_state *rst, ddsi_seqno_t seq, struct ddsi_serdata *serdata, ddsi_sedp_kind_t sedp_kind) + ddsrt_nonnull_all; + +#if defined (__cplusplus) +} +#endif + +#endif /* DDSI__DISCOVERY_SEDP_H */ diff --git a/src/core/ddsi/src/ddsi__discovery_spdp.h b/src/core/ddsi/src/ddsi__discovery_spdp.h new file mode 100644 index 0000000000..bd4dbd24cf --- /dev/null +++ b/src/core/ddsi/src/ddsi__discovery_spdp.h @@ -0,0 +1,65 @@ +/* + * Copyright(c) 2023 ZettaScale Technology and others + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License + * v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ +#ifndef DDSI__DISCOVERY_SPDP_H +#define DDSI__DISCOVERY_SPDP_H + +#include "dds/ddsi/ddsi_unused.h" +#include "dds/ddsi/ddsi_domaingv.h" // FIXME: MAX_XMIT_CONNS + +#if defined (__cplusplus) +extern "C" { +#endif + +struct ddsi_participant; +struct ddsi_topic; +struct ddsi_writer; +struct ddsi_reader; +struct ddsi_rsample_info; +struct ddsi_rdata; +struct ddsi_plist; +struct ddsi_xevent; +struct ddsi_xpack; +struct ddsi_domaingv; + +struct ddsi_receiver_state; +struct ddsi_serdata; + +struct ddsi_participant_builtin_topic_data_locators { + struct ddsi_locators_one def_uni[MAX_XMIT_CONNS], meta_uni[MAX_XMIT_CONNS]; + struct ddsi_locators_one def_multi, meta_multi; +}; + +/** @component discovery */ +void ddsi_get_participant_builtin_topic_data (const struct ddsi_participant *pp, ddsi_plist_t *dst, struct ddsi_participant_builtin_topic_data_locators *locs) + ddsrt_nonnull_all; + +/** @component discovery */ +int ddsi_spdp_write (struct ddsi_participant *pp); + +/** @component discovery */ +int ddsi_spdp_dispose_unregister (struct ddsi_participant *pp); + +struct ddsi_xevent_spdp_broadcast_cb_arg { + ddsi_guid_t pp_guid; +}; + +/** @component discovery */ +void ddsi_xevent_spdp_broadcast_cb (struct ddsi_domaingv *gv, struct ddsi_xevent *ev, UNUSED_ARG (struct ddsi_xpack *xp), void *varg, ddsrt_mtime_t tnow); + +/** @component discovery */ +void ddsi_handle_spdp (const struct ddsi_receiver_state *rst, ddsi_entityid_t pwr_entityid, ddsi_seqno_t seq, const struct ddsi_serdata *serdata); + +#if defined (__cplusplus) +} +#endif + +#endif /* DDSI__DISCOVERY_SPDP_H */ diff --git a/src/core/ddsi/src/ddsi__discovery_topic.h b/src/core/ddsi/src/ddsi__discovery_topic.h new file mode 100644 index 0000000000..e35efde7b3 --- /dev/null +++ b/src/core/ddsi/src/ddsi__discovery_topic.h @@ -0,0 +1,49 @@ +/* + * Copyright(c) 2023 ZettaScale Technology and others + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License + * v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ +#ifndef DDSI__DISCOVERY_TOPIC_H +#define DDSI__DISCOVERY_TOPIC_H + +#include "dds/ddsi/ddsi_unused.h" +#include "dds/ddsi/ddsi_domaingv.h" +#include "ddsi__discovery_sedp.h" + +#if defined (__cplusplus) +extern "C" { +#endif + +struct ddsi_participant; +struct ddsi_topic; +struct ddsi_writer; +struct ddsi_reader; +struct ddsi_rsample_info; +struct ddsi_rdata; +struct ddsi_plist; +struct ddsi_xevent; +struct ddsi_xpack; +struct ddsi_domaingv; + +/** @component discovery */ +int ddsi_sedp_write_topic (struct ddsi_topic *tp, bool alive) ddsrt_nonnull_all; + +/** @component discovery */ +void ddsi_handle_sedp_alive_topic (const struct ddsi_receiver_state *rst, ddsi_seqno_t seq, ddsi_plist_t *datap /* note: potentially modifies datap */, const ddsi_guid_prefix_t *src_guid_prefix, ddsi_vendorid_t vendorid, ddsrt_wctime_t timestamp) + ddsrt_nonnull_all; + +/** @component discovery */ +void ddsi_handle_sedp_dead_topic (const struct ddsi_receiver_state *rst, ddsi_plist_t *datap, ddsrt_wctime_t timestamp) + ddsrt_nonnull_all; + +#if defined (__cplusplus) +} +#endif + +#endif /* DDSI__DISCOVERY_TOPIC_H */ diff --git a/src/core/ddsi/src/ddsi__pmd.h b/src/core/ddsi/src/ddsi__pmd.h index c8379062ca..10f0b65c48 100644 --- a/src/core/ddsi/src/ddsi__pmd.h +++ b/src/core/ddsi/src/ddsi__pmd.h @@ -27,6 +27,7 @@ extern "C" { struct ddsi_domaingv; struct ddsi_thread_state; struct ddsi_guid; +struct ddsi_xevent; struct ddsi_xpack; struct ddsi_participant; struct ddsi_receiver_state; @@ -48,6 +49,13 @@ void ddsi_write_pmd_message (struct ddsi_thread_state * const ts1, struct ddsi_x /** @component pmd */ void ddsi_handle_pmd_message (const struct ddsi_receiver_state *rst, struct ddsi_serdata *sample_common); +struct ddsi_xevent_write_pmd_message_cb_arg { + ddsi_guid_t pp_guid; +}; + +/** @component pmd */ +void ddsi_xevent_write_pmd_message_cb (struct ddsi_domaingv *gv, struct ddsi_xevent *ev, struct ddsi_xpack *xp, void *varg, ddsrt_mtime_t tnow); + #if defined (__cplusplus) } #endif diff --git a/src/core/ddsi/src/ddsi__transmit.h b/src/core/ddsi/src/ddsi__transmit.h index aa2be7dc64..74513b7a2a 100644 --- a/src/core/ddsi/src/ddsi__transmit.h +++ b/src/core/ddsi/src/ddsi__transmit.h @@ -44,6 +44,8 @@ int ddsi_write_sample_gc_notk (struct ddsi_thread_state * const thrst, struct dd /** @component outgoing_rtps */ int ddsi_write_sample_nogc_notk (struct ddsi_thread_state * const thrst, struct ddsi_xpack *xp, struct ddsi_writer *wr, struct ddsi_serdata *serdata); +/** @component outgoing_rtps */ +int ddsi_write_and_fini_plist (struct ddsi_writer *wr, ddsi_plist_t *ps, bool alive); /* When calling the following functions, wr->lock must be held */ diff --git a/src/core/ddsi/src/ddsi__xevent.h b/src/core/ddsi/src/ddsi__xevent.h index 1fba889591..8d5bdcf4d7 100644 --- a/src/core/ddsi/src/ddsi__xevent.h +++ b/src/core/ddsi/src/ddsi__xevent.h @@ -74,21 +74,14 @@ enum ddsi_qxev_msg_rexmit_result { /** @component timed_events */ enum ddsi_qxev_msg_rexmit_result ddsi_qxev_msg_rexmit_wrlock_held (struct ddsi_xeventq *evq, struct ddsi_xmsg *msg, int force); -/** @component timed_events */ -struct ddsi_xevent *ddsi_qxev_heartbeat (struct ddsi_xeventq *evq, ddsrt_mtime_t tsched, const ddsi_guid_t *wr_guid); - -/** @component timed_events */ -struct ddsi_xevent *ddsi_qxev_acknack (struct ddsi_xeventq *evq, ddsrt_mtime_t tsched, const ddsi_guid_t *pwr_guid, const ddsi_guid_t *rd_guid); - -/** @component timed_events */ -struct ddsi_xevent *ddsi_qxev_spdp (struct ddsi_xeventq *evq, ddsrt_mtime_t tsched, const ddsi_guid_t *pp_guid, const ddsi_guid_t *proxypp_guid); - -/** @component timed_events */ -struct ddsi_xevent *ddsi_qxev_pmd_update (struct ddsi_xeventq *evq, ddsrt_mtime_t tsched, const ddsi_guid_t *pp_guid); - -/** @component timed_events */ -struct ddsi_xevent *ddsi_qxev_delete_writer (struct ddsi_xeventq *evq, ddsrt_mtime_t tsched, const ddsi_guid_t *guid); - +#ifndef NDEBUG +/** + * @component timed_events + * @remark: locks EVQ for the duration of the operation + * @param ev the event + */ +bool ddsi_delete_xevent_pending (struct ddsi_xevent *ev); +#endif #if defined (__cplusplus) } diff --git a/src/core/ddsi/src/ddsi_deadline.c b/src/core/ddsi/src/ddsi_deadline.c index ebf3c8081f..378266e20c 100644 --- a/src/core/ddsi/src/ddsi_deadline.c +++ b/src/core/ddsi/src/ddsi_deadline.c @@ -16,9 +16,16 @@ #include "dds/ddsi/ddsi_deadline.h" #include "ddsi__xevent.h" -static void instance_deadline_missed_cb (struct ddsi_xevent *xev, void *varg, ddsrt_mtime_t tnow) +struct instance_deadline_missed_cb_arg { + struct ddsi_deadline_adm *deadline_adm; +}; + +static void instance_deadline_missed_cb (struct ddsi_domaingv *gv, struct ddsi_xevent *xev, struct ddsi_xpack *xp, void *varg, ddsrt_mtime_t tnow) { - struct ddsi_deadline_adm * const deadline_adm = varg; + (void) gv; + (void) xp; + struct instance_deadline_missed_cb_arg * const arg = varg; + struct ddsi_deadline_adm * const deadline_adm = arg->deadline_adm; const ddsrt_mtime_t next = deadline_adm->deadline_missed_cb ((char *)deadline_adm - deadline_adm->list_offset, tnow); // Rate-limit repeated deadline miss notifications. The first deadline miss following an // update of the instance is always handled by ddsi_deadline_register_instance_real and @@ -80,7 +87,8 @@ ddsrt_mtime_t ddsi_deadline_next_missed_locked (struct ddsi_deadline_adm *deadli void ddsi_deadline_init (const struct ddsi_domaingv *gv, struct ddsi_deadline_adm *deadline_adm, size_t list_offset, size_t elem_offset, deadline_missed_cb_t deadline_missed_cb) { ddsrt_circlist_init (&deadline_adm->list); - deadline_adm->evt = ddsi_qxev_callback (gv->xevents, DDSRT_MTIME_NEVER, instance_deadline_missed_cb, deadline_adm); + struct instance_deadline_missed_cb_arg arg = { .deadline_adm = deadline_adm }; + deadline_adm->evt = ddsi_qxev_callback (gv->xevents, DDSRT_MTIME_NEVER, instance_deadline_missed_cb, &arg, sizeof (arg), true); deadline_adm->deadline_missed_cb = deadline_missed_cb; deadline_adm->list_offset = list_offset; deadline_adm->elem_offset = elem_offset; @@ -88,7 +96,7 @@ void ddsi_deadline_init (const struct ddsi_domaingv *gv, struct ddsi_deadline_ad void ddsi_deadline_stop (const struct ddsi_deadline_adm *deadline_adm) { - ddsi_delete_xevent_callback (deadline_adm->evt); + ddsi_delete_xevent (deadline_adm->evt); } void ddsi_deadline_clear (struct ddsi_deadline_adm *deadline_adm) diff --git a/src/core/ddsi/src/ddsi_discovery.c b/src/core/ddsi/src/ddsi_discovery.c index 531e916df4..387d2b131a 100644 --- a/src/core/ddsi/src/ddsi_discovery.c +++ b/src/core/ddsi/src/ddsi_discovery.c @@ -30,1859 +30,40 @@ #include "ddsi__misc.h" #include "ddsi__xevent.h" #include "ddsi__discovery.h" -#include "ddsi__serdata_plist.h" -#include "ddsi__radmin.h" -#include "ddsi__entity_index.h" -#include "ddsi__entity.h" -#include "ddsi__participant.h" -#include "ddsi__xmsg.h" -#include "ddsi__transmit.h" -#include "ddsi__lease.h" -#include "ddsi__security_omg.h" -#include "ddsi__pmd.h" -#include "ddsi__endpoint.h" -#include "ddsi__plist.h" -#include "ddsi__proxy_endpoint.h" -#include "ddsi__proxy_participant.h" -#include "ddsi__topic.h" -#include "ddsi__tran.h" -#include "ddsi__typelib.h" -#include "ddsi__vendor.h" -#include "ddsi__xqos.h" -#include "ddsi__addrset.h" -#ifdef DDS_HAS_TYPE_DISCOVERY -#include "ddsi__typelookup.h" -#endif - -#ifdef DDS_HAS_SECURITY -#include "ddsi__security_exchange.h" -#endif - -typedef enum ddsi_sedp_kind { - SEDP_KIND_READER, - SEDP_KIND_WRITER, - SEDP_KIND_TOPIC -} ddsi_sedp_kind_t; - -static void allowmulticast_aware_add_to_addrset (const struct ddsi_domaingv *gv, uint32_t allow_multicast, struct ddsi_addrset *as, const ddsi_xlocator_t *loc) -{ -#ifdef DDS_HAS_SSM - if (ddsi_is_ssm_mcaddr (gv, &loc->c)) - { - if (!(allow_multicast & DDSI_AMC_SSM)) - return; - } - else if (ddsi_is_mcaddr (gv, &loc->c)) - { - if (!(allow_multicast & DDSI_AMC_ASM)) - return; - } -#else - if (ddsi_is_mcaddr (gv, &loc->c) && !(allow_multicast & DDSI_AMC_ASM)) - return; -#endif - ddsi_add_xlocator_to_addrset (gv, as, loc); -} - -typedef struct interface_set { - bool xs[MAX_XMIT_CONNS]; -} interface_set_t; - -static void interface_set_init (interface_set_t *intfs) -{ - for (size_t i = 0; i < sizeof (intfs->xs) / sizeof (intfs->xs[0]); i++) - intfs->xs[i] = false; -} - -static void addrset_from_locatorlists_add_one (struct ddsi_domaingv const * const gv, const ddsi_locator_t *loc, struct ddsi_addrset *as, interface_set_t *intfs, bool *direct) -{ - size_t interf_idx; - switch (ddsi_is_nearby_address (gv, loc, (size_t) gv->n_interfaces, gv->interfaces, &interf_idx)) - { - case DNAR_SELF: - case DNAR_LOCAL: - // if it matches an interface, use that one and record that this is a - // directly connected interface: those will then all be possibilities - // for transmitting multicasts (assuming capable, allowed, &c.) - assert (interf_idx < MAX_XMIT_CONNS); - ddsi_add_xlocator_to_addrset (gv, as, &(const ddsi_xlocator_t) { - .conn = gv->xmit_conns[interf_idx], - .c = *loc }); - intfs->xs[interf_idx] = true; - *direct = true; - break; - case DNAR_DISTANT: - // If DONT_ROUTE is set and there is no matching interface, then presumably - // one would not be able to reach this address. - if (!gv->config.dontRoute) - { - // Pick the first selected interface that isn't link-local or loopback - // (maybe it matters, maybe not, but it doesn't make sense to assign - // a transmit socket for a local interface to a distant host). If none - // exists, skip the address. - for (int i = 0; i < gv->n_interfaces; i++) - { - // do not use link-local or loopback interfaces transmit conn for distant nodes - if (gv->interfaces[i].link_local || gv->interfaces[i].loopback) - continue; - ddsi_add_xlocator_to_addrset (gv, as, &(const ddsi_xlocator_t) { - .conn = gv->xmit_conns[i], - .c = *loc }); - break; - } - } - break; - case DNAR_UNREACHABLE: - break; - } -} - -/** @brief Constructs a new address set from uni- and multicast locators received in SPDP or SEDP - * - * The construction process uses heuristics for determining which interfaces appear to be applicable for and uses - * this information to set (1) the transmit sockets and (2) choose the interfaces with which to associate multicast - * addresses. - * - * Loopback addresses are accepted if it can be determined that they originate on the same machine: - * - if all enabled interfaces are loopback interfaces, the peer must be on the same host (this ought to be cached) - * - if all advertised addresses are loopback addresses - * - if there is a non-unicast address that matches one of the (enabled) addresses of the host - * - * Unicast addresses are matched against interface addresses to determine whether the address is likely to be - * reachable without any routing. If so, the address is assigned to the interface and the interface is marked as - * "enabled" for the purposes of multicast handling. If not, it is associated with the first enabled non-loopback - * interface on the assumption that unicast routing works fine (but the interface is not "enabled" for multicast - * handling). - * - * Multicast addresses are added only for interfaces that are "enabled" based on unicast processing. If none are - * and the source locator matches an interface, it will enable that interface. - * - * @param[in] gv domain state, needed for interfaces, transports, tracing - * @param[in] uc list of advertised unicast locators - * @param[in] mc list of advertised multicast locators - * @param[in] srcloc source address for discovery packet, or "invalid" - * @param[in,out] inherited_intfs set of applicable interfaces, may be NULL - * - * @return new addrset, possibly empty */ -static struct ddsi_addrset *addrset_from_locatorlists (const struct ddsi_domaingv *gv, const ddsi_locators_t *uc, const ddsi_locators_t *mc, const ddsi_locator_t *srcloc, const interface_set_t *inherited_intfs) -{ - struct ddsi_addrset *as = ddsi_new_addrset (); - interface_set_t intfs; - interface_set_init (&intfs); - - // if all interfaces are loopback, or all locators in uc are loopback, we're cool with loopback addresses - bool allow_loopback; - { - bool a = true; - for (int i = 0; i < gv->n_interfaces && a; i++) - if (!gv->interfaces[i].loopback) - a = false; - bool b = true; - // FIXME: what about the cases where SEDP gives just a loopback address, but the proxypp is known to be on a remote node? - for (struct ddsi_locators_one *l = uc->first; l != NULL && b; l = l->next) - b = ddsi_is_loopbackaddr (gv, &l->loc); - allow_loopback = (a || b); - } - - // if any non-loopback address is identical to one of our own addresses (actual or advertised), - // assume it is the same machine, in which case loopback addresses may be picked up - for (struct ddsi_locators_one *l = uc->first; l != NULL && !allow_loopback; l = l->next) - { - if (ddsi_is_loopbackaddr (gv, &l->loc)) - continue; - allow_loopback = (ddsi_is_nearby_address (gv, &l->loc, (size_t) gv->n_interfaces, gv->interfaces, NULL) == DNAR_SELF); - } - //GVTRACE(" allow_loopback=%d\n", allow_loopback); - - bool direct = false; - for (struct ddsi_locators_one *l = uc->first; l != NULL; l = l->next) - { -#if 0 - { - char buf[DDSI_LOCSTRLEN]; - ddsi_locator_to_string_no_port (buf, sizeof (buf), &l->loc); - GVTRACE("%s: ignore %d loopback %d\n", buf, l->loc.tran->m_ignore, ddsi_is_loopbackaddr (gv, &l->loc)); - } -#endif - // skip unrecognized ones, as well as loopback ones if not on the same host - if (!allow_loopback && ddsi_is_loopbackaddr (gv, &l->loc)) - continue; - - ddsi_locator_t loc = l->loc; - - // if the advertised locator matches our own external locator, than presumably - // it is the same machine and should be addressed using the actual interface - // address - bool extloc_of_self = false; - for (int i = 0; i < gv->n_interfaces; i++) - { - if (loc.kind == gv->interfaces[i].loc.kind && memcmp (loc.address, gv->interfaces[i].extloc.address, sizeof (loc.address)) == 0) - { - memcpy (loc.address, gv->interfaces[i].loc.address, sizeof (loc.address)); - extloc_of_self = true; - break; - } - } - - if (!extloc_of_self && loc.kind == DDSI_LOCATOR_KIND_UDPv4 && gv->extmask.kind != DDSI_LOCATOR_KIND_INVALID) - { - /* If the examined locator is in the same subnet as our own - external IP address, this locator will be translated into one - in the same subnet as our own local ip and selected. */ - assert (gv->n_interfaces == 1); // gv->extmask: the hack is only supported if limited to a single interface - struct in_addr tmp4 = *((struct in_addr *) (loc.address + 12)); - const struct in_addr ownip = *((struct in_addr *) (gv->interfaces[0].loc.address + 12)); - const struct in_addr extip = *((struct in_addr *) (gv->interfaces[0].extloc.address + 12)); - const struct in_addr extmask = *((struct in_addr *) (gv->extmask.address + 12)); - - if ((tmp4.s_addr & extmask.s_addr) == (extip.s_addr & extmask.s_addr)) - { - /* translate network part of the IP address from the external - one to the internal one */ - tmp4.s_addr = (tmp4.s_addr & ~extmask.s_addr) | (ownip.s_addr & extmask.s_addr); - memcpy (loc.address + 12, &tmp4, 4); - } - } - - addrset_from_locatorlists_add_one (gv, &loc, as, &intfs, &direct); - } - - if (ddsi_addrset_empty (as) && !ddsi_is_unspec_locator (srcloc)) - { - //GVTRACE("add srcloc\n"); - // FIXME: conn_read should provide interface information in source address - //GVTRACE (" add-srcloc"); - addrset_from_locatorlists_add_one (gv, srcloc, as, &intfs, &direct); - } - - if (ddsi_addrset_empty (as) && inherited_intfs) - { - // implies no interfaces enabled in "intfs" yet -- just use whatever - // we inherited for the purposes of selecting multicast addresses - assert (!direct); - for (int i = 0; i < gv->n_interfaces; i++) - assert (!intfs.xs[i]); - //GVTRACE (" using-inherited-intfs"); - intfs = *inherited_intfs; - } - else if (!direct && gv->config.multicast_ttl > 1) - { - //GVTRACE("assuming multicast routing works\n"); - // if not directly connected but multicast TTL allows routing, - // assume any non-local interface will do - //GVTRACE (" enabling-non-loopback/link-local"); - for (int i = 0; i < gv->n_interfaces; i++) - { - assert (!intfs.xs[i]); - intfs.xs[i] = !(gv->interfaces[i].link_local || gv->interfaces[i].loopback); - } - } - -#if 0 - GVTRACE("enabled interfaces for multicast:"); - for (int i = 0; i < gv->n_interfaces; i++) - { - if (intfs[i]) - GVTRACE(" %s(%d)", gv->interfaces[i].name, gv->interfaces[i].mc_capable); - } - GVTRACE("\n"); -#endif - - for (struct ddsi_locators_one *l = mc->first; l != NULL; l = l->next) - { - for (int i = 0; i < gv->n_interfaces; i++) - { - if (intfs.xs[i] && gv->interfaces[i].mc_capable) - { - const ddsi_xlocator_t loc = { - .conn = gv->xmit_conns[i], - .c = l->loc - }; - if (ddsi_factory_supports (loc.conn->m_factory, loc.c.kind)) - allowmulticast_aware_add_to_addrset (gv, gv->config.allowMulticast, as, &loc); - } - } - } - return as; -} - - -/****************************************************************************** - *** - *** SPDP - *** - *****************************************************************************/ - -static void maybe_add_pp_as_meta_to_as_disc (struct ddsi_domaingv *gv, const struct ddsi_addrset *as_meta) -{ - if (ddsi_addrset_empty_mc (as_meta) || !(gv->config.allowMulticast & DDSI_AMC_SPDP)) - { - ddsi_xlocator_t loc; - if (ddsi_addrset_any_uc (as_meta, &loc)) - { - ddsi_add_xlocator_to_addrset (gv, gv->as_disc, &loc); - } - } -} - -struct locators_builder { - ddsi_locators_t *dst; - struct ddsi_locators_one *storage; - size_t storage_n; -}; - -static struct locators_builder locators_builder_init (ddsi_locators_t *dst, struct ddsi_locators_one *storage, size_t storage_n) -{ - dst->n = 0; - dst->first = NULL; - dst->last = NULL; - return (struct locators_builder) { - .dst = dst, - .storage = storage, - .storage_n = storage_n - }; -} - -static bool locators_add_one (struct locators_builder *b, const ddsi_locator_t *loc, uint32_t port_override) -{ - if (b->dst->n >= b->storage_n) - return false; - if (b->dst->n == 0) - b->dst->first = b->storage; - else - b->dst->last->next = &b->storage[b->dst->n]; - b->dst->last = &b->storage[b->dst->n++]; - b->dst->last->loc = *loc; - if (port_override != DDSI_LOCATOR_PORT_INVALID) - b->dst->last->loc.port = port_override; - b->dst->last->next = NULL; - return true; -} - -static bool include_multicast_locator_in_discovery (const struct ddsi_participant *pp) -{ -#ifdef DDS_HAS_SSM - /* Note that if the default multicast address is an SSM address, - we will simply advertise it. The recipients better understand - it means the writers will publish to address and the readers - favour SSM. */ - if (ddsi_is_ssm_mcaddr (pp->e.gv, &pp->e.gv->loc_default_mc)) - return (pp->e.gv->config.allowMulticast & DDSI_AMC_SSM) != 0; - else - return (pp->e.gv->config.allowMulticast & DDSI_AMC_ASM) != 0; -#else - return (pp->e.gv->config.allowMulticast & DDSI_AMC_ASM) != 0; -#endif -} - -void ddsi_get_participant_builtin_topic_data (const struct ddsi_participant *pp, ddsi_plist_t *dst, struct ddsi_participant_builtin_topic_data_locators *locs) -{ - size_t size; - char node[64]; - uint64_t qosdiff; - - ddsi_plist_init_empty (dst); - dst->present |= PP_PARTICIPANT_GUID | PP_BUILTIN_ENDPOINT_SET | - PP_PROTOCOL_VERSION | PP_VENDORID | PP_DOMAIN_ID; - dst->participant_guid = pp->e.guid; - dst->builtin_endpoint_set = pp->bes; - dst->protocol_version.major = DDSI_RTPS_MAJOR; - dst->protocol_version.minor = DDSI_RTPS_MINOR; - dst->vendorid = DDSI_VENDORID_ECLIPSE; - dst->domain_id = pp->e.gv->config.extDomainId.value; - /* Be sure not to send a DOMAIN_TAG when it is the default (an empty) - string: it is an "incompatible-if-unrecognized" parameter, and so - implementations that don't understand the parameter will refuse to - discover us, and so sending the default would break backwards - compatibility. */ - if (strcmp (pp->e.gv->config.domainTag, "") != 0) - { - dst->present |= PP_DOMAIN_TAG; - dst->aliased |= PP_DOMAIN_TAG; - dst->domain_tag = pp->e.gv->config.domainTag; - } - - // Construct unicast locator parameters - { - struct locators_builder def_uni = locators_builder_init (&dst->default_unicast_locators, locs->def_uni, MAX_XMIT_CONNS); - struct locators_builder meta_uni = locators_builder_init (&dst->metatraffic_unicast_locators, locs->meta_uni, MAX_XMIT_CONNS); - for (int i = 0; i < pp->e.gv->n_interfaces; i++) - { - if (!pp->e.gv->xmit_conns[i]->m_factory->m_enable_spdp) - { - // skip any interfaces where the address kind doesn't match the selected transport - // as a reasonablish way of not advertising iceoryx locators here - continue; - } -#ifndef NDEBUG - int32_t kind; -#endif - uint32_t data_port, meta_port; - if (pp->e.gv->config.many_sockets_mode != DDSI_MSM_MANY_UNICAST) - { -#ifndef NDEBUG - kind = pp->e.gv->loc_default_uc.kind; -#endif - assert (kind == pp->e.gv->loc_meta_uc.kind); - data_port = pp->e.gv->loc_default_uc.port; - meta_port = pp->e.gv->loc_meta_uc.port; - } - else - { -#ifndef NDEBUG - kind = pp->m_locator.kind; -#endif - data_port = meta_port = pp->m_locator.port; - } - assert (kind == pp->e.gv->interfaces[i].extloc.kind); - locators_add_one (&def_uni, &pp->e.gv->interfaces[i].extloc, data_port); - locators_add_one (&meta_uni, &pp->e.gv->interfaces[i].extloc, meta_port); - } - if (pp->e.gv->config.publish_uc_locators) - { - dst->present |= PP_DEFAULT_UNICAST_LOCATOR | PP_METATRAFFIC_UNICAST_LOCATOR; - dst->aliased |= PP_DEFAULT_UNICAST_LOCATOR | PP_METATRAFFIC_UNICAST_LOCATOR; - } - } - - if (include_multicast_locator_in_discovery (pp)) - { - dst->present |= PP_DEFAULT_MULTICAST_LOCATOR | PP_METATRAFFIC_MULTICAST_LOCATOR; - dst->aliased |= PP_DEFAULT_MULTICAST_LOCATOR | PP_METATRAFFIC_MULTICAST_LOCATOR; - struct locators_builder def_mc = locators_builder_init (&dst->default_multicast_locators, &locs->def_multi, 1); - struct locators_builder meta_mc = locators_builder_init (&dst->metatraffic_multicast_locators, &locs->meta_multi, 1); - locators_add_one (&def_mc, &pp->e.gv->loc_default_mc, DDSI_LOCATOR_PORT_INVALID); - locators_add_one (&meta_mc, &pp->e.gv->loc_meta_mc, DDSI_LOCATOR_PORT_INVALID); - } - - /* Add Adlink specific version information */ - { - dst->present |= PP_ADLINK_PARTICIPANT_VERSION_INFO; - memset (&dst->adlink_participant_version_info, 0, sizeof (dst->adlink_participant_version_info)); - dst->adlink_participant_version_info.version = 0; - dst->adlink_participant_version_info.flags = - DDSI_ADLINK_FL_DDSI2_PARTICIPANT_FLAG | - DDSI_ADLINK_FL_PTBES_FIXED_0 | - DDSI_ADLINK_FL_SUPPORTS_STATUSINFOX; - if (pp->e.gv->config.besmode == DDSI_BESMODE_MINIMAL) - dst->adlink_participant_version_info.flags |= DDSI_ADLINK_FL_MINIMAL_BES_MODE; - ddsrt_mutex_lock (&pp->e.gv->privileged_pp_lock); - if (pp->is_ddsi2_pp) - dst->adlink_participant_version_info.flags |= DDSI_ADLINK_FL_PARTICIPANT_IS_DDSI2; - ddsrt_mutex_unlock (&pp->e.gv->privileged_pp_lock); - -#if DDSRT_HAVE_GETHOSTNAME - if (ddsrt_gethostname(node, sizeof(node)-1) < 0) -#endif - (void) ddsrt_strlcpy (node, "unknown", sizeof (node)); - size = strlen(node) + strlen(DDS_VERSION) + strlen(DDS_HOST_NAME) + strlen(DDS_TARGET_NAME) + 4; /* + ///'\0' */ - dst->adlink_participant_version_info.internals = ddsrt_malloc(size); - (void) snprintf(dst->adlink_participant_version_info.internals, size, "%s/%s/%s/%s", node, DDS_VERSION, DDS_HOST_NAME, DDS_TARGET_NAME); - ETRACE (pp, "ddsi_spdp_write("PGUIDFMT") - internals: %s\n", PGUID (pp->e.guid), dst->adlink_participant_version_info.internals); - } - - /* Add Cyclone specific information */ - { - const uint32_t bufsz = ddsi_receive_buffer_size (pp->e.gv->m_factory); - if (bufsz > 0) - { - dst->present |= PP_CYCLONE_RECEIVE_BUFFER_SIZE; - dst->cyclone_receive_buffer_size = bufsz; - } - } - if (pp->e.gv->config.redundant_networking) - { - dst->present |= PP_CYCLONE_REDUNDANT_NETWORKING; - dst->cyclone_redundant_networking = true; - } - -#ifdef DDS_HAS_SECURITY - /* Add Security specific information. */ - if (ddsi_omg_get_participant_security_info (pp, &(dst->participant_security_info))) { - dst->present |= PP_PARTICIPANT_SECURITY_INFO; - dst->aliased |= PP_PARTICIPANT_SECURITY_INFO; - } -#endif - - /* Participant QoS's insofar as they are set, different from the default, and mapped to the SPDP data, rather than to the Adlink-specific CMParticipant endpoint. */ - qosdiff = ddsi_xqos_delta (&pp->plist->qos, &ddsi_default_qos_participant, DDSI_QP_USER_DATA | DDSI_QP_ENTITY_NAME | DDSI_QP_PROPERTY_LIST | DDSI_QP_LIVELINESS); - if (pp->e.gv->config.explicitly_publish_qos_set_to_default) - qosdiff |= ~(DDSI_QP_UNRECOGNIZED_INCOMPATIBLE_MASK | DDSI_QP_LIVELINESS); - - assert (dst->qos.present == 0); - ddsi_plist_mergein_missing (dst, pp->plist, 0, qosdiff); -#ifdef DDS_HAS_SECURITY - if (ddsi_omg_participant_is_secure(pp)) - ddsi_plist_mergein_missing (dst, pp->plist, PP_IDENTITY_TOKEN | PP_PERMISSIONS_TOKEN, 0); -#endif -} - -static int write_and_fini_plist (struct ddsi_writer *wr, ddsi_plist_t *ps, bool alive) -{ - struct ddsi_serdata *serdata = ddsi_serdata_from_sample (wr->type, alive ? SDK_DATA : SDK_KEY, ps); - ddsi_plist_fini (ps); - serdata->statusinfo = alive ? 0 : (DDSI_STATUSINFO_DISPOSE | DDSI_STATUSINFO_UNREGISTER); - serdata->timestamp = ddsrt_time_wallclock (); - return ddsi_write_sample_nogc_notk (ddsi_lookup_thread_state (), NULL, wr, serdata); -} - -int ddsi_spdp_write (struct ddsi_participant *pp) -{ - struct ddsi_writer *wr; - ddsi_plist_t ps; - struct ddsi_participant_builtin_topic_data_locators locs; - - if (pp->e.onlylocal) { - /* This topic is only locally available. */ - return 0; - } - - ETRACE (pp, "ddsi_spdp_write("PGUIDFMT")\n", PGUID (pp->e.guid)); - - if ((wr = ddsi_get_builtin_writer (pp, DDSI_ENTITYID_SPDP_BUILTIN_PARTICIPANT_WRITER)) == NULL) - { - ETRACE (pp, "ddsi_spdp_write("PGUIDFMT") - builtin participant writer not found\n", PGUID (pp->e.guid)); - return 0; - } - - ddsi_get_participant_builtin_topic_data (pp, &ps, &locs); - return write_and_fini_plist (wr, &ps, true); -} - -static int ddsi_spdp_dispose_unregister_with_wr (struct ddsi_participant *pp, unsigned entityid) -{ - ddsi_plist_t ps; - struct ddsi_writer *wr; - - if ((wr = ddsi_get_builtin_writer (pp, entityid)) == NULL) - { - ETRACE (pp, "ddsi_spdp_dispose_unregister("PGUIDFMT") - builtin participant %s writer not found\n", - PGUID (pp->e.guid), - entityid == DDSI_ENTITYID_SPDP_RELIABLE_BUILTIN_PARTICIPANT_SECURE_WRITER ? "secure" : ""); - return 0; - } - - ddsi_plist_init_empty (&ps); - ps.present |= PP_PARTICIPANT_GUID; - ps.participant_guid = pp->e.guid; - return write_and_fini_plist (wr, &ps, false); -} - -int ddsi_spdp_dispose_unregister (struct ddsi_participant *pp) -{ - /* - * When disposing a participant, it should be announced on both the - * non-secure and secure writers. - * The receiver will decide from which writer it accepts the dispose. - */ - int ret = ddsi_spdp_dispose_unregister_with_wr(pp, DDSI_ENTITYID_SPDP_BUILTIN_PARTICIPANT_WRITER); - if ((ret > 0) && ddsi_omg_participant_is_secure(pp)) - { - ret = ddsi_spdp_dispose_unregister_with_wr(pp, DDSI_ENTITYID_SPDP_RELIABLE_BUILTIN_PARTICIPANT_SECURE_WRITER); - } - return ret; -} - -static unsigned pseudo_random_delay (const ddsi_guid_t *x, const ddsi_guid_t *y, ddsrt_mtime_t tnow) -{ - /* You know, an ordinary random generator would be even better, but - the C library doesn't have a reentrant one and I don't feel like - integrating, say, the Mersenne Twister right now. */ - static const uint64_t cs[] = { - UINT64_C (15385148050874689571), - UINT64_C (17503036526311582379), - UINT64_C (11075621958654396447), - UINT64_C ( 9748227842331024047), - UINT64_C (14689485562394710107), - UINT64_C (17256284993973210745), - UINT64_C ( 9288286355086959209), - UINT64_C (17718429552426935775), - UINT64_C (10054290541876311021), - UINT64_C (13417933704571658407) - }; - uint32_t a = x->prefix.u[0], b = x->prefix.u[1], c = x->prefix.u[2], d = x->entityid.u; - uint32_t e = y->prefix.u[0], f = y->prefix.u[1], g = y->prefix.u[2], h = y->entityid.u; - uint32_t i = (uint32_t) ((uint64_t) tnow.v >> 32), j = (uint32_t) tnow.v; - uint64_t m = 0; - m += (a + cs[0]) * (b + cs[1]); - m += (c + cs[2]) * (d + cs[3]); - m += (e + cs[4]) * (f + cs[5]); - m += (g + cs[6]) * (h + cs[7]); - m += (i + cs[8]) * (j + cs[9]); - return (unsigned) (m >> 32); -} - -static void respond_to_spdp (const struct ddsi_domaingv *gv, const ddsi_guid_t *dest_proxypp_guid) -{ - struct ddsi_entity_enum_participant est; - struct ddsi_participant *pp; - ddsrt_mtime_t tnow = ddsrt_time_monotonic (); - ddsi_entidx_enum_participant_init (&est, gv->entity_index); - while ((pp = ddsi_entidx_enum_participant_next (&est)) != NULL) - { - /* delay_base has 32 bits, so delay_norm is approximately 1s max; - delay_max <= 1s by gv.config checks */ - unsigned delay_base = pseudo_random_delay (&pp->e.guid, dest_proxypp_guid, tnow); - unsigned delay_norm = delay_base >> 2; - int64_t delay_max_ms = gv->config.spdp_response_delay_max / 1000000; - int64_t delay = (int64_t) delay_norm * delay_max_ms / 1000; - ddsrt_mtime_t tsched = ddsrt_mtime_add_duration (tnow, delay); - GVTRACE (" %"PRId64, delay); - if (!pp->e.gv->config.unicast_response_to_spdp_messages) - /* pp can't reach gc_delete_participant => can safely reschedule */ - (void) ddsi_resched_xevent_if_earlier (pp->spdp_xevent, tsched); - else - ddsi_qxev_spdp (gv->xevents, tsched, &pp->e.guid, dest_proxypp_guid); - } - ddsi_entidx_enum_participant_fini (&est); -} - -static int handle_spdp_dead (const struct ddsi_receiver_state *rst, ddsi_entityid_t pwr_entityid, ddsrt_wctime_t timestamp, const ddsi_plist_t *datap, unsigned statusinfo) -{ - struct ddsi_domaingv * const gv = rst->gv; - ddsi_guid_t guid; - - GVLOGDISC ("SPDP ST%x", statusinfo); - - if (datap->present & PP_PARTICIPANT_GUID) - { - guid = datap->participant_guid; - GVLOGDISC (" %"PRIx32":%"PRIx32":%"PRIx32":%"PRIx32, PGUID (guid)); - assert (guid.entityid.u == DDSI_ENTITYID_PARTICIPANT); - if (ddsi_is_proxy_participant_deletion_allowed(gv, &guid, pwr_entityid)) - { - if (ddsi_delete_proxy_participant_by_guid (gv, &guid, timestamp, 0) < 0) - { - GVLOGDISC (" unknown"); - } - else - { - GVLOGDISC (" delete"); - } - } - else - { - GVLOGDISC (" not allowed"); - } - } - else - { - GVWARNING ("data (SPDP, vendor %u.%u): no/invalid payload\n", rst->vendor.id[0], rst->vendor.id[1]); - } - return 1; -} - -static struct ddsi_proxy_participant *find_ddsi2_proxy_participant (const struct ddsi_entity_index *entidx, const ddsi_guid_t *ppguid) -{ - struct ddsi_entity_enum_proxy_participant it; - struct ddsi_proxy_participant *pp; - ddsi_entidx_enum_proxy_participant_init (&it, entidx); - while ((pp = ddsi_entidx_enum_proxy_participant_next (&it)) != NULL) - { - if (ddsi_vendor_is_eclipse_or_opensplice (pp->vendor) && pp->e.guid.prefix.u[0] == ppguid->prefix.u[0] && pp->is_ddsi2_pp) - break; - } - ddsi_entidx_enum_proxy_participant_fini (&it); - return pp; -} - -static void make_participants_dependent_on_ddsi2 (struct ddsi_domaingv *gv, const ddsi_guid_t *ddsi2guid, ddsrt_wctime_t timestamp) -{ - struct ddsi_entity_enum_proxy_participant it; - struct ddsi_proxy_participant *pp, *d2pp; - if ((d2pp = ddsi_entidx_lookup_proxy_participant_guid (gv->entity_index, ddsi2guid)) == NULL) - return; - ddsi_entidx_enum_proxy_participant_init (&it, gv->entity_index); - while ((pp = ddsi_entidx_enum_proxy_participant_next (&it)) != NULL) - { - if (ddsi_vendor_is_eclipse_or_opensplice (pp->vendor) && pp->e.guid.prefix.u[0] == ddsi2guid->prefix.u[0] && !pp->is_ddsi2_pp) - { - GVTRACE ("proxy participant "PGUIDFMT" depends on ddsi2 "PGUIDFMT, PGUID (pp->e.guid), PGUID (*ddsi2guid)); - ddsrt_mutex_lock (&pp->e.lock); - pp->privileged_pp_guid = *ddsi2guid; - ddsrt_mutex_unlock (&pp->e.lock); - ddsi_proxy_participant_reassign_lease (pp, d2pp->lease); - GVTRACE ("\n"); - - if (ddsi_entidx_lookup_proxy_participant_guid (gv->entity_index, ddsi2guid) == NULL) - { - /* If DDSI2 has been deleted here (i.e., very soon after - having been created), we don't know whether pp will be - deleted */ - break; - } - } - } - ddsi_entidx_enum_proxy_participant_fini (&it); - - if (pp != NULL) - { - GVTRACE ("make_participants_dependent_on_ddsi2: ddsi2 "PGUIDFMT" is no more, delete "PGUIDFMT"\n", PGUID (*ddsi2guid), PGUID (pp->e.guid)); - ddsi_delete_proxy_participant_by_guid (gv, &pp->e.guid, timestamp, 1); - } -} - -static int handle_spdp_alive (const struct ddsi_receiver_state *rst, ddsi_seqno_t seq, ddsrt_wctime_t timestamp, const ddsi_plist_t *datap) -{ - struct ddsi_domaingv * const gv = rst->gv; - const unsigned bes_sedp_announcer_mask = - DDSI_DISC_BUILTIN_ENDPOINT_SUBSCRIPTION_ANNOUNCER | - DDSI_DISC_BUILTIN_ENDPOINT_PUBLICATION_ANNOUNCER; - struct ddsi_addrset *as_meta, *as_default; - uint32_t builtin_endpoint_set; - ddsi_guid_t privileged_pp_guid; - dds_duration_t lease_duration; - unsigned custom_flags = 0; - - /* If advertised domain id or domain tag doesn't match, ignore the message. Do this first to - minimize the impact such messages have. */ - { - const uint32_t domain_id = (datap->present & PP_DOMAIN_ID) ? datap->domain_id : gv->config.extDomainId.value; - const char *domain_tag = (datap->present & PP_DOMAIN_TAG) ? datap->domain_tag : ""; - if (domain_id != gv->config.extDomainId.value || strcmp (domain_tag, gv->config.domainTag) != 0) - { - GVTRACE ("ignore remote participant in mismatching domain %"PRIu32" tag \"%s\"\n", domain_id, domain_tag); - return 0; - } - } - - if (!(datap->present & PP_PARTICIPANT_GUID) || !(datap->present & PP_BUILTIN_ENDPOINT_SET)) - { - GVWARNING ("data (SPDP, vendor %u.%u): no/invalid payload\n", rst->vendor.id[0], rst->vendor.id[1]); - return 0; - } - - /* At some point the RTI implementation didn't mention - BUILTIN_ENDPOINT_DDSI_PARTICIPANT_MESSAGE_DATA_READER & ...WRITER, or - so it seemed; and yet they are necessary for correct operation, - so add them. */ - builtin_endpoint_set = datap->builtin_endpoint_set; - if (ddsi_vendor_is_rti (rst->vendor) && - ((builtin_endpoint_set & - (DDSI_BUILTIN_ENDPOINT_PARTICIPANT_MESSAGE_DATA_READER | - DDSI_BUILTIN_ENDPOINT_PARTICIPANT_MESSAGE_DATA_WRITER)) - != (DDSI_BUILTIN_ENDPOINT_PARTICIPANT_MESSAGE_DATA_READER | - DDSI_BUILTIN_ENDPOINT_PARTICIPANT_MESSAGE_DATA_WRITER)) && - gv->config.assume_rti_has_pmd_endpoints) - { - GVLOGDISC ("data (SPDP, vendor %u.%u): assuming unadvertised PMD endpoints do exist\n", - rst->vendor.id[0], rst->vendor.id[1]); - builtin_endpoint_set |= - DDSI_BUILTIN_ENDPOINT_PARTICIPANT_MESSAGE_DATA_READER | - DDSI_BUILTIN_ENDPOINT_PARTICIPANT_MESSAGE_DATA_WRITER; - } - - /* Do we know this GUID already? */ - { - struct ddsi_entity_common *existing_entity; - if ((existing_entity = ddsi_entidx_lookup_guid_untyped (gv->entity_index, &datap->participant_guid)) == NULL) - { - /* Local SPDP packets may be looped back, and that can include ones - for participants currently being deleted. The first thing that - happens when deleting a participant is removing it from the hash - table, and consequently the looped back packet may appear to be - from an unknown participant. So we handle that. */ - if (ddsi_is_deleted_participant_guid (gv->deleted_participants, &datap->participant_guid, DDSI_DELETED_PPGUID_REMOTE)) - { - RSTTRACE ("SPDP ST0 "PGUIDFMT" (recently deleted)", PGUID (datap->participant_guid)); - return 0; - } - } - else if (existing_entity->kind == DDSI_EK_PARTICIPANT) - { - RSTTRACE ("SPDP ST0 "PGUIDFMT" (local)", PGUID (datap->participant_guid)); - return 0; - } - else if (existing_entity->kind == DDSI_EK_PROXY_PARTICIPANT) - { - struct ddsi_proxy_participant *proxypp = (struct ddsi_proxy_participant *) existing_entity; - struct ddsi_lease *lease; - int interesting = 0; - RSTTRACE ("SPDP ST0 "PGUIDFMT" (known)", PGUID (datap->participant_guid)); - /* SPDP processing is so different from normal processing that we are - even skipping the automatic lease renewal. Note that proxy writers - that are not alive are not set alive here. This is done only when - data is received from a particular pwr (in handle_regular) */ - if ((lease = ddsrt_atomic_ldvoidp (&proxypp->minl_auto)) != NULL) - ddsi_lease_renew (lease, ddsrt_time_elapsed ()); - ddsrt_mutex_lock (&proxypp->e.lock); - if (proxypp->implicitly_created || seq > proxypp->seq) - { - interesting = 1; - if (!(gv->logconfig.c.mask & DDS_LC_TRACE)) - GVLOGDISC ("SPDP ST0 "PGUIDFMT, PGUID (datap->participant_guid)); - GVLOGDISC (proxypp->implicitly_created ? " (NEW was-implicitly-created)" : " (update)"); - proxypp->implicitly_created = 0; - ddsi_update_proxy_participant_plist_locked (proxypp, seq, datap, timestamp); - } - ddsrt_mutex_unlock (&proxypp->e.lock); - return interesting; - } - else - { - /* mismatch on entity kind: that should never have gotten past the - input validation */ - GVWARNING ("data (SPDP, vendor %u.%u): "PGUIDFMT" kind mismatch\n", rst->vendor.id[0], rst->vendor.id[1], PGUID (datap->participant_guid)); - return 0; - } - } - - const bool is_secure = ((datap->builtin_endpoint_set & DDSI_DISC_BUILTIN_ENDPOINT_PARTICIPANT_SECURE_ANNOUNCER) != 0 && - (datap->present & PP_IDENTITY_TOKEN)); - /* Make sure we don't create any security builtin endpoint when it's considered unsecure. */ - if (!is_secure) - builtin_endpoint_set &= DDSI_BES_MASK_NON_SECURITY; - GVLOGDISC ("SPDP ST0 "PGUIDFMT" bes %"PRIx32"%s NEW", PGUID (datap->participant_guid), builtin_endpoint_set, is_secure ? " (secure)" : ""); - - if (datap->present & PP_ADLINK_PARTICIPANT_VERSION_INFO) { - if ((datap->adlink_participant_version_info.flags & DDSI_ADLINK_FL_DDSI2_PARTICIPANT_FLAG) && - (datap->adlink_participant_version_info.flags & DDSI_ADLINK_FL_PARTICIPANT_IS_DDSI2)) - custom_flags |= DDSI_CF_PARTICIPANT_IS_DDSI2; - - GVLOGDISC (" (0x%08"PRIx32"-0x%08"PRIx32"-0x%08"PRIx32"-0x%08"PRIx32"-0x%08"PRIx32" %s)", - datap->adlink_participant_version_info.version, - datap->adlink_participant_version_info.flags, - datap->adlink_participant_version_info.unused[0], - datap->adlink_participant_version_info.unused[1], - datap->adlink_participant_version_info.unused[2], - datap->adlink_participant_version_info.internals); - } - - /* Can't do "mergein_missing" because of constness of *datap */ - if (datap->qos.present & DDSI_QP_LIVELINESS) - lease_duration = datap->qos.liveliness.lease_duration; - else - { - assert (ddsi_default_qos_participant.present & DDSI_QP_LIVELINESS); - lease_duration = ddsi_default_qos_participant.liveliness.lease_duration; - } - /* If any of the SEDP announcer are missing AND the guid prefix of - the SPDP writer differs from the guid prefix of the new participant, - we make it dependent on the writer's participant. See also the - lease expiration handling. Note that the entityid MUST be - DDSI_ENTITYID_PARTICIPANT or entidx_lookup will assert. So we only - zero the prefix. */ - privileged_pp_guid.prefix = rst->src_guid_prefix; - privileged_pp_guid.entityid.u = DDSI_ENTITYID_PARTICIPANT; - if ((builtin_endpoint_set & bes_sedp_announcer_mask) != bes_sedp_announcer_mask && - memcmp (&privileged_pp_guid, &datap->participant_guid, sizeof (ddsi_guid_t)) != 0) - { - GVLOGDISC (" (depends on "PGUIDFMT")", PGUID (privileged_pp_guid)); - /* never expire lease for this proxy: it won't actually expire - until the "privileged" one expires anyway */ - lease_duration = DDS_INFINITY; - } - else if (ddsi_vendor_is_eclipse_or_opensplice (rst->vendor) && !(custom_flags & DDSI_CF_PARTICIPANT_IS_DDSI2)) - { - /* Non-DDSI2 participants are made dependent on DDSI2 (but DDSI2 - itself need not be discovered yet) */ - struct ddsi_proxy_participant *ddsi2; - if ((ddsi2 = find_ddsi2_proxy_participant (gv->entity_index, &datap->participant_guid)) == NULL) - memset (&privileged_pp_guid.prefix, 0, sizeof (privileged_pp_guid.prefix)); - else - { - privileged_pp_guid.prefix = ddsi2->e.guid.prefix; - lease_duration = DDS_INFINITY; - GVLOGDISC (" (depends on "PGUIDFMT")", PGUID (privileged_pp_guid)); - } - } - else - { - memset (&privileged_pp_guid.prefix, 0, sizeof (privileged_pp_guid.prefix)); - } - - /* Choose locators */ - { - const ddsi_locators_t emptyset = { .n = 0, .first = NULL, .last = NULL }; - const ddsi_locators_t *uc; - const ddsi_locators_t *mc; - ddsi_locator_t srcloc; - interface_set_t intfs; - - srcloc = rst->srcloc; - uc = (datap->present & PP_DEFAULT_UNICAST_LOCATOR) ? &datap->default_unicast_locators : ∅ - mc = (datap->present & PP_DEFAULT_MULTICAST_LOCATOR) ? &datap->default_multicast_locators : ∅ - if (gv->config.tcp_use_peeraddr_for_unicast) - uc = ∅ // force use of source locator - else if (uc != &emptyset) - ddsi_set_unspec_locator (&srcloc); // can't always use the source address - - interface_set_init (&intfs); - as_default = addrset_from_locatorlists (gv, uc, mc, &srcloc, &intfs); - - srcloc = rst->srcloc; - uc = (datap->present & PP_METATRAFFIC_UNICAST_LOCATOR) ? &datap->metatraffic_unicast_locators : ∅ - mc = (datap->present & PP_METATRAFFIC_MULTICAST_LOCATOR) ? &datap->metatraffic_multicast_locators : ∅ - if (gv->config.tcp_use_peeraddr_for_unicast) - uc = ∅ // force use of source locator - else if (uc != &emptyset) - ddsi_set_unspec_locator (&srcloc); // can't always use the source address - interface_set_init (&intfs); - as_meta = addrset_from_locatorlists (gv, uc, mc, &srcloc, &intfs); - - ddsi_log_addrset (gv, DDS_LC_DISCOVERY, " (data", as_default); - ddsi_log_addrset (gv, DDS_LC_DISCOVERY, " meta", as_meta); - GVLOGDISC (")"); - } - - if (ddsi_addrset_empty_uc (as_default) || ddsi_addrset_empty_uc (as_meta)) - { - GVLOGDISC (" (no unicast address"); - ddsi_unref_addrset (as_default); - ddsi_unref_addrset (as_meta); - return 1; - } - - GVLOGDISC (" QOS={"); - ddsi_xqos_log (DDS_LC_DISCOVERY, &gv->logconfig, &datap->qos); - GVLOGDISC ("}\n"); - - maybe_add_pp_as_meta_to_as_disc (gv, as_meta); - - if (!ddsi_new_proxy_participant (gv, &datap->participant_guid, builtin_endpoint_set, &privileged_pp_guid, as_default, as_meta, datap, lease_duration, rst->vendor, custom_flags, timestamp, seq)) - { - /* If no proxy participant was created, don't respond */ - return 0; - } - else - { - /* Force transmission of SPDP messages - we're not very careful - in avoiding the processing of SPDP packets addressed to others - so filter here */ - int have_dst = (rst->dst_guid_prefix.u[0] != 0 || rst->dst_guid_prefix.u[1] != 0 || rst->dst_guid_prefix.u[2] != 0); - if (!have_dst) - { - GVLOGDISC ("broadcasted SPDP packet -> answering"); - respond_to_spdp (gv, &datap->participant_guid); - } - else - { - GVLOGDISC ("directed SPDP packet -> not responding\n"); - } - - if (custom_flags & DDSI_CF_PARTICIPANT_IS_DDSI2) - { - /* If we just discovered DDSI2, make sure any existing - participants served by it are made dependent on it */ - make_participants_dependent_on_ddsi2 (gv, &datap->participant_guid, timestamp); - } - else if (privileged_pp_guid.prefix.u[0] || privileged_pp_guid.prefix.u[1] || privileged_pp_guid.prefix.u[2]) - { - /* If we just created a participant dependent on DDSI2, make sure - DDSI2 still exists. There is a risk of racing the lease expiry - of DDSI2. */ - if (ddsi_entidx_lookup_proxy_participant_guid (gv->entity_index, &privileged_pp_guid) == NULL) - { - GVLOGDISC ("make_participants_dependent_on_ddsi2: ddsi2 "PGUIDFMT" is no more, delete "PGUIDFMT"\n", - PGUID (privileged_pp_guid), PGUID (datap->participant_guid)); - ddsi_delete_proxy_participant_by_guid (gv, &datap->participant_guid, timestamp, 1); - } - } - return 1; - } -} - -static void handle_spdp (const struct ddsi_receiver_state *rst, ddsi_entityid_t pwr_entityid, ddsi_seqno_t seq, const struct ddsi_serdata *serdata) -{ - struct ddsi_domaingv * const gv = rst->gv; - ddsi_plist_t decoded_data; - if (ddsi_serdata_to_sample (serdata, &decoded_data, NULL, NULL)) - { - int interesting = 0; - switch (serdata->statusinfo & (DDSI_STATUSINFO_DISPOSE | DDSI_STATUSINFO_UNREGISTER)) - { - case 0: - interesting = handle_spdp_alive (rst, seq, serdata->timestamp, &decoded_data); - break; - - case DDSI_STATUSINFO_DISPOSE: - case DDSI_STATUSINFO_UNREGISTER: - case (DDSI_STATUSINFO_DISPOSE | DDSI_STATUSINFO_UNREGISTER): - interesting = handle_spdp_dead (rst, pwr_entityid, serdata->timestamp, &decoded_data, serdata->statusinfo); - break; - } - - ddsi_plist_fini (&decoded_data); - GVLOG (interesting ? DDS_LC_DISCOVERY : DDS_LC_TRACE, "\n"); - } -} - -struct add_locator_to_ps_arg { - struct ddsi_domaingv *gv; - ddsi_plist_t *ps; -}; - -static void add_locator_to_ps (const ddsi_locator_t *loc, void *varg) -{ - struct add_locator_to_ps_arg *arg = varg; - struct ddsi_locators_one *elem = ddsrt_malloc (sizeof (struct ddsi_locators_one)); - struct ddsi_locators *locs; - unsigned present_flag; - - elem->loc = *loc; - elem->next = NULL; - - if (ddsi_is_mcaddr (arg->gv, loc)) { - locs = &arg->ps->multicast_locators; - present_flag = PP_MULTICAST_LOCATOR; - } else { - locs = &arg->ps->unicast_locators; - present_flag = PP_UNICAST_LOCATOR; - } - - if (!(arg->ps->present & present_flag)) - { - locs->n = 0; - locs->first = locs->last = NULL; - arg->ps->present |= present_flag; - } - locs->n++; - if (locs->first) - locs->last->next = elem; - else - locs->first = elem; - locs->last = elem; -} - -static void add_xlocator_to_ps (const ddsi_xlocator_t *loc, void *varg) -{ - add_locator_to_ps (&loc->c, varg); -} - -#ifdef DDS_HAS_SHM -static void add_iox_locator_to_ps(const ddsi_locator_t* loc, struct add_locator_to_ps_arg *arg) -{ - struct ddsi_locators_one* elem = ddsrt_malloc(sizeof(struct ddsi_locators_one)); - struct ddsi_locators* locs = &arg->ps->unicast_locators; - unsigned present_flag = PP_UNICAST_LOCATOR; - - elem->loc = *loc; - elem->next = NULL; - - if (!(arg->ps->present & present_flag)) - { - locs->n = 0; - locs->first = locs->last = NULL; - arg->ps->present |= present_flag; - } - - //add iceoryx to the FRONT of the list of addresses, to indicate its higher priority - if (locs->first) - elem->next = locs->first; - else - locs->last = elem; - locs->first = elem; - locs->n++; -} -#endif - -/****************************************************************************** - *** - *** SEDP - *** - *****************************************************************************/ - -static struct ddsi_writer *get_sedp_writer (const struct ddsi_participant *pp, unsigned entityid) -{ - struct ddsi_writer *sedp_wr = ddsi_get_builtin_writer (pp, entityid); - if (sedp_wr == NULL) - DDS_FATAL ("sedp_write_writer: no SEDP builtin writer %x for "PGUIDFMT"\n", entityid, PGUID (pp->e.guid)); - return sedp_wr; -} - -static int sedp_write_endpoint_impl -( - struct ddsi_writer *wr, int alive, const ddsi_guid_t *guid, - const struct ddsi_endpoint_common *epcommon, - const dds_qos_t *xqos, struct ddsi_addrset *as, ddsi_security_info_t *security -#ifdef DDS_HAS_TYPE_DISCOVERY - , const struct ddsi_sertype *sertype -#endif -) -{ - struct ddsi_domaingv * const gv = wr->e.gv; - const dds_qos_t *defqos = NULL; - if (ddsi_is_writer_entityid (guid->entityid)) - defqos = &ddsi_default_qos_writer; - else if (ddsi_is_reader_entityid (guid->entityid)) - defqos = &ddsi_default_qos_reader; - else - assert (false); - - uint64_t qosdiff; - ddsi_plist_t ps; - - ddsi_plist_init_empty (&ps); - ps.present |= PP_ENDPOINT_GUID; - ps.endpoint_guid = *guid; - -#ifdef DDS_HAS_SECURITY - if (security) - { - ps.present |= PP_ENDPOINT_SECURITY_INFO; - memcpy(&ps.endpoint_security_info, security, sizeof(ddsi_security_info_t)); - } -#else - (void)security; - assert(security == NULL); -#endif - - if (!alive) - { - assert (xqos == NULL); - assert (epcommon == NULL); - qosdiff = 0; - } - else - { - assert (xqos != NULL); - ps.present |= PP_PROTOCOL_VERSION | PP_VENDORID; - ps.protocol_version.major = DDSI_RTPS_MAJOR; - ps.protocol_version.minor = DDSI_RTPS_MINOR; - ps.vendorid = DDSI_VENDORID_ECLIPSE; - - assert (epcommon != NULL); - - if (epcommon->group_guid.entityid.u != 0) - { - ps.present |= PP_GROUP_GUID; - ps.group_guid = epcommon->group_guid; - } - - if (!ddsi_is_writer_entityid (guid->entityid)) - { - const struct ddsi_reader *rd = ddsi_entidx_lookup_reader_guid (gv->entity_index, guid); - assert (rd); - if (rd->request_keyhash) - { - ps.present |= PP_CYCLONE_REQUESTS_KEYHASH; - ps.cyclone_requests_keyhash = 1u; - } - } - -#ifdef DDS_HAS_SSM - /* A bit of a hack -- the easy alternative would be to make it yet - another parameter. We only set "reader favours SSM" if we - really do: no point in telling the world that everything is at - the default. */ - if (ddsi_is_reader_entityid (guid->entityid)) - { - const struct ddsi_reader *rd = ddsi_entidx_lookup_reader_guid (gv->entity_index, guid); - assert (rd); - if (rd->favours_ssm) - { - ps.present |= PP_READER_FAVOURS_SSM; - ps.reader_favours_ssm.state = 1u; - } - } -#endif - - qosdiff = ddsi_xqos_delta (xqos, defqos, ~(uint64_t)0); - if (gv->config.explicitly_publish_qos_set_to_default) - qosdiff |= ~DDSI_QP_UNRECOGNIZED_INCOMPATIBLE_MASK; - - struct add_locator_to_ps_arg arg; - arg.gv = gv; - arg.ps = &ps; - if (as) - ddsi_addrset_forall (as, add_xlocator_to_ps, &arg); - -#ifdef DDS_HAS_SHM - assert(wr->xqos->present & DDSI_QP_LOCATOR_MASK); - if (!(xqos->ignore_locator_type & DDSI_LOCATOR_KIND_SHEM)) - { - if (!(arg.ps->present & PP_UNICAST_LOCATOR) || 0 == arg.ps->unicast_locators.n) - { - if (epcommon->pp->e.gv->config.many_sockets_mode == DDSI_MSM_MANY_UNICAST) - add_locator_to_ps(&epcommon->pp->m_locator, &arg); - else - { - // FIXME: same as what SPDP uses, should be refactored, now more than ever - for (int i = 0; i < epcommon->pp->e.gv->n_interfaces; i++) - { - if (!epcommon->pp->e.gv->xmit_conns[i]->m_factory->m_enable_spdp) - { - // skip any interfaces where the address kind doesn't match the selected transport - // as a reasonablish way of not advertising iceoryx locators here - continue; - } - // FIXME: should have multiple loc_default_uc/loc_meta_uc or compute ports here - ddsi_locator_t loc = epcommon->pp->e.gv->interfaces[i].extloc; - loc.port = epcommon->pp->e.gv->loc_default_uc.port; - add_locator_to_ps(&loc, &arg); - } - } - } - - if (!(arg.ps->present & PP_MULTICAST_LOCATOR) || 0 == arg.ps->multicast_locators.n) - { - if (include_multicast_locator_in_discovery (epcommon->pp)) - add_locator_to_ps (&epcommon->pp->e.gv->loc_default_mc, &arg); - } - - add_iox_locator_to_ps(&gv->loc_iceoryx_addr, &arg); - } -#endif - -#ifdef DDS_HAS_TYPE_DISCOVERY - assert (sertype); - if ((ps.qos.type_information = ddsi_sertype_typeinfo (sertype))) - ps.qos.present |= DDSI_QP_TYPE_INFORMATION; -#endif - } - - if (xqos) - ddsi_xqos_mergein_missing (&ps.qos, xqos, qosdiff); - return write_and_fini_plist (wr, &ps, alive); -} - +#include "ddsi__discovery_addrset.h" +#include "ddsi__discovery_spdp.h" +#include "ddsi__discovery_sedp.h" +#include "ddsi__discovery_endpoint.h" #ifdef DDS_HAS_TOPIC_DISCOVERY - -static int ddsi_sedp_write_topic_impl (struct ddsi_writer *wr, int alive, const ddsi_guid_t *guid, const dds_qos_t *xqos, ddsi_typeinfo_t *type_info) -{ - struct ddsi_domaingv * const gv = wr->e.gv; - const dds_qos_t *defqos = &ddsi_default_qos_topic; - - ddsi_plist_t ps; - ddsi_plist_init_empty (&ps); - ps.present |= PP_CYCLONE_TOPIC_GUID; - ps.topic_guid = *guid; - - assert (xqos != NULL); - ps.present |= PP_PROTOCOL_VERSION | PP_VENDORID; - ps.protocol_version.major = DDSI_RTPS_MAJOR; - ps.protocol_version.minor = DDSI_RTPS_MINOR; - ps.vendorid = DDSI_VENDORID_ECLIPSE; - - uint64_t qosdiff = ddsi_xqos_delta (xqos, defqos, ~(uint64_t)0); - if (gv->config.explicitly_publish_qos_set_to_default) - qosdiff |= ~DDSI_QP_UNRECOGNIZED_INCOMPATIBLE_MASK; - - if (type_info) - { - ps.qos.type_information = type_info; - ps.qos.present |= DDSI_QP_TYPE_INFORMATION; - } - if (xqos) - ddsi_xqos_mergein_missing (&ps.qos, xqos, qosdiff); - return write_and_fini_plist (wr, &ps, alive); -} - -int ddsi_sedp_write_topic (struct ddsi_topic *tp, bool alive) -{ - int res = 0; - if (!(tp->pp->bes & DDSI_DISC_BUILTIN_ENDPOINT_TOPICS_ANNOUNCER)) - return res; - if (!ddsi_is_builtin_entityid (tp->e.guid.entityid, DDSI_VENDORID_ECLIPSE) && !tp->e.onlylocal) - { - unsigned entityid = ddsi_determine_topic_writer (tp); - struct ddsi_writer *sedp_wr = get_sedp_writer (tp->pp, entityid); - ddsrt_mutex_lock (&tp->e.qos_lock); - // the allocation type info object is freed with the plist - res = ddsi_sedp_write_topic_impl (sedp_wr, alive, &tp->e.guid, tp->definition->xqos, ddsi_type_pair_complete_info (tp->e.gv, tp->definition->type_pair)); - ddsrt_mutex_unlock (&tp->e.qos_lock); - } - return res; -} - -#endif /* DDS_HAS_TOPIC_DISCOVERY */ - -int ddsi_sedp_write_writer (struct ddsi_writer *wr) -{ - if ((!ddsi_is_builtin_entityid(wr->e.guid.entityid, DDSI_VENDORID_ECLIPSE)) && (!wr->e.onlylocal)) - { - unsigned entityid = ddsi_determine_publication_writer(wr); - struct ddsi_writer *sedp_wr = get_sedp_writer (wr->c.pp, entityid); - ddsi_security_info_t *security = NULL; -#ifdef DDS_HAS_SSM - struct ddsi_addrset *as = wr->ssm_as; -#else - struct ddsi_addrset *as = NULL; -#endif -#ifdef DDS_HAS_SECURITY - ddsi_security_info_t tmp; - if (ddsi_omg_get_writer_security_info (wr, &tmp)) - { - security = &tmp; - } +#include "ddsi__discovery_topic.h" #endif +#include "ddsi__serdata_plist.h" +#include "ddsi__radmin.h" +#include "ddsi__entity_index.h" +#include "ddsi__entity.h" +#include "ddsi__participant.h" +#include "ddsi__xmsg.h" +#include "ddsi__transmit.h" +#include "ddsi__lease.h" +#include "ddsi__security_omg.h" +#include "ddsi__pmd.h" +#include "ddsi__endpoint.h" +#include "ddsi__plist.h" +#include "ddsi__proxy_endpoint.h" +#include "ddsi__proxy_participant.h" +#include "ddsi__topic.h" +#include "ddsi__tran.h" +#include "ddsi__typelib.h" +#include "ddsi__vendor.h" +#include "ddsi__xqos.h" +#include "ddsi__addrset.h" #ifdef DDS_HAS_TYPE_DISCOVERY - return sedp_write_endpoint_impl (sedp_wr, 1, &wr->e.guid, &wr->c, wr->xqos, as, security, wr->type); -#else - return sedp_write_endpoint_impl (sedp_wr, 1, &wr->e.guid, &wr->c, wr->xqos, as, security); +#include "ddsi__typelookup.h" #endif - } - return 0; -} - -int ddsi_sedp_write_reader (struct ddsi_reader *rd) -{ - if (ddsi_is_builtin_entityid (rd->e.guid.entityid, DDSI_VENDORID_ECLIPSE) || rd->e.onlylocal) - return 0; - unsigned entityid = ddsi_determine_subscription_writer(rd); - struct ddsi_writer *sedp_wr = get_sedp_writer (rd->c.pp, entityid); - ddsi_security_info_t *security = NULL; - struct ddsi_addrset *as = NULL; -#ifdef DDS_HAS_NETWORK_PARTITIONS - if (rd->uc_as != NULL || rd->mc_as != NULL) - { - // FIXME: do this without first creating a temporary addrset - as = ddsi_new_addrset (); - // use a placeholder connection to avoid exploding the multicast addreses to multiple - // interfaces - for (const struct ddsi_networkpartition_address *a = rd->uc_as; a != NULL; a = a->next) - ddsi_add_xlocator_to_addrset(rd->e.gv, as, &(const ddsi_xlocator_t) { - .c = a->loc, - .conn = rd->e.gv->xmit_conns[0] }); - for (const struct ddsi_networkpartition_address *a = rd->mc_as; a != NULL; a = a->next) - ddsi_add_xlocator_to_addrset(rd->e.gv, as, &(const ddsi_xlocator_t) { - .c = a->loc, - .conn = rd->e.gv->xmit_conns[0] }); - } -#endif #ifdef DDS_HAS_SECURITY - ddsi_security_info_t tmp; - if (ddsi_omg_get_reader_security_info (rd, &tmp)) - { - security = &tmp; - } -#endif -#ifdef DDS_HAS_TYPE_DISCOVERY - const int ret = sedp_write_endpoint_impl (sedp_wr, 1, &rd->e.guid, &rd->c, rd->xqos, as, security, rd->type); -#else - const int ret = sedp_write_endpoint_impl (sedp_wr, 1, &rd->e.guid, &rd->c, rd->xqos, as, security); -#endif - ddsi_unref_addrset (as); - return ret; -} - -int ddsi_sedp_dispose_unregister_writer (struct ddsi_writer *wr) -{ - if ((!ddsi_is_builtin_entityid(wr->e.guid.entityid, DDSI_VENDORID_ECLIPSE)) && (!wr->e.onlylocal)) - { - unsigned entityid = ddsi_determine_publication_writer(wr); - struct ddsi_writer *sedp_wr = get_sedp_writer (wr->c.pp, entityid); -#ifdef DDS_HAS_TYPE_DISCOVERY - return sedp_write_endpoint_impl (sedp_wr, 0, &wr->e.guid, NULL, NULL, NULL, NULL, NULL); -#else - return sedp_write_endpoint_impl (sedp_wr, 0, &wr->e.guid, NULL, NULL, NULL, NULL); -#endif - } - return 0; -} - -int ddsi_sedp_dispose_unregister_reader (struct ddsi_reader *rd) -{ - if ((!ddsi_is_builtin_entityid(rd->e.guid.entityid, DDSI_VENDORID_ECLIPSE)) && (!rd->e.onlylocal)) - { - unsigned entityid = ddsi_determine_subscription_writer(rd); - struct ddsi_writer *sedp_wr = get_sedp_writer (rd->c.pp, entityid); -#ifdef DDS_HAS_TYPE_DISCOVERY - return sedp_write_endpoint_impl (sedp_wr, 0, &rd->e.guid, NULL, NULL, NULL, NULL, NULL); -#else - return sedp_write_endpoint_impl (sedp_wr, 0, &rd->e.guid, NULL, NULL, NULL, NULL); -#endif - } - return 0; -} - -static const char *durability_to_string (dds_durability_kind_t k) -{ - switch (k) - { - case DDS_DURABILITY_VOLATILE: return "volatile"; - case DDS_DURABILITY_TRANSIENT_LOCAL: return "transient-local"; - case DDS_DURABILITY_TRANSIENT: return "transient"; - case DDS_DURABILITY_PERSISTENT: return "persistent"; - } - return "undefined-durability"; -} - -static struct ddsi_proxy_participant *implicitly_create_proxypp (struct ddsi_domaingv *gv, const ddsi_guid_t *ppguid, ddsi_plist_t *datap /* note: potentially modifies datap */, const ddsi_guid_prefix_t *src_guid_prefix, ddsi_vendorid_t vendorid, ddsrt_wctime_t timestamp, ddsi_seqno_t seq) -{ - ddsi_guid_t privguid; - ddsi_plist_t pp_plist; - - if (memcmp (&ppguid->prefix, src_guid_prefix, sizeof (ppguid->prefix)) == 0) - /* if the writer is owned by the participant itself, we're not interested */ - return NULL; - - privguid.prefix = *src_guid_prefix; - privguid.entityid = ddsi_to_entityid (DDSI_ENTITYID_PARTICIPANT); - ddsi_plist_init_empty(&pp_plist); - - if (ddsi_vendor_is_cloud (vendorid)) - { - ddsi_vendorid_t actual_vendorid; - /* Some endpoint that we discovered through the DS, but then it must have at least some locators */ - GVTRACE (" from-DS %"PRIx32":%"PRIx32":%"PRIx32":%"PRIx32, PGUID (privguid)); - /* avoid "no address" case, so we never create the proxy participant for nothing (FIXME: rework some of this) */ - if (!(datap->present & (PP_UNICAST_LOCATOR | PP_MULTICAST_LOCATOR))) - { - GVTRACE (" data locator absent\n"); - goto err; - } - GVTRACE (" new-proxypp "PGUIDFMT"\n", PGUID (*ppguid)); - /* We need to handle any source of entities, but we really want to try to keep the GIDs (and - certainly the systemId component) unchanged for OSPL. The new proxy participant will take - the GID from the GUID if it is from a "modern" OSPL that advertises it includes all GIDs in - the endpoint discovery; else if it is OSPL it will take at the systemId and fake the rest. - However, (1) Cloud filters out the GIDs from the discovery, and (2) DDSI2 deliberately - doesn't include the GID for internally generated endpoints (such as the fictitious transient - data readers) to signal that these are internal and have no GID (and not including a GID if - there is none is quite a reasonable approach). Point (2) means we have no reliable way of - determining whether GIDs are included based on the first endpoint, and so there is no point - doing anything about (1). That means we fall back to the legacy mode of locally generating - GIDs but leaving the system id unchanged if the remote is OSPL. */ - actual_vendorid = (datap->present & PP_VENDORID) ? datap->vendorid : vendorid; - (void) ddsi_new_proxy_participant (gv, ppguid, 0, &privguid, ddsi_new_addrset(), ddsi_new_addrset(), &pp_plist, DDS_INFINITY, actual_vendorid, DDSI_CF_IMPLICITLY_CREATED_PROXYPP, timestamp, seq); - } - else if (ppguid->prefix.u[0] == src_guid_prefix->u[0] && ddsi_vendor_is_eclipse_or_opensplice (vendorid)) - { - /* FIXME: requires address sets to be those of ddsi2, no built-in - readers or writers, only if remote ddsi2 is provably running - with a minimal built-in endpoint set */ - struct ddsi_proxy_participant *privpp; - if ((privpp = ddsi_entidx_lookup_proxy_participant_guid (gv->entity_index, &privguid)) == NULL) { - GVTRACE (" unknown-src-proxypp?\n"); - goto err; - } else if (!privpp->is_ddsi2_pp) { - GVTRACE (" src-proxypp-not-ddsi2?\n"); - goto err; - } else if (!privpp->minimal_bes_mode) { - GVTRACE (" src-ddsi2-not-minimal-bes-mode?\n"); - goto err; - } else { - struct ddsi_addrset *as_default, *as_meta; - ddsi_plist_t tmp_plist; - GVTRACE (" from-ddsi2 "PGUIDFMT, PGUID (privguid)); - ddsi_plist_init_empty (&pp_plist); - - ddsrt_mutex_lock (&privpp->e.lock); - as_default = ddsi_ref_addrset(privpp->as_default); - as_meta = ddsi_ref_addrset(privpp->as_meta); - /* copy just what we need */ - tmp_plist = *privpp->plist; - tmp_plist.present = PP_PARTICIPANT_GUID | PP_ADLINK_PARTICIPANT_VERSION_INFO; - tmp_plist.participant_guid = *ppguid; - ddsi_plist_mergein_missing (&pp_plist, &tmp_plist, ~(uint64_t)0, ~(uint64_t)0); - ddsrt_mutex_unlock (&privpp->e.lock); - - pp_plist.adlink_participant_version_info.flags &= ~DDSI_ADLINK_FL_PARTICIPANT_IS_DDSI2; - ddsi_new_proxy_participant (gv, ppguid, 0, &privguid, as_default, as_meta, &pp_plist, DDS_INFINITY, vendorid, DDSI_CF_IMPLICITLY_CREATED_PROXYPP | DDSI_CF_PROXYPP_NO_SPDP, timestamp, seq); - } - } - - err: - ddsi_plist_fini (&pp_plist); - return ddsi_entidx_lookup_proxy_participant_guid (gv->entity_index, ppguid); -} - -static bool check_sedp_kind_and_guid (ddsi_sedp_kind_t sedp_kind, const ddsi_guid_t *entity_guid) -{ - switch (sedp_kind) - { - case SEDP_KIND_TOPIC: - return ddsi_is_topic_entityid (entity_guid->entityid); - case SEDP_KIND_WRITER: - return ddsi_is_writer_entityid (entity_guid->entityid); - case SEDP_KIND_READER: - return ddsi_is_reader_entityid (entity_guid->entityid); - } - assert (0); - return false; -} - -static bool handle_sedp_checks (struct ddsi_domaingv * const gv, ddsi_sedp_kind_t sedp_kind, ddsi_guid_t *entity_guid, ddsi_plist_t *datap, - const ddsi_guid_prefix_t *src_guid_prefix, ddsi_vendorid_t vendorid, ddsrt_wctime_t timestamp, - struct ddsi_proxy_participant **proxypp, ddsi_guid_t *ppguid) -{ -#define E(msg, lbl) do { GVLOGDISC (msg); return false; } while (0) - if (!check_sedp_kind_and_guid (sedp_kind, entity_guid)) - E (" SEDP topic/GUID entity kind mismatch\n", err); - ppguid->prefix = entity_guid->prefix; - ppguid->entityid.u = DDSI_ENTITYID_PARTICIPANT; - // Accept the presence of a participant GUID, but only if it matches - if ((datap->present & PP_PARTICIPANT_GUID) && memcmp (&datap->participant_guid, ppguid, sizeof (*ppguid)) != 0) - E (" endpoint/participant GUID mismatch", err); - if (ddsi_is_deleted_participant_guid (gv->deleted_participants, ppguid, DDSI_DELETED_PPGUID_REMOTE)) - E (" local dead pp?\n", err); - if (ddsi_entidx_lookup_participant_guid (gv->entity_index, ppguid) != NULL) - E (" local pp?\n", err); - if (ddsi_is_builtin_entityid (entity_guid->entityid, vendorid)) - E (" built-in\n", err); - if (!(datap->qos.present & DDSI_QP_TOPIC_NAME)) - E (" no topic?\n", err); - if (!(datap->qos.present & DDSI_QP_TYPE_NAME)) - E (" no typename?\n", err); - if ((*proxypp = ddsi_entidx_lookup_proxy_participant_guid (gv->entity_index, ppguid)) == NULL) - { - GVLOGDISC (" unknown-proxypp"); - if ((*proxypp = implicitly_create_proxypp (gv, ppguid, datap, src_guid_prefix, vendorid, timestamp, 0)) == NULL) - E ("?\n", err); - /* Repeat regular SEDP trace for convenience */ - GVLOGDISC ("SEDP ST0 "PGUIDFMT" (cont)", PGUID (*entity_guid)); - } - return true; -#undef E -} - -struct ddsi_addrset_from_locatorlists_collect_interfaces_arg { - const struct ddsi_domaingv *gv; - interface_set_t *intfs; -}; - -/** @brief Figure out which interfaces are touched by (extended) locator @p loc - * - * Does this by looking up the connection in @p loc in the set of transmit connections. (There's plenty of room for optimisation here.) - * - * @param[in] loc locator - * @param[in] varg argument pointer, must point to a struct ddsi_addrset_from_locatorlists_collect_interfaces_arg - */ -static void addrset_from_locatorlists_collect_interfaces (const ddsi_xlocator_t *loc, void *varg) -{ - struct ddsi_addrset_from_locatorlists_collect_interfaces_arg *arg = varg; - struct ddsi_domaingv const * const gv = arg->gv; - for (int i = 0; i < gv->n_interfaces; i++) - { - //GVTRACE(" {%p,%p}", loc->conn, gv->xmit_conns[i]); - if (loc->conn == gv->xmit_conns[i]) - { - arg->intfs->xs[i] = true; - break; - } - } -} - -struct ddsi_addrset *ddsi_get_endpoint_addrset (const struct ddsi_domaingv *gv, const ddsi_plist_t *datap, struct ddsi_addrset *proxypp_as_default, const ddsi_locator_t *rst_srcloc) -{ - const ddsi_locators_t emptyset = { .n = 0, .first = NULL, .last = NULL }; - const ddsi_locators_t *uc = (datap->present & PP_UNICAST_LOCATOR) ? &datap->unicast_locators : ∅ - const ddsi_locators_t *mc = (datap->present & PP_MULTICAST_LOCATOR) ? &datap->multicast_locators : ∅ - ddsi_locator_t srcloc; - if (rst_srcloc == NULL) - ddsi_set_unspec_locator (&srcloc); - else // force use of source locator - { - uc = ∅ - srcloc = *rst_srcloc; - } - - // any interface that works for the participant is presumed ok - interface_set_t intfs; - interface_set_init (&intfs); - ddsi_addrset_forall (proxypp_as_default, addrset_from_locatorlists_collect_interfaces, &(struct ddsi_addrset_from_locatorlists_collect_interfaces_arg){ - .gv = gv, .intfs = &intfs - }); - //GVTRACE(" {%d%d%d%d}", intfs.xs[0], intfs.xs[1], intfs.xs[2], intfs.xs[3]); - struct ddsi_addrset *as = addrset_from_locatorlists (gv, uc, mc, &srcloc, &intfs); - // if SEDP gives: - // - no addresses, use ppant uni- and multicast addresses - // - only multicast, use those for multicast and use ppant address for unicast - // - only unicast, use only those (i.e., disable multicast for this reader) - // - both, use only those - // FIXME: then you can't do a specific unicast address + SSM ... oh well - if (ddsi_addrset_empty (as)) - ddsi_copy_addrset_into_addrset_mc (gv, as, proxypp_as_default); - if (ddsi_addrset_empty_uc (as)) - ddsi_copy_addrset_into_addrset_uc (gv, as, proxypp_as_default); - return as; -} - -static void handle_sedp_alive_endpoint (const struct ddsi_receiver_state *rst, ddsi_seqno_t seq, ddsi_plist_t *datap /* note: potentially modifies datap */, ddsi_sedp_kind_t sedp_kind, const ddsi_guid_prefix_t *src_guid_prefix, ddsi_vendorid_t vendorid, ddsrt_wctime_t timestamp) -{ -#define E(msg, lbl) do { GVLOGDISC (msg); goto lbl; } while (0) - struct ddsi_domaingv * const gv = rst->gv; - struct ddsi_proxy_participant *proxypp; - struct ddsi_proxy_writer * pwr = NULL; - struct ddsi_proxy_reader * prd = NULL; - ddsi_guid_t ppguid; - dds_qos_t *xqos; - int reliable; - struct ddsi_addrset *as; -#ifdef DDS_HAS_SSM - int ssm; -#endif - - assert (datap); - assert (datap->present & PP_ENDPOINT_GUID); - GVLOGDISC (" "PGUIDFMT, PGUID (datap->endpoint_guid)); - - if (!handle_sedp_checks (gv, sedp_kind, &datap->endpoint_guid, datap, src_guid_prefix, vendorid, timestamp, &proxypp, &ppguid)) - goto err; - - xqos = &datap->qos; - if (sedp_kind == SEDP_KIND_READER) - ddsi_xqos_mergein_missing (xqos, &ddsi_default_qos_reader, ~(uint64_t)0); - else if (sedp_kind == SEDP_KIND_WRITER) - { - ddsi_xqos_mergein_missing (xqos, &ddsi_default_qos_writer, ~(uint64_t)0); - if (!ddsi_vendor_is_eclipse_or_adlink (vendorid)) - { - // there is a difference in interpretation of autodispose between vendors - xqos->writer_data_lifecycle.autodispose_unregistered_instances = 0; - } - } - else - E (" invalid entity kind\n", err); - - /* After copy + merge, should have at least the ones present in the - input. Also verify reliability and durability are present, - because we explicitly read those. */ - assert ((xqos->present & datap->qos.present) == datap->qos.present); - assert (xqos->present & DDSI_QP_RELIABILITY); - assert (xqos->present & DDSI_QP_DURABILITY); - reliable = (xqos->reliability.kind == DDS_RELIABILITY_RELIABLE); - - GVLOGDISC (" %s %s %s %s: %s%s.%s/%s", - reliable ? "reliable" : "best-effort", - durability_to_string (xqos->durability.kind), - sedp_kind == SEDP_KIND_WRITER ? "writer" : "reader", - (xqos->present & DDSI_QP_ENTITY_NAME) ? xqos->entity_name : "unnamed", - ((!(xqos->present & DDSI_QP_PARTITION) || xqos->partition.n == 0 || *xqos->partition.strs[0] == '\0') - ? "(default)" : xqos->partition.strs[0]), - ((xqos->present & DDSI_QP_PARTITION) && xqos->partition.n > 1) ? "+" : "", - xqos->topic_name, xqos->type_name); - - if (sedp_kind == SEDP_KIND_READER && (datap->present & PP_EXPECTS_INLINE_QOS) && datap->expects_inline_qos) - E ("******* AARGH - it expects inline QoS ********\n", err); - - ddsi_omg_log_endpoint_protection (gv, datap); - if (ddsi_omg_is_endpoint_protected (datap) && !ddsi_omg_proxy_participant_is_secure (proxypp)) - E (" remote endpoint is protected while local federation is not secure\n", err); - - if (sedp_kind == SEDP_KIND_WRITER) - pwr = ddsi_entidx_lookup_proxy_writer_guid (gv->entity_index, &datap->endpoint_guid); - else - prd = ddsi_entidx_lookup_proxy_reader_guid (gv->entity_index, &datap->endpoint_guid); - if (pwr || prd) - { - /* Re-bind the proxy participant to the discovery service - and do this if it is currently - bound to another DS instance, because that other DS instance may have already failed and - with a new one taking over, without our noticing it. */ - GVLOGDISC (" known%s", ddsi_vendor_is_cloud (vendorid) ? "-DS" : ""); - if (ddsi_vendor_is_cloud (vendorid) && proxypp->implicitly_created && memcmp (&proxypp->privileged_pp_guid.prefix, src_guid_prefix, sizeof(proxypp->privileged_pp_guid.prefix)) != 0) - { - GVLOGDISC (" "PGUIDFMT" attach-to-DS "PGUIDFMT, PGUID(proxypp->e.guid), PGUIDPREFIX(*src_guid_prefix), proxypp->privileged_pp_guid.entityid.u); - ddsrt_mutex_lock (&proxypp->e.lock); - proxypp->privileged_pp_guid.prefix = *src_guid_prefix; - ddsi_lease_set_expiry (proxypp->lease, DDSRT_ETIME_NEVER); - ddsrt_mutex_unlock (&proxypp->e.lock); - } - GVLOGDISC ("\n"); - } - else - { - GVLOGDISC (" NEW"); - } - - as = ddsi_get_endpoint_addrset (gv, datap, proxypp->as_default, gv->config.tcp_use_peeraddr_for_unicast ? &rst->srcloc : NULL); - if (ddsi_addrset_empty (as)) - { - ddsi_unref_addrset (as); - E (" no address", err); - } - - ddsi_log_addrset(gv, DDS_LC_DISCOVERY, " (as", as); -#ifdef DDS_HAS_SSM - ssm = 0; - if (sedp_kind == SEDP_KIND_WRITER) - ssm = ddsi_addrset_contains_ssm (gv, as); - else if (datap->present & PP_READER_FAVOURS_SSM) - ssm = (datap->reader_favours_ssm.state != 0); - GVLOGDISC (" ssm=%u", ssm); -#endif - GVLOGDISC (") QOS={"); - ddsi_xqos_log (DDS_LC_DISCOVERY, &gv->logconfig, xqos); - GVLOGDISC ("}\n"); - - if ((datap->endpoint_guid.entityid.u & DDSI_ENTITYID_SOURCE_MASK) == DDSI_ENTITYID_SOURCE_VENDOR && !ddsi_vendor_is_eclipse_or_adlink (vendorid)) - { - GVLOGDISC ("ignoring vendor-specific endpoint "PGUIDFMT"\n", PGUID (datap->endpoint_guid)); - } - else - { - if (sedp_kind == SEDP_KIND_WRITER) - { - if (pwr) - ddsi_update_proxy_writer (pwr, seq, as, xqos, timestamp); - else - { - /* not supposed to get here for built-in ones, so can determine the channel based on the transport priority */ - assert (!ddsi_is_builtin_entityid (datap->endpoint_guid.entityid, vendorid)); - ddsi_new_proxy_writer (gv, &ppguid, &datap->endpoint_guid, as, datap, gv->user_dqueue, gv->xevents, timestamp, seq); - } - } - else - { - if (prd) - ddsi_update_proxy_reader (prd, seq, as, xqos, timestamp); - else - { -#ifdef DDS_HAS_SSM - ddsi_new_proxy_reader (gv, &ppguid, &datap->endpoint_guid, as, datap, timestamp, seq, ssm); -#else - ddsi_new_proxy_reader (gv, &ppguid, &datap->endpoint_guid, as, datap, timestamp, seq); -#endif - } - } - } - ddsi_unref_addrset (as); - -err: - return; -#undef E -} - -static void handle_sedp_dead_endpoint (const struct ddsi_receiver_state *rst, ddsi_plist_t *datap, ddsi_sedp_kind_t sedp_kind, ddsrt_wctime_t timestamp) -{ - struct ddsi_domaingv * const gv = rst->gv; - int res = -1; - assert (datap->present & PP_ENDPOINT_GUID); - GVLOGDISC (" "PGUIDFMT" ", PGUID (datap->endpoint_guid)); - if (!check_sedp_kind_and_guid (sedp_kind, &datap->endpoint_guid)) - return; - else if (sedp_kind == SEDP_KIND_WRITER) - res = ddsi_delete_proxy_writer (gv, &datap->endpoint_guid, timestamp, 0); - else - res = ddsi_delete_proxy_reader (gv, &datap->endpoint_guid, timestamp, 0); - GVLOGDISC (" %s\n", (res < 0) ? " unknown" : " delete"); -} - -#ifdef DDS_HAS_TOPIC_DISCOVERY - -static void handle_sedp_alive_topic (const struct ddsi_receiver_state *rst, ddsi_seqno_t seq, ddsi_plist_t *datap /* note: potentially modifies datap */, const ddsi_guid_prefix_t *src_guid_prefix, ddsi_vendorid_t vendorid, ddsrt_wctime_t timestamp) -{ - struct ddsi_domaingv * const gv = rst->gv; - struct ddsi_proxy_participant *proxypp; - ddsi_guid_t ppguid; - dds_qos_t *xqos; - int reliable; - const ddsi_typeid_t *type_id_minimal = NULL, *type_id_complete = NULL; - - assert (datap); - assert (datap->present & PP_CYCLONE_TOPIC_GUID); - GVLOGDISC (" "PGUIDFMT, PGUID (datap->topic_guid)); - - if (!handle_sedp_checks (gv, SEDP_KIND_TOPIC, &datap->topic_guid, datap, src_guid_prefix, vendorid, timestamp, &proxypp, &ppguid)) - return; - - xqos = &datap->qos; - ddsi_xqos_mergein_missing (xqos, &ddsi_default_qos_topic, ~(uint64_t)0); - /* After copy + merge, should have at least the ones present in the - input. Also verify reliability and durability are present, - because we explicitly read those. */ - assert ((xqos->present & datap->qos.present) == datap->qos.present); - assert (xqos->present & DDSI_QP_RELIABILITY); - assert (xqos->present & DDSI_QP_DURABILITY); - reliable = (xqos->reliability.kind == DDS_RELIABILITY_RELIABLE); - - GVLOGDISC (" %s %s %s: %s/%s", - reliable ? "reliable" : "best-effort", - durability_to_string (xqos->durability.kind), - "topic", xqos->topic_name, xqos->type_name); - if (xqos->present & DDSI_QP_TYPE_INFORMATION) - { - struct ddsi_typeid_str strm, strc; - type_id_minimal = ddsi_typeinfo_minimal_typeid (xqos->type_information); - type_id_complete = ddsi_typeinfo_complete_typeid (xqos->type_information); - GVLOGDISC (" tid %s/%s", ddsi_make_typeid_str(&strm, type_id_minimal), ddsi_make_typeid_str(&strc, type_id_complete)); - } - GVLOGDISC (" QOS={"); - ddsi_xqos_log (DDS_LC_DISCOVERY, &gv->logconfig, xqos); - GVLOGDISC ("}\n"); - - if ((datap->topic_guid.entityid.u & DDSI_ENTITYID_SOURCE_MASK) == DDSI_ENTITYID_SOURCE_VENDOR && !ddsi_vendor_is_eclipse_or_adlink (vendorid)) - { - GVLOGDISC ("ignoring vendor-specific topic "PGUIDFMT"\n", PGUID (datap->topic_guid)); - } - else - { - // FIXME: check compatibility with known topic definitions - struct ddsi_proxy_topic *ptp = ddsi_lookup_proxy_topic (proxypp, &datap->topic_guid); - if (ptp) - { - GVLOGDISC (" update known proxy-topic%s\n", ddsi_vendor_is_cloud (vendorid) ? "-DS" : ""); - ddsi_update_proxy_topic (proxypp, ptp, seq, xqos, timestamp); - } - else - { - GVLOGDISC (" NEW proxy-topic"); - if (ddsi_new_proxy_topic (proxypp, seq, &datap->topic_guid, type_id_minimal, type_id_complete, xqos, timestamp) != DDS_RETCODE_OK) - GVLOGDISC (" failed"); - } - } -} - -static void handle_sedp_dead_topic (const struct ddsi_receiver_state *rst, ddsi_plist_t *datap, ddsrt_wctime_t timestamp) -{ - struct ddsi_proxy_participant *proxypp; - struct ddsi_proxy_topic *proxytp; - struct ddsi_domaingv * const gv = rst->gv; - assert (datap->present & PP_CYCLONE_TOPIC_GUID); - GVLOGDISC (" "PGUIDFMT" ", PGUID (datap->topic_guid)); - if (!check_sedp_kind_and_guid (SEDP_KIND_TOPIC, &datap->topic_guid)) - return; - ddsi_guid_t ppguid = { .prefix = datap->topic_guid.prefix, .entityid.u = DDSI_ENTITYID_PARTICIPANT }; - if ((proxypp = ddsi_entidx_lookup_proxy_participant_guid (gv->entity_index, &ppguid)) == NULL) - GVLOGDISC (" unknown proxypp\n"); - else if ((proxytp = ddsi_lookup_proxy_topic (proxypp, &datap->topic_guid)) == NULL) - GVLOGDISC (" unknown proxy topic\n"); - else - { - ddsrt_mutex_lock (&proxypp->e.lock); - int res = ddsi_delete_proxy_topic_locked (proxypp, proxytp, timestamp); - GVLOGDISC (" %s\n", res == DDS_RETCODE_PRECONDITION_NOT_MET ? " already-deleting" : " delete"); - ddsrt_mutex_unlock (&proxypp->e.lock); - } -} - -#endif /* DDS_HAS_TOPIC_DISCOVERY */ - -static void handle_sedp (const struct ddsi_receiver_state *rst, ddsi_seqno_t seq, struct ddsi_serdata *serdata, ddsi_sedp_kind_t sedp_kind) -{ - ddsi_plist_t decoded_data; - if (ddsi_serdata_to_sample (serdata, &decoded_data, NULL, NULL)) - { - struct ddsi_domaingv * const gv = rst->gv; - GVLOGDISC ("SEDP ST%"PRIx32, serdata->statusinfo); - switch (serdata->statusinfo & (DDSI_STATUSINFO_DISPOSE | DDSI_STATUSINFO_UNREGISTER)) - { - case 0: - switch (sedp_kind) - { - case SEDP_KIND_TOPIC: -#ifdef DDS_HAS_TOPIC_DISCOVERY - handle_sedp_alive_topic (rst, seq, &decoded_data, &rst->src_guid_prefix, rst->vendor, serdata->timestamp); -#endif - break; - case SEDP_KIND_READER: - case SEDP_KIND_WRITER: - handle_sedp_alive_endpoint (rst, seq, &decoded_data, sedp_kind, &rst->src_guid_prefix, rst->vendor, serdata->timestamp); - break; - } - break; - case DDSI_STATUSINFO_DISPOSE: - case DDSI_STATUSINFO_UNREGISTER: - case (DDSI_STATUSINFO_DISPOSE | DDSI_STATUSINFO_UNREGISTER): - switch (sedp_kind) - { - case SEDP_KIND_TOPIC: -#ifdef DDS_HAS_TOPIC_DISCOVERY - handle_sedp_dead_topic (rst, &decoded_data, serdata->timestamp); +#include "ddsi__security_exchange.h" #endif - break; - case SEDP_KIND_READER: - case SEDP_KIND_WRITER: - handle_sedp_dead_endpoint (rst, &decoded_data, sedp_kind, serdata->timestamp); - break; - } - break; - } - ddsi_plist_fini (&decoded_data); - } -} #ifdef DDS_HAS_TYPE_DISCOVERY static void handle_typelookup (const struct ddsi_receiver_state *rst, ddsi_entityid_t wr_entity_id, struct ddsi_serdata *serdata) @@ -1900,9 +81,6 @@ static void handle_typelookup (const struct ddsi_receiver_state *rst, ddsi_entit } #endif -/****************************************************************************** - *****************************************************************************/ - int ddsi_builtins_dqueue_handler (const struct ddsi_rsample_info *sampleinfo, const struct ddsi_rdata *fragchain, UNUSED_ARG (const ddsi_guid_t *rdguid), UNUSED_ARG (void *qarg)) { struct ddsi_domaingv * const gv = sampleinfo->rst->gv; @@ -2097,19 +275,21 @@ int ddsi_builtins_dqueue_handler (const struct ddsi_rsample_info *sampleinfo, co { case DDSI_ENTITYID_SPDP_BUILTIN_PARTICIPANT_WRITER: case DDSI_ENTITYID_SPDP_RELIABLE_BUILTIN_PARTICIPANT_SECURE_WRITER: - handle_spdp (sampleinfo->rst, srcguid.entityid, sampleinfo->seq, d); + ddsi_handle_spdp (sampleinfo->rst, srcguid.entityid, sampleinfo->seq, d); break; case DDSI_ENTITYID_SEDP_BUILTIN_PUBLICATIONS_WRITER: case DDSI_ENTITYID_SEDP_BUILTIN_PUBLICATIONS_SECURE_WRITER: - handle_sedp (sampleinfo->rst, sampleinfo->seq, d, SEDP_KIND_WRITER); + ddsi_handle_sedp (sampleinfo->rst, sampleinfo->seq, d, SEDP_KIND_WRITER); break; case DDSI_ENTITYID_SEDP_BUILTIN_SUBSCRIPTIONS_WRITER: case DDSI_ENTITYID_SEDP_BUILTIN_SUBSCRIPTIONS_SECURE_WRITER: - handle_sedp (sampleinfo->rst, sampleinfo->seq, d, SEDP_KIND_READER); + ddsi_handle_sedp (sampleinfo->rst, sampleinfo->seq, d, SEDP_KIND_READER); break; +#ifdef DDS_HAS_TOPIC_DISCOVERY case DDSI_ENTITYID_SEDP_BUILTIN_TOPIC_WRITER: - handle_sedp (sampleinfo->rst, sampleinfo->seq, d, SEDP_KIND_TOPIC); + ddsi_handle_sedp (sampleinfo->rst, sampleinfo->seq, d, SEDP_KIND_TOPIC); break; +#endif case DDSI_ENTITYID_P2P_BUILTIN_PARTICIPANT_MESSAGE_WRITER: case DDSI_ENTITYID_P2P_BUILTIN_PARTICIPANT_MESSAGE_SECURE_WRITER: ddsi_handle_pmd_message (sampleinfo->rst, d); diff --git a/src/core/ddsi/src/ddsi_discovery_addrset.c b/src/core/ddsi/src/ddsi_discovery_addrset.c new file mode 100644 index 0000000000..81af44a106 --- /dev/null +++ b/src/core/ddsi/src/ddsi_discovery_addrset.c @@ -0,0 +1,252 @@ +/* + * Copyright(c) 2023 ZettaScale Technology and others + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License + * v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ +#include +#include +#include +#include + +#include "dds/version.h" +#include "dds/ddsrt/heap.h" +#include "dds/ddsrt/log.h" +#include "dds/ddsi/ddsi_domaingv.h" +#include "ddsi__discovery_addrset.h" +#include "ddsi__participant.h" +#include "ddsi__tran.h" +#include "ddsi__addrset.h" + +void ddsi_interface_set_init (ddsi_interface_set_t *intfs) +{ + for (size_t i = 0; i < sizeof (intfs->xs) / sizeof (intfs->xs[0]); i++) + intfs->xs[i] = false; +} + +bool ddsi_include_multicast_locator_in_discovery (const struct ddsi_domaingv *gv) +{ +#ifdef DDS_HAS_SSM + /* Note that if the default multicast address is an SSM address, + we will simply advertise it. The recipients better understand + it means the writers will publish to address and the readers + favour SSM. */ + if (ddsi_is_ssm_mcaddr (gv, &gv->loc_default_mc)) + return (gv->config.allowMulticast & DDSI_AMC_SSM) != 0; + else + return (gv->config.allowMulticast & DDSI_AMC_ASM) != 0; +#else + return (gv->config.allowMulticast & DDSI_AMC_ASM) != 0; +#endif +} + +static void allowmulticast_aware_add_to_addrset (const struct ddsi_domaingv *gv, uint32_t allow_multicast, struct ddsi_addrset *as, const ddsi_xlocator_t *loc) +{ +#ifdef DDS_HAS_SSM + if (ddsi_is_ssm_mcaddr (gv, &loc->c)) + { + if (!(allow_multicast & DDSI_AMC_SSM)) + return; + } + else if (ddsi_is_mcaddr (gv, &loc->c)) + { + if (!(allow_multicast & DDSI_AMC_ASM)) + return; + } +#else + if (ddsi_is_mcaddr (gv, &loc->c) && !(allow_multicast & DDSI_AMC_ASM)) + return; +#endif + ddsi_add_xlocator_to_addrset (gv, as, loc); +} + +static void addrset_from_locatorlists_add_one (struct ddsi_domaingv const * const gv, const ddsi_locator_t *loc, struct ddsi_addrset *as, ddsi_interface_set_t *intfs, bool *direct) +{ + size_t interf_idx; + switch (ddsi_is_nearby_address (gv, loc, (size_t) gv->n_interfaces, gv->interfaces, &interf_idx)) + { + case DNAR_SELF: + case DNAR_LOCAL: + // if it matches an interface, use that one and record that this is a + // directly connected interface: those will then all be possibilities + // for transmitting multicasts (assuming capable, allowed, &c.) + assert (interf_idx < MAX_XMIT_CONNS); + ddsi_add_xlocator_to_addrset (gv, as, &(const ddsi_xlocator_t) { + .conn = gv->xmit_conns[interf_idx], + .c = *loc }); + intfs->xs[interf_idx] = true; + *direct = true; + break; + case DNAR_DISTANT: + // If DONT_ROUTE is set and there is no matching interface, then presumably + // one would not be able to reach this address. + if (!gv->config.dontRoute) + { + // Pick the first selected interface that isn't link-local or loopback + // (maybe it matters, maybe not, but it doesn't make sense to assign + // a transmit socket for a local interface to a distant host). If none + // exists, skip the address. + for (int i = 0; i < gv->n_interfaces; i++) + { + // do not use link-local or loopback interfaces transmit conn for distant nodes + if (gv->interfaces[i].link_local || gv->interfaces[i].loopback) + continue; + ddsi_add_xlocator_to_addrset (gv, as, &(const ddsi_xlocator_t) { + .conn = gv->xmit_conns[i], + .c = *loc }); + break; + } + } + break; + case DNAR_UNREACHABLE: + break; + } +} + +struct ddsi_addrset *ddsi_addrset_from_locatorlists (const struct ddsi_domaingv *gv, const ddsi_locators_t *uc, const ddsi_locators_t *mc, const ddsi_locator_t *srcloc, const ddsi_interface_set_t *inherited_intfs) +{ + struct ddsi_addrset *as = ddsi_new_addrset (); + ddsi_interface_set_t intfs; + ddsi_interface_set_init (&intfs); + + // if all interfaces are loopback, or all locators in uc are loopback, we're cool with loopback addresses + bool allow_loopback; + { + bool a = true; + for (int i = 0; i < gv->n_interfaces && a; i++) + if (!gv->interfaces[i].loopback) + a = false; + bool b = true; + // FIXME: what about the cases where SEDP gives just a loopback address, but the proxypp is known to be on a remote node? + for (struct ddsi_locators_one *l = uc->first; l != NULL && b; l = l->next) + b = ddsi_is_loopbackaddr (gv, &l->loc); + allow_loopback = (a || b); + } + + // if any non-loopback address is identical to one of our own addresses (actual or advertised), + // assume it is the same machine, in which case loopback addresses may be picked up + for (struct ddsi_locators_one *l = uc->first; l != NULL && !allow_loopback; l = l->next) + { + if (ddsi_is_loopbackaddr (gv, &l->loc)) + continue; + allow_loopback = (ddsi_is_nearby_address (gv, &l->loc, (size_t) gv->n_interfaces, gv->interfaces, NULL) == DNAR_SELF); + } + //GVTRACE(" allow_loopback=%d\n", allow_loopback); + + bool direct = false; + for (struct ddsi_locators_one *l = uc->first; l != NULL; l = l->next) + { +#if 0 + { + char buf[DDSI_LOCSTRLEN]; + ddsi_locator_to_string_no_port (buf, sizeof (buf), &l->loc); + GVTRACE("%s: ignore %d loopback %d\n", buf, l->loc.tran->m_ignore, ddsi_is_loopbackaddr (gv, &l->loc)); + } +#endif + // skip unrecognized ones, as well as loopback ones if not on the same host + if (!allow_loopback && ddsi_is_loopbackaddr (gv, &l->loc)) + continue; + + ddsi_locator_t loc = l->loc; + + // if the advertised locator matches our own external locator, than presumably + // it is the same machine and should be addressed using the actual interface + // address + bool extloc_of_self = false; + for (int i = 0; i < gv->n_interfaces; i++) + { + if (loc.kind == gv->interfaces[i].loc.kind && memcmp (loc.address, gv->interfaces[i].extloc.address, sizeof (loc.address)) == 0) + { + memcpy (loc.address, gv->interfaces[i].loc.address, sizeof (loc.address)); + extloc_of_self = true; + break; + } + } + + if (!extloc_of_self && loc.kind == DDSI_LOCATOR_KIND_UDPv4 && gv->extmask.kind != DDSI_LOCATOR_KIND_INVALID) + { + /* If the examined locator is in the same subnet as our own + external IP address, this locator will be translated into one + in the same subnet as our own local ip and selected. */ + assert (gv->n_interfaces == 1); // gv->extmask: the hack is only supported if limited to a single interface + struct in_addr tmp4 = *((struct in_addr *) (loc.address + 12)); + const struct in_addr ownip = *((struct in_addr *) (gv->interfaces[0].loc.address + 12)); + const struct in_addr extip = *((struct in_addr *) (gv->interfaces[0].extloc.address + 12)); + const struct in_addr extmask = *((struct in_addr *) (gv->extmask.address + 12)); + + if ((tmp4.s_addr & extmask.s_addr) == (extip.s_addr & extmask.s_addr)) + { + /* translate network part of the IP address from the external + one to the internal one */ + tmp4.s_addr = (tmp4.s_addr & ~extmask.s_addr) | (ownip.s_addr & extmask.s_addr); + memcpy (loc.address + 12, &tmp4, 4); + } + } + + addrset_from_locatorlists_add_one (gv, &loc, as, &intfs, &direct); + } + + if (ddsi_addrset_empty (as) && !ddsi_is_unspec_locator (srcloc)) + { + //GVTRACE("add srcloc\n"); + // FIXME: conn_read should provide interface information in source address + //GVTRACE (" add-srcloc"); + addrset_from_locatorlists_add_one (gv, srcloc, as, &intfs, &direct); + } + + if (ddsi_addrset_empty (as) && inherited_intfs) + { + // implies no interfaces enabled in "intfs" yet -- just use whatever + // we inherited for the purposes of selecting multicast addresses + assert (!direct); + for (int i = 0; i < gv->n_interfaces; i++) + assert (!intfs.xs[i]); + //GVTRACE (" using-inherited-intfs"); + intfs = *inherited_intfs; + } + else if (!direct && gv->config.multicast_ttl > 1) + { + //GVTRACE("assuming multicast routing works\n"); + // if not directly connected but multicast TTL allows routing, + // assume any non-local interface will do + //GVTRACE (" enabling-non-loopback/link-local"); + for (int i = 0; i < gv->n_interfaces; i++) + { + assert (!intfs.xs[i]); + intfs.xs[i] = !(gv->interfaces[i].link_local || gv->interfaces[i].loopback); + } + } + +#if 0 + GVTRACE("enabled interfaces for multicast:"); + for (int i = 0; i < gv->n_interfaces; i++) + { + if (intfs[i]) + GVTRACE(" %s(%d)", gv->interfaces[i].name, gv->interfaces[i].mc_capable); + } + GVTRACE("\n"); +#endif + + for (struct ddsi_locators_one *l = mc->first; l != NULL; l = l->next) + { + for (int i = 0; i < gv->n_interfaces; i++) + { + if (intfs.xs[i] && gv->interfaces[i].mc_capable) + { + const ddsi_xlocator_t loc = { + .conn = gv->xmit_conns[i], + .c = l->loc + }; + if (ddsi_factory_supports (loc.conn->m_factory, loc.c.kind)) + allowmulticast_aware_add_to_addrset (gv, gv->config.allowMulticast, as, &loc); + } + } + } + return as; +} + diff --git a/src/core/ddsi/src/ddsi_discovery_endpoint.c b/src/core/ddsi/src/ddsi_discovery_endpoint.c new file mode 100644 index 0000000000..d6f4860ab5 --- /dev/null +++ b/src/core/ddsi/src/ddsi_discovery_endpoint.c @@ -0,0 +1,593 @@ +/* + * Copyright(c) 2006 to 2022 ZettaScale Technology and others + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License + * v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ +//#include +#include +#include +#include +#include + +#include "dds/version.h" +#include "dds/ddsrt/heap.h" +#include "dds/ddsrt/log.h" +#include "dds/ddsi/ddsi_domaingv.h" +#include "ddsi__discovery_addrset.h" +#include "ddsi__discovery_sedp.h" +#include "ddsi__discovery_endpoint.h" +#include "ddsi__serdata_plist.h" +#include "ddsi__entity_index.h" +#include "ddsi__entity.h" +#include "ddsi__participant.h" +#include "ddsi__transmit.h" +#include "ddsi__lease.h" +#include "ddsi__security_omg.h" +#include "ddsi__endpoint.h" +#include "ddsi__plist.h" +#include "ddsi__proxy_endpoint.h" +#include "ddsi__tran.h" +#include "ddsi__vendor.h" +#include "ddsi__xqos.h" +#include "ddsi__addrset.h" + +struct add_locator_to_ps_arg { + struct ddsi_domaingv *gv; + ddsi_plist_t *ps; +}; + +static void add_locator_to_ps (const ddsi_locator_t *loc, void *varg) +{ + struct add_locator_to_ps_arg *arg = varg; + struct ddsi_locators_one *elem = ddsrt_malloc (sizeof (struct ddsi_locators_one)); + struct ddsi_locators *locs; + unsigned present_flag; + + elem->loc = *loc; + elem->next = NULL; + + if (ddsi_is_mcaddr (arg->gv, loc)) { + locs = &arg->ps->multicast_locators; + present_flag = PP_MULTICAST_LOCATOR; + } else { + locs = &arg->ps->unicast_locators; + present_flag = PP_UNICAST_LOCATOR; + } + + if (!(arg->ps->present & present_flag)) + { + locs->n = 0; + locs->first = locs->last = NULL; + arg->ps->present |= present_flag; + } + locs->n++; + if (locs->first) + locs->last->next = elem; + else + locs->first = elem; + locs->last = elem; +} + +static void add_xlocator_to_ps (const ddsi_xlocator_t *loc, void *varg) +{ + add_locator_to_ps (&loc->c, varg); +} + +#ifdef DDS_HAS_SHM +static void add_iox_locator_to_ps(const ddsi_locator_t* loc, struct add_locator_to_ps_arg *arg) +{ + struct ddsi_locators_one* elem = ddsrt_malloc(sizeof(struct ddsi_locators_one)); + struct ddsi_locators* locs = &arg->ps->unicast_locators; + unsigned present_flag = PP_UNICAST_LOCATOR; + + elem->loc = *loc; + elem->next = NULL; + + if (!(arg->ps->present & present_flag)) + { + locs->n = 0; + locs->first = locs->last = NULL; + arg->ps->present |= present_flag; + } + + //add iceoryx to the FRONT of the list of addresses, to indicate its higher priority + if (locs->first) + elem->next = locs->first; + else + locs->last = elem; + locs->first = elem; + locs->n++; +} +#endif + +/****************************************************************************** + *** + *** SEDP + *** + *****************************************************************************/ + +static int sedp_write_endpoint_impl +( + struct ddsi_writer *wr, int alive, const ddsi_guid_t *guid, + const struct ddsi_endpoint_common *epcommon, + const dds_qos_t *xqos, struct ddsi_addrset *as, ddsi_security_info_t *security +#ifdef DDS_HAS_TYPE_DISCOVERY + , const struct ddsi_sertype *sertype +#endif +) +{ + struct ddsi_domaingv * const gv = wr->e.gv; + const dds_qos_t *defqos = NULL; + if (ddsi_is_writer_entityid (guid->entityid)) + defqos = &ddsi_default_qos_writer; + else if (ddsi_is_reader_entityid (guid->entityid)) + defqos = &ddsi_default_qos_reader; + else + assert (false); + + uint64_t qosdiff; + ddsi_plist_t ps; + + ddsi_plist_init_empty (&ps); + ps.present |= PP_ENDPOINT_GUID; + ps.endpoint_guid = *guid; + +#ifdef DDS_HAS_SECURITY + if (security) + { + ps.present |= PP_ENDPOINT_SECURITY_INFO; + memcpy(&ps.endpoint_security_info, security, sizeof(ddsi_security_info_t)); + } +#else + (void)security; + assert(security == NULL); +#endif + + if (!alive) + { + assert (xqos == NULL); + assert (epcommon == NULL); + qosdiff = 0; + } + else + { + assert (xqos != NULL); + ps.present |= PP_PROTOCOL_VERSION | PP_VENDORID; + ps.protocol_version.major = DDSI_RTPS_MAJOR; + ps.protocol_version.minor = DDSI_RTPS_MINOR; + ps.vendorid = DDSI_VENDORID_ECLIPSE; + + assert (epcommon != NULL); + + if (epcommon->group_guid.entityid.u != 0) + { + ps.present |= PP_GROUP_GUID; + ps.group_guid = epcommon->group_guid; + } + + if (!ddsi_is_writer_entityid (guid->entityid)) + { + const struct ddsi_reader *rd = ddsi_entidx_lookup_reader_guid (gv->entity_index, guid); + assert (rd); + if (rd->request_keyhash) + { + ps.present |= PP_CYCLONE_REQUESTS_KEYHASH; + ps.cyclone_requests_keyhash = 1u; + } + } + +#ifdef DDS_HAS_SSM + /* A bit of a hack -- the easy alternative would be to make it yet + another parameter. We only set "reader favours SSM" if we + really do: no point in telling the world that everything is at + the default. */ + if (ddsi_is_reader_entityid (guid->entityid)) + { + const struct ddsi_reader *rd = ddsi_entidx_lookup_reader_guid (gv->entity_index, guid); + assert (rd); + if (rd->favours_ssm) + { + ps.present |= PP_READER_FAVOURS_SSM; + ps.reader_favours_ssm.state = 1u; + } + } +#endif + + qosdiff = ddsi_xqos_delta (xqos, defqos, ~(uint64_t)0); + if (gv->config.explicitly_publish_qos_set_to_default) + qosdiff |= ~DDSI_QP_UNRECOGNIZED_INCOMPATIBLE_MASK; + + struct add_locator_to_ps_arg arg; + arg.gv = gv; + arg.ps = &ps; + if (as) + ddsi_addrset_forall (as, add_xlocator_to_ps, &arg); + +#ifdef DDS_HAS_SHM + assert(wr->xqos->present & DDSI_QP_LOCATOR_MASK); + if (!(xqos->ignore_locator_type & DDSI_LOCATOR_KIND_SHEM)) + { + if (!(arg.ps->present & PP_UNICAST_LOCATOR) || 0 == arg.ps->unicast_locators.n) + { + if (epcommon->pp->e.gv->config.many_sockets_mode == DDSI_MSM_MANY_UNICAST) + add_locator_to_ps(&epcommon->pp->m_locator, &arg); + else + { + // FIXME: same as what SPDP uses, should be refactored, now more than ever + for (int i = 0; i < epcommon->pp->e.gv->n_interfaces; i++) + { + if (!epcommon->pp->e.gv->xmit_conns[i]->m_factory->m_enable_spdp) + { + // skip any interfaces where the address kind doesn't match the selected transport + // as a reasonablish way of not advertising iceoryx locators here + continue; + } + // FIXME: should have multiple loc_default_uc/loc_meta_uc or compute ports here + ddsi_locator_t loc = epcommon->pp->e.gv->interfaces[i].extloc; + loc.port = epcommon->pp->e.gv->loc_default_uc.port; + add_locator_to_ps(&loc, &arg); + } + } + } + + if (!(arg.ps->present & PP_MULTICAST_LOCATOR) || 0 == arg.ps->multicast_locators.n) + { + if (ddsi_include_multicast_locator_in_discovery (epcommon->pp->e.gv)) + add_locator_to_ps (&epcommon->pp->e.gv->loc_default_mc, &arg); + } + + add_iox_locator_to_ps(&gv->loc_iceoryx_addr, &arg); + } +#endif + +#ifdef DDS_HAS_TYPE_DISCOVERY + assert (sertype); + if ((ps.qos.type_information = ddsi_sertype_typeinfo (sertype))) + ps.qos.present |= DDSI_QP_TYPE_INFORMATION; +#endif + } + + if (xqos) + ddsi_xqos_mergein_missing (&ps.qos, xqos, qosdiff); + return ddsi_write_and_fini_plist (wr, &ps, alive); +} + +int ddsi_sedp_write_writer (struct ddsi_writer *wr) +{ + if ((!ddsi_is_builtin_entityid(wr->e.guid.entityid, DDSI_VENDORID_ECLIPSE)) && (!wr->e.onlylocal)) + { + unsigned entityid = ddsi_determine_publication_writer(wr); + struct ddsi_writer *sedp_wr = ddsi_get_sedp_writer (wr->c.pp, entityid); + ddsi_security_info_t *security = NULL; +#ifdef DDS_HAS_SSM + struct ddsi_addrset *as = wr->ssm_as; +#else + struct ddsi_addrset *as = NULL; +#endif +#ifdef DDS_HAS_SECURITY + ddsi_security_info_t tmp; + if (ddsi_omg_get_writer_security_info (wr, &tmp)) + { + security = &tmp; + } +#endif +#ifdef DDS_HAS_TYPE_DISCOVERY + return sedp_write_endpoint_impl (sedp_wr, 1, &wr->e.guid, &wr->c, wr->xqos, as, security, wr->type); +#else + return sedp_write_endpoint_impl (sedp_wr, 1, &wr->e.guid, &wr->c, wr->xqos, as, security); +#endif + } + return 0; +} + +int ddsi_sedp_write_reader (struct ddsi_reader *rd) +{ + if (ddsi_is_builtin_entityid (rd->e.guid.entityid, DDSI_VENDORID_ECLIPSE) || rd->e.onlylocal) + return 0; + + unsigned entityid = ddsi_determine_subscription_writer(rd); + struct ddsi_writer *sedp_wr = ddsi_get_sedp_writer (rd->c.pp, entityid); + ddsi_security_info_t *security = NULL; + struct ddsi_addrset *as = NULL; +#ifdef DDS_HAS_NETWORK_PARTITIONS + if (rd->uc_as != NULL || rd->mc_as != NULL) + { + // FIXME: do this without first creating a temporary addrset + as = ddsi_new_addrset (); + // use a placeholder connection to avoid exploding the multicast addreses to multiple + // interfaces + for (const struct ddsi_networkpartition_address *a = rd->uc_as; a != NULL; a = a->next) + ddsi_add_xlocator_to_addrset(rd->e.gv, as, &(const ddsi_xlocator_t) { + .c = a->loc, + .conn = rd->e.gv->xmit_conns[0] }); + for (const struct ddsi_networkpartition_address *a = rd->mc_as; a != NULL; a = a->next) + ddsi_add_xlocator_to_addrset(rd->e.gv, as, &(const ddsi_xlocator_t) { + .c = a->loc, + .conn = rd->e.gv->xmit_conns[0] }); + } +#endif +#ifdef DDS_HAS_SECURITY + ddsi_security_info_t tmp; + if (ddsi_omg_get_reader_security_info (rd, &tmp)) + { + security = &tmp; + } +#endif +#ifdef DDS_HAS_TYPE_DISCOVERY + const int ret = sedp_write_endpoint_impl (sedp_wr, 1, &rd->e.guid, &rd->c, rd->xqos, as, security, rd->type); +#else + const int ret = sedp_write_endpoint_impl (sedp_wr, 1, &rd->e.guid, &rd->c, rd->xqos, as, security); +#endif + ddsi_unref_addrset (as); + return ret; +} + +int ddsi_sedp_dispose_unregister_writer (struct ddsi_writer *wr) +{ + if ((!ddsi_is_builtin_entityid(wr->e.guid.entityid, DDSI_VENDORID_ECLIPSE)) && (!wr->e.onlylocal)) + { + unsigned entityid = ddsi_determine_publication_writer(wr); + struct ddsi_writer *sedp_wr = ddsi_get_sedp_writer (wr->c.pp, entityid); +#ifdef DDS_HAS_TYPE_DISCOVERY + return sedp_write_endpoint_impl (sedp_wr, 0, &wr->e.guid, NULL, NULL, NULL, NULL, NULL); +#else + return sedp_write_endpoint_impl (sedp_wr, 0, &wr->e.guid, NULL, NULL, NULL, NULL); +#endif + } + return 0; +} + +int ddsi_sedp_dispose_unregister_reader (struct ddsi_reader *rd) +{ + if ((!ddsi_is_builtin_entityid(rd->e.guid.entityid, DDSI_VENDORID_ECLIPSE)) && (!rd->e.onlylocal)) + { + unsigned entityid = ddsi_determine_subscription_writer(rd); + struct ddsi_writer *sedp_wr = ddsi_get_sedp_writer (rd->c.pp, entityid); +#ifdef DDS_HAS_TYPE_DISCOVERY + return sedp_write_endpoint_impl (sedp_wr, 0, &rd->e.guid, NULL, NULL, NULL, NULL, NULL); +#else + return sedp_write_endpoint_impl (sedp_wr, 0, &rd->e.guid, NULL, NULL, NULL, NULL); +#endif + } + return 0; +} + +static const char *durability_to_string (dds_durability_kind_t k) +{ + switch (k) + { + case DDS_DURABILITY_VOLATILE: return "volatile"; + case DDS_DURABILITY_TRANSIENT_LOCAL: return "transient-local"; + case DDS_DURABILITY_TRANSIENT: return "transient"; + case DDS_DURABILITY_PERSISTENT: return "persistent"; + } + return "undefined-durability"; +} + +struct ddsi_addrset_from_locatorlists_collect_interfaces_arg { + const struct ddsi_domaingv *gv; + ddsi_interface_set_t *intfs; +}; + +/** @brief Figure out which interfaces are touched by (extended) locator @p loc + * + * Does this by looking up the connection in @p loc in the set of transmit connections. (There's plenty of room for optimisation here.) + * + * @param[in] loc locator + * @param[in] varg argument pointer, must point to a struct ddsi_addrset_from_locatorlists_collect_interfaces_arg + */ +static void addrset_from_locatorlists_collect_interfaces (const ddsi_xlocator_t *loc, void *varg) +{ + struct ddsi_addrset_from_locatorlists_collect_interfaces_arg *arg = varg; + struct ddsi_domaingv const * const gv = arg->gv; + for (int i = 0; i < gv->n_interfaces; i++) + { + //GVTRACE(" {%p,%p}", loc->conn, gv->xmit_conns[i]); + if (loc->conn == gv->xmit_conns[i]) + { + arg->intfs->xs[i] = true; + break; + } + } +} + +struct ddsi_addrset *ddsi_get_endpoint_addrset (const struct ddsi_domaingv *gv, const ddsi_plist_t *datap, struct ddsi_addrset *proxypp_as_default, const ddsi_locator_t *rst_srcloc) +{ + const ddsi_locators_t emptyset = { .n = 0, .first = NULL, .last = NULL }; + const ddsi_locators_t *uc = (datap->present & PP_UNICAST_LOCATOR) ? &datap->unicast_locators : ∅ + const ddsi_locators_t *mc = (datap->present & PP_MULTICAST_LOCATOR) ? &datap->multicast_locators : ∅ + ddsi_locator_t srcloc; + if (rst_srcloc == NULL) + ddsi_set_unspec_locator (&srcloc); + else // force use of source locator + { + uc = ∅ + srcloc = *rst_srcloc; + } + + // any interface that works for the participant is presumed ok + ddsi_interface_set_t intfs; + ddsi_interface_set_init (&intfs); + ddsi_addrset_forall (proxypp_as_default, addrset_from_locatorlists_collect_interfaces, &(struct ddsi_addrset_from_locatorlists_collect_interfaces_arg){ + .gv = gv, .intfs = &intfs + }); + //GVTRACE(" {%d%d%d%d}", intfs.xs[0], intfs.xs[1], intfs.xs[2], intfs.xs[3]); + struct ddsi_addrset *as = ddsi_addrset_from_locatorlists (gv, uc, mc, &srcloc, &intfs); + // if SEDP gives: + // - no addresses, use ppant uni- and multicast addresses + // - only multicast, use those for multicast and use ppant address for unicast + // - only unicast, use only those (i.e., disable multicast for this reader) + // - both, use only those + // FIXME: then you can't do a specific unicast address + SSM ... oh well + if (ddsi_addrset_empty (as)) + ddsi_copy_addrset_into_addrset_mc (gv, as, proxypp_as_default); + if (ddsi_addrset_empty_uc (as)) + ddsi_copy_addrset_into_addrset_uc (gv, as, proxypp_as_default); + return as; +} + +void ddsi_handle_sedp_alive_endpoint (const struct ddsi_receiver_state *rst, ddsi_seqno_t seq, ddsi_plist_t *datap /* note: potentially modifies datap */, ddsi_sedp_kind_t sedp_kind, const ddsi_guid_prefix_t *src_guid_prefix, ddsi_vendorid_t vendorid, ddsrt_wctime_t timestamp) +{ +#define E(msg, lbl) do { GVLOGDISC (msg); goto lbl; } while (0) + struct ddsi_domaingv * const gv = rst->gv; + struct ddsi_proxy_participant *proxypp; + struct ddsi_proxy_writer * pwr = NULL; + struct ddsi_proxy_reader * prd = NULL; + ddsi_guid_t ppguid; + dds_qos_t *xqos; + int reliable; + struct ddsi_addrset *as; +#ifdef DDS_HAS_SSM + int ssm; +#endif + + assert (datap); + assert (datap->present & PP_ENDPOINT_GUID); + GVLOGDISC (" "PGUIDFMT, PGUID (datap->endpoint_guid)); + + if (!ddsi_handle_sedp_checks (gv, sedp_kind, &datap->endpoint_guid, datap, src_guid_prefix, vendorid, timestamp, &proxypp, &ppguid)) + goto err; + + xqos = &datap->qos; + if (sedp_kind == SEDP_KIND_READER) + ddsi_xqos_mergein_missing (xqos, &ddsi_default_qos_reader, ~(uint64_t)0); + else if (sedp_kind == SEDP_KIND_WRITER) + { + ddsi_xqos_mergein_missing (xqos, &ddsi_default_qos_writer, ~(uint64_t)0); + if (!ddsi_vendor_is_eclipse_or_adlink (vendorid)) + { + // there is a difference in interpretation of autodispose between vendors + xqos->writer_data_lifecycle.autodispose_unregistered_instances = 0; + } + } + else + E (" invalid entity kind\n", err); + + /* After copy + merge, should have at least the ones present in the + input. Also verify reliability and durability are present, + because we explicitly read those. */ + assert ((xqos->present & datap->qos.present) == datap->qos.present); + assert (xqos->present & DDSI_QP_RELIABILITY); + assert (xqos->present & DDSI_QP_DURABILITY); + reliable = (xqos->reliability.kind == DDS_RELIABILITY_RELIABLE); + + GVLOGDISC (" %s %s %s %s: %s%s.%s/%s", + reliable ? "reliable" : "best-effort", + durability_to_string (xqos->durability.kind), + sedp_kind == SEDP_KIND_WRITER ? "writer" : "reader", + (xqos->present & DDSI_QP_ENTITY_NAME) ? xqos->entity_name : "unnamed", + ((!(xqos->present & DDSI_QP_PARTITION) || xqos->partition.n == 0 || *xqos->partition.strs[0] == '\0') + ? "(default)" : xqos->partition.strs[0]), + ((xqos->present & DDSI_QP_PARTITION) && xqos->partition.n > 1) ? "+" : "", + xqos->topic_name, xqos->type_name); + + if (sedp_kind == SEDP_KIND_READER && (datap->present & PP_EXPECTS_INLINE_QOS) && datap->expects_inline_qos) + E ("******* AARGH - it expects inline QoS ********\n", err); + + ddsi_omg_log_endpoint_protection (gv, datap); + if (ddsi_omg_is_endpoint_protected (datap) && !ddsi_omg_proxy_participant_is_secure (proxypp)) + E (" remote endpoint is protected while local federation is not secure\n", err); + + if (sedp_kind == SEDP_KIND_WRITER) + pwr = ddsi_entidx_lookup_proxy_writer_guid (gv->entity_index, &datap->endpoint_guid); + else + prd = ddsi_entidx_lookup_proxy_reader_guid (gv->entity_index, &datap->endpoint_guid); + if (pwr || prd) + { + /* Re-bind the proxy participant to the discovery service - and do this if it is currently + bound to another DS instance, because that other DS instance may have already failed and + with a new one taking over, without our noticing it. */ + GVLOGDISC (" known%s", ddsi_vendor_is_cloud (vendorid) ? "-DS" : ""); + if (ddsi_vendor_is_cloud (vendorid) && proxypp->implicitly_created && memcmp (&proxypp->privileged_pp_guid.prefix, src_guid_prefix, sizeof(proxypp->privileged_pp_guid.prefix)) != 0) + { + GVLOGDISC (" "PGUIDFMT" attach-to-DS "PGUIDFMT, PGUID(proxypp->e.guid), PGUIDPREFIX(*src_guid_prefix), proxypp->privileged_pp_guid.entityid.u); + ddsrt_mutex_lock (&proxypp->e.lock); + proxypp->privileged_pp_guid.prefix = *src_guid_prefix; + ddsi_lease_set_expiry (proxypp->lease, DDSRT_ETIME_NEVER); + ddsrt_mutex_unlock (&proxypp->e.lock); + } + GVLOGDISC ("\n"); + } + else + { + GVLOGDISC (" NEW"); + } + + as = ddsi_get_endpoint_addrset (gv, datap, proxypp->as_default, gv->config.tcp_use_peeraddr_for_unicast ? &rst->srcloc : NULL); + if (ddsi_addrset_empty (as)) + { + ddsi_unref_addrset (as); + E (" no address", err); + } + + ddsi_log_addrset(gv, DDS_LC_DISCOVERY, " (as", as); +#ifdef DDS_HAS_SSM + ssm = 0; + if (sedp_kind == SEDP_KIND_WRITER) + ssm = ddsi_addrset_contains_ssm (gv, as); + else if (datap->present & PP_READER_FAVOURS_SSM) + ssm = (datap->reader_favours_ssm.state != 0); + GVLOGDISC (" ssm=%u", ssm); +#endif + GVLOGDISC (") QOS={"); + ddsi_xqos_log (DDS_LC_DISCOVERY, &gv->logconfig, xqos); + GVLOGDISC ("}\n"); + + if ((datap->endpoint_guid.entityid.u & DDSI_ENTITYID_SOURCE_MASK) == DDSI_ENTITYID_SOURCE_VENDOR && !ddsi_vendor_is_eclipse_or_adlink (vendorid)) + { + GVLOGDISC ("ignoring vendor-specific endpoint "PGUIDFMT"\n", PGUID (datap->endpoint_guid)); + } + else + { + if (sedp_kind == SEDP_KIND_WRITER) + { + if (pwr) + ddsi_update_proxy_writer (pwr, seq, as, xqos, timestamp); + else + { + /* not supposed to get here for built-in ones, so can determine the channel based on the transport priority */ + assert (!ddsi_is_builtin_entityid (datap->endpoint_guid.entityid, vendorid)); + ddsi_new_proxy_writer (gv, &ppguid, &datap->endpoint_guid, as, datap, gv->user_dqueue, gv->xevents, timestamp, seq); + } + } + else + { + if (prd) + ddsi_update_proxy_reader (prd, seq, as, xqos, timestamp); + else + { +#ifdef DDS_HAS_SSM + ddsi_new_proxy_reader (gv, &ppguid, &datap->endpoint_guid, as, datap, timestamp, seq, ssm); +#else + ddsi_new_proxy_reader (gv, &ppguid, &datap->endpoint_guid, as, datap, timestamp, seq); +#endif + } + } + } + ddsi_unref_addrset (as); + +err: + return; +#undef E +} + +void ddsi_handle_sedp_dead_endpoint (const struct ddsi_receiver_state *rst, ddsi_plist_t *datap, ddsi_sedp_kind_t sedp_kind, ddsrt_wctime_t timestamp) +{ + struct ddsi_domaingv * const gv = rst->gv; + int res = -1; + assert (datap->present & PP_ENDPOINT_GUID); + GVLOGDISC (" "PGUIDFMT" ", PGUID (datap->endpoint_guid)); + if (!ddsi_check_sedp_kind_and_guid (sedp_kind, &datap->endpoint_guid)) + return; + else if (sedp_kind == SEDP_KIND_WRITER) + res = ddsi_delete_proxy_writer (gv, &datap->endpoint_guid, timestamp, 0); + else + res = ddsi_delete_proxy_reader (gv, &datap->endpoint_guid, timestamp, 0); + GVLOGDISC (" %s\n", (res < 0) ? " unknown" : " delete"); +} diff --git a/src/core/ddsi/src/ddsi_discovery_sedp.c b/src/core/ddsi/src/ddsi_discovery_sedp.c new file mode 100644 index 0000000000..a9bdf313b1 --- /dev/null +++ b/src/core/ddsi/src/ddsi_discovery_sedp.c @@ -0,0 +1,212 @@ +/* + * Copyright(c) 2023 ZettaScale Technology and others + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License + * v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ +#include +#include +#include +#include + +#include "dds/ddsi/ddsi_domaingv.h" +#include "ddsi__discovery_sedp.h" +#include "ddsi__discovery_endpoint.h" +#ifdef DDS_HAS_TOPIC_DISCOVERY +#include "ddsi__discovery_topic.h" +#endif +#include "ddsi__serdata_plist.h" +#include "ddsi__entity_index.h" +#include "ddsi__entity.h" +#include "ddsi__security_omg.h" +#include "ddsi__endpoint.h" +#include "ddsi__plist.h" +#include "ddsi__proxy_participant.h" +#include "ddsi__topic.h" +#include "ddsi__vendor.h" + +struct ddsi_writer *ddsi_get_sedp_writer (const struct ddsi_participant *pp, unsigned entityid) +{ + struct ddsi_writer *sedp_wr = ddsi_get_builtin_writer (pp, entityid); + if (sedp_wr == NULL) + DDS_FATAL ("sedp_write_writer: no SEDP builtin writer %x for "PGUIDFMT"\n", entityid, PGUID (pp->e.guid)); + return sedp_wr; +} + +struct ddsi_proxy_participant *ddsi_implicitly_create_proxypp (struct ddsi_domaingv *gv, const ddsi_guid_t *ppguid, ddsi_plist_t *datap /* note: potentially modifies datap */, const ddsi_guid_prefix_t *src_guid_prefix, ddsi_vendorid_t vendorid, ddsrt_wctime_t timestamp, ddsi_seqno_t seq) +{ + ddsi_guid_t privguid; + ddsi_plist_t pp_plist; + + if (memcmp (&ppguid->prefix, src_guid_prefix, sizeof (ppguid->prefix)) == 0) + /* if the writer is owned by the participant itself, we're not interested */ + return NULL; + + privguid.prefix = *src_guid_prefix; + privguid.entityid = ddsi_to_entityid (DDSI_ENTITYID_PARTICIPANT); + ddsi_plist_init_empty(&pp_plist); + + if (ddsi_vendor_is_cloud (vendorid)) + { + ddsi_vendorid_t actual_vendorid; + /* Some endpoint that we discovered through the DS, but then it must have at least some locators */ + GVTRACE (" from-DS %"PRIx32":%"PRIx32":%"PRIx32":%"PRIx32, PGUID (privguid)); + /* avoid "no address" case, so we never create the proxy participant for nothing (FIXME: rework some of this) */ + if (!(datap->present & (PP_UNICAST_LOCATOR | PP_MULTICAST_LOCATOR))) + { + GVTRACE (" data locator absent\n"); + goto err; + } + GVTRACE (" new-proxypp "PGUIDFMT"\n", PGUID (*ppguid)); + /* We need to handle any source of entities, but we really want to try to keep the GIDs (and + certainly the systemId component) unchanged for OSPL. The new proxy participant will take + the GID from the GUID if it is from a "modern" OSPL that advertises it includes all GIDs in + the endpoint discovery; else if it is OSPL it will take at the systemId and fake the rest. + However, (1) Cloud filters out the GIDs from the discovery, and (2) DDSI2 deliberately + doesn't include the GID for internally generated endpoints (such as the fictitious transient + data readers) to signal that these are internal and have no GID (and not including a GID if + there is none is quite a reasonable approach). Point (2) means we have no reliable way of + determining whether GIDs are included based on the first endpoint, and so there is no point + doing anything about (1). That means we fall back to the legacy mode of locally generating + GIDs but leaving the system id unchanged if the remote is OSPL. */ + actual_vendorid = (datap->present & PP_VENDORID) ? datap->vendorid : vendorid; + (void) ddsi_new_proxy_participant (gv, ppguid, 0, &privguid, ddsi_new_addrset(), ddsi_new_addrset(), &pp_plist, DDS_INFINITY, actual_vendorid, DDSI_CF_IMPLICITLY_CREATED_PROXYPP, timestamp, seq); + } + else if (ppguid->prefix.u[0] == src_guid_prefix->u[0] && ddsi_vendor_is_eclipse_or_opensplice (vendorid)) + { + /* FIXME: requires address sets to be those of ddsi2, no built-in + readers or writers, only if remote ddsi2 is provably running + with a minimal built-in endpoint set */ + struct ddsi_proxy_participant *privpp; + if ((privpp = ddsi_entidx_lookup_proxy_participant_guid (gv->entity_index, &privguid)) == NULL) { + GVTRACE (" unknown-src-proxypp?\n"); + goto err; + } else if (!privpp->is_ddsi2_pp) { + GVTRACE (" src-proxypp-not-ddsi2?\n"); + goto err; + } else if (!privpp->minimal_bes_mode) { + GVTRACE (" src-ddsi2-not-minimal-bes-mode?\n"); + goto err; + } else { + struct ddsi_addrset *as_default, *as_meta; + ddsi_plist_t tmp_plist; + GVTRACE (" from-ddsi2 "PGUIDFMT, PGUID (privguid)); + ddsi_plist_init_empty (&pp_plist); + + ddsrt_mutex_lock (&privpp->e.lock); + as_default = ddsi_ref_addrset(privpp->as_default); + as_meta = ddsi_ref_addrset(privpp->as_meta); + /* copy just what we need */ + tmp_plist = *privpp->plist; + tmp_plist.present = PP_PARTICIPANT_GUID | PP_ADLINK_PARTICIPANT_VERSION_INFO; + tmp_plist.participant_guid = *ppguid; + ddsi_plist_mergein_missing (&pp_plist, &tmp_plist, ~(uint64_t)0, ~(uint64_t)0); + ddsrt_mutex_unlock (&privpp->e.lock); + + pp_plist.adlink_participant_version_info.flags &= ~DDSI_ADLINK_FL_PARTICIPANT_IS_DDSI2; + ddsi_new_proxy_participant (gv, ppguid, 0, &privguid, as_default, as_meta, &pp_plist, DDS_INFINITY, vendorid, DDSI_CF_IMPLICITLY_CREATED_PROXYPP | DDSI_CF_PROXYPP_NO_SPDP, timestamp, seq); + } + } + + err: + ddsi_plist_fini (&pp_plist); + return ddsi_entidx_lookup_proxy_participant_guid (gv->entity_index, ppguid); +} + +bool ddsi_check_sedp_kind_and_guid (ddsi_sedp_kind_t sedp_kind, const ddsi_guid_t *entity_guid) +{ + switch (sedp_kind) + { + case SEDP_KIND_TOPIC: + return ddsi_is_topic_entityid (entity_guid->entityid); + case SEDP_KIND_WRITER: + return ddsi_is_writer_entityid (entity_guid->entityid); + case SEDP_KIND_READER: + return ddsi_is_reader_entityid (entity_guid->entityid); + } + assert (0); + return false; +} + +bool ddsi_handle_sedp_checks (struct ddsi_domaingv * const gv, ddsi_sedp_kind_t sedp_kind, ddsi_guid_t *entity_guid, ddsi_plist_t *datap, + const ddsi_guid_prefix_t *src_guid_prefix, ddsi_vendorid_t vendorid, ddsrt_wctime_t timestamp, + struct ddsi_proxy_participant **proxypp, ddsi_guid_t *ppguid) +{ +#define E(msg, lbl) do { GVLOGDISC (msg); return false; } while (0) + if (!ddsi_check_sedp_kind_and_guid (sedp_kind, entity_guid)) + E (" SEDP topic/GUID entity kind mismatch\n", err); + ppguid->prefix = entity_guid->prefix; + ppguid->entityid.u = DDSI_ENTITYID_PARTICIPANT; + // Accept the presence of a participant GUID, but only if it matches + if ((datap->present & PP_PARTICIPANT_GUID) && memcmp (&datap->participant_guid, ppguid, sizeof (*ppguid)) != 0) + E (" endpoint/participant GUID mismatch", err); + if (ddsi_is_deleted_participant_guid (gv->deleted_participants, ppguid, DDSI_DELETED_PPGUID_REMOTE)) + E (" local dead pp?\n", err); + if (ddsi_entidx_lookup_participant_guid (gv->entity_index, ppguid) != NULL) + E (" local pp?\n", err); + if (ddsi_is_builtin_entityid (entity_guid->entityid, vendorid)) + E (" built-in\n", err); + if (!(datap->qos.present & DDSI_QP_TOPIC_NAME)) + E (" no topic?\n", err); + if (!(datap->qos.present & DDSI_QP_TYPE_NAME)) + E (" no typename?\n", err); + if ((*proxypp = ddsi_entidx_lookup_proxy_participant_guid (gv->entity_index, ppguid)) == NULL) + { + GVLOGDISC (" unknown-proxypp"); + if ((*proxypp = ddsi_implicitly_create_proxypp (gv, ppguid, datap, src_guid_prefix, vendorid, timestamp, 0)) == NULL) + E ("?\n", err); + /* Repeat regular SEDP trace for convenience */ + GVLOGDISC ("SEDP ST0 "PGUIDFMT" (cont)", PGUID (*entity_guid)); + } + return true; +#undef E +} + +void ddsi_handle_sedp (const struct ddsi_receiver_state *rst, ddsi_seqno_t seq, struct ddsi_serdata *serdata, ddsi_sedp_kind_t sedp_kind) +{ + ddsi_plist_t decoded_data; + if (ddsi_serdata_to_sample (serdata, &decoded_data, NULL, NULL)) + { + struct ddsi_domaingv * const gv = rst->gv; + GVLOGDISC ("SEDP ST%"PRIx32, serdata->statusinfo); + switch (serdata->statusinfo & (DDSI_STATUSINFO_DISPOSE | DDSI_STATUSINFO_UNREGISTER)) + { + case 0: + switch (sedp_kind) + { + case SEDP_KIND_TOPIC: +#ifdef DDS_HAS_TOPIC_DISCOVERY + ddsi_handle_sedp_alive_topic (rst, seq, &decoded_data, &rst->src_guid_prefix, rst->vendor, serdata->timestamp); +#endif + break; + case SEDP_KIND_READER: + case SEDP_KIND_WRITER: + ddsi_handle_sedp_alive_endpoint (rst, seq, &decoded_data, sedp_kind, &rst->src_guid_prefix, rst->vendor, serdata->timestamp); + break; + } + break; + case DDSI_STATUSINFO_DISPOSE: + case DDSI_STATUSINFO_UNREGISTER: + case (DDSI_STATUSINFO_DISPOSE | DDSI_STATUSINFO_UNREGISTER): + switch (sedp_kind) + { + case SEDP_KIND_TOPIC: +#ifdef DDS_HAS_TOPIC_DISCOVERY + ddsi_handle_sedp_dead_topic (rst, &decoded_data, serdata->timestamp); +#endif + break; + case SEDP_KIND_READER: + case SEDP_KIND_WRITER: + ddsi_handle_sedp_dead_endpoint (rst, &decoded_data, sedp_kind, serdata->timestamp); + break; + } + break; + } + ddsi_plist_fini (&decoded_data); + } +} diff --git a/src/core/ddsi/src/ddsi_discovery_spdp.c b/src/core/ddsi/src/ddsi_discovery_spdp.c new file mode 100644 index 0000000000..04c455cc30 --- /dev/null +++ b/src/core/ddsi/src/ddsi_discovery_spdp.c @@ -0,0 +1,858 @@ +/* + * Copyright(c) 2006 to 2022 ZettaScale Technology and others + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License + * v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ +#include +#include +#include +#include + +#include "dds/version.h" +#include "dds/ddsrt/heap.h" +#include "dds/ddsrt/string.h" +#include "dds/ddsi/ddsi_domaingv.h" +#include "ddsi__discovery_spdp.h" +#include "ddsi__discovery_addrset.h" +#include "ddsi__discovery_endpoint.h" +#include "ddsi__serdata_plist.h" +#include "ddsi__entity_index.h" +#include "ddsi__entity.h" +#include "ddsi__security_omg.h" +#include "ddsi__endpoint.h" +#include "ddsi__plist.h" +#include "ddsi__proxy_participant.h" +#include "ddsi__topic.h" +#include "ddsi__vendor.h" +#include "ddsi__xevent.h" +#include "ddsi__transmit.h" +#include "ddsi__lease.h" +#include "ddsi__xqos.h" + +static void maybe_add_pp_as_meta_to_as_disc (struct ddsi_domaingv *gv, const struct ddsi_addrset *as_meta) +{ + if (ddsi_addrset_empty_mc (as_meta) || !(gv->config.allowMulticast & DDSI_AMC_SPDP)) + { + ddsi_xlocator_t loc; + if (ddsi_addrset_any_uc (as_meta, &loc)) + { + ddsi_add_xlocator_to_addrset (gv, gv->as_disc, &loc); + } + } +} + +struct locators_builder { + ddsi_locators_t *dst; + struct ddsi_locators_one *storage; + size_t storage_n; +}; + +static struct locators_builder locators_builder_init (ddsi_locators_t *dst, struct ddsi_locators_one *storage, size_t storage_n) +{ + dst->n = 0; + dst->first = NULL; + dst->last = NULL; + return (struct locators_builder) { + .dst = dst, + .storage = storage, + .storage_n = storage_n + }; +} + +static bool locators_add_one (struct locators_builder *b, const ddsi_locator_t *loc, uint32_t port_override) +{ + if (b->dst->n >= b->storage_n) + return false; + if (b->dst->n == 0) + b->dst->first = b->storage; + else + b->dst->last->next = &b->storage[b->dst->n]; + b->dst->last = &b->storage[b->dst->n++]; + b->dst->last->loc = *loc; + if (port_override != DDSI_LOCATOR_PORT_INVALID) + b->dst->last->loc.port = port_override; + b->dst->last->next = NULL; + return true; +} + +void ddsi_get_participant_builtin_topic_data (const struct ddsi_participant *pp, ddsi_plist_t *dst, struct ddsi_participant_builtin_topic_data_locators *locs) +{ + struct ddsi_domaingv * const gv = pp->e.gv; + size_t size; + char node[64]; + uint64_t qosdiff; + + ddsi_plist_init_empty (dst); + dst->present |= PP_PARTICIPANT_GUID | PP_BUILTIN_ENDPOINT_SET | + PP_PROTOCOL_VERSION | PP_VENDORID | PP_DOMAIN_ID; + dst->participant_guid = pp->e.guid; + dst->builtin_endpoint_set = pp->bes; + dst->protocol_version.major = DDSI_RTPS_MAJOR; + dst->protocol_version.minor = DDSI_RTPS_MINOR; + dst->vendorid = DDSI_VENDORID_ECLIPSE; + dst->domain_id = gv->config.extDomainId.value; + /* Be sure not to send a DOMAIN_TAG when it is the default (an empty) + string: it is an "incompatible-if-unrecognized" parameter, and so + implementations that don't understand the parameter will refuse to + discover us, and so sending the default would break backwards + compatibility. */ + if (strcmp (gv->config.domainTag, "") != 0) + { + dst->present |= PP_DOMAIN_TAG; + dst->aliased |= PP_DOMAIN_TAG; + dst->domain_tag = gv->config.domainTag; + } + + // Construct unicast locator parameters + { + struct locators_builder def_uni = locators_builder_init (&dst->default_unicast_locators, locs->def_uni, MAX_XMIT_CONNS); + struct locators_builder meta_uni = locators_builder_init (&dst->metatraffic_unicast_locators, locs->meta_uni, MAX_XMIT_CONNS); + for (int i = 0; i < gv->n_interfaces; i++) + { + if (!gv->xmit_conns[i]->m_factory->m_enable_spdp) + { + // skip any interfaces where the address kind doesn't match the selected transport + // as a reasonablish way of not advertising iceoryx locators here + continue; + } +#ifndef NDEBUG + int32_t kind; +#endif + uint32_t data_port, meta_port; + if (gv->config.many_sockets_mode != DDSI_MSM_MANY_UNICAST) + { +#ifndef NDEBUG + kind = gv->loc_default_uc.kind; +#endif + assert (kind == gv->loc_meta_uc.kind); + data_port = gv->loc_default_uc.port; + meta_port = gv->loc_meta_uc.port; + } + else + { +#ifndef NDEBUG + kind = pp->m_locator.kind; +#endif + data_port = meta_port = pp->m_locator.port; + } + assert (kind == gv->interfaces[i].extloc.kind); + locators_add_one (&def_uni, &gv->interfaces[i].extloc, data_port); + locators_add_one (&meta_uni, &gv->interfaces[i].extloc, meta_port); + } + if (gv->config.publish_uc_locators) + { + dst->present |= PP_DEFAULT_UNICAST_LOCATOR | PP_METATRAFFIC_UNICAST_LOCATOR; + dst->aliased |= PP_DEFAULT_UNICAST_LOCATOR | PP_METATRAFFIC_UNICAST_LOCATOR; + } + } + + if (ddsi_include_multicast_locator_in_discovery (gv)) + { + dst->present |= PP_DEFAULT_MULTICAST_LOCATOR | PP_METATRAFFIC_MULTICAST_LOCATOR; + dst->aliased |= PP_DEFAULT_MULTICAST_LOCATOR | PP_METATRAFFIC_MULTICAST_LOCATOR; + struct locators_builder def_mc = locators_builder_init (&dst->default_multicast_locators, &locs->def_multi, 1); + struct locators_builder meta_mc = locators_builder_init (&dst->metatraffic_multicast_locators, &locs->meta_multi, 1); + locators_add_one (&def_mc, &gv->loc_default_mc, DDSI_LOCATOR_PORT_INVALID); + locators_add_one (&meta_mc, &gv->loc_meta_mc, DDSI_LOCATOR_PORT_INVALID); + } + + /* Add Adlink specific version information */ + { + dst->present |= PP_ADLINK_PARTICIPANT_VERSION_INFO; + memset (&dst->adlink_participant_version_info, 0, sizeof (dst->adlink_participant_version_info)); + dst->adlink_participant_version_info.version = 0; + dst->adlink_participant_version_info.flags = + DDSI_ADLINK_FL_DDSI2_PARTICIPANT_FLAG | + DDSI_ADLINK_FL_PTBES_FIXED_0 | + DDSI_ADLINK_FL_SUPPORTS_STATUSINFOX; + if (gv->config.besmode == DDSI_BESMODE_MINIMAL) + dst->adlink_participant_version_info.flags |= DDSI_ADLINK_FL_MINIMAL_BES_MODE; + ddsrt_mutex_lock (&gv->privileged_pp_lock); + if (pp->is_ddsi2_pp) + dst->adlink_participant_version_info.flags |= DDSI_ADLINK_FL_PARTICIPANT_IS_DDSI2; + ddsrt_mutex_unlock (&gv->privileged_pp_lock); + +#if DDSRT_HAVE_GETHOSTNAME + if (ddsrt_gethostname(node, sizeof(node)-1) < 0) +#endif + (void) ddsrt_strlcpy (node, "unknown", sizeof (node)); + size = strlen(node) + strlen(DDS_VERSION) + strlen(DDS_HOST_NAME) + strlen(DDS_TARGET_NAME) + 4; /* + ///'\0' */ + dst->adlink_participant_version_info.internals = ddsrt_malloc(size); + (void) snprintf(dst->adlink_participant_version_info.internals, size, "%s/%s/%s/%s", node, DDS_VERSION, DDS_HOST_NAME, DDS_TARGET_NAME); + ETRACE (pp, "ddsi_spdp_write("PGUIDFMT") - internals: %s\n", PGUID (pp->e.guid), dst->adlink_participant_version_info.internals); + } + + /* Add Cyclone specific information */ + { + const uint32_t bufsz = ddsi_receive_buffer_size (gv->m_factory); + if (bufsz > 0) + { + dst->present |= PP_CYCLONE_RECEIVE_BUFFER_SIZE; + dst->cyclone_receive_buffer_size = bufsz; + } + } + if (gv->config.redundant_networking) + { + dst->present |= PP_CYCLONE_REDUNDANT_NETWORKING; + dst->cyclone_redundant_networking = true; + } + +#ifdef DDS_HAS_SECURITY + /* Add Security specific information. */ + if (ddsi_omg_get_participant_security_info (pp, &(dst->participant_security_info))) { + dst->present |= PP_PARTICIPANT_SECURITY_INFO; + dst->aliased |= PP_PARTICIPANT_SECURITY_INFO; + } +#endif + + /* Participant QoS's insofar as they are set, different from the default, and mapped to the SPDP data, rather than to the Adlink-specific CMParticipant endpoint. */ + qosdiff = ddsi_xqos_delta (&pp->plist->qos, &ddsi_default_qos_participant, DDSI_QP_USER_DATA | DDSI_QP_ENTITY_NAME | DDSI_QP_PROPERTY_LIST | DDSI_QP_LIVELINESS); + if (gv->config.explicitly_publish_qos_set_to_default) + qosdiff |= ~(DDSI_QP_UNRECOGNIZED_INCOMPATIBLE_MASK | DDSI_QP_LIVELINESS); + + assert (dst->qos.present == 0); + ddsi_plist_mergein_missing (dst, pp->plist, 0, qosdiff); +#ifdef DDS_HAS_SECURITY + if (ddsi_omg_participant_is_secure(pp)) + ddsi_plist_mergein_missing (dst, pp->plist, PP_IDENTITY_TOKEN | PP_PERMISSIONS_TOKEN, 0); +#endif +} + +int ddsi_spdp_write (struct ddsi_participant *pp) +{ + struct ddsi_writer *wr; + ddsi_plist_t ps; + struct ddsi_participant_builtin_topic_data_locators locs; + + if (pp->e.onlylocal) { + /* This topic is only locally available. */ + return 0; + } + + ETRACE (pp, "ddsi_spdp_write("PGUIDFMT")\n", PGUID (pp->e.guid)); + + if ((wr = ddsi_get_builtin_writer (pp, DDSI_ENTITYID_SPDP_BUILTIN_PARTICIPANT_WRITER)) == NULL) + { + ETRACE (pp, "ddsi_spdp_write("PGUIDFMT") - builtin participant writer not found\n", PGUID (pp->e.guid)); + return 0; + } + + ddsi_get_participant_builtin_topic_data (pp, &ps, &locs); + return ddsi_write_and_fini_plist (wr, &ps, true); +} + +static int ddsi_spdp_dispose_unregister_with_wr (struct ddsi_participant *pp, unsigned entityid) +{ + ddsi_plist_t ps; + struct ddsi_writer *wr; + + if ((wr = ddsi_get_builtin_writer (pp, entityid)) == NULL) + { + ETRACE (pp, "ddsi_spdp_dispose_unregister("PGUIDFMT") - builtin participant %s writer not found\n", + PGUID (pp->e.guid), + entityid == DDSI_ENTITYID_SPDP_RELIABLE_BUILTIN_PARTICIPANT_SECURE_WRITER ? "secure" : ""); + return 0; + } + + ddsi_plist_init_empty (&ps); + ps.present |= PP_PARTICIPANT_GUID; + ps.participant_guid = pp->e.guid; + return ddsi_write_and_fini_plist (wr, &ps, false); +} + +int ddsi_spdp_dispose_unregister (struct ddsi_participant *pp) +{ + /* + * When disposing a participant, it should be announced on both the + * non-secure and secure writers. + * The receiver will decide from which writer it accepts the dispose. + */ + int ret = ddsi_spdp_dispose_unregister_with_wr(pp, DDSI_ENTITYID_SPDP_BUILTIN_PARTICIPANT_WRITER); + if ((ret > 0) && ddsi_omg_participant_is_secure(pp)) + { + ret = ddsi_spdp_dispose_unregister_with_wr(pp, DDSI_ENTITYID_SPDP_RELIABLE_BUILTIN_PARTICIPANT_SECURE_WRITER); + } + return ret; +} + +struct xevent_spdp_directed_cb_arg { + ddsi_guid_t pp_guid; + int nrepeats; + ddsi_guid_prefix_t dest_proxypp_guid_prefix; +}; + +static bool resend_spdp_sample_by_guid_key (struct ddsi_writer *wr, const ddsi_guid_t *guid, struct ddsi_proxy_reader *prd) +{ + /* Look up data in (transient-local) WHC by key value -- FIXME: clearly + a slightly more efficient and elegant way of looking up the key value + is to be preferred */ + struct ddsi_domaingv *gv = wr->e.gv; + bool sample_found; + ddsi_plist_t ps; + ddsi_plist_init_empty (&ps); + ps.present |= PP_PARTICIPANT_GUID; + ps.participant_guid = *guid; + struct ddsi_serdata *sd = ddsi_serdata_from_sample (gv->spdp_type, SDK_KEY, &ps); + ddsi_plist_fini (&ps); + struct ddsi_whc_borrowed_sample sample; + + ddsrt_mutex_lock (&wr->e.lock); + sample_found = ddsi_whc_borrow_sample_key (wr->whc, sd, &sample); + if (sample_found) + { + /* Claiming it is new rather than a retransmit so that the rexmit + limiting won't kick in. It is best-effort and therefore the + updating of the last transmitted sequence number won't take + place anyway. Nor is it necessary to fiddle with heartbeat + control stuff. */ + ddsi_enqueue_spdp_sample_wrlock_held (wr, sample.seq, sample.serdata, prd); + ddsi_whc_return_sample(wr->whc, &sample, false); + } + ddsrt_mutex_unlock (&wr->e.lock); + ddsi_serdata_unref (sd); + return sample_found; +} + +static bool get_pp_and_spdp_wr (struct ddsi_domaingv *gv, const ddsi_guid_t *pp_guid, struct ddsi_participant **pp, struct ddsi_writer **spdp_wr) +{ + if ((*pp = ddsi_entidx_lookup_participant_guid (gv->entity_index, pp_guid)) == NULL) + { + GVTRACE ("handle_xevk_spdp "PGUIDFMT" - unknown guid\n", PGUID (*pp_guid)); + return false; + } + if ((*spdp_wr = ddsi_get_builtin_writer (*pp, DDSI_ENTITYID_SPDP_BUILTIN_PARTICIPANT_WRITER)) == NULL) + { + GVTRACE ("handle_xevk_spdp "PGUIDFMT" - spdp writer of participant not found\n", PGUID (*pp_guid)); + return false; + } + return true; +} + +static void xevent_spdp_directed_cb (struct ddsi_domaingv *gv, struct ddsi_xevent *ev, UNUSED_ARG (struct ddsi_xpack *xp), void *varg, ddsrt_mtime_t tnow) +{ + struct xevent_spdp_directed_cb_arg * const arg = varg; + struct ddsi_participant *pp; + struct ddsi_writer *spdp_wr; + + if (!get_pp_and_spdp_wr (gv, &arg->pp_guid, &pp, &spdp_wr)) + { + ddsi_delete_xevent (ev); + return; + } + + const ddsi_guid_t guid = { .prefix = arg->dest_proxypp_guid_prefix, .entityid = { .u = DDSI_ENTITYID_SPDP_BUILTIN_PARTICIPANT_READER } }; + struct ddsi_proxy_reader *prd; + if ((prd = ddsi_entidx_lookup_proxy_reader_guid (gv->entity_index, &guid)) == NULL) + GVTRACE ("xmit spdp: no proxy reader "PGUIDFMT"\n", PGUID (guid)); + else if (!resend_spdp_sample_by_guid_key (spdp_wr, &arg->pp_guid, prd)) + GVTRACE ("xmit spdp: suppressing early spdp response from "PGUIDFMT" to %"PRIx32":%"PRIx32":%"PRIx32":%x\n", + PGUID (pp->e.guid), PGUIDPREFIX (arg->dest_proxypp_guid_prefix), DDSI_ENTITYID_PARTICIPANT); + + // Directed events are used to send SPDP packets to newly discovered peers, and used just once + if (--arg->nrepeats == 0 || gv->config.spdp_interval < DDS_SECS (1) || pp->plist->qos.liveliness.lease_duration < DDS_SECS (1)) + ddsi_delete_xevent (ev); + else + { + ddsrt_mtime_t tnext = ddsrt_mtime_add_duration (tnow, DDS_SECS (1)); + GVTRACE ("xmit spdp "PGUIDFMT" to %"PRIx32":%"PRIx32":%"PRIx32":%x (resched %gs)\n", + PGUID (pp->e.guid), + PGUIDPREFIX (arg->dest_proxypp_guid_prefix), DDSI_ENTITYID_SPDP_BUILTIN_PARTICIPANT_READER, + (double)(tnext.v - tnow.v) / 1e9); + (void) ddsi_resched_xevent_if_earlier (ev, tnext); + } +} + +static void resched_spdp_broadcast (struct ddsi_xevent *ev, struct ddsi_participant *pp, ddsrt_mtime_t tnow) +{ + /* schedule next when 80% of the interval has elapsed, or 2s + before the lease ends, whichever comes first (similar to PMD), + but never wait longer than spdp_interval */ + struct ddsi_domaingv * const gv = pp->e.gv; + const dds_duration_t mindelta = DDS_MSECS (10); + const dds_duration_t ldur = pp->plist->qos.liveliness.lease_duration; + ddsrt_mtime_t tnext; + int64_t intv; + + if (ldur < 5 * mindelta / 4) + intv = mindelta; + else if (ldur < DDS_SECS (10)) + intv = 4 * ldur / 5; + else + intv = ldur - DDS_SECS (2); + if (intv > gv->config.spdp_interval) + intv = gv->config.spdp_interval; + + tnext = ddsrt_mtime_add_duration (tnow, intv); + GVTRACE ("xmit spdp "PGUIDFMT" to %"PRIx32":%"PRIx32":%"PRIx32":%x (resched %gs)\n", + PGUID (pp->e.guid), + 0,0,0, DDSI_ENTITYID_SPDP_BUILTIN_PARTICIPANT_READER, + (double)(tnext.v - tnow.v) / 1e9); + (void) ddsi_resched_xevent_if_earlier (ev, tnext); +} + +void ddsi_xevent_spdp_broadcast_cb (struct ddsi_domaingv *gv, struct ddsi_xevent *ev, UNUSED_ARG (struct ddsi_xpack *xp), void *varg, ddsrt_mtime_t tnow) +{ + /* Like the writer pointer in the heartbeat event, the participant pointer in the spdp event is assumed valid. */ + struct ddsi_xevent_spdp_broadcast_cb_arg * const arg = varg; + struct ddsi_participant *pp; + struct ddsi_writer *spdp_wr; + + if (!get_pp_and_spdp_wr (gv, &arg->pp_guid, &pp, &spdp_wr)) + return; + + if (!resend_spdp_sample_by_guid_key (spdp_wr, &arg->pp_guid, NULL)) + { +#ifndef NDEBUG + /* If undirected, it is pp->spdp_xevent, and that one must never + run into an empty WHC unless it is already marked for deletion. + + If directed, it may happen in response to an SPDP packet during + creation of the participant. This is because pp is inserted in + the hash table quite early on, which, in turn, is because it + needs to be visible for creating its builtin endpoints. But in + this case, the initial broadcast of the SPDP packet of pp will + happen shortly. */ + ddsrt_mutex_lock (&pp->e.lock); + assert (ddsi_delete_xevent_pending (ev)); + ddsrt_mutex_unlock (&pp->e.lock); +#endif + } + + resched_spdp_broadcast (ev, pp, tnow); +} + +static unsigned pseudo_random_delay (const ddsi_guid_t *x, const ddsi_guid_t *y, ddsrt_mtime_t tnow) +{ + /* You know, an ordinary random generator would be even better, but + the C library doesn't have a reentrant one and I don't feel like + integrating, say, the Mersenne Twister right now. */ + static const uint64_t cs[] = { + UINT64_C (15385148050874689571), + UINT64_C (17503036526311582379), + UINT64_C (11075621958654396447), + UINT64_C ( 9748227842331024047), + UINT64_C (14689485562394710107), + UINT64_C (17256284993973210745), + UINT64_C ( 9288286355086959209), + UINT64_C (17718429552426935775), + UINT64_C (10054290541876311021), + UINT64_C (13417933704571658407) + }; + uint32_t a = x->prefix.u[0], b = x->prefix.u[1], c = x->prefix.u[2], d = x->entityid.u; + uint32_t e = y->prefix.u[0], f = y->prefix.u[1], g = y->prefix.u[2], h = y->entityid.u; + uint32_t i = (uint32_t) ((uint64_t) tnow.v >> 32), j = (uint32_t) tnow.v; + uint64_t m = 0; + m += (a + cs[0]) * (b + cs[1]); + m += (c + cs[2]) * (d + cs[3]); + m += (e + cs[4]) * (f + cs[5]); + m += (g + cs[6]) * (h + cs[7]); + m += (i + cs[8]) * (j + cs[9]); + return (unsigned) (m >> 32); +} + +static void respond_to_spdp (const struct ddsi_domaingv *gv, const ddsi_guid_t *dest_proxypp_guid) +{ + struct ddsi_entity_enum_participant est; + struct ddsi_participant *pp; + ddsrt_mtime_t tnow = ddsrt_time_monotonic (); + ddsi_entidx_enum_participant_init (&est, gv->entity_index); + while ((pp = ddsi_entidx_enum_participant_next (&est)) != NULL) + { + /* delay_base has 32 bits, so delay_norm is approximately 1s max; + delay_max <= 1s by gv.config checks */ + unsigned delay_base = pseudo_random_delay (&pp->e.guid, dest_proxypp_guid, tnow); + unsigned delay_norm = delay_base >> 2; + int64_t delay_max_ms = gv->config.spdp_response_delay_max / 1000000; + int64_t delay = (int64_t) delay_norm * delay_max_ms / 1000; + ddsrt_mtime_t tsched = ddsrt_mtime_add_duration (tnow, delay); + GVTRACE (" %"PRId64, delay); + if (!pp->e.gv->config.unicast_response_to_spdp_messages) + /* pp can't reach gc_delete_participant => can safely reschedule */ + (void) ddsi_resched_xevent_if_earlier (pp->spdp_xevent, tsched); + else + { + struct xevent_spdp_directed_cb_arg arg = { + .pp_guid = pp->e.guid, + .nrepeats = 4, .dest_proxypp_guid_prefix = dest_proxypp_guid->prefix + }; + ddsi_qxev_callback (gv->xevents, tsched, xevent_spdp_directed_cb, &arg, sizeof (arg), false); + } + } + ddsi_entidx_enum_participant_fini (&est); +} + +static int handle_spdp_dead (const struct ddsi_receiver_state *rst, ddsi_entityid_t pwr_entityid, ddsrt_wctime_t timestamp, const ddsi_plist_t *datap, unsigned statusinfo) +{ + struct ddsi_domaingv * const gv = rst->gv; + ddsi_guid_t guid; + + GVLOGDISC ("SPDP ST%x", statusinfo); + + if (datap->present & PP_PARTICIPANT_GUID) + { + guid = datap->participant_guid; + GVLOGDISC (" %"PRIx32":%"PRIx32":%"PRIx32":%"PRIx32, PGUID (guid)); + assert (guid.entityid.u == DDSI_ENTITYID_PARTICIPANT); + if (ddsi_is_proxy_participant_deletion_allowed(gv, &guid, pwr_entityid)) + { + if (ddsi_delete_proxy_participant_by_guid (gv, &guid, timestamp, 0) < 0) + { + GVLOGDISC (" unknown"); + } + else + { + GVLOGDISC (" delete"); + } + } + else + { + GVLOGDISC (" not allowed"); + } + } + else + { + GVWARNING ("data (SPDP, vendor %u.%u): no/invalid payload\n", rst->vendor.id[0], rst->vendor.id[1]); + } + return 1; +} + +static struct ddsi_proxy_participant *find_ddsi2_proxy_participant (const struct ddsi_entity_index *entidx, const ddsi_guid_t *ppguid) +{ + struct ddsi_entity_enum_proxy_participant it; + struct ddsi_proxy_participant *pp; + ddsi_entidx_enum_proxy_participant_init (&it, entidx); + while ((pp = ddsi_entidx_enum_proxy_participant_next (&it)) != NULL) + { + if (ddsi_vendor_is_eclipse_or_opensplice (pp->vendor) && pp->e.guid.prefix.u[0] == ppguid->prefix.u[0] && pp->is_ddsi2_pp) + break; + } + ddsi_entidx_enum_proxy_participant_fini (&it); + return pp; +} + +static void make_participants_dependent_on_ddsi2 (struct ddsi_domaingv *gv, const ddsi_guid_t *ddsi2guid, ddsrt_wctime_t timestamp) +{ + struct ddsi_entity_enum_proxy_participant it; + struct ddsi_proxy_participant *pp, *d2pp; + if ((d2pp = ddsi_entidx_lookup_proxy_participant_guid (gv->entity_index, ddsi2guid)) == NULL) + return; + ddsi_entidx_enum_proxy_participant_init (&it, gv->entity_index); + while ((pp = ddsi_entidx_enum_proxy_participant_next (&it)) != NULL) + { + if (ddsi_vendor_is_eclipse_or_opensplice (pp->vendor) && pp->e.guid.prefix.u[0] == ddsi2guid->prefix.u[0] && !pp->is_ddsi2_pp) + { + GVTRACE ("proxy participant "PGUIDFMT" depends on ddsi2 "PGUIDFMT, PGUID (pp->e.guid), PGUID (*ddsi2guid)); + ddsrt_mutex_lock (&pp->e.lock); + pp->privileged_pp_guid = *ddsi2guid; + ddsrt_mutex_unlock (&pp->e.lock); + ddsi_proxy_participant_reassign_lease (pp, d2pp->lease); + GVTRACE ("\n"); + + if (ddsi_entidx_lookup_proxy_participant_guid (gv->entity_index, ddsi2guid) == NULL) + { + /* If DDSI2 has been deleted here (i.e., very soon after + having been created), we don't know whether pp will be + deleted */ + break; + } + } + } + ddsi_entidx_enum_proxy_participant_fini (&it); + + if (pp != NULL) + { + GVTRACE ("make_participants_dependent_on_ddsi2: ddsi2 "PGUIDFMT" is no more, delete "PGUIDFMT"\n", PGUID (*ddsi2guid), PGUID (pp->e.guid)); + ddsi_delete_proxy_participant_by_guid (gv, &pp->e.guid, timestamp, 1); + } +} + +static int handle_spdp_alive (const struct ddsi_receiver_state *rst, ddsi_seqno_t seq, ddsrt_wctime_t timestamp, const ddsi_plist_t *datap) +{ + struct ddsi_domaingv * const gv = rst->gv; + const unsigned bes_sedp_announcer_mask = + DDSI_DISC_BUILTIN_ENDPOINT_SUBSCRIPTION_ANNOUNCER | + DDSI_DISC_BUILTIN_ENDPOINT_PUBLICATION_ANNOUNCER; + struct ddsi_addrset *as_meta, *as_default; + uint32_t builtin_endpoint_set; + ddsi_guid_t privileged_pp_guid; + dds_duration_t lease_duration; + unsigned custom_flags = 0; + + /* If advertised domain id or domain tag doesn't match, ignore the message. Do this first to + minimize the impact such messages have. */ + { + const uint32_t domain_id = (datap->present & PP_DOMAIN_ID) ? datap->domain_id : gv->config.extDomainId.value; + const char *domain_tag = (datap->present & PP_DOMAIN_TAG) ? datap->domain_tag : ""; + if (domain_id != gv->config.extDomainId.value || strcmp (domain_tag, gv->config.domainTag) != 0) + { + GVTRACE ("ignore remote participant in mismatching domain %"PRIu32" tag \"%s\"\n", domain_id, domain_tag); + return 0; + } + } + + if (!(datap->present & PP_PARTICIPANT_GUID) || !(datap->present & PP_BUILTIN_ENDPOINT_SET)) + { + GVWARNING ("data (SPDP, vendor %u.%u): no/invalid payload\n", rst->vendor.id[0], rst->vendor.id[1]); + return 0; + } + + /* At some point the RTI implementation didn't mention + BUILTIN_ENDPOINT_DDSI_PARTICIPANT_MESSAGE_DATA_READER & ...WRITER, or + so it seemed; and yet they are necessary for correct operation, + so add them. */ + builtin_endpoint_set = datap->builtin_endpoint_set; + if (ddsi_vendor_is_rti (rst->vendor) && + ((builtin_endpoint_set & + (DDSI_BUILTIN_ENDPOINT_PARTICIPANT_MESSAGE_DATA_READER | + DDSI_BUILTIN_ENDPOINT_PARTICIPANT_MESSAGE_DATA_WRITER)) + != (DDSI_BUILTIN_ENDPOINT_PARTICIPANT_MESSAGE_DATA_READER | + DDSI_BUILTIN_ENDPOINT_PARTICIPANT_MESSAGE_DATA_WRITER)) && + gv->config.assume_rti_has_pmd_endpoints) + { + GVLOGDISC ("data (SPDP, vendor %u.%u): assuming unadvertised PMD endpoints do exist\n", + rst->vendor.id[0], rst->vendor.id[1]); + builtin_endpoint_set |= + DDSI_BUILTIN_ENDPOINT_PARTICIPANT_MESSAGE_DATA_READER | + DDSI_BUILTIN_ENDPOINT_PARTICIPANT_MESSAGE_DATA_WRITER; + } + + /* Do we know this GUID already? */ + { + struct ddsi_entity_common *existing_entity; + if ((existing_entity = ddsi_entidx_lookup_guid_untyped (gv->entity_index, &datap->participant_guid)) == NULL) + { + /* Local SPDP packets may be looped back, and that can include ones + for participants currently being deleted. The first thing that + happens when deleting a participant is removing it from the hash + table, and consequently the looped back packet may appear to be + from an unknown participant. So we handle that. */ + if (ddsi_is_deleted_participant_guid (gv->deleted_participants, &datap->participant_guid, DDSI_DELETED_PPGUID_REMOTE)) + { + RSTTRACE ("SPDP ST0 "PGUIDFMT" (recently deleted)", PGUID (datap->participant_guid)); + return 0; + } + } + else if (existing_entity->kind == DDSI_EK_PARTICIPANT) + { + RSTTRACE ("SPDP ST0 "PGUIDFMT" (local)", PGUID (datap->participant_guid)); + return 0; + } + else if (existing_entity->kind == DDSI_EK_PROXY_PARTICIPANT) + { + struct ddsi_proxy_participant *proxypp = (struct ddsi_proxy_participant *) existing_entity; + struct ddsi_lease *lease; + int interesting = 0; + RSTTRACE ("SPDP ST0 "PGUIDFMT" (known)", PGUID (datap->participant_guid)); + /* SPDP processing is so different from normal processing that we are + even skipping the automatic lease renewal. Note that proxy writers + that are not alive are not set alive here. This is done only when + data is received from a particular pwr (in handle_regular) */ + if ((lease = ddsrt_atomic_ldvoidp (&proxypp->minl_auto)) != NULL) + ddsi_lease_renew (lease, ddsrt_time_elapsed ()); + ddsrt_mutex_lock (&proxypp->e.lock); + if (proxypp->implicitly_created || seq > proxypp->seq) + { + interesting = 1; + if (!(gv->logconfig.c.mask & DDS_LC_TRACE)) + GVLOGDISC ("SPDP ST0 "PGUIDFMT, PGUID (datap->participant_guid)); + GVLOGDISC (proxypp->implicitly_created ? " (NEW was-implicitly-created)" : " (update)"); + proxypp->implicitly_created = 0; + ddsi_update_proxy_participant_plist_locked (proxypp, seq, datap, timestamp); + } + ddsrt_mutex_unlock (&proxypp->e.lock); + return interesting; + } + else + { + /* mismatch on entity kind: that should never have gotten past the + input validation */ + GVWARNING ("data (SPDP, vendor %u.%u): "PGUIDFMT" kind mismatch\n", rst->vendor.id[0], rst->vendor.id[1], PGUID (datap->participant_guid)); + return 0; + } + } + + const bool is_secure = ((datap->builtin_endpoint_set & DDSI_DISC_BUILTIN_ENDPOINT_PARTICIPANT_SECURE_ANNOUNCER) != 0 && + (datap->present & PP_IDENTITY_TOKEN)); + /* Make sure we don't create any security builtin endpoint when it's considered unsecure. */ + if (!is_secure) + builtin_endpoint_set &= DDSI_BES_MASK_NON_SECURITY; + GVLOGDISC ("SPDP ST0 "PGUIDFMT" bes %"PRIx32"%s NEW", PGUID (datap->participant_guid), builtin_endpoint_set, is_secure ? " (secure)" : ""); + + if (datap->present & PP_ADLINK_PARTICIPANT_VERSION_INFO) { + if ((datap->adlink_participant_version_info.flags & DDSI_ADLINK_FL_DDSI2_PARTICIPANT_FLAG) && + (datap->adlink_participant_version_info.flags & DDSI_ADLINK_FL_PARTICIPANT_IS_DDSI2)) + custom_flags |= DDSI_CF_PARTICIPANT_IS_DDSI2; + + GVLOGDISC (" (0x%08"PRIx32"-0x%08"PRIx32"-0x%08"PRIx32"-0x%08"PRIx32"-0x%08"PRIx32" %s)", + datap->adlink_participant_version_info.version, + datap->adlink_participant_version_info.flags, + datap->adlink_participant_version_info.unused[0], + datap->adlink_participant_version_info.unused[1], + datap->adlink_participant_version_info.unused[2], + datap->adlink_participant_version_info.internals); + } + + /* Can't do "mergein_missing" because of constness of *datap */ + if (datap->qos.present & DDSI_QP_LIVELINESS) + lease_duration = datap->qos.liveliness.lease_duration; + else + { + assert (ddsi_default_qos_participant.present & DDSI_QP_LIVELINESS); + lease_duration = ddsi_default_qos_participant.liveliness.lease_duration; + } + /* If any of the SEDP announcer are missing AND the guid prefix of + the SPDP writer differs from the guid prefix of the new participant, + we make it dependent on the writer's participant. See also the + lease expiration handling. Note that the entityid MUST be + DDSI_ENTITYID_PARTICIPANT or entidx_lookup will assert. So we only + zero the prefix. */ + privileged_pp_guid.prefix = rst->src_guid_prefix; + privileged_pp_guid.entityid.u = DDSI_ENTITYID_PARTICIPANT; + if ((builtin_endpoint_set & bes_sedp_announcer_mask) != bes_sedp_announcer_mask && + memcmp (&privileged_pp_guid, &datap->participant_guid, sizeof (ddsi_guid_t)) != 0) + { + GVLOGDISC (" (depends on "PGUIDFMT")", PGUID (privileged_pp_guid)); + /* never expire lease for this proxy: it won't actually expire + until the "privileged" one expires anyway */ + lease_duration = DDS_INFINITY; + } + else if (ddsi_vendor_is_eclipse_or_opensplice (rst->vendor) && !(custom_flags & DDSI_CF_PARTICIPANT_IS_DDSI2)) + { + /* Non-DDSI2 participants are made dependent on DDSI2 (but DDSI2 + itself need not be discovered yet) */ + struct ddsi_proxy_participant *ddsi2; + if ((ddsi2 = find_ddsi2_proxy_participant (gv->entity_index, &datap->participant_guid)) == NULL) + memset (&privileged_pp_guid.prefix, 0, sizeof (privileged_pp_guid.prefix)); + else + { + privileged_pp_guid.prefix = ddsi2->e.guid.prefix; + lease_duration = DDS_INFINITY; + GVLOGDISC (" (depends on "PGUIDFMT")", PGUID (privileged_pp_guid)); + } + } + else + { + memset (&privileged_pp_guid.prefix, 0, sizeof (privileged_pp_guid.prefix)); + } + + /* Choose locators */ + { + const ddsi_locators_t emptyset = { .n = 0, .first = NULL, .last = NULL }; + const ddsi_locators_t *uc; + const ddsi_locators_t *mc; + ddsi_locator_t srcloc; + ddsi_interface_set_t intfs; + + srcloc = rst->srcloc; + uc = (datap->present & PP_DEFAULT_UNICAST_LOCATOR) ? &datap->default_unicast_locators : ∅ + mc = (datap->present & PP_DEFAULT_MULTICAST_LOCATOR) ? &datap->default_multicast_locators : ∅ + if (gv->config.tcp_use_peeraddr_for_unicast) + uc = ∅ // force use of source locator + else if (uc != &emptyset) + ddsi_set_unspec_locator (&srcloc); // can't always use the source address + + ddsi_interface_set_init (&intfs); + as_default = ddsi_addrset_from_locatorlists (gv, uc, mc, &srcloc, &intfs); + + srcloc = rst->srcloc; + uc = (datap->present & PP_METATRAFFIC_UNICAST_LOCATOR) ? &datap->metatraffic_unicast_locators : ∅ + mc = (datap->present & PP_METATRAFFIC_MULTICAST_LOCATOR) ? &datap->metatraffic_multicast_locators : ∅ + if (gv->config.tcp_use_peeraddr_for_unicast) + uc = ∅ // force use of source locator + else if (uc != &emptyset) + ddsi_set_unspec_locator (&srcloc); // can't always use the source address + ddsi_interface_set_init (&intfs); + as_meta = ddsi_addrset_from_locatorlists (gv, uc, mc, &srcloc, &intfs); + + ddsi_log_addrset (gv, DDS_LC_DISCOVERY, " (data", as_default); + ddsi_log_addrset (gv, DDS_LC_DISCOVERY, " meta", as_meta); + GVLOGDISC (")"); + } + + if (ddsi_addrset_empty_uc (as_default) || ddsi_addrset_empty_uc (as_meta)) + { + GVLOGDISC (" (no unicast address"); + ddsi_unref_addrset (as_default); + ddsi_unref_addrset (as_meta); + return 1; + } + + GVLOGDISC (" QOS={"); + ddsi_xqos_log (DDS_LC_DISCOVERY, &gv->logconfig, &datap->qos); + GVLOGDISC ("}\n"); + + maybe_add_pp_as_meta_to_as_disc (gv, as_meta); + + if (!ddsi_new_proxy_participant (gv, &datap->participant_guid, builtin_endpoint_set, &privileged_pp_guid, as_default, as_meta, datap, lease_duration, rst->vendor, custom_flags, timestamp, seq)) + { + /* If no proxy participant was created, don't respond */ + return 0; + } + else + { + /* Force transmission of SPDP messages - we're not very careful + in avoiding the processing of SPDP packets addressed to others + so filter here */ + int have_dst = (rst->dst_guid_prefix.u[0] != 0 || rst->dst_guid_prefix.u[1] != 0 || rst->dst_guid_prefix.u[2] != 0); + if (!have_dst) + { + GVLOGDISC ("broadcasted SPDP packet -> answering"); + respond_to_spdp (gv, &datap->participant_guid); + } + else + { + GVLOGDISC ("directed SPDP packet -> not responding\n"); + } + + if (custom_flags & DDSI_CF_PARTICIPANT_IS_DDSI2) + { + /* If we just discovered DDSI2, make sure any existing + participants served by it are made dependent on it */ + make_participants_dependent_on_ddsi2 (gv, &datap->participant_guid, timestamp); + } + else if (privileged_pp_guid.prefix.u[0] || privileged_pp_guid.prefix.u[1] || privileged_pp_guid.prefix.u[2]) + { + /* If we just created a participant dependent on DDSI2, make sure + DDSI2 still exists. There is a risk of racing the lease expiry + of DDSI2. */ + if (ddsi_entidx_lookup_proxy_participant_guid (gv->entity_index, &privileged_pp_guid) == NULL) + { + GVLOGDISC ("make_participants_dependent_on_ddsi2: ddsi2 "PGUIDFMT" is no more, delete "PGUIDFMT"\n", + PGUID (privileged_pp_guid), PGUID (datap->participant_guid)); + ddsi_delete_proxy_participant_by_guid (gv, &datap->participant_guid, timestamp, 1); + } + } + return 1; + } +} + +void ddsi_handle_spdp (const struct ddsi_receiver_state *rst, ddsi_entityid_t pwr_entityid, ddsi_seqno_t seq, const struct ddsi_serdata *serdata) +{ + struct ddsi_domaingv * const gv = rst->gv; + ddsi_plist_t decoded_data; + if (ddsi_serdata_to_sample (serdata, &decoded_data, NULL, NULL)) + { + int interesting = 0; + switch (serdata->statusinfo & (DDSI_STATUSINFO_DISPOSE | DDSI_STATUSINFO_UNREGISTER)) + { + case 0: + interesting = handle_spdp_alive (rst, seq, serdata->timestamp, &decoded_data); + break; + + case DDSI_STATUSINFO_DISPOSE: + case DDSI_STATUSINFO_UNREGISTER: + case (DDSI_STATUSINFO_DISPOSE | DDSI_STATUSINFO_UNREGISTER): + interesting = handle_spdp_dead (rst, pwr_entityid, serdata->timestamp, &decoded_data, serdata->statusinfo); + break; + } + + ddsi_plist_fini (&decoded_data); + GVLOG (interesting ? DDS_LC_DISCOVERY : DDS_LC_TRACE, "\n"); + } +} diff --git a/src/core/ddsi/src/ddsi_discovery_topic.c b/src/core/ddsi/src/ddsi_discovery_topic.c new file mode 100644 index 0000000000..7e22b23e87 --- /dev/null +++ b/src/core/ddsi/src/ddsi_discovery_topic.c @@ -0,0 +1,177 @@ +/* + * Copyright(c) 2006 to 2022 ZettaScale Technology and others + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License + * v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ +#include +#include +#include +#include + +#include "dds/ddsrt/heap.h" +#include "dds/ddsrt/log.h" +#include "dds/ddsi/ddsi_domaingv.h" +#include "ddsi__discovery_sedp.h" +#include "ddsi__discovery_topic.h" +#include "ddsi__entity_index.h" +#include "ddsi__entity.h" +#include "ddsi__participant.h" +#include "ddsi__transmit.h" +#include "ddsi__security_omg.h" +#include "ddsi__endpoint.h" +#include "ddsi__plist.h" +#include "ddsi__topic.h" +#include "ddsi__vendor.h" +#include "ddsi__xqos.h" +#include "ddsi__typelib.h" + +static int ddsi_sedp_write_topic_impl (struct ddsi_writer *wr, int alive, const ddsi_guid_t *guid, const dds_qos_t *xqos, ddsi_typeinfo_t *type_info) +{ + struct ddsi_domaingv * const gv = wr->e.gv; + const dds_qos_t *defqos = &ddsi_default_qos_topic; + + ddsi_plist_t ps; + ddsi_plist_init_empty (&ps); + ps.present |= PP_CYCLONE_TOPIC_GUID; + ps.topic_guid = *guid; + + assert (xqos != NULL); + ps.present |= PP_PROTOCOL_VERSION | PP_VENDORID; + ps.protocol_version.major = DDSI_RTPS_MAJOR; + ps.protocol_version.minor = DDSI_RTPS_MINOR; + ps.vendorid = DDSI_VENDORID_ECLIPSE; + + uint64_t qosdiff = ddsi_xqos_delta (xqos, defqos, ~(uint64_t)0); + if (gv->config.explicitly_publish_qos_set_to_default) + qosdiff |= ~DDSI_QP_UNRECOGNIZED_INCOMPATIBLE_MASK; + + if (type_info) + { + ps.qos.type_information = type_info; + ps.qos.present |= DDSI_QP_TYPE_INFORMATION; + } + if (xqos) + ddsi_xqos_mergein_missing (&ps.qos, xqos, qosdiff); + return ddsi_write_and_fini_plist (wr, &ps, alive); +} + +int ddsi_sedp_write_topic (struct ddsi_topic *tp, bool alive) +{ + int res = 0; + if (!(tp->pp->bes & DDSI_DISC_BUILTIN_ENDPOINT_TOPICS_ANNOUNCER)) + return res; + if (!ddsi_is_builtin_entityid (tp->e.guid.entityid, DDSI_VENDORID_ECLIPSE) && !tp->e.onlylocal) + { + unsigned entityid = ddsi_determine_topic_writer (tp); + struct ddsi_writer *sedp_wr = ddsi_get_sedp_writer (tp->pp, entityid); + ddsrt_mutex_lock (&tp->e.qos_lock); + // the allocation type info object is freed with the plist + res = ddsi_sedp_write_topic_impl (sedp_wr, alive, &tp->e.guid, tp->definition->xqos, ddsi_type_pair_complete_info (tp->e.gv, tp->definition->type_pair)); + ddsrt_mutex_unlock (&tp->e.qos_lock); + } + return res; +} + +static const char *durability_to_string (dds_durability_kind_t k) +{ + switch (k) + { + case DDS_DURABILITY_VOLATILE: return "volatile"; + case DDS_DURABILITY_TRANSIENT_LOCAL: return "transient-local"; + case DDS_DURABILITY_TRANSIENT: return "transient"; + case DDS_DURABILITY_PERSISTENT: return "persistent"; + } + return "undefined-durability"; +} + +void ddsi_handle_sedp_alive_topic (const struct ddsi_receiver_state *rst, ddsi_seqno_t seq, ddsi_plist_t *datap /* note: potentially modifies datap */, const ddsi_guid_prefix_t *src_guid_prefix, ddsi_vendorid_t vendorid, ddsrt_wctime_t timestamp) +{ + struct ddsi_domaingv * const gv = rst->gv; + struct ddsi_proxy_participant *proxypp; + ddsi_guid_t ppguid; + dds_qos_t *xqos; + int reliable; + const ddsi_typeid_t *type_id_minimal = NULL, *type_id_complete = NULL; + + assert (datap); + assert (datap->present & PP_CYCLONE_TOPIC_GUID); + GVLOGDISC (" "PGUIDFMT, PGUID (datap->topic_guid)); + + if (!ddsi_handle_sedp_checks (gv, SEDP_KIND_TOPIC, &datap->topic_guid, datap, src_guid_prefix, vendorid, timestamp, &proxypp, &ppguid)) + return; + + xqos = &datap->qos; + ddsi_xqos_mergein_missing (xqos, &ddsi_default_qos_topic, ~(uint64_t)0); + /* After copy + merge, should have at least the ones present in the + input. Also verify reliability and durability are present, + because we explicitly read those. */ + assert ((xqos->present & datap->qos.present) == datap->qos.present); + assert (xqos->present & DDSI_QP_RELIABILITY); + assert (xqos->present & DDSI_QP_DURABILITY); + reliable = (xqos->reliability.kind == DDS_RELIABILITY_RELIABLE); + + GVLOGDISC (" %s %s %s: %s/%s", + reliable ? "reliable" : "best-effort", + durability_to_string (xqos->durability.kind), + "topic", xqos->topic_name, xqos->type_name); + if (xqos->present & DDSI_QP_TYPE_INFORMATION) + { + struct ddsi_typeid_str strm, strc; + type_id_minimal = ddsi_typeinfo_minimal_typeid (xqos->type_information); + type_id_complete = ddsi_typeinfo_complete_typeid (xqos->type_information); + GVLOGDISC (" tid %s/%s", ddsi_make_typeid_str(&strm, type_id_minimal), ddsi_make_typeid_str(&strc, type_id_complete)); + } + GVLOGDISC (" QOS={"); + ddsi_xqos_log (DDS_LC_DISCOVERY, &gv->logconfig, xqos); + GVLOGDISC ("}\n"); + + if ((datap->topic_guid.entityid.u & DDSI_ENTITYID_SOURCE_MASK) == DDSI_ENTITYID_SOURCE_VENDOR && !ddsi_vendor_is_eclipse_or_adlink (vendorid)) + { + GVLOGDISC ("ignoring vendor-specific topic "PGUIDFMT"\n", PGUID (datap->topic_guid)); + } + else + { + // FIXME: check compatibility with known topic definitions + struct ddsi_proxy_topic *ptp = ddsi_lookup_proxy_topic (proxypp, &datap->topic_guid); + if (ptp) + { + GVLOGDISC (" update known proxy-topic%s\n", ddsi_vendor_is_cloud (vendorid) ? "-DS" : ""); + ddsi_update_proxy_topic (proxypp, ptp, seq, xqos, timestamp); + } + else + { + GVLOGDISC (" NEW proxy-topic"); + if (ddsi_new_proxy_topic (proxypp, seq, &datap->topic_guid, type_id_minimal, type_id_complete, xqos, timestamp) != DDS_RETCODE_OK) + GVLOGDISC (" failed"); + } + } +} + +void ddsi_handle_sedp_dead_topic (const struct ddsi_receiver_state *rst, ddsi_plist_t *datap, ddsrt_wctime_t timestamp) +{ + struct ddsi_proxy_participant *proxypp; + struct ddsi_proxy_topic *proxytp; + struct ddsi_domaingv * const gv = rst->gv; + assert (datap->present & PP_CYCLONE_TOPIC_GUID); + GVLOGDISC (" "PGUIDFMT" ", PGUID (datap->topic_guid)); + if (!ddsi_check_sedp_kind_and_guid (SEDP_KIND_TOPIC, &datap->topic_guid)) + return; + ddsi_guid_t ppguid = { .prefix = datap->topic_guid.prefix, .entityid.u = DDSI_ENTITYID_PARTICIPANT }; + if ((proxypp = ddsi_entidx_lookup_proxy_participant_guid (gv->entity_index, &ppguid)) == NULL) + GVLOGDISC (" unknown proxypp\n"); + else if ((proxytp = ddsi_lookup_proxy_topic (proxypp, &datap->topic_guid)) == NULL) + GVLOGDISC (" unknown proxy topic\n"); + else + { + ddsrt_mutex_lock (&proxypp->e.lock); + int res = ddsi_delete_proxy_topic_locked (proxypp, proxytp, timestamp); + GVLOGDISC (" %s\n", res == DDS_RETCODE_PRECONDITION_NOT_MET ? " already-deleting" : " delete"); + ddsrt_mutex_unlock (&proxypp->e.lock); + } +} diff --git a/src/core/ddsi/src/ddsi_endpoint.c b/src/core/ddsi/src/ddsi_endpoint.c index e79096f879..79254d68b0 100644 --- a/src/core/ddsi/src/ddsi_endpoint.c +++ b/src/core/ddsi/src/ddsi_endpoint.c @@ -12,6 +12,7 @@ #include #include #include +#include #include "dds/ddsrt/avl.h" #include "dds/ddsrt/heap.h" @@ -30,7 +31,7 @@ #include "ddsi__udp.h" #include "ddsi__wraddrset.h" #include "ddsi__security_omg.h" -#include "ddsi__discovery.h" +#include "ddsi__discovery_endpoint.h" #include "ddsi__whc.h" #include "ddsi__xevent.h" #include "ddsi__addrset.h" @@ -726,6 +727,193 @@ int ddsi_writer_set_notalive (struct ddsi_writer *wr, bool notify) return ret; } +#ifdef DDS_HAS_SECURITY +static int send_heartbeat_to_all_readers_check_and_sched (struct ddsi_xevent *ev, struct ddsi_writer *wr, const struct ddsi_whc_state *whcst, ddsrt_mtime_t tnow, ddsrt_mtime_t *t_next) +{ + int send; + if (!ddsi_writer_must_have_hb_scheduled (wr, whcst)) + { + wr->hbcontrol.tsched = DDSRT_MTIME_NEVER; + send = -1; + } + else if (!ddsi_writer_hbcontrol_must_send (wr, whcst, tnow)) + { + wr->hbcontrol.tsched = ddsrt_mtime_add_duration (tnow, ddsi_writer_hbcontrol_intv (wr, whcst, tnow)); + send = -1; + } + else + { + const int hbansreq = ddsi_writer_hbcontrol_ack_required (wr, whcst, tnow); + wr->hbcontrol.tsched = ddsrt_mtime_add_duration (tnow, ddsi_writer_hbcontrol_intv (wr, whcst, tnow)); + send = hbansreq; + } + + ddsi_resched_xevent_if_earlier (ev, wr->hbcontrol.tsched); + *t_next = wr->hbcontrol.tsched; + return send; +} + +static void send_heartbeat_to_all_readers (struct ddsi_xpack *xp, struct ddsi_xevent *ev, struct ddsi_writer *wr, ddsrt_mtime_t tnow) +{ + struct ddsi_whc_state whcst; + ddsrt_mtime_t t_next; + unsigned count = 0; + + ddsrt_mutex_lock (&wr->e.lock); + + ddsi_whc_get_state(wr->whc, &whcst); + const int hbansreq = send_heartbeat_to_all_readers_check_and_sched (ev, wr, &whcst, tnow, &t_next); + if (hbansreq >= 0) + { + struct ddsi_wr_prd_match *m; + struct ddsi_guid last_guid = { .prefix = {.u = {0,0,0}}, .entityid = {0} }; + + while ((m = ddsrt_avl_lookup_succ (&ddsi_wr_readers_treedef, &wr->readers, &last_guid)) != NULL) + { + last_guid = m->prd_guid; + if (m->seq < m->last_seq) + { + struct ddsi_proxy_reader *prd; + + prd = ddsi_entidx_lookup_proxy_reader_guid (wr->e.gv->entity_index, &m->prd_guid); + if (prd) + { + ETRACE (wr, " heartbeat(wr "PGUIDFMT" rd "PGUIDFMT" %s) send, resched in %g s (min-ack %"PRIu64", avail-seq %"PRIu64")\n", + PGUID (wr->e.guid), + PGUID (m->prd_guid), + hbansreq ? "" : " final", + (double)(t_next.v - tnow.v) / 1e9, + m->seq, + m->last_seq); + + struct ddsi_xmsg *msg = ddsi_writer_hbcontrol_p2p(wr, &whcst, hbansreq, prd); + if (msg != NULL) + { + ddsrt_mutex_unlock (&wr->e.lock); + ddsi_xpack_addmsg (xp, msg, 0); + ddsrt_mutex_lock (&wr->e.lock); + } + count++; + } + } + } + } + + if (count == 0) + { + if (ddsrt_avl_is_empty (&wr->readers)) + { + ETRACE (wr, "heartbeat(wr "PGUIDFMT") suppressed, resched in %g s (min-ack [none], avail-seq %"PRIu64", xmit %"PRIu64")\n", + PGUID (wr->e.guid), + (t_next.v == DDS_NEVER) ? INFINITY : (double)(t_next.v - tnow.v) / 1e9, + whcst.max_seq, + ddsi_writer_read_seq_xmit(wr)); + } + else + { + ETRACE (wr, "heartbeat(wr "PGUIDFMT") suppressed, resched in %g s (min-ack %"PRIu64"%s, avail-seq %"PRIu64", xmit %"PRIu64")\n", + PGUID (wr->e.guid), + (t_next.v == DDS_NEVER) ? INFINITY : (double)(t_next.v - tnow.v) / 1e9, + ((struct ddsi_wr_prd_match *) ddsrt_avl_root (&ddsi_wr_readers_treedef, &wr->readers))->min_seq, + ((struct ddsi_wr_prd_match *) ddsrt_avl_root (&ddsi_wr_readers_treedef, &wr->readers))->all_have_replied_to_hb ? "" : "!", + whcst.max_seq, + ddsi_writer_read_seq_xmit(wr)); + } + } + + ddsrt_mutex_unlock (&wr->e.lock); +} +#endif + +struct handle_heartbeat_event_arg { + ddsi_guid_t wr_guid; +}; + +static void handle_heartbeat_event (struct ddsi_domaingv *gv, struct ddsi_xevent *ev, struct ddsi_xpack *xp, void *varg, ddsrt_mtime_t tnow) +{ + struct handle_heartbeat_event_arg const * const arg = varg; + struct ddsi_writer *wr; + if ((wr = ddsi_entidx_lookup_writer_guid (gv->entity_index, &arg->wr_guid)) == NULL) + { + return; + } + + struct ddsi_xmsg *msg; + ddsrt_mtime_t t_next; + int hbansreq = 0; + struct ddsi_whc_state whcst; + +#ifdef DDS_HAS_SECURITY + if (wr->e.guid.entityid.u == DDSI_ENTITYID_P2P_BUILTIN_PARTICIPANT_VOLATILE_SECURE_WRITER) + { + send_heartbeat_to_all_readers(xp, ev, wr, tnow); + return; + } +#endif + + ddsrt_mutex_lock (&wr->e.lock); + assert (wr->reliable); + ddsi_whc_get_state(wr->whc, &whcst); + if (!ddsi_writer_must_have_hb_scheduled (wr, &whcst)) + { + hbansreq = 1; /* just for trace */ + msg = NULL; /* Need not send it now, and no need to schedule it for the future */ + t_next.v = DDS_NEVER; + } + else if (!ddsi_writer_hbcontrol_must_send (wr, &whcst, tnow)) + { + hbansreq = 1; /* just for trace */ + msg = NULL; + t_next.v = tnow.v + ddsi_writer_hbcontrol_intv (wr, &whcst, tnow); + } + else + { + hbansreq = ddsi_writer_hbcontrol_ack_required (wr, &whcst, tnow); + msg = ddsi_writer_hbcontrol_create_heartbeat (wr, &whcst, tnow, hbansreq, 0); + t_next.v = tnow.v + ddsi_writer_hbcontrol_intv (wr, &whcst, tnow); + } + + if (ddsrt_avl_is_empty (&wr->readers)) + { + GVTRACE ("heartbeat(wr "PGUIDFMT"%s) %s, resched in %g s (min-ack [none], avail-seq %"PRIu64", xmit %"PRIu64")\n", + PGUID (wr->e.guid), + hbansreq ? "" : " final", + msg ? "sent" : "suppressed", + (t_next.v == DDS_NEVER) ? INFINITY : (double)(t_next.v - tnow.v) / 1e9, + whcst.max_seq, ddsi_writer_read_seq_xmit (wr)); + } + else + { + GVTRACE ("heartbeat(wr "PGUIDFMT"%s) %s, resched in %g s (min-ack %"PRId64"%s, avail-seq %"PRIu64", xmit %"PRIu64")\n", + PGUID (wr->e.guid), + hbansreq ? "" : " final", + msg ? "sent" : "suppressed", + (t_next.v == DDS_NEVER) ? INFINITY : (double)(t_next.v - tnow.v) / 1e9, + ((struct ddsi_wr_prd_match *) ddsrt_avl_root_non_empty (&ddsi_wr_readers_treedef, &wr->readers))->min_seq, + ((struct ddsi_wr_prd_match *) ddsrt_avl_root_non_empty (&ddsi_wr_readers_treedef, &wr->readers))->all_have_replied_to_hb ? "" : "!", + whcst.max_seq, ddsi_writer_read_seq_xmit (wr)); + } + (void) ddsi_resched_xevent_if_earlier (ev, t_next); + wr->hbcontrol.tsched = t_next; + ddsrt_mutex_unlock (&wr->e.lock); + + /* Can't transmit synchronously with writer lock held: trying to add + the heartbeat to the xp may cause xp to be sent out, which may + require updating wr->seq_xmit for other messages already in xp. + Besides, ddsi_xpack_addmsg may sleep for bandwidth-limited channels + and we certainly don't want to hold the lock during that time. */ + if (msg) + { + if (!wr->test_suppress_heartbeat) + ddsi_xpack_addmsg (xp, msg, 0); + else + { + GVTRACE ("test_suppress_heartbeat\n"); + ddsi_xmsg_free (msg); + } + } +} + static void ddsi_new_writer_guid_common_init (struct ddsi_writer *wr, const char *topic_name, const struct ddsi_sertype *type, const struct dds_qos *xqos, struct ddsi_whc *whc, ddsi_status_cb_t status_cb, void * status_entity) { ddsrt_cond_init (&wr->throttle_cond); @@ -863,10 +1051,13 @@ static void ddsi_new_writer_guid_common_init (struct ddsi_writer *wr, const char writer for it in the hash table. NEVER => won't ever be scheduled, and this can only change by writing data, which won't happen until after it becomes visible. */ - if (wr->reliable) - wr->heartbeat_xevent = ddsi_qxev_heartbeat (wr->evq, DDSRT_MTIME_NEVER, &wr->e.guid); - else + if (!wr->reliable) wr->heartbeat_xevent = NULL; + else + { + struct handle_heartbeat_event_arg arg = {.wr_guid = wr->e.guid }; + wr->heartbeat_xevent = ddsi_qxev_callback (wr->evq, DDSRT_MTIME_NEVER, handle_heartbeat_event, &arg, sizeof (arg), false); + } assert (wr->xqos->present & DDSI_QP_LIVELINESS); if (wr->xqos->liveliness.lease_duration != DDS_INFINITY) @@ -1263,6 +1454,19 @@ void ddsi_delete_local_orphan_writer (struct ddsi_local_orphan_writer *lowr) ddsrt_mutex_unlock (&lowr->wr.e.lock); } +struct xevent_delete_writer_cb_arg { + ddsi_guid_t wr_guid; +}; + +static void xevent_delete_writer_cb (struct ddsi_domaingv *gv, struct ddsi_xevent *ev, UNUSED_ARG (struct ddsi_xpack *xp), void *varg, UNUSED_ARG (ddsrt_mtime_t tnow)) +{ + struct xevent_delete_writer_cb_arg const * const arg = varg; + /* don't worry if the writer is already gone by the time we get here, delete_writer_nolinger checks for that. */ + GVTRACE ("handle_xevk_delete_writer: "PGUIDFMT"\n", PGUID (arg->wr_guid)); + ddsi_delete_writer_nolinger (gv, &arg->wr_guid); + ddsi_delete_xevent (ev); +} + dds_return_t ddsi_delete_writer (struct ddsi_domaingv *gv, const struct ddsi_guid *guid) { struct ddsi_writer *wr; @@ -1296,7 +1500,9 @@ dds_return_t ddsi_delete_writer (struct ddsi_domaingv *gv, const struct ddsi_gui ddsrt_mtime_to_sec_usec (&tsec, &tusec, tsched); GVLOGDISC ("delete_writer(guid "PGUIDFMT") - unack'ed samples, will delete when ack'd or at t = %"PRId32".%06"PRId32"\n", PGUID (*guid), tsec, tusec); - ddsi_qxev_delete_writer (gv->xevents, tsched, &wr->e.guid); + + struct xevent_delete_writer_cb_arg arg = { .wr_guid = wr->e.guid }; + ddsi_qxev_callback (gv->xevents, tsched, xevent_delete_writer_cb, &arg, sizeof (arg), false); } return 0; } diff --git a/src/core/ddsi/src/ddsi_endpoint_match.c b/src/core/ddsi/src/ddsi_endpoint_match.c index 91e747baeb..811fd1a13a 100644 --- a/src/core/ddsi/src/ddsi_endpoint_match.c +++ b/src/core/ddsi/src/ddsi_endpoint_match.c @@ -946,6 +946,131 @@ void ddsi_reader_add_local_connection (struct ddsi_reader *rd, struct ddsi_write } } +#include "ddsi__acknack.h" +#include "ddsi__misc.h" + +static dds_duration_t preemptive_acknack_interval (const struct ddsi_pwr_rd_match *rwn) +{ + if (rwn->t_last_ack.v < rwn->tcreate.v) + return 0; + else + { + const dds_duration_t age = rwn->t_last_ack.v - rwn->tcreate.v; + if (age <= DDS_SECS (10)) + return DDS_SECS (1); + else if (age <= DDS_SECS (60)) + return DDS_SECS (2); + else if (age <= DDS_SECS (120)) + return DDS_SECS (5); + else + return DDS_SECS (10); + } +} + +static struct ddsi_xmsg *make_preemptive_acknack (struct ddsi_xevent *ev, struct ddsi_proxy_writer *pwr, struct ddsi_pwr_rd_match *rwn, ddsrt_mtime_t tnow) +{ + const dds_duration_t old_intv = preemptive_acknack_interval (rwn); + if (tnow.v < ddsrt_mtime_add_duration (rwn->t_last_ack, old_intv).v) + { + (void) ddsi_resched_xevent_if_earlier (ev, ddsrt_mtime_add_duration (rwn->t_last_ack, old_intv)); + return NULL; + } + + struct ddsi_domaingv * const gv = pwr->e.gv; + struct ddsi_participant *pp = NULL; + if (ddsi_omg_proxy_participant_is_secure (pwr->c.proxypp)) + { + struct ddsi_reader *rd = ddsi_entidx_lookup_reader_guid (gv->entity_index, &rwn->rd_guid); + if (rd) + pp = rd->c.pp; + } + + struct ddsi_xmsg *msg; + if ((msg = ddsi_xmsg_new (gv->xmsgpool, &rwn->rd_guid, pp, DDSI_ACKNACK_SIZE_MAX, DDSI_XMSG_KIND_CONTROL)) == NULL) + { + // if out of memory, try again later + (void) ddsi_resched_xevent_if_earlier (ev, ddsrt_mtime_add_duration (tnow, old_intv)); + return NULL; + } + + ddsi_xmsg_setdst_pwr (msg, pwr); + struct ddsi_xmsg_marker sm_marker; + ddsi_rtps_acknack_t *an = ddsi_xmsg_append (msg, &sm_marker, DDSI_ACKNACK_SIZE (0)); + ddsi_xmsg_submsg_init (msg, sm_marker, DDSI_RTPS_SMID_ACKNACK); + an->readerId = ddsi_hton_entityid (rwn->rd_guid.entityid); + an->writerId = ddsi_hton_entityid (pwr->e.guid.entityid); + an->readerSNState.bitmap_base = ddsi_to_seqno (1); + an->readerSNState.numbits = 0; + ddsi_count_t * const countp = + (ddsi_count_t *) ((char *) an + offsetof (ddsi_rtps_acknack_t, bits) + DDSI_SEQUENCE_NUMBER_SET_BITS_SIZE (0)); + *countp = 0; + ddsi_xmsg_submsg_setnext (msg, sm_marker); + ddsi_security_encode_datareader_submsg (msg, sm_marker, pwr, &rwn->rd_guid); + + rwn->t_last_ack = tnow; + const dds_duration_t new_intv = preemptive_acknack_interval (rwn); + (void) ddsi_resched_xevent_if_earlier (ev, ddsrt_mtime_add_duration (rwn->t_last_ack, new_intv)); + + // numbits is always 0 here, so need to print the bitmap + ETRACE (pwr, "acknack "PGUIDFMT" -> "PGUIDFMT": #%"PRIu32":%"PRId64"/%"PRIu32":\n", + PGUID (rwn->rd_guid), PGUID (pwr->e.guid), *countp, + ddsi_from_seqno (an->readerSNState.bitmap_base), an->readerSNState.numbits); + return msg; +} + +struct handle_xevk_acknack_arg { + ddsi_guid_t pwr_guid; + ddsi_guid_t rd_guid; +}; + +static void handle_xevk_acknack (struct ddsi_domaingv *gv, struct ddsi_xevent *ev, struct ddsi_xpack *xp, void *varg, ddsrt_mtime_t tnow) +{ + /* FIXME: ought to keep track of which NACKs are being generated in + response to a Heartbeat. There is no point in having multiple + readers NACK the data. + + FIXME: ought to determine the set of missing samples (as it does + now), and then check which for of those fragments are available already. + A little snag is that the defragmenter can throw out partial samples in + favour of others, so MUST ensure that the defragmenter won't start + threshing and fail to make progress! */ + struct handle_xevk_acknack_arg const * const arg = varg; + struct ddsi_proxy_writer *pwr; + struct ddsi_xmsg *msg; + struct ddsi_pwr_rd_match *rwn; + + if ((pwr = ddsi_entidx_lookup_proxy_writer_guid (gv->entity_index, &arg->pwr_guid)) == NULL) + { + return; + } + + ddsrt_mutex_lock (&pwr->e.lock); + if ((rwn = ddsrt_avl_lookup (&ddsi_pwr_readers_treedef, &pwr->readers, &arg->rd_guid)) == NULL) + { + ddsrt_mutex_unlock (&pwr->e.lock); + return; + } + + if (!pwr->have_seen_heartbeat) + msg = make_preemptive_acknack (ev, pwr, rwn, tnow); + else + msg = ddsi_make_and_resched_acknack (ev, pwr, rwn, tnow, false); + ddsrt_mutex_unlock (&pwr->e.lock); + + /* ddsi_xpack_addmsg may sleep (for bandwidth-limited channels), so + must be outside the lock */ + if (msg) + { + // a possible result of trying to encode a submessage is that it is removed, + // in which case we may end up with an empty one. + // FIXME: change ddsi_security_encode_datareader_submsg so that it returns this and make it warn_unused_result + if (ddsi_xmsg_size (msg) == 0) + ddsi_xmsg_free (msg); + else + ddsi_xpack_addmsg (xp, msg, 0); + } +} + void ddsi_proxy_writer_add_connection (struct ddsi_proxy_writer *pwr, struct ddsi_reader *rd, ddsrt_mtime_t tnow, ddsi_count_t init_count, int64_t crypto_handle) { struct ddsi_pwr_rd_match *m = ddsrt_malloc (sizeof (*m)); @@ -1076,7 +1201,10 @@ void ddsi_proxy_writer_add_connection (struct ddsi_proxy_writer *pwr, struct dds } const ddsrt_mtime_t tsched = use_iceoryx ? DDSRT_MTIME_NEVER : ddsrt_mtime_add_duration (tnow, pwr->e.gv->config.preemptive_ack_delay); - m->acknack_xevent = ddsi_qxev_acknack (pwr->evq, tsched, &pwr->e.guid, &rd->e.guid); + { + struct handle_xevk_acknack_arg arg = { .pwr_guid = pwr->e.guid, .rd_guid = rd->e.guid }; + m->acknack_xevent = ddsi_qxev_callback (pwr->evq, tsched, handle_xevk_acknack, &arg, sizeof (arg), false); + } m->u.not_in_sync.reorder = ddsi_reorder_new (&pwr->e.gv->logconfig, DDSI_REORDER_MODE_NORMAL, secondary_reorder_maxsamples, pwr->e.gv->config.late_ack_mode); pwr->n_reliable_readers++; diff --git a/src/core/ddsi/src/ddsi_init.c b/src/core/ddsi/src/ddsi_init.c index c91cd65cfa..74fc8dd5e1 100644 --- a/src/core/ddsi/src/ddsi_init.c +++ b/src/core/ddsi/src/ddsi_init.c @@ -677,26 +677,24 @@ static void ddsi_term_prep (struct ddsi_domaingv *gv) } struct wait_for_receive_threads_helper_arg { - struct ddsi_domaingv *gv; unsigned count; }; -static void wait_for_receive_threads_helper (struct ddsi_xevent *xev, void *varg, ddsrt_mtime_t tnow) +static void wait_for_receive_threads_helper (struct ddsi_domaingv *gv, struct ddsi_xevent *xev, struct ddsi_xpack *xp, void *varg, ddsrt_mtime_t tnow) { struct wait_for_receive_threads_helper_arg * const arg = varg; - if (arg->count++ == arg->gv->config.recv_thread_stop_maxretries) + (void) xp; + if (arg->count++ == gv->config.recv_thread_stop_maxretries) abort (); - ddsi_trigger_recv_threads (arg->gv); + ddsi_trigger_recv_threads (gv); (void) ddsi_resched_xevent_if_earlier (xev, ddsrt_mtime_add_duration (tnow, DDS_SECS (1))); } static void wait_for_receive_threads (struct ddsi_domaingv *gv) { struct ddsi_xevent *trigev; - struct wait_for_receive_threads_helper_arg cbarg; - cbarg.gv = gv; - cbarg.count = 0; - if ((trigev = ddsi_qxev_callback (gv->xevents, ddsrt_mtime_add_duration (ddsrt_time_monotonic (), DDS_SECS (1)), wait_for_receive_threads_helper, &cbarg)) == NULL) + struct wait_for_receive_threads_helper_arg cbarg = { .count = 0 }; + if ((trigev = ddsi_qxev_callback (gv->xevents, ddsrt_mtime_add_duration (ddsrt_time_monotonic (), DDS_SECS (1)), wait_for_receive_threads_helper, &cbarg, sizeof (cbarg), true)) == NULL) { /* retrying is to deal a packet geting lost because the socket buffer is full or because the macOS firewall (and perhaps others) likes to ask if the process is allowed to receive data, @@ -714,7 +712,7 @@ static void wait_for_receive_threads (struct ddsi_domaingv *gv) } if (trigev) { - ddsi_delete_xevent_callback (trigev); + ddsi_delete_xevent (trigev); } } @@ -962,9 +960,8 @@ static uint32_t topic_definition_hash_wrap (const void *tpd) } #endif /* DDS_HAS_TYPE_DISCOVERY */ -static void reset_deaf_mute (struct ddsi_xevent *xev, void *varg, UNUSED_ARG (ddsrt_mtime_t tnow)) +static void reset_deaf_mute (struct ddsi_domaingv *gv, struct ddsi_xevent *xev, UNUSED_ARG (struct ddsi_xpack *xp), UNUSED_ARG (void *varg), UNUSED_ARG (ddsrt_mtime_t tnow)) { - struct ddsi_domaingv *gv = varg; gv->deaf = 0; gv->mute = 0; GVLOGDISC ("DEAFMUTE auto-reset to [deaf, mute]=[%d, %d]\n", gv->deaf, gv->mute); @@ -980,7 +977,7 @@ void ddsi_set_deafmute (struct ddsi_domaingv *gv, bool deaf, bool mute, int64_t { ddsrt_mtime_t when = ddsrt_mtime_add_duration (ddsrt_time_monotonic (), reset_after); GVTRACE (" reset after %"PRId64".%09u ns", reset_after / DDS_NSECS_IN_SEC, (unsigned) (reset_after % DDS_NSECS_IN_SEC)); - ddsi_qxev_callback (gv->xevents, when, reset_deaf_mute, gv); + ddsi_qxev_callback (gv->xevents, when, reset_deaf_mute, NULL, 0, true); } GVLOGDISC ("\n"); } @@ -1605,7 +1602,7 @@ int ddsi_init (struct ddsi_domaingv *gv) gv->user_dqueue = ddsi_dqueue_new ("user", gv, gv->config.delivery_queue_maxsamples, ddsi_user_dqueue_handler, NULL); if (reset_deaf_mute_time.v < DDS_NEVER) - ddsi_qxev_callback (gv->xevents, reset_deaf_mute_time, reset_deaf_mute, gv); + ddsi_qxev_callback (gv->xevents, reset_deaf_mute_time, reset_deaf_mute, NULL, 0, true); return 0; #if 0 diff --git a/src/core/ddsi/src/ddsi_lifespan.c b/src/core/ddsi/src/ddsi_lifespan.c index 3548dd6255..ac0a09d867 100644 --- a/src/core/ddsi/src/ddsi_lifespan.c +++ b/src/core/ddsi/src/ddsi_lifespan.c @@ -25,9 +25,16 @@ static int compare_lifespan_texp (const void *va, const void *vb) const ddsrt_fibheap_def_t lifespan_fhdef = DDSRT_FIBHEAPDEF_INITIALIZER(offsetof (struct ddsi_lifespan_fhnode, heapnode), compare_lifespan_texp); -static void lifespan_rhc_node_exp (struct ddsi_xevent *xev, void *varg, ddsrt_mtime_t tnow) +struct lifespan_rhc_node_exp_arg { + struct ddsi_lifespan_adm *lifespan_adm; +}; + +static void lifespan_rhc_node_exp (struct ddsi_domaingv *gv, struct ddsi_xevent *xev, struct ddsi_xpack *xp, void *varg, ddsrt_mtime_t tnow) { - struct ddsi_lifespan_adm * const lifespan_adm = varg; + struct lifespan_rhc_node_exp_arg * const arg = varg; + struct ddsi_lifespan_adm * const lifespan_adm = arg->lifespan_adm; + (void) gv; + (void) xp; ddsrt_mtime_t next_valid = lifespan_adm->sample_expired_cb((char *)lifespan_adm - lifespan_adm->fh_offset, tnow); ddsi_resched_xevent_if_earlier (xev, next_valid); } @@ -51,7 +58,8 @@ ddsrt_mtime_t ddsi_lifespan_next_expired_locked (const struct ddsi_lifespan_adm void ddsi_lifespan_init (const struct ddsi_domaingv *gv, struct ddsi_lifespan_adm *lifespan_adm, size_t fh_offset, size_t fh_node_offset, ddsi_sample_expired_cb_t sample_expired_cb) { ddsrt_fibheap_init (&lifespan_fhdef, &lifespan_adm->ls_exp_heap); - lifespan_adm->evt = ddsi_qxev_callback (gv->xevents, DDSRT_MTIME_NEVER, lifespan_rhc_node_exp, lifespan_adm); + struct lifespan_rhc_node_exp_arg arg = { .lifespan_adm = lifespan_adm }; + lifespan_adm->evt = ddsi_qxev_callback (gv->xevents, DDSRT_MTIME_NEVER, lifespan_rhc_node_exp, &arg, sizeof (arg), true); lifespan_adm->sample_expired_cb = sample_expired_cb; lifespan_adm->fh_offset = fh_offset; lifespan_adm->fhn_offset = fh_node_offset; @@ -60,7 +68,7 @@ void ddsi_lifespan_init (const struct ddsi_domaingv *gv, struct ddsi_lifespan_ad void ddsi_lifespan_fini (const struct ddsi_lifespan_adm *lifespan_adm) { assert (ddsrt_fibheap_min (&lifespan_fhdef, &lifespan_adm->ls_exp_heap) == NULL); - ddsi_delete_xevent_callback (lifespan_adm->evt); + ddsi_delete_xevent (lifespan_adm->evt); } extern inline void ddsi_lifespan_register_sample_locked (struct ddsi_lifespan_adm *lifespan_adm, struct ddsi_lifespan_fhnode *node); diff --git a/src/core/ddsi/src/ddsi_participant.c b/src/core/ddsi/src/ddsi_participant.c index c475116ba9..70a44f8cf6 100644 --- a/src/core/ddsi/src/ddsi_participant.c +++ b/src/core/ddsi/src/ddsi_participant.c @@ -23,7 +23,7 @@ #include "ddsi__entity_index.h" #include "ddsi__security_omg.h" #include "ddsi__handshake.h" -#include "ddsi__discovery.h" +#include "ddsi__discovery_spdp.h" #include "ddsi__xevent.h" #include "ddsi__lease.h" #include "ddsi__receive.h" @@ -32,6 +32,7 @@ #include "ddsi__endpoint_match.h" #include "ddsi__gc.h" #include "ddsi__plist.h" +#include "ddsi__pmd.h" #include "ddsi__protocol.h" #include "ddsi__tran.h" #include "ddsi__vendor.h" @@ -979,13 +980,14 @@ static dds_return_t new_participant_guid (ddsi_guid_t *ppguid, struct ddsi_domai fire before the calls return. If the initial sample wasn't accepted, all is lost, but we continue nonetheless, even though the participant won't be able to discover or be discovered. */ - pp->spdp_xevent = ddsi_qxev_spdp (gv->xevents, ddsrt_mtime_add_duration (ddsrt_time_monotonic (), DDS_MSECS (100)), &pp->e.guid, NULL); + struct ddsi_xevent_spdp_broadcast_cb_arg arg = { .pp_guid = pp->e.guid }; + pp->spdp_xevent = ddsi_qxev_callback (gv->xevents, ddsrt_mtime_add_duration (ddsrt_time_monotonic (), DDS_MSECS (100)), ddsi_xevent_spdp_broadcast_cb, &arg, sizeof (arg), false); } { - ddsrt_mtime_t tsched; - tsched = (pp->plist->qos.liveliness.lease_duration == DDS_INFINITY) ? DDSRT_MTIME_NEVER : (ddsrt_mtime_t){0}; - pp->pmd_update_xevent = ddsi_qxev_pmd_update (gv->xevents, tsched, &pp->e.guid); + ddsrt_mtime_t tsched = (pp->plist->qos.liveliness.lease_duration == DDS_INFINITY) ? DDSRT_MTIME_NEVER : (ddsrt_mtime_t){0}; + struct ddsi_xevent_write_pmd_message_cb_arg arg = { .pp_guid = pp->e.guid }; + pp->pmd_update_xevent = ddsi_qxev_callback (gv->xevents, tsched, ddsi_xevent_write_pmd_message_cb, &arg, sizeof (arg), false); } #ifdef DDS_HAS_SECURITY diff --git a/src/core/ddsi/src/ddsi_pmd.c b/src/core/ddsi/src/ddsi_pmd.c index 528ca6030f..cc1674e20a 100644 --- a/src/core/ddsi/src/ddsi_pmd.c +++ b/src/core/ddsi/src/ddsi_pmd.c @@ -27,7 +27,7 @@ #include "ddsi__transmit.h" #include "ddsi__xmsg.h" #include "ddsi__proxy_participant.h" -#include "ddsi__sysdeps.h" +#include "ddsi__xevent.h" /* note: treating guid prefix + kind as if it were a GUID because that matches the octet-sequence/sequence-of-uint32 distinction between the specified wire @@ -124,3 +124,39 @@ void ddsi_handle_pmd_message (const struct ddsi_receiver_state *rst, struct ddsi } RSTTRACE ("\n"); } + +void ddsi_xevent_write_pmd_message_cb (struct ddsi_domaingv *gv, struct ddsi_xevent *ev, struct ddsi_xpack *xp, void *varg, ddsrt_mtime_t tnow) +{ + struct ddsi_thread_state * const thrst = ddsi_lookup_thread_state (); + struct ddsi_xevent_write_pmd_message_cb_arg const * const arg = varg; + struct ddsi_participant *pp; + dds_duration_t intv; + ddsrt_mtime_t tnext; + + if ((pp = ddsi_entidx_lookup_participant_guid (gv->entity_index, &arg->pp_guid)) == NULL) + { + return; + } + + ddsi_write_pmd_message (thrst, xp, pp, DDSI_PARTICIPANT_MESSAGE_DATA_KIND_AUTOMATIC_LIVELINESS_UPDATE); + + intv = ddsi_participant_get_pmd_interval (pp); + if (intv == DDS_INFINITY) + { + tnext.v = DDS_NEVER; + GVTRACE ("resched pmd("PGUIDFMT"): never\n", PGUID (pp->e.guid)); + } + else + { + /* schedule next when 80% of the interval has elapsed, or 2s + before the lease ends, whichever comes first */ + if (intv >= DDS_SECS (10)) + tnext.v = tnow.v + intv - DDS_SECS (2); + else + tnext.v = tnow.v + 4 * intv / 5; + GVTRACE ("resched pmd("PGUIDFMT"): %gs\n", PGUID (pp->e.guid), (double)(tnext.v - tnow.v) / 1e9); + } + + (void) ddsi_resched_xevent_if_earlier (ev, tnext); +} + diff --git a/src/core/ddsi/src/ddsi_security_exchange.c b/src/core/ddsi/src/ddsi_security_exchange.c index 2b4a81f9f0..5020d027e3 100644 --- a/src/core/ddsi/src/ddsi_security_exchange.c +++ b/src/core/ddsi/src/ddsi_security_exchange.c @@ -25,7 +25,7 @@ #include "ddsi__security_omg.h" #include "ddsi__handshake.h" #include "ddsi__serdata_pserop.h" -#include "ddsi__discovery.h" +#include "ddsi__discovery_spdp.h" #include "ddsi__xmsg.h" #include "ddsi__transmit.h" #include "ddsi__entity.h" diff --git a/src/core/ddsi/src/ddsi_security_omg.c b/src/core/ddsi/src/ddsi_security_omg.c index 67678e29ef..2a9aee0f9b 100644 --- a/src/core/ddsi/src/ddsi_security_omg.c +++ b/src/core/ddsi/src/ddsi_security_omg.c @@ -334,8 +334,6 @@ static void free_pending_match(struct pending_match *match) } } -static void pending_match_expiry_cb(struct ddsi_xevent *xev, void *varg, ddsrt_mtime_t tnow); - static struct pending_match * find_or_create_pending_entity_match(struct pending_match_index *index, enum ddsi_entity_kind kind, const ddsi_guid_t *remote_guid, const ddsi_guid_t *local_guid, int64_t crypto_handle, DDS_Security_ParticipantCryptoTokenSeq *tokens) { struct guid_pair guids = { .remote_guid = *remote_guid, .local_guid = *local_guid}; @@ -410,9 +408,16 @@ static void delete_pending_match(struct pending_match_index *index, struct pendi ddsrt_mutex_unlock(&index->lock); } -static void pending_match_expiry_cb(struct ddsi_xevent *xev, void *varg, ddsrt_mtime_t tnow) +struct pending_match_expiry_cb_arg { + struct pending_match_index *index; +}; + +static void pending_match_expiry_cb(struct ddsi_domaingv *gv, struct ddsi_xevent *xev, struct ddsi_xpack *xp, void *varg, ddsrt_mtime_t tnow) { - struct pending_match_index *index = varg; + struct pending_match_expiry_cb_arg * const arg = varg; + struct pending_match_index *index = arg->index; + (void) gv; + (void) xp; ddsrt_mutex_lock(&index->lock); struct pending_match *match = ddsrt_fibheap_min(&pending_match_expiry_fhdef, &index->expiry_timers); @@ -475,12 +480,13 @@ static void pending_match_index_init(const struct ddsi_domaingv *gv, struct pend ddsrt_avl_init(&pending_match_index_treedef, &index->pending_matches); ddsrt_fibheap_init(&pending_match_expiry_fhdef, &index->expiry_timers); index->gv = gv; - index->evt = ddsi_qxev_callback(gv->xevents, DDSRT_MTIME_NEVER, pending_match_expiry_cb, index);; + struct pending_match_expiry_cb_arg arg = { .index = index }; + index->evt = ddsi_qxev_callback(gv->xevents, DDSRT_MTIME_NEVER, pending_match_expiry_cb, &arg, sizeof (arg), true); } static void pending_match_index_deinit(struct pending_match_index *index) { - ddsi_delete_xevent_callback(index->evt); + ddsi_delete_xevent(index->evt); ddsrt_mutex_destroy(&index->lock); assert(ddsrt_avl_is_empty(&index->pending_matches)); ddsrt_avl_free(&pending_match_index_treedef, &index->pending_matches, 0); diff --git a/src/core/ddsi/src/ddsi_topic.c b/src/core/ddsi/src/ddsi_topic.c index f544ef4c45..bfdd23cc94 100644 --- a/src/core/ddsi/src/ddsi_topic.c +++ b/src/core/ddsi/src/ddsi_topic.c @@ -23,6 +23,9 @@ #include "ddsi__topic.h" #include "ddsi__entity_index.h" #include "ddsi__discovery.h" +#ifdef DDS_HAS_TOPIC_DISCOVERY +#include "ddsi__discovery_topic.h" +#endif #include "ddsi__xmsg.h" #include "ddsi__misc.h" #include "ddsi__gc.h" diff --git a/src/core/ddsi/src/ddsi_transmit.c b/src/core/ddsi/src/ddsi_transmit.c index 6e1cd21dd1..e23726b0f9 100644 --- a/src/core/ddsi/src/ddsi_transmit.c +++ b/src/core/ddsi/src/ddsi_transmit.c @@ -1361,3 +1361,12 @@ int ddsi_write_sample_nogc_notk (struct ddsi_thread_state * const thrst, struct ddsi_tkmap_instance_unref (wr->e.gv->m_tkmap, tk); return res; } + +int ddsi_write_and_fini_plist (struct ddsi_writer *wr, ddsi_plist_t *ps, bool alive) +{ + struct ddsi_serdata *serdata = ddsi_serdata_from_sample (wr->type, alive ? SDK_DATA : SDK_KEY, ps); + ddsi_plist_fini (ps); + serdata->statusinfo = alive ? 0 : (DDSI_STATUSINFO_DISPOSE | DDSI_STATUSINFO_UNREGISTER); + serdata->timestamp = ddsrt_time_wallclock (); + return ddsi_write_sample_nogc_notk (ddsi_lookup_thread_state (), NULL, wr, serdata); +} diff --git a/src/core/ddsi/src/ddsi_xevent.c b/src/core/ddsi/src/ddsi_xevent.c index f9c9f33244..994e3490ae 100644 --- a/src/core/ddsi/src/ddsi_xevent.c +++ b/src/core/ddsi/src/ddsi_xevent.c @@ -10,43 +10,24 @@ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ #include +#include #include #include "dds/ddsrt/atomics.h" #include "dds/ddsrt/heap.h" #include "dds/ddsrt/sync.h" -#include "dds/ddsrt/avl.h" #include "dds/ddsrt/fibheap.h" #include "dds/ddsi/ddsi_unused.h" #include "dds/ddsi/ddsi_domaingv.h" -#include "dds/ddsi/ddsi_serdata.h" -#include "dds/ddsi/ddsi_tkmap.h" #include "ddsi__log.h" -#include "ddsi__addrset.h" -#include "ddsi__xmsg.h" #include "ddsi__xevent.h" #include "ddsi__thread.h" -#include "ddsi__entity_index.h" #include "ddsi__transmit.h" #include "ddsi__entity.h" -#include "ddsi__participant.h" -#include "ddsi__misc.h" -#include "ddsi__radmin.h" -#include "ddsi__bitset.h" -#include "ddsi__lease.h" #include "ddsi__xmsg.h" -#include "ddsi__security_omg.h" -#include "ddsi__pmd.h" -#include "ddsi__acknack.h" -#include "ddsi__endpoint.h" -#include "ddsi__endpoint_match.h" -#include "ddsi__plist.h" #include "ddsi__proxy_endpoint.h" #include "ddsi__tran.h" -#include "ddsi__hbcontrol.h" #include "ddsi__sysdeps.h" -#include "dds__whc.h" - #define EVQTRACE(...) DDS_CTRACE (&evq->gv->logconfig, __VA_ARGS__) @@ -54,14 +35,10 @@ != 0 -- and note that it had better be 2's complement machine! */ #define TSCHED_DELETE ((int64_t) ((uint64_t) 1 << 63)) -enum ddsi_xeventkind -{ - XEVK_HEARTBEAT, - XEVK_ACKNACK, - XEVK_SPDP, - XEVK_PMD_UPDATE, - XEVK_DELETE_WRITER, - XEVK_CALLBACK +enum cb_sync_state { + CSS_DONTCARE, + CSS_SCHEDULED, + CSS_EXECUTING }; struct ddsi_xevent @@ -69,36 +46,16 @@ struct ddsi_xevent ddsrt_fibheap_node_t heapnode; struct ddsi_xeventq *evq; ddsrt_mtime_t tsched; - enum ddsi_xeventkind kind; + + enum cb_sync_state sync_state; union { - struct { - ddsi_guid_t wr_guid; - } heartbeat; - struct { - ddsi_guid_t pwr_guid; - ddsi_guid_t rd_guid; - } acknack; - struct { - ddsi_guid_t pp_guid; - ddsi_guid_prefix_t dest_proxypp_guid_prefix; /* only if "directed" */ - int directed; /* if 0, undirected; if > 0, number of directed ones to send in reasonably short succession */ - } spdp; - struct { - ddsi_guid_t pp_guid; - } pmd_update; -#if 0 - struct { - } info; -#endif - struct { - ddsi_guid_t guid; - } delete_writer; - struct { - void (*cb) (struct ddsi_xevent *ev, void *arg, ddsrt_mtime_t tnow); - void *arg; - bool executing; - } callback; - } u; + ddsi_xevent_cb_t cb; + // ensure alignment of arg is good + void *p; + uint64_t u64; + double d; + } cb; + char arg[]; }; enum ddsi_xeventkind_nt @@ -319,27 +276,14 @@ static int nontimed_xevent_in_queue (struct ddsi_xeventq *evq, struct ddsi_xeven static void free_xevent (struct ddsi_xeventq *evq, struct ddsi_xevent *ev) { (void) evq; - if (ev->tsched.v != TSCHED_DELETE) - { - switch (ev->kind) - { - case XEVK_HEARTBEAT: - case XEVK_ACKNACK: - case XEVK_SPDP: - case XEVK_PMD_UPDATE: - case XEVK_DELETE_WRITER: - case XEVK_CALLBACK: - break; - } - } ddsrt_free (ev); } -void ddsi_delete_xevent (struct ddsi_xevent *ev) +static void ddsi_delete_xevent_nosync (struct ddsi_xevent *ev) { struct ddsi_xeventq *evq = ev->evq; ddsrt_mutex_lock (&evq->lock); - assert (ev->kind != XEVK_CALLBACK || ev->u.callback.executing); + assert (ev->sync_state != CSS_EXECUTING); /* Can delete it only once, no matter how we implement it internally */ assert (ev->tsched.v != TSCHED_DELETE); assert (TSCHED_DELETE < ev->tsched.v); @@ -359,13 +303,12 @@ void ddsi_delete_xevent (struct ddsi_xevent *ev) ddsrt_mutex_unlock (&evq->lock); } -void ddsi_delete_xevent_callback (struct ddsi_xevent *ev) +static void ddsi_delete_xevent_sync (struct ddsi_xevent *ev) { struct ddsi_xeventq *evq = ev->evq; - assert (ev->kind == XEVK_CALLBACK); ddsrt_mutex_lock (&evq->lock); /* wait until neither scheduled nor executing; loop in case the callback reschedules the event */ - while (ev->tsched.v != DDS_NEVER || ev->u.callback.executing) + while (ev->tsched.v != DDS_NEVER || ev->sync_state == CSS_EXECUTING) { if (ev->tsched.v != DDS_NEVER) { @@ -373,7 +316,7 @@ void ddsi_delete_xevent_callback (struct ddsi_xevent *ev) ddsrt_fibheap_delete (&evq_xevents_fhdef, &evq->xevents, ev); ev->tsched.v = DDS_NEVER; } - if (ev->u.callback.executing) + if (ev->sync_state == CSS_EXECUTING) { ddsrt_cond_wait (&evq->cond, &evq->lock); } @@ -382,6 +325,14 @@ void ddsi_delete_xevent_callback (struct ddsi_xevent *ev) free_xevent (evq, ev); } +void ddsi_delete_xevent (struct ddsi_xevent *ev) +{ + if (ev->sync_state == CSS_DONTCARE) + ddsi_delete_xevent_nosync (ev); + else + ddsi_delete_xevent_sync (ev); +} + int ddsi_resched_xevent_if_earlier (struct ddsi_xevent *ev, ddsrt_mtime_t tsched) { struct ddsi_xeventq *evq = ev->evq; @@ -417,6 +368,17 @@ int ddsi_resched_xevent_if_earlier (struct ddsi_xevent *ev, ddsrt_mtime_t tsched return is_resched; } +#ifndef NDEBUG +bool ddsi_delete_xevent_pending (struct ddsi_xevent *ev) +{ + struct ddsi_xeventq *evq = ev->evq; + ddsrt_mutex_lock (&evq->lock); + const bool is_pending = (ev->tsched.v == TSCHED_DELETE); + ddsrt_mutex_unlock (&evq->lock); + return is_pending; +} +#endif + static ddsrt_mtime_t mtime_round_up (ddsrt_mtime_t t, int64_t round) { /* This function rounds up t to the nearest next multiple of round. @@ -435,29 +397,6 @@ static ddsrt_mtime_t mtime_round_up (ddsrt_mtime_t t, int64_t round) } } -static struct ddsi_xevent *qxev_common (struct ddsi_xeventq *evq, ddsrt_mtime_t tsched, enum ddsi_xeventkind kind) -{ - /* qxev_common is the route by which all timed xevents are - created. */ - struct ddsi_xevent *ev = ddsrt_malloc (sizeof (*ev)); - - assert (tsched.v != TSCHED_DELETE); - ASSERT_MUTEX_HELD (&evq->lock); - - /* round up the scheduled time if required */ - if (tsched.v != DDS_NEVER && evq->gv->config.schedule_time_rounding != 0) - { - ddsrt_mtime_t tsched_rounded = mtime_round_up (tsched, evq->gv->config.schedule_time_rounding); - EVQTRACE ("rounded event scheduled for %"PRId64" to %"PRId64"\n", tsched.v, tsched_rounded.v); - tsched = tsched_rounded; - } - - ev->evq = evq; - ev->tsched = tsched; - ev->kind = kind; - return ev; -} - static struct ddsi_xevent_nt *qxev_common_nt (struct ddsi_xeventq *evq, enum ddsi_xeventkind_nt kind) { /* qxev_common_nt is the route by which all non-timed xevents are created. */ @@ -618,541 +557,6 @@ static void handle_xevk_entityid (struct ddsi_xpack *xp, struct ddsi_xevent_nt * ddsi_xpack_addmsg (xp, ev->u.entityid.msg, 0); } -#ifdef DDS_HAS_SECURITY -static int send_heartbeat_to_all_readers_check_and_sched (struct ddsi_xevent *ev, struct ddsi_writer *wr, const struct ddsi_whc_state *whcst, ddsrt_mtime_t tnow, ddsrt_mtime_t *t_next) -{ - int send; - if (!ddsi_writer_must_have_hb_scheduled (wr, whcst)) - { - wr->hbcontrol.tsched = DDSRT_MTIME_NEVER; - send = -1; - } - else if (!ddsi_writer_hbcontrol_must_send (wr, whcst, tnow)) - { - wr->hbcontrol.tsched = ddsrt_mtime_add_duration (tnow, ddsi_writer_hbcontrol_intv (wr, whcst, tnow)); - send = -1; - } - else - { - const int hbansreq = ddsi_writer_hbcontrol_ack_required (wr, whcst, tnow); - wr->hbcontrol.tsched = ddsrt_mtime_add_duration (tnow, ddsi_writer_hbcontrol_intv (wr, whcst, tnow)); - send = hbansreq; - } - - ddsi_resched_xevent_if_earlier (ev, wr->hbcontrol.tsched); - *t_next = wr->hbcontrol.tsched; - return send; -} - -static void send_heartbeat_to_all_readers (struct ddsi_xpack *xp, struct ddsi_xevent *ev, struct ddsi_writer *wr, ddsrt_mtime_t tnow) -{ - struct ddsi_whc_state whcst; - ddsrt_mtime_t t_next; - unsigned count = 0; - - ddsrt_mutex_lock (&wr->e.lock); - - ddsi_whc_get_state(wr->whc, &whcst); - const int hbansreq = send_heartbeat_to_all_readers_check_and_sched (ev, wr, &whcst, tnow, &t_next); - if (hbansreq >= 0) - { - struct ddsi_wr_prd_match *m; - struct ddsi_guid last_guid = { .prefix = {.u = {0,0,0}}, .entityid = {0} }; - - while ((m = ddsrt_avl_lookup_succ (&ddsi_wr_readers_treedef, &wr->readers, &last_guid)) != NULL) - { - last_guid = m->prd_guid; - if (m->seq < m->last_seq) - { - struct ddsi_proxy_reader *prd; - - prd = ddsi_entidx_lookup_proxy_reader_guid (wr->e.gv->entity_index, &m->prd_guid); - if (prd) - { - ETRACE (wr, " heartbeat(wr "PGUIDFMT" rd "PGUIDFMT" %s) send, resched in %g s (min-ack %"PRIu64", avail-seq %"PRIu64")\n", - PGUID (wr->e.guid), - PGUID (m->prd_guid), - hbansreq ? "" : " final", - (double)(t_next.v - tnow.v) / 1e9, - m->seq, - m->last_seq); - - struct ddsi_xmsg *msg = ddsi_writer_hbcontrol_p2p(wr, &whcst, hbansreq, prd); - if (msg != NULL) - { - ddsrt_mutex_unlock (&wr->e.lock); - ddsi_xpack_addmsg (xp, msg, 0); - ddsrt_mutex_lock (&wr->e.lock); - } - count++; - } - } - } - } - - if (count == 0) - { - if (ddsrt_avl_is_empty (&wr->readers)) - { - ETRACE (wr, "heartbeat(wr "PGUIDFMT") suppressed, resched in %g s (min-ack [none], avail-seq %"PRIu64", xmit %"PRIu64")\n", - PGUID (wr->e.guid), - (t_next.v == DDS_NEVER) ? INFINITY : (double)(t_next.v - tnow.v) / 1e9, - whcst.max_seq, - ddsi_writer_read_seq_xmit(wr)); - } - else - { - ETRACE (wr, "heartbeat(wr "PGUIDFMT") suppressed, resched in %g s (min-ack %"PRIu64"%s, avail-seq %"PRIu64", xmit %"PRIu64")\n", - PGUID (wr->e.guid), - (t_next.v == DDS_NEVER) ? INFINITY : (double)(t_next.v - tnow.v) / 1e9, - ((struct ddsi_wr_prd_match *) ddsrt_avl_root (&ddsi_wr_readers_treedef, &wr->readers))->min_seq, - ((struct ddsi_wr_prd_match *) ddsrt_avl_root (&ddsi_wr_readers_treedef, &wr->readers))->all_have_replied_to_hb ? "" : "!", - whcst.max_seq, - ddsi_writer_read_seq_xmit(wr)); - } - } - - ddsrt_mutex_unlock (&wr->e.lock); -} -#endif - -static void handle_xevk_heartbeat (struct ddsi_xpack *xp, struct ddsi_xevent *ev, ddsrt_mtime_t tnow) -{ - struct ddsi_domaingv const * const gv = ev->evq->gv; - struct ddsi_xmsg *msg; - struct ddsi_writer *wr; - ddsrt_mtime_t t_next; - int hbansreq = 0; - struct ddsi_whc_state whcst; - - if ((wr = ddsi_entidx_lookup_writer_guid (gv->entity_index, &ev->u.heartbeat.wr_guid)) == NULL) - { - GVTRACE("heartbeat(wr "PGUIDFMT") writer gone\n", PGUID (ev->u.heartbeat.wr_guid)); - return; - } - -#ifdef DDS_HAS_SECURITY - if (wr->e.guid.entityid.u == DDSI_ENTITYID_P2P_BUILTIN_PARTICIPANT_VOLATILE_SECURE_WRITER) - { - send_heartbeat_to_all_readers(xp, ev, wr, tnow); - return; - } -#endif - - ddsrt_mutex_lock (&wr->e.lock); - assert (wr->reliable); - ddsi_whc_get_state(wr->whc, &whcst); - if (!ddsi_writer_must_have_hb_scheduled (wr, &whcst)) - { - hbansreq = 1; /* just for trace */ - msg = NULL; /* Need not send it now, and no need to schedule it for the future */ - t_next.v = DDS_NEVER; - } - else if (!ddsi_writer_hbcontrol_must_send (wr, &whcst, tnow)) - { - hbansreq = 1; /* just for trace */ - msg = NULL; - t_next.v = tnow.v + ddsi_writer_hbcontrol_intv (wr, &whcst, tnow); - } - else - { - hbansreq = ddsi_writer_hbcontrol_ack_required (wr, &whcst, tnow); - msg = ddsi_writer_hbcontrol_create_heartbeat (wr, &whcst, tnow, hbansreq, 0); - t_next.v = tnow.v + ddsi_writer_hbcontrol_intv (wr, &whcst, tnow); - } - - if (ddsrt_avl_is_empty (&wr->readers)) - { - GVTRACE ("heartbeat(wr "PGUIDFMT"%s) %s, resched in %g s (min-ack [none], avail-seq %"PRIu64", xmit %"PRIu64")\n", - PGUID (wr->e.guid), - hbansreq ? "" : " final", - msg ? "sent" : "suppressed", - (t_next.v == DDS_NEVER) ? INFINITY : (double)(t_next.v - tnow.v) / 1e9, - whcst.max_seq, ddsi_writer_read_seq_xmit (wr)); - } - else - { - GVTRACE ("heartbeat(wr "PGUIDFMT"%s) %s, resched in %g s (min-ack %"PRId64"%s, avail-seq %"PRIu64", xmit %"PRIu64")\n", - PGUID (wr->e.guid), - hbansreq ? "" : " final", - msg ? "sent" : "suppressed", - (t_next.v == DDS_NEVER) ? INFINITY : (double)(t_next.v - tnow.v) / 1e9, - ((struct ddsi_wr_prd_match *) ddsrt_avl_root_non_empty (&ddsi_wr_readers_treedef, &wr->readers))->min_seq, - ((struct ddsi_wr_prd_match *) ddsrt_avl_root_non_empty (&ddsi_wr_readers_treedef, &wr->readers))->all_have_replied_to_hb ? "" : "!", - whcst.max_seq, ddsi_writer_read_seq_xmit (wr)); - } - (void) ddsi_resched_xevent_if_earlier (ev, t_next); - wr->hbcontrol.tsched = t_next; - ddsrt_mutex_unlock (&wr->e.lock); - - /* Can't transmit synchronously with writer lock held: trying to add - the heartbeat to the xp may cause xp to be sent out, which may - require updating wr->seq_xmit for other messages already in xp. - Besides, ddsi_xpack_addmsg may sleep for bandwidth-limited channels - and we certainly don't want to hold the lock during that time. */ - if (msg) - { - if (!wr->test_suppress_heartbeat) - ddsi_xpack_addmsg (xp, msg, 0); - else - { - GVTRACE ("test_suppress_heartbeat\n"); - ddsi_xmsg_free (msg); - } - } -} - -static dds_duration_t preemptive_acknack_interval (const struct ddsi_pwr_rd_match *rwn) -{ - if (rwn->t_last_ack.v < rwn->tcreate.v) - return 0; - else - { - const dds_duration_t age = rwn->t_last_ack.v - rwn->tcreate.v; - if (age <= DDS_SECS (10)) - return DDS_SECS (1); - else if (age <= DDS_SECS (60)) - return DDS_SECS (2); - else if (age <= DDS_SECS (120)) - return DDS_SECS (5); - else - return DDS_SECS (10); - } -} - -static struct ddsi_xmsg *make_preemptive_acknack (struct ddsi_xevent *ev, struct ddsi_proxy_writer *pwr, struct ddsi_pwr_rd_match *rwn, ddsrt_mtime_t tnow) -{ - const dds_duration_t old_intv = preemptive_acknack_interval (rwn); - if (tnow.v < ddsrt_mtime_add_duration (rwn->t_last_ack, old_intv).v) - { - (void) ddsi_resched_xevent_if_earlier (ev, ddsrt_mtime_add_duration (rwn->t_last_ack, old_intv)); - return NULL; - } - - struct ddsi_domaingv * const gv = pwr->e.gv; - struct ddsi_participant *pp = NULL; - if (ddsi_omg_proxy_participant_is_secure (pwr->c.proxypp)) - { - struct ddsi_reader *rd = ddsi_entidx_lookup_reader_guid (gv->entity_index, &rwn->rd_guid); - if (rd) - pp = rd->c.pp; - } - - struct ddsi_xmsg *msg; - if ((msg = ddsi_xmsg_new (gv->xmsgpool, &rwn->rd_guid, pp, DDSI_ACKNACK_SIZE_MAX, DDSI_XMSG_KIND_CONTROL)) == NULL) - { - // if out of memory, try again later - (void) ddsi_resched_xevent_if_earlier (ev, ddsrt_mtime_add_duration (tnow, old_intv)); - return NULL; - } - - ddsi_xmsg_setdst_pwr (msg, pwr); - struct ddsi_xmsg_marker sm_marker; - ddsi_rtps_acknack_t *an = ddsi_xmsg_append (msg, &sm_marker, DDSI_ACKNACK_SIZE (0)); - ddsi_xmsg_submsg_init (msg, sm_marker, DDSI_RTPS_SMID_ACKNACK); - an->readerId = ddsi_hton_entityid (rwn->rd_guid.entityid); - an->writerId = ddsi_hton_entityid (pwr->e.guid.entityid); - an->readerSNState.bitmap_base = ddsi_to_seqno (1); - an->readerSNState.numbits = 0; - ddsi_count_t * const countp = - (ddsi_count_t *) ((char *) an + offsetof (ddsi_rtps_acknack_t, bits) + DDSI_SEQUENCE_NUMBER_SET_BITS_SIZE (0)); - *countp = 0; - ddsi_xmsg_submsg_setnext (msg, sm_marker); - ddsi_security_encode_datareader_submsg (msg, sm_marker, pwr, &rwn->rd_guid); - - rwn->t_last_ack = tnow; - const dds_duration_t new_intv = preemptive_acknack_interval (rwn); - (void) ddsi_resched_xevent_if_earlier (ev, ddsrt_mtime_add_duration (rwn->t_last_ack, new_intv)); - - // numbits is always 0 here, so need to print the bitmap - ETRACE (pwr, "acknack "PGUIDFMT" -> "PGUIDFMT": #%"PRIu32":%"PRId64"/%"PRIu32":\n", - PGUID (rwn->rd_guid), PGUID (pwr->e.guid), *countp, - ddsi_from_seqno (an->readerSNState.bitmap_base), an->readerSNState.numbits); - return msg; -} - -static void handle_xevk_acknack (struct ddsi_xpack *xp, struct ddsi_xevent *ev, ddsrt_mtime_t tnow) -{ - /* FIXME: ought to keep track of which NACKs are being generated in - response to a Heartbeat. There is no point in having multiple - readers NACK the data. - - FIXME: ought to determine the set of missing samples (as it does - now), and then check which for of those fragments are available already. - A little snag is that the defragmenter can throw out partial samples in - favour of others, so MUST ensure that the defragmenter won't start - threshing and fail to make progress! */ - struct ddsi_domaingv *gv = ev->evq->gv; - struct ddsi_proxy_writer *pwr; - struct ddsi_xmsg *msg; - struct ddsi_pwr_rd_match *rwn; - - if ((pwr = ddsi_entidx_lookup_proxy_writer_guid (gv->entity_index, &ev->u.acknack.pwr_guid)) == NULL) - { - return; - } - - ddsrt_mutex_lock (&pwr->e.lock); - if ((rwn = ddsrt_avl_lookup (&ddsi_pwr_readers_treedef, &pwr->readers, &ev->u.acknack.rd_guid)) == NULL) - { - ddsrt_mutex_unlock (&pwr->e.lock); - return; - } - - if (!pwr->have_seen_heartbeat) - msg = make_preemptive_acknack (ev, pwr, rwn, tnow); - else - msg = ddsi_make_and_resched_acknack (ev, pwr, rwn, tnow, false); - ddsrt_mutex_unlock (&pwr->e.lock); - - /* ddsi_xpack_addmsg may sleep (for bandwidth-limited channels), so - must be outside the lock */ - if (msg) - { - // a possible result of trying to encode a submessage is that it is removed, - // in which case we may end up with an empty one. - // FIXME: change ddsi_security_encode_datareader_submsg so that it returns this and make it warn_unused_result - if (ddsi_xmsg_size (msg) == 0) - ddsi_xmsg_free (msg); - else - ddsi_xpack_addmsg (xp, msg, 0); - } -} - -static bool resend_spdp_sample_by_guid_key (struct ddsi_writer *wr, const ddsi_guid_t *guid, struct ddsi_proxy_reader *prd) -{ - /* Look up data in (transient-local) WHC by key value -- FIXME: clearly - a slightly more efficient and elegant way of looking up the key value - is to be preferred */ - struct ddsi_domaingv *gv = wr->e.gv; - bool sample_found; - ddsi_plist_t ps; - ddsi_plist_init_empty (&ps); - ps.present |= PP_PARTICIPANT_GUID; - ps.participant_guid = *guid; - struct ddsi_serdata *sd = ddsi_serdata_from_sample (gv->spdp_type, SDK_KEY, &ps); - ddsi_plist_fini (&ps); - struct ddsi_whc_borrowed_sample sample; - - ddsrt_mutex_lock (&wr->e.lock); - sample_found = ddsi_whc_borrow_sample_key (wr->whc, sd, &sample); - if (sample_found) - { - /* Claiming it is new rather than a retransmit so that the rexmit - limiting won't kick in. It is best-effort and therefore the - updating of the last transmitted sequence number won't take - place anyway. Nor is it necessary to fiddle with heartbeat - control stuff. */ - ddsi_enqueue_spdp_sample_wrlock_held (wr, sample.seq, sample.serdata, prd); - ddsi_whc_return_sample(wr->whc, &sample, false); - } - ddsrt_mutex_unlock (&wr->e.lock); - ddsi_serdata_unref (sd); - return sample_found; -} - -static void handle_xevk_spdp (UNUSED_ARG (struct ddsi_xpack *xp), struct ddsi_xevent *ev, ddsrt_mtime_t tnow) -{ - /* Like the writer pointer in the heartbeat event, the participant pointer in the spdp event is assumed valid. */ - struct ddsi_domaingv *gv = ev->evq->gv; - struct ddsi_participant *pp; - struct ddsi_proxy_reader *prd; - struct ddsi_writer *spdp_wr; - bool do_write; - - if ((pp = ddsi_entidx_lookup_participant_guid (gv->entity_index, &ev->u.spdp.pp_guid)) == NULL) - { - GVTRACE ("handle_xevk_spdp "PGUIDFMT" - unknown guid\n", PGUID (ev->u.spdp.pp_guid)); - if (ev->u.spdp.directed) - ddsi_delete_xevent (ev); - return; - } - - if ((spdp_wr = ddsi_get_builtin_writer (pp, DDSI_ENTITYID_SPDP_BUILTIN_PARTICIPANT_WRITER)) == NULL) - { - GVTRACE ("handle_xevk_spdp "PGUIDFMT" - spdp writer of participant not found\n", PGUID (ev->u.spdp.pp_guid)); - if (ev->u.spdp.directed) - ddsi_delete_xevent (ev); - return; - } - - if (!ev->u.spdp.directed) - { - /* memset is for tracing output */ - memset (&ev->u.spdp.dest_proxypp_guid_prefix, 0, sizeof (ev->u.spdp.dest_proxypp_guid_prefix)); - prd = NULL; - do_write = true; - } - else - { - ddsi_guid_t guid; - guid.prefix = ev->u.spdp.dest_proxypp_guid_prefix; - guid.entityid.u = DDSI_ENTITYID_SPDP_BUILTIN_PARTICIPANT_READER; - prd = ddsi_entidx_lookup_proxy_reader_guid (gv->entity_index, &guid); - do_write = (prd != NULL); - if (!do_write) - GVTRACE ("xmit spdp: no proxy reader "PGUIDFMT"\n", PGUID (guid)); - } - - if (do_write && !resend_spdp_sample_by_guid_key (spdp_wr, &ev->u.spdp.pp_guid, prd)) - { -#ifndef NDEBUG - /* If undirected, it is pp->spdp_xevent, and that one must never - run into an empty WHC unless it is already marked for deletion. - - If directed, it may happen in response to an SPDP packet during - creation of the participant. This is because pp is inserted in - the hash table quite early on, which, in turn, is because it - needs to be visible for creating its builtin endpoints. But in - this case, the initial broadcast of the SPDP packet of pp will - happen shortly. */ - if (!ev->u.spdp.directed) - { - ddsrt_mutex_lock (&pp->e.lock); - ddsrt_mutex_lock (&ev->evq->lock); - assert (ev->tsched.v == TSCHED_DELETE); - ddsrt_mutex_unlock (&ev->evq->lock); - ddsrt_mutex_unlock (&pp->e.lock); - } - else - { - GVTRACE ("xmit spdp: suppressing early spdp response from "PGUIDFMT" to %"PRIx32":%"PRIx32":%"PRIx32":%x\n", - PGUID (pp->e.guid), PGUIDPREFIX (ev->u.spdp.dest_proxypp_guid_prefix), DDSI_ENTITYID_PARTICIPANT); - } -#endif - } - - if (ev->u.spdp.directed) - { - /* Directed events are used to send SPDP packets to newly - discovered peers, and used just once. */ - if (--ev->u.spdp.directed == 0 || gv->config.spdp_interval < DDS_SECS (1) || pp->plist->qos.liveliness.lease_duration < DDS_SECS (1)) - ddsi_delete_xevent (ev); - else - { - ddsrt_mtime_t tnext = ddsrt_mtime_add_duration (tnow, DDS_SECS (1)); - GVTRACE ("xmit spdp "PGUIDFMT" to %"PRIx32":%"PRIx32":%"PRIx32":%x (resched %gs)\n", - PGUID (pp->e.guid), - PGUIDPREFIX (ev->u.spdp.dest_proxypp_guid_prefix), DDSI_ENTITYID_SPDP_BUILTIN_PARTICIPANT_READER, - (double)(tnext.v - tnow.v) / 1e9); - (void) ddsi_resched_xevent_if_earlier (ev, tnext); - } - } - else - { - /* schedule next when 80% of the interval has elapsed, or 2s - before the lease ends, whichever comes first (similar to PMD), - but never wait longer than spdp_interval */ - const dds_duration_t mindelta = DDS_MSECS (10); - const dds_duration_t ldur = pp->plist->qos.liveliness.lease_duration; - ddsrt_mtime_t tnext; - int64_t intv; - - if (ldur < 5 * mindelta / 4) - intv = mindelta; - else if (ldur < DDS_SECS (10)) - intv = 4 * ldur / 5; - else - intv = ldur - DDS_SECS (2); - if (intv > gv->config.spdp_interval) - intv = gv->config.spdp_interval; - - tnext = ddsrt_mtime_add_duration (tnow, intv); - GVTRACE ("xmit spdp "PGUIDFMT" to %"PRIx32":%"PRIx32":%"PRIx32":%x (resched %gs)\n", - PGUID (pp->e.guid), - PGUIDPREFIX (ev->u.spdp.dest_proxypp_guid_prefix), DDSI_ENTITYID_SPDP_BUILTIN_PARTICIPANT_READER, - (double)(tnext.v - tnow.v) / 1e9); - (void) ddsi_resched_xevent_if_earlier (ev, tnext); - } -} - -static void handle_xevk_pmd_update (struct ddsi_thread_state * const thrst, struct ddsi_xpack *xp, struct ddsi_xevent *ev, ddsrt_mtime_t tnow) -{ - struct ddsi_domaingv * const gv = ev->evq->gv; - struct ddsi_participant *pp; - dds_duration_t intv; - ddsrt_mtime_t tnext; - - if ((pp = ddsi_entidx_lookup_participant_guid (gv->entity_index, &ev->u.pmd_update.pp_guid)) == NULL) - { - return; - } - - ddsi_write_pmd_message (thrst, xp, pp, DDSI_PARTICIPANT_MESSAGE_DATA_KIND_AUTOMATIC_LIVELINESS_UPDATE); - - intv = ddsi_participant_get_pmd_interval (pp); - if (intv == DDS_INFINITY) - { - tnext.v = DDS_NEVER; - GVTRACE ("resched pmd("PGUIDFMT"): never\n", PGUID (pp->e.guid)); - } - else - { - /* schedule next when 80% of the interval has elapsed, or 2s - before the lease ends, whichever comes first */ - if (intv >= DDS_SECS (10)) - tnext.v = tnow.v + intv - DDS_SECS (2); - else - tnext.v = tnow.v + 4 * intv / 5; - GVTRACE ("resched pmd("PGUIDFMT"): %gs\n", PGUID (pp->e.guid), (double)(tnext.v - tnow.v) / 1e9); - } - - (void) ddsi_resched_xevent_if_earlier (ev, tnext); -} - -static void handle_xevk_delete_writer (UNUSED_ARG (struct ddsi_xpack *xp), struct ddsi_xevent *ev, UNUSED_ARG (ddsrt_mtime_t tnow)) -{ - /* don't worry if the writer is already gone by the time we get here, delete_writer_nolinger checks for that. */ - struct ddsi_domaingv * const gv = ev->evq->gv; - GVTRACE ("handle_xevk_delete_writer: "PGUIDFMT"\n", PGUID (ev->u.delete_writer.guid)); - ddsi_delete_writer_nolinger (gv, &ev->u.delete_writer.guid); - ddsi_delete_xevent (ev); -} - -static void handle_individual_xevent (struct ddsi_thread_state * const thrst, struct ddsi_xevent *xev, struct ddsi_xpack *xp, ddsrt_mtime_t tnow) -{ - struct ddsi_xeventq *xevq = xev->evq; - /* We relinquish the lock while processing the event, but require it - held for administrative work. */ - ASSERT_MUTEX_HELD (&xevq->lock); - if (xev->kind == XEVK_CALLBACK) - { - xev->u.callback.executing = true; - ddsrt_mutex_unlock (&xevq->lock); - xev->u.callback.cb (xev, xev->u.callback.arg, tnow); - ddsrt_mutex_lock (&xevq->lock); - xev->u.callback.executing = false; - ddsrt_cond_broadcast (&xevq->cond); - } - else - { - ddsrt_mutex_unlock (&xevq->lock); - switch (xev->kind) - { - case XEVK_HEARTBEAT: - handle_xevk_heartbeat (xp, xev, tnow); - break; - case XEVK_ACKNACK: - handle_xevk_acknack (xp, xev, tnow); - break; - case XEVK_SPDP: - handle_xevk_spdp (xp, xev, tnow); - break; - case XEVK_PMD_UPDATE: - handle_xevk_pmd_update (thrst, xp, xev, tnow); - break; - case XEVK_DELETE_WRITER: - handle_xevk_delete_writer (xp, xev, tnow); - break; - case XEVK_CALLBACK: - assert (0); - break; - } - ddsrt_mutex_lock (&xevq->lock); - } - ASSERT_MUTEX_HELD (&xevq->lock); -} - static void handle_individual_xevent_nt (struct ddsi_xevent_nt *xev, struct ddsi_xpack *xp) { switch (xev->kind) @@ -1174,14 +578,6 @@ static void handle_individual_xevent_nt (struct ddsi_xevent_nt *xev, struct ddsi ddsrt_free (xev); } -static void handle_timed_xevent (struct ddsi_thread_state * const thrst, struct ddsi_xevent *xev, struct ddsi_xpack *xp, ddsrt_mtime_t tnow /* monotonic */) -{ - /* This function handles the individual xevent irrespective of - whether it is a "timed" or "non-timed" xevent */ - assert (xev->tsched.v != TSCHED_DELETE); - handle_individual_xevent (thrst, xev, xp, tnow /* monotonic */); -} - static void handle_nontimed_xevent (struct ddsi_xevent_nt *xev, struct ddsi_xpack *xp) { /* This function handles the individual xevent irrespective of @@ -1202,7 +598,7 @@ static void handle_nontimed_xevent (struct ddsi_xevent_nt *xev, struct ddsi_xpac ASSERT_MUTEX_HELD (&xevq->lock); } -static void handle_xevents (struct ddsi_thread_state * const thrst, struct ddsi_xeventq *xevq, struct ddsi_xpack *xp, ddsrt_mtime_t tnow /* monotonic */) +static void handle_xevents (struct ddsi_thread_state * const thrst, struct ddsi_xeventq *xevq, struct ddsi_xpack *xp, ddsrt_mtime_t tnow) { int xeventsToProcess = 1; @@ -1234,7 +630,23 @@ static void handle_xevents (struct ddsi_thread_state * const thrst, struct ddsi_ currently isn't. */ xev->tsched.v = DDS_NEVER; ddsi_thread_state_awake_to_awake_no_nest (thrst); - handle_timed_xevent (thrst, xev, xp, tnow); + + /* We relinquish the lock while processing the event. */ + if (xev->sync_state == CSS_DONTCARE) + { + ddsrt_mutex_unlock (&xevq->lock); + xev->cb.cb (xevq->gv, xev, xp, xev->arg, tnow); + ddsrt_mutex_lock (&xevq->lock); + } + else + { + xev->sync_state = CSS_EXECUTING; + ddsrt_mutex_unlock (&xevq->lock); + xev->cb.cb (xevq->gv, xev, xp, xev->arg, tnow); + ddsrt_mutex_lock (&xevq->lock); + xev->sync_state = CSS_SCHEDULED; + ddsrt_cond_broadcast (&xevq->cond); + } } /* Limited-bandwidth channels means events can take a LONG time @@ -1443,82 +855,26 @@ enum ddsi_qxev_msg_rexmit_result ddsi_qxev_msg_rexmit_wrlock_held (struct ddsi_x } } -struct ddsi_xevent *ddsi_qxev_heartbeat (struct ddsi_xeventq *evq, ddsrt_mtime_t tsched, const ddsi_guid_t *wr_guid) -{ - /* Event _must_ be deleted before enough of the writer is freed to - cause trouble. Currently used exclusively for - wr->heartbeat_xevent. */ - struct ddsi_xevent *ev; - assert(evq); - ddsrt_mutex_lock (&evq->lock); - ev = qxev_common (evq, tsched, XEVK_HEARTBEAT); - ev->u.heartbeat.wr_guid = *wr_guid; - qxev_insert (ev); - ddsrt_mutex_unlock (&evq->lock); - return ev; -} - -struct ddsi_xevent *ddsi_qxev_acknack (struct ddsi_xeventq *evq, ddsrt_mtime_t tsched, const ddsi_guid_t *pwr_guid, const ddsi_guid_t *rd_guid) +struct ddsi_xevent *ddsi_qxev_callback (struct ddsi_xeventq *evq, ddsrt_mtime_t tsched, ddsi_xevent_cb_t cb, const void *arg, size_t arg_size, bool sync_on_delete) { - struct ddsi_xevent *ev; - assert(evq); + assert (tsched.v != TSCHED_DELETE); + struct ddsi_xevent *ev = ddsrt_malloc (sizeof (*ev) + arg_size); ddsrt_mutex_lock (&evq->lock); - ev = qxev_common (evq, tsched, XEVK_ACKNACK); - ev->u.acknack.pwr_guid = *pwr_guid; - ev->u.acknack.rd_guid = *rd_guid; - qxev_insert (ev); - ddsrt_mutex_unlock (&evq->lock); - return ev; -} -struct ddsi_xevent *ddsi_qxev_spdp (struct ddsi_xeventq *evq, ddsrt_mtime_t tsched, const ddsi_guid_t *pp_guid, const ddsi_guid_t *dest_proxypp_guid) -{ - struct ddsi_xevent *ev; - ddsrt_mutex_lock (&evq->lock); - ev = qxev_common (evq, tsched, XEVK_SPDP); - ev->u.spdp.pp_guid = *pp_guid; - if (dest_proxypp_guid == NULL) - ev->u.spdp.directed = 0; - else + /* round up the scheduled time if required */ + if (tsched.v != DDS_NEVER && evq->gv->config.schedule_time_rounding != 0) { - ev->u.spdp.dest_proxypp_guid_prefix = dest_proxypp_guid->prefix; - ev->u.spdp.directed = 4; + ddsrt_mtime_t tsched_rounded = mtime_round_up (tsched, evq->gv->config.schedule_time_rounding); + EVQTRACE ("rounded event scheduled for %"PRId64" to %"PRId64"\n", tsched.v, tsched_rounded.v); + tsched = tsched_rounded; } - qxev_insert (ev); - ddsrt_mutex_unlock (&evq->lock); - return ev; -} - -struct ddsi_xevent *ddsi_qxev_pmd_update (struct ddsi_xeventq *evq, ddsrt_mtime_t tsched, const ddsi_guid_t *pp_guid) -{ - struct ddsi_xevent *ev; - ddsrt_mutex_lock (&evq->lock); - ev = qxev_common (evq, tsched, XEVK_PMD_UPDATE); - ev->u.pmd_update.pp_guid = *pp_guid; - qxev_insert (ev); - ddsrt_mutex_unlock (&evq->lock); - return ev; -} - -struct ddsi_xevent *ddsi_qxev_delete_writer (struct ddsi_xeventq *evq, ddsrt_mtime_t tsched, const ddsi_guid_t *guid) -{ - struct ddsi_xevent *ev; - ddsrt_mutex_lock (&evq->lock); - ev = qxev_common (evq, tsched, XEVK_DELETE_WRITER); - ev->u.delete_writer.guid = *guid; - qxev_insert (ev); - ddsrt_mutex_unlock (&evq->lock); - return ev; -} -struct ddsi_xevent *ddsi_qxev_callback (struct ddsi_xeventq *evq, ddsrt_mtime_t tsched, void (*cb) (struct ddsi_xevent *ev, void *arg, ddsrt_mtime_t tnow), void *arg) -{ - struct ddsi_xevent *ev; - ddsrt_mutex_lock (&evq->lock); - ev = qxev_common (evq, tsched, XEVK_CALLBACK); - ev->u.callback.cb = cb; - ev->u.callback.arg = arg; - ev->u.callback.executing = false; + ev->evq = evq; + ev->tsched = tsched; + ev->cb.cb = cb; + ev->sync_state = sync_on_delete ? CSS_SCHEDULED : CSS_DONTCARE; + if (arg_size) // so arg = NULL, arg_size = 0 is allowed + memcpy (ev->arg, arg, arg_size); qxev_insert (ev); ddsrt_mutex_unlock (&evq->lock); return ev; diff --git a/src/security/core/src/dds_security_timed_cb.c b/src/security/core/src/dds_security_timed_cb.c index d4e951a801..eaf5d439b8 100644 --- a/src/security/core/src/dds_security_timed_cb.c +++ b/src/security/core/src/dds_security_timed_cb.c @@ -96,9 +96,16 @@ static ddsrt_mtime_t calc_tsched (const struct dds_security_timed_event *ev, dds } } -static void timed_event_cb (struct ddsi_xevent *xev, void *arg, ddsrt_mtime_t tnow) +struct timed_event_cb_arg { + struct dds_security_timed_dispatcher *dispatcher; +}; + +static void timed_event_cb (struct ddsi_domaingv *gv, struct ddsi_xevent *xev, struct ddsi_xpack *xp, void *varg, ddsrt_mtime_t tnow) { - struct dds_security_timed_dispatcher * const dispatcher = arg; + struct timed_event_cb_arg * const arg = varg; + struct dds_security_timed_dispatcher * const dispatcher = arg->dispatcher; + (void) gv; + (void) xp; (void) tnow; ddsrt_mutex_lock (&dispatcher->lock); @@ -142,7 +149,8 @@ void dds_security_timed_dispatcher_enable (struct dds_security_timed_dispatcher if (d->evt == NULL) { struct dds_security_timed_event const * const ev = ddsrt_fibheap_min (&timed_cb_queue_fhdef, &d->timers); - d->evt = ddsi_qxev_callback (d->evq, calc_tsched (ev, dds_time ()), timed_event_cb, d); + struct timed_event_cb_arg arg = { .dispatcher = d }; + d->evt = ddsi_qxev_callback (d->evq, calc_tsched (ev, dds_time ()), timed_event_cb, &arg, sizeof (arg), true); } ddsrt_mutex_unlock (&d->lock); } @@ -182,7 +190,7 @@ bool dds_security_timed_dispatcher_disable (struct dds_security_timed_dispatcher ddsrt_mutex_unlock (&d->lock); if (evt != NULL) - ddsi_delete_xevent_callback (evt); + ddsi_delete_xevent (evt); return (evt != NULL); } From 7f9369a4ebe7880580eb7043ac6d3e8153139b3b Mon Sep 17 00:00:00 2001 From: Erik Boasson Date: Tue, 21 Mar 2023 13:25:44 +0100 Subject: [PATCH 02/11] Move heartbeat control to separate file Signed-off-by: Erik Boasson --- src/core/ddsi/CMakeLists.txt | 1 + src/core/ddsi/src/ddsi__hbcontrol.h | 5 + src/core/ddsi/src/ddsi_endpoint.c | 191 +-------- src/core/ddsi/src/ddsi_hbcontrol.c | 614 ++++++++++++++++++++++++++++ src/core/ddsi/src/ddsi_transmit.c | 396 ------------------ 5 files changed, 622 insertions(+), 585 deletions(-) create mode 100644 src/core/ddsi/src/ddsi_hbcontrol.c diff --git a/src/core/ddsi/CMakeLists.txt b/src/core/ddsi/CMakeLists.txt index eda89d3194..a7b1b0640c 100644 --- a/src/core/ddsi/CMakeLists.txt +++ b/src/core/ddsi/CMakeLists.txt @@ -86,6 +86,7 @@ set(srcs_ddsi ddsi_xevent.c ddsi_xmsg.c ddsi_freelist.c + ddsi_hbcontrol.c ) set(hdrs_ddsi diff --git a/src/core/ddsi/src/ddsi__hbcontrol.h b/src/core/ddsi/src/ddsi__hbcontrol.h index 0cf1478246..5a899d073d 100644 --- a/src/core/ddsi/src/ddsi__hbcontrol.h +++ b/src/core/ddsi/src/ddsi__hbcontrol.h @@ -49,6 +49,11 @@ struct ddsi_xmsg *ddsi_writer_hbcontrol_create_heartbeat (struct ddsi_writer *wr struct ddsi_xmsg *ddsi_writer_hbcontrol_p2p(struct ddsi_writer *wr, const struct ddsi_whc_state *whcst, int hbansreq, struct ddsi_proxy_reader *prd); #endif +struct ddsi_xevent_heartbeat_cb_arg { + ddsi_guid_t wr_guid; +}; + +void ddsi_xevent_heartbeat_cb (struct ddsi_domaingv *gv, struct ddsi_xevent *ev, struct ddsi_xpack *xp, void *varg, ddsrt_mtime_t tnow); #if defined (__cplusplus) } diff --git a/src/core/ddsi/src/ddsi_endpoint.c b/src/core/ddsi/src/ddsi_endpoint.c index 79254d68b0..ddbf51f249 100644 --- a/src/core/ddsi/src/ddsi_endpoint.c +++ b/src/core/ddsi/src/ddsi_endpoint.c @@ -727,193 +727,6 @@ int ddsi_writer_set_notalive (struct ddsi_writer *wr, bool notify) return ret; } -#ifdef DDS_HAS_SECURITY -static int send_heartbeat_to_all_readers_check_and_sched (struct ddsi_xevent *ev, struct ddsi_writer *wr, const struct ddsi_whc_state *whcst, ddsrt_mtime_t tnow, ddsrt_mtime_t *t_next) -{ - int send; - if (!ddsi_writer_must_have_hb_scheduled (wr, whcst)) - { - wr->hbcontrol.tsched = DDSRT_MTIME_NEVER; - send = -1; - } - else if (!ddsi_writer_hbcontrol_must_send (wr, whcst, tnow)) - { - wr->hbcontrol.tsched = ddsrt_mtime_add_duration (tnow, ddsi_writer_hbcontrol_intv (wr, whcst, tnow)); - send = -1; - } - else - { - const int hbansreq = ddsi_writer_hbcontrol_ack_required (wr, whcst, tnow); - wr->hbcontrol.tsched = ddsrt_mtime_add_duration (tnow, ddsi_writer_hbcontrol_intv (wr, whcst, tnow)); - send = hbansreq; - } - - ddsi_resched_xevent_if_earlier (ev, wr->hbcontrol.tsched); - *t_next = wr->hbcontrol.tsched; - return send; -} - -static void send_heartbeat_to_all_readers (struct ddsi_xpack *xp, struct ddsi_xevent *ev, struct ddsi_writer *wr, ddsrt_mtime_t tnow) -{ - struct ddsi_whc_state whcst; - ddsrt_mtime_t t_next; - unsigned count = 0; - - ddsrt_mutex_lock (&wr->e.lock); - - ddsi_whc_get_state(wr->whc, &whcst); - const int hbansreq = send_heartbeat_to_all_readers_check_and_sched (ev, wr, &whcst, tnow, &t_next); - if (hbansreq >= 0) - { - struct ddsi_wr_prd_match *m; - struct ddsi_guid last_guid = { .prefix = {.u = {0,0,0}}, .entityid = {0} }; - - while ((m = ddsrt_avl_lookup_succ (&ddsi_wr_readers_treedef, &wr->readers, &last_guid)) != NULL) - { - last_guid = m->prd_guid; - if (m->seq < m->last_seq) - { - struct ddsi_proxy_reader *prd; - - prd = ddsi_entidx_lookup_proxy_reader_guid (wr->e.gv->entity_index, &m->prd_guid); - if (prd) - { - ETRACE (wr, " heartbeat(wr "PGUIDFMT" rd "PGUIDFMT" %s) send, resched in %g s (min-ack %"PRIu64", avail-seq %"PRIu64")\n", - PGUID (wr->e.guid), - PGUID (m->prd_guid), - hbansreq ? "" : " final", - (double)(t_next.v - tnow.v) / 1e9, - m->seq, - m->last_seq); - - struct ddsi_xmsg *msg = ddsi_writer_hbcontrol_p2p(wr, &whcst, hbansreq, prd); - if (msg != NULL) - { - ddsrt_mutex_unlock (&wr->e.lock); - ddsi_xpack_addmsg (xp, msg, 0); - ddsrt_mutex_lock (&wr->e.lock); - } - count++; - } - } - } - } - - if (count == 0) - { - if (ddsrt_avl_is_empty (&wr->readers)) - { - ETRACE (wr, "heartbeat(wr "PGUIDFMT") suppressed, resched in %g s (min-ack [none], avail-seq %"PRIu64", xmit %"PRIu64")\n", - PGUID (wr->e.guid), - (t_next.v == DDS_NEVER) ? INFINITY : (double)(t_next.v - tnow.v) / 1e9, - whcst.max_seq, - ddsi_writer_read_seq_xmit(wr)); - } - else - { - ETRACE (wr, "heartbeat(wr "PGUIDFMT") suppressed, resched in %g s (min-ack %"PRIu64"%s, avail-seq %"PRIu64", xmit %"PRIu64")\n", - PGUID (wr->e.guid), - (t_next.v == DDS_NEVER) ? INFINITY : (double)(t_next.v - tnow.v) / 1e9, - ((struct ddsi_wr_prd_match *) ddsrt_avl_root (&ddsi_wr_readers_treedef, &wr->readers))->min_seq, - ((struct ddsi_wr_prd_match *) ddsrt_avl_root (&ddsi_wr_readers_treedef, &wr->readers))->all_have_replied_to_hb ? "" : "!", - whcst.max_seq, - ddsi_writer_read_seq_xmit(wr)); - } - } - - ddsrt_mutex_unlock (&wr->e.lock); -} -#endif - -struct handle_heartbeat_event_arg { - ddsi_guid_t wr_guid; -}; - -static void handle_heartbeat_event (struct ddsi_domaingv *gv, struct ddsi_xevent *ev, struct ddsi_xpack *xp, void *varg, ddsrt_mtime_t tnow) -{ - struct handle_heartbeat_event_arg const * const arg = varg; - struct ddsi_writer *wr; - if ((wr = ddsi_entidx_lookup_writer_guid (gv->entity_index, &arg->wr_guid)) == NULL) - { - return; - } - - struct ddsi_xmsg *msg; - ddsrt_mtime_t t_next; - int hbansreq = 0; - struct ddsi_whc_state whcst; - -#ifdef DDS_HAS_SECURITY - if (wr->e.guid.entityid.u == DDSI_ENTITYID_P2P_BUILTIN_PARTICIPANT_VOLATILE_SECURE_WRITER) - { - send_heartbeat_to_all_readers(xp, ev, wr, tnow); - return; - } -#endif - - ddsrt_mutex_lock (&wr->e.lock); - assert (wr->reliable); - ddsi_whc_get_state(wr->whc, &whcst); - if (!ddsi_writer_must_have_hb_scheduled (wr, &whcst)) - { - hbansreq = 1; /* just for trace */ - msg = NULL; /* Need not send it now, and no need to schedule it for the future */ - t_next.v = DDS_NEVER; - } - else if (!ddsi_writer_hbcontrol_must_send (wr, &whcst, tnow)) - { - hbansreq = 1; /* just for trace */ - msg = NULL; - t_next.v = tnow.v + ddsi_writer_hbcontrol_intv (wr, &whcst, tnow); - } - else - { - hbansreq = ddsi_writer_hbcontrol_ack_required (wr, &whcst, tnow); - msg = ddsi_writer_hbcontrol_create_heartbeat (wr, &whcst, tnow, hbansreq, 0); - t_next.v = tnow.v + ddsi_writer_hbcontrol_intv (wr, &whcst, tnow); - } - - if (ddsrt_avl_is_empty (&wr->readers)) - { - GVTRACE ("heartbeat(wr "PGUIDFMT"%s) %s, resched in %g s (min-ack [none], avail-seq %"PRIu64", xmit %"PRIu64")\n", - PGUID (wr->e.guid), - hbansreq ? "" : " final", - msg ? "sent" : "suppressed", - (t_next.v == DDS_NEVER) ? INFINITY : (double)(t_next.v - tnow.v) / 1e9, - whcst.max_seq, ddsi_writer_read_seq_xmit (wr)); - } - else - { - GVTRACE ("heartbeat(wr "PGUIDFMT"%s) %s, resched in %g s (min-ack %"PRId64"%s, avail-seq %"PRIu64", xmit %"PRIu64")\n", - PGUID (wr->e.guid), - hbansreq ? "" : " final", - msg ? "sent" : "suppressed", - (t_next.v == DDS_NEVER) ? INFINITY : (double)(t_next.v - tnow.v) / 1e9, - ((struct ddsi_wr_prd_match *) ddsrt_avl_root_non_empty (&ddsi_wr_readers_treedef, &wr->readers))->min_seq, - ((struct ddsi_wr_prd_match *) ddsrt_avl_root_non_empty (&ddsi_wr_readers_treedef, &wr->readers))->all_have_replied_to_hb ? "" : "!", - whcst.max_seq, ddsi_writer_read_seq_xmit (wr)); - } - (void) ddsi_resched_xevent_if_earlier (ev, t_next); - wr->hbcontrol.tsched = t_next; - ddsrt_mutex_unlock (&wr->e.lock); - - /* Can't transmit synchronously with writer lock held: trying to add - the heartbeat to the xp may cause xp to be sent out, which may - require updating wr->seq_xmit for other messages already in xp. - Besides, ddsi_xpack_addmsg may sleep for bandwidth-limited channels - and we certainly don't want to hold the lock during that time. */ - if (msg) - { - if (!wr->test_suppress_heartbeat) - ddsi_xpack_addmsg (xp, msg, 0); - else - { - GVTRACE ("test_suppress_heartbeat\n"); - ddsi_xmsg_free (msg); - } - } -} - static void ddsi_new_writer_guid_common_init (struct ddsi_writer *wr, const char *topic_name, const struct ddsi_sertype *type, const struct dds_qos *xqos, struct ddsi_whc *whc, ddsi_status_cb_t status_cb, void * status_entity) { ddsrt_cond_init (&wr->throttle_cond); @@ -1055,8 +868,8 @@ static void ddsi_new_writer_guid_common_init (struct ddsi_writer *wr, const char wr->heartbeat_xevent = NULL; else { - struct handle_heartbeat_event_arg arg = {.wr_guid = wr->e.guid }; - wr->heartbeat_xevent = ddsi_qxev_callback (wr->evq, DDSRT_MTIME_NEVER, handle_heartbeat_event, &arg, sizeof (arg), false); + struct ddsi_xevent_heartbeat_cb_arg arg = {.wr_guid = wr->e.guid }; + wr->heartbeat_xevent = ddsi_qxev_callback (wr->evq, DDSRT_MTIME_NEVER, ddsi_xevent_heartbeat_cb, &arg, sizeof (arg), false); } assert (wr->xqos->present & DDSI_QP_LIVELINESS); diff --git a/src/core/ddsi/src/ddsi_hbcontrol.c b/src/core/ddsi/src/ddsi_hbcontrol.c new file mode 100644 index 0000000000..b6c078612d --- /dev/null +++ b/src/core/ddsi/src/ddsi_hbcontrol.c @@ -0,0 +1,614 @@ +/* + * Copyright(c) 2006 to 2023 ZettaScale Technology and others + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License + * v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ +#include +#include +#include + +#include "dds/ddsrt/sync.h" +#include "dds/ddsrt/avl.h" +#include "dds/ddsi/ddsi_domaingv.h" +#include "dds/ddsi/ddsi_unused.h" +#include "ddsi__entity_index.h" +#include "ddsi__xmsg.h" +#include "ddsi__misc.h" +#include "ddsi__xevent.h" +#include "ddsi__transmit.h" +#include "ddsi__hbcontrol.h" +#include "ddsi__security_omg.h" +#include "ddsi__sysdeps.h" +#include "ddsi__endpoint.h" +#include "ddsi__endpoint_match.h" +#include "ddsi__protocol.h" + +static const struct ddsi_wr_prd_match *root_rdmatch (const struct ddsi_writer *wr) +{ + return ddsrt_avl_root (&ddsi_wr_readers_treedef, &wr->readers); +} + +void ddsi_writer_hbcontrol_init (struct ddsi_hbcontrol *hbc) +{ + hbc->t_of_last_write.v = 0; + hbc->t_of_last_hb.v = 0; + hbc->t_of_last_ackhb.v = 0; + hbc->tsched = DDSRT_MTIME_NEVER; + hbc->hbs_since_last_write = 0; + hbc->last_packetid = 0; +} + +static void writer_hbcontrol_note_hb (struct ddsi_writer *wr, ddsrt_mtime_t tnow, int ansreq) +{ + struct ddsi_hbcontrol * const hbc = &wr->hbcontrol; + + if (ansreq) + hbc->t_of_last_ackhb = tnow; + hbc->t_of_last_hb = tnow; + + /* Count number of heartbeats since last write, used to lower the + heartbeat rate. Overflow doesn't matter, it'll just revert to a + highish rate for a short while. */ + hbc->hbs_since_last_write++; +} + +int64_t ddsi_writer_hbcontrol_intv (const struct ddsi_writer *wr, const struct ddsi_whc_state *whcst, UNUSED_ARG (ddsrt_mtime_t tnow)) +{ + struct ddsi_domaingv const * const gv = wr->e.gv; + struct ddsi_hbcontrol const * const hbc = &wr->hbcontrol; + int64_t ret = gv->config.const_hb_intv_sched; + size_t n_unacked; + + if (hbc->hbs_since_last_write > 5) + { + unsigned cnt = (hbc->hbs_since_last_write - 5) / 2; + while (cnt-- != 0 && 2 * ret < gv->config.const_hb_intv_sched_max) + ret *= 2; + } + + n_unacked = whcst->unacked_bytes; + if (n_unacked >= wr->whc_low + 3 * (wr->whc_high - wr->whc_low) / 4) + ret /= 2; + if (n_unacked >= wr->whc_low + (wr->whc_high - wr->whc_low) / 2) + ret /= 2; + if (wr->throttling) + ret /= 2; + if (ret < gv->config.const_hb_intv_sched_min) + ret = gv->config.const_hb_intv_sched_min; + return ret; +} + +void ddsi_writer_hbcontrol_note_asyncwrite (struct ddsi_writer *wr, ddsrt_mtime_t tnow) +{ + struct ddsi_domaingv const * const gv = wr->e.gv; + struct ddsi_hbcontrol * const hbc = &wr->hbcontrol; + ddsrt_mtime_t tnext; + + /* Reset number of heartbeats since last write: that means the + heartbeat rate will go back up to the default */ + hbc->hbs_since_last_write = 0; + + /* We know this is new data, so we want a heartbeat event after one + base interval */ + tnext.v = tnow.v + gv->config.const_hb_intv_sched; + if (tnext.v < hbc->tsched.v) + { + /* Insertion of a message with WHC locked => must now have at + least one unacked msg if there are reliable readers, so must + have a heartbeat scheduled. Do so now */ + hbc->tsched = tnext; + (void) ddsi_resched_xevent_if_earlier (wr->heartbeat_xevent, tnext); + } +} + +int ddsi_writer_hbcontrol_must_send (const struct ddsi_writer *wr, const struct ddsi_whc_state *whcst, ddsrt_mtime_t tnow /* monotonic */) +{ + struct ddsi_hbcontrol const * const hbc = &wr->hbcontrol; + return (tnow.v >= hbc->t_of_last_hb.v + ddsi_writer_hbcontrol_intv (wr, whcst, tnow)); +} + +struct ddsi_xmsg *ddsi_writer_hbcontrol_create_heartbeat (struct ddsi_writer *wr, const struct ddsi_whc_state *whcst, ddsrt_mtime_t tnow, int hbansreq, int issync) +{ + struct ddsi_domaingv const * const gv = wr->e.gv; + struct ddsi_xmsg *msg; + const ddsi_guid_t *prd_guid; + + ASSERT_MUTEX_HELD (&wr->e.lock); + assert (wr->reliable); + assert (hbansreq >= 0); + + if ((msg = ddsi_xmsg_new (gv->xmsgpool, &wr->e.guid, wr->c.pp, sizeof (ddsi_rtps_info_ts_t) + sizeof (ddsi_rtps_heartbeat_t), DDSI_XMSG_KIND_CONTROL)) == NULL) + /* out of memory at worst slows down traffic */ + return NULL; + + if (ddsrt_avl_is_empty (&wr->readers) || wr->num_reliable_readers == 0) + { + /* Not really supposed to come here, at least not for the first + case. Secondly, there really seems to be little use for + optimising reliable writers with only best-effort readers. And + in any case, it is always legal to multicast a heartbeat from a + reliable writer. */ + prd_guid = NULL; + } + else if (wr->seq != root_rdmatch (wr)->max_seq) + { + /* If the writer is ahead of its readers, multicast. Couldn't care + less about the pessimal cases such as multicasting when there + is one reliable reader & multiple best-effort readers. See + comment above. */ + prd_guid = NULL; + } + else + { + const uint32_t n_unacked = wr->num_reliable_readers - root_rdmatch (wr)->num_reliable_readers_where_seq_equals_max; + if (n_unacked == 0) + prd_guid = NULL; + else + { + assert (root_rdmatch (wr)->arbitrary_unacked_reader.entityid.u != DDSI_ENTITYID_UNKNOWN); + if (n_unacked > 1) + prd_guid = NULL; + else + prd_guid = &(root_rdmatch (wr)->arbitrary_unacked_reader); + } + } + + ETRACE (wr, "writer_hbcontrol: wr "PGUIDFMT" ", PGUID (wr->e.guid)); + if (prd_guid == NULL) + ETRACE (wr, "multicasting "); + else + ETRACE (wr, "unicasting to prd "PGUIDFMT" ", PGUID (*prd_guid)); + if (ddsrt_avl_is_empty (&wr->readers)) + { + ETRACE (wr, "(rel-prd %"PRId32" seq-eq-max [none] seq %"PRId64" maxseq [none])\n", + wr->num_reliable_readers, wr->seq); + } + else + { + ETRACE (wr, "(rel-prd %"PRId32" seq-eq-max %"PRId32" seq %"PRIu64" maxseq %"PRIu64")\n", + wr->num_reliable_readers, + (int32_t) root_rdmatch (wr)->num_reliable_readers_where_seq_equals_max, + wr->seq, + root_rdmatch (wr)->max_seq); + } + + if (prd_guid == NULL) + { + ddsi_xmsg_setdst_addrset (msg, wr->as); + ddsi_add_heartbeat (msg, wr, whcst, hbansreq, 0, ddsi_to_entityid (DDSI_ENTITYID_UNKNOWN), issync); + } + else + { + struct ddsi_proxy_reader *prd; + if ((prd = ddsi_entidx_lookup_proxy_reader_guid (gv->entity_index, prd_guid)) == NULL) + { + ETRACE (wr, "writer_hbcontrol: wr "PGUIDFMT" unknown prd "PGUIDFMT"\n", PGUID (wr->e.guid), PGUID (*prd_guid)); + ddsi_xmsg_free (msg); + return NULL; + } + /* set the destination explicitly to the unicast destination and the fourth + param of ddsi_add_heartbeat needs to be the guid of the reader */ + ddsi_xmsg_setdst_prd (msg, prd); + // send to all readers in the participant: whether or not the entityid is set affects + // the retransmit requests + ddsi_add_heartbeat (msg, wr, whcst, hbansreq, 0, ddsi_to_entityid (DDSI_ENTITYID_UNKNOWN), issync); + } + + /* It is possible that the encoding removed the submessage(s). */ + if (ddsi_xmsg_size(msg) == 0) + { + ddsi_xmsg_free (msg); + msg = NULL; + } + + writer_hbcontrol_note_hb (wr, tnow, hbansreq); + return msg; +} + +static int writer_hbcontrol_ack_required_generic (const struct ddsi_writer *wr, const struct ddsi_whc_state *whcst, ddsrt_mtime_t tlast, ddsrt_mtime_t tnow, int piggyback) +{ + struct ddsi_domaingv const * const gv = wr->e.gv; + struct ddsi_hbcontrol const * const hbc = &wr->hbcontrol; + const int64_t hb_intv_ack = gv->config.const_hb_intv_sched; + assert(wr->heartbeat_xevent != NULL && whcst != NULL); + + if (piggyback) + { + /* If it is likely that a heartbeat requiring an ack will go out + shortly after the sample was written, it is better to piggyback + it onto the sample. The current idea is that a write shortly + before the next heartbeat will go out should have one + piggybacked onto it, so that the scheduled heartbeat can be + suppressed. */ + if (tnow.v >= tlast.v + 4 * hb_intv_ack / 5) + return 2; + } + else + { + /* For heartbeat events use a slightly longer interval */ + if (tnow.v >= tlast.v + hb_intv_ack) + return 2; + } + + if (whcst->unacked_bytes >= wr->whc_low + (wr->whc_high - wr->whc_low) / 2) + { + if (tnow.v >= hbc->t_of_last_ackhb.v + gv->config.const_hb_intv_sched_min) + return 2; + else if (tnow.v >= hbc->t_of_last_ackhb.v + gv->config.const_hb_intv_min) + return 1; + } + + return 0; +} + +int ddsi_writer_hbcontrol_ack_required (const struct ddsi_writer *wr, const struct ddsi_whc_state *whcst, ddsrt_mtime_t tnow) +{ + struct ddsi_hbcontrol const * const hbc = &wr->hbcontrol; + return writer_hbcontrol_ack_required_generic (wr, whcst, hbc->t_of_last_write, tnow, 0); +} + +struct ddsi_xmsg *ddsi_writer_hbcontrol_piggyback (struct ddsi_writer *wr, const struct ddsi_whc_state *whcst, ddsrt_mtime_t tnow, uint32_t packetid, int *hbansreq) +{ + struct ddsi_hbcontrol * const hbc = &wr->hbcontrol; + uint32_t last_packetid; + ddsrt_mtime_t tlast; + ddsrt_mtime_t t_of_last_hb; + struct ddsi_xmsg *msg; + + tlast = hbc->t_of_last_write; + last_packetid = hbc->last_packetid; + t_of_last_hb = hbc->t_of_last_hb; + + hbc->t_of_last_write = tnow; + hbc->last_packetid = packetid; + + /* Update statistics, intervals, scheduling of heartbeat event, + &c. -- there's no real difference between async and sync so we + reuse the async version. */ + ddsi_writer_hbcontrol_note_asyncwrite (wr, tnow); + + *hbansreq = writer_hbcontrol_ack_required_generic (wr, whcst, tlast, tnow, 1); + if (*hbansreq >= 2) { + /* So we force a heartbeat in - but we also rely on our caller to + send the packet out */ + msg = ddsi_writer_hbcontrol_create_heartbeat (wr, whcst, tnow, *hbansreq, 1); + } else if (last_packetid != packetid && tnow.v - t_of_last_hb.v > DDS_USECS (100)) { + /* If we crossed a packet boundary since the previous write, + piggyback a heartbeat, with *hbansreq determining whether or + not an ACK is needed. We don't force the packet out either: + this is just to ensure a regular flow of ACKs for cleaning up + the WHC & for allowing readers to NACK missing samples. + + Still rate-limit: if there are new readers that haven't sent an + an ACK yet, the FINAL flag will be cleared and so we get an ACK + storm if writing at a high rate without batching which eats up + a *large* amount of time because there are out-of-order readers + present. */ + msg = ddsi_writer_hbcontrol_create_heartbeat (wr, whcst, tnow, *hbansreq, 1); + } else { + *hbansreq = 0; + msg = NULL; + } + + if (msg) + { + if (ddsrt_avl_is_empty (&wr->readers)) + { + ETRACE (wr, "heartbeat(wr "PGUIDFMT"%s) piggybacked, resched in %g s (min-ack [none], avail-seq %"PRIu64", xmit %"PRIu64")\n", + PGUID (wr->e.guid), + *hbansreq ? "" : " final", + (hbc->tsched.v == DDS_NEVER) ? INFINITY : (double) (hbc->tsched.v - tnow.v) / 1e9, + whcst->max_seq, ddsi_writer_read_seq_xmit(wr)); + } + else + { + ETRACE (wr, "heartbeat(wr "PGUIDFMT"%s) piggybacked, resched in %g s (min-ack %"PRIu64"%s, avail-seq %"PRIu64", xmit %"PRIu64")\n", + PGUID (wr->e.guid), + *hbansreq ? "" : " final", + (hbc->tsched.v == DDS_NEVER) ? INFINITY : (double) (hbc->tsched.v - tnow.v) / 1e9, + root_rdmatch (wr)->min_seq, + root_rdmatch (wr)->all_have_replied_to_hb ? "" : "!", + whcst->max_seq, ddsi_writer_read_seq_xmit(wr)); + } + } + + return msg; +} + +#ifdef DDS_HAS_SECURITY +struct ddsi_xmsg *ddsi_writer_hbcontrol_p2p(struct ddsi_writer *wr, const struct ddsi_whc_state *whcst, int hbansreq, struct ddsi_proxy_reader *prd) +{ + struct ddsi_domaingv const * const gv = wr->e.gv; + struct ddsi_xmsg *msg; + + ASSERT_MUTEX_HELD (&wr->e.lock); + assert (wr->reliable); + + if ((msg = ddsi_xmsg_new (gv->xmsgpool, &wr->e.guid, wr->c.pp, sizeof (ddsi_rtps_info_ts_t) + sizeof (ddsi_rtps_heartbeat_t), DDSI_XMSG_KIND_CONTROL)) == NULL) + return NULL; + + ETRACE (wr, "writer_hbcontrol_p2p: wr "PGUIDFMT" unicasting to prd "PGUIDFMT" ", PGUID (wr->e.guid), PGUID (prd->e.guid)); + if (ddsrt_avl_is_empty (&wr->readers)) + { + ETRACE (wr, "(rel-prd %d seq-eq-max [none] seq %"PRIu64")\n", wr->num_reliable_readers, wr->seq); + } + else + { + ETRACE (wr, "(rel-prd %d seq-eq-max %d seq %"PRIu64" maxseq %"PRIu64")\n", + wr->num_reliable_readers, + (int32_t) root_rdmatch (wr)->num_reliable_readers_where_seq_equals_max, + wr->seq, + root_rdmatch (wr)->max_seq); + } + + /* set the destination explicitly to the unicast destination and the fourth + param of ddsi_add_heartbeat needs to be the guid of the reader */ + ddsi_xmsg_setdst_prd (msg, prd); + ddsi_add_heartbeat (msg, wr, whcst, hbansreq, 0, prd->e.guid.entityid, 1); + + if (ddsi_xmsg_size(msg) == 0) + { + ddsi_xmsg_free (msg); + msg = NULL; + } + + return msg; +} +#endif + +void ddsi_add_heartbeat (struct ddsi_xmsg *msg, struct ddsi_writer *wr, const struct ddsi_whc_state *whcst, int hbansreq, int hbliveliness, ddsi_entityid_t dst, int issync) +{ + struct ddsi_domaingv const * const gv = wr->e.gv; + struct ddsi_xmsg_marker sm_marker; + ddsi_rtps_heartbeat_t * hb; + ddsi_seqno_t max, min; + + ASSERT_MUTEX_HELD (&wr->e.lock); + + assert (wr->reliable); + assert (hbansreq >= 0); + assert (hbliveliness >= 0); + + if (gv->config.meas_hb_to_ack_latency) + { + /* If configured to measure heartbeat-to-ack latency, we must add + a timestamp. No big deal if it fails. */ + ddsi_xmsg_add_timestamp (msg, ddsrt_time_wallclock ()); + } + + hb = ddsi_xmsg_append (msg, &sm_marker, sizeof (ddsi_rtps_heartbeat_t)); + ddsi_xmsg_submsg_init (msg, sm_marker, DDSI_RTPS_SMID_HEARTBEAT); + + if (!hbansreq) + hb->smhdr.flags |= DDSI_HEARTBEAT_FLAG_FINAL; + if (hbliveliness) + hb->smhdr.flags |= DDSI_HEARTBEAT_FLAG_LIVELINESS; + + hb->readerId = ddsi_hton_entityid (dst); + hb->writerId = ddsi_hton_entityid (wr->e.guid.entityid); + if (DDSI_WHCST_ISEMPTY(whcst)) + { + max = wr->seq; + min = max + 1; + } + else + { + /* If data present in WHC, wr->seq > 0, but xmit_seq possibly still 0 */ + min = whcst->min_seq; + max = wr->seq; + const ddsi_seqno_t seq_xmit = ddsi_writer_read_seq_xmit (wr); + assert (min <= max); + /* Informing readers of samples that haven't even been transmitted makes little sense, + but for transient-local data, we let the first heartbeat determine the time at which + we trigger wait_for_historical_data, so it had better be correct */ + if (!issync && seq_xmit < max && !wr->handle_as_transient_local) + { + /* When: queue data ; queue heartbeat ; transmit data ; update + seq_xmit, max may be < min. But we must never advertise the + minimum available sequence number incorrectly! */ + if (seq_xmit >= min) { + /* Advertise some but not all data */ + max = seq_xmit; + } else { + /* Advertise no data yet */ + max = min - 1; + } + } + } + hb->firstSN = ddsi_to_seqno (min); + hb->lastSN = ddsi_to_seqno (max); + + hb->count = wr->hbcount++; + + ddsi_xmsg_submsg_setnext (msg, sm_marker); + ddsi_security_encode_datawriter_submsg(msg, sm_marker, wr); +} + +#ifdef DDS_HAS_SECURITY +static int send_heartbeat_to_all_readers_check_and_sched (struct ddsi_xevent *ev, struct ddsi_writer *wr, const struct ddsi_whc_state *whcst, ddsrt_mtime_t tnow, ddsrt_mtime_t *t_next) +{ + int send; + if (!ddsi_writer_must_have_hb_scheduled (wr, whcst)) + { + wr->hbcontrol.tsched = DDSRT_MTIME_NEVER; + send = -1; + } + else if (!ddsi_writer_hbcontrol_must_send (wr, whcst, tnow)) + { + wr->hbcontrol.tsched = ddsrt_mtime_add_duration (tnow, ddsi_writer_hbcontrol_intv (wr, whcst, tnow)); + send = -1; + } + else + { + const int hbansreq = ddsi_writer_hbcontrol_ack_required (wr, whcst, tnow); + wr->hbcontrol.tsched = ddsrt_mtime_add_duration (tnow, ddsi_writer_hbcontrol_intv (wr, whcst, tnow)); + send = hbansreq; + } + + ddsi_resched_xevent_if_earlier (ev, wr->hbcontrol.tsched); + *t_next = wr->hbcontrol.tsched; + return send; +} + +static void send_heartbeat_to_all_readers (struct ddsi_xpack *xp, struct ddsi_xevent *ev, struct ddsi_writer *wr, ddsrt_mtime_t tnow) +{ + struct ddsi_whc_state whcst; + ddsrt_mtime_t t_next; + unsigned count = 0; + + ddsrt_mutex_lock (&wr->e.lock); + + ddsi_whc_get_state(wr->whc, &whcst); + const int hbansreq = send_heartbeat_to_all_readers_check_and_sched (ev, wr, &whcst, tnow, &t_next); + if (hbansreq >= 0) + { + struct ddsi_wr_prd_match *m; + struct ddsi_guid last_guid = { .prefix = {.u = {0,0,0}}, .entityid = {0} }; + + while ((m = ddsrt_avl_lookup_succ (&ddsi_wr_readers_treedef, &wr->readers, &last_guid)) != NULL) + { + last_guid = m->prd_guid; + if (m->seq < m->last_seq) + { + struct ddsi_proxy_reader *prd; + + prd = ddsi_entidx_lookup_proxy_reader_guid (wr->e.gv->entity_index, &m->prd_guid); + if (prd) + { + ETRACE (wr, " heartbeat(wr "PGUIDFMT" rd "PGUIDFMT" %s) send, resched in %g s (min-ack %"PRIu64", avail-seq %"PRIu64")\n", + PGUID (wr->e.guid), + PGUID (m->prd_guid), + hbansreq ? "" : " final", + (double)(t_next.v - tnow.v) / 1e9, + m->seq, + m->last_seq); + + struct ddsi_xmsg *msg = ddsi_writer_hbcontrol_p2p(wr, &whcst, hbansreq, prd); + if (msg != NULL) + { + ddsrt_mutex_unlock (&wr->e.lock); + ddsi_xpack_addmsg (xp, msg, 0); + ddsrt_mutex_lock (&wr->e.lock); + } + count++; + } + } + } + } + + if (count == 0) + { + if (ddsrt_avl_is_empty (&wr->readers)) + { + ETRACE (wr, "heartbeat(wr "PGUIDFMT") suppressed, resched in %g s (min-ack [none], avail-seq %"PRIu64", xmit %"PRIu64")\n", + PGUID (wr->e.guid), + (t_next.v == DDS_NEVER) ? INFINITY : (double)(t_next.v - tnow.v) / 1e9, + whcst.max_seq, + ddsi_writer_read_seq_xmit(wr)); + } + else + { + ETRACE (wr, "heartbeat(wr "PGUIDFMT") suppressed, resched in %g s (min-ack %"PRIu64"%s, avail-seq %"PRIu64", xmit %"PRIu64")\n", + PGUID (wr->e.guid), + (t_next.v == DDS_NEVER) ? INFINITY : (double)(t_next.v - tnow.v) / 1e9, + ((struct ddsi_wr_prd_match *) ddsrt_avl_root (&ddsi_wr_readers_treedef, &wr->readers))->min_seq, + ((struct ddsi_wr_prd_match *) ddsrt_avl_root (&ddsi_wr_readers_treedef, &wr->readers))->all_have_replied_to_hb ? "" : "!", + whcst.max_seq, + ddsi_writer_read_seq_xmit(wr)); + } + } + + ddsrt_mutex_unlock (&wr->e.lock); +} +#endif + +void ddsi_xevent_heartbeat_cb (struct ddsi_domaingv *gv, struct ddsi_xevent *ev, struct ddsi_xpack *xp, void *varg, ddsrt_mtime_t tnow) +{ + struct ddsi_xevent_heartbeat_cb_arg const * const arg = varg; + struct ddsi_writer *wr; + if ((wr = ddsi_entidx_lookup_writer_guid (gv->entity_index, &arg->wr_guid)) == NULL) + { + return; + } + + struct ddsi_xmsg *msg; + ddsrt_mtime_t t_next; + int hbansreq = 0; + struct ddsi_whc_state whcst; + +#ifdef DDS_HAS_SECURITY + if (wr->e.guid.entityid.u == DDSI_ENTITYID_P2P_BUILTIN_PARTICIPANT_VOLATILE_SECURE_WRITER) + { + send_heartbeat_to_all_readers(xp, ev, wr, tnow); + return; + } +#endif + + ddsrt_mutex_lock (&wr->e.lock); + assert (wr->reliable); + ddsi_whc_get_state(wr->whc, &whcst); + if (!ddsi_writer_must_have_hb_scheduled (wr, &whcst)) + { + hbansreq = 1; /* just for trace */ + msg = NULL; /* Need not send it now, and no need to schedule it for the future */ + t_next.v = DDS_NEVER; + } + else if (!ddsi_writer_hbcontrol_must_send (wr, &whcst, tnow)) + { + hbansreq = 1; /* just for trace */ + msg = NULL; + t_next.v = tnow.v + ddsi_writer_hbcontrol_intv (wr, &whcst, tnow); + } + else + { + hbansreq = ddsi_writer_hbcontrol_ack_required (wr, &whcst, tnow); + msg = ddsi_writer_hbcontrol_create_heartbeat (wr, &whcst, tnow, hbansreq, 0); + t_next.v = tnow.v + ddsi_writer_hbcontrol_intv (wr, &whcst, tnow); + } + + if (ddsrt_avl_is_empty (&wr->readers)) + { + GVTRACE ("heartbeat(wr "PGUIDFMT"%s) %s, resched in %g s (min-ack [none], avail-seq %"PRIu64", xmit %"PRIu64")\n", + PGUID (wr->e.guid), + hbansreq ? "" : " final", + msg ? "sent" : "suppressed", + (t_next.v == DDS_NEVER) ? INFINITY : (double)(t_next.v - tnow.v) / 1e9, + whcst.max_seq, ddsi_writer_read_seq_xmit (wr)); + } + else + { + GVTRACE ("heartbeat(wr "PGUIDFMT"%s) %s, resched in %g s (min-ack %"PRId64"%s, avail-seq %"PRIu64", xmit %"PRIu64")\n", + PGUID (wr->e.guid), + hbansreq ? "" : " final", + msg ? "sent" : "suppressed", + (t_next.v == DDS_NEVER) ? INFINITY : (double)(t_next.v - tnow.v) / 1e9, + ((struct ddsi_wr_prd_match *) ddsrt_avl_root_non_empty (&ddsi_wr_readers_treedef, &wr->readers))->min_seq, + ((struct ddsi_wr_prd_match *) ddsrt_avl_root_non_empty (&ddsi_wr_readers_treedef, &wr->readers))->all_have_replied_to_hb ? "" : "!", + whcst.max_seq, ddsi_writer_read_seq_xmit (wr)); + } + (void) ddsi_resched_xevent_if_earlier (ev, t_next); + wr->hbcontrol.tsched = t_next; + ddsrt_mutex_unlock (&wr->e.lock); + + /* Can't transmit synchronously with writer lock held: trying to add + the heartbeat to the xp may cause xp to be sent out, which may + require updating wr->seq_xmit for other messages already in xp. + Besides, ddsi_xpack_addmsg may sleep for bandwidth-limited channels + and we certainly don't want to hold the lock during that time. */ + if (msg) + { + if (!wr->test_suppress_heartbeat) + ddsi_xpack_addmsg (xp, msg, 0); + else + { + GVTRACE ("test_suppress_heartbeat\n"); + ddsi_xmsg_free (msg); + } + } +} diff --git a/src/core/ddsi/src/ddsi_transmit.c b/src/core/ddsi/src/ddsi_transmit.c index e23726b0f9..d4a1bca5b9 100644 --- a/src/core/ddsi/src/ddsi_transmit.c +++ b/src/core/ddsi/src/ddsi_transmit.c @@ -55,402 +55,6 @@ static int have_reliable_subs (const struct ddsi_writer *wr) return 1; } -void ddsi_writer_hbcontrol_init (struct ddsi_hbcontrol *hbc) -{ - hbc->t_of_last_write.v = 0; - hbc->t_of_last_hb.v = 0; - hbc->t_of_last_ackhb.v = 0; - hbc->tsched = DDSRT_MTIME_NEVER; - hbc->hbs_since_last_write = 0; - hbc->last_packetid = 0; -} - -static void writer_hbcontrol_note_hb (struct ddsi_writer *wr, ddsrt_mtime_t tnow, int ansreq) -{ - struct ddsi_hbcontrol * const hbc = &wr->hbcontrol; - - if (ansreq) - hbc->t_of_last_ackhb = tnow; - hbc->t_of_last_hb = tnow; - - /* Count number of heartbeats since last write, used to lower the - heartbeat rate. Overflow doesn't matter, it'll just revert to a - highish rate for a short while. */ - hbc->hbs_since_last_write++; -} - -int64_t ddsi_writer_hbcontrol_intv (const struct ddsi_writer *wr, const struct ddsi_whc_state *whcst, UNUSED_ARG (ddsrt_mtime_t tnow)) -{ - struct ddsi_domaingv const * const gv = wr->e.gv; - struct ddsi_hbcontrol const * const hbc = &wr->hbcontrol; - int64_t ret = gv->config.const_hb_intv_sched; - size_t n_unacked; - - if (hbc->hbs_since_last_write > 5) - { - unsigned cnt = (hbc->hbs_since_last_write - 5) / 2; - while (cnt-- != 0 && 2 * ret < gv->config.const_hb_intv_sched_max) - ret *= 2; - } - - n_unacked = whcst->unacked_bytes; - if (n_unacked >= wr->whc_low + 3 * (wr->whc_high - wr->whc_low) / 4) - ret /= 2; - if (n_unacked >= wr->whc_low + (wr->whc_high - wr->whc_low) / 2) - ret /= 2; - if (wr->throttling) - ret /= 2; - if (ret < gv->config.const_hb_intv_sched_min) - ret = gv->config.const_hb_intv_sched_min; - return ret; -} - -void ddsi_writer_hbcontrol_note_asyncwrite (struct ddsi_writer *wr, ddsrt_mtime_t tnow) -{ - struct ddsi_domaingv const * const gv = wr->e.gv; - struct ddsi_hbcontrol * const hbc = &wr->hbcontrol; - ddsrt_mtime_t tnext; - - /* Reset number of heartbeats since last write: that means the - heartbeat rate will go back up to the default */ - hbc->hbs_since_last_write = 0; - - /* We know this is new data, so we want a heartbeat event after one - base interval */ - tnext.v = tnow.v + gv->config.const_hb_intv_sched; - if (tnext.v < hbc->tsched.v) - { - /* Insertion of a message with WHC locked => must now have at - least one unacked msg if there are reliable readers, so must - have a heartbeat scheduled. Do so now */ - hbc->tsched = tnext; - (void) ddsi_resched_xevent_if_earlier (wr->heartbeat_xevent, tnext); - } -} - -int ddsi_writer_hbcontrol_must_send (const struct ddsi_writer *wr, const struct ddsi_whc_state *whcst, ddsrt_mtime_t tnow /* monotonic */) -{ - struct ddsi_hbcontrol const * const hbc = &wr->hbcontrol; - return (tnow.v >= hbc->t_of_last_hb.v + ddsi_writer_hbcontrol_intv (wr, whcst, tnow)); -} - -struct ddsi_xmsg *ddsi_writer_hbcontrol_create_heartbeat (struct ddsi_writer *wr, const struct ddsi_whc_state *whcst, ddsrt_mtime_t tnow, int hbansreq, int issync) -{ - struct ddsi_domaingv const * const gv = wr->e.gv; - struct ddsi_xmsg *msg; - const ddsi_guid_t *prd_guid; - - ASSERT_MUTEX_HELD (&wr->e.lock); - assert (wr->reliable); - assert (hbansreq >= 0); - - if ((msg = ddsi_xmsg_new (gv->xmsgpool, &wr->e.guid, wr->c.pp, sizeof (ddsi_rtps_info_ts_t) + sizeof (ddsi_rtps_heartbeat_t), DDSI_XMSG_KIND_CONTROL)) == NULL) - /* out of memory at worst slows down traffic */ - return NULL; - - if (ddsrt_avl_is_empty (&wr->readers) || wr->num_reliable_readers == 0) - { - /* Not really supposed to come here, at least not for the first - case. Secondly, there really seems to be little use for - optimising reliable writers with only best-effort readers. And - in any case, it is always legal to multicast a heartbeat from a - reliable writer. */ - prd_guid = NULL; - } - else if (wr->seq != root_rdmatch (wr)->max_seq) - { - /* If the writer is ahead of its readers, multicast. Couldn't care - less about the pessimal cases such as multicasting when there - is one reliable reader & multiple best-effort readers. See - comment above. */ - prd_guid = NULL; - } - else - { - const uint32_t n_unacked = wr->num_reliable_readers - root_rdmatch (wr)->num_reliable_readers_where_seq_equals_max; - if (n_unacked == 0) - prd_guid = NULL; - else - { - assert (root_rdmatch (wr)->arbitrary_unacked_reader.entityid.u != DDSI_ENTITYID_UNKNOWN); - if (n_unacked > 1) - prd_guid = NULL; - else - prd_guid = &(root_rdmatch (wr)->arbitrary_unacked_reader); - } - } - - ETRACE (wr, "writer_hbcontrol: wr "PGUIDFMT" ", PGUID (wr->e.guid)); - if (prd_guid == NULL) - ETRACE (wr, "multicasting "); - else - ETRACE (wr, "unicasting to prd "PGUIDFMT" ", PGUID (*prd_guid)); - if (ddsrt_avl_is_empty (&wr->readers)) - { - ETRACE (wr, "(rel-prd %"PRId32" seq-eq-max [none] seq %"PRId64" maxseq [none])\n", - wr->num_reliable_readers, wr->seq); - } - else - { - ETRACE (wr, "(rel-prd %"PRId32" seq-eq-max %"PRId32" seq %"PRIu64" maxseq %"PRIu64")\n", - wr->num_reliable_readers, - (int32_t) root_rdmatch (wr)->num_reliable_readers_where_seq_equals_max, - wr->seq, - root_rdmatch (wr)->max_seq); - } - - if (prd_guid == NULL) - { - ddsi_xmsg_setdst_addrset (msg, wr->as); - ddsi_add_heartbeat (msg, wr, whcst, hbansreq, 0, ddsi_to_entityid (DDSI_ENTITYID_UNKNOWN), issync); - } - else - { - struct ddsi_proxy_reader *prd; - if ((prd = ddsi_entidx_lookup_proxy_reader_guid (gv->entity_index, prd_guid)) == NULL) - { - ETRACE (wr, "writer_hbcontrol: wr "PGUIDFMT" unknown prd "PGUIDFMT"\n", PGUID (wr->e.guid), PGUID (*prd_guid)); - ddsi_xmsg_free (msg); - return NULL; - } - /* set the destination explicitly to the unicast destination and the fourth - param of ddsi_add_heartbeat needs to be the guid of the reader */ - ddsi_xmsg_setdst_prd (msg, prd); - // send to all readers in the participant: whether or not the entityid is set affects - // the retransmit requests - ddsi_add_heartbeat (msg, wr, whcst, hbansreq, 0, ddsi_to_entityid (DDSI_ENTITYID_UNKNOWN), issync); - } - - /* It is possible that the encoding removed the submessage(s). */ - if (ddsi_xmsg_size(msg) == 0) - { - ddsi_xmsg_free (msg); - msg = NULL; - } - - writer_hbcontrol_note_hb (wr, tnow, hbansreq); - return msg; -} - -static int writer_hbcontrol_ack_required_generic (const struct ddsi_writer *wr, const struct ddsi_whc_state *whcst, ddsrt_mtime_t tlast, ddsrt_mtime_t tnow, int piggyback) -{ - struct ddsi_domaingv const * const gv = wr->e.gv; - struct ddsi_hbcontrol const * const hbc = &wr->hbcontrol; - const int64_t hb_intv_ack = gv->config.const_hb_intv_sched; - assert(wr->heartbeat_xevent != NULL && whcst != NULL); - - if (piggyback) - { - /* If it is likely that a heartbeat requiring an ack will go out - shortly after the sample was written, it is better to piggyback - it onto the sample. The current idea is that a write shortly - before the next heartbeat will go out should have one - piggybacked onto it, so that the scheduled heartbeat can be - suppressed. */ - if (tnow.v >= tlast.v + 4 * hb_intv_ack / 5) - return 2; - } - else - { - /* For heartbeat events use a slightly longer interval */ - if (tnow.v >= tlast.v + hb_intv_ack) - return 2; - } - - if (whcst->unacked_bytes >= wr->whc_low + (wr->whc_high - wr->whc_low) / 2) - { - if (tnow.v >= hbc->t_of_last_ackhb.v + gv->config.const_hb_intv_sched_min) - return 2; - else if (tnow.v >= hbc->t_of_last_ackhb.v + gv->config.const_hb_intv_min) - return 1; - } - - return 0; -} - -int ddsi_writer_hbcontrol_ack_required (const struct ddsi_writer *wr, const struct ddsi_whc_state *whcst, ddsrt_mtime_t tnow) -{ - struct ddsi_hbcontrol const * const hbc = &wr->hbcontrol; - return writer_hbcontrol_ack_required_generic (wr, whcst, hbc->t_of_last_write, tnow, 0); -} - -struct ddsi_xmsg *ddsi_writer_hbcontrol_piggyback (struct ddsi_writer *wr, const struct ddsi_whc_state *whcst, ddsrt_mtime_t tnow, uint32_t packetid, int *hbansreq) -{ - struct ddsi_hbcontrol * const hbc = &wr->hbcontrol; - uint32_t last_packetid; - ddsrt_mtime_t tlast; - ddsrt_mtime_t t_of_last_hb; - struct ddsi_xmsg *msg; - - tlast = hbc->t_of_last_write; - last_packetid = hbc->last_packetid; - t_of_last_hb = hbc->t_of_last_hb; - - hbc->t_of_last_write = tnow; - hbc->last_packetid = packetid; - - /* Update statistics, intervals, scheduling of heartbeat event, - &c. -- there's no real difference between async and sync so we - reuse the async version. */ - ddsi_writer_hbcontrol_note_asyncwrite (wr, tnow); - - *hbansreq = writer_hbcontrol_ack_required_generic (wr, whcst, tlast, tnow, 1); - if (*hbansreq >= 2) { - /* So we force a heartbeat in - but we also rely on our caller to - send the packet out */ - msg = ddsi_writer_hbcontrol_create_heartbeat (wr, whcst, tnow, *hbansreq, 1); - } else if (last_packetid != packetid && tnow.v - t_of_last_hb.v > DDS_USECS (100)) { - /* If we crossed a packet boundary since the previous write, - piggyback a heartbeat, with *hbansreq determining whether or - not an ACK is needed. We don't force the packet out either: - this is just to ensure a regular flow of ACKs for cleaning up - the WHC & for allowing readers to NACK missing samples. - - Still rate-limit: if there are new readers that haven't sent an - an ACK yet, the FINAL flag will be cleared and so we get an ACK - storm if writing at a high rate without batching which eats up - a *large* amount of time because there are out-of-order readers - present. */ - msg = ddsi_writer_hbcontrol_create_heartbeat (wr, whcst, tnow, *hbansreq, 1); - } else { - *hbansreq = 0; - msg = NULL; - } - - if (msg) - { - if (ddsrt_avl_is_empty (&wr->readers)) - { - ETRACE (wr, "heartbeat(wr "PGUIDFMT"%s) piggybacked, resched in %g s (min-ack [none], avail-seq %"PRIu64", xmit %"PRIu64")\n", - PGUID (wr->e.guid), - *hbansreq ? "" : " final", - (hbc->tsched.v == DDS_NEVER) ? INFINITY : (double) (hbc->tsched.v - tnow.v) / 1e9, - whcst->max_seq, ddsi_writer_read_seq_xmit(wr)); - } - else - { - ETRACE (wr, "heartbeat(wr "PGUIDFMT"%s) piggybacked, resched in %g s (min-ack %"PRIu64"%s, avail-seq %"PRIu64", xmit %"PRIu64")\n", - PGUID (wr->e.guid), - *hbansreq ? "" : " final", - (hbc->tsched.v == DDS_NEVER) ? INFINITY : (double) (hbc->tsched.v - tnow.v) / 1e9, - root_rdmatch (wr)->min_seq, - root_rdmatch (wr)->all_have_replied_to_hb ? "" : "!", - whcst->max_seq, ddsi_writer_read_seq_xmit(wr)); - } - } - - return msg; -} - -#ifdef DDS_HAS_SECURITY -struct ddsi_xmsg *ddsi_writer_hbcontrol_p2p(struct ddsi_writer *wr, const struct ddsi_whc_state *whcst, int hbansreq, struct ddsi_proxy_reader *prd) -{ - struct ddsi_domaingv const * const gv = wr->e.gv; - struct ddsi_xmsg *msg; - - ASSERT_MUTEX_HELD (&wr->e.lock); - assert (wr->reliable); - - if ((msg = ddsi_xmsg_new (gv->xmsgpool, &wr->e.guid, wr->c.pp, sizeof (ddsi_rtps_info_ts_t) + sizeof (ddsi_rtps_heartbeat_t), DDSI_XMSG_KIND_CONTROL)) == NULL) - return NULL; - - ETRACE (wr, "writer_hbcontrol_p2p: wr "PGUIDFMT" unicasting to prd "PGUIDFMT" ", PGUID (wr->e.guid), PGUID (prd->e.guid)); - if (ddsrt_avl_is_empty (&wr->readers)) - { - ETRACE (wr, "(rel-prd %d seq-eq-max [none] seq %"PRIu64")\n", wr->num_reliable_readers, wr->seq); - } - else - { - ETRACE (wr, "(rel-prd %d seq-eq-max %d seq %"PRIu64" maxseq %"PRIu64")\n", - wr->num_reliable_readers, - (int32_t) root_rdmatch (wr)->num_reliable_readers_where_seq_equals_max, - wr->seq, - root_rdmatch (wr)->max_seq); - } - - /* set the destination explicitly to the unicast destination and the fourth - param of ddsi_add_heartbeat needs to be the guid of the reader */ - ddsi_xmsg_setdst_prd (msg, prd); - ddsi_add_heartbeat (msg, wr, whcst, hbansreq, 0, prd->e.guid.entityid, 1); - - if (ddsi_xmsg_size(msg) == 0) - { - ddsi_xmsg_free (msg); - msg = NULL; - } - - return msg; -} -#endif - -void ddsi_add_heartbeat (struct ddsi_xmsg *msg, struct ddsi_writer *wr, const struct ddsi_whc_state *whcst, int hbansreq, int hbliveliness, ddsi_entityid_t dst, int issync) -{ - struct ddsi_domaingv const * const gv = wr->e.gv; - struct ddsi_xmsg_marker sm_marker; - ddsi_rtps_heartbeat_t * hb; - ddsi_seqno_t max, min; - - ASSERT_MUTEX_HELD (&wr->e.lock); - - assert (wr->reliable); - assert (hbansreq >= 0); - assert (hbliveliness >= 0); - - if (gv->config.meas_hb_to_ack_latency) - { - /* If configured to measure heartbeat-to-ack latency, we must add - a timestamp. No big deal if it fails. */ - ddsi_xmsg_add_timestamp (msg, ddsrt_time_wallclock ()); - } - - hb = ddsi_xmsg_append (msg, &sm_marker, sizeof (ddsi_rtps_heartbeat_t)); - ddsi_xmsg_submsg_init (msg, sm_marker, DDSI_RTPS_SMID_HEARTBEAT); - - if (!hbansreq) - hb->smhdr.flags |= DDSI_HEARTBEAT_FLAG_FINAL; - if (hbliveliness) - hb->smhdr.flags |= DDSI_HEARTBEAT_FLAG_LIVELINESS; - - hb->readerId = ddsi_hton_entityid (dst); - hb->writerId = ddsi_hton_entityid (wr->e.guid.entityid); - if (DDSI_WHCST_ISEMPTY(whcst)) - { - max = wr->seq; - min = max + 1; - } - else - { - /* If data present in WHC, wr->seq > 0, but xmit_seq possibly still 0 */ - min = whcst->min_seq; - max = wr->seq; - const ddsi_seqno_t seq_xmit = ddsi_writer_read_seq_xmit (wr); - assert (min <= max); - /* Informing readers of samples that haven't even been transmitted makes little sense, - but for transient-local data, we let the first heartbeat determine the time at which - we trigger wait_for_historical_data, so it had better be correct */ - if (!issync && seq_xmit < max && !wr->handle_as_transient_local) - { - /* When: queue data ; queue heartbeat ; transmit data ; update - seq_xmit, max may be < min. But we must never advertise the - minimum available sequence number incorrectly! */ - if (seq_xmit >= min) { - /* Advertise some but not all data */ - max = seq_xmit; - } else { - /* Advertise no data yet */ - max = min - 1; - } - } - } - hb->firstSN = ddsi_to_seqno (min); - hb->lastSN = ddsi_to_seqno (max); - - hb->count = wr->hbcount++; - - ddsi_xmsg_submsg_setnext (msg, sm_marker); - ddsi_security_encode_datawriter_submsg(msg, sm_marker, wr); -} - static dds_return_t ddsi_create_fragment_message_simple (struct ddsi_writer *wr, ddsi_seqno_t seq, struct ddsi_serdata *serdata, struct ddsi_xmsg **pmsg) { #define TEST_KEYHASH 0 From c5978f929ba5fa2911ee8959bb52dea72adad5d3 Mon Sep 17 00:00:00 2001 From: Erik Boasson Date: Tue, 21 Mar 2023 13:36:30 +0100 Subject: [PATCH 03/11] Consolidate ACKNACK related code Signed-off-by: Erik Boasson --- src/core/ddsi/src/ddsi__acknack.h | 8 +- src/core/ddsi/src/ddsi__endpoint.h | 1 + src/core/ddsi/src/ddsi_acknack.c | 120 +++++++++++++++++++++- src/core/ddsi/src/ddsi_endpoint_match.c | 130 +----------------------- 4 files changed, 129 insertions(+), 130 deletions(-) diff --git a/src/core/ddsi/src/ddsi__acknack.h b/src/core/ddsi/src/ddsi__acknack.h index 294d468657..b497a3be8e 100644 --- a/src/core/ddsi/src/ddsi__acknack.h +++ b/src/core/ddsi/src/ddsi__acknack.h @@ -56,8 +56,12 @@ struct ddsi_add_acknack_info { /** @component incoming_rtps */ void ddsi_sched_acknack_if_needed (struct ddsi_xevent *ev, struct ddsi_proxy_writer *pwr, struct ddsi_pwr_rd_match *rwn, ddsrt_mtime_t tnow, bool avoid_suppressed_nack); -/** @component incoming_rtps */ -struct ddsi_xmsg *ddsi_make_and_resched_acknack (struct ddsi_xevent *ev, struct ddsi_proxy_writer *pwr, struct ddsi_pwr_rd_match *rwn, ddsrt_mtime_t tnow, bool avoid_suppressed_nack); +struct ddsi_xevent_acknack_cb_arg { + ddsi_guid_t pwr_guid; + ddsi_guid_t rd_guid; +}; + +void ddsi_xevent_acknack_cb (struct ddsi_domaingv *gv, struct ddsi_xevent *ev, struct ddsi_xpack *xp, void *varg, ddsrt_mtime_t tnow); #if defined (__cplusplus) } diff --git a/src/core/ddsi/src/ddsi__endpoint.h b/src/core/ddsi/src/ddsi__endpoint.h index 0c62c17a4e..cf3c74998b 100644 --- a/src/core/ddsi/src/ddsi__endpoint.h +++ b/src/core/ddsi/src/ddsi__endpoint.h @@ -27,6 +27,7 @@ struct ddsi_participant; struct ddsi_type_pair; struct ddsi_entity_common; struct ddsi_endpoint_common; +struct ddsi_alive_state; struct dds_qos; struct ddsi_ldur_fhnode { diff --git a/src/core/ddsi/src/ddsi_acknack.c b/src/core/ddsi/src/ddsi_acknack.c index f2dcb3473d..f77579d5cb 100644 --- a/src/core/ddsi/src/ddsi_acknack.c +++ b/src/core/ddsi/src/ddsi_acknack.c @@ -20,6 +20,7 @@ #include "ddsi__bitset.h" #include "ddsi__acknack.h" #include "ddsi__entity_index.h" +#include "ddsi__proxy_endpoint.h" #include "ddsi__endpoint_match.h" #include "ddsi__security_omg.h" #include "ddsi__xqos.h" @@ -369,7 +370,7 @@ void ddsi_sched_acknack_if_needed (struct ddsi_xevent *ev, struct ddsi_proxy_wri (void) ddsi_resched_xevent_if_earlier (ev, tnow); } -struct ddsi_xmsg *ddsi_make_and_resched_acknack (struct ddsi_xevent *ev, struct ddsi_proxy_writer *pwr, struct ddsi_pwr_rd_match *rwn, ddsrt_mtime_t tnow, bool avoid_suppressed_nack) +static struct ddsi_xmsg *make_and_resched_acknack (struct ddsi_xevent *ev, struct ddsi_proxy_writer *pwr, struct ddsi_pwr_rd_match *rwn, ddsrt_mtime_t tnow, bool avoid_suppressed_nack) { struct ddsi_domaingv * const gv = pwr->e.gv; struct ddsi_xmsg *msg; @@ -503,3 +504,120 @@ struct ddsi_xmsg *ddsi_make_and_resched_acknack (struct ddsi_xevent *ev, struct GVTRACE ("send acknack(rd "PGUIDFMT" -> pwr "PGUIDFMT")\n", PGUID (rwn->rd_guid), PGUID (pwr->e.guid)); return msg; } + +static dds_duration_t preemptive_acknack_interval (const struct ddsi_pwr_rd_match *rwn) +{ + if (rwn->t_last_ack.v < rwn->tcreate.v) + return 0; + else + { + const dds_duration_t age = rwn->t_last_ack.v - rwn->tcreate.v; + if (age <= DDS_SECS (10)) + return DDS_SECS (1); + else if (age <= DDS_SECS (60)) + return DDS_SECS (2); + else if (age <= DDS_SECS (120)) + return DDS_SECS (5); + else + return DDS_SECS (10); + } +} + +static struct ddsi_xmsg *make_preemptive_acknack (struct ddsi_xevent *ev, struct ddsi_proxy_writer *pwr, struct ddsi_pwr_rd_match *rwn, ddsrt_mtime_t tnow) +{ + const dds_duration_t old_intv = preemptive_acknack_interval (rwn); + if (tnow.v < ddsrt_mtime_add_duration (rwn->t_last_ack, old_intv).v) + { + (void) ddsi_resched_xevent_if_earlier (ev, ddsrt_mtime_add_duration (rwn->t_last_ack, old_intv)); + return NULL; + } + + struct ddsi_domaingv * const gv = pwr->e.gv; + struct ddsi_participant *pp = NULL; + if (ddsi_omg_proxy_participant_is_secure (pwr->c.proxypp)) + { + struct ddsi_reader *rd = ddsi_entidx_lookup_reader_guid (gv->entity_index, &rwn->rd_guid); + if (rd) + pp = rd->c.pp; + } + + struct ddsi_xmsg *msg; + if ((msg = ddsi_xmsg_new (gv->xmsgpool, &rwn->rd_guid, pp, DDSI_ACKNACK_SIZE_MAX, DDSI_XMSG_KIND_CONTROL)) == NULL) + { + // if out of memory, try again later + (void) ddsi_resched_xevent_if_earlier (ev, ddsrt_mtime_add_duration (tnow, old_intv)); + return NULL; + } + + ddsi_xmsg_setdst_pwr (msg, pwr); + struct ddsi_xmsg_marker sm_marker; + ddsi_rtps_acknack_t *an = ddsi_xmsg_append (msg, &sm_marker, DDSI_ACKNACK_SIZE (0)); + ddsi_xmsg_submsg_init (msg, sm_marker, DDSI_RTPS_SMID_ACKNACK); + an->readerId = ddsi_hton_entityid (rwn->rd_guid.entityid); + an->writerId = ddsi_hton_entityid (pwr->e.guid.entityid); + an->readerSNState.bitmap_base = ddsi_to_seqno (1); + an->readerSNState.numbits = 0; + ddsi_count_t * const countp = + (ddsi_count_t *) ((char *) an + offsetof (ddsi_rtps_acknack_t, bits) + DDSI_SEQUENCE_NUMBER_SET_BITS_SIZE (0)); + *countp = 0; + ddsi_xmsg_submsg_setnext (msg, sm_marker); + ddsi_security_encode_datareader_submsg (msg, sm_marker, pwr, &rwn->rd_guid); + + rwn->t_last_ack = tnow; + const dds_duration_t new_intv = preemptive_acknack_interval (rwn); + (void) ddsi_resched_xevent_if_earlier (ev, ddsrt_mtime_add_duration (rwn->t_last_ack, new_intv)); + + // numbits is always 0 here, so need to print the bitmap + ETRACE (pwr, "acknack "PGUIDFMT" -> "PGUIDFMT": #%"PRIu32":%"PRId64"/%"PRIu32":\n", + PGUID (rwn->rd_guid), PGUID (pwr->e.guid), *countp, + ddsi_from_seqno (an->readerSNState.bitmap_base), an->readerSNState.numbits); + return msg; +} + +void ddsi_xevent_acknack_cb (struct ddsi_domaingv *gv, struct ddsi_xevent *ev, struct ddsi_xpack *xp, void *varg, ddsrt_mtime_t tnow) +{ + /* FIXME: ought to keep track of which NACKs are being generated in + response to a Heartbeat. There is no point in having multiple + readers NACK the data. + + FIXME: ought to determine the set of missing samples (as it does + now), and then check which for of those fragments are available already. + A little snag is that the defragmenter can throw out partial samples in + favour of others, so MUST ensure that the defragmenter won't start + threshing and fail to make progress! */ + struct ddsi_xevent_acknack_cb_arg const * const arg = varg; + struct ddsi_proxy_writer *pwr; + struct ddsi_xmsg *msg; + struct ddsi_pwr_rd_match *rwn; + + if ((pwr = ddsi_entidx_lookup_proxy_writer_guid (gv->entity_index, &arg->pwr_guid)) == NULL) + { + return; + } + + ddsrt_mutex_lock (&pwr->e.lock); + if ((rwn = ddsrt_avl_lookup (&ddsi_pwr_readers_treedef, &pwr->readers, &arg->rd_guid)) == NULL) + { + ddsrt_mutex_unlock (&pwr->e.lock); + return; + } + + if (!pwr->have_seen_heartbeat) + msg = make_preemptive_acknack (ev, pwr, rwn, tnow); + else + msg = make_and_resched_acknack (ev, pwr, rwn, tnow, false); + ddsrt_mutex_unlock (&pwr->e.lock); + + /* ddsi_xpack_addmsg may sleep (for bandwidth-limited channels), so + must be outside the lock */ + if (msg) + { + // a possible result of trying to encode a submessage is that it is removed, + // in which case we may end up with an empty one. + // FIXME: change ddsi_security_encode_datareader_submsg so that it returns this and make it warn_unused_result + if (ddsi_xmsg_size (msg) == 0) + ddsi_xmsg_free (msg); + else + ddsi_xpack_addmsg (xp, msg, 0); + } +} diff --git a/src/core/ddsi/src/ddsi_endpoint_match.c b/src/core/ddsi/src/ddsi_endpoint_match.c index 811fd1a13a..6d56082b83 100644 --- a/src/core/ddsi/src/ddsi_endpoint_match.c +++ b/src/core/ddsi/src/ddsi_endpoint_match.c @@ -33,6 +33,7 @@ #include "ddsi__typelib.h" #include "ddsi__vendor.h" #include "ddsi__lat_estim.h" +#include "ddsi__acknack.h" #ifdef DDS_HAS_TYPE_DISCOVERY #include "ddsi__typelookup.h" #endif @@ -946,131 +947,6 @@ void ddsi_reader_add_local_connection (struct ddsi_reader *rd, struct ddsi_write } } -#include "ddsi__acknack.h" -#include "ddsi__misc.h" - -static dds_duration_t preemptive_acknack_interval (const struct ddsi_pwr_rd_match *rwn) -{ - if (rwn->t_last_ack.v < rwn->tcreate.v) - return 0; - else - { - const dds_duration_t age = rwn->t_last_ack.v - rwn->tcreate.v; - if (age <= DDS_SECS (10)) - return DDS_SECS (1); - else if (age <= DDS_SECS (60)) - return DDS_SECS (2); - else if (age <= DDS_SECS (120)) - return DDS_SECS (5); - else - return DDS_SECS (10); - } -} - -static struct ddsi_xmsg *make_preemptive_acknack (struct ddsi_xevent *ev, struct ddsi_proxy_writer *pwr, struct ddsi_pwr_rd_match *rwn, ddsrt_mtime_t tnow) -{ - const dds_duration_t old_intv = preemptive_acknack_interval (rwn); - if (tnow.v < ddsrt_mtime_add_duration (rwn->t_last_ack, old_intv).v) - { - (void) ddsi_resched_xevent_if_earlier (ev, ddsrt_mtime_add_duration (rwn->t_last_ack, old_intv)); - return NULL; - } - - struct ddsi_domaingv * const gv = pwr->e.gv; - struct ddsi_participant *pp = NULL; - if (ddsi_omg_proxy_participant_is_secure (pwr->c.proxypp)) - { - struct ddsi_reader *rd = ddsi_entidx_lookup_reader_guid (gv->entity_index, &rwn->rd_guid); - if (rd) - pp = rd->c.pp; - } - - struct ddsi_xmsg *msg; - if ((msg = ddsi_xmsg_new (gv->xmsgpool, &rwn->rd_guid, pp, DDSI_ACKNACK_SIZE_MAX, DDSI_XMSG_KIND_CONTROL)) == NULL) - { - // if out of memory, try again later - (void) ddsi_resched_xevent_if_earlier (ev, ddsrt_mtime_add_duration (tnow, old_intv)); - return NULL; - } - - ddsi_xmsg_setdst_pwr (msg, pwr); - struct ddsi_xmsg_marker sm_marker; - ddsi_rtps_acknack_t *an = ddsi_xmsg_append (msg, &sm_marker, DDSI_ACKNACK_SIZE (0)); - ddsi_xmsg_submsg_init (msg, sm_marker, DDSI_RTPS_SMID_ACKNACK); - an->readerId = ddsi_hton_entityid (rwn->rd_guid.entityid); - an->writerId = ddsi_hton_entityid (pwr->e.guid.entityid); - an->readerSNState.bitmap_base = ddsi_to_seqno (1); - an->readerSNState.numbits = 0; - ddsi_count_t * const countp = - (ddsi_count_t *) ((char *) an + offsetof (ddsi_rtps_acknack_t, bits) + DDSI_SEQUENCE_NUMBER_SET_BITS_SIZE (0)); - *countp = 0; - ddsi_xmsg_submsg_setnext (msg, sm_marker); - ddsi_security_encode_datareader_submsg (msg, sm_marker, pwr, &rwn->rd_guid); - - rwn->t_last_ack = tnow; - const dds_duration_t new_intv = preemptive_acknack_interval (rwn); - (void) ddsi_resched_xevent_if_earlier (ev, ddsrt_mtime_add_duration (rwn->t_last_ack, new_intv)); - - // numbits is always 0 here, so need to print the bitmap - ETRACE (pwr, "acknack "PGUIDFMT" -> "PGUIDFMT": #%"PRIu32":%"PRId64"/%"PRIu32":\n", - PGUID (rwn->rd_guid), PGUID (pwr->e.guid), *countp, - ddsi_from_seqno (an->readerSNState.bitmap_base), an->readerSNState.numbits); - return msg; -} - -struct handle_xevk_acknack_arg { - ddsi_guid_t pwr_guid; - ddsi_guid_t rd_guid; -}; - -static void handle_xevk_acknack (struct ddsi_domaingv *gv, struct ddsi_xevent *ev, struct ddsi_xpack *xp, void *varg, ddsrt_mtime_t tnow) -{ - /* FIXME: ought to keep track of which NACKs are being generated in - response to a Heartbeat. There is no point in having multiple - readers NACK the data. - - FIXME: ought to determine the set of missing samples (as it does - now), and then check which for of those fragments are available already. - A little snag is that the defragmenter can throw out partial samples in - favour of others, so MUST ensure that the defragmenter won't start - threshing and fail to make progress! */ - struct handle_xevk_acknack_arg const * const arg = varg; - struct ddsi_proxy_writer *pwr; - struct ddsi_xmsg *msg; - struct ddsi_pwr_rd_match *rwn; - - if ((pwr = ddsi_entidx_lookup_proxy_writer_guid (gv->entity_index, &arg->pwr_guid)) == NULL) - { - return; - } - - ddsrt_mutex_lock (&pwr->e.lock); - if ((rwn = ddsrt_avl_lookup (&ddsi_pwr_readers_treedef, &pwr->readers, &arg->rd_guid)) == NULL) - { - ddsrt_mutex_unlock (&pwr->e.lock); - return; - } - - if (!pwr->have_seen_heartbeat) - msg = make_preemptive_acknack (ev, pwr, rwn, tnow); - else - msg = ddsi_make_and_resched_acknack (ev, pwr, rwn, tnow, false); - ddsrt_mutex_unlock (&pwr->e.lock); - - /* ddsi_xpack_addmsg may sleep (for bandwidth-limited channels), so - must be outside the lock */ - if (msg) - { - // a possible result of trying to encode a submessage is that it is removed, - // in which case we may end up with an empty one. - // FIXME: change ddsi_security_encode_datareader_submsg so that it returns this and make it warn_unused_result - if (ddsi_xmsg_size (msg) == 0) - ddsi_xmsg_free (msg); - else - ddsi_xpack_addmsg (xp, msg, 0); - } -} - void ddsi_proxy_writer_add_connection (struct ddsi_proxy_writer *pwr, struct ddsi_reader *rd, ddsrt_mtime_t tnow, ddsi_count_t init_count, int64_t crypto_handle) { struct ddsi_pwr_rd_match *m = ddsrt_malloc (sizeof (*m)); @@ -1202,8 +1078,8 @@ void ddsi_proxy_writer_add_connection (struct ddsi_proxy_writer *pwr, struct dds const ddsrt_mtime_t tsched = use_iceoryx ? DDSRT_MTIME_NEVER : ddsrt_mtime_add_duration (tnow, pwr->e.gv->config.preemptive_ack_delay); { - struct handle_xevk_acknack_arg arg = { .pwr_guid = pwr->e.guid, .rd_guid = rd->e.guid }; - m->acknack_xevent = ddsi_qxev_callback (pwr->evq, tsched, handle_xevk_acknack, &arg, sizeof (arg), false); + struct ddsi_xevent_acknack_cb_arg arg = { .pwr_guid = pwr->e.guid, .rd_guid = rd->e.guid }; + m->acknack_xevent = ddsi_qxev_callback (pwr->evq, tsched, ddsi_xevent_acknack_cb, &arg, sizeof (arg), false); } m->u.not_in_sync.reorder = ddsi_reorder_new (&pwr->e.gv->logconfig, DDSI_REORDER_MODE_NORMAL, secondary_reorder_maxsamples, pwr->e.gv->config.late_ack_mode); From e61bc8a1f4bc02283c8e05e62e6e56d060a69a17 Mon Sep 17 00:00:00 2001 From: Erik Boasson Date: Tue, 21 Mar 2023 15:23:45 +0100 Subject: [PATCH 04/11] Move "entityid" events out of ddsi_xevent They really are no more than conditional queueing of a particular message, and that is all intimately tied to proxy endpoints and the matching thereof. Constructing the message in the proxy endpoint code and simply queueing it is arguably cleaner, and it removes all dependencies on (proxy) entity implementation details from the event queue. Signed-off-by: Erik Boasson --- src/core/ddsi/src/ddsi__proxy_endpoint.h | 5 + src/core/ddsi/src/ddsi__xevent.h | 6 - src/core/ddsi/src/ddsi_endpoint_match.c | 4 +- src/core/ddsi/src/ddsi_proxy_endpoint.c | 32 ++++- src/core/ddsi/src/ddsi_xevent.c | 154 ++++------------------- 5 files changed, 64 insertions(+), 137 deletions(-) diff --git a/src/core/ddsi/src/ddsi__proxy_endpoint.h b/src/core/ddsi/src/ddsi__proxy_endpoint.h index b07891ed5a..8058ae97eb 100644 --- a/src/core/ddsi/src/ddsi__proxy_endpoint.h +++ b/src/core/ddsi/src/ddsi__proxy_endpoint.h @@ -49,6 +49,11 @@ struct ddsi_entity_common *ddsi_entity_common_from_proxy_endpoint_common (const /** @component ddsi_proxy_endpoint */ bool ddsi_is_proxy_endpoint (const struct ddsi_entity_common *e); +/** @component timed_events */ +void ddsi_send_entityid_to_pwr (struct ddsi_proxy_writer *pwr, const ddsi_guid_t *guid); + +/** @component timed_events */ +void ddsi_send_entityid_to_prd (struct ddsi_proxy_reader *prd, const ddsi_guid_t *guid); /** * @brief To create a new proxy writer diff --git a/src/core/ddsi/src/ddsi__xevent.h b/src/core/ddsi/src/ddsi__xevent.h index 8d5bdcf4d7..633cf81324 100644 --- a/src/core/ddsi/src/ddsi__xevent.h +++ b/src/core/ddsi/src/ddsi__xevent.h @@ -56,12 +56,6 @@ void ddsi_xeventq_stop (struct ddsi_xeventq *evq); /** @component timed_events */ void ddsi_qxev_msg (struct ddsi_xeventq *evq, struct ddsi_xmsg *msg); -/** @component timed_events */ -void ddsi_qxev_pwr_entityid (struct ddsi_proxy_writer * pwr, const ddsi_guid_t *guid); - -/** @component timed_events */ -void ddsi_qxev_prd_entityid (struct ddsi_proxy_reader * prd, const ddsi_guid_t *guid); - /** @component timed_events */ void ddsi_qxev_nt_callback (struct ddsi_xeventq *evq, void (*cb) (void *arg), void *arg); diff --git a/src/core/ddsi/src/ddsi_endpoint_match.c b/src/core/ddsi/src/ddsi_endpoint_match.c index 6d56082b83..f3e6d0b288 100644 --- a/src/core/ddsi/src/ddsi_endpoint_match.c +++ b/src/core/ddsi/src/ddsi_endpoint_match.c @@ -1102,7 +1102,7 @@ void ddsi_proxy_writer_add_connection (struct ddsi_proxy_writer *pwr, struct dds #endif ddsrt_mutex_unlock (&pwr->e.lock); - ddsi_qxev_pwr_entityid (pwr, &rd->e.guid); + ddsi_send_entityid_to_pwr (pwr, &rd->e.guid); ELOGDISC (pwr, "\n"); return; @@ -1141,7 +1141,7 @@ void ddsi_proxy_reader_add_connection (struct ddsi_proxy_reader *prd, struct dds PGUID (wr->e.guid), PGUID (prd->e.guid)); ddsrt_avl_insert_ipath (&ddsi_prd_writers_treedef, &prd->writers, m, &path); ddsrt_mutex_unlock (&prd->e.lock); - ddsi_qxev_prd_entityid (prd, &wr->e.guid); + ddsi_send_entityid_to_prd (prd, &wr->e.guid); } } diff --git a/src/core/ddsi/src/ddsi_proxy_endpoint.c b/src/core/ddsi/src/ddsi_proxy_endpoint.c index a60d0552d8..c5afaa7ccd 100644 --- a/src/core/ddsi/src/ddsi_proxy_endpoint.c +++ b/src/core/ddsi/src/ddsi_proxy_endpoint.c @@ -170,6 +170,34 @@ bool ddsi_is_proxy_endpoint (const struct ddsi_entity_common *e) } #endif /* DDS_HAS_TYPE_DISCOVERY */ +void ddsi_send_entityid_to_prd (struct ddsi_proxy_reader *prd, const ddsi_guid_t *guid) +{ + /* For connected transports, may need to establish and identify connection */ + struct ddsi_domaingv * const gv = prd->e.gv; + if (!gv->m_factory->m_connless) + { + GVTRACE (" ddsi_send_entityid_to_prd (%"PRIx32":%"PRIx32":%"PRIx32")\n", PGUIDPREFIX (guid->prefix)); + struct ddsi_xmsg *msg = ddsi_xmsg_new (gv->xmsgpool, guid, NULL, sizeof (ddsi_rtps_entityid_t), DDSI_XMSG_KIND_CONTROL); + ddsi_xmsg_setdst_prd (msg, prd); + ddsi_xmsg_add_entityid (msg); + ddsi_qxev_msg (gv->xevents, msg); + } +} + +void ddsi_send_entityid_to_pwr (struct ddsi_proxy_writer *pwr, const ddsi_guid_t *guid) +{ + /* For connected transports, may need to establish and identify connection */ + struct ddsi_domaingv * const gv = pwr->e.gv; + if (!gv->m_factory->m_connless) + { + GVTRACE (" ddsi_send_entityid_to_pwr (%"PRIx32":%"PRIx32":%"PRIx32")\n", PGUIDPREFIX (guid->prefix)); + struct ddsi_xmsg *msg = ddsi_xmsg_new (gv->xmsgpool, guid, NULL, sizeof (ddsi_rtps_entityid_t), DDSI_XMSG_KIND_CONTROL); + ddsi_xmsg_setdst_pwr (msg, pwr); + ddsi_xmsg_add_entityid (msg); + ddsi_qxev_msg (gv->xevents, msg); + } +} + /* PROXY-WRITER ----------------------------------------------------- */ static enum ddsi_reorder_mode get_proxy_writer_reorder_mode(const ddsi_entityid_t pwr_entityid, int isreliable) @@ -338,7 +366,7 @@ void ddsi_update_proxy_writer (struct ddsi_proxy_writer *pwr, ddsi_seqno_t seq, rd = ddsi_entidx_lookup_reader_guid (pwr->e.gv->entity_index, &m->rd_guid); if (rd) { - ddsi_qxev_pwr_entityid (pwr, &rd->e.guid); + ddsi_send_entityid_to_pwr (pwr, &rd->e.guid); } m = ddsrt_avl_iter_next (&iter); } @@ -638,7 +666,7 @@ void ddsi_update_proxy_reader (struct ddsi_proxy_reader *prd, ddsi_seqno_t seq, ddsrt_mutex_lock (&wr->e.lock); ddsi_rebuild_writer_addrset (wr); ddsrt_mutex_unlock (&wr->e.lock); - ddsi_qxev_prd_entityid (prd, &wr->e.guid); + ddsi_send_entityid_to_prd (prd, &wr->e.guid); } wrguid = guid_next; ddsrt_mutex_lock (&prd->e.lock); diff --git a/src/core/ddsi/src/ddsi_xevent.c b/src/core/ddsi/src/ddsi_xevent.c index 994e3490ae..258ad6fd0d 100644 --- a/src/core/ddsi/src/ddsi_xevent.c +++ b/src/core/ddsi/src/ddsi_xevent.c @@ -23,9 +23,7 @@ #include "ddsi__xevent.h" #include "ddsi__thread.h" #include "ddsi__transmit.h" -#include "ddsi__entity.h" #include "ddsi__xmsg.h" -#include "ddsi__proxy_endpoint.h" #include "ddsi__tran.h" #include "ddsi__sysdeps.h" @@ -63,7 +61,6 @@ enum ddsi_xeventkind_nt XEVK_MSG, XEVK_MSG_REXMIT, XEVK_MSG_REXMIT_NOMERGE, - XEVK_ENTITYID, XEVK_NT_CALLBACK }; @@ -87,10 +84,6 @@ struct ddsi_xevent_nt size_t queued_rexmit_bytes; ddsrt_avl_node_t msg_avlnode; } msg_rexmit; /* and msg_rexmit_nomerge */ - struct { - /* xmsg is self-contained / relies on reference counts */ - struct ddsi_xmsg *msg; - } entityid; struct { void (*cb) (void *arg); void *arg; @@ -103,6 +96,7 @@ struct ddsi_xeventq { ddsrt_avl_tree_t msg_xevents; struct ddsi_xevent_nt *non_timed_xmit_list_oldest; struct ddsi_xevent_nt *non_timed_xmit_list_newest; /* undefined if ..._oldest == NULL */ + size_t non_timed_xmit_list_length; size_t queued_rexmit_bytes; size_t queued_rexmit_msgs; size_t max_queued_rexmit_bytes; @@ -133,17 +127,13 @@ static int compare_xevent_tsched (const void *va, const void *vb) return (a->tsched.v == b->tsched.v) ? 0 : (a->tsched.v < b->tsched.v) ? -1 : 1; } -static void update_rexmit_counts (struct ddsi_xeventq *evq, struct ddsi_xevent_nt *ev) +static void update_rexmit_counts (struct ddsi_xeventq *evq, size_t msg_rexmit_queued_rexmit_bytes) { -#if 0 - EVQTRACE ("ZZZ(%p,%"PRIuSIZE")", (void *) ev, ev->u.msg_rexmit.queued_rexmit_bytes); -#endif - assert (ev->kind == XEVK_MSG_REXMIT || ev->kind == XEVK_MSG_REXMIT_NOMERGE); - assert (ev->u.msg_rexmit.queued_rexmit_bytes <= evq->queued_rexmit_bytes); + assert (msg_rexmit_queued_rexmit_bytes <= evq->queued_rexmit_bytes); assert (evq->queued_rexmit_msgs > 0); - evq->queued_rexmit_bytes -= ev->u.msg_rexmit.queued_rexmit_bytes; + evq->queued_rexmit_bytes -= msg_rexmit_queued_rexmit_bytes; evq->queued_rexmit_msgs--; - evq->cum_rexmit_bytes += ev->u.msg_rexmit.queued_rexmit_bytes; + evq->cum_rexmit_bytes += msg_rexmit_queued_rexmit_bytes; } #if 0 @@ -206,6 +196,7 @@ static void add_to_non_timed_xmit_list (struct ddsi_xeventq *evq, struct ddsi_xe evq->non_timed_xmit_list_newest->listnode.next = ev; } evq->non_timed_xmit_list_newest = ev; + evq->non_timed_xmit_list_length++; if (ev->kind == XEVK_MSG_REXMIT) remember_msg (evq, ev); @@ -220,6 +211,7 @@ static struct ddsi_xevent_nt *getnext_from_non_timed_xmit_list (struct ddsi_xev struct ddsi_xevent_nt *ev = evq->non_timed_xmit_list_oldest; if (ev != NULL) { + evq->non_timed_xmit_list_length--; evq->non_timed_xmit_list_oldest = ev->listnode.next; if (ev->kind == XEVK_MSG_REXMIT) @@ -237,22 +229,6 @@ static int non_timed_xmit_list_is_empty (struct ddsi_xeventq *evq) return (evq->non_timed_xmit_list_oldest == NULL); } -static int compute_non_timed_xmit_list_size (struct ddsi_xeventq *evq) -{ - /* returns how many "non-timed" xevents are pending by counting the - number of events in the list -- it'd be easy to compute the - length incrementally in the add_... and next_... functions, but - it isn't really being used anywhere, so why bother? */ - struct ddsi_xevent_nt *current = evq->non_timed_xmit_list_oldest; - int i = 0; - while (current) - { - current = current->listnode.next; - i++; - } - return i; -} - #ifndef NDEBUG static int nontimed_xevent_in_queue (struct ddsi_xeventq *evq, struct ddsi_xevent_nt *ev) { @@ -434,7 +410,7 @@ static void qxev_insert_nt (struct ddsi_xevent_nt *ev) struct ddsi_xeventq *evq = ev->evq; ASSERT_MUTEX_HELD (&evq->lock); add_to_non_timed_xmit_list (evq, ev); - EVQTRACE ("non-timed queue now has %d items\n", compute_non_timed_xmit_list_size (evq)); + EVQTRACE (" (%"PRIuSIZE" in queue)\n", evq->non_timed_xmit_list_length); } static int msg_xevents_cmp (const void *a, const void *b) @@ -452,6 +428,7 @@ struct ddsi_xeventq * ddsi_xeventq_new (struct ddsi_domaingv *gv, size_t max_que ddsrt_avl_init (&msg_xevents_treedef, &evq->msg_xevents); evq->non_timed_xmit_list_oldest = NULL; evq->non_timed_xmit_list_newest = NULL; + evq->non_timed_xmit_list_length = 0; evq->terminate = 0; evq->thrst = NULL; evq->max_queued_rexmit_bytes = max_queued_rexmit_bytes; @@ -530,72 +507,39 @@ void ddsi_xeventq_free (struct ddsi_xeventq *evq) /* EVENT QUEUE EVENT HANDLERS ******************************************************/ -static void handle_xevk_msg (struct ddsi_xpack *xp, struct ddsi_xevent_nt *ev) -{ - assert (!nontimed_xevent_in_queue (ev->evq, ev)); - ddsi_xpack_addmsg (xp, ev->u.msg.msg, 0); -} - -static void handle_xevk_msg_rexmit (struct ddsi_xpack *xp, struct ddsi_xevent_nt *ev) +static void handle_nontimed_xevent (struct ddsi_xevent_nt *xev, struct ddsi_xpack *xp) { - struct ddsi_xeventq *evq = ev->evq; - - assert (!nontimed_xevent_in_queue (ev->evq, ev)); - - ddsi_xpack_addmsg (xp, ev->u.msg_rexmit.msg, 0); + /* This function handles the individual xevent irrespective of + whether it is a "timed" or "non-timed" xevent */ + struct ddsi_xeventq *evq = xev->evq; + size_t msg_rexmit_queued_rexmit_bytes = SIZE_MAX; - /* FIXME: less than happy about having to relock the queue for a - little while here */ - ddsrt_mutex_lock (&evq->lock); - update_rexmit_counts (evq, ev); + /* We relinquish the lock while processing the event, but require it + held for administrative work. */ + ASSERT_MUTEX_HELD (&evq->lock); ddsrt_mutex_unlock (&evq->lock); -} - -static void handle_xevk_entityid (struct ddsi_xpack *xp, struct ddsi_xevent_nt *ev) -{ - assert (!nontimed_xevent_in_queue (ev->evq, ev)); - ddsi_xpack_addmsg (xp, ev->u.entityid.msg, 0); -} - -static void handle_individual_xevent_nt (struct ddsi_xevent_nt *xev, struct ddsi_xpack *xp) -{ switch (xev->kind) { case XEVK_MSG: - handle_xevk_msg (xp, xev); + assert (!nontimed_xevent_in_queue (evq, xev)); + ddsi_xpack_addmsg (xp, xev->u.msg.msg, 0); break; case XEVK_MSG_REXMIT: case XEVK_MSG_REXMIT_NOMERGE: - handle_xevk_msg_rexmit (xp, xev); - break; - case XEVK_ENTITYID: - handle_xevk_entityid (xp, xev); + assert (!nontimed_xevent_in_queue (evq, xev)); + ddsi_xpack_addmsg (xp, xev->u.msg_rexmit.msg, 0); + msg_rexmit_queued_rexmit_bytes = xev->u.msg_rexmit.queued_rexmit_bytes; + assert (msg_rexmit_queued_rexmit_bytes < SIZE_MAX); break; case XEVK_NT_CALLBACK: xev->u.callback.cb (xev->u.callback.arg); break; } ddsrt_free (xev); -} - -static void handle_nontimed_xevent (struct ddsi_xevent_nt *xev, struct ddsi_xpack *xp) -{ - /* This function handles the individual xevent irrespective of - whether it is a "timed" or "non-timed" xevent */ - struct ddsi_xeventq *xevq = xev->evq; - - /* We relinquish the lock while processing the event, but require it - held for administrative work. */ - ASSERT_MUTEX_HELD (&xevq->lock); - - assert (xev->evq == xevq); - - ddsrt_mutex_unlock (&xevq->lock); - handle_individual_xevent_nt (xev, xp); - /* non-timed xevents are freed by the handlers */ - ddsrt_mutex_lock (&xevq->lock); - - ASSERT_MUTEX_HELD (&xevq->lock); + ddsrt_mutex_lock (&evq->lock); + if (msg_rexmit_queued_rexmit_bytes < SIZE_MAX) { + update_rexmit_counts (evq, msg_rexmit_queued_rexmit_bytes); + } } static void handle_xevents (struct ddsi_thread_state * const thrst, struct ddsi_xeventq *xevq, struct ddsi_xpack *xp, ddsrt_mtime_t tnow) @@ -750,50 +694,6 @@ void ddsi_qxev_nt_callback (struct ddsi_xeventq *evq, void (*cb) (void *arg), vo ddsrt_mutex_unlock (&evq->lock); } -void ddsi_qxev_prd_entityid (struct ddsi_proxy_reader *prd, const ddsi_guid_t *guid) -{ - struct ddsi_domaingv * const gv = prd->e.gv; - struct ddsi_xmsg *msg; - struct ddsi_xevent_nt *ev; - - /* For connected transports, may need to establish and identify connection */ - - if (! gv->m_factory->m_connless) - { - msg = ddsi_xmsg_new (gv->xmsgpool, guid, NULL, sizeof (ddsi_rtps_entityid_t), DDSI_XMSG_KIND_CONTROL); - ddsi_xmsg_setdst_prd (msg, prd); - GVTRACE (" ddsi_qxev_prd_entityid (%"PRIx32":%"PRIx32":%"PRIx32")\n", PGUIDPREFIX (guid->prefix)); - ddsi_xmsg_add_entityid (msg); - ddsrt_mutex_lock (&gv->xevents->lock); - ev = qxev_common_nt (gv->xevents, XEVK_ENTITYID); - ev->u.entityid.msg = msg; - qxev_insert_nt (ev); - ddsrt_mutex_unlock (&gv->xevents->lock); - } -} - -void ddsi_qxev_pwr_entityid (struct ddsi_proxy_writer *pwr, const ddsi_guid_t *guid) -{ - struct ddsi_domaingv * const gv = pwr->e.gv; - struct ddsi_xmsg *msg; - struct ddsi_xevent_nt *ev; - - /* For connected transports, may need to establish and identify connection */ - - if (! gv->m_factory->m_connless) - { - msg = ddsi_xmsg_new (gv->xmsgpool, guid, NULL, sizeof (ddsi_rtps_entityid_t), DDSI_XMSG_KIND_CONTROL); - ddsi_xmsg_setdst_pwr (msg, pwr); - GVTRACE (" ddsi_qxev_pwr_entityid (%"PRIx32":%"PRIx32":%"PRIx32")\n", PGUIDPREFIX (guid->prefix)); - ddsi_xmsg_add_entityid (msg); - ddsrt_mutex_lock (&pwr->evq->lock); - ev = qxev_common_nt (pwr->evq, XEVK_ENTITYID); - ev->u.entityid.msg = msg; - qxev_insert_nt (ev); - ddsrt_mutex_unlock (&pwr->evq->lock); - } -} - enum ddsi_qxev_msg_rexmit_result ddsi_qxev_msg_rexmit_wrlock_held (struct ddsi_xeventq *evq, struct ddsi_xmsg *msg, int force) { struct ddsi_domaingv * const gv = evq->gv; From 529a35886a9ae112ddb3ab2840daf29742d37fe9 Mon Sep 17 00:00:00 2001 From: Erik Boasson Date: Tue, 21 Mar 2023 16:08:23 +0100 Subject: [PATCH 05/11] Rework (timed) event queue scheduling a bit This moves the "schedule time rounding" into the sleeping a bit longer in ddsrt_cond_waitfor, which should be a slightly cheaper approximation of the old behaviour that's close-enough for a probably never-used feature. It also changes the event handling loop to not re-read the clock on every processed event. This makes the timestamp passed into the handler further behind, but it also means the inner loop handling timed events will stop earlier, and so starvation of non-timed events is less likely to occur. Signed-off-by: Erik Boasson --- src/core/ddsi/src/ddsi_xevent.c | 124 ++++++++++++-------------------- 1 file changed, 44 insertions(+), 80 deletions(-) diff --git a/src/core/ddsi/src/ddsi_xevent.c b/src/core/ddsi/src/ddsi_xevent.c index 258ad6fd0d..07332fe241 100644 --- a/src/core/ddsi/src/ddsi_xevent.c +++ b/src/core/ddsi/src/ddsi_xevent.c @@ -114,7 +114,7 @@ static uint32_t xevent_thread (struct ddsi_xeventq *xevq); static ddsrt_mtime_t earliest_in_xeventq (struct ddsi_xeventq *evq); static int msg_xevents_cmp (const void *a, const void *b); static int compare_xevent_tsched (const void *va, const void *vb); -static void handle_nontimed_xevent (struct ddsi_xevent_nt *xev, struct ddsi_xpack *xp); +static void handle_nontimed_xevent (struct ddsi_xeventq *evq, struct ddsi_xevent_nt *xev, struct ddsi_xpack *xp); static const ddsrt_avl_treedef_t msg_xevents_treedef = DDSRT_AVL_TREEDEF_INITIALIZER_INDKEY (offsetof (struct ddsi_xevent_nt, u.msg_rexmit.msg_avlnode), offsetof (struct ddsi_xevent_nt, u.msg_rexmit.msg), msg_xevents_cmp, 0); @@ -355,24 +355,6 @@ bool ddsi_delete_xevent_pending (struct ddsi_xevent *ev) } #endif -static ddsrt_mtime_t mtime_round_up (ddsrt_mtime_t t, int64_t round) -{ - /* This function rounds up t to the nearest next multiple of round. - t is nanoseconds, round is milliseconds. Avoid functions from - maths libraries to keep code portable */ - assert (t.v >= 0 && round >= 0); - if (round == 0 || t.v == DDS_INFINITY) - return t; - else - { - int64_t remainder = t.v % round; - if (remainder == 0) - return t; - else - return (ddsrt_mtime_t) { t.v + round - remainder }; - } -} - static struct ddsi_xevent_nt *qxev_common_nt (struct ddsi_xeventq *evq, enum ddsi_xeventkind_nt kind) { /* qxev_common_nt is the route by which all non-timed xevents are created. */ @@ -491,7 +473,7 @@ void ddsi_xeventq_free (struct ddsi_xeventq *evq) while (!non_timed_xmit_list_is_empty (evq)) { ddsi_thread_state_awake_to_awake_no_nest (ddsi_lookup_thread_state ()); - handle_nontimed_xevent (getnext_from_non_timed_xmit_list (evq), xp); + handle_nontimed_xevent (evq, getnext_from_non_timed_xmit_list (evq), xp); } ddsrt_mutex_unlock (&evq->lock); ddsi_xpack_send (xp, false); @@ -507,11 +489,36 @@ void ddsi_xeventq_free (struct ddsi_xeventq *evq) /* EVENT QUEUE EVENT HANDLERS ******************************************************/ -static void handle_nontimed_xevent (struct ddsi_xevent_nt *xev, struct ddsi_xpack *xp) +static void handle_timed_xevent (struct ddsi_xeventq *evq, struct ddsi_xevent *xev, struct ddsi_xpack *xp, ddsrt_mtime_t tnow) +{ + /* event rescheduling functions look at xev->tsched to + determine whether it is currently on the heap or not (i.e., + scheduled or not), so set to TSCHED_NEVER to indicate it + currently isn't. */ + xev->tsched.v = DDS_NEVER; + + /* We relinquish the lock while processing the event. */ + if (xev->sync_state == CSS_DONTCARE) + { + ddsrt_mutex_unlock (&evq->lock); + xev->cb.cb (evq->gv, xev, xp, xev->arg, tnow); + ddsrt_mutex_lock (&evq->lock); + } + else + { + xev->sync_state = CSS_EXECUTING; + ddsrt_mutex_unlock (&evq->lock); + xev->cb.cb (evq->gv, xev, xp, xev->arg, tnow); + ddsrt_mutex_lock (&evq->lock); + xev->sync_state = CSS_SCHEDULED; + ddsrt_cond_broadcast (&evq->cond); + } +} + +static void handle_nontimed_xevent (struct ddsi_xeventq *evq, struct ddsi_xevent_nt *xev, struct ddsi_xpack *xp) { /* This function handles the individual xevent irrespective of whether it is a "timed" or "non-timed" xevent */ - struct ddsi_xeventq *evq = xev->evq; size_t msg_rexmit_queued_rexmit_bytes = SIZE_MAX; /* We relinquish the lock while processing the event, but require it @@ -544,8 +551,6 @@ static void handle_nontimed_xevent (struct ddsi_xevent_nt *xev, struct ddsi_xpac static void handle_xevents (struct ddsi_thread_state * const thrst, struct ddsi_xeventq *xevq, struct ddsi_xpack *xp, ddsrt_mtime_t tnow) { - int xeventsToProcess = 1; - ASSERT_MUTEX_HELD (&xevq->lock); assert (ddsi_thread_is_awake ()); @@ -557,71 +562,42 @@ static void handle_xevents (struct ddsi_thread_state * const thrst, struct ddsi_ clock and continue the loop, i.e. test again to see whether any "timed" events are now due. */ - while (xeventsToProcess) - { - while (earliest_in_xeventq(xevq).v <= tnow.v) + bool cont; + do { + cont = false; + while (earliest_in_xeventq (xevq).v <= tnow.v) { struct ddsi_xevent *xev = ddsrt_fibheap_extract_min (&evq_xevents_fhdef, &xevq->xevents); if (xev->tsched.v == TSCHED_DELETE) - { free_xevent (xevq, xev); - } else { - /* event rescheduling functions look at xev->tsched to - determine whether it is currently on the heap or not (i.e., - scheduled or not), so set to TSCHED_NEVER to indicate it - currently isn't. */ - xev->tsched.v = DDS_NEVER; ddsi_thread_state_awake_to_awake_no_nest (thrst); - - /* We relinquish the lock while processing the event. */ - if (xev->sync_state == CSS_DONTCARE) - { - ddsrt_mutex_unlock (&xevq->lock); - xev->cb.cb (xevq->gv, xev, xp, xev->arg, tnow); - ddsrt_mutex_lock (&xevq->lock); - } - else - { - xev->sync_state = CSS_EXECUTING; - ddsrt_mutex_unlock (&xevq->lock); - xev->cb.cb (xevq->gv, xev, xp, xev->arg, tnow); - ddsrt_mutex_lock (&xevq->lock); - xev->sync_state = CSS_SCHEDULED; - ddsrt_cond_broadcast (&xevq->cond); - } + handle_timed_xevent (xevq, xev, xp, tnow); + cont = true; } - - /* Limited-bandwidth channels means events can take a LONG time - to process. So read the clock more often. */ - tnow = ddsrt_time_monotonic (); } if (!non_timed_xmit_list_is_empty (xevq)) { struct ddsi_xevent_nt *xev = getnext_from_non_timed_xmit_list (xevq); ddsi_thread_state_awake_to_awake_no_nest (thrst); - handle_nontimed_xevent (xev, xp); - tnow = ddsrt_time_monotonic (); - } - else - { - xeventsToProcess = 0; + handle_nontimed_xevent (xevq, xev, xp); + cont = true; } - } + tnow = ddsrt_time_monotonic (); + } while (cont); ASSERT_MUTEX_HELD (&xevq->lock); } static uint32_t xevent_thread (struct ddsi_xeventq * xevq) { struct ddsi_thread_state * const thrst = ddsi_lookup_thread_state (); - struct ddsi_xpack *xp; + const dds_duration_t rounding = xevq->gv->config.schedule_time_rounding; ddsrt_mtime_t next_thread_cputime = { 0 }; - xp = ddsi_xpack_new (xevq->gv, false); - + struct ddsi_xpack * const xp = ddsi_xpack_new (xevq->gv, false); ddsrt_mutex_lock (&xevq->lock); while (!xevq->terminate) { @@ -651,14 +627,11 @@ static uint32_t xevent_thread (struct ddsi_xeventq * xevq) } else { - /* Although we assumed instantaneous handling of events, we - don't want to sleep much longer than we have to. With - os_condTimedWait requiring a relative time, we don't have - much choice but to read the clock now */ + /* "Wrong" time-base here ... should make cond_waitfor time-base aware */ tnow = ddsrt_time_monotonic (); if (twakeup.v > tnow.v) { - twakeup.v -= tnow.v; /* ddsrt_cond_waitfor: relative timeout */ + twakeup.v = ddsrt_mtime_add_duration(twakeup, rounding).v - tnow.v; ddsrt_cond_waitfor (&xevq->cond, &xevq->lock, twakeup.v); } } @@ -759,22 +732,13 @@ struct ddsi_xevent *ddsi_qxev_callback (struct ddsi_xeventq *evq, ddsrt_mtime_t { assert (tsched.v != TSCHED_DELETE); struct ddsi_xevent *ev = ddsrt_malloc (sizeof (*ev) + arg_size); - ddsrt_mutex_lock (&evq->lock); - - /* round up the scheduled time if required */ - if (tsched.v != DDS_NEVER && evq->gv->config.schedule_time_rounding != 0) - { - ddsrt_mtime_t tsched_rounded = mtime_round_up (tsched, evq->gv->config.schedule_time_rounding); - EVQTRACE ("rounded event scheduled for %"PRId64" to %"PRId64"\n", tsched.v, tsched_rounded.v); - tsched = tsched_rounded; - } - ev->evq = evq; ev->tsched = tsched; ev->cb.cb = cb; ev->sync_state = sync_on_delete ? CSS_SCHEDULED : CSS_DONTCARE; if (arg_size) // so arg = NULL, arg_size = 0 is allowed memcpy (ev->arg, arg, arg_size); + ddsrt_mutex_lock (&evq->lock); qxev_insert (ev); ddsrt_mutex_unlock (&evq->lock); return ev; From 5d0041765251bb6069c7ac155a753c1702c35a12 Mon Sep 17 00:00:00 2001 From: Erik Boasson Date: Thu, 23 Mar 2023 13:09:15 +0100 Subject: [PATCH 06/11] Deprecate/remove ScheduleTimeRounding option To the best of our knowledge this has not been used for at least a decade and so it is probably better to completely drop it. This commit removes the implementation but does retain the configuration option as a deprecated option so that use of it will cause a warning rather than a hard error. Signed-off-by: Erik Boasson --- docs/manual/config/config_file_reference.rst | 22 ++++---------------- docs/manual/options.md | 18 ++++------------ etc/cyclonedds.rnc | 13 +++--------- etc/cyclonedds.xsd | 15 +++---------- src/core/ddsi/defconfig.c | 6 +++--- src/core/ddsi/include/dds/ddsi/ddsi_config.h | 1 - src/core/ddsi/src/ddsi__cfgelems.h | 6 +++--- src/core/ddsi/src/ddsi_config.c | 7 +++++++ src/core/ddsi/src/ddsi_xevent.c | 3 +-- 9 files changed, 28 insertions(+), 63 deletions(-) diff --git a/docs/manual/config/config_file_reference.rst b/docs/manual/config/config_file_reference.rst index eeec2ecabd..4d47617f71 100644 --- a/docs/manual/config/config_file_reference.rst +++ b/docs/manual/config/config_file_reference.rst @@ -714,7 +714,7 @@ The default value is: ``default`` //CycloneDDS/Domain/Internal ============================ -Children: `//CycloneDDS/Domain/Internal/AccelerateRexmitBlockSize`_, `//CycloneDDS/Domain/Internal/AckDelay`_, `//CycloneDDS/Domain/Internal/AutoReschedNackDelay`_, `//CycloneDDS/Domain/Internal/BuiltinEndpointSet`_, `//CycloneDDS/Domain/Internal/BurstSize`_, `//CycloneDDS/Domain/Internal/ControlTopic`_, `//CycloneDDS/Domain/Internal/DefragReliableMaxSamples`_, `//CycloneDDS/Domain/Internal/DefragUnreliableMaxSamples`_, `//CycloneDDS/Domain/Internal/DeliveryQueueMaxSamples`_, `//CycloneDDS/Domain/Internal/EnableExpensiveChecks`_, `//CycloneDDS/Domain/Internal/GenerateKeyhash`_, `//CycloneDDS/Domain/Internal/HeartbeatInterval`_, `//CycloneDDS/Domain/Internal/LateAckMode`_, `//CycloneDDS/Domain/Internal/LivelinessMonitoring`_, `//CycloneDDS/Domain/Internal/MaxParticipants`_, `//CycloneDDS/Domain/Internal/MaxQueuedRexmitBytes`_, `//CycloneDDS/Domain/Internal/MaxQueuedRexmitMessages`_, `//CycloneDDS/Domain/Internal/MaxSampleSize`_, `//CycloneDDS/Domain/Internal/MeasureHbToAckLatency`_, `//CycloneDDS/Domain/Internal/MonitorPort`_, `//CycloneDDS/Domain/Internal/MultipleReceiveThreads`_, `//CycloneDDS/Domain/Internal/NackDelay`_, `//CycloneDDS/Domain/Internal/PreEmptiveAckDelay`_, `//CycloneDDS/Domain/Internal/PrimaryReorderMaxSamples`_, `//CycloneDDS/Domain/Internal/PrioritizeRetransmit`_, `//CycloneDDS/Domain/Internal/RediscoveryBlacklistDuration`_, `//CycloneDDS/Domain/Internal/RetransmitMerging`_, `//CycloneDDS/Domain/Internal/RetransmitMergingPeriod`_, `//CycloneDDS/Domain/Internal/RetryOnRejectBestEffort`_, `//CycloneDDS/Domain/Internal/SPDPResponseMaxDelay`_, `//CycloneDDS/Domain/Internal/ScheduleTimeRounding`_, `//CycloneDDS/Domain/Internal/SecondaryReorderMaxSamples`_, `//CycloneDDS/Domain/Internal/SocketReceiveBufferSize`_, `//CycloneDDS/Domain/Internal/SocketSendBufferSize`_, `//CycloneDDS/Domain/Internal/SquashParticipants`_, `//CycloneDDS/Domain/Internal/SynchronousDeliveryLatencyBound`_, `//CycloneDDS/Domain/Internal/SynchronousDeliveryPriorityThreshold`_, `//CycloneDDS/Domain/Internal/Test`_, `//CycloneDDS/Domain/Internal/UnicastResponseToSPDPMessages`_, `//CycloneDDS/Domain/Internal/UseMulticastIfMreqn`_, `//CycloneDDS/Domain/Internal/Watermarks`_, `//CycloneDDS/Domain/Internal/WriteBatch`_, `//CycloneDDS/Domain/Internal/WriterLingerDuration`_ +Children: `//CycloneDDS/Domain/Internal/AccelerateRexmitBlockSize`_, `//CycloneDDS/Domain/Internal/AckDelay`_, `//CycloneDDS/Domain/Internal/AutoReschedNackDelay`_, `//CycloneDDS/Domain/Internal/BuiltinEndpointSet`_, `//CycloneDDS/Domain/Internal/BurstSize`_, `//CycloneDDS/Domain/Internal/ControlTopic`_, `//CycloneDDS/Domain/Internal/DefragReliableMaxSamples`_, `//CycloneDDS/Domain/Internal/DefragUnreliableMaxSamples`_, `//CycloneDDS/Domain/Internal/DeliveryQueueMaxSamples`_, `//CycloneDDS/Domain/Internal/EnableExpensiveChecks`_, `//CycloneDDS/Domain/Internal/GenerateKeyhash`_, `//CycloneDDS/Domain/Internal/HeartbeatInterval`_, `//CycloneDDS/Domain/Internal/LateAckMode`_, `//CycloneDDS/Domain/Internal/LivelinessMonitoring`_, `//CycloneDDS/Domain/Internal/MaxParticipants`_, `//CycloneDDS/Domain/Internal/MaxQueuedRexmitBytes`_, `//CycloneDDS/Domain/Internal/MaxQueuedRexmitMessages`_, `//CycloneDDS/Domain/Internal/MaxSampleSize`_, `//CycloneDDS/Domain/Internal/MeasureHbToAckLatency`_, `//CycloneDDS/Domain/Internal/MonitorPort`_, `//CycloneDDS/Domain/Internal/MultipleReceiveThreads`_, `//CycloneDDS/Domain/Internal/NackDelay`_, `//CycloneDDS/Domain/Internal/PreEmptiveAckDelay`_, `//CycloneDDS/Domain/Internal/PrimaryReorderMaxSamples`_, `//CycloneDDS/Domain/Internal/PrioritizeRetransmit`_, `//CycloneDDS/Domain/Internal/RediscoveryBlacklistDuration`_, `//CycloneDDS/Domain/Internal/RetransmitMerging`_, `//CycloneDDS/Domain/Internal/RetransmitMergingPeriod`_, `//CycloneDDS/Domain/Internal/RetryOnRejectBestEffort`_, `//CycloneDDS/Domain/Internal/SPDPResponseMaxDelay`_, `//CycloneDDS/Domain/Internal/SecondaryReorderMaxSamples`_, `//CycloneDDS/Domain/Internal/SocketReceiveBufferSize`_, `//CycloneDDS/Domain/Internal/SocketSendBufferSize`_, `//CycloneDDS/Domain/Internal/SquashParticipants`_, `//CycloneDDS/Domain/Internal/SynchronousDeliveryLatencyBound`_, `//CycloneDDS/Domain/Internal/SynchronousDeliveryPriorityThreshold`_, `//CycloneDDS/Domain/Internal/Test`_, `//CycloneDDS/Domain/Internal/UnicastResponseToSPDPMessages`_, `//CycloneDDS/Domain/Internal/UseMulticastIfMreqn`_, `//CycloneDDS/Domain/Internal/Watermarks`_, `//CycloneDDS/Domain/Internal/WriteBatch`_, `//CycloneDDS/Domain/Internal/WriterLingerDuration`_ The Internal elements deal with a variety of settings that are evolving and that are not necessarily fully supported. For the majority of the Internal settings the functionality is supported, but the right to change the way the options control the functionality is reserved. This includes renaming or moving options. @@ -1249,20 +1249,6 @@ The unit must be specified explicitly. Recognised units: ns, us, ms, s, min, hr, The default value is: ``0 ms`` -.. _`//CycloneDDS/Domain/Internal/ScheduleTimeRounding`: - -//CycloneDDS/Domain/Internal/ScheduleTimeRounding -------------------------------------------------- - -Number-with-unit - -This setting allows the timing of scheduled events to be rounded up so that more events can be handled in a single cycle of the event queue. The default is 0 and causes no rounding at all, i.e. are scheduled exactly, whereas a value of 10ms would mean that events are rounded up to the nearest 10 milliseconds. - -The unit must be specified explicitly. Recognised units: ns, us, ms, s, min, hr, day. - -The default value is: ``0 ms`` - - .. _`//CycloneDDS/Domain/Internal/SecondaryReorderMaxSamples`: //CycloneDDS/Domain/Internal/SecondaryReorderMaxSamples @@ -2660,10 +2646,10 @@ The categorisation of tracing output is incomplete and hence most of the verbosi The default value is: ``none`` .. - generated from ddsi_config.h[d0e749e6d58d560b8172dcb0cb109d6e5f6897ba] + generated from ddsi_config.h[af204240792218d7541714019cac9ae65b87878f] generated from ddsi__cfgunits.h[be1b976c6e9466472b0c331487c05180ec1052d4] - generated from ddsi__cfgelems.h[63812c3c423805d6d3ad61b1706651329935ac40] - generated from ddsi_config.c[3564342f60516165409e1a563f2346bc91895f15] + generated from ddsi__cfgelems.h[8090d347c12ef665e43b0de41fa57f309c4e9980] + generated from ddsi_config.c[c8b22d1fe853f4cc6545b65610d441dee5558011] generated from _confgen.h[f2d235d5551cbf920a8a2962831dddeabd2856ac] generated from _confgen.c[d74e4fd06e485c5d299dbcc7741cbdb95c5ec706] generated from generate_rnc.c[a2ec6e48d33ac14a320c8ec3f320028a737920e0] diff --git a/docs/manual/options.md b/docs/manual/options.md index 605fc408aa..75b7255459 100644 --- a/docs/manual/options.md +++ b/docs/manual/options.md @@ -478,7 +478,7 @@ The default value is: `default` ### //CycloneDDS/Domain/Internal -Children: [AccelerateRexmitBlockSize](#cycloneddsdomaininternalacceleraterexmitblocksize), [AckDelay](#cycloneddsdomaininternalackdelay), [AutoReschedNackDelay](#cycloneddsdomaininternalautoreschednackdelay), [BuiltinEndpointSet](#cycloneddsdomaininternalbuiltinendpointset), [BurstSize](#cycloneddsdomaininternalburstsize), [ControlTopic](#cycloneddsdomaininternalcontroltopic), [DefragReliableMaxSamples](#cycloneddsdomaininternaldefragreliablemaxsamples), [DefragUnreliableMaxSamples](#cycloneddsdomaininternaldefragunreliablemaxsamples), [DeliveryQueueMaxSamples](#cycloneddsdomaininternaldeliveryqueuemaxsamples), [EnableExpensiveChecks](#cycloneddsdomaininternalenableexpensivechecks), [GenerateKeyhash](#cycloneddsdomaininternalgeneratekeyhash), [HeartbeatInterval](#cycloneddsdomaininternalheartbeatinterval), [LateAckMode](#cycloneddsdomaininternallateackmode), [LivelinessMonitoring](#cycloneddsdomaininternallivelinessmonitoring), [MaxParticipants](#cycloneddsdomaininternalmaxparticipants), [MaxQueuedRexmitBytes](#cycloneddsdomaininternalmaxqueuedrexmitbytes), [MaxQueuedRexmitMessages](#cycloneddsdomaininternalmaxqueuedrexmitmessages), [MaxSampleSize](#cycloneddsdomaininternalmaxsamplesize), [MeasureHbToAckLatency](#cycloneddsdomaininternalmeasurehbtoacklatency), [MonitorPort](#cycloneddsdomaininternalmonitorport), [MultipleReceiveThreads](#cycloneddsdomaininternalmultiplereceivethreads), [NackDelay](#cycloneddsdomaininternalnackdelay), [PreEmptiveAckDelay](#cycloneddsdomaininternalpreemptiveackdelay), [PrimaryReorderMaxSamples](#cycloneddsdomaininternalprimaryreordermaxsamples), [PrioritizeRetransmit](#cycloneddsdomaininternalprioritizeretransmit), [RediscoveryBlacklistDuration](#cycloneddsdomaininternalrediscoveryblacklistduration), [RetransmitMerging](#cycloneddsdomaininternalretransmitmerging), [RetransmitMergingPeriod](#cycloneddsdomaininternalretransmitmergingperiod), [RetryOnRejectBestEffort](#cycloneddsdomaininternalretryonrejectbesteffort), [SPDPResponseMaxDelay](#cycloneddsdomaininternalspdpresponsemaxdelay), [ScheduleTimeRounding](#cycloneddsdomaininternalscheduletimerounding), [SecondaryReorderMaxSamples](#cycloneddsdomaininternalsecondaryreordermaxsamples), [SocketReceiveBufferSize](#cycloneddsdomaininternalsocketreceivebuffersize), [SocketSendBufferSize](#cycloneddsdomaininternalsocketsendbuffersize), [SquashParticipants](#cycloneddsdomaininternalsquashparticipants), [SynchronousDeliveryLatencyBound](#cycloneddsdomaininternalsynchronousdeliverylatencybound), [SynchronousDeliveryPriorityThreshold](#cycloneddsdomaininternalsynchronousdeliveryprioritythreshold), [Test](#cycloneddsdomaininternaltest), [UnicastResponseToSPDPMessages](#cycloneddsdomaininternalunicastresponsetospdpmessages), [UseMulticastIfMreqn](#cycloneddsdomaininternalusemulticastifmreqn), [Watermarks](#cycloneddsdomaininternalwatermarks), [WriteBatch](#cycloneddsdomaininternalwritebatch), [WriterLingerDuration](#cycloneddsdomaininternalwriterlingerduration) +Children: [AccelerateRexmitBlockSize](#cycloneddsdomaininternalacceleraterexmitblocksize), [AckDelay](#cycloneddsdomaininternalackdelay), [AutoReschedNackDelay](#cycloneddsdomaininternalautoreschednackdelay), [BuiltinEndpointSet](#cycloneddsdomaininternalbuiltinendpointset), [BurstSize](#cycloneddsdomaininternalburstsize), [ControlTopic](#cycloneddsdomaininternalcontroltopic), [DefragReliableMaxSamples](#cycloneddsdomaininternaldefragreliablemaxsamples), [DefragUnreliableMaxSamples](#cycloneddsdomaininternaldefragunreliablemaxsamples), [DeliveryQueueMaxSamples](#cycloneddsdomaininternaldeliveryqueuemaxsamples), [EnableExpensiveChecks](#cycloneddsdomaininternalenableexpensivechecks), [GenerateKeyhash](#cycloneddsdomaininternalgeneratekeyhash), [HeartbeatInterval](#cycloneddsdomaininternalheartbeatinterval), [LateAckMode](#cycloneddsdomaininternallateackmode), [LivelinessMonitoring](#cycloneddsdomaininternallivelinessmonitoring), [MaxParticipants](#cycloneddsdomaininternalmaxparticipants), [MaxQueuedRexmitBytes](#cycloneddsdomaininternalmaxqueuedrexmitbytes), [MaxQueuedRexmitMessages](#cycloneddsdomaininternalmaxqueuedrexmitmessages), [MaxSampleSize](#cycloneddsdomaininternalmaxsamplesize), [MeasureHbToAckLatency](#cycloneddsdomaininternalmeasurehbtoacklatency), [MonitorPort](#cycloneddsdomaininternalmonitorport), [MultipleReceiveThreads](#cycloneddsdomaininternalmultiplereceivethreads), [NackDelay](#cycloneddsdomaininternalnackdelay), [PreEmptiveAckDelay](#cycloneddsdomaininternalpreemptiveackdelay), [PrimaryReorderMaxSamples](#cycloneddsdomaininternalprimaryreordermaxsamples), [PrioritizeRetransmit](#cycloneddsdomaininternalprioritizeretransmit), [RediscoveryBlacklistDuration](#cycloneddsdomaininternalrediscoveryblacklistduration), [RetransmitMerging](#cycloneddsdomaininternalretransmitmerging), [RetransmitMergingPeriod](#cycloneddsdomaininternalretransmitmergingperiod), [RetryOnRejectBestEffort](#cycloneddsdomaininternalretryonrejectbesteffort), [SPDPResponseMaxDelay](#cycloneddsdomaininternalspdpresponsemaxdelay), [SecondaryReorderMaxSamples](#cycloneddsdomaininternalsecondaryreordermaxsamples), [SocketReceiveBufferSize](#cycloneddsdomaininternalsocketreceivebuffersize), [SocketSendBufferSize](#cycloneddsdomaininternalsocketsendbuffersize), [SquashParticipants](#cycloneddsdomaininternalsquashparticipants), [SynchronousDeliveryLatencyBound](#cycloneddsdomaininternalsynchronousdeliverylatencybound), [SynchronousDeliveryPriorityThreshold](#cycloneddsdomaininternalsynchronousdeliveryprioritythreshold), [Test](#cycloneddsdomaininternaltest), [UnicastResponseToSPDPMessages](#cycloneddsdomaininternalunicastresponsetospdpmessages), [UseMulticastIfMreqn](#cycloneddsdomaininternalusemulticastifmreqn), [Watermarks](#cycloneddsdomaininternalwatermarks), [WriteBatch](#cycloneddsdomaininternalwritebatch), [WriterLingerDuration](#cycloneddsdomaininternalwriterlingerduration) The Internal elements deal with a variety of settings that are evolving and that are not necessarily fully supported. For the majority of the Internal settings the functionality is supported, but the right to change the way the options control the functionality is reserved. This includes renaming or moving options. @@ -855,16 +855,6 @@ The unit must be specified explicitly. Recognised units: ns, us, ms, s, min, hr, The default value is: `0 ms` -#### //CycloneDDS/Domain/Internal/ScheduleTimeRounding -Number-with-unit - -This setting allows the timing of scheduled events to be rounded up so that more events can be handled in a single cycle of the event queue. The default is 0 and causes no rounding at all, i.e. are scheduled exactly, whereas a value of 10ms would mean that events are rounded up to the nearest 10 milliseconds. - -The unit must be specified explicitly. Recognised units: ns, us, ms, s, min, hr, day. - -The default value is: `0 ms` - - #### //CycloneDDS/Domain/Internal/SecondaryReorderMaxSamples Integer @@ -1864,10 +1854,10 @@ While none prevents any message from being written to a DDSI2 log file. The categorisation of tracing output is incomplete and hence most of the verbosity levels and categories are not of much use in the current release. This is an ongoing process and here we describe the target situation rather than the current situation. Currently, the most useful verbosity levels are config, fine and finest. The default value is: `none` - + - - + + diff --git a/etc/cyclonedds.rnc b/etc/cyclonedds.rnc index cc701d9252..79fd0dc00c 100644 --- a/etc/cyclonedds.rnc +++ b/etc/cyclonedds.rnc @@ -607,13 +607,6 @@ CycloneDDS configuration""" ] ] duration }? & [ a:documentation [ xml:lang="en" """ -

This setting allows the timing of scheduled events to be rounded up so that more events can be handled in a single cycle of the event queue. The default is 0 and causes no rounding at all, i.e. are scheduled exactly, whereas a value of 10ms would mean that events are rounded up to the nearest 10 milliseconds.

-

The unit must be specified explicitly. Recognised units: ns, us, ms, s, min, hr, day.

-

The default value is: 0 ms

""" ] ] - element ScheduleTimeRounding { - duration - }? - & [ a:documentation [ xml:lang="en" """

This element sets the maximum size in samples of a secondary re-order administration. The secondary re-order administration is per reader needing historical data.

The default value is: 128

""" ] ] element SecondaryReorderMaxSamples { @@ -1293,10 +1286,10 @@ MIIEpAIBAAKCAQEA3HIh...AOBaaqSV37XBUJg==
duration_inf = xsd:token { pattern = "inf|0|(\d+(\.\d*)?([Ee][\-+]?\d+)?|\.\d+([Ee][\-+]?\d+)?) *([num]?s|min|hr|day)" } memsize = xsd:token { pattern = "0|(\d+(\.\d*)?([Ee][\-+]?\d+)?|\.\d+([Ee][\-+]?\d+)?) *([kMG]i?)?B" } } -# generated from ddsi_config.h[d0e749e6d58d560b8172dcb0cb109d6e5f6897ba] +# generated from ddsi_config.h[af204240792218d7541714019cac9ae65b87878f] # generated from ddsi__cfgunits.h[be1b976c6e9466472b0c331487c05180ec1052d4] -# generated from ddsi__cfgelems.h[63812c3c423805d6d3ad61b1706651329935ac40] -# generated from ddsi_config.c[3564342f60516165409e1a563f2346bc91895f15] +# generated from ddsi__cfgelems.h[8090d347c12ef665e43b0de41fa57f309c4e9980] +# generated from ddsi_config.c[c8b22d1fe853f4cc6545b65610d441dee5558011] # generated from _confgen.h[f2d235d5551cbf920a8a2962831dddeabd2856ac] # generated from _confgen.c[d74e4fd06e485c5d299dbcc7741cbdb95c5ec706] # generated from generate_rnc.c[a2ec6e48d33ac14a320c8ec3f320028a737920e0] diff --git a/etc/cyclonedds.xsd b/etc/cyclonedds.xsd index 703f6b8002..8f69c4c2ec 100644 --- a/etc/cyclonedds.xsd +++ b/etc/cyclonedds.xsd @@ -583,7 +583,6 @@ CycloneDDS configuration - @@ -954,14 +953,6 @@ CycloneDDS configuration <p>Maximum pseudo-random delay in milliseconds between discovering aremote participant and responding to it.</p> <p>The unit must be specified explicitly. Recognised units: ns, us, ms, s, min, hr, day.</p> -<p>The default value is: <code>0 ms</code></p> - - - - - -<p>This setting allows the timing of scheduled events to be rounded up so that more events can be handled in a single cycle of the event queue. The default is 0 and causes no rounding at all, i.e. are scheduled exactly, whereas a value of 10ms would mean that events are rounded up to the nearest 10 milliseconds.</p> -<p>The unit must be specified explicitly. Recognised units: ns, us, ms, s, min, hr, day.</p> <p>The default value is: <code>0 ms</code></p> @@ -1964,10 +1955,10 @@ MIIEpAIBAAKCAQEA3HIh...AOBaaqSV37XBUJg==<br> - + - - + + diff --git a/src/core/ddsi/defconfig.c b/src/core/ddsi/defconfig.c index 79aa712029..9bd70f308c 100644 --- a/src/core/ddsi/defconfig.c +++ b/src/core/ddsi/defconfig.c @@ -103,10 +103,10 @@ void ddsi_config_init_default (struct ddsi_config *cfg) cfg->shm_log_lvl = INT32_C (4); #endif /* DDS_HAS_SHM */ } -/* generated from ddsi_config.h[d0e749e6d58d560b8172dcb0cb109d6e5f6897ba] */ +/* generated from ddsi_config.h[af204240792218d7541714019cac9ae65b87878f] */ /* generated from ddsi__cfgunits.h[be1b976c6e9466472b0c331487c05180ec1052d4] */ -/* generated from ddsi__cfgelems.h[63812c3c423805d6d3ad61b1706651329935ac40] */ -/* generated from ddsi_config.c[3564342f60516165409e1a563f2346bc91895f15] */ +/* generated from ddsi__cfgelems.h[8090d347c12ef665e43b0de41fa57f309c4e9980] */ +/* generated from ddsi_config.c[c8b22d1fe853f4cc6545b65610d441dee5558011] */ /* generated from _confgen.h[f2d235d5551cbf920a8a2962831dddeabd2856ac] */ /* generated from _confgen.c[d74e4fd06e485c5d299dbcc7741cbdb95c5ec706] */ /* generated from generate_rnc.c[a2ec6e48d33ac14a320c8ec3f320028a737920e0] */ diff --git a/src/core/ddsi/include/dds/ddsi/ddsi_config.h b/src/core/ddsi/include/dds/ddsi/ddsi_config.h index 06c1e00e7d..fb5cc26936 100644 --- a/src/core/ddsi/include/dds/ddsi/ddsi_config.h +++ b/src/core/ddsi/include/dds/ddsi/ddsi_config.h @@ -373,7 +373,6 @@ struct ddsi_config int64_t ack_delay; int64_t nack_delay; int64_t preemptive_ack_delay; - int64_t schedule_time_rounding; int64_t auto_resched_nack_delay; int64_t ds_grace_period; uint32_t max_queued_rexmit_bytes; diff --git a/src/core/ddsi/src/ddsi__cfgelems.h b/src/core/ddsi/src/ddsi__cfgelems.h index 8b916aae50..fbaa9c711b 100644 --- a/src/core/ddsi/src/ddsi__cfgelems.h +++ b/src/core/ddsi/src/ddsi__cfgelems.h @@ -1352,9 +1352,9 @@ static struct cfgelem internal_cfgelems[] = { "writer and sending a pre-emptive AckNack to discover the available " "range of data.

"), UNIT("duration")), - STRING("ScheduleTimeRounding", NULL, 1, "0 ms", - MEMBER(schedule_time_rounding), - FUNCTIONS(0, uf_duration_ms_1hr, 0, pf_duration), + STRING(DEPRECATED("ScheduleTimeRounding"), NULL, 1, "0 ms", + NOMEMBER, + FUNCTIONS(0, uf_nop_duration_ms_1hr, 0, 0), DESCRIPTION( "

This setting allows the timing of scheduled events to be rounded " "up so that more events can be handled in a single cycle of the event " diff --git a/src/core/ddsi/src/ddsi_config.c b/src/core/ddsi/src/ddsi_config.c index 64072b9c19..fb4fb25339 100644 --- a/src/core/ddsi/src/ddsi_config.c +++ b/src/core/ddsi/src/ddsi_config.c @@ -169,6 +169,7 @@ DU(duration_ms_1hr); DU(duration_ms_1s); DU(duration_us_1s); DU(duration_100ms_1hr); +DU(nop_duration_ms_1hr); PF(duration); DUPF(standards_conformance); DUPF(besmode); @@ -1395,6 +1396,12 @@ static enum update_result uf_duration_ms_1hr (struct ddsi_cfgst *cfgst, void *pa return uf_duration_gen (cfgst, parent, cfgelem, value, DDS_MSECS (1), 0, DDS_SECS (3600)); } +static enum update_result uf_nop_duration_ms_1hr (struct ddsi_cfgst *cfgst, UNUSED_ARG(void *parent), UNUSED_ARG(struct cfgelem const * const cfgelem), UNUSED_ARG (int first), const char *value) +{ + int64_t dummy; + return uf_natint64_unit (cfgst, &dummy, value, unittab_duration, DDS_MSECS (1), 0, DDS_SECS (3600)); +} + static enum update_result uf_duration_ms_1s (struct ddsi_cfgst *cfgst, void *parent, struct cfgelem const * const cfgelem, UNUSED_ARG (int first), const char *value) { return uf_duration_gen (cfgst, parent, cfgelem, value, DDS_MSECS (1), 0, DDS_SECS (1)); diff --git a/src/core/ddsi/src/ddsi_xevent.c b/src/core/ddsi/src/ddsi_xevent.c index 07332fe241..3c272f91aa 100644 --- a/src/core/ddsi/src/ddsi_xevent.c +++ b/src/core/ddsi/src/ddsi_xevent.c @@ -594,7 +594,6 @@ static void handle_xevents (struct ddsi_thread_state * const thrst, struct ddsi_ static uint32_t xevent_thread (struct ddsi_xeventq * xevq) { struct ddsi_thread_state * const thrst = ddsi_lookup_thread_state (); - const dds_duration_t rounding = xevq->gv->config.schedule_time_rounding; ddsrt_mtime_t next_thread_cputime = { 0 }; struct ddsi_xpack * const xp = ddsi_xpack_new (xevq->gv, false); @@ -631,7 +630,7 @@ static uint32_t xevent_thread (struct ddsi_xeventq * xevq) tnow = ddsrt_time_monotonic (); if (twakeup.v > tnow.v) { - twakeup.v = ddsrt_mtime_add_duration(twakeup, rounding).v - tnow.v; + twakeup.v -= tnow.v; ddsrt_cond_waitfor (&xevq->cond, &xevq->lock, twakeup.v); } } From ed81f37df0b88665ed33929e41452723259df7d7 Mon Sep 17 00:00:00 2001 From: Erik Boasson Date: Thu, 23 Mar 2023 13:12:27 +0100 Subject: [PATCH 07/11] Improve comments/naming for xevent implementation Signed-off-by: Erik Boasson --- src/core/ddsi/include/dds/ddsi/ddsi_xevent.h | 63 ++++++++++++++------ src/core/ddsi/src/ddsi_xevent.c | 26 ++++---- 2 files changed, 58 insertions(+), 31 deletions(-) diff --git a/src/core/ddsi/include/dds/ddsi/ddsi_xevent.h b/src/core/ddsi/include/dds/ddsi/ddsi_xevent.h index 4a122dc53c..1c771ae956 100644 --- a/src/core/ddsi/include/dds/ddsi/ddsi_xevent.h +++ b/src/core/ddsi/include/dds/ddsi/ddsi_xevent.h @@ -27,34 +27,61 @@ struct ddsi_xpack; typedef void (*ddsi_xevent_cb_t) (struct ddsi_domaingv *gv, struct ddsi_xevent *ev, struct ddsi_xpack *xp, void *arg, ddsrt_mtime_t tnow); -/** +/** @brief removes an event from the event queue and frees it * @component timed_events - * @remark: locks EVQ for the duration of the operation - * @param ev the event + * + * @remark: may be called from inside the event handler + * @remark: if the event was created with the `sync_on_delete` flag set, it blocks until + * the system guarantees that it is not and will not be executing anymore + * + * @param[in] ev the event to be deleted */ -void ddsi_delete_xevent (struct ddsi_xevent *ev); +void ddsi_delete_xevent (struct ddsi_xevent *ev) + ddsrt_nonnull_all; -/** +/** @brief reschedules the event iff `tsched` precedes the time for which it is currently scheduled * @component timed_events - * @remark: locks EVQ for the duration of the operation - * @param ev the event + * + * @remark: may be called from inside the event handler + * + * @param[in] ev the event to be rescheduled + * @param[in] tsched new scheduled time + * + * @return whether the event was actually rescheduled, i.e., whether `tsched` preceded the + * time for which it was scheduled before + * + * @retval 0 not rescheduled + * @retval 1 rescheduled */ -int ddsi_resched_xevent_if_earlier (struct ddsi_xevent *ev, ddsrt_mtime_t tsched); +int ddsi_resched_xevent_if_earlier (struct ddsi_xevent *ev, ddsrt_mtime_t tsched) + ddsrt_nonnull_all; -/** +/** @brief creates a new event object on the given event queue for invoking `cb` in the + * future on the thread handling this queue * @component timed_events * - * @remark: delete does not block until an ongoing callback finishes - * @remark: cb will be called with now = NEVER if the event is still enqueued when when ddsi_xeventq_free starts cleaning up + * @remark: delete does not block until an ongoing callback finishes, unless + * `sync_on_delete` is set + * @remark: just before invoking the callback, the schedule time is set to NEVER + * @remark: cb will be called with now = NEVER if the event is still enqueued when + * `ddsi_xeventq_free` starts cleaning up (a consequence of the combination of + * this and being allowed to delete the event from within the callback is that + * one need not always store the returned event pointer) + * + * @param[in] evq event queue + * @param[in] tsched timestamp scheduled, may be NEVER + * @param[in] cb callback function pointer + * @param[in] arg argument (copied into ddsi_xevent, may be a null pointer + * when arg_size = 0) + * @param[in] arg_size size of argument in bytes + * @param[in] sync_on_delete whether `ddsi_delete_xevent` must block until the + * system guarantees that `cb` is not and will not be executing + * anymore * - * @param evq event queue - * @param tsched timestamp scheduled - * @param cb callback function pointer - * @param arg argument (copied into ddsi_xevent) - * @param arg_size size of argument - * @return struct ddsi_xevent* + * @return the new event object */ -struct ddsi_xevent *ddsi_qxev_callback (struct ddsi_xeventq *evq, ddsrt_mtime_t tsched, ddsi_xevent_cb_t cb, const void *arg, size_t arg_size, bool sync_on_delete); +struct ddsi_xevent *ddsi_qxev_callback (struct ddsi_xeventq *evq, ddsrt_mtime_t tsched, ddsi_xevent_cb_t cb, const void *arg, size_t arg_size, bool sync_on_delete) + ddsrt_nonnull((1,3)); #if defined (__cplusplus) } diff --git a/src/core/ddsi/src/ddsi_xevent.c b/src/core/ddsi/src/ddsi_xevent.c index 3c272f91aa..31f60bc0cc 100644 --- a/src/core/ddsi/src/ddsi_xevent.c +++ b/src/core/ddsi/src/ddsi_xevent.c @@ -33,10 +33,10 @@ != 0 -- and note that it had better be 2's complement machine! */ #define TSCHED_DELETE ((int64_t) ((uint64_t) 1 << 63)) -enum cb_sync_state { - CSS_DONTCARE, - CSS_SCHEDULED, - CSS_EXECUTING +enum cb_sync_on_delete_state { + CSODS_NO_SYNC_NEEDED, + CSODS_SCHEDULED, + CSODS_EXECUTING }; struct ddsi_xevent @@ -45,7 +45,7 @@ struct ddsi_xevent struct ddsi_xeventq *evq; ddsrt_mtime_t tsched; - enum cb_sync_state sync_state; + enum cb_sync_on_delete_state sync_state; union { ddsi_xevent_cb_t cb; // ensure alignment of arg is good @@ -259,7 +259,7 @@ static void ddsi_delete_xevent_nosync (struct ddsi_xevent *ev) { struct ddsi_xeventq *evq = ev->evq; ddsrt_mutex_lock (&evq->lock); - assert (ev->sync_state != CSS_EXECUTING); + assert (ev->sync_state != CSODS_EXECUTING); /* Can delete it only once, no matter how we implement it internally */ assert (ev->tsched.v != TSCHED_DELETE); assert (TSCHED_DELETE < ev->tsched.v); @@ -284,7 +284,7 @@ static void ddsi_delete_xevent_sync (struct ddsi_xevent *ev) struct ddsi_xeventq *evq = ev->evq; ddsrt_mutex_lock (&evq->lock); /* wait until neither scheduled nor executing; loop in case the callback reschedules the event */ - while (ev->tsched.v != DDS_NEVER || ev->sync_state == CSS_EXECUTING) + while (ev->tsched.v != DDS_NEVER || ev->sync_state == CSODS_EXECUTING) { if (ev->tsched.v != DDS_NEVER) { @@ -292,7 +292,7 @@ static void ddsi_delete_xevent_sync (struct ddsi_xevent *ev) ddsrt_fibheap_delete (&evq_xevents_fhdef, &evq->xevents, ev); ev->tsched.v = DDS_NEVER; } - if (ev->sync_state == CSS_EXECUTING) + if (ev->sync_state == CSODS_EXECUTING) { ddsrt_cond_wait (&evq->cond, &evq->lock); } @@ -303,7 +303,7 @@ static void ddsi_delete_xevent_sync (struct ddsi_xevent *ev) void ddsi_delete_xevent (struct ddsi_xevent *ev) { - if (ev->sync_state == CSS_DONTCARE) + if (ev->sync_state == CSODS_NO_SYNC_NEEDED) ddsi_delete_xevent_nosync (ev); else ddsi_delete_xevent_sync (ev); @@ -498,7 +498,7 @@ static void handle_timed_xevent (struct ddsi_xeventq *evq, struct ddsi_xevent *x xev->tsched.v = DDS_NEVER; /* We relinquish the lock while processing the event. */ - if (xev->sync_state == CSS_DONTCARE) + if (xev->sync_state == CSODS_NO_SYNC_NEEDED) { ddsrt_mutex_unlock (&evq->lock); xev->cb.cb (evq->gv, xev, xp, xev->arg, tnow); @@ -506,11 +506,11 @@ static void handle_timed_xevent (struct ddsi_xeventq *evq, struct ddsi_xevent *x } else { - xev->sync_state = CSS_EXECUTING; + xev->sync_state = CSODS_EXECUTING; ddsrt_mutex_unlock (&evq->lock); xev->cb.cb (evq->gv, xev, xp, xev->arg, tnow); ddsrt_mutex_lock (&evq->lock); - xev->sync_state = CSS_SCHEDULED; + xev->sync_state = CSODS_SCHEDULED; ddsrt_cond_broadcast (&evq->cond); } } @@ -734,7 +734,7 @@ struct ddsi_xevent *ddsi_qxev_callback (struct ddsi_xeventq *evq, ddsrt_mtime_t ev->evq = evq; ev->tsched = tsched; ev->cb.cb = cb; - ev->sync_state = sync_on_delete ? CSS_SCHEDULED : CSS_DONTCARE; + ev->sync_state = sync_on_delete ? CSODS_SCHEDULED : CSODS_NO_SYNC_NEEDED; if (arg_size) // so arg = NULL, arg_size = 0 is allowed memcpy (ev->arg, arg, arg_size); ddsrt_mutex_lock (&evq->lock); From 8e881ae45f68a96ae4f3b5a0530fe790f092fb45 Mon Sep 17 00:00:00 2001 From: Erik Boasson Date: Thu, 23 Mar 2023 13:13:24 +0100 Subject: [PATCH 08/11] Restore lost trace in HEARTBEAT xevent handler Signed-off-by: Erik Boasson --- src/core/ddsi/src/ddsi_hbcontrol.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/ddsi/src/ddsi_hbcontrol.c b/src/core/ddsi/src/ddsi_hbcontrol.c index b6c078612d..916e40b8e8 100644 --- a/src/core/ddsi/src/ddsi_hbcontrol.c +++ b/src/core/ddsi/src/ddsi_hbcontrol.c @@ -534,6 +534,7 @@ void ddsi_xevent_heartbeat_cb (struct ddsi_domaingv *gv, struct ddsi_xevent *ev, struct ddsi_writer *wr; if ((wr = ddsi_entidx_lookup_writer_guid (gv->entity_index, &arg->wr_guid)) == NULL) { + GVTRACE("heartbeat(wr "PGUIDFMT") writer gone\n", PGUID (arg->wr_guid)); return; } From 0d90e6e7b4ee179a9da2aaefcfa9c04b4afbb2e3 Mon Sep 17 00:00:00 2001 From: Erik Boasson Date: Thu, 23 Mar 2023 13:16:05 +0100 Subject: [PATCH 09/11] Move ddsi_discovery_sedp into ddsi_discovery Trying to move the common parts of SEDP support into a separate file only confused things, in hindsight. This moves the contents of that file back into ddsi_discovery. Signed-off-by: Erik Boasson --- src/core/ddsi/CMakeLists.txt | 2 - src/core/ddsi/src/ddsi__discovery.h | 24 +++ src/core/ddsi/src/ddsi__discovery_endpoint.h | 3 +- src/core/ddsi/src/ddsi__discovery_sedp.h | 57 ----- src/core/ddsi/src/ddsi__discovery_topic.h | 2 +- src/core/ddsi/src/ddsi_discovery.c | 182 +++++++++++++++- src/core/ddsi/src/ddsi_discovery_endpoint.c | 8 +- src/core/ddsi/src/ddsi_discovery_sedp.c | 212 ------------------- src/core/ddsi/src/ddsi_discovery_topic.c | 2 +- 9 files changed, 210 insertions(+), 282 deletions(-) delete mode 100644 src/core/ddsi/src/ddsi__discovery_sedp.h delete mode 100644 src/core/ddsi/src/ddsi_discovery_sedp.c diff --git a/src/core/ddsi/CMakeLists.txt b/src/core/ddsi/CMakeLists.txt index a7b1b0640c..ffa7bf3596 100644 --- a/src/core/ddsi/CMakeLists.txt +++ b/src/core/ddsi/CMakeLists.txt @@ -66,7 +66,6 @@ set(srcs_ddsi ddsi_discovery.c ddsi_discovery_addrset.c ddsi_discovery_spdp.c - ddsi_discovery_sedp.c ddsi_discovery_endpoint.c ddsi_debmon.c ddsi_init.c @@ -195,7 +194,6 @@ set(hdrs_private_ddsi ddsi__discovery.h ddsi__discovery_addrset.h ddsi__discovery_spdp.h - ddsi__discovery_sedp.h ddsi__discovery_endpoint.h ddsi__debmon.h ddsi__hbcontrol.h diff --git a/src/core/ddsi/src/ddsi__discovery.h b/src/core/ddsi/src/ddsi__discovery.h index 2ab98f18af..fdd2e5f410 100644 --- a/src/core/ddsi/src/ddsi__discovery.h +++ b/src/core/ddsi/src/ddsi__discovery.h @@ -30,6 +30,30 @@ struct ddsi_xevent; struct ddsi_xpack; struct ddsi_domaingv; +typedef enum ddsi_sedp_kind { + SEDP_KIND_READER, + SEDP_KIND_WRITER, + SEDP_KIND_TOPIC +} ddsi_sedp_kind_t; + +/** @component discovery */ +struct ddsi_writer *ddsi_get_sedp_writer (const struct ddsi_participant *pp, unsigned entityid) + ddsrt_nonnull_all; + +/** @component discovery */ +struct ddsi_proxy_participant *ddsi_implicitly_create_proxypp (struct ddsi_domaingv *gv, const ddsi_guid_t *ppguid, ddsi_plist_t *datap /* note: potentially modifies datap */, const ddsi_guid_prefix_t *src_guid_prefix, ddsi_vendorid_t vendorid, ddsrt_wctime_t timestamp, ddsi_seqno_t seq) + ddsrt_nonnull_all; + +/** @component discovery */ +bool ddsi_check_sedp_kind_and_guid (ddsi_sedp_kind_t sedp_kind, const ddsi_guid_t *entity_guid) + ddsrt_nonnull_all; + +/** @component discovery */ +bool ddsi_handle_sedp_checks (struct ddsi_domaingv * const gv, ddsi_sedp_kind_t sedp_kind, ddsi_guid_t *entity_guid, ddsi_plist_t *datap, + const ddsi_guid_prefix_t *src_guid_prefix, ddsi_vendorid_t vendorid, ddsrt_wctime_t timestamp, + struct ddsi_proxy_participant **proxypp, ddsi_guid_t *ppguid) + ddsrt_nonnull_all; + /** @component discovery */ int ddsi_builtins_dqueue_handler (const struct ddsi_rsample_info *sampleinfo, const struct ddsi_rdata *fragchain, const ddsi_guid_t *rdguid, void *qarg); diff --git a/src/core/ddsi/src/ddsi__discovery_endpoint.h b/src/core/ddsi/src/ddsi__discovery_endpoint.h index e4e295740f..8e7bdaaef1 100644 --- a/src/core/ddsi/src/ddsi__discovery_endpoint.h +++ b/src/core/ddsi/src/ddsi__discovery_endpoint.h @@ -14,7 +14,7 @@ #include "dds/ddsi/ddsi_unused.h" #include "dds/ddsi/ddsi_domaingv.h" -#include "ddsi__discovery_sedp.h" +#include "ddsi__discovery.h" #if defined (__cplusplus) extern "C" { @@ -30,6 +30,7 @@ struct ddsi_plist; struct ddsi_xevent; struct ddsi_xpack; struct ddsi_domaingv; +struct ddsi_receiver_state; /** @component discovery */ struct ddsi_addrset *ddsi_get_endpoint_addrset (const struct ddsi_domaingv *gv, const ddsi_plist_t *datap, struct ddsi_addrset *proxypp_as_default, const ddsi_locator_t *rst_srcloc) diff --git a/src/core/ddsi/src/ddsi__discovery_sedp.h b/src/core/ddsi/src/ddsi__discovery_sedp.h deleted file mode 100644 index 911ef64b5a..0000000000 --- a/src/core/ddsi/src/ddsi__discovery_sedp.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright(c) 2006 to 2021 ZettaScale Technology and others - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License - * v. 1.0 which is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause - */ -#ifndef DDSI__DISCOVERY_SEDP_H -#define DDSI__DISCOVERY_SEDP_H - -#include "dds/ddsi/ddsi_domaingv.h" - -#if defined (__cplusplus) -extern "C" { -#endif - -struct ddsi_receiver_state; -struct ddsi_serdata; -struct ddsi_proxy_participant; - -typedef enum ddsi_sedp_kind { - SEDP_KIND_READER, - SEDP_KIND_WRITER, - SEDP_KIND_TOPIC -} ddsi_sedp_kind_t; - -/** @component discovery */ -struct ddsi_writer *ddsi_get_sedp_writer (const struct ddsi_participant *pp, unsigned entityid) - ddsrt_nonnull_all; - -/** @component discovery */ -struct ddsi_proxy_participant *ddsi_implicitly_create_proxypp (struct ddsi_domaingv *gv, const ddsi_guid_t *ppguid, ddsi_plist_t *datap /* note: potentially modifies datap */, const ddsi_guid_prefix_t *src_guid_prefix, ddsi_vendorid_t vendorid, ddsrt_wctime_t timestamp, ddsi_seqno_t seq) - ddsrt_nonnull_all; - -/** @component discovery */ -bool ddsi_check_sedp_kind_and_guid (ddsi_sedp_kind_t sedp_kind, const ddsi_guid_t *entity_guid) - ddsrt_nonnull_all; - -/** @component discovery */ -bool ddsi_handle_sedp_checks (struct ddsi_domaingv * const gv, ddsi_sedp_kind_t sedp_kind, ddsi_guid_t *entity_guid, ddsi_plist_t *datap, - const ddsi_guid_prefix_t *src_guid_prefix, ddsi_vendorid_t vendorid, ddsrt_wctime_t timestamp, - struct ddsi_proxy_participant **proxypp, ddsi_guid_t *ppguid) - ddsrt_nonnull_all; - -/** @component discovery */ -void ddsi_handle_sedp (const struct ddsi_receiver_state *rst, ddsi_seqno_t seq, struct ddsi_serdata *serdata, ddsi_sedp_kind_t sedp_kind) - ddsrt_nonnull_all; - -#if defined (__cplusplus) -} -#endif - -#endif /* DDSI__DISCOVERY_SEDP_H */ diff --git a/src/core/ddsi/src/ddsi__discovery_topic.h b/src/core/ddsi/src/ddsi__discovery_topic.h index e35efde7b3..47abb7a84e 100644 --- a/src/core/ddsi/src/ddsi__discovery_topic.h +++ b/src/core/ddsi/src/ddsi__discovery_topic.h @@ -14,7 +14,6 @@ #include "dds/ddsi/ddsi_unused.h" #include "dds/ddsi/ddsi_domaingv.h" -#include "ddsi__discovery_sedp.h" #if defined (__cplusplus) extern "C" { @@ -30,6 +29,7 @@ struct ddsi_plist; struct ddsi_xevent; struct ddsi_xpack; struct ddsi_domaingv; +struct ddsi_receiver_state; /** @component discovery */ int ddsi_sedp_write_topic (struct ddsi_topic *tp, bool alive) ddsrt_nonnull_all; diff --git a/src/core/ddsi/src/ddsi_discovery.c b/src/core/ddsi/src/ddsi_discovery.c index 387d2b131a..fce8dac44d 100644 --- a/src/core/ddsi/src/ddsi_discovery.c +++ b/src/core/ddsi/src/ddsi_discovery.c @@ -32,7 +32,6 @@ #include "ddsi__discovery.h" #include "ddsi__discovery_addrset.h" #include "ddsi__discovery_spdp.h" -#include "ddsi__discovery_sedp.h" #include "ddsi__discovery_endpoint.h" #ifdef DDS_HAS_TOPIC_DISCOVERY #include "ddsi__discovery_topic.h" @@ -65,6 +64,187 @@ #include "ddsi__security_exchange.h" #endif +struct ddsi_writer *ddsi_get_sedp_writer (const struct ddsi_participant *pp, unsigned entityid) +{ + struct ddsi_writer *sedp_wr = ddsi_get_builtin_writer (pp, entityid); + if (sedp_wr == NULL) + DDS_FATAL ("sedp_write_writer: no SEDP builtin writer %x for "PGUIDFMT"\n", entityid, PGUID (pp->e.guid)); + return sedp_wr; +} + +struct ddsi_proxy_participant *ddsi_implicitly_create_proxypp (struct ddsi_domaingv *gv, const ddsi_guid_t *ppguid, ddsi_plist_t *datap /* note: potentially modifies datap */, const ddsi_guid_prefix_t *src_guid_prefix, ddsi_vendorid_t vendorid, ddsrt_wctime_t timestamp, ddsi_seqno_t seq) +{ + ddsi_guid_t privguid; + ddsi_plist_t pp_plist; + + if (memcmp (&ppguid->prefix, src_guid_prefix, sizeof (ppguid->prefix)) == 0) + /* if the writer is owned by the participant itself, we're not interested */ + return NULL; + + privguid.prefix = *src_guid_prefix; + privguid.entityid = ddsi_to_entityid (DDSI_ENTITYID_PARTICIPANT); + ddsi_plist_init_empty(&pp_plist); + + if (ddsi_vendor_is_cloud (vendorid)) + { + ddsi_vendorid_t actual_vendorid; + /* Some endpoint that we discovered through the DS, but then it must have at least some locators */ + GVTRACE (" from-DS %"PRIx32":%"PRIx32":%"PRIx32":%"PRIx32, PGUID (privguid)); + /* avoid "no address" case, so we never create the proxy participant for nothing (FIXME: rework some of this) */ + if (!(datap->present & (PP_UNICAST_LOCATOR | PP_MULTICAST_LOCATOR))) + { + GVTRACE (" data locator absent\n"); + goto err; + } + GVTRACE (" new-proxypp "PGUIDFMT"\n", PGUID (*ppguid)); + /* We need to handle any source of entities, but we really want to try to keep the GIDs (and + certainly the systemId component) unchanged for OSPL. The new proxy participant will take + the GID from the GUID if it is from a "modern" OSPL that advertises it includes all GIDs in + the endpoint discovery; else if it is OSPL it will take at the systemId and fake the rest. + However, (1) Cloud filters out the GIDs from the discovery, and (2) DDSI2 deliberately + doesn't include the GID for internally generated endpoints (such as the fictitious transient + data readers) to signal that these are internal and have no GID (and not including a GID if + there is none is quite a reasonable approach). Point (2) means we have no reliable way of + determining whether GIDs are included based on the first endpoint, and so there is no point + doing anything about (1). That means we fall back to the legacy mode of locally generating + GIDs but leaving the system id unchanged if the remote is OSPL. */ + actual_vendorid = (datap->present & PP_VENDORID) ? datap->vendorid : vendorid; + (void) ddsi_new_proxy_participant (gv, ppguid, 0, &privguid, ddsi_new_addrset(), ddsi_new_addrset(), &pp_plist, DDS_INFINITY, actual_vendorid, DDSI_CF_IMPLICITLY_CREATED_PROXYPP, timestamp, seq); + } + else if (ppguid->prefix.u[0] == src_guid_prefix->u[0] && ddsi_vendor_is_eclipse_or_opensplice (vendorid)) + { + /* FIXME: requires address sets to be those of ddsi2, no built-in + readers or writers, only if remote ddsi2 is provably running + with a minimal built-in endpoint set */ + struct ddsi_proxy_participant *privpp; + if ((privpp = ddsi_entidx_lookup_proxy_participant_guid (gv->entity_index, &privguid)) == NULL) { + GVTRACE (" unknown-src-proxypp?\n"); + goto err; + } else if (!privpp->is_ddsi2_pp) { + GVTRACE (" src-proxypp-not-ddsi2?\n"); + goto err; + } else if (!privpp->minimal_bes_mode) { + GVTRACE (" src-ddsi2-not-minimal-bes-mode?\n"); + goto err; + } else { + struct ddsi_addrset *as_default, *as_meta; + ddsi_plist_t tmp_plist; + GVTRACE (" from-ddsi2 "PGUIDFMT, PGUID (privguid)); + ddsi_plist_init_empty (&pp_plist); + + ddsrt_mutex_lock (&privpp->e.lock); + as_default = ddsi_ref_addrset(privpp->as_default); + as_meta = ddsi_ref_addrset(privpp->as_meta); + /* copy just what we need */ + tmp_plist = *privpp->plist; + tmp_plist.present = PP_PARTICIPANT_GUID | PP_ADLINK_PARTICIPANT_VERSION_INFO; + tmp_plist.participant_guid = *ppguid; + ddsi_plist_mergein_missing (&pp_plist, &tmp_plist, ~(uint64_t)0, ~(uint64_t)0); + ddsrt_mutex_unlock (&privpp->e.lock); + + pp_plist.adlink_participant_version_info.flags &= ~DDSI_ADLINK_FL_PARTICIPANT_IS_DDSI2; + ddsi_new_proxy_participant (gv, ppguid, 0, &privguid, as_default, as_meta, &pp_plist, DDS_INFINITY, vendorid, DDSI_CF_IMPLICITLY_CREATED_PROXYPP | DDSI_CF_PROXYPP_NO_SPDP, timestamp, seq); + } + } + + err: + ddsi_plist_fini (&pp_plist); + return ddsi_entidx_lookup_proxy_participant_guid (gv->entity_index, ppguid); +} + +bool ddsi_check_sedp_kind_and_guid (ddsi_sedp_kind_t sedp_kind, const ddsi_guid_t *entity_guid) +{ + switch (sedp_kind) + { + case SEDP_KIND_TOPIC: + return ddsi_is_topic_entityid (entity_guid->entityid); + case SEDP_KIND_WRITER: + return ddsi_is_writer_entityid (entity_guid->entityid); + case SEDP_KIND_READER: + return ddsi_is_reader_entityid (entity_guid->entityid); + } + assert (0); + return false; +} + +bool ddsi_handle_sedp_checks (struct ddsi_domaingv * const gv, ddsi_sedp_kind_t sedp_kind, ddsi_guid_t *entity_guid, ddsi_plist_t *datap, + const ddsi_guid_prefix_t *src_guid_prefix, ddsi_vendorid_t vendorid, ddsrt_wctime_t timestamp, + struct ddsi_proxy_participant **proxypp, ddsi_guid_t *ppguid) +{ +#define E(msg, lbl) do { GVLOGDISC (msg); return false; } while (0) + if (!ddsi_check_sedp_kind_and_guid (sedp_kind, entity_guid)) + E (" SEDP topic/GUID entity kind mismatch\n", err); + ppguid->prefix = entity_guid->prefix; + ppguid->entityid.u = DDSI_ENTITYID_PARTICIPANT; + // Accept the presence of a participant GUID, but only if it matches + if ((datap->present & PP_PARTICIPANT_GUID) && memcmp (&datap->participant_guid, ppguid, sizeof (*ppguid)) != 0) + E (" endpoint/participant GUID mismatch", err); + if (ddsi_is_deleted_participant_guid (gv->deleted_participants, ppguid, DDSI_DELETED_PPGUID_REMOTE)) + E (" local dead pp?\n", err); + if (ddsi_entidx_lookup_participant_guid (gv->entity_index, ppguid) != NULL) + E (" local pp?\n", err); + if (ddsi_is_builtin_entityid (entity_guid->entityid, vendorid)) + E (" built-in\n", err); + if (!(datap->qos.present & DDSI_QP_TOPIC_NAME)) + E (" no topic?\n", err); + if (!(datap->qos.present & DDSI_QP_TYPE_NAME)) + E (" no typename?\n", err); + if ((*proxypp = ddsi_entidx_lookup_proxy_participant_guid (gv->entity_index, ppguid)) == NULL) + { + GVLOGDISC (" unknown-proxypp"); + if ((*proxypp = ddsi_implicitly_create_proxypp (gv, ppguid, datap, src_guid_prefix, vendorid, timestamp, 0)) == NULL) + E ("?\n", err); + /* Repeat regular SEDP trace for convenience */ + GVLOGDISC ("SEDP ST0 "PGUIDFMT" (cont)", PGUID (*entity_guid)); + } + return true; +#undef E +} + +static void ddsi_handle_sedp (const struct ddsi_receiver_state *rst, ddsi_seqno_t seq, struct ddsi_serdata *serdata, ddsi_sedp_kind_t sedp_kind) +{ + ddsi_plist_t decoded_data; + if (ddsi_serdata_to_sample (serdata, &decoded_data, NULL, NULL)) + { + struct ddsi_domaingv * const gv = rst->gv; + GVLOGDISC ("SEDP ST%"PRIx32, serdata->statusinfo); + switch (serdata->statusinfo & (DDSI_STATUSINFO_DISPOSE | DDSI_STATUSINFO_UNREGISTER)) + { + case 0: + switch (sedp_kind) + { + case SEDP_KIND_TOPIC: +#ifdef DDS_HAS_TOPIC_DISCOVERY + ddsi_handle_sedp_alive_topic (rst, seq, &decoded_data, &rst->src_guid_prefix, rst->vendor, serdata->timestamp); +#endif + break; + case SEDP_KIND_READER: + case SEDP_KIND_WRITER: + ddsi_handle_sedp_alive_endpoint (rst, seq, &decoded_data, sedp_kind, &rst->src_guid_prefix, rst->vendor, serdata->timestamp); + break; + } + break; + case DDSI_STATUSINFO_DISPOSE: + case DDSI_STATUSINFO_UNREGISTER: + case (DDSI_STATUSINFO_DISPOSE | DDSI_STATUSINFO_UNREGISTER): + switch (sedp_kind) + { + case SEDP_KIND_TOPIC: +#ifdef DDS_HAS_TOPIC_DISCOVERY + ddsi_handle_sedp_dead_topic (rst, &decoded_data, serdata->timestamp); +#endif + break; + case SEDP_KIND_READER: + case SEDP_KIND_WRITER: + ddsi_handle_sedp_dead_endpoint (rst, &decoded_data, sedp_kind, serdata->timestamp); + break; + } + break; + } + ddsi_plist_fini (&decoded_data); + } +} + #ifdef DDS_HAS_TYPE_DISCOVERY static void handle_typelookup (const struct ddsi_receiver_state *rst, ddsi_entityid_t wr_entity_id, struct ddsi_serdata *serdata) { diff --git a/src/core/ddsi/src/ddsi_discovery_endpoint.c b/src/core/ddsi/src/ddsi_discovery_endpoint.c index d6f4860ab5..97f43aec6c 100644 --- a/src/core/ddsi/src/ddsi_discovery_endpoint.c +++ b/src/core/ddsi/src/ddsi_discovery_endpoint.c @@ -19,8 +19,8 @@ #include "dds/ddsrt/heap.h" #include "dds/ddsrt/log.h" #include "dds/ddsi/ddsi_domaingv.h" +#include "ddsi__discovery.h" #include "ddsi__discovery_addrset.h" -#include "ddsi__discovery_sedp.h" #include "ddsi__discovery_endpoint.h" #include "ddsi__serdata_plist.h" #include "ddsi__entity_index.h" @@ -106,12 +106,6 @@ static void add_iox_locator_to_ps(const ddsi_locator_t* loc, struct add_locator_ } #endif -/****************************************************************************** - *** - *** SEDP - *** - *****************************************************************************/ - static int sedp_write_endpoint_impl ( struct ddsi_writer *wr, int alive, const ddsi_guid_t *guid, diff --git a/src/core/ddsi/src/ddsi_discovery_sedp.c b/src/core/ddsi/src/ddsi_discovery_sedp.c deleted file mode 100644 index a9bdf313b1..0000000000 --- a/src/core/ddsi/src/ddsi_discovery_sedp.c +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Copyright(c) 2023 ZettaScale Technology and others - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License - * v. 1.0 which is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause - */ -#include -#include -#include -#include - -#include "dds/ddsi/ddsi_domaingv.h" -#include "ddsi__discovery_sedp.h" -#include "ddsi__discovery_endpoint.h" -#ifdef DDS_HAS_TOPIC_DISCOVERY -#include "ddsi__discovery_topic.h" -#endif -#include "ddsi__serdata_plist.h" -#include "ddsi__entity_index.h" -#include "ddsi__entity.h" -#include "ddsi__security_omg.h" -#include "ddsi__endpoint.h" -#include "ddsi__plist.h" -#include "ddsi__proxy_participant.h" -#include "ddsi__topic.h" -#include "ddsi__vendor.h" - -struct ddsi_writer *ddsi_get_sedp_writer (const struct ddsi_participant *pp, unsigned entityid) -{ - struct ddsi_writer *sedp_wr = ddsi_get_builtin_writer (pp, entityid); - if (sedp_wr == NULL) - DDS_FATAL ("sedp_write_writer: no SEDP builtin writer %x for "PGUIDFMT"\n", entityid, PGUID (pp->e.guid)); - return sedp_wr; -} - -struct ddsi_proxy_participant *ddsi_implicitly_create_proxypp (struct ddsi_domaingv *gv, const ddsi_guid_t *ppguid, ddsi_plist_t *datap /* note: potentially modifies datap */, const ddsi_guid_prefix_t *src_guid_prefix, ddsi_vendorid_t vendorid, ddsrt_wctime_t timestamp, ddsi_seqno_t seq) -{ - ddsi_guid_t privguid; - ddsi_plist_t pp_plist; - - if (memcmp (&ppguid->prefix, src_guid_prefix, sizeof (ppguid->prefix)) == 0) - /* if the writer is owned by the participant itself, we're not interested */ - return NULL; - - privguid.prefix = *src_guid_prefix; - privguid.entityid = ddsi_to_entityid (DDSI_ENTITYID_PARTICIPANT); - ddsi_plist_init_empty(&pp_plist); - - if (ddsi_vendor_is_cloud (vendorid)) - { - ddsi_vendorid_t actual_vendorid; - /* Some endpoint that we discovered through the DS, but then it must have at least some locators */ - GVTRACE (" from-DS %"PRIx32":%"PRIx32":%"PRIx32":%"PRIx32, PGUID (privguid)); - /* avoid "no address" case, so we never create the proxy participant for nothing (FIXME: rework some of this) */ - if (!(datap->present & (PP_UNICAST_LOCATOR | PP_MULTICAST_LOCATOR))) - { - GVTRACE (" data locator absent\n"); - goto err; - } - GVTRACE (" new-proxypp "PGUIDFMT"\n", PGUID (*ppguid)); - /* We need to handle any source of entities, but we really want to try to keep the GIDs (and - certainly the systemId component) unchanged for OSPL. The new proxy participant will take - the GID from the GUID if it is from a "modern" OSPL that advertises it includes all GIDs in - the endpoint discovery; else if it is OSPL it will take at the systemId and fake the rest. - However, (1) Cloud filters out the GIDs from the discovery, and (2) DDSI2 deliberately - doesn't include the GID for internally generated endpoints (such as the fictitious transient - data readers) to signal that these are internal and have no GID (and not including a GID if - there is none is quite a reasonable approach). Point (2) means we have no reliable way of - determining whether GIDs are included based on the first endpoint, and so there is no point - doing anything about (1). That means we fall back to the legacy mode of locally generating - GIDs but leaving the system id unchanged if the remote is OSPL. */ - actual_vendorid = (datap->present & PP_VENDORID) ? datap->vendorid : vendorid; - (void) ddsi_new_proxy_participant (gv, ppguid, 0, &privguid, ddsi_new_addrset(), ddsi_new_addrset(), &pp_plist, DDS_INFINITY, actual_vendorid, DDSI_CF_IMPLICITLY_CREATED_PROXYPP, timestamp, seq); - } - else if (ppguid->prefix.u[0] == src_guid_prefix->u[0] && ddsi_vendor_is_eclipse_or_opensplice (vendorid)) - { - /* FIXME: requires address sets to be those of ddsi2, no built-in - readers or writers, only if remote ddsi2 is provably running - with a minimal built-in endpoint set */ - struct ddsi_proxy_participant *privpp; - if ((privpp = ddsi_entidx_lookup_proxy_participant_guid (gv->entity_index, &privguid)) == NULL) { - GVTRACE (" unknown-src-proxypp?\n"); - goto err; - } else if (!privpp->is_ddsi2_pp) { - GVTRACE (" src-proxypp-not-ddsi2?\n"); - goto err; - } else if (!privpp->minimal_bes_mode) { - GVTRACE (" src-ddsi2-not-minimal-bes-mode?\n"); - goto err; - } else { - struct ddsi_addrset *as_default, *as_meta; - ddsi_plist_t tmp_plist; - GVTRACE (" from-ddsi2 "PGUIDFMT, PGUID (privguid)); - ddsi_plist_init_empty (&pp_plist); - - ddsrt_mutex_lock (&privpp->e.lock); - as_default = ddsi_ref_addrset(privpp->as_default); - as_meta = ddsi_ref_addrset(privpp->as_meta); - /* copy just what we need */ - tmp_plist = *privpp->plist; - tmp_plist.present = PP_PARTICIPANT_GUID | PP_ADLINK_PARTICIPANT_VERSION_INFO; - tmp_plist.participant_guid = *ppguid; - ddsi_plist_mergein_missing (&pp_plist, &tmp_plist, ~(uint64_t)0, ~(uint64_t)0); - ddsrt_mutex_unlock (&privpp->e.lock); - - pp_plist.adlink_participant_version_info.flags &= ~DDSI_ADLINK_FL_PARTICIPANT_IS_DDSI2; - ddsi_new_proxy_participant (gv, ppguid, 0, &privguid, as_default, as_meta, &pp_plist, DDS_INFINITY, vendorid, DDSI_CF_IMPLICITLY_CREATED_PROXYPP | DDSI_CF_PROXYPP_NO_SPDP, timestamp, seq); - } - } - - err: - ddsi_plist_fini (&pp_plist); - return ddsi_entidx_lookup_proxy_participant_guid (gv->entity_index, ppguid); -} - -bool ddsi_check_sedp_kind_and_guid (ddsi_sedp_kind_t sedp_kind, const ddsi_guid_t *entity_guid) -{ - switch (sedp_kind) - { - case SEDP_KIND_TOPIC: - return ddsi_is_topic_entityid (entity_guid->entityid); - case SEDP_KIND_WRITER: - return ddsi_is_writer_entityid (entity_guid->entityid); - case SEDP_KIND_READER: - return ddsi_is_reader_entityid (entity_guid->entityid); - } - assert (0); - return false; -} - -bool ddsi_handle_sedp_checks (struct ddsi_domaingv * const gv, ddsi_sedp_kind_t sedp_kind, ddsi_guid_t *entity_guid, ddsi_plist_t *datap, - const ddsi_guid_prefix_t *src_guid_prefix, ddsi_vendorid_t vendorid, ddsrt_wctime_t timestamp, - struct ddsi_proxy_participant **proxypp, ddsi_guid_t *ppguid) -{ -#define E(msg, lbl) do { GVLOGDISC (msg); return false; } while (0) - if (!ddsi_check_sedp_kind_and_guid (sedp_kind, entity_guid)) - E (" SEDP topic/GUID entity kind mismatch\n", err); - ppguid->prefix = entity_guid->prefix; - ppguid->entityid.u = DDSI_ENTITYID_PARTICIPANT; - // Accept the presence of a participant GUID, but only if it matches - if ((datap->present & PP_PARTICIPANT_GUID) && memcmp (&datap->participant_guid, ppguid, sizeof (*ppguid)) != 0) - E (" endpoint/participant GUID mismatch", err); - if (ddsi_is_deleted_participant_guid (gv->deleted_participants, ppguid, DDSI_DELETED_PPGUID_REMOTE)) - E (" local dead pp?\n", err); - if (ddsi_entidx_lookup_participant_guid (gv->entity_index, ppguid) != NULL) - E (" local pp?\n", err); - if (ddsi_is_builtin_entityid (entity_guid->entityid, vendorid)) - E (" built-in\n", err); - if (!(datap->qos.present & DDSI_QP_TOPIC_NAME)) - E (" no topic?\n", err); - if (!(datap->qos.present & DDSI_QP_TYPE_NAME)) - E (" no typename?\n", err); - if ((*proxypp = ddsi_entidx_lookup_proxy_participant_guid (gv->entity_index, ppguid)) == NULL) - { - GVLOGDISC (" unknown-proxypp"); - if ((*proxypp = ddsi_implicitly_create_proxypp (gv, ppguid, datap, src_guid_prefix, vendorid, timestamp, 0)) == NULL) - E ("?\n", err); - /* Repeat regular SEDP trace for convenience */ - GVLOGDISC ("SEDP ST0 "PGUIDFMT" (cont)", PGUID (*entity_guid)); - } - return true; -#undef E -} - -void ddsi_handle_sedp (const struct ddsi_receiver_state *rst, ddsi_seqno_t seq, struct ddsi_serdata *serdata, ddsi_sedp_kind_t sedp_kind) -{ - ddsi_plist_t decoded_data; - if (ddsi_serdata_to_sample (serdata, &decoded_data, NULL, NULL)) - { - struct ddsi_domaingv * const gv = rst->gv; - GVLOGDISC ("SEDP ST%"PRIx32, serdata->statusinfo); - switch (serdata->statusinfo & (DDSI_STATUSINFO_DISPOSE | DDSI_STATUSINFO_UNREGISTER)) - { - case 0: - switch (sedp_kind) - { - case SEDP_KIND_TOPIC: -#ifdef DDS_HAS_TOPIC_DISCOVERY - ddsi_handle_sedp_alive_topic (rst, seq, &decoded_data, &rst->src_guid_prefix, rst->vendor, serdata->timestamp); -#endif - break; - case SEDP_KIND_READER: - case SEDP_KIND_WRITER: - ddsi_handle_sedp_alive_endpoint (rst, seq, &decoded_data, sedp_kind, &rst->src_guid_prefix, rst->vendor, serdata->timestamp); - break; - } - break; - case DDSI_STATUSINFO_DISPOSE: - case DDSI_STATUSINFO_UNREGISTER: - case (DDSI_STATUSINFO_DISPOSE | DDSI_STATUSINFO_UNREGISTER): - switch (sedp_kind) - { - case SEDP_KIND_TOPIC: -#ifdef DDS_HAS_TOPIC_DISCOVERY - ddsi_handle_sedp_dead_topic (rst, &decoded_data, serdata->timestamp); -#endif - break; - case SEDP_KIND_READER: - case SEDP_KIND_WRITER: - ddsi_handle_sedp_dead_endpoint (rst, &decoded_data, sedp_kind, serdata->timestamp); - break; - } - break; - } - ddsi_plist_fini (&decoded_data); - } -} diff --git a/src/core/ddsi/src/ddsi_discovery_topic.c b/src/core/ddsi/src/ddsi_discovery_topic.c index 7e22b23e87..79fb2c0d06 100644 --- a/src/core/ddsi/src/ddsi_discovery_topic.c +++ b/src/core/ddsi/src/ddsi_discovery_topic.c @@ -17,7 +17,7 @@ #include "dds/ddsrt/heap.h" #include "dds/ddsrt/log.h" #include "dds/ddsi/ddsi_domaingv.h" -#include "ddsi__discovery_sedp.h" +#include "ddsi__discovery.h" #include "ddsi__discovery_topic.h" #include "ddsi__entity_index.h" #include "ddsi__entity.h" From e3005bcfac0fb3e8077c1dccaa684e71a815c4a7 Mon Sep 17 00:00:00 2001 From: Erik Boasson Date: Thu, 23 Mar 2023 13:17:46 +0100 Subject: [PATCH 10/11] Rename ddsi_xevent_..._cb to ddsi_..._xevent_cb Having "xevent" at the beginning suggests these somehow belong to the xevent handling, which they do not. The intent was to use the name to suggest these are callbacks intended to be called through the xevent mechanism. Moving the "xevent" to the end of the name seems to do a better job at this. Signed-off-by: Erik Boasson --- src/core/ddsi/src/ddsi__acknack.h | 4 ++-- src/core/ddsi/src/ddsi__discovery_spdp.h | 4 ++-- src/core/ddsi/src/ddsi__hbcontrol.h | 4 ++-- src/core/ddsi/src/ddsi__pmd.h | 4 ++-- src/core/ddsi/src/ddsi_acknack.c | 4 ++-- src/core/ddsi/src/ddsi_discovery_spdp.c | 14 +++++++------- src/core/ddsi/src/ddsi_endpoint.c | 14 +++++++------- src/core/ddsi/src/ddsi_endpoint_match.c | 4 ++-- src/core/ddsi/src/ddsi_hbcontrol.c | 4 ++-- src/core/ddsi/src/ddsi_participant.c | 8 ++++---- src/core/ddsi/src/ddsi_pmd.c | 4 ++-- 11 files changed, 34 insertions(+), 34 deletions(-) diff --git a/src/core/ddsi/src/ddsi__acknack.h b/src/core/ddsi/src/ddsi__acknack.h index b497a3be8e..a71bb48f37 100644 --- a/src/core/ddsi/src/ddsi__acknack.h +++ b/src/core/ddsi/src/ddsi__acknack.h @@ -56,12 +56,12 @@ struct ddsi_add_acknack_info { /** @component incoming_rtps */ void ddsi_sched_acknack_if_needed (struct ddsi_xevent *ev, struct ddsi_proxy_writer *pwr, struct ddsi_pwr_rd_match *rwn, ddsrt_mtime_t tnow, bool avoid_suppressed_nack); -struct ddsi_xevent_acknack_cb_arg { +struct ddsi_acknack_xevent_cb_arg { ddsi_guid_t pwr_guid; ddsi_guid_t rd_guid; }; -void ddsi_xevent_acknack_cb (struct ddsi_domaingv *gv, struct ddsi_xevent *ev, struct ddsi_xpack *xp, void *varg, ddsrt_mtime_t tnow); +void ddsi_acknack_xevent_cb (struct ddsi_domaingv *gv, struct ddsi_xevent *ev, struct ddsi_xpack *xp, void *varg, ddsrt_mtime_t tnow); #if defined (__cplusplus) } diff --git a/src/core/ddsi/src/ddsi__discovery_spdp.h b/src/core/ddsi/src/ddsi__discovery_spdp.h index bd4dbd24cf..47213d29f9 100644 --- a/src/core/ddsi/src/ddsi__discovery_spdp.h +++ b/src/core/ddsi/src/ddsi__discovery_spdp.h @@ -48,12 +48,12 @@ int ddsi_spdp_write (struct ddsi_participant *pp); /** @component discovery */ int ddsi_spdp_dispose_unregister (struct ddsi_participant *pp); -struct ddsi_xevent_spdp_broadcast_cb_arg { +struct ddsi_spdp_broadcast_xevent_cb_arg { ddsi_guid_t pp_guid; }; /** @component discovery */ -void ddsi_xevent_spdp_broadcast_cb (struct ddsi_domaingv *gv, struct ddsi_xevent *ev, UNUSED_ARG (struct ddsi_xpack *xp), void *varg, ddsrt_mtime_t tnow); +void ddsi_spdp_broadcast_xevent_cb (struct ddsi_domaingv *gv, struct ddsi_xevent *ev, UNUSED_ARG (struct ddsi_xpack *xp), void *varg, ddsrt_mtime_t tnow); /** @component discovery */ void ddsi_handle_spdp (const struct ddsi_receiver_state *rst, ddsi_entityid_t pwr_entityid, ddsi_seqno_t seq, const struct ddsi_serdata *serdata); diff --git a/src/core/ddsi/src/ddsi__hbcontrol.h b/src/core/ddsi/src/ddsi__hbcontrol.h index 5a899d073d..ded4501880 100644 --- a/src/core/ddsi/src/ddsi__hbcontrol.h +++ b/src/core/ddsi/src/ddsi__hbcontrol.h @@ -49,11 +49,11 @@ struct ddsi_xmsg *ddsi_writer_hbcontrol_create_heartbeat (struct ddsi_writer *wr struct ddsi_xmsg *ddsi_writer_hbcontrol_p2p(struct ddsi_writer *wr, const struct ddsi_whc_state *whcst, int hbansreq, struct ddsi_proxy_reader *prd); #endif -struct ddsi_xevent_heartbeat_cb_arg { +struct ddsi_heartbeat_xevent_cb_arg { ddsi_guid_t wr_guid; }; -void ddsi_xevent_heartbeat_cb (struct ddsi_domaingv *gv, struct ddsi_xevent *ev, struct ddsi_xpack *xp, void *varg, ddsrt_mtime_t tnow); +void ddsi_heartbeat_xevent_cb (struct ddsi_domaingv *gv, struct ddsi_xevent *ev, struct ddsi_xpack *xp, void *varg, ddsrt_mtime_t tnow); #if defined (__cplusplus) } diff --git a/src/core/ddsi/src/ddsi__pmd.h b/src/core/ddsi/src/ddsi__pmd.h index 10f0b65c48..e16addd98d 100644 --- a/src/core/ddsi/src/ddsi__pmd.h +++ b/src/core/ddsi/src/ddsi__pmd.h @@ -49,12 +49,12 @@ void ddsi_write_pmd_message (struct ddsi_thread_state * const ts1, struct ddsi_x /** @component pmd */ void ddsi_handle_pmd_message (const struct ddsi_receiver_state *rst, struct ddsi_serdata *sample_common); -struct ddsi_xevent_write_pmd_message_cb_arg { +struct ddsi_write_pmd_message_xevent_cb_arg { ddsi_guid_t pp_guid; }; /** @component pmd */ -void ddsi_xevent_write_pmd_message_cb (struct ddsi_domaingv *gv, struct ddsi_xevent *ev, struct ddsi_xpack *xp, void *varg, ddsrt_mtime_t tnow); +void ddsi_write_pmd_message_xevent_cb (struct ddsi_domaingv *gv, struct ddsi_xevent *ev, struct ddsi_xpack *xp, void *varg, ddsrt_mtime_t tnow); #if defined (__cplusplus) } diff --git a/src/core/ddsi/src/ddsi_acknack.c b/src/core/ddsi/src/ddsi_acknack.c index f77579d5cb..1a3b83acf6 100644 --- a/src/core/ddsi/src/ddsi_acknack.c +++ b/src/core/ddsi/src/ddsi_acknack.c @@ -574,7 +574,7 @@ static struct ddsi_xmsg *make_preemptive_acknack (struct ddsi_xevent *ev, struct return msg; } -void ddsi_xevent_acknack_cb (struct ddsi_domaingv *gv, struct ddsi_xevent *ev, struct ddsi_xpack *xp, void *varg, ddsrt_mtime_t tnow) +void ddsi_acknack_xevent_cb (struct ddsi_domaingv *gv, struct ddsi_xevent *ev, struct ddsi_xpack *xp, void *varg, ddsrt_mtime_t tnow) { /* FIXME: ought to keep track of which NACKs are being generated in response to a Heartbeat. There is no point in having multiple @@ -585,7 +585,7 @@ void ddsi_xevent_acknack_cb (struct ddsi_domaingv *gv, struct ddsi_xevent *ev, s A little snag is that the defragmenter can throw out partial samples in favour of others, so MUST ensure that the defragmenter won't start threshing and fail to make progress! */ - struct ddsi_xevent_acknack_cb_arg const * const arg = varg; + struct ddsi_acknack_xevent_cb_arg const * const arg = varg; struct ddsi_proxy_writer *pwr; struct ddsi_xmsg *msg; struct ddsi_pwr_rd_match *rwn; diff --git a/src/core/ddsi/src/ddsi_discovery_spdp.c b/src/core/ddsi/src/ddsi_discovery_spdp.c index 04c455cc30..210f52c324 100644 --- a/src/core/ddsi/src/ddsi_discovery_spdp.c +++ b/src/core/ddsi/src/ddsi_discovery_spdp.c @@ -281,7 +281,7 @@ int ddsi_spdp_dispose_unregister (struct ddsi_participant *pp) return ret; } -struct xevent_spdp_directed_cb_arg { +struct ddsi_spdp_directed_xevent_cb_arg { ddsi_guid_t pp_guid; int nrepeats; ddsi_guid_prefix_t dest_proxypp_guid_prefix; @@ -334,9 +334,9 @@ static bool get_pp_and_spdp_wr (struct ddsi_domaingv *gv, const ddsi_guid_t *pp_ return true; } -static void xevent_spdp_directed_cb (struct ddsi_domaingv *gv, struct ddsi_xevent *ev, UNUSED_ARG (struct ddsi_xpack *xp), void *varg, ddsrt_mtime_t tnow) +static void ddsi_spdp_directed_xevent_cb (struct ddsi_domaingv *gv, struct ddsi_xevent *ev, UNUSED_ARG (struct ddsi_xpack *xp), void *varg, ddsrt_mtime_t tnow) { - struct xevent_spdp_directed_cb_arg * const arg = varg; + struct ddsi_spdp_directed_xevent_cb_arg * const arg = varg; struct ddsi_participant *pp; struct ddsi_writer *spdp_wr; @@ -396,10 +396,10 @@ static void resched_spdp_broadcast (struct ddsi_xevent *ev, struct ddsi_particip (void) ddsi_resched_xevent_if_earlier (ev, tnext); } -void ddsi_xevent_spdp_broadcast_cb (struct ddsi_domaingv *gv, struct ddsi_xevent *ev, UNUSED_ARG (struct ddsi_xpack *xp), void *varg, ddsrt_mtime_t tnow) +void ddsi_spdp_broadcast_xevent_cb (struct ddsi_domaingv *gv, struct ddsi_xevent *ev, UNUSED_ARG (struct ddsi_xpack *xp), void *varg, ddsrt_mtime_t tnow) { /* Like the writer pointer in the heartbeat event, the participant pointer in the spdp event is assumed valid. */ - struct ddsi_xevent_spdp_broadcast_cb_arg * const arg = varg; + struct ddsi_spdp_broadcast_xevent_cb_arg * const arg = varg; struct ddsi_participant *pp; struct ddsi_writer *spdp_wr; @@ -477,11 +477,11 @@ static void respond_to_spdp (const struct ddsi_domaingv *gv, const ddsi_guid_t * (void) ddsi_resched_xevent_if_earlier (pp->spdp_xevent, tsched); else { - struct xevent_spdp_directed_cb_arg arg = { + struct ddsi_spdp_directed_xevent_cb_arg arg = { .pp_guid = pp->e.guid, .nrepeats = 4, .dest_proxypp_guid_prefix = dest_proxypp_guid->prefix }; - ddsi_qxev_callback (gv->xevents, tsched, xevent_spdp_directed_cb, &arg, sizeof (arg), false); + ddsi_qxev_callback (gv->xevents, tsched, ddsi_spdp_directed_xevent_cb, &arg, sizeof (arg), false); } } ddsi_entidx_enum_participant_fini (&est); diff --git a/src/core/ddsi/src/ddsi_endpoint.c b/src/core/ddsi/src/ddsi_endpoint.c index ddbf51f249..4eb715318d 100644 --- a/src/core/ddsi/src/ddsi_endpoint.c +++ b/src/core/ddsi/src/ddsi_endpoint.c @@ -868,8 +868,8 @@ static void ddsi_new_writer_guid_common_init (struct ddsi_writer *wr, const char wr->heartbeat_xevent = NULL; else { - struct ddsi_xevent_heartbeat_cb_arg arg = {.wr_guid = wr->e.guid }; - wr->heartbeat_xevent = ddsi_qxev_callback (wr->evq, DDSRT_MTIME_NEVER, ddsi_xevent_heartbeat_cb, &arg, sizeof (arg), false); + struct ddsi_heartbeat_xevent_cb_arg arg = {.wr_guid = wr->e.guid }; + wr->heartbeat_xevent = ddsi_qxev_callback (wr->evq, DDSRT_MTIME_NEVER, ddsi_heartbeat_xevent_cb, &arg, sizeof (arg), false); } assert (wr->xqos->present & DDSI_QP_LIVELINESS); @@ -1267,13 +1267,13 @@ void ddsi_delete_local_orphan_writer (struct ddsi_local_orphan_writer *lowr) ddsrt_mutex_unlock (&lowr->wr.e.lock); } -struct xevent_delete_writer_cb_arg { +struct ddsi_delete_writer_xevent_cb_arg { ddsi_guid_t wr_guid; }; -static void xevent_delete_writer_cb (struct ddsi_domaingv *gv, struct ddsi_xevent *ev, UNUSED_ARG (struct ddsi_xpack *xp), void *varg, UNUSED_ARG (ddsrt_mtime_t tnow)) +static void ddsi_delete_writer_xevent_cb (struct ddsi_domaingv *gv, struct ddsi_xevent *ev, UNUSED_ARG (struct ddsi_xpack *xp), void *varg, UNUSED_ARG (ddsrt_mtime_t tnow)) { - struct xevent_delete_writer_cb_arg const * const arg = varg; + struct ddsi_delete_writer_xevent_cb_arg const * const arg = varg; /* don't worry if the writer is already gone by the time we get here, delete_writer_nolinger checks for that. */ GVTRACE ("handle_xevk_delete_writer: "PGUIDFMT"\n", PGUID (arg->wr_guid)); ddsi_delete_writer_nolinger (gv, &arg->wr_guid); @@ -1314,8 +1314,8 @@ dds_return_t ddsi_delete_writer (struct ddsi_domaingv *gv, const struct ddsi_gui GVLOGDISC ("delete_writer(guid "PGUIDFMT") - unack'ed samples, will delete when ack'd or at t = %"PRId32".%06"PRId32"\n", PGUID (*guid), tsec, tusec); - struct xevent_delete_writer_cb_arg arg = { .wr_guid = wr->e.guid }; - ddsi_qxev_callback (gv->xevents, tsched, xevent_delete_writer_cb, &arg, sizeof (arg), false); + struct ddsi_delete_writer_xevent_cb_arg arg = { .wr_guid = wr->e.guid }; + ddsi_qxev_callback (gv->xevents, tsched, ddsi_delete_writer_xevent_cb, &arg, sizeof (arg), false); } return 0; } diff --git a/src/core/ddsi/src/ddsi_endpoint_match.c b/src/core/ddsi/src/ddsi_endpoint_match.c index f3e6d0b288..a8f4db21ed 100644 --- a/src/core/ddsi/src/ddsi_endpoint_match.c +++ b/src/core/ddsi/src/ddsi_endpoint_match.c @@ -1078,8 +1078,8 @@ void ddsi_proxy_writer_add_connection (struct ddsi_proxy_writer *pwr, struct dds const ddsrt_mtime_t tsched = use_iceoryx ? DDSRT_MTIME_NEVER : ddsrt_mtime_add_duration (tnow, pwr->e.gv->config.preemptive_ack_delay); { - struct ddsi_xevent_acknack_cb_arg arg = { .pwr_guid = pwr->e.guid, .rd_guid = rd->e.guid }; - m->acknack_xevent = ddsi_qxev_callback (pwr->evq, tsched, ddsi_xevent_acknack_cb, &arg, sizeof (arg), false); + struct ddsi_acknack_xevent_cb_arg arg = { .pwr_guid = pwr->e.guid, .rd_guid = rd->e.guid }; + m->acknack_xevent = ddsi_qxev_callback (pwr->evq, tsched, ddsi_acknack_xevent_cb, &arg, sizeof (arg), false); } m->u.not_in_sync.reorder = ddsi_reorder_new (&pwr->e.gv->logconfig, DDSI_REORDER_MODE_NORMAL, secondary_reorder_maxsamples, pwr->e.gv->config.late_ack_mode); diff --git a/src/core/ddsi/src/ddsi_hbcontrol.c b/src/core/ddsi/src/ddsi_hbcontrol.c index 916e40b8e8..e2d4b5cd79 100644 --- a/src/core/ddsi/src/ddsi_hbcontrol.c +++ b/src/core/ddsi/src/ddsi_hbcontrol.c @@ -528,9 +528,9 @@ static void send_heartbeat_to_all_readers (struct ddsi_xpack *xp, struct ddsi_xe } #endif -void ddsi_xevent_heartbeat_cb (struct ddsi_domaingv *gv, struct ddsi_xevent *ev, struct ddsi_xpack *xp, void *varg, ddsrt_mtime_t tnow) +void ddsi_heartbeat_xevent_cb (struct ddsi_domaingv *gv, struct ddsi_xevent *ev, struct ddsi_xpack *xp, void *varg, ddsrt_mtime_t tnow) { - struct ddsi_xevent_heartbeat_cb_arg const * const arg = varg; + struct ddsi_heartbeat_xevent_cb_arg const * const arg = varg; struct ddsi_writer *wr; if ((wr = ddsi_entidx_lookup_writer_guid (gv->entity_index, &arg->wr_guid)) == NULL) { diff --git a/src/core/ddsi/src/ddsi_participant.c b/src/core/ddsi/src/ddsi_participant.c index 70a44f8cf6..7f7f9c9792 100644 --- a/src/core/ddsi/src/ddsi_participant.c +++ b/src/core/ddsi/src/ddsi_participant.c @@ -980,14 +980,14 @@ static dds_return_t new_participant_guid (ddsi_guid_t *ppguid, struct ddsi_domai fire before the calls return. If the initial sample wasn't accepted, all is lost, but we continue nonetheless, even though the participant won't be able to discover or be discovered. */ - struct ddsi_xevent_spdp_broadcast_cb_arg arg = { .pp_guid = pp->e.guid }; - pp->spdp_xevent = ddsi_qxev_callback (gv->xevents, ddsrt_mtime_add_duration (ddsrt_time_monotonic (), DDS_MSECS (100)), ddsi_xevent_spdp_broadcast_cb, &arg, sizeof (arg), false); + struct ddsi_spdp_broadcast_xevent_cb_arg arg = { .pp_guid = pp->e.guid }; + pp->spdp_xevent = ddsi_qxev_callback (gv->xevents, ddsrt_mtime_add_duration (ddsrt_time_monotonic (), DDS_MSECS (100)), ddsi_spdp_broadcast_xevent_cb, &arg, sizeof (arg), false); } { ddsrt_mtime_t tsched = (pp->plist->qos.liveliness.lease_duration == DDS_INFINITY) ? DDSRT_MTIME_NEVER : (ddsrt_mtime_t){0}; - struct ddsi_xevent_write_pmd_message_cb_arg arg = { .pp_guid = pp->e.guid }; - pp->pmd_update_xevent = ddsi_qxev_callback (gv->xevents, tsched, ddsi_xevent_write_pmd_message_cb, &arg, sizeof (arg), false); + struct ddsi_write_pmd_message_xevent_cb_arg arg = { .pp_guid = pp->e.guid }; + pp->pmd_update_xevent = ddsi_qxev_callback (gv->xevents, tsched, ddsi_write_pmd_message_xevent_cb, &arg, sizeof (arg), false); } #ifdef DDS_HAS_SECURITY diff --git a/src/core/ddsi/src/ddsi_pmd.c b/src/core/ddsi/src/ddsi_pmd.c index cc1674e20a..b8d1c8110d 100644 --- a/src/core/ddsi/src/ddsi_pmd.c +++ b/src/core/ddsi/src/ddsi_pmd.c @@ -125,10 +125,10 @@ void ddsi_handle_pmd_message (const struct ddsi_receiver_state *rst, struct ddsi RSTTRACE ("\n"); } -void ddsi_xevent_write_pmd_message_cb (struct ddsi_domaingv *gv, struct ddsi_xevent *ev, struct ddsi_xpack *xp, void *varg, ddsrt_mtime_t tnow) +void ddsi_write_pmd_message_xevent_cb (struct ddsi_domaingv *gv, struct ddsi_xevent *ev, struct ddsi_xpack *xp, void *varg, ddsrt_mtime_t tnow) { struct ddsi_thread_state * const thrst = ddsi_lookup_thread_state (); - struct ddsi_xevent_write_pmd_message_cb_arg const * const arg = varg; + struct ddsi_write_pmd_message_xevent_cb_arg const * const arg = varg; struct ddsi_participant *pp; dds_duration_t intv; ddsrt_mtime_t tnext; From ea9baa52ee33cdd2bbc0c136faaabcbcf9383d19 Mon Sep 17 00:00:00 2001 From: Erik Boasson Date: Thu, 23 Mar 2023 13:26:45 +0100 Subject: [PATCH 11/11] Fix component mislabelling after moving code Signed-off-by: Erik Boasson --- src/core/ddsi/src/ddsi__proxy_endpoint.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/ddsi/src/ddsi__proxy_endpoint.h b/src/core/ddsi/src/ddsi__proxy_endpoint.h index 8058ae97eb..d52395d210 100644 --- a/src/core/ddsi/src/ddsi__proxy_endpoint.h +++ b/src/core/ddsi/src/ddsi__proxy_endpoint.h @@ -49,10 +49,10 @@ struct ddsi_entity_common *ddsi_entity_common_from_proxy_endpoint_common (const /** @component ddsi_proxy_endpoint */ bool ddsi_is_proxy_endpoint (const struct ddsi_entity_common *e); -/** @component timed_events */ +/** @component ddsi_proxy_endpoint */ void ddsi_send_entityid_to_pwr (struct ddsi_proxy_writer *pwr, const ddsi_guid_t *guid); -/** @component timed_events */ +/** @component ddsi_proxy_endpoint */ void ddsi_send_entityid_to_prd (struct ddsi_proxy_reader *prd, const ddsi_guid_t *guid); /**