Update local delivery code for multiple sertopics
This also removes the code duplication for the handling delivery from local vs remote writers. (And it adds a test.) Signed-off-by: Erik Boasson <eb@ilities.com>
This commit is contained in:
parent
27d7c72626
commit
d92d491b83
13 changed files with 1205 additions and 189 deletions
|
@ -288,6 +288,7 @@ const struct ddsi_serdata_ops ddsi_serdata_ops_builtintopic = {
|
|||
.eqkey = serdata_builtin_eqkey,
|
||||
.free = serdata_builtin_free,
|
||||
.from_ser = 0,
|
||||
.from_ser_iov = 0,
|
||||
.from_keyhash = ddsi_serdata_builtin_from_keyhash,
|
||||
.from_sample = 0,
|
||||
.to_ser = serdata_builtin_to_ser,
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "dds/ddsi/q_entity.h"
|
||||
#include "dds/ddsi/q_radmin.h"
|
||||
#include "dds/ddsi/q_globals.h"
|
||||
#include "dds/ddsi/ddsi_deliver_locally.h"
|
||||
|
||||
dds_return_t dds_write (dds_entity_t writer, const void *data)
|
||||
{
|
||||
|
@ -71,80 +72,103 @@ dds_return_t dds_write_ts (dds_entity_t writer, const void *data, dds_time_t tim
|
|||
return ret;
|
||||
}
|
||||
|
||||
static dds_return_t try_store (struct ddsi_rhc *rhc, const struct ddsi_writer_info *pwr_info, struct ddsi_serdata *payload, struct ddsi_tkmap_instance *tk, dds_duration_t *max_block_ms)
|
||||
static struct reader *writer_first_in_sync_reader (struct entity_index *entity_index, struct entity_common *wrcmn, ddsrt_avl_iter_t *it)
|
||||
{
|
||||
while (! ddsi_rhc_store (rhc, pwr_info, payload, tk))
|
||||
assert (wrcmn->kind == EK_WRITER);
|
||||
struct writer *wr = (struct writer *) wrcmn;
|
||||
struct wr_rd_match *m = ddsrt_avl_iter_first (&wr_local_readers_treedef, &wr->local_readers, it);
|
||||
return m ? entidx_lookup_reader_guid (entity_index, &m->rd_guid) : NULL;
|
||||
}
|
||||
|
||||
static struct reader *writer_next_in_sync_reader (struct entity_index *entity_index, ddsrt_avl_iter_t *it)
|
||||
{
|
||||
struct wr_rd_match *m = ddsrt_avl_iter_next (it);
|
||||
return m ? entidx_lookup_reader_guid (entity_index, &m->rd_guid) : NULL;
|
||||
}
|
||||
|
||||
struct local_sourceinfo {
|
||||
const struct ddsi_sertopic *src_topic;
|
||||
struct ddsi_serdata *src_payload;
|
||||
struct ddsi_tkmap_instance *src_tk;
|
||||
nn_mtime_t timeout;
|
||||
};
|
||||
|
||||
static struct ddsi_serdata *local_make_sample (struct ddsi_tkmap_instance **tk, struct q_globals *gv, struct ddsi_sertopic const * const topic, void *vsourceinfo)
|
||||
{
|
||||
struct local_sourceinfo *si = vsourceinfo;
|
||||
if (topic == si->src_topic)
|
||||
{
|
||||
if (*max_block_ms > 0)
|
||||
*tk = si->src_tk;
|
||||
/* FIXME: see if this pair of refc increments can't be avoided
|
||||
They're needed because free_sample_after_delivery will always be called, but
|
||||
in the common case of a local writer and a single sertopic, make_sample doesn't
|
||||
actually create a sample, and so free_sample_after_delivery doesn't actually
|
||||
have to free anything */
|
||||
ddsi_tkmap_instance_ref (si->src_tk);
|
||||
return ddsi_serdata_ref (si->src_payload);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* ouch ... convert a serdata from one sertopic to another ... */
|
||||
ddsrt_iovec_t iov;
|
||||
uint32_t size = ddsi_serdata_size (si->src_payload);
|
||||
(void) ddsi_serdata_to_ser_ref (si->src_payload, 0, size, &iov);
|
||||
struct ddsi_serdata *d = ddsi_serdata_from_ser_iov (topic, si->src_payload->kind, 1, &iov, size);
|
||||
ddsi_serdata_to_ser_unref (si->src_payload, &iov);
|
||||
if (d)
|
||||
{
|
||||
dds_sleepfor (DDS_HEADBANG_TIMEOUT);
|
||||
*max_block_ms -= DDS_HEADBANG_TIMEOUT;
|
||||
d->statusinfo = si->src_payload->statusinfo;
|
||||
d->timestamp = si->src_payload->timestamp;
|
||||
*tk = ddsi_tkmap_lookup_instance_ref (gv->m_tkmap, d);
|
||||
}
|
||||
else
|
||||
{
|
||||
return DDS_RETCODE_TIMEOUT;
|
||||
DDS_CWARNING (&gv->logconfig, "local: deserialization %s/%s failed in topic type conversion\n", topic->name, topic->type_name);
|
||||
}
|
||||
return d;
|
||||
}
|
||||
}
|
||||
|
||||
static dds_return_t local_on_delivery_failure_fastpath (struct entity_common *source_entity, bool source_entity_locked, struct local_reader_ary *fastpath_rdary, void *vsourceinfo)
|
||||
{
|
||||
(void) fastpath_rdary;
|
||||
(void) source_entity_locked;
|
||||
assert (source_entity->kind == EK_WRITER);
|
||||
struct writer *wr = (struct writer *) source_entity;
|
||||
struct local_sourceinfo *si = vsourceinfo;
|
||||
nn_mtime_t tnow = now_mt ();
|
||||
if (si->timeout.v == 0)
|
||||
si->timeout = add_duration_to_mtime (tnow, wr->xqos->reliability.max_blocking_time);
|
||||
if (tnow.v >= si->timeout.v)
|
||||
return DDS_RETCODE_TIMEOUT;
|
||||
else
|
||||
{
|
||||
dds_sleepfor (DDS_HEADBANG_TIMEOUT);
|
||||
return DDS_RETCODE_OK;
|
||||
}
|
||||
return DDS_RETCODE_OK;
|
||||
}
|
||||
|
||||
static dds_return_t deliver_locally (struct writer *wr, struct ddsi_serdata *payload, struct ddsi_tkmap_instance *tk)
|
||||
{
|
||||
dds_return_t ret = DDS_RETCODE_OK;
|
||||
ddsrt_mutex_lock (&wr->rdary.rdary_lock);
|
||||
if (wr->rdary.fastpath_ok)
|
||||
{
|
||||
struct reader ** const rdary = wr->rdary.rdary;
|
||||
if (rdary[0])
|
||||
{
|
||||
dds_duration_t max_block_ms = wr->xqos->reliability.max_blocking_time;
|
||||
struct ddsi_writer_info pwr_info;
|
||||
ddsi_make_writer_info (&pwr_info, &wr->e, wr->xqos, payload->statusinfo);
|
||||
for (uint32_t i = 0; rdary[i]; i++) {
|
||||
DDS_CTRACE (&wr->e.gv->logconfig, "reader "PGUIDFMT"\n", PGUID (rdary[i]->e.guid));
|
||||
if ((ret = try_store (rdary[i]->rhc, &pwr_info, payload, tk, &max_block_ms)) != DDS_RETCODE_OK)
|
||||
break;
|
||||
}
|
||||
}
|
||||
ddsrt_mutex_unlock (&wr->rdary.rdary_lock);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* When deleting, pwr is no longer accessible via the hash
|
||||
tables, and consequently, a reader may be deleted without
|
||||
it being possible to remove it from rdary. The primary
|
||||
reason rdary exists is to avoid locking the proxy writer
|
||||
but this is less of an issue when we are deleting it, so
|
||||
we fall back to using the GUIDs so that we can deliver all
|
||||
samples we received from it. As writer being deleted any
|
||||
reliable samples that are rejected are simply discarded. */
|
||||
ddsrt_avl_iter_t it;
|
||||
struct pwr_rd_match *m;
|
||||
struct ddsi_writer_info wrinfo;
|
||||
const struct entity_index *gh = wr->e.gv->entity_index;
|
||||
dds_duration_t max_block_ms = wr->xqos->reliability.max_blocking_time;
|
||||
ddsrt_mutex_unlock (&wr->rdary.rdary_lock);
|
||||
ddsi_make_writer_info (&wrinfo, &wr->e, wr->xqos, payload->statusinfo);
|
||||
ddsrt_mutex_lock (&wr->e.lock);
|
||||
for (m = ddsrt_avl_iter_first (&wr_local_readers_treedef, &wr->local_readers, &it); m != NULL; m = ddsrt_avl_iter_next (&it))
|
||||
{
|
||||
struct reader *rd;
|
||||
if ((rd = entidx_lookup_reader_guid (gh, &m->rd_guid)) != NULL)
|
||||
{
|
||||
DDS_CTRACE (&wr->e.gv->logconfig, "reader-via-guid "PGUIDFMT"\n", PGUID (rd->e.guid));
|
||||
/* Copied the return value ignore from DDSI deliver_user_data () function. */
|
||||
if ((ret = try_store (rd->rhc, &wrinfo, payload, tk, &max_block_ms)) != DDS_RETCODE_OK)
|
||||
break;
|
||||
}
|
||||
}
|
||||
ddsrt_mutex_unlock (&wr->e.lock);
|
||||
}
|
||||
|
||||
if (ret == DDS_RETCODE_TIMEOUT)
|
||||
{
|
||||
static const struct deliver_locally_ops deliver_locally_ops = {
|
||||
.makesample = local_make_sample,
|
||||
.first_reader = writer_first_in_sync_reader,
|
||||
.next_reader = writer_next_in_sync_reader,
|
||||
.on_failure_fastpath = local_on_delivery_failure_fastpath
|
||||
};
|
||||
struct local_sourceinfo sourceinfo = {
|
||||
.src_topic = wr->topic,
|
||||
.src_payload = payload,
|
||||
.src_tk = tk,
|
||||
.timeout = { 0 },
|
||||
};
|
||||
dds_return_t rc;
|
||||
struct ddsi_writer_info wrinfo;
|
||||
ddsi_make_writer_info (&wrinfo, &wr->e, wr->xqos, payload->statusinfo);
|
||||
rc = deliver_locally_allinsync (wr->e.gv, &wr->e, false, &wr->rdary, &wrinfo, &deliver_locally_ops, &sourceinfo);
|
||||
if (rc == DDS_RETCODE_TIMEOUT)
|
||||
DDS_CERROR (&wr->e.gv->logconfig, "The writer could not deliver data on time, probably due to a local reader resources being full\n");
|
||||
}
|
||||
return ret;
|
||||
return rc;
|
||||
}
|
||||
|
||||
dds_return_t dds_write_impl (dds_writer *wr, const void * data, dds_time_t tstamp, dds_write_action action)
|
||||
|
|
|
@ -30,6 +30,7 @@ set(ddsc_test_sources
|
|||
"instance_get_key.c"
|
||||
"listener.c"
|
||||
"liveliness.c"
|
||||
"multi_sertopic.c"
|
||||
"participant.c"
|
||||
"publisher.c"
|
||||
"qos.c"
|
||||
|
|
609
src/core/ddsc/tests/multi_sertopic.c
Normal file
609
src/core/ddsc/tests/multi_sertopic.c
Normal file
|
@ -0,0 +1,609 @@
|
|||
/*
|
||||
* Copyright(c) 2006 to 2018 ADLINK Technology Limited 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 <assert.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "dds/dds.h"
|
||||
#include "CUnit/Theory.h"
|
||||
#include "Space.h"
|
||||
#include "config_env.h"
|
||||
|
||||
#include "dds/version.h"
|
||||
#include "dds__entity.h"
|
||||
#include "dds/ddsi/q_entity.h"
|
||||
#include "dds/ddsi/ddsi_serdata.h"
|
||||
#include "dds/ddsi/ddsi_entity_index.h"
|
||||
#include "dds/ddsrt/cdtors.h"
|
||||
#include "dds/ddsrt/misc.h"
|
||||
#include "dds/ddsrt/process.h"
|
||||
#include "dds/ddsrt/threads.h"
|
||||
#include "dds/ddsrt/environ.h"
|
||||
#include "dds/ddsrt/atomics.h"
|
||||
#include "dds/ddsrt/time.h"
|
||||
|
||||
#define DDS_DOMAINID_PUB 0
|
||||
#define DDS_DOMAINID_SUB 1
|
||||
#define DDS_CONFIG_NO_PORT_GAIN "${CYCLONEDDS_URI}${CYCLONEDDS_URI:+,}<Discovery><ExternalDomainId>0</ExternalDomainId></Discovery>"
|
||||
#define DDS_CONFIG_NO_PORT_GAIN_LOG "${CYCLONEDDS_URI}${CYCLONEDDS_URI:+,}<Tracing><OutputFile>cyclonedds_multi_sertopic_tests.${CYCLONEDDS_DOMAIN_ID}.${CYCLONEDDS_PID}.log</OutputFile><Verbosity>finest</Verbosity></Tracing><Discovery><ExternalDomainId>0</ExternalDomainId></Discovery>"
|
||||
|
||||
/* IDL preprocessing is not really friendly towards creating multiple descriptors
|
||||
for the same type name with different definitions, so we do it by hand. */
|
||||
struct uint32_seq {
|
||||
uint32_t _maximum;
|
||||
uint32_t _length;
|
||||
uint32_t *_buffer;
|
||||
bool _release;
|
||||
};
|
||||
|
||||
struct two_uint32 {
|
||||
uint32_t v[2];
|
||||
};
|
||||
|
||||
struct two_uint32_seq {
|
||||
uint32_t _maximum;
|
||||
uint32_t _length;
|
||||
struct two_uint32 *_buffer;
|
||||
bool _release;
|
||||
};
|
||||
|
||||
struct type_seq {
|
||||
struct uint32_seq x;
|
||||
};
|
||||
|
||||
struct type_ary {
|
||||
uint32_t x[4];
|
||||
};
|
||||
|
||||
struct type_uni {
|
||||
uint32_t _d;
|
||||
union
|
||||
{
|
||||
struct two_uint32_seq a;
|
||||
uint32_t b[4];
|
||||
} _u;
|
||||
};
|
||||
|
||||
static const dds_topic_descriptor_t type_seq_desc =
|
||||
{
|
||||
.m_size = sizeof (struct type_seq),
|
||||
.m_align = sizeof (void *),
|
||||
.m_flagset = DDS_TOPIC_NO_OPTIMIZE,
|
||||
.m_nkeys = 0,
|
||||
.m_typename = "multi_sertopic_type",
|
||||
.m_keys = NULL,
|
||||
.m_nops = 2,
|
||||
.m_ops = (const uint32_t[]) {
|
||||
DDS_OP_ADR | DDS_OP_TYPE_SEQ | DDS_OP_SUBTYPE_4BY, offsetof (struct type_seq, x),
|
||||
DDS_OP_RTS
|
||||
},
|
||||
.m_meta = "" /* this is on its way out anyway */
|
||||
};
|
||||
|
||||
static const dds_topic_descriptor_t type_ary_desc =
|
||||
{
|
||||
.m_size = sizeof (struct type_ary),
|
||||
.m_align = 4u,
|
||||
.m_flagset = DDS_TOPIC_NO_OPTIMIZE,
|
||||
.m_nkeys = 0,
|
||||
.m_typename = "multi_sertopic_type",
|
||||
.m_keys = NULL,
|
||||
.m_nops = 2,
|
||||
.m_ops = (const uint32_t[]) {
|
||||
DDS_OP_ADR | DDS_OP_TYPE_ARR | DDS_OP_SUBTYPE_4BY, offsetof (struct type_ary, x), 4,
|
||||
DDS_OP_RTS
|
||||
},
|
||||
.m_meta = "" /* this is on its way out anyway */
|
||||
};
|
||||
|
||||
static const dds_topic_descriptor_t type_uni_desc =
|
||||
{
|
||||
.m_size = sizeof (struct type_uni),
|
||||
.m_align = sizeof (void *),
|
||||
.m_flagset = DDS_TOPIC_NO_OPTIMIZE | DDS_TOPIC_CONTAINS_UNION,
|
||||
.m_nkeys = 0,
|
||||
.m_typename = "multi_sertopic_type",
|
||||
.m_keys = NULL,
|
||||
.m_nops = 8,
|
||||
.m_ops = (const uint32_t[]) {
|
||||
DDS_OP_ADR | DDS_OP_TYPE_UNI | DDS_OP_SUBTYPE_4BY | DDS_OP_FLAG_DEF, offsetof (struct type_uni, _d), 2u, (23u << 16) + 4u,
|
||||
DDS_OP_JEQ | DDS_OP_TYPE_SEQ | 6, 3, offsetof (struct type_uni, _u.a),
|
||||
DDS_OP_JEQ | DDS_OP_TYPE_ARR | 12, 0, offsetof (struct type_uni, _u.b),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_SEQ | DDS_OP_SUBTYPE_STU, 0u,
|
||||
sizeof (struct two_uint32), (8u << 16u) + 4u,
|
||||
DDS_OP_ADR | DDS_OP_TYPE_ARR | DDS_OP_SUBTYPE_4BY, offsetof (struct two_uint32, v), 2,
|
||||
DDS_OP_RTS,
|
||||
DDS_OP_RTS,
|
||||
DDS_OP_ADR | DDS_OP_TYPE_ARR | DDS_OP_SUBTYPE_4BY, 0u, 4,
|
||||
DDS_OP_RTS,
|
||||
DDS_OP_RTS
|
||||
|
||||
},
|
||||
.m_meta = "" /* this is on its way out anyway */
|
||||
};
|
||||
|
||||
/* The slow delivery path has a switchover at 4 sertopics (well, today it has ...) so it is better to
|
||||
to test with > 4 different sertopics. That path (again, today) iterates over GUIDs in increasing
|
||||
order, and as all readers are created in the participant and the entity ids are strictly
|
||||
monotonically increasing for the first ~ 16M entities (again, today), creating additional
|
||||
readers for these topics at the end means that "ary2" is the one that ends up in > 4 case.
|
||||
Calling takecdr */
|
||||
static const dds_topic_descriptor_t type_ary1_desc =
|
||||
{
|
||||
.m_size = sizeof (struct type_ary),
|
||||
.m_align = 1u,
|
||||
.m_flagset = DDS_TOPIC_NO_OPTIMIZE,
|
||||
.m_nkeys = 0,
|
||||
.m_typename = "multi_sertopic_type",
|
||||
.m_keys = NULL,
|
||||
.m_nops = 2,
|
||||
.m_ops = (const uint32_t[]) {
|
||||
DDS_OP_ADR | DDS_OP_TYPE_ARR | DDS_OP_SUBTYPE_1BY, offsetof (struct type_ary, x), 16,
|
||||
DDS_OP_RTS
|
||||
},
|
||||
.m_meta = "" /* this is on its way out anyway */
|
||||
};
|
||||
|
||||
static const dds_topic_descriptor_t type_ary2_desc =
|
||||
{
|
||||
.m_size = sizeof (struct type_ary),
|
||||
.m_align = 2u,
|
||||
.m_flagset = DDS_TOPIC_NO_OPTIMIZE,
|
||||
.m_nkeys = 0,
|
||||
.m_typename = "multi_sertopic_type",
|
||||
.m_keys = NULL,
|
||||
.m_nops = 2,
|
||||
.m_ops = (const uint32_t[]) {
|
||||
DDS_OP_ADR | DDS_OP_TYPE_ARR | DDS_OP_SUBTYPE_2BY, offsetof (struct type_ary, x), 8,
|
||||
DDS_OP_RTS
|
||||
},
|
||||
.m_meta = "" /* this is on its way out anyway */
|
||||
};
|
||||
|
||||
static uint32_t g_topic_nr = 0;
|
||||
static dds_entity_t g_pub_domain = 0;
|
||||
static dds_entity_t g_pub_participant = 0;
|
||||
static dds_entity_t g_pub_publisher = 0;
|
||||
|
||||
static dds_entity_t g_sub_domain = 0;
|
||||
static dds_entity_t g_sub_participant = 0;
|
||||
static dds_entity_t g_sub_subscriber = 0;
|
||||
|
||||
static char *create_topic_name (const char *prefix, uint32_t nr, char *name, size_t size)
|
||||
{
|
||||
/* Get unique g_topic name. */
|
||||
ddsrt_pid_t pid = ddsrt_getpid();
|
||||
ddsrt_tid_t tid = ddsrt_gettid();
|
||||
(void) snprintf (name, size, "%s%d_pid%" PRIdPID "_tid%" PRIdTID "", prefix, nr, pid, tid);
|
||||
return name;
|
||||
}
|
||||
|
||||
static void multi_sertopic_init (void)
|
||||
{
|
||||
/* Domains for pub and sub use a different domain id, but the portgain setting
|
||||
* in configuration is 0, so that both domains will map to the same port number.
|
||||
* This allows to create two domains in a single test process. */
|
||||
char *conf_pub = ddsrt_expand_envvars (DDS_CONFIG_NO_PORT_GAIN, DDS_DOMAINID_PUB);
|
||||
char *conf_sub = ddsrt_expand_envvars (DDS_CONFIG_NO_PORT_GAIN, DDS_DOMAINID_SUB);
|
||||
g_pub_domain = dds_create_domain (DDS_DOMAINID_PUB, conf_pub);
|
||||
g_sub_domain = dds_create_domain (DDS_DOMAINID_SUB, conf_sub);
|
||||
dds_free (conf_pub);
|
||||
dds_free (conf_sub);
|
||||
|
||||
g_pub_participant = dds_create_participant(DDS_DOMAINID_PUB, NULL, NULL);
|
||||
CU_ASSERT_FATAL (g_pub_participant > 0);
|
||||
g_sub_participant = dds_create_participant(DDS_DOMAINID_SUB, NULL, NULL);
|
||||
CU_ASSERT_FATAL (g_sub_participant > 0);
|
||||
|
||||
g_pub_publisher = dds_create_publisher(g_pub_participant, NULL, NULL);
|
||||
CU_ASSERT_FATAL (g_pub_publisher > 0);
|
||||
g_sub_subscriber = dds_create_subscriber(g_sub_participant, NULL, NULL);
|
||||
CU_ASSERT_FATAL (g_sub_subscriber > 0);
|
||||
}
|
||||
|
||||
static void multi_sertopic_fini (void)
|
||||
{
|
||||
dds_delete (g_sub_subscriber);
|
||||
dds_delete (g_pub_publisher);
|
||||
dds_delete (g_sub_participant);
|
||||
dds_delete (g_pub_participant);
|
||||
dds_delete (g_sub_domain);
|
||||
dds_delete (g_pub_domain);
|
||||
}
|
||||
|
||||
static bool get_and_check_writer_status (size_t nwr, const dds_entity_t *wrs, size_t nrd)
|
||||
{
|
||||
dds_return_t rc;
|
||||
struct dds_publication_matched_status x;
|
||||
for (size_t i = 0; i < nwr; i++)
|
||||
{
|
||||
rc = dds_get_publication_matched_status (wrs[i], &x);
|
||||
CU_ASSERT_FATAL (rc == DDS_RETCODE_OK);
|
||||
if (x.current_count != nrd)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool get_and_check_reader_status (size_t nrd, const dds_entity_t *rds, size_t nwr)
|
||||
{
|
||||
dds_return_t rc;
|
||||
struct dds_subscription_matched_status x;
|
||||
for (size_t i = 0; i < nrd; i++)
|
||||
{
|
||||
rc = dds_get_subscription_matched_status (rds[i], &x);
|
||||
CU_ASSERT_FATAL (rc == DDS_RETCODE_OK);
|
||||
if (x.current_count != nwr)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void waitfor_or_reset_fastpath (dds_entity_t rdhandle, bool fastpath, size_t nwr)
|
||||
{
|
||||
dds_return_t rc;
|
||||
struct dds_entity *x;
|
||||
|
||||
rc = dds_entity_pin (rdhandle, &x);
|
||||
CU_ASSERT_FATAL (rc == DDS_RETCODE_OK);
|
||||
CU_ASSERT_FATAL (dds_entity_kind (x) == DDS_KIND_READER);
|
||||
|
||||
struct reader * const rd = ((struct dds_reader *) x)->m_rd;
|
||||
struct rd_pwr_match *m;
|
||||
ddsi_guid_t cursor;
|
||||
size_t wrcount = 0;
|
||||
thread_state_awake (lookup_thread_state (), rd->e.gv);
|
||||
ddsrt_mutex_lock (&rd->e.lock);
|
||||
|
||||
memset (&cursor, 0, sizeof (cursor));
|
||||
while ((m = ddsrt_avl_lookup_succ (&rd_writers_treedef, &rd->writers, &cursor)) != NULL)
|
||||
{
|
||||
cursor = m->pwr_guid;
|
||||
ddsrt_mutex_unlock (&rd->e.lock);
|
||||
struct proxy_writer * const pwr = entidx_lookup_proxy_writer_guid (rd->e.gv->entity_index, &cursor);
|
||||
ddsrt_mutex_lock (&pwr->rdary.rdary_lock);
|
||||
if (!fastpath)
|
||||
pwr->rdary.fastpath_ok = false;
|
||||
else
|
||||
{
|
||||
while (!pwr->rdary.fastpath_ok)
|
||||
{
|
||||
ddsrt_mutex_unlock (&pwr->rdary.rdary_lock);
|
||||
dds_sleepfor (DDS_MSECS (10));
|
||||
ddsrt_mutex_lock (&pwr->rdary.rdary_lock);
|
||||
}
|
||||
}
|
||||
wrcount++;
|
||||
ddsrt_mutex_unlock (&pwr->rdary.rdary_lock);
|
||||
ddsrt_mutex_lock (&rd->e.lock);
|
||||
}
|
||||
|
||||
memset (&cursor, 0, sizeof (cursor));
|
||||
while ((m = ddsrt_avl_lookup_succ (&rd_local_writers_treedef, &rd->local_writers, &cursor)) != NULL)
|
||||
{
|
||||
cursor = m->pwr_guid;
|
||||
ddsrt_mutex_unlock (&rd->e.lock);
|
||||
struct writer * const wr = entidx_lookup_writer_guid (rd->e.gv->entity_index, &cursor);
|
||||
ddsrt_mutex_lock (&wr->rdary.rdary_lock);
|
||||
if (!fastpath)
|
||||
wr->rdary.fastpath_ok = fastpath;
|
||||
else
|
||||
{
|
||||
while (!wr->rdary.fastpath_ok)
|
||||
{
|
||||
ddsrt_mutex_unlock (&wr->rdary.rdary_lock);
|
||||
dds_sleepfor (DDS_MSECS (10));
|
||||
ddsrt_mutex_lock (&wr->rdary.rdary_lock);
|
||||
}
|
||||
}
|
||||
wrcount++;
|
||||
ddsrt_mutex_unlock (&wr->rdary.rdary_lock);
|
||||
ddsrt_mutex_lock (&rd->e.lock);
|
||||
}
|
||||
ddsrt_mutex_unlock (&rd->e.lock);
|
||||
thread_state_asleep (lookup_thread_state ());
|
||||
dds_entity_unpin (x);
|
||||
|
||||
CU_ASSERT_FATAL (wrcount == nwr);
|
||||
}
|
||||
|
||||
static struct ddsi_sertopic *get_sertopic_from_reader (dds_entity_t reader)
|
||||
{
|
||||
/* not refcounting the sertopic: so this presumes it is kept alive for other reasons */
|
||||
dds_return_t rc;
|
||||
struct dds_entity *x;
|
||||
struct dds_reader *rd;
|
||||
struct ddsi_sertopic *sertopic;
|
||||
rc = dds_entity_pin (reader, &x);
|
||||
CU_ASSERT_FATAL (rc == DDS_RETCODE_OK);
|
||||
CU_ASSERT_FATAL (dds_entity_kind (x) == DDS_KIND_READER);
|
||||
rd = (struct dds_reader *) x;
|
||||
sertopic = rd->m_topic->m_stopic;
|
||||
dds_entity_unpin (x);
|
||||
return sertopic;
|
||||
}
|
||||
|
||||
static void logsink (void *arg, const dds_log_data_t *msg)
|
||||
{
|
||||
ddsrt_atomic_uint32_t *deser_fail = arg;
|
||||
fputs (msg->message - msg->hdrsize, stderr);
|
||||
if (strstr (msg->message, "deserialization") && strstr (msg->message, "failed"))
|
||||
ddsrt_atomic_inc32 (deser_fail);
|
||||
}
|
||||
|
||||
static void ddsc_multi_sertopic_impl (dds_entity_t pp_pub, dds_entity_t pp_sub, bool fastpath)
|
||||
{
|
||||
#define SEQ_IDX 0
|
||||
#define ARY_IDX 1
|
||||
#define UNI_IDX 2
|
||||
char name[100];
|
||||
static const dds_topic_descriptor_t *descs[] = {
|
||||
&type_seq_desc, &type_ary_desc, &type_uni_desc,
|
||||
&type_ary1_desc, &type_ary2_desc
|
||||
};
|
||||
dds_entity_t pub_topics[3], writers[3];
|
||||
dds_entity_t sub_topics[5];
|
||||
dds_entity_t readers[15];
|
||||
dds_entity_t waitset;
|
||||
dds_qos_t *qos;
|
||||
dds_return_t rc;
|
||||
|
||||
printf ("multi_sertopic: %s %s\n", (pp_pub == pp_sub) ? "local" : "remote", fastpath ? "fastpath" : "slowpath");
|
||||
|
||||
waitset = dds_create_waitset (DDS_CYCLONEDDS_HANDLE);
|
||||
CU_ASSERT_FATAL (waitset > 0);
|
||||
|
||||
qos = dds_create_qos ();
|
||||
CU_ASSERT_FATAL (qos != NULL);
|
||||
dds_qset_reliability (qos, DDS_RELIABILITY_RELIABLE, DDS_INFINITY);
|
||||
dds_qset_destination_order (qos, DDS_DESTINATIONORDER_BY_SOURCE_TIMESTAMP);
|
||||
dds_qset_history (qos, DDS_HISTORY_KEEP_ALL, 0);
|
||||
|
||||
create_topic_name ("ddsc_multi_sertopic_lease_duration_zero", g_topic_nr++, name, sizeof name);
|
||||
|
||||
for (size_t i = 0; i < sizeof (pub_topics) / sizeof (pub_topics[0]); i++)
|
||||
{
|
||||
pub_topics[i] = dds_create_topic (pp_pub, descs[i], name, qos, NULL);
|
||||
CU_ASSERT_FATAL (pub_topics[i] > 0);
|
||||
}
|
||||
for (size_t i = 0; i < sizeof (writers) / sizeof (writers[0]); i++)
|
||||
{
|
||||
writers[i] = dds_create_writer (pp_pub, pub_topics[i], qos, NULL);
|
||||
CU_ASSERT_FATAL (writers[i] > 0);
|
||||
}
|
||||
for (size_t i = 0; i < sizeof (sub_topics) / sizeof (sub_topics[0]); i++)
|
||||
{
|
||||
sub_topics[i] = dds_create_topic (pp_sub, descs[i], name, qos, NULL);
|
||||
CU_ASSERT_FATAL (sub_topics[i] > 0);
|
||||
}
|
||||
DDSRT_STATIC_ASSERT (sizeof (readers) >= sizeof (sub_topics));
|
||||
DDSRT_STATIC_ASSERT ((sizeof (readers) % sizeof (sub_topics)) == 0);
|
||||
for (size_t i = 0; i < sizeof (sub_topics) / sizeof (sub_topics[0]); i++)
|
||||
{
|
||||
readers[i] = dds_create_reader (pp_sub, sub_topics[i], qos, NULL);
|
||||
CU_ASSERT_FATAL (readers[i] > 0);
|
||||
}
|
||||
for (size_t i = sizeof (sub_topics) / sizeof (sub_topics[0]); i < sizeof (readers) / sizeof (readers[0]); i++)
|
||||
{
|
||||
const size_t nrd = sizeof (readers) / sizeof (readers[0]);
|
||||
const size_t ntp = sizeof (sub_topics) / sizeof (sub_topics[0]);
|
||||
readers[i] = dds_create_reader (pp_sub, sub_topics[(i - ntp) / (nrd / ntp - 1)], qos, NULL);
|
||||
CU_ASSERT_FATAL (readers[i] > 0);
|
||||
}
|
||||
|
||||
dds_delete_qos (qos);
|
||||
|
||||
/* wait for discovery to complete */
|
||||
for (size_t i = 0; i < sizeof (writers) / sizeof (writers[0]); i++)
|
||||
{
|
||||
rc = dds_set_status_mask (writers[i], DDS_PUBLICATION_MATCHED_STATUS);
|
||||
CU_ASSERT_FATAL (rc == DDS_RETCODE_OK);
|
||||
rc = dds_waitset_attach (waitset, writers[i], -(dds_attach_t)i - 1);
|
||||
CU_ASSERT_FATAL (rc == DDS_RETCODE_OK);
|
||||
}
|
||||
for (size_t i = 0; i < sizeof (readers) / sizeof (readers[0]); i++)
|
||||
{
|
||||
rc = dds_set_status_mask (readers[i], DDS_SUBSCRIPTION_MATCHED_STATUS | DDS_DATA_AVAILABLE_STATUS);
|
||||
CU_ASSERT_FATAL (rc == DDS_RETCODE_OK);
|
||||
rc = dds_waitset_attach (waitset, readers[i], (dds_attach_t)i);
|
||||
CU_ASSERT_FATAL (rc == DDS_RETCODE_OK);
|
||||
}
|
||||
|
||||
printf ("wait for discovery, fastpath_ok; delete & recreate readers\n");
|
||||
while (!(get_and_check_writer_status (sizeof (writers) / sizeof (writers[0]), writers, sizeof (readers) / sizeof (readers[0])) &&
|
||||
get_and_check_reader_status (sizeof (readers) / sizeof (readers[0]), readers, sizeof (writers) / sizeof (writers[0]))))
|
||||
{
|
||||
rc = dds_waitset_wait (waitset, NULL, 0, DDS_SECS(5));
|
||||
CU_ASSERT_FATAL (rc >= 1);
|
||||
}
|
||||
|
||||
/* we want to check both the fast path and the slow path ... so first wait
|
||||
for it to be set on all (proxy) writers, then possibly reset it */
|
||||
for (size_t i = 0; i < sizeof (readers) / sizeof (readers[0]); i++)
|
||||
waitfor_or_reset_fastpath (readers[i], true, sizeof (writers) / sizeof (writers[0]));
|
||||
if (!fastpath)
|
||||
{
|
||||
printf ("clear fastpath_ok\n");
|
||||
for (size_t i = 0; i < sizeof (readers) / sizeof (readers[0]); i++)
|
||||
waitfor_or_reset_fastpath (readers[i], false, sizeof (writers) / sizeof (writers[0]));
|
||||
}
|
||||
|
||||
/* check the log output for deserialization failures */
|
||||
ddsrt_atomic_uint32_t deser_fail = DDSRT_ATOMIC_UINT32_INIT (0);
|
||||
dds_set_log_sink (logsink, &deser_fail);
|
||||
|
||||
/* Write one of each type: all of these samples result in the same serialised
|
||||
form but interpreting the memory layout for type X as-if it were of type Y
|
||||
wreaks havoc. */
|
||||
{
|
||||
struct type_seq s = {
|
||||
.x = {
|
||||
._length = 3, ._maximum = 3, ._release = false, ._buffer = (uint32_t[]) { 1, 4, 2 }
|
||||
}
|
||||
};
|
||||
struct type_ary a = {
|
||||
.x = { 3, 1, 4, 2 }
|
||||
};
|
||||
struct type_uni u = {
|
||||
._d = 3,
|
||||
._u = { .a = {
|
||||
._length = 1, ._maximum = 1, ._release = false, ._buffer = (struct two_uint32[]) { { { 4, 2 } } }
|
||||
} }
|
||||
};
|
||||
printf ("writing ...\n");
|
||||
rc = dds_write_ts (writers[SEQ_IDX], &s, 1);
|
||||
CU_ASSERT_FATAL (rc == DDS_RETCODE_OK);
|
||||
rc = dds_write_ts (writers[ARY_IDX], &a, 2);
|
||||
CU_ASSERT_FATAL (rc == DDS_RETCODE_OK);
|
||||
rc = dds_write_ts (writers[UNI_IDX], &u, 3);
|
||||
CU_ASSERT_FATAL (rc == DDS_RETCODE_OK);
|
||||
|
||||
/* Also write a sample that can't be deserialised by the other types */
|
||||
struct type_seq s1 = {
|
||||
.x = {
|
||||
._length = 1, ._maximum = 1, ._release = false, ._buffer = (uint32_t[]) { 1 }
|
||||
}
|
||||
};
|
||||
rc = dds_write_ts (writers[SEQ_IDX], &s1, 4);
|
||||
CU_ASSERT_FATAL (rc == DDS_RETCODE_OK);
|
||||
}
|
||||
|
||||
/* All readers should have received three samples, and those that are of type seq
|
||||
should have received one extra (whereas the others should cause deserialization
|
||||
failure warnings) */
|
||||
printf ("reading\n");
|
||||
const size_t nexp = ((sizeof (writers) / sizeof (writers[0])) *
|
||||
(sizeof (readers) / sizeof (readers[0])) +
|
||||
((sizeof (readers) / sizeof (readers[0])) / (sizeof (sub_topics) / sizeof (sub_topics[0]))));
|
||||
/* expecting exactly as many deserialization failures as there are topics other than seq */
|
||||
const size_t nexp_fail = sizeof (sub_topics) / sizeof (sub_topics[0]) - 1;
|
||||
uint32_t nseen = 0;
|
||||
while (nseen < nexp)
|
||||
{
|
||||
dds_sample_info_t si;
|
||||
|
||||
rc = dds_waitset_wait (waitset, NULL, 0, DDS_SECS (5));
|
||||
CU_ASSERT_FATAL (rc >= 1);
|
||||
|
||||
{
|
||||
struct type_seq s = { .x = { 0 } };
|
||||
void *raws[] = { &s };
|
||||
while (dds_take (readers[SEQ_IDX], raws, &si, 1, 1) == 1)
|
||||
{
|
||||
if (!si.valid_data)
|
||||
continue;
|
||||
printf ("recv: seq %"PRId64"\n", si.source_timestamp);
|
||||
if (si.source_timestamp == 4)
|
||||
{
|
||||
CU_ASSERT_FATAL (s.x._length == 1);
|
||||
CU_ASSERT_FATAL (s.x._buffer[0] == 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
CU_ASSERT_FATAL (si.source_timestamp >= 1 && si.source_timestamp <= 3);
|
||||
CU_ASSERT_FATAL (s.x._length == 3);
|
||||
CU_ASSERT_FATAL (s.x._buffer[0] == 1);
|
||||
CU_ASSERT_FATAL (s.x._buffer[1] == 4);
|
||||
CU_ASSERT_FATAL (s.x._buffer[2] == 2);
|
||||
}
|
||||
nseen++;
|
||||
}
|
||||
dds_free (s.x._buffer);
|
||||
}
|
||||
|
||||
{
|
||||
struct type_ary a;
|
||||
void *rawa[] = { &a };
|
||||
while (dds_take (readers[ARY_IDX], rawa, &si, 1, 1) == 1)
|
||||
{
|
||||
if (!si.valid_data)
|
||||
continue;
|
||||
printf ("recv: ary %"PRId64"\n", si.source_timestamp);
|
||||
CU_ASSERT_FATAL (si.source_timestamp >= 1 && si.source_timestamp <= 3);
|
||||
CU_ASSERT_FATAL (a.x[0] == 3);
|
||||
CU_ASSERT_FATAL (a.x[1] == 1);
|
||||
CU_ASSERT_FATAL (a.x[2] == 4);
|
||||
CU_ASSERT_FATAL (a.x[3] == 2);
|
||||
nseen++;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
struct type_uni u = { ._u.a = { 0 } };
|
||||
void *rawu[] = { &u };
|
||||
while (dds_take (readers[UNI_IDX], rawu, &si, 1, 1) == 1)
|
||||
{
|
||||
if (!si.valid_data)
|
||||
continue;
|
||||
printf ("recv: uni %"PRId64"\n", si.source_timestamp);
|
||||
CU_ASSERT_FATAL (si.source_timestamp >= 1 && si.source_timestamp <= 3);
|
||||
CU_ASSERT_FATAL (u._d == 3);
|
||||
CU_ASSERT_FATAL (u._u.a._length == 1);
|
||||
assert (u._u.a._buffer != NULL); /* for Clang static analyzer */
|
||||
CU_ASSERT_FATAL (u._u.a._buffer[0].v[0] == 4);
|
||||
CU_ASSERT_FATAL (u._u.a._buffer[0].v[1] == 2);
|
||||
dds_free (u._u.a._buffer);
|
||||
u._u.a._buffer = NULL;
|
||||
nseen++;
|
||||
}
|
||||
}
|
||||
|
||||
DDSRT_STATIC_ASSERT (((1u << SEQ_IDX) | (1u << ARY_IDX) | (1u << UNI_IDX)) == 7);
|
||||
for (size_t i = 3; i < sizeof (readers) / sizeof (readers[0]); i++)
|
||||
{
|
||||
struct ddsi_serdata *sample;
|
||||
while (dds_takecdr (readers[i], &sample, 1, &si, DDS_ANY_STATE) == 1)
|
||||
{
|
||||
if (!si.valid_data)
|
||||
continue;
|
||||
printf ("recv: reader %zu %"PRId64"\n", i, si.source_timestamp);
|
||||
CU_ASSERT_FATAL (sample->topic == get_sertopic_from_reader (readers[i]));
|
||||
ddsi_serdata_unref (sample);
|
||||
nseen++;
|
||||
}
|
||||
}
|
||||
}
|
||||
CU_ASSERT_FATAL (nseen == nexp);
|
||||
|
||||
/* data from remote writers can cause a deserialization failure after all
|
||||
expected samples have been seen (becasue it is written last); so wait
|
||||
for them */
|
||||
while (ddsrt_atomic_ld32 (&deser_fail) < nexp_fail)
|
||||
dds_sleepfor (DDS_MSECS (10));
|
||||
CU_ASSERT_FATAL (ddsrt_atomic_ld32 (&deser_fail) == nexp_fail);
|
||||
|
||||
/* deleting the waitset is important: it is bound to the library rather than to
|
||||
a domain and consequently won't be deleted simply because all domains are */
|
||||
rc = dds_delete (waitset);
|
||||
|
||||
CU_ASSERT_FATAL (rc == DDS_RETCODE_OK);
|
||||
dds_set_log_sink (0, NULL);
|
||||
}
|
||||
|
||||
CU_Test(ddsc_multi_sertopic, local, .init = multi_sertopic_init, .fini = multi_sertopic_fini)
|
||||
{
|
||||
ddsc_multi_sertopic_impl (g_pub_participant, g_pub_participant, true);
|
||||
}
|
||||
|
||||
CU_Test(ddsc_multi_sertopic, remote, .init = multi_sertopic_init, .fini = multi_sertopic_fini)
|
||||
{
|
||||
ddsc_multi_sertopic_impl (g_pub_participant, g_sub_participant, true);
|
||||
}
|
||||
|
||||
CU_Test(ddsc_multi_sertopic, local_slowpath, .init = multi_sertopic_init, .fini = multi_sertopic_fini)
|
||||
{
|
||||
ddsc_multi_sertopic_impl (g_pub_participant, g_pub_participant, false);
|
||||
}
|
||||
|
||||
CU_Test(ddsc_multi_sertopic, remote_slowpath, .init = multi_sertopic_init, .fini = multi_sertopic_fini)
|
||||
{
|
||||
ddsc_multi_sertopic_impl (g_pub_participant, g_sub_participant, false);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue