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