From e83a34ec02b606f549a68b1b01db59a0e134c871 Mon Sep 17 00:00:00 2001
From: Erik Boasson
Date: Sat, 18 Apr 2020 17:32:56 +0200
Subject: [PATCH 01/36] Distinguish signed, float in type descriptor
* Add a flag to indicate signed integral values and one to indicate
floating-point values
* Set these flags in the output of idlc
* Use them when printing sample contents to the trace
By encoding the information as flags in reserved bits the actual
serialization and deserialization is unaffected.
Signed-off-by: Erik Boasson
---
.../ddsc/include/dds/ddsc/dds_public_impl.h | 7 +++
src/core/ddsi/src/ddsi_cdrstream.c | 62 ++++++++++++++-----
.../cyclonedds/generator/BasicType.java | 12 ++--
3 files changed, 58 insertions(+), 23 deletions(-)
diff --git a/src/core/ddsc/include/dds/ddsc/dds_public_impl.h b/src/core/ddsc/include/dds/ddsc/dds_public_impl.h
index 730020cd49..46bfcbe1e6 100644
--- a/src/core/ddsc/include/dds/ddsc/dds_public_impl.h
+++ b/src/core/ddsc/include/dds/ddsc/dds_public_impl.h
@@ -225,6 +225,13 @@ enum dds_stream_typecode_subtype {
#define DDS_OP_FLAG_KEY 0x01 /* key field: applicable to {1,2,4,8}BY, STR, BST, ARR-of-{1,2,4,8}BY */
#define DDS_OP_FLAG_DEF 0x02 /* union has a default case (for DDS_OP_ADR | DDS_OP_TYPE_UNI) */
+/* For a union: (1) the discriminator may be a key field; (2) there may be a default value;
+ and (3) the discriminator can be an integral type (or enumerated - here treated as equivalent).
+ What it can't be is a floating-point type. So DEF and FP need never be set at the same time.
+ There are only a few flag bits, so saving one is not such a bad idea. */
+#define DDS_OP_FLAG_FP 0x02 /* floating-point: applicable to {4,8}BY and arrays, sequences of them */
+#define DDS_OP_FLAG_SGN 0x04 /* signed: applicable to {1,2,4,8}BY and arrays, sequences of them */
+
/**
* Description : Enable or disable write batching. Overrides default configuration
* setting for write batching (Internal/WriteBatch).
diff --git a/src/core/ddsi/src/ddsi_cdrstream.c b/src/core/ddsi/src/ddsi_cdrstream.c
index 3565d45281..6cdfb80678 100644
--- a/src/core/ddsi/src/ddsi_cdrstream.c
+++ b/src/core/ddsi/src/ddsi_cdrstream.c
@@ -1995,14 +1995,42 @@ static size_t isprint_runlen (const unsigned char *s, size_t n)
return m;
}
-static bool prtf_simple (char * __restrict *buf, size_t * __restrict bufsize, dds_istream_t * __restrict is, enum dds_stream_typecode type)
+static bool prtf_simple (char * __restrict *buf, size_t * __restrict bufsize, dds_istream_t * __restrict is, enum dds_stream_typecode type, unsigned flags)
{
switch (type)
{
- case DDS_OP_VAL_1BY: return prtf (buf, bufsize, "%"PRIu8, dds_is_get1 (is));
- case DDS_OP_VAL_2BY: return prtf (buf, bufsize, "%"PRIu16, dds_is_get2 (is));
- case DDS_OP_VAL_4BY: return prtf (buf, bufsize, "%"PRIu32, dds_is_get4 (is));
- case DDS_OP_VAL_8BY: return prtf (buf, bufsize, "%"PRIu64, dds_is_get8 (is));
+ case DDS_OP_VAL_1BY: {
+ const union { int8_t s; uint8_t u; } x = { .u = dds_is_get1 (is) };
+ if (flags & DDS_OP_FLAG_SGN)
+ return prtf (buf, bufsize, "%"PRId8, x.s);
+ else
+ return prtf (buf, bufsize, "%"PRIu8, x.u);
+ }
+ case DDS_OP_VAL_2BY: {
+ const union { int16_t s; uint16_t u; } x = { .u = dds_is_get2 (is) };
+ if (flags & DDS_OP_FLAG_SGN)
+ return prtf (buf, bufsize, "%"PRId16, x.s);
+ else
+ return prtf (buf, bufsize, "%"PRIu16, x.u);
+ }
+ case DDS_OP_VAL_4BY: {
+ const union { int32_t s; uint32_t u; float f; } x = { .u = dds_is_get4 (is) };
+ if (flags & DDS_OP_FLAG_FP)
+ return prtf (buf, bufsize, "%g", x.f);
+ else if (flags & DDS_OP_FLAG_SGN)
+ return prtf (buf, bufsize, "%"PRId32, x.s);
+ else
+ return prtf (buf, bufsize, "%"PRIu32, x.u);
+ }
+ case DDS_OP_VAL_8BY: {
+ const union { int64_t s; uint64_t u; double f; } x = { .u = dds_is_get8 (is) };
+ if (flags & DDS_OP_FLAG_FP)
+ return prtf (buf, bufsize, "%g", x.f);
+ else if (flags & DDS_OP_FLAG_SGN)
+ return prtf (buf, bufsize, "%"PRId64, x.s);
+ else
+ return prtf (buf, bufsize, "%"PRIu64, x.u);
+ }
case DDS_OP_VAL_STR: case DDS_OP_VAL_BST: return prtf_str (buf, bufsize, is);
case DDS_OP_VAL_ARR: case DDS_OP_VAL_SEQ: case DDS_OP_VAL_UNI: case DDS_OP_VAL_STU:
abort ();
@@ -2010,7 +2038,7 @@ static bool prtf_simple (char * __restrict *buf, size_t * __restrict bufsize, dd
return false;
}
-static bool prtf_simple_array (char * __restrict *buf, size_t * __restrict bufsize, dds_istream_t * __restrict is, uint32_t num, enum dds_stream_typecode type)
+static bool prtf_simple_array (char * __restrict *buf, size_t * __restrict bufsize, dds_istream_t * __restrict is, uint32_t num, enum dds_stream_typecode type, unsigned flags)
{
bool cont = prtf (buf, bufsize, "{");
switch (type)
@@ -2033,7 +2061,7 @@ static bool prtf_simple_array (char * __restrict *buf, size_t * __restrict bufsi
{
if (i != 0)
(void) prtf (buf, bufsize, ",");
- cont = prtf_simple (buf, bufsize, is, type);
+ cont = prtf_simple (buf, bufsize, is, type, flags);
i++;
}
}
@@ -2045,7 +2073,7 @@ static bool prtf_simple_array (char * __restrict *buf, size_t * __restrict bufsi
{
if (i != 0)
(void) prtf (buf, bufsize, ",");
- cont = prtf_simple (buf, bufsize, is, type);
+ cont = prtf_simple (buf, bufsize, is, type, flags);
}
break;
default:
@@ -2070,10 +2098,10 @@ static const uint32_t *prtf_seq (char * __restrict *buf, size_t *bufsize, dds_is
switch (subtype)
{
case DDS_OP_VAL_1BY: case DDS_OP_VAL_2BY: case DDS_OP_VAL_4BY: case DDS_OP_VAL_8BY:
- (void) prtf_simple_array (buf, bufsize, is, num, subtype);
+ (void) prtf_simple_array (buf, bufsize, is, num, subtype, DDS_OP_FLAGS (insn));
return ops + 2;
case DDS_OP_VAL_STR: case DDS_OP_VAL_BST:
- (void) prtf_simple_array (buf, bufsize, is, num, subtype);
+ (void) prtf_simple_array (buf, bufsize, is, num, subtype, DDS_OP_FLAGS (insn));
return ops + (subtype == DDS_OP_VAL_STR ? 2 : 3);
case DDS_OP_VAL_SEQ: case DDS_OP_VAL_ARR: case DDS_OP_VAL_UNI: case DDS_OP_VAL_STU: {
const uint32_t jmp = DDS_OP_ADR_JMP (ops[3]);
@@ -2098,10 +2126,10 @@ static const uint32_t *prtf_arr (char * __restrict *buf, size_t *bufsize, dds_is
switch (subtype)
{
case DDS_OP_VAL_1BY: case DDS_OP_VAL_2BY: case DDS_OP_VAL_4BY: case DDS_OP_VAL_8BY:
- (void) prtf_simple_array (buf, bufsize, is, num, subtype);
+ (void) prtf_simple_array (buf, bufsize, is, num, subtype, DDS_OP_FLAGS (insn));
return ops + 3;
case DDS_OP_VAL_STR: case DDS_OP_VAL_BST:
- (void) prtf_simple_array (buf, bufsize, is, num, subtype);
+ (void) prtf_simple_array (buf, bufsize, is, num, subtype, DDS_OP_FLAGS (insn));
return ops + (subtype == DDS_OP_VAL_STR ? 3 : 5);
case DDS_OP_VAL_SEQ: case DDS_OP_VAL_ARR: case DDS_OP_VAL_UNI: case DDS_OP_VAL_STU: {
const uint32_t *jsr_ops = ops + DDS_OP_ADR_JSR (ops[3]);
@@ -2132,7 +2160,7 @@ static const uint32_t *prtf_uni (char * __restrict *buf, size_t *bufsize, dds_is
{
case DDS_OP_VAL_1BY: case DDS_OP_VAL_2BY: case DDS_OP_VAL_4BY: case DDS_OP_VAL_8BY:
case DDS_OP_VAL_STR: case DDS_OP_VAL_BST:
- (void) prtf_simple (buf, bufsize, is, valtype);
+ (void) prtf_simple (buf, bufsize, is, valtype, DDS_OP_FLAGS (jeq_op[0]));
break;
case DDS_OP_VAL_SEQ: case DDS_OP_VAL_ARR: case DDS_OP_VAL_UNI: case DDS_OP_VAL_STU:
(void) dds_stream_print_sample1 (buf, bufsize, is, jeq_op + DDS_OP_ADR_JSR (jeq_op[0]), valtype == DDS_OP_VAL_STU);
@@ -2161,11 +2189,11 @@ static bool dds_stream_print_sample1 (char * __restrict *buf, size_t * __restric
{
case DDS_OP_VAL_1BY: case DDS_OP_VAL_2BY: case DDS_OP_VAL_4BY: case DDS_OP_VAL_8BY:
case DDS_OP_VAL_STR:
- cont = prtf_simple (buf, bufsize, is, DDS_OP_TYPE (insn));
+ cont = prtf_simple (buf, bufsize, is, DDS_OP_TYPE (insn), DDS_OP_FLAGS (insn));
ops += 2;
break;
case DDS_OP_VAL_BST:
- cont = prtf_simple (buf, bufsize, is, DDS_OP_TYPE (insn));
+ cont = prtf_simple (buf, bufsize, is, DDS_OP_TYPE (insn), DDS_OP_FLAGS (insn));
ops += 3;
break;
case DDS_OP_VAL_SEQ:
@@ -2217,10 +2245,10 @@ size_t dds_stream_print_key (dds_istream_t * __restrict is, const struct ddsi_se
{
case DDS_OP_VAL_1BY: case DDS_OP_VAL_2BY: case DDS_OP_VAL_4BY: case DDS_OP_VAL_8BY:
case DDS_OP_VAL_STR: case DDS_OP_VAL_BST:
- cont = prtf_simple (&buf, &bufsize, is, DDS_OP_TYPE (*op));
+ cont = prtf_simple (&buf, &bufsize, is, DDS_OP_TYPE (*op), DDS_OP_FLAGS (*op));
break;
case DDS_OP_VAL_ARR:
- cont = prtf_simple_array (&buf, &bufsize, is, op[2], DDS_OP_SUBTYPE (*op));
+ cont = prtf_simple_array (&buf, &bufsize, is, op[2], DDS_OP_SUBTYPE (*op), DDS_OP_FLAGS (*op));
break;
case DDS_OP_VAL_SEQ: case DDS_OP_VAL_UNI: case DDS_OP_VAL_STU:
abort ();
diff --git a/src/idlc/src/org/eclipse/cyclonedds/generator/BasicType.java b/src/idlc/src/org/eclipse/cyclonedds/generator/BasicType.java
index 6ddb67b43e..8964e7fb4b 100644
--- a/src/idlc/src/org/eclipse/cyclonedds/generator/BasicType.java
+++ b/src/idlc/src/org/eclipse/cyclonedds/generator/BasicType.java
@@ -20,15 +20,15 @@ public enum BT
{
BOOLEAN ("bool", "DDS_OP_TYPE_BOO", "DDS_OP_SUBTYPE_BOO", Alignment.BOOL, "Boolean"),
OCTET ("uint8_t", "DDS_OP_TYPE_1BY", "DDS_OP_SUBTYPE_1BY", Alignment.ONE, "Octet"),
- CHAR ("char", "DDS_OP_TYPE_1BY", "DDS_OP_SUBTYPE_1BY", Alignment.ONE, "Char"),
- SHORT ("int16_t", "DDS_OP_TYPE_2BY", "DDS_OP_SUBTYPE_2BY", Alignment.TWO, "Short"),
+ CHAR ("char", "DDS_OP_TYPE_1BY | DDS_OP_FLAG_SGN", "DDS_OP_SUBTYPE_1BY | DDS_OP_FLAG_SGN", Alignment.ONE, "Char"),
+ SHORT ("int16_t", "DDS_OP_TYPE_2BY | DDS_OP_FLAG_SGN", "DDS_OP_SUBTYPE_2BY | DDS_OP_FLAG_SGN", Alignment.TWO, "Short"),
USHORT ("uint16_t", "DDS_OP_TYPE_2BY", "DDS_OP_SUBTYPE_2BY", Alignment.TWO, "UShort"),
- LONG ("int32_t", "DDS_OP_TYPE_4BY", "DDS_OP_SUBTYPE_4BY", Alignment.FOUR, "Long"),
+ LONG ("int32_t", "DDS_OP_TYPE_4BY | DDS_OP_FLAG_SGN", "DDS_OP_SUBTYPE_4BY | DDS_OP_FLAG_SGN", Alignment.FOUR, "Long"),
ULONG ("uint32_t", "DDS_OP_TYPE_4BY", "DDS_OP_SUBTYPE_4BY", Alignment.FOUR, "ULong"),
- LONGLONG ("int64_t", "DDS_OP_TYPE_8BY", "DDS_OP_SUBTYPE_8BY", Alignment.EIGHT, "LongLong"),
+ LONGLONG ("int64_t", "DDS_OP_TYPE_8BY | DDS_OP_FLAG_SGN", "DDS_OP_SUBTYPE_8BY | DDS_OP_FLAG_SGN", Alignment.EIGHT, "LongLong"),
ULONGLONG ("uint64_t", "DDS_OP_TYPE_8BY", "DDS_OP_SUBTYPE_8BY", Alignment.EIGHT, "ULongLong"),
- FLOAT ("float", "DDS_OP_TYPE_4BY", "DDS_OP_SUBTYPE_4BY", Alignment.FOUR, "Float"),
- DOUBLE ("double", "DDS_OP_TYPE_8BY", "DDS_OP_SUBTYPE_8BY", Alignment.EIGHT, "Double"),
+ FLOAT ("float", "DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP", "DDS_OP_SUBTYPE_4BY | DDS_OP_FLAG_FP", Alignment.FOUR, "Float"),
+ DOUBLE ("double", "DDS_OP_TYPE_8BY | DDS_OP_FLAG_FP", "DDS_OP_SUBTYPE_8BY | DDS_OP_FLAG_FP", Alignment.EIGHT, "Double"),
STRING ("char *", "DDS_OP_TYPE_STR", "DDS_OP_SUBTYPE_STR", Alignment.PTR, "String");
public final String cType;
From a2b91da2e9e6badb9f24493c9238aa95aaa3843c Mon Sep 17 00:00:00 2001
From: Erik Boasson
Date: Sun, 19 Apr 2020 17:36:01 +0200
Subject: [PATCH 02/36] Do not scan instances in dds_{read,take}_instance
Scanning all instances was never good for anything: the RHC is organised
as hash table on instance id (which is an alias for "instance handle")
and it was always designed to do this with a fast lookup.
Signed-off-by: Erik Boasson
---
src/core/ddsc/include/dds/ddsc/dds_rhc.h | 17 +-
src/core/ddsc/src/dds_read.c | 20 +-
src/core/ddsc/src/dds_rhc_default.c | 692 ++++++++++-------------
3 files changed, 312 insertions(+), 417 deletions(-)
diff --git a/src/core/ddsc/include/dds/ddsc/dds_rhc.h b/src/core/ddsc/include/dds/ddsc/dds_rhc.h
index d0413e6fc9..b5cba3499b 100644
--- a/src/core/ddsc/include/dds/ddsc/dds_rhc.h
+++ b/src/core/ddsc/include/dds/ddsc/dds_rhc.h
@@ -27,9 +27,8 @@ struct dds_reader;
struct ddsi_tkmap;
typedef dds_return_t (*dds_rhc_associate_t) (struct dds_rhc *rhc, struct dds_reader *reader, const struct ddsi_sertopic *topic, struct ddsi_tkmap *tkmap);
-typedef int (*dds_rhc_read_t) (struct dds_rhc *rhc, bool lock, void **values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t mask, dds_instance_handle_t handle, struct dds_readcond *cond);
-typedef int (*dds_rhc_take_t) (struct dds_rhc *rhc, bool lock, void **values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t mask, dds_instance_handle_t handle, struct dds_readcond *cond);
-typedef int (*dds_rhc_takecdr_t) (struct dds_rhc *rhc, bool lock, struct ddsi_serdata **values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t sample_states, uint32_t view_states, uint32_t instance_states, dds_instance_handle_t handle);
+typedef int32_t (*dds_rhc_read_take_t) (struct dds_rhc *rhc, bool lock, void **values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t mask, dds_instance_handle_t handle, struct dds_readcond *cond);
+typedef int32_t (*dds_rhc_read_take_cdr_t) (struct dds_rhc *rhc, bool lock, struct ddsi_serdata **values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t sample_states, uint32_t view_states, uint32_t instance_states, dds_instance_handle_t handle);
typedef bool (*dds_rhc_add_readcondition_t) (struct dds_rhc *rhc, struct dds_readcond *cond);
typedef void (*dds_rhc_remove_readcondition_t) (struct dds_rhc *rhc, struct dds_readcond *cond);
@@ -40,9 +39,9 @@ struct dds_rhc_ops {
/* A copy of DDSI rhc ops comes first so we can use either interface without
additional indirections */
struct ddsi_rhc_ops rhc_ops;
- dds_rhc_read_t read;
- dds_rhc_take_t take;
- dds_rhc_takecdr_t takecdr;
+ dds_rhc_read_take_t read;
+ dds_rhc_read_take_t take;
+ dds_rhc_read_take_cdr_t takecdr;
dds_rhc_add_readcondition_t add_readcondition;
dds_rhc_remove_readcondition_t remove_readcondition;
dds_rhc_lock_samples_t lock_samples;
@@ -76,13 +75,13 @@ DDS_EXPORT inline void dds_rhc_set_qos (struct dds_rhc *rhc, const struct dds_qo
DDS_EXPORT inline void dds_rhc_free (struct dds_rhc *rhc) {
rhc->common.ops->rhc_ops.free (&rhc->common.rhc);
}
-DDS_EXPORT inline int dds_rhc_read (struct dds_rhc *rhc, bool lock, void **values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t mask, dds_instance_handle_t handle, struct dds_readcond *cond) {
+DDS_EXPORT inline int32_t dds_rhc_read (struct dds_rhc *rhc, bool lock, void **values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t mask, dds_instance_handle_t handle, struct dds_readcond *cond) {
return (rhc->common.ops->read) (rhc, lock, values, info_seq, max_samples, mask, handle, cond);
}
-DDS_EXPORT inline int dds_rhc_take (struct dds_rhc *rhc, bool lock, void **values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t mask, dds_instance_handle_t handle, struct dds_readcond *cond) {
+DDS_EXPORT inline int32_t dds_rhc_take (struct dds_rhc *rhc, bool lock, void **values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t mask, dds_instance_handle_t handle, struct dds_readcond *cond) {
return rhc->common.ops->take (rhc, lock, values, info_seq, max_samples, mask, handle, cond);
}
-DDS_EXPORT inline int dds_rhc_takecdr (struct dds_rhc *rhc, bool lock, struct ddsi_serdata **values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t sample_states, uint32_t view_states, uint32_t instance_states, dds_instance_handle_t handle) {
+DDS_EXPORT inline int32_t dds_rhc_takecdr (struct dds_rhc *rhc, bool lock, struct ddsi_serdata **values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t sample_states, uint32_t view_states, uint32_t instance_states, dds_instance_handle_t handle) {
return rhc->common.ops->takecdr (rhc, lock, values, info_seq, max_samples, sample_states, view_states, instance_states, handle);
}
DDS_EXPORT inline bool dds_rhc_add_readcondition (struct dds_rhc *rhc, struct dds_readcond *cond) {
diff --git a/src/core/ddsc/src/dds_read.c b/src/core/ddsc/src/dds_read.c
index 701f50d821..fd9ae5b731 100644
--- a/src/core/ddsc/src/dds_read.c
+++ b/src/core/ddsc/src/dds_read.c
@@ -40,7 +40,7 @@ static dds_return_t dds_read_impl (bool take, dds_entity_t reader_or_condition,
#define NC_FREE_BUF 2u
#define NC_RESET_BUF 4u
- if (buf == NULL || si == NULL || maxs == 0 || bufsz == 0 || bufsz < maxs)
+ if (buf == NULL || si == NULL || maxs == 0 || bufsz == 0 || bufsz < maxs || maxs > INT32_MAX)
return DDS_RETCODE_BAD_PARAMETER;
if ((ret = dds_entity_pin (reader_or_condition, &entity)) < 0) {
@@ -61,14 +61,6 @@ static dds_return_t dds_read_impl (bool take, dds_entity_t reader_or_condition,
thread_state_awake (ts1, &entity->m_domain->gv);
- if (hand != DDS_HANDLE_NIL)
- {
- if (ddsi_tkmap_find_by_id (entity->m_domain->gv.m_tkmap, hand) == NULL) {
- ret = DDS_RETCODE_PRECONDITION_NOT_MET;
- goto fail_awake_pinned;
- }
- }
-
/* Allocate samples if not provided (assuming all or none provided) */
if (buf[0] == NULL)
{
@@ -142,8 +134,6 @@ static dds_return_t dds_read_impl (bool take, dds_entity_t reader_or_condition,
#undef NC_FREE_BUF
#undef NC_RESET_BUF
-fail_awake_pinned:
- thread_state_asleep (ts1);
fail_pinned:
dds_entity_unpin (entity);
fail:
@@ -157,12 +147,8 @@ static dds_return_t dds_readcdr_impl (bool take, dds_entity_t reader_or_conditio
struct dds_reader *rd;
struct dds_entity *entity;
- assert (take);
- assert (buf);
- assert (si);
- assert (hand == DDS_HANDLE_NIL);
- assert (maxs > 0);
- (void)take;
+ if (buf == NULL || si == NULL || maxs == 0 || maxs > INT32_MAX)
+ return DDS_RETCODE_BAD_PARAMETER;
if ((ret = dds_entity_pin (reader_or_condition, &entity)) < 0) {
return ret;
diff --git a/src/core/ddsc/src/dds_rhc_default.c b/src/core/ddsc/src/dds_rhc_default.c
index 50313ed4dc..07692e78f5 100644
--- a/src/core/ddsc/src/dds_rhc_default.c
+++ b/src/core/ddsc/src/dds_rhc_default.c
@@ -337,7 +337,7 @@ struct dds_rhc_default {
};
struct trigger_info_cmn {
- unsigned qminst;
+ uint32_t qminst;
bool has_read;
bool has_not_read;
};
@@ -368,9 +368,9 @@ static bool dds_rhc_default_store (struct dds_rhc_default * __restrict rhc, cons
static void dds_rhc_default_unregister_wr (struct dds_rhc_default * __restrict rhc, const struct ddsi_writer_info * __restrict wrinfo);
static void dds_rhc_default_relinquish_ownership (struct dds_rhc_default * __restrict rhc, const uint64_t wr_iid);
static void dds_rhc_default_set_qos (struct dds_rhc_default *rhc, const struct dds_qos *qos);
-static int dds_rhc_default_read (struct dds_rhc_default *rhc, bool lock, void **values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t mask, dds_instance_handle_t handle, dds_readcond *cond);
-static int dds_rhc_default_take (struct dds_rhc_default *rhc, bool lock, void **values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t mask, dds_instance_handle_t handle, dds_readcond *cond);
-static int dds_rhc_default_takecdr (struct dds_rhc_default *rhc, bool lock, struct ddsi_serdata ** values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t sample_states, uint32_t view_states, uint32_t instance_states, dds_instance_handle_t handle);
+static int32_t dds_rhc_default_read (struct dds_rhc_default *rhc, bool lock, void **values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t mask, dds_instance_handle_t handle, dds_readcond *cond);
+static int32_t dds_rhc_default_take (struct dds_rhc_default *rhc, bool lock, void **values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t mask, dds_instance_handle_t handle, dds_readcond *cond);
+static int32_t dds_rhc_default_takecdr (struct dds_rhc_default *rhc, bool lock, struct ddsi_serdata ** values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t sample_states, uint32_t view_states, uint32_t instance_states, dds_instance_handle_t handle);
static bool dds_rhc_default_add_readcondition (struct dds_rhc_default *rhc, dds_readcond *cond);
static void dds_rhc_default_remove_readcondition (struct dds_rhc_default *rhc, dds_readcond *cond);
static uint32_t dds_rhc_default_lock_samples (struct dds_rhc_default *rhc);
@@ -390,13 +390,13 @@ static void dds_rhc_default_relinquish_ownership_wrap (struct ddsi_rhc * __restr
static void dds_rhc_default_set_qos_wrap (struct ddsi_rhc *rhc, const struct dds_qos *qos) {
dds_rhc_default_set_qos ((struct dds_rhc_default *) rhc, qos);
}
-static int dds_rhc_default_read_wrap (struct dds_rhc *rhc, bool lock, void **values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t mask, dds_instance_handle_t handle, dds_readcond *cond) {
+static int32_t dds_rhc_default_read_wrap (struct dds_rhc *rhc, bool lock, void **values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t mask, dds_instance_handle_t handle, dds_readcond *cond) {
return dds_rhc_default_read ((struct dds_rhc_default *) rhc, lock, values, info_seq, max_samples, mask, handle, cond);
}
-static int dds_rhc_default_take_wrap (struct dds_rhc *rhc, bool lock, void **values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t mask, dds_instance_handle_t handle, dds_readcond *cond) {
+static int32_t dds_rhc_default_take_wrap (struct dds_rhc *rhc, bool lock, void **values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t mask, dds_instance_handle_t handle, dds_readcond *cond) {
return dds_rhc_default_take ((struct dds_rhc_default *) rhc, lock, values, info_seq, max_samples, mask, handle, cond);
}
-static int dds_rhc_default_takecdr_wrap (struct dds_rhc *rhc, bool lock, struct ddsi_serdata **values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t sample_states, uint32_t view_states, uint32_t instance_states, dds_instance_handle_t handle) {
+static int32_t dds_rhc_default_takecdr_wrap (struct dds_rhc *rhc, bool lock, struct ddsi_serdata **values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t sample_states, uint32_t view_states, uint32_t instance_states, dds_instance_handle_t handle) {
return dds_rhc_default_takecdr ((struct dds_rhc_default *) rhc, lock, values, info_seq, max_samples, sample_states, view_states, instance_states, handle);
}
static bool dds_rhc_default_add_readcondition_wrap (struct dds_rhc *rhc, dds_readcond *cond) {
@@ -432,12 +432,12 @@ static const struct dds_rhc_ops dds_rhc_default_ops = {
.associate = dds_rhc_default_associate
};
-static unsigned qmask_of_sample (const struct rhc_sample *s)
+static uint32_t qmask_of_sample (const struct rhc_sample *s)
{
return s->isread ? DDS_READ_SAMPLE_STATE : DDS_NOT_READ_SAMPLE_STATE;
}
-static unsigned qmask_of_invsample (const struct rhc_instance *i)
+static uint32_t qmask_of_invsample (const struct rhc_instance *i)
{
return i->inv_isread ? DDS_READ_SAMPLE_STATE : DDS_NOT_READ_SAMPLE_STATE;
}
@@ -467,17 +467,17 @@ static bool inst_has_unread (const struct rhc_instance *i)
return inst_nread (i) < inst_nsamples (i);
}
-static void topicless_to_clean_invsample (const struct ddsi_sertopic *topic, const struct ddsi_serdata *d, void *sample, void **bufptr, void *buflim)
+static bool topicless_to_clean_invsample (const struct ddsi_sertopic *topic, const struct ddsi_serdata *d, void *sample, void **bufptr, void *buflim)
{
/* ddsi_serdata_topicless_to_sample just deals with the key value, without paying any attention to attributes;
but that makes life harder for the user: the attributes of an invalid sample would be garbage, but would
nonetheless have to be freed in the end. Zero'ing it explicitly solves that problem. */
ddsi_sertopic_free_sample (topic, sample, DDS_FREE_CONTENTS);
ddsi_sertopic_zero_sample (topic, sample);
- ddsi_serdata_topicless_to_sample (topic, d, sample, bufptr, buflim);
+ return ddsi_serdata_topicless_to_sample (topic, d, sample, bufptr, buflim);
}
-static unsigned qmask_of_inst (const struct rhc_instance *inst);
+static uint32_t qmask_of_inst (const struct rhc_instance *inst);
static void free_sample (struct dds_rhc_default *rhc, struct rhc_instance *inst, struct rhc_sample *s);
static void get_trigger_info_cmn (struct trigger_info_cmn *info, struct rhc_instance *inst);
static void get_trigger_info_pre (struct trigger_info_pre *info, struct rhc_instance *inst);
@@ -1462,7 +1462,7 @@ static rhc_store_result_t rhc_store_new_instance (struct rhc_instance **out_inst
static bool dds_rhc_default_store (struct dds_rhc_default * __restrict rhc, const struct ddsi_writer_info * __restrict wrinfo, struct ddsi_serdata * __restrict sample, struct ddsi_tkmap_instance * __restrict tk)
{
const uint64_t wr_iid = wrinfo->iid;
- const unsigned statusinfo = sample->statusinfo;
+ const uint32_t statusinfo = sample->statusinfo;
const bool has_data = (sample->kind == SDK_DATA);
const int is_dispose = (statusinfo & NN_STATUSINFO_DISPOSE) != 0;
struct rhc_instance dummy_instance;
@@ -1845,9 +1845,9 @@ static void dds_rhc_default_relinquish_ownership (struct dds_rhc_default * __res
instance: ANY, ALIVE, NOT_ALIVE, NOT_ALIVE_NO_WRITERS, NOT_ALIVE_DISPOSED
*/
-static unsigned qmask_of_inst (const struct rhc_instance *inst)
+static uint32_t qmask_of_inst (const struct rhc_instance *inst)
{
- unsigned qm = inst->isnew ? DDS_NEW_VIEW_STATE : DDS_NOT_NEW_VIEW_STATE;
+ uint32_t qm = inst->isnew ? DDS_NEW_VIEW_STATE : DDS_NOT_NEW_VIEW_STATE;
if (inst->isdisposed)
qm |= DDS_NOT_ALIVE_DISPOSED_INSTANCE_STATE;
@@ -1905,9 +1905,9 @@ static uint32_t qmask_from_dcpsquery (uint32_t sample_states, uint32_t view_stat
return qminv;
}
-static unsigned qmask_from_mask_n_cond (uint32_t mask, dds_readcond* cond)
+static uint32_t qmask_from_mask_n_cond (uint32_t mask, dds_readcond* cond)
{
- unsigned qminv;
+ uint32_t qminv;
if (mask == NO_STATE_MASK_SET) {
if (cond) {
/* No mask set, use the one from the condition. */
@@ -1962,12 +1962,11 @@ static void patch_generations (dds_sample_info_t *si, uint32_t last_of_inst)
{
if (last_of_inst > 0)
{
- const unsigned ref =
+ const uint32_t ref =
si[last_of_inst].disposed_generation_count + si[last_of_inst].no_writers_generation_count;
- uint32_t i;
assert (si[last_of_inst].sample_rank == 0);
assert (si[last_of_inst].generation_rank == 0);
- for (i = 0; i < last_of_inst; i++)
+ for (uint32_t i = 0; i < last_of_inst; i++)
{
si[i].sample_rank = last_of_inst - i;
si[i].generation_rank = ref - (si[i].disposed_generation_count + si[i].no_writers_generation_count);
@@ -2014,418 +2013,329 @@ static bool take_sample_update_conditions (struct dds_rhc_default *rhc, struct t
return false;
}
-static int dds_rhc_read_w_qminv (struct dds_rhc_default *rhc, bool lock, void **values, dds_sample_info_t *info_seq, uint32_t max_samples, unsigned qminv, dds_instance_handle_t handle, dds_readcond *cond)
+typedef bool (*read_take_to_sample_t) (const struct ddsi_serdata * __restrict d, void *__restrict *__restrict sample, void * __restrict * __restrict bufptr, void * __restrict buflim);
+typedef bool (*read_take_to_invsample_t) (const struct ddsi_sertopic * __restrict topic, const struct ddsi_serdata * __restrict d, void *__restrict * __restrict sample, void * __restrict * __restrict bufptr, void * __restrict buflim);
+
+static bool read_take_to_sample (const struct ddsi_serdata * __restrict d, void * __restrict * __restrict sample, void * __restrict * __restrict bufptr, void * __restrict buflim)
{
- uint32_t n = 0;
+ return ddsi_serdata_to_sample (d, *sample, (void **) bufptr, buflim);
+}
- if (lock)
+static bool read_take_to_invsample (const struct ddsi_sertopic * __restrict topic, const struct ddsi_serdata * __restrict d, void * __restrict * __restrict sample, void * __restrict * __restrict bufptr, void * __restrict buflim)
+{
+ return topicless_to_clean_invsample (topic, d, *sample, (void **) bufptr, buflim);
+}
+
+static bool read_take_to_sample_ref (const struct ddsi_serdata * __restrict d, void * __restrict * __restrict sample, void * __restrict * __restrict bufptr, void * __restrict buflim)
+{
+ (void) bufptr; (void) buflim;
+ *sample = ddsi_serdata_ref (d);
+ return true;
+}
+
+static bool read_take_to_invsample_ref (const struct ddsi_sertopic * __restrict topic, const struct ddsi_serdata * __restrict d, void * __restrict * __restrict sample, void * __restrict * __restrict bufptr, void * __restrict buflim)
+{
+ (void) topic; (void) bufptr; (void) buflim;
+ *sample = ddsi_serdata_ref (d);
+ return true;
+}
+
+static int32_t read_w_qminv_inst (struct dds_rhc_default * const __restrict rhc, struct rhc_instance * const __restrict inst, void * __restrict * __restrict values, dds_sample_info_t * __restrict info_seq, const int32_t max_samples, const uint32_t qminv, const dds_querycond_mask_t qcmask, read_take_to_sample_t to_sample, read_take_to_invsample_t to_invsample)
+{
+ assert (max_samples > 0);
+ if (inst_is_empty (inst) || (qmask_of_inst (inst) & qminv) != 0)
{
- ddsrt_mutex_lock (&rhc->lock);
+ /* no samples present, or the instance/view state doesn't match */
+ return 0;
}
- TRACE ("read_w_qminv(%p,%p,%p,%"PRIu32",%x,%p) - inst %"PRIu32" nonempty %"PRIu32" disp %"PRIu32" nowr %"PRIu32" new %"PRIu32" samples %"PRIu32"+%"PRIu32" read %"PRIu32"+%"PRIu32"\n",
- (void *) rhc, (void *) values, (void *) info_seq, max_samples, qminv, (void *) cond,
- rhc->n_instances, rhc->n_nonempty_instances, rhc->n_not_alive_disposed,
- rhc->n_not_alive_no_writers, rhc->n_new, rhc->n_vsamples, rhc->n_invsamples,
- rhc->n_vread, rhc->n_invread);
+ struct trigger_info_pre pre;
+ struct trigger_info_post post;
+ struct trigger_info_qcond trig_qc;
+ const uint32_t nread = inst_nread (inst);
+ int32_t n = 0;
+ get_trigger_info_pre (&pre, inst);
+ init_trigger_info_qcond (&trig_qc);
- if (!ddsrt_circlist_isempty (&rhc->nonempty_instances))
+ /* any valid samples precede a possible invalid sample */
+ if (inst->latest)
{
- const dds_querycond_mask_t qcmask = (cond && cond->m_query.m_filter) ? cond->m_query.m_qcmask : 0;
- struct rhc_instance * inst = oldest_nonempty_instance (rhc);
- struct rhc_instance * const end = inst;
- do
- {
- if (handle == DDS_HANDLE_NIL || inst->iid == handle)
+ struct rhc_sample *sample = inst->latest->next, * const end1 = sample;
+ do {
+ if ((qmask_of_sample (sample) & qminv) == 0 && (qcmask == 0 || (sample->conds & qcmask)))
{
- if (!inst_is_empty (inst) && (qmask_of_inst (inst) & qminv) == 0)
+ /* sample state matches too */
+ set_sample_info (info_seq + n, inst, sample);
+ to_sample (sample->sample, values + n, 0, 0);
+ if (!sample->isread)
{
- /* samples present & instance, view state matches */
- struct trigger_info_pre pre;
- struct trigger_info_post post;
- struct trigger_info_qcond trig_qc;
- const unsigned nread = inst_nread (inst);
- const uint32_t n_first = n;
- get_trigger_info_pre (&pre, inst);
- init_trigger_info_qcond (&trig_qc);
-
- if (inst->latest)
- {
- struct rhc_sample *sample = inst->latest->next, * const end1 = sample;
- do
- {
- if ((qmask_of_sample (sample) & qminv) == 0 && (qcmask == 0 || (sample->conds & qcmask)))
- {
- /* sample state matches too */
- set_sample_info (info_seq + n, inst, sample);
- ddsi_serdata_to_sample (sample->sample, values[n], 0, 0);
- if (!sample->isread)
- {
- TRACE ("s");
- read_sample_update_conditions (rhc, &pre, &post, &trig_qc, inst, sample->conds, false);
- sample->isread = true;
- inst->nvread++;
- rhc->n_vread++;
- }
- if (++n == max_samples)
- {
- break;
- }
- }
- sample = sample->next;
- }
- while (sample != end1);
- }
-
- if (inst->inv_exists && n < max_samples && (qmask_of_invsample (inst) & qminv) == 0 && (qcmask == 0 || (inst->conds & qcmask)))
- {
- set_sample_info_invsample (info_seq + n, inst);
- topicless_to_clean_invsample (rhc->topic, inst->tk->m_sample, values[n], 0, 0);
- if (!inst->inv_isread)
- {
- TRACE ("i");
- read_sample_update_conditions (rhc, &pre, &post, &trig_qc, inst, inst->conds, false);
- inst->inv_isread = 1;
- rhc->n_invread++;
- }
- ++n;
- }
-
- bool inst_became_old = false;
- if (n > n_first && inst->isnew)
- {
- inst_became_old = true;
- inst->isnew = 0;
- rhc->n_new--;
- }
- if (nread != inst_nread (inst) || inst_became_old)
- {
- size_t ntriggers = SIZE_MAX;
- get_trigger_info_cmn (&post.c, inst);
- assert (trig_qc.dec_conds_invsample == 0);
- assert (trig_qc.dec_conds_sample == 0);
- assert (trig_qc.inc_conds_invsample == 0);
- assert (trig_qc.inc_conds_sample == 0);
- update_conditions_locked (rhc, false, &pre, &post, &trig_qc, inst, NULL, &ntriggers);
- }
-
- if (n > n_first) {
- patch_generations (info_seq + n_first, n - n_first - 1);
- }
- }
- if (inst->iid == handle)
- {
- break;
+ TRACE ("s");
+ read_sample_update_conditions (rhc, &pre, &post, &trig_qc, inst, sample->conds, false);
+ sample->isread = true;
+ inst->nvread++;
+ rhc->n_vread++;
}
+ ++n;
}
- inst = next_nonempty_instance (inst);
+ sample = sample->next;
+ } while (n < max_samples && sample != end1);
+ }
+
+ /* add an invalid sample if it exists, matches and there is room in the result */
+ if (inst->inv_exists && n < max_samples && (qmask_of_invsample (inst) & qminv) == 0 && (qcmask == 0 || (inst->conds & qcmask)))
+ {
+ set_sample_info_invsample (info_seq + n, inst);
+ to_invsample (rhc->topic, inst->tk->m_sample, values + n, 0, 0);
+ if (!inst->inv_isread)
+ {
+ TRACE ("i");
+ read_sample_update_conditions (rhc, &pre, &post, &trig_qc, inst, inst->conds, false);
+ inst->inv_isread = 1;
+ rhc->n_invread++;
}
- while (inst != end && n < max_samples);
+ ++n;
}
- TRACE ("read: returning %"PRIu32"\n", n);
- assert (rhc_check_counts_locked (rhc, true, false));
- ddsrt_mutex_unlock (&rhc->lock);
- assert (n <= INT_MAX);
- return (int)n;
+
+ /* set generation counts in sample info now that we can compute them; update instance state */
+ bool inst_became_old = false;
+ if (n > 0)
+ {
+ patch_generations (info_seq, (uint32_t) n - 1);
+ if (inst->isnew)
+ {
+ inst_became_old = true;
+ inst->isnew = 0;
+ rhc->n_new--;
+ }
+ }
+ if (nread != inst_nread (inst) || inst_became_old)
+ {
+ size_t ntriggers = SIZE_MAX;
+ get_trigger_info_cmn (&post.c, inst);
+ assert (trig_qc.dec_conds_invsample == 0);
+ assert (trig_qc.dec_conds_sample == 0);
+ assert (trig_qc.inc_conds_invsample == 0);
+ assert (trig_qc.inc_conds_sample == 0);
+ update_conditions_locked (rhc, false, &pre, &post, &trig_qc, inst, NULL, &ntriggers);
+ }
+ return n;
}
-static int dds_rhc_take_w_qminv (struct dds_rhc_default *rhc, bool lock, void **values, dds_sample_info_t *info_seq, uint32_t max_samples, unsigned qminv, dds_instance_handle_t handle, dds_readcond *cond)
+static int32_t take_w_qminv_inst (struct dds_rhc_default * const __restrict rhc, struct rhc_instance * const __restrict inst, void * __restrict * __restrict values, dds_sample_info_t * __restrict info_seq, const int32_t max_samples, const uint32_t qminv, const dds_querycond_mask_t qcmask, size_t * __restrict ntriggers, read_take_to_sample_t to_sample, read_take_to_invsample_t to_invsample)
{
- uint64_t iid;
- uint32_t n = 0;
- size_t ntriggers = SIZE_MAX;
-
- if (lock)
+ assert (max_samples > 0);
+ if (inst_is_empty (inst) || (qmask_of_inst (inst) & qminv) != 0)
{
- ddsrt_mutex_lock (&rhc->lock);
+ /* no samples present, or the instance/view state doesn't match */
+ return 0;
}
- TRACE ("take_w_qminv(%p,%p,%p,%"PRIu32",%x) - inst %"PRIu32" nonempty %"PRIu32" disp %"PRIu32" nowr %"PRIu32" new %"PRIu32" samples %"PRIu32"+%"PRIu32" read %"PRIu32"+%"PRIu32"\n",
- (void*) rhc, (void*) values, (void*) info_seq, max_samples, qminv,
- rhc->n_instances, rhc->n_nonempty_instances, rhc->n_not_alive_disposed,
- rhc->n_not_alive_no_writers, rhc->n_new, rhc->n_vsamples,
- rhc->n_invsamples, rhc->n_vread, rhc->n_invread);
+ struct trigger_info_pre pre;
+ struct trigger_info_post post;
+ struct trigger_info_qcond trig_qc;
+ int32_t n = 0;
+ get_trigger_info_pre (&pre, inst);
+ init_trigger_info_qcond (&trig_qc);
- if (!ddsrt_circlist_isempty (&rhc->nonempty_instances))
+ if (inst->latest)
{
- const dds_querycond_mask_t qcmask = (cond && cond->m_query.m_filter) ? cond->m_query.m_qcmask : 0;
- struct rhc_instance *inst = oldest_nonempty_instance (rhc);
- unsigned n_insts = rhc->n_nonempty_instances;
- while (n_insts-- > 0 && n < max_samples)
+ struct rhc_sample *psample = inst->latest;
+ struct rhc_sample *sample = psample->next;
+ uint32_t nvsamples = inst->nvsamples;
+ while (nvsamples--)
{
- struct rhc_instance * const inst1 = next_nonempty_instance (inst);
- iid = inst->iid;
- if (handle == DDS_HANDLE_NIL || iid == handle)
+ struct rhc_sample * const sample1 = sample->next;
+ if ((qmask_of_sample (sample) & qminv) != 0 || (qcmask != 0 && !(sample->conds & qcmask)))
{
- if (!inst_is_empty (inst) && (qmask_of_inst (inst) & qminv) == 0)
+ /* sample mask doesn't match, or content predicate doesn't match */
+ psample = sample;
+ }
+ else
+ {
+ take_sample_update_conditions (rhc, &pre, &post, &trig_qc, inst, sample->conds, sample->isread);
+ set_sample_info (info_seq + n, inst, sample);
+ to_sample (sample->sample, values + n, 0, 0);
+ rhc->n_vsamples--;
+ if (sample->isread)
{
- struct trigger_info_pre pre;
- struct trigger_info_post post;
- struct trigger_info_qcond trig_qc;
- unsigned nvsamples = inst->nvsamples;
- const uint32_t n_first = n;
- get_trigger_info_pre (&pre, inst);
- init_trigger_info_qcond (&trig_qc);
-
- if (inst->latest)
- {
- struct rhc_sample *psample = inst->latest;
- struct rhc_sample *sample = psample->next;
- while (nvsamples--)
- {
- struct rhc_sample * const sample1 = sample->next;
-
- if ((qmask_of_sample (sample) & qminv) != 0 || (qcmask != 0 && !(sample->conds & qcmask)))
- {
- /* sample mask doesn't match, or content predicate doesn't match */
- psample = sample;
- }
- else
- {
- take_sample_update_conditions (rhc, &pre, &post, &trig_qc, inst, sample->conds, sample->isread);
-
- set_sample_info (info_seq + n, inst, sample);
- ddsi_serdata_to_sample (sample->sample, values[n], 0, 0);
- rhc->n_vsamples--;
- if (sample->isread)
- {
- inst->nvread--;
- rhc->n_vread--;
- }
-
- if (--inst->nvsamples > 0)
- {
- if (inst->latest == sample)
- inst->latest = psample;
- psample->next = sample1;
- }
- else
- {
- inst->latest = NULL;
- }
-
- free_sample (rhc, inst, sample);
-
- if (++n == max_samples)
- {
- break;
- }
- }
- sample = sample1;
- }
- }
+ inst->nvread--;
+ rhc->n_vread--;
+ }
+ if (--inst->nvsamples == 0)
+ inst->latest = NULL;
+ else
+ {
+ if (inst->latest == sample)
+ inst->latest = psample;
+ psample->next = sample1;
+ }
+ free_sample (rhc, inst, sample);
+ if (++n == max_samples)
+ break;
+ }
+ sample = sample1;
+ }
+ }
- if (inst->inv_exists && n < max_samples && (qmask_of_invsample (inst) & qminv) == 0 && (qcmask == 0 || (inst->conds & qcmask) != 0))
- {
- struct trigger_info_qcond dummy_trig_qc;
+ if (inst->inv_exists && n < max_samples && (qmask_of_invsample (inst) & qminv) == 0 && (qcmask == 0 || (inst->conds & qcmask) != 0))
+ {
+ struct trigger_info_qcond dummy_trig_qc;
#ifndef NDEBUG
- init_trigger_info_qcond (&dummy_trig_qc);
+ init_trigger_info_qcond (&dummy_trig_qc);
#endif
- take_sample_update_conditions (rhc, &pre, &post, &trig_qc, inst, inst->conds, inst->inv_isread);
- set_sample_info_invsample (info_seq + n, inst);
- topicless_to_clean_invsample (rhc->topic, inst->tk->m_sample, values[n], 0, 0);
- inst_clear_invsample (rhc, inst, &dummy_trig_qc);
- ++n;
- }
+ take_sample_update_conditions (rhc, &pre, &post, &trig_qc, inst, inst->conds, inst->inv_isread);
+ set_sample_info_invsample (info_seq + n, inst);
+ to_invsample (rhc->topic, inst->tk->m_sample, values + n, 0, 0);
+ inst_clear_invsample (rhc, inst, &dummy_trig_qc);
+ ++n;
+ }
- if (n > n_first && inst->isnew)
- {
- inst->isnew = 0;
- rhc->n_new--;
- }
+ if (n > 0)
+ {
+ patch_generations (info_seq, (uint32_t) n - 1);
+ if (inst->isnew)
+ {
+ inst->isnew = 0;
+ rhc->n_new--;
+ }
+ /* if nsamples = 0, it won't match anything, so no need to do anything here for drop_instance_noupdate_no_writers */
+ get_trigger_info_cmn (&post.c, inst);
+ assert (trig_qc.dec_conds_invsample == 0);
+ assert (trig_qc.dec_conds_sample == 0);
+ assert (trig_qc.inc_conds_invsample == 0);
+ assert (trig_qc.inc_conds_sample == 0);
+ update_conditions_locked (rhc, false, &pre, &post, &trig_qc, inst, NULL, ntriggers);
+ }
- if (n > n_first)
- {
- /* if nsamples = 0, it won't match anything, so no need to do
- anything here for drop_instance_noupdate_no_writers */
- get_trigger_info_cmn (&post.c, inst);
- assert (trig_qc.dec_conds_invsample == 0);
- assert (trig_qc.dec_conds_sample == 0);
- assert (trig_qc.inc_conds_invsample == 0);
- assert (trig_qc.inc_conds_sample == 0);
- update_conditions_locked (rhc, false, &pre, &post, &trig_qc, inst, NULL, &ntriggers);
- }
+ if (inst_is_empty (inst))
+ {
+ remove_inst_from_nonempty_list (rhc, inst);
+ if (inst->isdisposed)
+ rhc->n_not_alive_disposed--;
+ if (inst->wrcount == 0)
+ {
+ TRACE ("take: iid %"PRIx64" #0,empty,drop\n", inst->iid);
+ if (!inst->isdisposed)
+ {
+ /* disposed has priority over no writers (why not just 2 bits?) */
+ rhc->n_not_alive_no_writers--;
+ }
+ drop_instance_noupdate_no_writers (rhc, inst);
+ }
+ }
+ return n;
+}
- if (inst_is_empty (inst))
- {
- remove_inst_from_nonempty_list (rhc, inst);
+static int32_t read_w_qminv (struct dds_rhc_default * __restrict rhc, bool lock, void * __restrict * __restrict values, dds_sample_info_t * __restrict info_seq, int32_t max_samples, uint32_t qminv, dds_instance_handle_t handle, dds_readcond * __restrict cond, read_take_to_sample_t to_sample, read_take_to_invsample_t to_invsample)
+{
+ int32_t n = 0;
+ assert (max_samples > 0);
+ if (lock)
+ {
+ ddsrt_mutex_lock (&rhc->lock);
+ }
- if (inst->isdisposed)
- {
- rhc->n_not_alive_disposed--;
- }
- if (inst->wrcount == 0)
- {
- TRACE ("take: iid %"PRIx64" #0,empty,drop\n", iid);
- if (!inst->isdisposed)
- {
- /* disposed has priority over no writers (why not just 2 bits?) */
- rhc->n_not_alive_no_writers--;
- }
- drop_instance_noupdate_no_writers (rhc, inst);
- }
- }
+ TRACE ("read_w_qminv(%p,%p,%p,%"PRId32",%x,%"PRIx64",%p) - inst %"PRIu32" nonempty %"PRIu32" disp %"PRIu32" nowr %"PRIu32" new %"PRIu32" samples %"PRIu32"+%"PRIu32" read %"PRIu32"+%"PRIu32"\n",
+ (void *) rhc, (void *) values, (void *) info_seq, max_samples, qminv, handle, (void *) cond,
+ rhc->n_instances, rhc->n_nonempty_instances, rhc->n_not_alive_disposed,
+ rhc->n_not_alive_no_writers, rhc->n_new, rhc->n_vsamples, rhc->n_invsamples,
+ rhc->n_vread, rhc->n_invread);
- if (n > n_first)
- patch_generations (info_seq + n_first, n - n_first - 1);
- }
- if (iid == handle)
- {
- break;
- }
- }
- inst = inst1;
- }
+ const dds_querycond_mask_t qcmask = (cond && cond->m_query.m_filter) ? cond->m_query.m_qcmask : 0;
+ if (handle)
+ {
+ struct rhc_instance template, *inst;
+ template.iid = handle;
+ if ((inst = ddsrt_hh_lookup (rhc->instances, &template)) != NULL)
+ n = read_w_qminv_inst (rhc, inst, values, info_seq, max_samples, qminv, qcmask, to_sample, to_invsample);
+ else
+ n = DDS_RETCODE_PRECONDITION_NOT_MET;
}
- TRACE ("take: returning %"PRIu32"\n", n);
+ else if (!ddsrt_circlist_isempty (&rhc->nonempty_instances))
+ {
+ struct rhc_instance * inst = oldest_nonempty_instance (rhc);
+ struct rhc_instance * const end = inst;
+ do {
+ n += read_w_qminv_inst(rhc, inst, values + n, info_seq + n, max_samples - n, qminv, qcmask, to_sample, to_invsample);
+ inst = next_nonempty_instance (inst);
+ } while (inst != end && n < max_samples);
+ }
+ TRACE ("read: returning %"PRIu32"\n", n);
assert (rhc_check_counts_locked (rhc, true, false));
+
+ // FIXME: conditional "lock" plus unconditional "unlock" is inexcusably bad design
+ // It appears to have been introduced at some point so another language binding could lock
+ // the RHC using dds_rhc_default_lock_samples to find out the number of samples present,
+ // then allocate stuff and call read/take with lock=true. All that needs fixing.
ddsrt_mutex_unlock (&rhc->lock);
- assert (n <= INT_MAX);
- return (int)n;
+ return n;
}
-static int dds_rhc_takecdr_w_qminv (struct dds_rhc_default *rhc, bool lock, struct ddsi_serdata ** values, dds_sample_info_t *info_seq, uint32_t max_samples, unsigned qminv, dds_instance_handle_t handle, dds_readcond *cond)
+static int32_t take_w_qminv (struct dds_rhc_default * __restrict rhc, bool lock, void * __restrict * __restrict values, dds_sample_info_t * __restrict info_seq, int32_t max_samples, uint32_t qminv, dds_instance_handle_t handle, dds_readcond * __restrict cond, read_take_to_sample_t to_sample, read_take_to_invsample_t to_invsample)
{
- uint64_t iid;
- uint32_t n = 0;
- (void)cond;
-
+ int32_t n = 0;
+ size_t ntriggers = SIZE_MAX;
+ assert (max_samples > 0);
if (lock)
{
ddsrt_mutex_lock (&rhc->lock);
}
- TRACE ("take_w_qminv(%p,%p,%p,%"PRIu32",%x) - inst %"PRIu32" nonempty %"PRIu32" disp %"PRIu32" nowr %"PRIu32" new %"PRIu32" samples %"PRIu32"+%"PRIu32" read %"PRIu32"+%"PRIu32"\n",
- (void*) rhc, (void*) values, (void*) info_seq, max_samples, qminv,
- rhc->n_instances, rhc->n_nonempty_instances, rhc->n_not_alive_disposed,
- rhc->n_not_alive_no_writers, rhc->n_new, rhc->n_vsamples,
- rhc->n_invsamples, rhc->n_vread, rhc->n_invread);
+ TRACE ("take_w_qminv(%p,%p,%p,%"PRId32",%x,%"PRIx64",%p) - inst %"PRIu32" nonempty %"PRIu32" disp %"PRIu32" nowr %"PRIu32" new %"PRIu32" samples %"PRIu32"+%"PRIu32" read %"PRIu32"+%"PRIu32"\n",
+ (void*) rhc, (void*) values, (void*) info_seq, max_samples, qminv, handle, (void *) cond,
+ rhc->n_instances, rhc->n_nonempty_instances, rhc->n_not_alive_disposed,
+ rhc->n_not_alive_no_writers, rhc->n_new, rhc->n_vsamples,
+ rhc->n_invsamples, rhc->n_vread, rhc->n_invread);
- if (!ddsrt_circlist_isempty (&rhc->nonempty_instances))
+ const dds_querycond_mask_t qcmask = (cond && cond->m_query.m_filter) ? cond->m_query.m_qcmask : 0;
+ if (handle)
+ {
+ struct rhc_instance template, *inst;
+ template.iid = handle;
+ if ((inst = ddsrt_hh_lookup (rhc->instances, &template)) != NULL)
+ n = take_w_qminv_inst (rhc, inst, values, info_seq, max_samples, qminv, qcmask, &ntriggers, to_sample, to_invsample);
+ else
+ n = DDS_RETCODE_PRECONDITION_NOT_MET;
+ }
+ else if (!ddsrt_circlist_isempty (&rhc->nonempty_instances))
{
- const dds_querycond_mask_t qcmask = (cond && cond->m_query.m_filter) ? cond->m_query.m_qcmask : 0;
struct rhc_instance *inst = oldest_nonempty_instance (rhc);
- unsigned n_insts = rhc->n_nonempty_instances;
+ uint32_t n_insts = rhc->n_nonempty_instances;
while (n_insts-- > 0 && n < max_samples)
{
struct rhc_instance * const inst1 = next_nonempty_instance (inst);
- iid = inst->iid;
- if (handle == DDS_HANDLE_NIL || iid == handle)
- {
- if (!inst_is_empty (inst) && (qmask_of_inst (inst) & qminv) == 0)
- {
- struct trigger_info_pre pre;
- struct trigger_info_post post;
- struct trigger_info_qcond trig_qc;
- unsigned nvsamples = inst->nvsamples;
- const uint32_t n_first = n;
- get_trigger_info_pre (&pre, inst);
- init_trigger_info_qcond (&trig_qc);
-
- if (inst->latest)
- {
- struct rhc_sample *psample = inst->latest;
- struct rhc_sample *sample = psample->next;
- while (nvsamples--)
- {
- struct rhc_sample * const sample1 = sample->next;
-
- if ((qmask_of_sample (sample) & qminv) != 0 || (qcmask && !(sample->conds & qcmask)))
- {
- psample = sample;
- }
- else
- {
- take_sample_update_conditions (rhc, &pre, &post, &trig_qc, inst, sample->conds, sample->isread);
- set_sample_info (info_seq + n, inst, sample);
- values[n] = ddsi_serdata_ref(sample->sample);
- rhc->n_vsamples--;
- if (sample->isread)
- {
- inst->nvread--;
- rhc->n_vread--;
- }
-
- if (--inst->nvsamples > 0)
- psample->next = sample1;
- else
- inst->latest = NULL;
-
- free_sample (rhc, inst, sample);
-
- if (++n == max_samples)
- {
- break;
- }
- }
- sample = sample1;
- }
- }
-
- if (inst->inv_exists && n < max_samples && (qmask_of_invsample (inst) & qminv) == 0 && (qcmask == 0 || (inst->conds & qcmask) != 0))
- {
- struct trigger_info_qcond dummy_trig_qc;
-#ifndef NDEBUG
- init_trigger_info_qcond (&dummy_trig_qc);
-#endif
- take_sample_update_conditions (rhc, &pre, &post, &trig_qc, inst, inst->conds, inst->inv_isread);
- set_sample_info_invsample (info_seq + n, inst);
- values[n] = ddsi_serdata_ref(inst->tk->m_sample);
- inst_clear_invsample (rhc, inst, &dummy_trig_qc);
- ++n;
- }
-
- if (n > n_first && inst->isnew)
- {
- inst->isnew = 0;
- rhc->n_new--;
- }
-
- if (n > n_first)
- {
- /* if nsamples = 0, it won't match anything, so no need to do
- anything here for drop_instance_noupdate_no_writers */
- size_t ntriggers = SIZE_MAX;
- get_trigger_info_cmn (&post.c, inst);
- update_conditions_locked (rhc, false, &pre, &post, &trig_qc, inst, NULL, &ntriggers);
- }
-
- if (inst_is_empty (inst))
- {
- remove_inst_from_nonempty_list (rhc, inst);
-
- if (inst->isdisposed)
- {
- rhc->n_not_alive_disposed--;
- }
- if (inst->wrcount == 0)
- {
- TRACE ("take: iid %"PRIx64" #0,empty,drop\n", iid);
- if (!inst->isdisposed)
- {
- /* disposed has priority over no writers (why not just 2 bits?) */
- rhc->n_not_alive_no_writers--;
- }
- drop_instance_noupdate_no_writers (rhc, inst);
- }
- }
-
- if (n > n_first)
- patch_generations (info_seq + n_first, n - n_first - 1);
- }
- if (iid == handle)
- {
- break;
- }
- }
+ n += take_w_qminv_inst (rhc, inst, values + n, info_seq + n, max_samples - n, qminv, qcmask, &ntriggers, to_sample, to_invsample);
inst = inst1;
}
}
TRACE ("take: returning %"PRIu32"\n", n);
assert (rhc_check_counts_locked (rhc, true, false));
+
+ // FIXME: conditional "lock" plus unconditional "unlock" is inexcusably bad design
+ // It appears to have been introduced at some point so another language binding could lock
+ // the RHC using dds_rhc_default_lock_samples to find out the number of samples present,
+ // then allocate stuff and call read/take with lock=true. All that needs fixing.
ddsrt_mutex_unlock (&rhc->lock);
- assert (n <= INT_MAX);
- return (int)n;
+ return n;
+}
+
+static int32_t dds_rhc_read_w_qminv (struct dds_rhc_default *rhc, bool lock, void **values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t qminv, dds_instance_handle_t handle, dds_readcond *cond)
+{
+ assert (max_samples <= INT32_MAX);
+ return read_w_qminv (rhc, lock, values, info_seq, (int32_t) max_samples, qminv, handle, cond, read_take_to_sample, read_take_to_invsample);
+}
+
+static int32_t dds_rhc_take_w_qminv (struct dds_rhc_default *rhc, bool lock, void **values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t qminv, dds_instance_handle_t handle, dds_readcond *cond)
+{
+ assert (max_samples <= INT32_MAX);
+ return take_w_qminv (rhc, lock, values, info_seq, (int32_t) max_samples, qminv, handle, cond, read_take_to_sample, read_take_to_invsample);
+}
+
+static int32_t dds_rhc_takecdr_w_qminv (struct dds_rhc_default *rhc, bool lock, struct ddsi_serdata **values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t qminv, dds_instance_handle_t handle, dds_readcond *cond)
+{
+ DDSRT_STATIC_ASSERT (sizeof (void *) == sizeof (struct ddsi_serdata *));
+ assert (max_samples <= INT32_MAX);
+ return take_w_qminv (rhc, lock, (void **) values, info_seq, (int32_t) max_samples, qminv, handle, cond, read_take_to_sample_ref, read_take_to_invsample_ref);
}
/*************************
@@ -2805,21 +2715,21 @@ static bool update_conditions_locked (struct dds_rhc_default *rhc, bool called_f
****** READ/TAKE ******
*************************/
-static int dds_rhc_default_read (struct dds_rhc_default *rhc, bool lock, void **values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t mask, dds_instance_handle_t handle, dds_readcond *cond)
+static int32_t dds_rhc_default_read (struct dds_rhc_default *rhc, bool lock, void **values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t mask, dds_instance_handle_t handle, dds_readcond *cond)
{
- unsigned qminv = qmask_from_mask_n_cond (mask, cond);
- return dds_rhc_read_w_qminv (rhc, lock, values, info_seq, max_samples, qminv, handle, cond);
+ uint32_t qminv = qmask_from_mask_n_cond (mask, cond);
+ return dds_rhc_read_w_qminv (rhc, lock, values, info_seq, max_samples, qminv, handle, cond);
}
-static int dds_rhc_default_take (struct dds_rhc_default *rhc, bool lock, void **values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t mask, dds_instance_handle_t handle, dds_readcond *cond)
+static int32_t dds_rhc_default_take (struct dds_rhc_default *rhc, bool lock, void **values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t mask, dds_instance_handle_t handle, dds_readcond *cond)
{
- unsigned qminv = qmask_from_mask_n_cond(mask, cond);
- return dds_rhc_take_w_qminv (rhc, lock, values, info_seq, max_samples, qminv, handle, cond);
+ uint32_t qminv = qmask_from_mask_n_cond(mask, cond);
+ return dds_rhc_take_w_qminv (rhc, lock, values, info_seq, max_samples, qminv, handle, cond);
}
-static int dds_rhc_default_takecdr (struct dds_rhc_default *rhc, bool lock, struct ddsi_serdata ** values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t sample_states, uint32_t view_states, uint32_t instance_states, dds_instance_handle_t handle)
+static int32_t dds_rhc_default_takecdr (struct dds_rhc_default *rhc, bool lock, struct ddsi_serdata ** values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t sample_states, uint32_t view_states, uint32_t instance_states, dds_instance_handle_t handle)
{
- unsigned qminv = qmask_from_dcpsquery (sample_states, view_states, instance_states);
+ uint32_t qminv = qmask_from_dcpsquery (sample_states, view_states, instance_states);
return dds_rhc_takecdr_w_qminv (rhc, lock, values, info_seq, max_samples, qminv, handle, NULL);
}
@@ -2835,11 +2745,11 @@ static int rhc_check_counts_locked (struct dds_rhc_default *rhc, bool check_cond
return 1;
const uint32_t ncheck = rhc->nconds < CHECK_MAX_CONDS ? rhc->nconds : CHECK_MAX_CONDS;
- unsigned n_instances = 0, n_nonempty_instances = 0;
- unsigned n_not_alive_disposed = 0, n_not_alive_no_writers = 0, n_new = 0;
- unsigned n_vsamples = 0, n_vread = 0;
- unsigned n_invsamples = 0, n_invread = 0;
- unsigned cond_match_count[CHECK_MAX_CONDS];
+ uint32_t n_instances = 0, n_nonempty_instances = 0;
+ uint32_t n_not_alive_disposed = 0, n_not_alive_no_writers = 0, n_new = 0;
+ uint32_t n_vsamples = 0, n_vread = 0;
+ uint32_t n_invsamples = 0, n_invread = 0;
+ uint32_t cond_match_count[CHECK_MAX_CONDS];
dds_querycond_mask_t enabled_qcmask = 0;
struct rhc_instance *inst;
struct ddsrt_hh_iter iter;
@@ -2860,7 +2770,7 @@ static int rhc_check_counts_locked (struct dds_rhc_default *rhc, bool check_cond
for (inst = ddsrt_hh_iter_first (rhc->instances, &iter); inst; inst = ddsrt_hh_iter_next (&iter))
{
- unsigned n_vsamples_in_instance = 0, n_read_vsamples_in_instance = 0;
+ uint32_t n_vsamples_in_instance = 0, n_read_vsamples_in_instance = 0;
bool a_sample_free = true;
n_instances++;
From ed4b4fb21fba1cab3125509482cd7aa61e871118 Mon Sep 17 00:00:00 2001
From: Erik Boasson
Date: Sun, 19 Apr 2020 17:38:25 +0200
Subject: [PATCH 03/36] Add a dds_readcdr analogous to dds_takecdr
Signed-off-by: Erik Boasson
---
src/core/ddsc/include/dds/dds.h | 47 ++++++++++++++++++++++++
src/core/ddsc/include/dds/ddsc/dds_rhc.h | 4 ++
src/core/ddsc/src/dds_read.c | 18 ++++++++-
src/core/ddsc/src/dds_rhc.c | 1 +
src/core/ddsc/src/dds_rhc_default.c | 18 +++++++++
5 files changed, 87 insertions(+), 1 deletion(-)
diff --git a/src/core/ddsc/include/dds/dds.h b/src/core/ddsc/include/dds/dds.h
index e62be5ee60..8383b06842 100644
--- a/src/core/ddsc/include/dds/dds.h
+++ b/src/core/ddsc/include/dds/dds.h
@@ -2756,6 +2756,53 @@ dds_take_mask_wl(
uint32_t maxs,
uint32_t mask);
+#define DDS_HAS_READCDR 1
+/**
+ * @brief Access the collection of serialized data values (of same type) and
+ * sample info from the data reader, readcondition or querycondition.
+ *
+ * This call accesses the serialized data from the data reader, readcondition or
+ * querycondition and makes it available to the application. The serialized data
+ * is made available through \ref ddsi_serdata structures. Returned samples are
+ * marked as READ.
+ *
+ * Return value provides information about the number of samples read, which will
+ * be <= maxs. Based on the count, the buffer will contain serialized data to be
+ * read only when valid_data bit in sample info structure is set.
+ * The buffer required for data values, could be allocated explicitly or can
+ * use the memory from data reader to prevent copy. In the latter case, buffer and
+ * sample_info should be returned back, once it is no longer using the data.
+ *
+ * @param[in] reader_or_condition Reader, readcondition or querycondition entity.
+ * @param[out] buf An array of pointers to \ref ddsi_serdata structures that contain
+ * the serialized data. The pointers can be NULL.
+ * @param[in] maxs Maximum number of samples to read.
+ * @param[out] si Pointer to an array of \ref dds_sample_info_t returned for each data value.
+ * @param[in] mask Filter the data based on dds_sample_state_t|dds_view_state_t|dds_instance_state_t.
+ *
+ * @returns A dds_return_t with the number of samples read or an error code.
+ *
+ * @retval >=0
+ * Number of samples read.
+ * @retval DDS_RETCODE_ERROR
+ * An internal error has occurred.
+ * @retval DDS_RETCODE_BAD_PARAMETER
+ * One of the given arguments is not valid.
+ * @retval DDS_RETCODE_ILLEGAL_OPERATION
+ * The operation is invoked on an inappropriate object.
+ * @retval DDS_RETCODE_ALREADY_DELETED
+ * The entity has already been deleted.
+ * @retval DDS_RETCODE_PRECONDITION_NOT_MET
+ * The precondition for this operation is not met.
+ */
+DDS_EXPORT dds_return_t
+dds_readcdr(
+ dds_entity_t reader_or_condition,
+ struct ddsi_serdata **buf,
+ uint32_t maxs,
+ dds_sample_info_t *si,
+ uint32_t mask);
+
/**
* @brief Access the collection of serialized data values (of same type) and
* sample info from the data reader, readcondition or querycondition.
diff --git a/src/core/ddsc/include/dds/ddsc/dds_rhc.h b/src/core/ddsc/include/dds/ddsc/dds_rhc.h
index b5cba3499b..e3bf4d666a 100644
--- a/src/core/ddsc/include/dds/ddsc/dds_rhc.h
+++ b/src/core/ddsc/include/dds/ddsc/dds_rhc.h
@@ -41,6 +41,7 @@ struct dds_rhc_ops {
struct ddsi_rhc_ops rhc_ops;
dds_rhc_read_take_t read;
dds_rhc_read_take_t take;
+ dds_rhc_read_take_cdr_t readcdr;
dds_rhc_read_take_cdr_t takecdr;
dds_rhc_add_readcondition_t add_readcondition;
dds_rhc_remove_readcondition_t remove_readcondition;
@@ -81,6 +82,9 @@ DDS_EXPORT inline int32_t dds_rhc_read (struct dds_rhc *rhc, bool lock, void **v
DDS_EXPORT inline int32_t dds_rhc_take (struct dds_rhc *rhc, bool lock, void **values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t mask, dds_instance_handle_t handle, struct dds_readcond *cond) {
return rhc->common.ops->take (rhc, lock, values, info_seq, max_samples, mask, handle, cond);
}
+DDS_EXPORT inline int32_t dds_rhc_readcdr (struct dds_rhc *rhc, bool lock, struct ddsi_serdata **values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t sample_states, uint32_t view_states, uint32_t instance_states, dds_instance_handle_t handle) {
+ return rhc->common.ops->readcdr (rhc, lock, values, info_seq, max_samples, sample_states, view_states, instance_states, handle);
+}
DDS_EXPORT inline int32_t dds_rhc_takecdr (struct dds_rhc *rhc, bool lock, struct ddsi_serdata **values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t sample_states, uint32_t view_states, uint32_t instance_states, dds_instance_handle_t handle) {
return rhc->common.ops->takecdr (rhc, lock, values, info_seq, max_samples, sample_states, view_states, instance_states, handle);
}
diff --git a/src/core/ddsc/src/dds_read.c b/src/core/ddsc/src/dds_read.c
index fd9ae5b731..a5c932d028 100644
--- a/src/core/ddsc/src/dds_read.c
+++ b/src/core/ddsc/src/dds_read.c
@@ -171,7 +171,11 @@ static dds_return_t dds_readcdr_impl (bool take, dds_entity_t reader_or_conditio
assert (dds_entity_kind (rd->m_entity.m_parent) == DDS_KIND_SUBSCRIBER);
dds_entity_status_reset (rd->m_entity.m_parent, DDS_DATA_ON_READERS_STATUS);
- ret = dds_rhc_takecdr (rd->m_rhc, lock, buf, si, maxs, mask & DDS_ANY_SAMPLE_STATE, mask & DDS_ANY_VIEW_STATE, mask & DDS_ANY_INSTANCE_STATE, hand);
+ if (take)
+ ret = dds_rhc_takecdr (rd->m_rhc, lock, buf, si, maxs, mask & DDS_ANY_SAMPLE_STATE, mask & DDS_ANY_VIEW_STATE, mask & DDS_ANY_INSTANCE_STATE, hand);
+ else
+ ret = dds_rhc_readcdr (rd->m_rhc, lock, buf, si, maxs, mask & DDS_ANY_SAMPLE_STATE, mask & DDS_ANY_VIEW_STATE, mask & DDS_ANY_INSTANCE_STATE, hand);
+
dds_entity_unpin (entity);
thread_state_asleep (ts1);
return ret;
@@ -225,6 +229,18 @@ dds_return_t dds_read_mask_wl (dds_entity_t rd_or_cnd, void **buf, dds_sample_in
return dds_read_impl (false, rd_or_cnd, buf, maxs, maxs, si, mask, DDS_HANDLE_NIL, lock, false);
}
+dds_return_t dds_readcdr (dds_entity_t rd_or_cnd, struct ddsi_serdata **buf, uint32_t maxs, dds_sample_info_t *si, uint32_t mask)
+{
+ bool lock = true;
+ if (maxs == DDS_READ_WITHOUT_LOCK)
+ {
+ lock = false;
+ /* FIXME: Fix the interface. */
+ maxs = 100;
+ }
+ return dds_readcdr_impl (false, rd_or_cnd, buf, maxs, si, mask, DDS_HANDLE_NIL, lock);
+}
+
dds_return_t dds_read_instance (dds_entity_t rd_or_cnd, void **buf, dds_sample_info_t *si, size_t bufsz, uint32_t maxs, dds_instance_handle_t handle)
{
bool lock = true;
diff --git a/src/core/ddsc/src/dds_rhc.c b/src/core/ddsc/src/dds_rhc.c
index f1594229f7..7db2ff0126 100644
--- a/src/core/ddsc/src/dds_rhc.c
+++ b/src/core/ddsc/src/dds_rhc.c
@@ -22,6 +22,7 @@ extern inline void dds_rhc_set_qos (struct dds_rhc *rhc, const struct dds_qos *q
extern inline void dds_rhc_free (struct dds_rhc *rhc);
extern inline int dds_rhc_read (struct dds_rhc *rhc, bool lock, void **values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t mask, dds_instance_handle_t handle, struct dds_readcond *cond);
extern inline int dds_rhc_take (struct dds_rhc *rhc, bool lock, void **values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t mask, dds_instance_handle_t handle, struct dds_readcond *cond);
+extern inline int dds_rhc_readcdr (struct dds_rhc *rhc, bool lock, struct ddsi_serdata **values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t sample_states, uint32_t view_states, uint32_t instance_states, dds_instance_handle_t handle);
extern inline int dds_rhc_takecdr (struct dds_rhc *rhc, bool lock, struct ddsi_serdata **values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t sample_states, uint32_t view_states, uint32_t instance_states, dds_instance_handle_t handle);
extern inline bool dds_rhc_add_readcondition (struct dds_rhc *rhc, struct dds_readcond *cond);
extern inline void dds_rhc_remove_readcondition (struct dds_rhc *rhc, struct dds_readcond *cond);
diff --git a/src/core/ddsc/src/dds_rhc_default.c b/src/core/ddsc/src/dds_rhc_default.c
index 07692e78f5..d5754f38ce 100644
--- a/src/core/ddsc/src/dds_rhc_default.c
+++ b/src/core/ddsc/src/dds_rhc_default.c
@@ -370,6 +370,7 @@ static void dds_rhc_default_relinquish_ownership (struct dds_rhc_default * __res
static void dds_rhc_default_set_qos (struct dds_rhc_default *rhc, const struct dds_qos *qos);
static int32_t dds_rhc_default_read (struct dds_rhc_default *rhc, bool lock, void **values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t mask, dds_instance_handle_t handle, dds_readcond *cond);
static int32_t dds_rhc_default_take (struct dds_rhc_default *rhc, bool lock, void **values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t mask, dds_instance_handle_t handle, dds_readcond *cond);
+static int32_t dds_rhc_default_readcdr (struct dds_rhc_default *rhc, bool lock, struct ddsi_serdata ** values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t sample_states, uint32_t view_states, uint32_t instance_states, dds_instance_handle_t handle);
static int32_t dds_rhc_default_takecdr (struct dds_rhc_default *rhc, bool lock, struct ddsi_serdata ** values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t sample_states, uint32_t view_states, uint32_t instance_states, dds_instance_handle_t handle);
static bool dds_rhc_default_add_readcondition (struct dds_rhc_default *rhc, dds_readcond *cond);
static void dds_rhc_default_remove_readcondition (struct dds_rhc_default *rhc, dds_readcond *cond);
@@ -396,6 +397,9 @@ static int32_t dds_rhc_default_read_wrap (struct dds_rhc *rhc, bool lock, void *
static int32_t dds_rhc_default_take_wrap (struct dds_rhc *rhc, bool lock, void **values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t mask, dds_instance_handle_t handle, dds_readcond *cond) {
return dds_rhc_default_take ((struct dds_rhc_default *) rhc, lock, values, info_seq, max_samples, mask, handle, cond);
}
+static int32_t dds_rhc_default_readcdr_wrap (struct dds_rhc *rhc, bool lock, struct ddsi_serdata **values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t sample_states, uint32_t view_states, uint32_t instance_states, dds_instance_handle_t handle) {
+ return dds_rhc_default_readcdr ((struct dds_rhc_default *) rhc, lock, values, info_seq, max_samples, sample_states, view_states, instance_states, handle);
+}
static int32_t dds_rhc_default_takecdr_wrap (struct dds_rhc *rhc, bool lock, struct ddsi_serdata **values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t sample_states, uint32_t view_states, uint32_t instance_states, dds_instance_handle_t handle) {
return dds_rhc_default_takecdr ((struct dds_rhc_default *) rhc, lock, values, info_seq, max_samples, sample_states, view_states, instance_states, handle);
}
@@ -425,6 +429,7 @@ static const struct dds_rhc_ops dds_rhc_default_ops = {
},
.read = dds_rhc_default_read_wrap,
.take = dds_rhc_default_take_wrap,
+ .readcdr = dds_rhc_default_readcdr_wrap,
.takecdr = dds_rhc_default_takecdr_wrap,
.add_readcondition = dds_rhc_default_add_readcondition_wrap,
.remove_readcondition = dds_rhc_default_remove_readcondition_wrap,
@@ -2331,6 +2336,13 @@ static int32_t dds_rhc_take_w_qminv (struct dds_rhc_default *rhc, bool lock, voi
return take_w_qminv (rhc, lock, values, info_seq, (int32_t) max_samples, qminv, handle, cond, read_take_to_sample, read_take_to_invsample);
}
+static int32_t dds_rhc_readcdr_w_qminv (struct dds_rhc_default *rhc, bool lock, struct ddsi_serdata **values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t qminv, dds_instance_handle_t handle, dds_readcond *cond)
+{
+ DDSRT_STATIC_ASSERT (sizeof (void *) == sizeof (struct ddsi_serdata *));
+ assert (max_samples <= INT32_MAX);
+ return read_w_qminv (rhc, lock, (void **) values, info_seq, (int32_t) max_samples, qminv, handle, cond, read_take_to_sample_ref, read_take_to_invsample_ref);
+}
+
static int32_t dds_rhc_takecdr_w_qminv (struct dds_rhc_default *rhc, bool lock, struct ddsi_serdata **values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t qminv, dds_instance_handle_t handle, dds_readcond *cond)
{
DDSRT_STATIC_ASSERT (sizeof (void *) == sizeof (struct ddsi_serdata *));
@@ -2727,6 +2739,12 @@ static int32_t dds_rhc_default_take (struct dds_rhc_default *rhc, bool lock, voi
return dds_rhc_take_w_qminv (rhc, lock, values, info_seq, max_samples, qminv, handle, cond);
}
+static int32_t dds_rhc_default_readcdr (struct dds_rhc_default *rhc, bool lock, struct ddsi_serdata ** values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t sample_states, uint32_t view_states, uint32_t instance_states, dds_instance_handle_t handle)
+{
+ uint32_t qminv = qmask_from_dcpsquery (sample_states, view_states, instance_states);
+ return dds_rhc_readcdr_w_qminv (rhc, lock, values, info_seq, max_samples, qminv, handle, NULL);
+}
+
static int32_t dds_rhc_default_takecdr (struct dds_rhc_default *rhc, bool lock, struct ddsi_serdata ** values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t sample_states, uint32_t view_states, uint32_t instance_states, dds_instance_handle_t handle)
{
uint32_t qminv = qmask_from_dcpsquery (sample_states, view_states, instance_states);
From 0563aec148c995093ab59a5b88b1513f1493e6fd Mon Sep 17 00:00:00 2001
From: Erik Boasson
Date: Mon, 20 Apr 2020 11:10:54 +0200
Subject: [PATCH 04/36] Remove a layer of wrapper functions in default RHC
Signed-off-by: Erik Boasson
---
src/core/ddsc/src/dds_rhc_default.c | 135 +++++++++++-----------------
1 file changed, 50 insertions(+), 85 deletions(-)
diff --git a/src/core/ddsc/src/dds_rhc_default.c b/src/core/ddsc/src/dds_rhc_default.c
index d5754f38ce..a4c774c90f 100644
--- a/src/core/ddsc/src/dds_rhc_default.c
+++ b/src/core/ddsc/src/dds_rhc_default.c
@@ -363,79 +363,7 @@ struct trigger_info_post {
struct trigger_info_cmn c;
};
-static void dds_rhc_default_free (struct dds_rhc_default *rhc);
-static bool dds_rhc_default_store (struct dds_rhc_default * __restrict rhc, const struct ddsi_writer_info * __restrict wrinfo, struct ddsi_serdata * __restrict sample, struct ddsi_tkmap_instance * __restrict tk);
-static void dds_rhc_default_unregister_wr (struct dds_rhc_default * __restrict rhc, const struct ddsi_writer_info * __restrict wrinfo);
-static void dds_rhc_default_relinquish_ownership (struct dds_rhc_default * __restrict rhc, const uint64_t wr_iid);
-static void dds_rhc_default_set_qos (struct dds_rhc_default *rhc, const struct dds_qos *qos);
-static int32_t dds_rhc_default_read (struct dds_rhc_default *rhc, bool lock, void **values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t mask, dds_instance_handle_t handle, dds_readcond *cond);
-static int32_t dds_rhc_default_take (struct dds_rhc_default *rhc, bool lock, void **values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t mask, dds_instance_handle_t handle, dds_readcond *cond);
-static int32_t dds_rhc_default_readcdr (struct dds_rhc_default *rhc, bool lock, struct ddsi_serdata ** values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t sample_states, uint32_t view_states, uint32_t instance_states, dds_instance_handle_t handle);
-static int32_t dds_rhc_default_takecdr (struct dds_rhc_default *rhc, bool lock, struct ddsi_serdata ** values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t sample_states, uint32_t view_states, uint32_t instance_states, dds_instance_handle_t handle);
-static bool dds_rhc_default_add_readcondition (struct dds_rhc_default *rhc, dds_readcond *cond);
-static void dds_rhc_default_remove_readcondition (struct dds_rhc_default *rhc, dds_readcond *cond);
-static uint32_t dds_rhc_default_lock_samples (struct dds_rhc_default *rhc);
-
-static void dds_rhc_default_free_wrap (struct ddsi_rhc *rhc) {
- dds_rhc_default_free ((struct dds_rhc_default *) rhc);
-}
-static bool dds_rhc_default_store_wrap (struct ddsi_rhc * __restrict rhc, const struct ddsi_writer_info * __restrict wrinfo, struct ddsi_serdata * __restrict sample, struct ddsi_tkmap_instance * __restrict tk) {
- return dds_rhc_default_store ((struct dds_rhc_default *) rhc, wrinfo, sample, tk);
-}
-static void dds_rhc_default_unregister_wr_wrap (struct ddsi_rhc * __restrict rhc, const struct ddsi_writer_info * __restrict wrinfo) {
- dds_rhc_default_unregister_wr ((struct dds_rhc_default *) rhc, wrinfo);
-}
-static void dds_rhc_default_relinquish_ownership_wrap (struct ddsi_rhc * __restrict rhc, const uint64_t wr_iid) {
- dds_rhc_default_relinquish_ownership ((struct dds_rhc_default *) rhc, wr_iid);
-}
-static void dds_rhc_default_set_qos_wrap (struct ddsi_rhc *rhc, const struct dds_qos *qos) {
- dds_rhc_default_set_qos ((struct dds_rhc_default *) rhc, qos);
-}
-static int32_t dds_rhc_default_read_wrap (struct dds_rhc *rhc, bool lock, void **values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t mask, dds_instance_handle_t handle, dds_readcond *cond) {
- return dds_rhc_default_read ((struct dds_rhc_default *) rhc, lock, values, info_seq, max_samples, mask, handle, cond);
-}
-static int32_t dds_rhc_default_take_wrap (struct dds_rhc *rhc, bool lock, void **values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t mask, dds_instance_handle_t handle, dds_readcond *cond) {
- return dds_rhc_default_take ((struct dds_rhc_default *) rhc, lock, values, info_seq, max_samples, mask, handle, cond);
-}
-static int32_t dds_rhc_default_readcdr_wrap (struct dds_rhc *rhc, bool lock, struct ddsi_serdata **values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t sample_states, uint32_t view_states, uint32_t instance_states, dds_instance_handle_t handle) {
- return dds_rhc_default_readcdr ((struct dds_rhc_default *) rhc, lock, values, info_seq, max_samples, sample_states, view_states, instance_states, handle);
-}
-static int32_t dds_rhc_default_takecdr_wrap (struct dds_rhc *rhc, bool lock, struct ddsi_serdata **values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t sample_states, uint32_t view_states, uint32_t instance_states, dds_instance_handle_t handle) {
- return dds_rhc_default_takecdr ((struct dds_rhc_default *) rhc, lock, values, info_seq, max_samples, sample_states, view_states, instance_states, handle);
-}
-static bool dds_rhc_default_add_readcondition_wrap (struct dds_rhc *rhc, dds_readcond *cond) {
- return dds_rhc_default_add_readcondition ((struct dds_rhc_default *) rhc, cond);
-}
-static void dds_rhc_default_remove_readcondition_wrap (struct dds_rhc *rhc, dds_readcond *cond) {
- dds_rhc_default_remove_readcondition ((struct dds_rhc_default *) rhc, cond);
-}
-static uint32_t dds_rhc_default_lock_samples_wrap (struct dds_rhc *rhc) {
- return dds_rhc_default_lock_samples ((struct dds_rhc_default *) rhc);
-}
-static dds_return_t dds_rhc_default_associate (struct dds_rhc *rhc, dds_reader *reader, const struct ddsi_sertopic *topic, struct ddsi_tkmap *tkmap)
-{
- /* ignored out of laziness */
- (void) rhc; (void) reader; (void) topic; (void) tkmap;
- return DDS_RETCODE_OK;
-}
-
-static const struct dds_rhc_ops dds_rhc_default_ops = {
- .rhc_ops = {
- .store = dds_rhc_default_store_wrap,
- .unregister_wr = dds_rhc_default_unregister_wr_wrap,
- .relinquish_ownership = dds_rhc_default_relinquish_ownership_wrap,
- .set_qos = dds_rhc_default_set_qos_wrap,
- .free = dds_rhc_default_free_wrap
- },
- .read = dds_rhc_default_read_wrap,
- .take = dds_rhc_default_take_wrap,
- .readcdr = dds_rhc_default_readcdr_wrap,
- .takecdr = dds_rhc_default_takecdr_wrap,
- .add_readcondition = dds_rhc_default_add_readcondition_wrap,
- .remove_readcondition = dds_rhc_default_remove_readcondition_wrap,
- .lock_samples = dds_rhc_default_lock_samples_wrap,
- .associate = dds_rhc_default_associate
-};
+static const struct dds_rhc_ops dds_rhc_default_ops;
static uint32_t qmask_of_sample (const struct rhc_sample *s)
{
@@ -675,8 +603,16 @@ struct dds_rhc *dds_rhc_default_new (dds_reader *reader, const struct ddsi_serto
return dds_rhc_default_new_xchecks (reader, &reader->m_entity.m_domain->gv, topic, (reader->m_entity.m_domain->gv.config.enabled_xchecks & DDS_XCHECK_RHC) != 0);
}
-static void dds_rhc_default_set_qos (struct dds_rhc_default * rhc, const dds_qos_t * qos)
+static dds_return_t dds_rhc_default_associate (struct dds_rhc *rhc, dds_reader *reader, const struct ddsi_sertopic *topic, struct ddsi_tkmap *tkmap)
{
+ /* ignored out of laziness */
+ (void) rhc; (void) reader; (void) topic; (void) tkmap;
+ return DDS_RETCODE_OK;
+}
+
+static void dds_rhc_default_set_qos (struct ddsi_rhc *rhc_common, const dds_qos_t * qos)
+{
+ struct dds_rhc_default * const rhc = (struct dds_rhc_default *) rhc_common;
/* Set read related QoS */
rhc->max_samples = qos->resource_limits.max_samples;
@@ -822,8 +758,9 @@ static void free_instance_rhc_free (struct rhc_instance *inst, struct dds_rhc_de
free_empty_instance(inst, rhc);
}
-static uint32_t dds_rhc_default_lock_samples (struct dds_rhc_default *rhc)
+static uint32_t dds_rhc_default_lock_samples (struct dds_rhc *rhc_common)
{
+ struct dds_rhc_default * const rhc = (struct dds_rhc_default *) rhc_common;
uint32_t no;
ddsrt_mutex_lock (&rhc->lock);
no = rhc->n_vsamples + rhc->n_invsamples;
@@ -839,8 +776,9 @@ static void free_instance_rhc_free_wrap (void *vnode, void *varg)
free_instance_rhc_free (vnode, varg);
}
-static void dds_rhc_default_free (struct dds_rhc_default *rhc)
+static void dds_rhc_default_free (struct ddsi_rhc *rhc_common)
{
+ struct dds_rhc_default *rhc = (struct dds_rhc_default *) rhc_common;
#ifdef DDSI_INCLUDE_LIFESPAN
dds_rhc_default_sample_expired_cb (rhc, DDSRT_MTIME_NEVER);
lifespan_fini (&rhc->lifespan);
@@ -1464,8 +1402,9 @@ static rhc_store_result_t rhc_store_new_instance (struct rhc_instance **out_inst
delivered (true unless a reliable sample rejected).
*/
-static bool dds_rhc_default_store (struct dds_rhc_default * __restrict rhc, const struct ddsi_writer_info * __restrict wrinfo, struct ddsi_serdata * __restrict sample, struct ddsi_tkmap_instance * __restrict tk)
+static bool dds_rhc_default_store (struct ddsi_rhc * __restrict rhc_common, const struct ddsi_writer_info * __restrict wrinfo, struct ddsi_serdata * __restrict sample, struct ddsi_tkmap_instance * __restrict tk)
{
+ struct dds_rhc_default * const __restrict rhc = (struct dds_rhc_default * __restrict) rhc_common;
const uint64_t wr_iid = wrinfo->iid;
const uint32_t statusinfo = sample->statusinfo;
const bool has_data = (sample->kind == SDK_DATA);
@@ -1740,7 +1679,7 @@ static bool dds_rhc_default_store (struct dds_rhc_default * __restrict rhc, cons
return delivered;
}
-static void dds_rhc_default_unregister_wr (struct dds_rhc_default * __restrict rhc, const struct ddsi_writer_info * __restrict wrinfo)
+static void dds_rhc_default_unregister_wr (struct ddsi_rhc * __restrict rhc_common, const struct ddsi_writer_info * __restrict wrinfo)
{
/* Only to be called when writer with ID WR_IID has died.
@@ -1757,6 +1696,7 @@ static void dds_rhc_default_unregister_wr (struct dds_rhc_default * __restrict r
need to get two IIDs: the one visible to the application in the
built-in topics and in get_instance_handle, and one used internally
for tracking registrations and unregistrations. */
+ struct dds_rhc_default * __restrict const rhc = (struct dds_rhc_default * __restrict) rhc_common;
bool notify_data_available = false;
struct rhc_instance *inst;
struct ddsrt_hh_iter iter;
@@ -1825,8 +1765,9 @@ static void dds_rhc_default_unregister_wr (struct dds_rhc_default * __restrict r
}
}
-static void dds_rhc_default_relinquish_ownership (struct dds_rhc_default * __restrict rhc, const uint64_t wr_iid)
+static void dds_rhc_default_relinquish_ownership (struct ddsi_rhc * __restrict rhc_common, const uint64_t wr_iid)
{
+ struct dds_rhc_default * __restrict const rhc = (struct dds_rhc_default * __restrict) rhc_common;
struct rhc_instance *inst;
struct ddsrt_hh_iter iter;
ddsrt_mutex_lock (&rhc->lock);
@@ -2393,12 +2334,13 @@ static bool cond_is_sample_state_dependent (const struct dds_readcond *cond)
}
}
-static bool dds_rhc_default_add_readcondition (struct dds_rhc_default *rhc, dds_readcond *cond)
+static bool dds_rhc_default_add_readcondition (struct dds_rhc *rhc_common, dds_readcond *cond)
{
/* On the assumption that a readcondition will be attached to a
waitset for nearly all of its life, we keep track of all
readconditions on a reader in one set, without distinguishing
between those attached to a waitset or not. */
+ struct dds_rhc_default * const rhc = (struct dds_rhc_default *) rhc_common;
struct ddsrt_hh_iter it;
assert ((dds_entity_kind (&cond->m_entity) == DDS_KIND_COND_READ && cond->m_query.m_filter == 0) ||
@@ -2498,8 +2440,9 @@ static bool dds_rhc_default_add_readcondition (struct dds_rhc_default *rhc, dds_
return true;
}
-static void dds_rhc_default_remove_readcondition (struct dds_rhc_default *rhc, dds_readcond *cond)
+static void dds_rhc_default_remove_readcondition (struct dds_rhc *rhc_common, dds_readcond *cond)
{
+ struct dds_rhc_default * const rhc = (struct dds_rhc_default *) rhc_common;
dds_readcond **ptr;
ddsrt_mutex_lock (&rhc->lock);
ptr = &rhc->conds;
@@ -2727,26 +2670,30 @@ static bool update_conditions_locked (struct dds_rhc_default *rhc, bool called_f
****** READ/TAKE ******
*************************/
-static int32_t dds_rhc_default_read (struct dds_rhc_default *rhc, bool lock, void **values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t mask, dds_instance_handle_t handle, dds_readcond *cond)
+static int32_t dds_rhc_default_read (struct dds_rhc *rhc_common, bool lock, void **values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t mask, dds_instance_handle_t handle, dds_readcond *cond)
{
+ struct dds_rhc_default * const rhc = (struct dds_rhc_default *) rhc_common;
uint32_t qminv = qmask_from_mask_n_cond (mask, cond);
return dds_rhc_read_w_qminv (rhc, lock, values, info_seq, max_samples, qminv, handle, cond);
}
-static int32_t dds_rhc_default_take (struct dds_rhc_default *rhc, bool lock, void **values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t mask, dds_instance_handle_t handle, dds_readcond *cond)
+static int32_t dds_rhc_default_take (struct dds_rhc *rhc_common, bool lock, void **values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t mask, dds_instance_handle_t handle, dds_readcond *cond)
{
+ struct dds_rhc_default * const rhc = (struct dds_rhc_default *) rhc_common;
uint32_t qminv = qmask_from_mask_n_cond(mask, cond);
return dds_rhc_take_w_qminv (rhc, lock, values, info_seq, max_samples, qminv, handle, cond);
}
-static int32_t dds_rhc_default_readcdr (struct dds_rhc_default *rhc, bool lock, struct ddsi_serdata ** values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t sample_states, uint32_t view_states, uint32_t instance_states, dds_instance_handle_t handle)
+static int32_t dds_rhc_default_readcdr (struct dds_rhc *rhc_common, bool lock, struct ddsi_serdata ** values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t sample_states, uint32_t view_states, uint32_t instance_states, dds_instance_handle_t handle)
{
+ struct dds_rhc_default * const rhc = (struct dds_rhc_default *) rhc_common;
uint32_t qminv = qmask_from_dcpsquery (sample_states, view_states, instance_states);
return dds_rhc_readcdr_w_qminv (rhc, lock, values, info_seq, max_samples, qminv, handle, NULL);
}
-static int32_t dds_rhc_default_takecdr (struct dds_rhc_default *rhc, bool lock, struct ddsi_serdata ** values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t sample_states, uint32_t view_states, uint32_t instance_states, dds_instance_handle_t handle)
+static int32_t dds_rhc_default_takecdr (struct dds_rhc *rhc_common, bool lock, struct ddsi_serdata ** values, dds_sample_info_t *info_seq, uint32_t max_samples, uint32_t sample_states, uint32_t view_states, uint32_t instance_states, dds_instance_handle_t handle)
{
+ struct dds_rhc_default * const rhc = (struct dds_rhc_default *) rhc_common;
uint32_t qminv = qmask_from_dcpsquery (sample_states, view_states, instance_states);
return dds_rhc_takecdr_w_qminv (rhc, lock, values, info_seq, max_samples, qminv, handle, NULL);
}
@@ -2924,3 +2871,21 @@ static int rhc_check_counts_locked (struct dds_rhc_default *rhc, bool check_cond
}
#undef CHECK_MAX_CONDS
#endif
+
+static const struct dds_rhc_ops dds_rhc_default_ops = {
+ .rhc_ops = {
+ .store = dds_rhc_default_store,
+ .unregister_wr = dds_rhc_default_unregister_wr,
+ .relinquish_ownership = dds_rhc_default_relinquish_ownership,
+ .set_qos = dds_rhc_default_set_qos,
+ .free = dds_rhc_default_free
+ },
+ .read = dds_rhc_default_read,
+ .take = dds_rhc_default_take,
+ .readcdr = dds_rhc_default_readcdr,
+ .takecdr = dds_rhc_default_takecdr,
+ .add_readcondition = dds_rhc_default_add_readcondition,
+ .remove_readcondition = dds_rhc_default_remove_readcondition,
+ .lock_samples = dds_rhc_default_lock_samples,
+ .associate = dds_rhc_default_associate
+};
From c9a2be7f77e82a7c989fa010d45dd47ec7978b41 Mon Sep 17 00:00:00 2001
From: Erik Boasson
Date: Sun, 26 Apr 2020 10:13:15 +0200
Subject: [PATCH 05/36] Null instance pointer after dropping instance
Do not pass a dangling pointer to update_conditions_locked after
dropping an instance. The dangling pointer did not actually get
dereferenced because of the state changes caused by dropping the
samples, but that is cutting a bit fine.
Signed-off-by: Erik Boasson
---
src/core/ddsc/src/dds_rhc_default.c | 101 +++++++++++++++-------------
1 file changed, 53 insertions(+), 48 deletions(-)
diff --git a/src/core/ddsc/src/dds_rhc_default.c b/src/core/ddsc/src/dds_rhc_default.c
index a4c774c90f..e1a17b1741 100644
--- a/src/core/ddsc/src/dds_rhc_default.c
+++ b/src/core/ddsc/src/dds_rhc_default.c
@@ -415,8 +415,9 @@ static void free_sample (struct dds_rhc_default *rhc, struct rhc_instance *inst,
static void get_trigger_info_cmn (struct trigger_info_cmn *info, struct rhc_instance *inst);
static void get_trigger_info_pre (struct trigger_info_pre *info, struct rhc_instance *inst);
static void init_trigger_info_qcond (struct trigger_info_qcond *qc);
-static void drop_instance_noupdate_no_writers (struct dds_rhc_default *rhc, struct rhc_instance *inst);
+static void drop_instance_noupdate_no_writers (struct dds_rhc_default * __restrict rhc, struct rhc_instance * __restrict * __restrict instptr);
static bool update_conditions_locked (struct dds_rhc_default *rhc, bool called_from_insert, const struct trigger_info_pre *pre, const struct trigger_info_post *post, const struct trigger_info_qcond *trig_qc, const struct rhc_instance *inst, struct dds_entity *triggers[], size_t *ntriggers);
+static void account_for_nonempty_to_empty_transition (struct dds_rhc_default * __restrict rhc, struct rhc_instance * __restrict * __restrict instptr, const char *__restrict traceprefix);
#ifndef NDEBUG
static int rhc_check_counts_locked (struct dds_rhc_default *rhc, bool check_conds, bool check_qcmask);
#endif
@@ -512,18 +513,7 @@ static void drop_expired_samples (struct dds_rhc_default *rhc, struct rhc_sample
get_trigger_info_cmn (&post.c, inst);
update_conditions_locked (rhc, false, &pre, &post, &trig_qc, inst, NULL, &ntriggers);
if (inst_is_empty (inst))
- {
- remove_inst_from_nonempty_list (rhc, inst);
- if (inst->isdisposed)
- rhc->n_not_alive_disposed--;
- if (inst->wrcount == 0)
- {
- TRACE ("; iid %"PRIx64" #0,empty,drop", inst->iid);
- if (!inst->isdisposed)
- rhc->n_not_alive_no_writers--;
- drop_instance_noupdate_no_writers (rhc, inst);
- }
- }
+ account_for_nonempty_to_empty_transition(rhc, &inst, "; ");
TRACE (")\n");
}
@@ -1023,8 +1013,9 @@ static void update_inst (struct rhc_instance *inst, const struct ddsi_writer_inf
inst->strength = wrinfo->ownership_strength;
}
-static void drop_instance_noupdate_no_writers (struct dds_rhc_default *rhc, struct rhc_instance *inst)
+static void drop_instance_noupdate_no_writers (struct dds_rhc_default *__restrict rhc, struct rhc_instance * __restrict * __restrict instptr)
{
+ struct rhc_instance *inst = *instptr;
int ret;
assert (inst_is_empty (inst));
@@ -1037,6 +1028,7 @@ static void drop_instance_noupdate_no_writers (struct dds_rhc_default *rhc, stru
(void) ret;
free_empty_instance (inst, rhc);
+ *instptr = NULL;
}
static void dds_rhc_register (struct dds_rhc_default *rhc, struct rhc_instance *inst, uint64_t wr_iid, bool iid_update)
@@ -1163,6 +1155,25 @@ static void account_for_empty_to_nonempty_transition (struct dds_rhc_default *rh
rhc->n_not_alive_no_writers++;
}
+static void account_for_nonempty_to_empty_transition (struct dds_rhc_default *__restrict rhc, struct rhc_instance * __restrict * __restrict instptr, const char * __restrict traceprefix)
+{
+ struct rhc_instance *inst = *instptr;
+ assert (inst_is_empty (inst));
+ remove_inst_from_nonempty_list (rhc, inst);
+ if (inst->isdisposed)
+ rhc->n_not_alive_disposed--;
+ if (inst->wrcount == 0)
+ {
+ TRACE ("%siid %"PRIx64" #0,empty,drop\n", traceprefix, inst->iid);
+ if (!inst->isdisposed)
+ {
+ /* disposed has priority over no writers (why not just 2 bits?) */
+ rhc->n_not_alive_no_writers--;
+ }
+ drop_instance_noupdate_no_writers (rhc, instptr);
+ }
+}
+
static int rhc_unregister_isreg_w_sideeffects (struct dds_rhc_default *rhc, const struct rhc_instance *inst, uint64_t wr_iid)
{
/* Returns 1 if last registration just disappeared */
@@ -1207,8 +1218,9 @@ static int rhc_unregister_isreg_w_sideeffects (struct dds_rhc_default *rhc, cons
}
}
-static int rhc_unregister_updateinst (struct dds_rhc_default *rhc, struct rhc_instance *inst, const struct ddsi_writer_info * __restrict wrinfo, ddsrt_wctime_t tstamp, struct trigger_info_qcond *trig_qc, bool *nda)
+static int rhc_unregister_updateinst (struct dds_rhc_default *rhc, struct rhc_instance **instptr, const struct ddsi_writer_info * __restrict wrinfo, ddsrt_wctime_t tstamp, struct trigger_info_qcond *trig_qc, bool *nda)
{
+ struct rhc_instance * const inst = *instptr;
assert (inst->wrcount > 0);
if (--inst->wrcount > 0)
@@ -1253,7 +1265,7 @@ static int rhc_unregister_updateinst (struct dds_rhc_default *rhc, struct rhc_in
{
/* No content left, no registrations left, so drop */
TRACE (",#0,empty,disposed,drop");
- drop_instance_noupdate_no_writers (rhc, inst);
+ drop_instance_noupdate_no_writers (rhc, instptr);
return 1;
}
else
@@ -1270,8 +1282,9 @@ static int rhc_unregister_updateinst (struct dds_rhc_default *rhc, struct rhc_in
}
}
-static bool dds_rhc_unregister (struct dds_rhc_default *rhc, struct rhc_instance *inst, const struct ddsi_writer_info * __restrict wrinfo, ddsrt_wctime_t tstamp, struct trigger_info_post *post, struct trigger_info_qcond *trig_qc)
+static bool dds_rhc_unregister (struct dds_rhc_default *rhc, struct rhc_instance **instptr, const struct ddsi_writer_info * __restrict wrinfo, ddsrt_wctime_t tstamp, struct trigger_info_post *post, struct trigger_info_qcond *trig_qc)
{
+ struct rhc_instance * const inst = *instptr;
bool notify_data_available = false;
/* 'post' always gets set; instance may have been freed upon return. */
@@ -1281,7 +1294,7 @@ static bool dds_rhc_unregister (struct dds_rhc_default *rhc, struct rhc_instance
/* other registrations remain */
get_trigger_info_cmn (&post->c, inst);
}
- else if (rhc_unregister_updateinst (rhc, inst, wrinfo, tstamp, trig_qc, ¬ify_data_available))
+ else if (rhc_unregister_updateinst (rhc, instptr, wrinfo, tstamp, trig_qc, ¬ify_data_available))
{
/* instance dropped */
init_trigger_info_cmn_nonmatch (&post->c);
@@ -1477,7 +1490,7 @@ static bool dds_rhc_default_store (struct ddsi_rhc * __restrict rhc_common, cons
}
if (statusinfo & NN_STATUSINFO_UNREGISTER)
{
- if (dds_rhc_unregister (rhc, inst, wrinfo, sample->timestamp, &post, &trig_qc))
+ if (dds_rhc_unregister (rhc, &inst, wrinfo, sample->timestamp, &post, &trig_qc))
notify_data_available = true;
}
else
@@ -1633,7 +1646,7 @@ static bool dds_rhc_default_store (struct ddsi_rhc * __restrict rhc_common, cons
mean an application reading "x" after the write and reading it
again after the unregister will see a change in the
no_writers_generation field? */
- dds_rhc_unregister (rhc, inst, wrinfo, sample->timestamp, &post, &trig_qc);
+ dds_rhc_unregister (rhc, &inst, wrinfo, sample->timestamp, &post, &trig_qc);
}
else
{
@@ -1744,7 +1757,7 @@ static void dds_rhc_default_unregister_wr (struct ddsi_rhc * __restrict rhc_comm
}
}
- (void) dds_rhc_unregister (rhc, inst, wrinfo, inst->tstamp, &post, &trig_qc);
+ (void) dds_rhc_unregister (rhc, &inst, wrinfo, inst->tstamp, &post, &trig_qc);
TRACE ("\n");
@@ -2067,8 +2080,9 @@ static int32_t read_w_qminv_inst (struct dds_rhc_default * const __restrict rhc,
return n;
}
-static int32_t take_w_qminv_inst (struct dds_rhc_default * const __restrict rhc, struct rhc_instance * const __restrict inst, void * __restrict * __restrict values, dds_sample_info_t * __restrict info_seq, const int32_t max_samples, const uint32_t qminv, const dds_querycond_mask_t qcmask, size_t * __restrict ntriggers, read_take_to_sample_t to_sample, read_take_to_invsample_t to_invsample)
+static int32_t take_w_qminv_inst (struct dds_rhc_default * const __restrict rhc, struct rhc_instance * __restrict * __restrict instptr, void * __restrict * __restrict values, dds_sample_info_t * __restrict info_seq, const int32_t max_samples, const uint32_t qminv, const dds_querycond_mask_t qcmask, size_t * __restrict ntriggers, read_take_to_sample_t to_sample, read_take_to_invsample_t to_invsample)
{
+ struct rhc_instance *inst = *instptr;
assert (max_samples > 0);
if (inst_is_empty (inst) || (qmask_of_inst (inst) & qminv) != 0)
{
@@ -2154,21 +2168,7 @@ static int32_t take_w_qminv_inst (struct dds_rhc_default * const __restrict rhc,
}
if (inst_is_empty (inst))
- {
- remove_inst_from_nonempty_list (rhc, inst);
- if (inst->isdisposed)
- rhc->n_not_alive_disposed--;
- if (inst->wrcount == 0)
- {
- TRACE ("take: iid %"PRIx64" #0,empty,drop\n", inst->iid);
- if (!inst->isdisposed)
- {
- /* disposed has priority over no writers (why not just 2 bits?) */
- rhc->n_not_alive_no_writers--;
- }
- drop_instance_noupdate_no_writers (rhc, inst);
- }
- }
+ account_for_nonempty_to_empty_transition (rhc, instptr, "take: ");
return n;
}
@@ -2239,7 +2239,7 @@ static int32_t take_w_qminv (struct dds_rhc_default * __restrict rhc, bool lock,
struct rhc_instance template, *inst;
template.iid = handle;
if ((inst = ddsrt_hh_lookup (rhc->instances, &template)) != NULL)
- n = take_w_qminv_inst (rhc, inst, values, info_seq, max_samples, qminv, qcmask, &ntriggers, to_sample, to_invsample);
+ n = take_w_qminv_inst (rhc, &inst, values, info_seq, max_samples, qminv, qcmask, &ntriggers, to_sample, to_invsample);
else
n = DDS_RETCODE_PRECONDITION_NOT_MET;
}
@@ -2250,7 +2250,7 @@ static int32_t take_w_qminv (struct dds_rhc_default * __restrict rhc, bool lock,
while (n_insts-- > 0 && n < max_samples)
{
struct rhc_instance * const inst1 = next_nonempty_instance (inst);
- n += take_w_qminv_inst (rhc, inst, values + n, info_seq + n, max_samples - n, qminv, qcmask, &ntriggers, to_sample, to_invsample);
+ n += take_w_qminv_inst (rhc, &inst, values + n, info_seq + n, max_samples - n, qminv, qcmask, &ntriggers, to_sample, to_invsample);
inst = inst1;
}
}
@@ -2475,7 +2475,9 @@ static bool update_conditions_locked (struct dds_rhc_default *rhc, bool called_f
TRACE ("update_conditions_locked(%p %p) - inst %"PRIu32" nonempty %"PRIu32" disp %"PRIu32" nowr %"PRIu32" new %"PRIu32" samples %"PRIu32" read %"PRIu32"\n",
(void *) rhc, (void *) inst, rhc->n_instances, rhc->n_nonempty_instances, rhc->n_not_alive_disposed,
rhc->n_not_alive_no_writers, rhc->n_new, rhc->n_vsamples, rhc->n_vread);
- TRACE (" read -[%d,%d]+[%d,%d] qcmask -[%"PRIx32",%"PRIx32"]+[%"PRIx32",%"PRIx32"]\n",
+ TRACE (" pre (%"PRIx32",%d,%d) post (%"PRIx32",%d,%d) read -[%d,%d]+[%d,%d] qcmask -[%"PRIx32",%"PRIx32"]+[%"PRIx32",%"PRIx32"]\n",
+ pre->c.qminst, pre->c.has_read, pre->c.has_not_read,
+ post->c.qminst, post->c.has_read, post->c.has_not_read,
trig_qc->dec_invsample_read, trig_qc->dec_sample_read, trig_qc->inc_invsample_read, trig_qc->inc_sample_read,
trig_qc->dec_conds_invsample, trig_qc->dec_conds_sample, trig_qc->inc_conds_invsample, trig_qc->inc_conds_sample);
@@ -2611,15 +2613,18 @@ static bool update_conditions_locked (struct dds_rhc_default *rhc, bool called_f
or there was a match and now there is not: so also scan all samples for matches. The only
difference is in whether the number of matches should be added or subtracted. */
int32_t mcurrent = 0;
- if (inst->inv_exists)
- mcurrent += (qmask_of_invsample (inst) & iter->m_qminv) == 0 && (inst->conds & qcmask) != 0;
- if (inst->latest)
+ if (inst)
{
- struct rhc_sample *sample = inst->latest->next, * const end = sample;
- do {
- mcurrent += (qmask_of_sample (sample) & iter->m_qminv) == 0 && (sample->conds & qcmask) != 0;
- sample = sample->next;
- } while (sample != end);
+ if (inst->inv_exists)
+ mcurrent += (qmask_of_invsample (inst) & iter->m_qminv) == 0 && (inst->conds & qcmask) != 0;
+ if (inst->latest)
+ {
+ struct rhc_sample *sample = inst->latest->next, * const end = sample;
+ do {
+ mcurrent += (qmask_of_sample (sample) & iter->m_qminv) == 0 && (sample->conds & qcmask) != 0;
+ sample = sample->next;
+ } while (sample != end);
+ }
}
if (mdelta == 0 && mcurrent == 0)
TRACE ("no change @ %"PRIu32" (2)", ddsrt_atomic_ld32 (&iter->m_entity.m_status.m_trigger));
From 9f6d1801ae6e0a39fc482127d871d3f3bf21735a Mon Sep 17 00:00:00 2001
From: Erik Boasson
Date: Wed, 29 Apr 2020 17:47:13 +0200
Subject: [PATCH 06/36] Track deadline registration, consolidate updates
Deadline registration, renewal and deregistration was somewhat spread
through the code and relied on the "isdisposed" flag as a proxy for
whether it was registered or not. This consolidates the deadline
handling code in a final step of updating the instance and uses a
separate flag to track whether the instance is currently registered in
the deadline administration or not.
This also makes it possible to trivially change the rules for when
deadline notifications are required, and so allows for, e.g., adding a
mode in which instances in the "no writers" state do not trigger any
deadline missed notifications, or just once (both of which seem useful
modes).
Signed-off-by: Erik Boasson
---
src/core/ddsc/src/dds_rhc_default.c | 129 +++++++++++++---------------
1 file changed, 59 insertions(+), 70 deletions(-)
diff --git a/src/core/ddsc/src/dds_rhc_default.c b/src/core/ddsc/src/dds_rhc_default.c
index e1a17b1741..2ee62f2234 100644
--- a/src/core/ddsc/src/dds_rhc_default.c
+++ b/src/core/ddsc/src/dds_rhc_default.c
@@ -270,6 +270,7 @@ struct rhc_instance {
unsigned wr_iid_islive : 1; /* whether wr_iid is of a live writer */
unsigned inv_exists : 1; /* whether or not state change occurred since last sample (i.e., must return invalid sample) */
unsigned inv_isread : 1; /* whether or not that state change has been read before */
+ unsigned deadline_reg : 1; /* whether or not registered for a deadline (== isdisposed, except store() defers updates) */
uint32_t disposed_gen; /* bloody generation counters - worst invention of mankind */
uint32_t no_writers_gen; /* __/ */
int32_t strength; /* "current" ownership strength */
@@ -713,7 +714,7 @@ static void free_empty_instance (struct rhc_instance *inst, struct dds_rhc_defau
assert (inst_is_empty (inst));
ddsi_tkmap_instance_unref (rhc->tkmap, inst->tk);
#ifdef DDSI_INCLUDE_DEADLINE_MISSED
- if (!inst->isdisposed)
+ if (inst->deadline_reg)
deadline_unregister_instance_locked (&rhc->deadline, &inst->deadline);
#endif
ddsrt_free (inst);
@@ -918,11 +919,6 @@ static bool add_sample (struct dds_rhc_default *rhc, struct rhc_instance *inst,
s->lifespan.t_expire = wrinfo->lifespan_exp;
lifespan_register_sample_locked (&rhc->lifespan, &s->lifespan);
#endif
-#ifdef DDSI_INCLUDE_DEADLINE_MISSED
- /* Only renew the deadline missed counter in case the sample is actually stored in the rhc */
- if (!inst->isdisposed)
- deadline_renew_instance_locked (&rhc->deadline, &inst->deadline);
-#endif
s->conds = 0;
if (rhc->nqconds != 0)
@@ -1264,8 +1260,7 @@ static int rhc_unregister_updateinst (struct dds_rhc_default *rhc, struct rhc_in
else if (inst->isdisposed)
{
/* No content left, no registrations left, so drop */
- TRACE (",#0,empty,disposed,drop");
- drop_instance_noupdate_no_writers (rhc, instptr);
+ TRACE (",#0,empty,nowriters,disposed");
return 1;
}
else
@@ -1318,6 +1313,7 @@ static struct rhc_instance *alloc_new_instance (struct dds_rhc_default *rhc, con
inst->tk = tk;
inst->wrcount = (serdata->statusinfo & NN_STATUSINFO_UNREGISTER) ? 0 : 1;
inst->isdisposed = (serdata->statusinfo & NN_STATUSINFO_DISPOSE) != 0;
+ inst->deadline_reg = 0;
inst->isnew = 1;
inst->a_sample_free = 1;
inst->conds = 0;
@@ -1337,12 +1333,6 @@ static struct rhc_instance *alloc_new_instance (struct dds_rhc_default *rhc, con
inst->conds |= c->m_query.m_qcmask;
}
}
-
-#ifdef DDSI_INCLUDE_DEADLINE_MISSED
- if (!inst->isdisposed)
- deadline_register_instance_locked (&rhc->deadline, &inst->deadline, ddsrt_time_monotonic ());
-#endif
-
return inst;
}
@@ -1410,6 +1400,44 @@ static rhc_store_result_t rhc_store_new_instance (struct rhc_instance **out_inst
return RHC_STORED;
}
+static void postprocess_instance_update (struct dds_rhc_default * __restrict rhc, struct rhc_instance * __restrict * __restrict instptr, const struct trigger_info_pre *pre, const struct trigger_info_post *post, struct trigger_info_qcond *trig_qc, dds_entity *triggers[], size_t *ntriggers)
+{
+ {
+ struct rhc_instance *inst = *instptr;
+
+#ifdef DDSI_INCLUDE_DEADLINE_MISSED
+ if (inst->isdisposed)
+ {
+ if (inst->deadline_reg)
+ {
+ inst->deadline_reg = 0;
+ deadline_unregister_instance_locked (&rhc->deadline, &inst->deadline);
+ }
+ }
+ else
+ {
+ if (inst->deadline_reg)
+ deadline_renew_instance_locked (&rhc->deadline, &inst->deadline);
+ else
+ {
+ deadline_register_instance_locked (&rhc->deadline, &inst->deadline, ddsrt_time_monotonic ());
+ inst->deadline_reg = 1;
+ }
+ }
+#endif
+
+ if (inst_is_empty (inst) && inst->wrcount == 0)
+ {
+ drop_instance_noupdate_no_writers (rhc, instptr);
+ }
+ }
+
+ if (trigger_info_differs (rhc, pre, post, trig_qc))
+ update_conditions_locked (rhc, true, pre, post, trig_qc, *instptr, triggers, ntriggers);
+
+ assert (rhc_check_counts_locked (rhc, true, true));
+}
+
/*
dds_rhc_store: DDSI up call into read cache to store new sample. Returns whether sample
delivered (true unless a reliable sample rejected).
@@ -1429,22 +1457,25 @@ static bool dds_rhc_default_store (struct ddsi_rhc * __restrict rhc_common, cons
struct trigger_info_qcond trig_qc;
rhc_store_result_t stored;
status_cb_data_t cb_data; /* Callback data for reader status callback */
- bool delivered = true;
- bool notify_data_available = false;
+ bool notify_data_available;
+ dds_entity *triggers[MAX_FAST_TRIGGERS];
+ size_t ntriggers;
- TRACE ("rhc_store(%"PRIx64",%"PRIx64" si %x has_data %d:", tk->m_iid, wr_iid, statusinfo, has_data);
+ TRACE ("rhc_store %"PRIx64",%"PRIx64" si %x has_data %d:", tk->m_iid, wr_iid, statusinfo, has_data);
if (!has_data && statusinfo == 0)
{
/* Write with nothing but a key -- I guess that would be a
register, which we do implicitly. (Currently DDSI2 won't allow
it through anyway.) */
- TRACE (" ignore explicit register)\n");
- return delivered;
+ TRACE (" ignore explicit register\n");
+ return true;
}
+ notify_data_available = false;
dummy_instance.iid = tk->m_iid;
stored = RHC_FILTERED;
cb_data.raw_status_id = -1;
+ ntriggers = 0;
init_trigger_info_qcond (&trig_qc);
@@ -1459,7 +1490,7 @@ static bool dds_rhc_default_store (struct ddsi_rhc * __restrict rhc_common, cons
*/
if (!has_data && !is_dispose)
{
- TRACE (" disp/unreg on unknown instance");
+ TRACE (" unreg on unknown instance\n");
goto error_or_nochange;
}
else
@@ -1481,7 +1512,7 @@ static bool dds_rhc_default_store (struct ddsi_rhc * __restrict rhc_common, cons
will raise a SAMPLE_REJECTED, and indicate that the system should
kill itself.) Not letting instances go to ALIVE or NEW based on
a rejected sample - (no one knows, it seemed) */
- TRACE (" instance rejects sample");
+ TRACE (" instance rejects sample\n");
get_trigger_info_pre (&pre, inst);
if (has_data || is_dispose)
@@ -1553,19 +1584,11 @@ static bool dds_rhc_default_store (struct ddsi_rhc * __restrict rhc_common, cons
TRACE (" disposed->notdisposed");
inst->isdisposed = 0;
inst->disposed_gen++;
-#ifdef DDSI_INCLUDE_DEADLINE_MISSED
- if (!is_dispose)
- deadline_register_instance_locked (&rhc->deadline, &inst->deadline, ddsrt_time_monotonic ());
-#endif
}
if (is_dispose)
{
inst->isdisposed = 1;
inst_became_disposed = !old_isdisposed;
-#ifdef DDSI_INCLUDE_DEADLINE_MISSED
- if (inst_became_disposed)
- deadline_unregister_instance_locked (&rhc->deadline, &inst->deadline);
-#endif
TRACE (" dispose(%d)", inst_became_disposed);
}
@@ -1587,17 +1610,11 @@ static bool dds_rhc_default_store (struct ddsi_rhc * __restrict rhc_common, cons
if (!inst->isdisposed)
{
inst->isdisposed = 1;
-#ifdef DDSI_INCLUDE_DEADLINE_MISSED
- deadline_unregister_instance_locked (&rhc->deadline, &inst->deadline);
-#endif
}
}
else if (inst->isdisposed)
{
inst->isdisposed = 0;
-#ifdef DDSI_INCLUDE_DEADLINE_MISSED
- deadline_register_instance_locked (&rhc->deadline, &inst->deadline, ddsrt_time_monotonic ());
-#endif
}
goto error_or_nochange;
}
@@ -1654,15 +1671,9 @@ static bool dds_rhc_default_store (struct ddsi_rhc * __restrict rhc_common, cons
}
}
- TRACE (")\n");
-
- dds_entity *triggers[MAX_FAST_TRIGGERS];
- size_t ntriggers = 0;
- if (trigger_info_differs (rhc, &pre, &post, &trig_qc))
- update_conditions_locked (rhc, true, &pre, &post, &trig_qc, inst, triggers, &ntriggers);
-
- assert (rhc_check_counts_locked (rhc, true, true));
+ postprocess_instance_update (rhc, &inst, &pre, &post, &trig_qc, triggers, &ntriggers);
+error_or_nochange:
ddsrt_mutex_unlock (&rhc->lock);
if (rhc->reader)
@@ -1671,25 +1682,10 @@ static bool dds_rhc_default_store (struct ddsi_rhc * __restrict rhc_common, cons
dds_reader_data_available_cb (rhc->reader);
for (size_t i = 0; i < ntriggers; i++)
dds_entity_status_signal (triggers[i], 0);
+ if (cb_data.raw_status_id >= 0)
+ dds_reader_status_cb (&rhc->reader->m_entity, &cb_data);
}
-
- return delivered;
-
-error_or_nochange:
-
- if (rhc->reliable && (stored == RHC_REJECTED))
- {
- delivered = false;
- }
-
- ddsrt_mutex_unlock (&rhc->lock);
- TRACE (")\n");
-
- /* Make any reader status callback */
-
- if (cb_data.raw_status_id >= 0 && rhc->reader)
- dds_reader_status_cb (&rhc->reader->m_entity, &cb_data);
- return delivered;
+ return !(rhc->reliable && stored == RHC_REJECTED);
}
static void dds_rhc_default_unregister_wr (struct ddsi_rhc * __restrict rhc_common, const struct ddsi_writer_info * __restrict wrinfo)
@@ -1736,9 +1732,6 @@ static void dds_rhc_default_unregister_wr (struct ddsi_rhc * __restrict rhc_comm
if (auto_dispose && !inst->isdisposed)
{
inst->isdisposed = 1;
-#ifdef DDSI_INCLUDE_DEADLINE_MISSED
- deadline_unregister_instance_locked (&rhc->deadline, &inst->deadline);
-#endif
/* Set invalid sample for disposing it (unregister may also set it for unregistering) */
if (inst->latest)
@@ -1758,13 +1751,9 @@ static void dds_rhc_default_unregister_wr (struct ddsi_rhc * __restrict rhc_comm
}
(void) dds_rhc_unregister (rhc, &inst, wrinfo, inst->tstamp, &post, &trig_qc);
-
- TRACE ("\n");
-
notify_data_available = true;
- if (trigger_info_differs (rhc, &pre, &post, &trig_qc))
- update_conditions_locked (rhc, true, &pre, &post, &trig_qc, inst, NULL, &ntriggers);
- assert (rhc_check_counts_locked (rhc, true, false));
+ postprocess_instance_update (rhc, &inst, &pre, &post, &trig_qc, NULL, &ntriggers);
+ TRACE ("\n");
}
}
TRACE (")\n");
From 999f9ed2bd603d7e52c913126bf9ad48e49bfb2e Mon Sep 17 00:00:00 2001
From: Erik Boasson
Date: Wed, 29 Apr 2020 17:59:22 +0200
Subject: [PATCH 07/36] Auto-dispose when the instance goes to NO_WRITERS
This changes the behaviour of auto-dispose writers: instead of always
disposing when the writer disposes the data, it now only disposes the
data when the instance would otherwise go to the "no writers" state.
This only affects the behaviour when there are multiple writers for the
same instance.
In case the writers use a different value for the auto-dispose setting,
it now tracks whether an instance has ever been touched by an writer
with auto-dispose enabled, and treats auto-disposes the instance when
the last writer leaves if this is the case. This way, if an instance is
registered by one auto-dispose and one non-auto-dispose writer, the
order of unregistering does not matter.
Signed-off-by: Erik Boasson
---
src/core/ddsc/src/dds_rhc_default.c | 67 ++++++++++++++++-------
src/core/ddsc/tests/listener.c | 6 +-
src/core/xtests/rhc_torture/rhc_torture.c | 44 +++++++++++----
3 files changed, 84 insertions(+), 33 deletions(-)
diff --git a/src/core/ddsc/src/dds_rhc_default.c b/src/core/ddsc/src/dds_rhc_default.c
index 2ee62f2234..09989e04a9 100644
--- a/src/core/ddsc/src/dds_rhc_default.c
+++ b/src/core/ddsc/src/dds_rhc_default.c
@@ -267,6 +267,7 @@ struct rhc_instance {
unsigned isnew : 1; /* NEW or NOT_NEW view state */
unsigned a_sample_free : 1; /* whether or not a_sample is in use */
unsigned isdisposed : 1; /* DISPOSED or NOT_DISPOSED (if not disposed, wrcount determines ALIVE/NOT_ALIVE_NO_WRITERS) */
+ unsigned autodispose : 1; /* wrcount > 0 => at least one registered writer has had auto-dispose set on some update */
unsigned wr_iid_islive : 1; /* whether wr_iid is of a live writer */
unsigned inv_exists : 1; /* whether or not state change occurred since last sample (i.e., must return invalid sample) */
unsigned inv_isread : 1; /* whether or not that state change has been read before */
@@ -696,7 +697,12 @@ static void inst_clear_invsample_if_exists (struct dds_rhc_default *rhc, struct
static void inst_set_invsample (struct dds_rhc_default *rhc, struct rhc_instance *inst, struct trigger_info_qcond *trig_qc, bool *nda)
{
- if (!inst->inv_exists || inst->inv_isread)
+ if (inst->inv_exists && !inst->inv_isread)
+ {
+ /* FIXME: should this indeed trigger a "notify data available" event?*/
+ *nda = true;
+ }
+ else
{
/* Obviously optimisable, but that is perhaps not worth the bother */
inst_clear_invsample_if_exists (rhc, inst, trig_qc);
@@ -1027,7 +1033,7 @@ static void drop_instance_noupdate_no_writers (struct dds_rhc_default *__restric
*instptr = NULL;
}
-static void dds_rhc_register (struct dds_rhc_default *rhc, struct rhc_instance *inst, uint64_t wr_iid, bool iid_update)
+static void dds_rhc_register (struct dds_rhc_default *rhc, struct rhc_instance *inst, uint64_t wr_iid, bool autodispose, bool iid_update)
{
const uint64_t inst_wr_iid = inst->wr_iid_islive ? inst->wr_iid : 0;
@@ -1064,6 +1070,7 @@ static void dds_rhc_register (struct dds_rhc_default *rhc, struct rhc_instance *
}
inst->wrcount++;
inst->no_writers_gen++;
+ inst->autodispose = autodispose;
TRACE ("new1");
if (!inst_is_empty (inst) && !inst->isdisposed)
@@ -1089,6 +1096,8 @@ static void dds_rhc_register (struct dds_rhc_default *rhc, struct rhc_instance *
if (lwregs_add (&rhc->registrations, inst->iid, wr_iid))
{
inst->wrcount++;
+ if (autodispose)
+ inst->autodispose = 1;
TRACE ("new2iidnull");
}
else
@@ -1125,6 +1134,8 @@ static void dds_rhc_register (struct dds_rhc_default *rhc, struct rhc_instance *
registers a previously unknown writer or not */
TRACE ("new3");
inst->wrcount++;
+ if (autodispose)
+ inst->autodispose = 1;
}
else
{
@@ -1218,6 +1229,8 @@ static int rhc_unregister_updateinst (struct dds_rhc_default *rhc, struct rhc_in
{
struct rhc_instance * const inst = *instptr;
assert (inst->wrcount > 0);
+ if (wrinfo->auto_dispose)
+ inst->autodispose = 1;
if (--inst->wrcount > 0)
{
@@ -1239,20 +1252,27 @@ static int rhc_unregister_updateinst (struct dds_rhc_default *rhc, struct rhc_in
if (!inst_is_empty (inst))
{
/* Instance still has content - do not drop until application
- takes the last sample. Set the invalid sample if the latest
- sample has been read already, so that the application can
- read the change to not-alive. (If the latest sample is still
- unread, we don't bother, even though it means the application
- won't see the timestamp for the unregister event. It shouldn't
- care.) */
- if (inst->latest == NULL || inst->latest->isread)
- {
- inst_set_invsample (rhc, inst, trig_qc, nda);
- update_inst (inst, wrinfo, false, tstamp);
- }
+ takes the last sample. Set the invalid sample if the latest
+ sample has been read already, so that the application can
+ read the change to not-alive. (If the latest sample is still
+ unread, we don't bother, even though it means the application
+ won't see the timestamp for the unregister event. It shouldn't
+ care.) */
if (!inst->isdisposed)
{
- rhc->n_not_alive_no_writers++;
+ if (inst->latest == NULL || inst->latest->isread)
+ {
+ inst_set_invsample (rhc, inst, trig_qc, nda);
+ update_inst (inst, wrinfo, false, tstamp);
+ }
+ if (!inst->autodispose)
+ rhc->n_not_alive_no_writers++;
+ else
+ {
+ TRACE (",autodispose");
+ inst->isdisposed = 1;
+ rhc->n_not_alive_disposed++;
+ }
}
inst->wr_iid_islive = 0;
return 0;
@@ -1270,6 +1290,11 @@ static int rhc_unregister_updateinst (struct dds_rhc_default *rhc, struct rhc_in
assert (inst_is_empty (inst));
inst_set_invsample (rhc, inst, trig_qc, nda);
update_inst (inst, wrinfo, false, tstamp);
+ if (inst->autodispose)
+ {
+ TRACE (",autodispose");
+ inst->isdisposed = 1;
+ }
account_for_empty_to_nonempty_transition (rhc, inst);
inst->wr_iid_islive = 0;
return 0;
@@ -1313,6 +1338,7 @@ static struct rhc_instance *alloc_new_instance (struct dds_rhc_default *rhc, con
inst->tk = tk;
inst->wrcount = (serdata->statusinfo & NN_STATUSINFO_UNREGISTER) ? 0 : 1;
inst->isdisposed = (serdata->statusinfo & NN_STATUSINFO_DISPOSE) != 0;
+ inst->autodispose = wrinfo->auto_dispose;
inst->deadline_reg = 0;
inst->isnew = 1;
inst->a_sample_free = 1;
@@ -1517,7 +1543,7 @@ static bool dds_rhc_default_store (struct ddsi_rhc * __restrict rhc_common, cons
get_trigger_info_pre (&pre, inst);
if (has_data || is_dispose)
{
- dds_rhc_register (rhc, inst, wr_iid, false);
+ dds_rhc_register (rhc, inst, wr_iid, wrinfo->auto_dispose, false);
}
if (statusinfo & NN_STATUSINFO_UNREGISTER)
{
@@ -1563,7 +1589,7 @@ static bool dds_rhc_default_store (struct ddsi_rhc * __restrict rhc_common, cons
(i.e., out-of-memory), abort the operation and hope that the
caller can still notify the application. */
- dds_rhc_register (rhc, inst, wr_iid, true);
+ dds_rhc_register (rhc, inst, wr_iid, wrinfo->auto_dispose, true);
/* Sample arriving for a NOT_ALIVE instance => view state NEW */
if (has_data && not_alive)
@@ -1710,16 +1736,16 @@ static void dds_rhc_default_unregister_wr (struct ddsi_rhc * __restrict rhc_comm
struct rhc_instance *inst;
struct ddsrt_hh_iter iter;
const uint64_t wr_iid = wrinfo->iid;
- const int auto_dispose = wrinfo->auto_dispose;
-
+ const bool auto_dispose = wrinfo->auto_dispose;
size_t ntriggers = SIZE_MAX;
ddsrt_mutex_lock (&rhc->lock);
- TRACE ("rhc_unregister_wr_iid(%"PRIx64",%d:\n", wr_iid, auto_dispose);
+ TRACE ("rhc_unregister_wr_iid %"PRIx64",%d:\n", wr_iid, wrinfo->auto_dispose);
for (inst = ddsrt_hh_iter_first (rhc->instances, &iter); inst; inst = ddsrt_hh_iter_next (&iter))
{
if ((inst->wr_iid_islive && inst->wr_iid == wr_iid) || lwregs_contains (&rhc->registrations, inst->iid, wr_iid))
{
+ assert (inst->wrcount > 0);
struct trigger_info_pre pre;
struct trigger_info_post post;
struct trigger_info_qcond trig_qc;
@@ -1731,10 +1757,11 @@ static void dds_rhc_default_unregister_wr (struct ddsi_rhc * __restrict rhc_comm
assert (inst->wrcount > 0);
if (auto_dispose && !inst->isdisposed)
{
+ notify_data_available = true;
inst->isdisposed = 1;
/* Set invalid sample for disposing it (unregister may also set it for unregistering) */
- if (inst->latest)
+ if (inst->latest && !inst->latest->isread)
{
assert (!inst->inv_exists);
rhc->n_not_alive_disposed++;
diff --git a/src/core/ddsc/tests/listener.c b/src/core/ddsc/tests/listener.c
index 1ad9f71488..0ab0108070 100644
--- a/src/core/ddsc/tests/listener.c
+++ b/src/core/ddsc/tests/listener.c
@@ -997,9 +997,9 @@ CU_Test(ddsc_listener, data_available_delete_writer_disposed, .init=init_trigger
ret = dds_delete (g_writer);
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
g_writer = 0;
- triggered = waitfor_cb(DDS_DATA_AVAILABLE_STATUS);
- CU_ASSERT_EQUAL_FATAL(triggered & DDS_DATA_AVAILABLE_STATUS, DDS_DATA_AVAILABLE_STATUS);
- CU_ASSERT_EQUAL_FATAL(cb_reader, g_reader);
+ ddsrt_mutex_lock(&g_mutex);
+ CU_ASSERT_EQUAL_FATAL(cb_called & DDS_DATA_AVAILABLE_STATUS_ID, 0);
+ ddsrt_mutex_unlock(&g_mutex);
/* The listener should have swallowed the status. */
ret = dds_read_status(g_subscriber, &status, DDS_DATA_ON_READERS_STATUS);
diff --git a/src/core/xtests/rhc_torture/rhc_torture.c b/src/core/xtests/rhc_torture/rhc_torture.c
index fe9dc53228..132fbc643b 100644
--- a/src/core/xtests/rhc_torture/rhc_torture.c
+++ b/src/core/xtests/rhc_torture/rhc_torture.c
@@ -935,6 +935,7 @@ int main (int argc, char **argv)
printf ("************* 0 *************\n");
struct dds_rhc *rhc = mkrhc (gv, NULL, DDS_HISTORY_KEEP_LAST, 1, DDS_DESTINATIONORDER_BY_SOURCE_TIMESTAMP);
struct proxy_writer *wr0 = mkwr (gv, 1);
+ struct proxy_writer *wr1 = mkwr (gv, 1);
uint64_t iid0, iid1, iid_t;
iid0 = store (tkmap, rhc, wr0, mksample (0, 0), print, false);
iid1 = store (tkmap, rhc, wr0, mksample (1, NN_STATUSINFO_DISPOSE), print, false);
@@ -944,17 +945,38 @@ int main (int argc, char **argv)
{ 0, 0, 0, 0, 0, 0, 0, 0 }
};
rdall (rhc, c0, print, states_seen);
- iid_t = store (tkmap, rhc, wr0, mkkeysample (0, NN_STATUSINFO_UNREGISTER), print, false);
+ /* write instance 0 with 2nd writer to have 2 live writers */
+ iid_t = store (tkmap, rhc, wr1, mksample (0, 0), print, false);
assert (iid_t == iid0);
- (void)iid0;
- (void)iid_t;
const struct check c1[] = {
- { "ROU", iid0, wr0->e.iid, 0,0, 1, 0,1 },
- { "NOU", iid0, 0, 0,0, 0, 0,0 },
+ { "NOA", iid0, wr1->e.iid, 0,0, 1, 0,3 },
{ "ROD", iid1, wr0->e.iid, 0,0, 1, 1,2 },
{ 0, 0, 0, 0, 0, 0, 0, 0 }
};
rdall (rhc, c1, print, states_seen);
+ /* unregister instance 0 with wr0 - autodispose, but 2nd writer keeps it alive, no visible change */
+ iid_t = store (tkmap, rhc, wr0, mkkeysample (0, NN_STATUSINFO_UNREGISTER), print, false);
+ assert (iid_t == iid0);
+ const struct check c2[] = {
+ { "ROA", iid0, wr1->e.iid, 0,0, 1, 0,3 },
+ { "ROD", iid1, wr0->e.iid, 0,0, 1, 1,2 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 }
+ };
+ rdall (rhc, c2, print, states_seen);
+ /* unregistering instance 0 again should be a no-op because wr0 no longer has it registered */
+ iid_t = store (tkmap, rhc, wr0, mkkeysample (0, NN_STATUSINFO_UNREGISTER), print, false);
+ assert (iid_t == iid0);
+ rdall (rhc, c2, print, states_seen);
+ /* unregistering instance 0 with wr1 - autodispose, no live writers -> dispose */
+ iid_t = store (tkmap, rhc, wr1, mkkeysample (0, NN_STATUSINFO_UNREGISTER), print, false);
+ assert (iid_t == iid0);
+ const struct check c3[] = {
+ { "ROD", iid0, wr1->e.iid, 0,0, 1, 0,3 },
+ { "NOD", iid0, 0, 0,0, 0, 0,0 },
+ { "ROD", iid1, wr0->e.iid, 0,0, 1, 1,2 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 }
+ };
+ rdall (rhc, c3, print, states_seen);
thread_state_awake_domain_ok (lookup_thread_state ());
struct ddsi_writer_info wr0_info;
wr0_info.auto_dispose = wr0->c.xqos->writer_data_lifecycle.autodispose_unregistered_instances;
@@ -966,16 +988,18 @@ int main (int argc, char **argv)
#endif
dds_rhc_unregister_wr (rhc, &wr0_info);
thread_state_asleep (lookup_thread_state ());
- const struct check c2[] = {
- { "ROU", iid0, wr0->e.iid, 0,0, 1, 0,1 },
- { "ROU", iid0, 0, 0,0, 0, 0,0 },
+ const struct check c4[] = {
+ { "ROD", iid0, wr1->e.iid, 0,0, 1, 0,3 },
+ { "ROD", iid0, 0, 0,0, 0, 0,0 },
{ "ROD", iid1, wr0->e.iid, 0,0, 1, 1,2 },
- { "NOD", iid1, 0, 0,0, 0, 1,0 },
+ // { "NOD", iid1, 0, 0,0, 0, 1,0 }, doesn't exist because it is already disposed
{ 0, 0, 0, 0, 0, 0, 0, 0 }
};
- tkall (rhc, c2, print, states_seen);
+ tkall (rhc, c4, print, states_seen);
frhc (rhc);
fwr (wr0);
+ fwr (wr1);
+ (void)iid_t;
}
if (1 >= first)
From 8c77d6a19c882b790394b5887cbb625d50cb95c6 Mon Sep 17 00:00:00 2001
From: Erik Boasson
Date: Wed, 29 Apr 2020 19:49:59 +0200
Subject: [PATCH 08/36] Default to doing expensive checks in rhc_torture
The entire point of this test program is to exercise the RHC while
checking its internal state. The likelihood of (at least some)
forgetting to enable the "expensive" checks has been proven to be
significant.
Signed-off-by: Erik Boasson
---
src/core/xtests/rhc_torture/rhc_torture.c | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/src/core/xtests/rhc_torture/rhc_torture.c b/src/core/xtests/rhc_torture/rhc_torture.c
index 132fbc643b..b08fa4f19c 100644
--- a/src/core/xtests/rhc_torture/rhc_torture.c
+++ b/src/core/xtests/rhc_torture/rhc_torture.c
@@ -894,6 +894,7 @@ int main (int argc, char **argv)
uint32_t states_seen[2 * 2 * 3][2] = {{ 0 }};
unsigned seed = 0;
bool print = false;
+ int xchecks = 1;
int first = 0, count = 10000;
ddsrt_mutex_init (&wait_gc_cycle_lock);
@@ -909,10 +910,21 @@ int main (int argc, char **argv)
count = atoi (argv[3]);
if (argc > 4)
print = (atoi (argv[4]) != 0);
+ if (argc > 5)
+ xchecks = atoi (argv[4]);
- printf ("prng seed %u first %d count %d print %d\n", seed, first, count, print);
+ printf ("prng seed %u first %d count %d print %d xchecks %d\n", seed, first, count, print, xchecks);
ddsrt_prng_init_simple (&prng, seed);
+ if (xchecks != 0)
+ {
+ struct ddsi_domaingv *gv = get_gv (pp);
+ if (xchecks > 0)
+ gv->config.enabled_xchecks = ~0u;
+ else
+ gv->config.enabled_xchecks = 0u;
+ }
+
memset (rres_mseq, 0, sizeof (rres_mseq));
for (size_t i = 0; i < sizeof (rres_iseq) / sizeof(rres_iseq[0]); i++)
rres_ptrs[i] = &rres_mseq[i];
From 71b7abdf35c9c9b0979f62f4e4373a14dcc6f57b Mon Sep 17 00:00:00 2001
From: Erik Boasson
Date: Wed, 29 Apr 2020 19:50:52 +0200
Subject: [PATCH 09/36] No auto-dispose in deadline test
Use of an auto-dispose writer meant the NO_WRITERS case did not actually
get tested. The behaviour of the implementation was to generate
deadline missed notifications for such instances, but the test expected
otherwise.
There is a disagreement between different DDS implementations on the
desirability of generating deadline missed notifications for NOT_ALIVE
instances. Deadline notifications on DISPOSED instances seems silly, as
it means end-of-life. Deadline notifications on a NO_WRITERS instance
are certainly valuable for applications that don't pay attention to the
number of writers (otherwise one has to monitor both liveliness changed
and deadline missed notifications to be be sure to get some
notification).
Different usage patterns definitely affect what is desirable and I doubt
one-size-fits-all is the right approach. This commit changes the test
and retains the behaviour, and if it errs, it at least errs on the side
of caution.
Signed-off-by: Erik Boasson
---
src/core/ddsc/tests/deadline.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/src/core/ddsc/tests/deadline.c b/src/core/ddsc/tests/deadline.c
index e10bd74409..c97debba91 100644
--- a/src/core/ddsc/tests/deadline.c
+++ b/src/core/ddsc/tests/deadline.c
@@ -125,6 +125,7 @@ static void deadline_init(void)
dds_qset_history(g_qos, DDS_HISTORY_KEEP_ALL, DDS_LENGTH_UNLIMITED);
dds_qset_durability(g_qos, DDS_DURABILITY_TRANSIENT_LOCAL);
dds_qset_reliability(g_qos, DDS_RELIABILITY_RELIABLE, DDS_INFINITY);
+ dds_qset_writer_data_lifecycle(g_qos, false);
}
static void deadline_fini(void)
@@ -422,7 +423,10 @@ CU_Theory((int32_t n_inst, uint8_t unreg_nth, uint8_t dispose_nth), ddsc_deadlin
n_dispose++;
}
}
- n_alive = n_inst - n_dispose - n_unreg;
+ /* FIXME: should unregistered instances cause deadline expirations? I do think so
+ and that is what it actually implemented
+ if they shouldn't: n_alive = n_inst - n_dispose - n_unreg */
+ n_alive = n_inst - n_dispose;
/* Sleep deadline_dur + 50% and check missed deadline count */
sleepfor(3 * deadline_dur / 2);
From 6809c6a5ee2e420d235114d3f98f4a522dc2a202 Mon Sep 17 00:00:00 2001
From: Erik Boasson
Date: Wed, 29 Apr 2020 20:00:51 +0200
Subject: [PATCH 10/36] Always add invalid sample if empty or latest read
Disposing an instance would only add an invalid sample if the instance
is empty, but it should also do so when the latest sample is read.
Otherwise reading all NOT_READ samples gives nothing or nonsensical
output.
Signed-off-by: Erik Boasson
---
src/core/ddsc/src/dds_rhc_default.c | 2 +-
src/core/ddsc/tests/reader_iterator.c | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/core/ddsc/src/dds_rhc_default.c b/src/core/ddsc/src/dds_rhc_default.c
index 09989e04a9..0a373e74bc 100644
--- a/src/core/ddsc/src/dds_rhc_default.c
+++ b/src/core/ddsc/src/dds_rhc_default.c
@@ -1648,7 +1648,7 @@ static bool dds_rhc_default_store (struct ddsi_rhc * __restrict rhc_common, cons
}
/* If instance became disposed, add an invalid sample if there are no samples left */
- if (inst_became_disposed && inst->latest == NULL)
+ if (inst_became_disposed && (inst->latest == NULL || inst->latest->isread))
inst_set_invsample (rhc, inst, &trig_qc, ¬ify_data_available);
update_inst (inst, wrinfo, true, sample->timestamp);
diff --git a/src/core/ddsc/tests/reader_iterator.c b/src/core/ddsc/tests/reader_iterator.c
index df1e4c1e5a..945564c112 100644
--- a/src/core/ddsc/tests/reader_iterator.c
+++ b/src/core/ddsc/tests/reader_iterator.c
@@ -55,7 +55,7 @@
#define MAX_SAMPLES 21
#define RDR_NOT_READ_CNT 11
-#define RDR_INV_READ_CNT 1
+#define RDR_INV_READ_CNT 2
int rdr_expected_long_2[RDR_NOT_READ_CNT] = { 0, 1, 2, 6, 7, 9, 11, 13, 14, 16, 19 };
/* Because we only read one sample at a time, only the first sample of an instance
From eca9b69f18d35a1a090226b68678fbc3e93df559 Mon Sep 17 00:00:00 2001
From: Erik Boasson
Date: Wed, 29 Apr 2020 20:08:48 +0200
Subject: [PATCH 11/36] Refactor storing and unregistering writers in RHC
Signed-off-by: Erik Boasson
---
src/core/ddsc/src/dds_rhc_default.c | 245 +++++++++++-----------------
1 file changed, 94 insertions(+), 151 deletions(-)
diff --git a/src/core/ddsc/src/dds_rhc_default.c b/src/core/ddsc/src/dds_rhc_default.c
index 0a373e74bc..9195cd9a3b 100644
--- a/src/core/ddsc/src/dds_rhc_default.c
+++ b/src/core/ddsc/src/dds_rhc_default.c
@@ -695,7 +695,7 @@ static void inst_clear_invsample_if_exists (struct dds_rhc_default *rhc, struct
inst_clear_invsample (rhc, inst, trig_qc);
}
-static void inst_set_invsample (struct dds_rhc_default *rhc, struct rhc_instance *inst, struct trigger_info_qcond *trig_qc, bool *nda)
+static void inst_set_invsample (struct dds_rhc_default *rhc, struct rhc_instance *inst, struct trigger_info_qcond *trig_qc, bool * __restrict nda)
{
if (inst->inv_exists && !inst->inv_isread)
{
@@ -842,7 +842,7 @@ static bool trigger_info_differs (const struct dds_rhc_default *rhc, const struc
trig_qc->dec_sample_read != trig_qc->inc_sample_read);
}
-static bool add_sample (struct dds_rhc_default *rhc, struct rhc_instance *inst, const struct ddsi_writer_info *wrinfo, const struct ddsi_serdata *sample, status_cb_data_t *cb_data, struct trigger_info_qcond *trig_qc)
+static bool add_sample (struct dds_rhc_default *rhc, struct rhc_instance *inst, const struct ddsi_writer_info *wrinfo, const struct ddsi_serdata *sample, status_cb_data_t *cb_data, struct trigger_info_qcond *trig_qc, bool * __restrict nda)
{
struct rhc_sample *s;
@@ -936,6 +936,7 @@ static bool add_sample (struct dds_rhc_default *rhc, struct rhc_instance *inst,
trig_qc->inc_conds_sample = s->conds;
inst->latest = s;
+ *nda = true;
return true;
}
@@ -1033,7 +1034,7 @@ static void drop_instance_noupdate_no_writers (struct dds_rhc_default *__restric
*instptr = NULL;
}
-static void dds_rhc_register (struct dds_rhc_default *rhc, struct rhc_instance *inst, uint64_t wr_iid, bool autodispose, bool iid_update)
+static void dds_rhc_register (struct dds_rhc_default *rhc, struct rhc_instance *inst, uint64_t wr_iid, bool autodispose, bool iid_update, bool * __restrict nda)
{
const uint64_t inst_wr_iid = inst->wr_iid_islive ? inst->wr_iid : 0;
@@ -1054,10 +1055,8 @@ static void dds_rhc_register (struct dds_rhc_default *rhc, struct rhc_instance *
else. */
TRACE ("cached");
assert (inst->wrcount > 0);
- return;
}
-
- if (inst->wrcount == 0)
+ else if (inst->wrcount == 0)
{
/* Currently no writers at all */
assert (!inst->wr_iid_islive);
@@ -1075,6 +1074,7 @@ static void dds_rhc_register (struct dds_rhc_default *rhc, struct rhc_instance *
if (!inst_is_empty (inst) && !inst->isdisposed)
rhc->n_not_alive_no_writers--;
+ *nda = true;
}
else if (inst_wr_iid == 0 && inst->wrcount == 1)
{
@@ -1136,6 +1136,7 @@ static void dds_rhc_register (struct dds_rhc_default *rhc, struct rhc_instance *
inst->wrcount++;
if (autodispose)
inst->autodispose = 1;
+ *nda = true;
}
else
{
@@ -1181,7 +1182,7 @@ static void account_for_nonempty_to_empty_transition (struct dds_rhc_default *__
}
}
-static int rhc_unregister_isreg_w_sideeffects (struct dds_rhc_default *rhc, const struct rhc_instance *inst, uint64_t wr_iid)
+static int rhc_unregister_delete_registration (struct dds_rhc_default *rhc, const struct rhc_instance *inst, uint64_t wr_iid)
{
/* Returns 1 if last registration just disappeared */
if (inst->wrcount == 0)
@@ -1225,9 +1226,8 @@ static int rhc_unregister_isreg_w_sideeffects (struct dds_rhc_default *rhc, cons
}
}
-static int rhc_unregister_updateinst (struct dds_rhc_default *rhc, struct rhc_instance **instptr, const struct ddsi_writer_info * __restrict wrinfo, ddsrt_wctime_t tstamp, struct trigger_info_qcond *trig_qc, bool *nda)
+static int rhc_unregister_updateinst (struct dds_rhc_default *rhc, struct rhc_instance *inst, const struct ddsi_writer_info * __restrict wrinfo, ddsrt_wctime_t tstamp, struct trigger_info_qcond *trig_qc, bool * __restrict nda)
{
- struct rhc_instance * const inst = *instptr;
assert (inst->wrcount > 0);
if (wrinfo->auto_dispose)
inst->autodispose = 1;
@@ -1273,6 +1273,7 @@ static int rhc_unregister_updateinst (struct dds_rhc_default *rhc, struct rhc_in
inst->isdisposed = 1;
rhc->n_not_alive_disposed++;
}
+ *nda = true;
}
inst->wr_iid_islive = 0;
return 0;
@@ -1297,34 +1298,27 @@ static int rhc_unregister_updateinst (struct dds_rhc_default *rhc, struct rhc_in
}
account_for_empty_to_nonempty_transition (rhc, inst);
inst->wr_iid_islive = 0;
+ *nda = true;
return 0;
}
}
}
-static bool dds_rhc_unregister (struct dds_rhc_default *rhc, struct rhc_instance **instptr, const struct ddsi_writer_info * __restrict wrinfo, ddsrt_wctime_t tstamp, struct trigger_info_post *post, struct trigger_info_qcond *trig_qc)
+static void dds_rhc_unregister (struct dds_rhc_default *rhc, struct rhc_instance *inst, const struct ddsi_writer_info * __restrict wrinfo, ddsrt_wctime_t tstamp, struct trigger_info_post *post, struct trigger_info_qcond *trig_qc, bool * __restrict nda)
{
- struct rhc_instance * const inst = *instptr;
- bool notify_data_available = false;
-
- /* 'post' always gets set; instance may have been freed upon return. */
+ /* 'post' always gets set */
TRACE (" unregister:");
- if (!rhc_unregister_isreg_w_sideeffects (rhc, inst, wrinfo->iid))
- {
+ if (!rhc_unregister_delete_registration (rhc, inst, wrinfo->iid)) {
/* other registrations remain */
get_trigger_info_cmn (&post->c, inst);
- }
- else if (rhc_unregister_updateinst (rhc, instptr, wrinfo, tstamp, trig_qc, ¬ify_data_available))
- {
+ } else if (rhc_unregister_updateinst (rhc, inst, wrinfo, tstamp, trig_qc, nda)) {
/* instance dropped */
init_trigger_info_cmn_nonmatch (&post->c);
- }
- else
- {
+ } else {
/* no writers remain, but instance not empty */
get_trigger_info_cmn (&post->c, inst);
}
- return notify_data_available;
+ TRACE (" nda=%d\n", *nda);
}
static struct rhc_instance *alloc_new_instance (struct dds_rhc_default *rhc, const struct ddsi_writer_info *wrinfo, struct ddsi_serdata *serdata, struct ddsi_tkmap_instance *tk)
@@ -1336,7 +1330,7 @@ static struct rhc_instance *alloc_new_instance (struct dds_rhc_default *rhc, con
memset (inst, 0, sizeof (*inst));
inst->iid = tk->m_iid;
inst->tk = tk;
- inst->wrcount = (serdata->statusinfo & NN_STATUSINFO_UNREGISTER) ? 0 : 1;
+ inst->wrcount = 1;
inst->isdisposed = (serdata->statusinfo & NN_STATUSINFO_DISPOSE) != 0;
inst->autodispose = wrinfo->auto_dispose;
inst->deadline_reg = 0;
@@ -1362,7 +1356,7 @@ static struct rhc_instance *alloc_new_instance (struct dds_rhc_default *rhc, con
return inst;
}
-static rhc_store_result_t rhc_store_new_instance (struct rhc_instance **out_inst, struct dds_rhc_default *rhc, const struct ddsi_writer_info *wrinfo, struct ddsi_serdata *sample, struct ddsi_tkmap_instance *tk, const bool has_data, status_cb_data_t *cb_data, struct trigger_info_post *post, struct trigger_info_qcond *trig_qc)
+static rhc_store_result_t rhc_store_new_instance (struct rhc_instance **out_inst, struct dds_rhc_default *rhc, const struct ddsi_writer_info *wrinfo, struct ddsi_serdata *sample, struct ddsi_tkmap_instance *tk, const bool has_data, status_cb_data_t *cb_data, struct trigger_info_qcond *trig_qc, bool * __restrict nda)
{
struct rhc_instance *inst;
int ret;
@@ -1400,7 +1394,7 @@ static rhc_store_result_t rhc_store_new_instance (struct rhc_instance **out_inst
inst = alloc_new_instance (rhc, wrinfo, sample, tk);
if (has_data)
{
- if (!add_sample (rhc, inst, wrinfo, sample, cb_data, trig_qc))
+ if (!add_sample (rhc, inst, wrinfo, sample, cb_data, trig_qc, nda))
{
free_empty_instance (inst, rhc);
return RHC_REJECTED;
@@ -1408,10 +1402,8 @@ static rhc_store_result_t rhc_store_new_instance (struct rhc_instance **out_inst
}
else
{
- if (inst->isdisposed) {
- bool nda_dummy = false;
- inst_set_invsample (rhc, inst, trig_qc, &nda_dummy);
- }
+ if (inst->isdisposed)
+ inst_set_invsample (rhc, inst, trig_qc, nda);
}
account_for_empty_to_nonempty_transition (rhc, inst);
@@ -1420,7 +1412,6 @@ static rhc_store_result_t rhc_store_new_instance (struct rhc_instance **out_inst
(void) ret;
rhc->n_instances++;
rhc->n_new++;
- get_trigger_info_cmn (&post->c, inst);
*out_inst = inst;
return RHC_STORED;
@@ -1464,6 +1455,43 @@ static void postprocess_instance_update (struct dds_rhc_default * __restrict rhc
assert (rhc_check_counts_locked (rhc, true, true));
}
+static void update_viewstate_and_disposedness (struct dds_rhc_default * __restrict rhc, struct rhc_instance * __restrict inst, bool has_data, bool not_alive, bool is_dispose, bool * __restrict nda)
+{
+ /* Sample arriving for a NOT_ALIVE instance => view state NEW */
+ if (has_data && not_alive)
+ {
+ TRACE (" notalive->alive");
+ inst->isnew = 1;
+ *nda = true;
+ }
+
+ /* Desired effect on instance state and disposed_gen:
+ op DISPOSED NOT_DISPOSED
+ W ND;gen++ ND
+ D D D
+ WD D;gen++ D
+ Simplest way is to toggle istate when it is currently DISPOSED
+ and the operation is WD. */
+ if (has_data && inst->isdisposed)
+ {
+ TRACE (" disposed->notdisposed");
+ inst->disposed_gen++;
+ if (!is_dispose)
+ inst->isdisposed = 0;
+ *nda = true;
+ }
+ if (is_dispose)
+ {
+ bool wasdisposed = inst->isdisposed;
+ if (!inst->isdisposed)
+ {
+ inst->isdisposed = 1;
+ *nda = true;
+ }
+ TRACE (" dispose(%d)", !wasdisposed);
+ }
+}
+
/*
dds_rhc_store: DDSI up call into read cache to store new sample. Returns whether sample
delivered (true unless a reliable sample rejected).
@@ -1521,14 +1549,12 @@ static bool dds_rhc_default_store (struct ddsi_rhc * __restrict rhc_common, cons
}
else
{
- TRACE (" new instance");
- stored = rhc_store_new_instance (&inst, rhc, wrinfo, sample, tk, has_data, &cb_data, &post, &trig_qc);
+ TRACE (" new instance\n");
+ stored = rhc_store_new_instance (&inst, rhc, wrinfo, sample, tk, has_data, &cb_data, &trig_qc, ¬ify_data_available);
if (stored != RHC_STORED)
- {
goto error_or_nochange;
- }
+
init_trigger_info_cmn_nonmatch (&pre.c);
- notify_data_available = true;
}
}
else if (!inst_accepts_sample (rhc, inst, wrinfo, sample, has_data))
@@ -1542,25 +1568,13 @@ static bool dds_rhc_default_store (struct ddsi_rhc * __restrict rhc_common, cons
get_trigger_info_pre (&pre, inst);
if (has_data || is_dispose)
- {
- dds_rhc_register (rhc, inst, wr_iid, wrinfo->auto_dispose, false);
- }
- if (statusinfo & NN_STATUSINFO_UNREGISTER)
- {
- if (dds_rhc_unregister (rhc, &inst, wrinfo, sample->timestamp, &post, &trig_qc))
- notify_data_available = true;
- }
- else
- {
- get_trigger_info_cmn (&post.c, inst);
- }
- /* notify sample lost */
+ dds_rhc_register (rhc, inst, wr_iid, wrinfo->auto_dispose, false, ¬ify_data_available);
+ /* notify sample lost */
cb_data.raw_status_id = (int) DDS_SAMPLE_LOST_STATUS_ID;
cb_data.extra = 0;
cb_data.handle = 0;
cb_data.add = true;
- goto error_or_nochange;
}
else
{
@@ -1579,7 +1593,6 @@ static bool dds_rhc_default_store (struct ddsi_rhc * __restrict rhc_common, cons
const bool old_isdisposed = inst->isdisposed;
const bool old_isnew = inst->isnew;
const bool was_empty = inst_is_empty (inst);
- int inst_became_disposed = 0;
/* Not just an unregister, so a write and/or a dispose (possibly
combined with an unregister). Write & dispose create a
@@ -1589,66 +1602,29 @@ static bool dds_rhc_default_store (struct ddsi_rhc * __restrict rhc_common, cons
(i.e., out-of-memory), abort the operation and hope that the
caller can still notify the application. */
- dds_rhc_register (rhc, inst, wr_iid, wrinfo->auto_dispose, true);
-
- /* Sample arriving for a NOT_ALIVE instance => view state NEW */
- if (has_data && not_alive)
- {
- TRACE (" notalive->alive");
- inst->isnew = 1;
- }
+ dds_rhc_register (rhc, inst, wr_iid, wrinfo->auto_dispose, true, ¬ify_data_available);
+ update_viewstate_and_disposedness (rhc, inst, has_data, not_alive, is_dispose, ¬ify_data_available);
- /* Desired effect on instance state and disposed_gen:
- op DISPOSED NOT_DISPOSED
- W ND;gen++ ND
- D D D
- WD D;gen++ D
- Simplest way is to toggle istate when it is currently DISPOSED
- and the operation is WD. */
- if (has_data && inst->isdisposed)
- {
- TRACE (" disposed->notdisposed");
- inst->isdisposed = 0;
- inst->disposed_gen++;
- }
- if (is_dispose)
- {
- inst->isdisposed = 1;
- inst_became_disposed = !old_isdisposed;
- TRACE (" dispose(%d)", inst_became_disposed);
- }
-
- /* Only need to add a sample to the history if the input actually
- is a sample. */
+ /* Only need to add a sample to the history if the input actually is a sample. */
if (has_data)
{
TRACE (" add_sample");
- if (!add_sample (rhc, inst, wrinfo, sample, &cb_data, &trig_qc))
+ if (!add_sample (rhc, inst, wrinfo, sample, &cb_data, &trig_qc, ¬ify_data_available))
{
- TRACE ("(reject)");
+ TRACE ("(reject)\n");
stored = RHC_REJECTED;
/* FIXME: fix the bad rejection handling, probably put back in a proper rollback, until then a band-aid like this will have to do: */
inst->isnew = old_isnew;
if (old_isdisposed)
- {
inst->disposed_gen--;
- if (!inst->isdisposed)
- {
- inst->isdisposed = 1;
- }
- }
- else if (inst->isdisposed)
- {
- inst->isdisposed = 0;
- }
+ inst->isdisposed = old_isdisposed;
goto error_or_nochange;
}
- notify_data_available = true;
}
/* If instance became disposed, add an invalid sample if there are no samples left */
- if (inst_became_disposed && (inst->latest == NULL || inst->latest->isread))
+ if ((bool) inst->isdisposed > old_isdisposed && (inst->latest == NULL || inst->latest->isread))
inst_set_invsample (rhc, inst, &trig_qc, ¬ify_data_available);
update_inst (inst, wrinfo, true, sample->timestamp);
@@ -1658,7 +1634,7 @@ static bool dds_rhc_default_store (struct ddsi_rhc * __restrict rhc_common, cons
guaranteed that we end up with a non-empty instance: for
example, if the instance was disposed & empty, nothing
changes. */
- if (inst->latest || inst_became_disposed)
+ if (inst->latest || (bool) inst->isdisposed > old_isdisposed)
{
if (was_empty)
account_for_empty_to_nonempty_transition (rhc, inst);
@@ -1672,29 +1648,30 @@ static bool dds_rhc_default_store (struct ddsi_rhc * __restrict rhc_common, cons
}
}
+ TRACE(" nda=%d\n", notify_data_available);
assert (rhc_check_counts_locked (rhc, false, false));
+ }
- if (statusinfo & NN_STATUSINFO_UNREGISTER)
- {
- /* Either a pure unregister, or the instance rejected the sample
- because of time stamps, content filter, or something else. If
- the writer unregisters the instance, I think we should ignore
- the acceptance filters and process it anyway.
+ if (statusinfo & NN_STATUSINFO_UNREGISTER)
+ {
+ /* Either a pure unregister, or the instance rejected the sample
+ because of time stamps, content filter, or something else. If
+ the writer unregisters the instance, I think we should ignore
+ the acceptance filters and process it anyway.
- It is a bit unclear what
+ It is a bit unclear what
- write_w_timestamp(x,1) ; unregister_w_timestamp(x,0)
+ write_w_timestamp(x,1) ; unregister_w_timestamp(x,0)
- actually means if BY_SOURCE ordering is selected: does that
- mean an application reading "x" after the write and reading it
- again after the unregister will see a change in the
- no_writers_generation field? */
- dds_rhc_unregister (rhc, &inst, wrinfo, sample->timestamp, &post, &trig_qc);
- }
- else
- {
- get_trigger_info_cmn (&post.c, inst);
- }
+ actually means if BY_SOURCE ordering is selected: does that
+ mean an application reading "x" after the write and reading it
+ again after the unregister will see a change in the
+ no_writers_generation field? */
+ dds_rhc_unregister (rhc, inst, wrinfo, sample->timestamp, &post, &trig_qc, ¬ify_data_available);
+ }
+ else
+ {
+ get_trigger_info_cmn (&post.c, inst);
}
postprocess_instance_update (rhc, &inst, &pre, &post, &trig_qc, triggers, &ntriggers);
@@ -1736,7 +1713,6 @@ static void dds_rhc_default_unregister_wr (struct ddsi_rhc * __restrict rhc_comm
struct rhc_instance *inst;
struct ddsrt_hh_iter iter;
const uint64_t wr_iid = wrinfo->iid;
- const bool auto_dispose = wrinfo->auto_dispose;
size_t ntriggers = SIZE_MAX;
ddsrt_mutex_lock (&rhc->lock);
@@ -1751,47 +1727,16 @@ static void dds_rhc_default_unregister_wr (struct ddsi_rhc * __restrict rhc_comm
struct trigger_info_qcond trig_qc;
get_trigger_info_pre (&pre, inst);
init_trigger_info_qcond (&trig_qc);
-
TRACE (" %"PRIx64":", inst->iid);
-
- assert (inst->wrcount > 0);
- if (auto_dispose && !inst->isdisposed)
- {
- notify_data_available = true;
- inst->isdisposed = 1;
-
- /* Set invalid sample for disposing it (unregister may also set it for unregistering) */
- if (inst->latest && !inst->latest->isread)
- {
- assert (!inst->inv_exists);
- rhc->n_not_alive_disposed++;
- }
- else
- {
- const bool was_empty = inst_is_empty (inst);
- inst_set_invsample (rhc, inst, &trig_qc, ¬ify_data_available);
- if (was_empty)
- account_for_empty_to_nonempty_transition (rhc, inst);
- else
- rhc->n_not_alive_disposed++;
- }
- }
-
- (void) dds_rhc_unregister (rhc, &inst, wrinfo, inst->tstamp, &post, &trig_qc);
- notify_data_available = true;
+ dds_rhc_unregister (rhc, inst, wrinfo, inst->tstamp, &post, &trig_qc, ¬ify_data_available);
postprocess_instance_update (rhc, &inst, &pre, &post, &trig_qc, NULL, &ntriggers);
TRACE ("\n");
}
}
- TRACE (")\n");
-
ddsrt_mutex_unlock (&rhc->lock);
- if (rhc->reader)
- {
- if (notify_data_available)
- dds_reader_data_available_cb (rhc->reader);
- }
+ if (rhc->reader && notify_data_available)
+ dds_reader_data_available_cb (rhc->reader);
}
static void dds_rhc_default_relinquish_ownership (struct ddsi_rhc * __restrict rhc_common, const uint64_t wr_iid)
@@ -2044,7 +1989,6 @@ static int32_t read_w_qminv_inst (struct dds_rhc_default * const __restrict rhc,
to_sample (sample->sample, values + n, 0, 0);
if (!sample->isread)
{
- TRACE ("s");
read_sample_update_conditions (rhc, &pre, &post, &trig_qc, inst, sample->conds, false);
sample->isread = true;
inst->nvread++;
@@ -2063,7 +2007,6 @@ static int32_t read_w_qminv_inst (struct dds_rhc_default * const __restrict rhc,
to_invsample (rhc->topic, inst->tk->m_sample, values + n, 0, 0);
if (!inst->inv_isread)
{
- TRACE ("i");
read_sample_update_conditions (rhc, &pre, &post, &trig_qc, inst, inst->conds, false);
inst->inv_isread = 1;
rhc->n_invread++;
From fa1eab852b8d51b9f5b89f7ca3395bceedd7e4a5 Mon Sep 17 00:00:00 2001
From: Erik Boasson
Date: Thu, 30 Apr 2020 09:20:04 +0200
Subject: [PATCH 12/36] Standard byte order when creating built-in samples
The standard defines GUIDs as an array of 16 uint8_t and so they are
presented on the network and in built-in topic samples. Internally they
are arrays of 4 uint32_t, requiring byte-order conversion.
A keyhash is also an array of 16 uint8_t, and used almost exclusively on
the network. The exception is the generation of built-in topic samples,
which relies on the "from_keyhash" function. One would expect the
keyhash here to contain the GUID in the external representation, and
this commit adds the byte-order conversions to conform to the
expectation.
Signed-off-by: Erik Boasson
---
src/core/ddsc/src/dds_builtin.c | 18 +++++++++++-------
src/core/ddsc/src/dds_serdata_builtintopic.c | 10 ++++++----
2 files changed, 17 insertions(+), 11 deletions(-)
diff --git a/src/core/ddsc/src/dds_builtin.c b/src/core/ddsc/src/dds_builtin.c
index eee50f11e4..d224810e93 100644
--- a/src/core/ddsc/src/dds_builtin.c
+++ b/src/core/ddsc/src/dds_builtin.c
@@ -15,6 +15,7 @@
#include "dds/ddsi/q_entity.h"
#include "dds/ddsi/q_thread.h"
#include "dds/ddsi/q_config.h"
+#include "dds/ddsi/q_bswap.h"
#include "dds/ddsi/ddsi_domaingv.h"
#include "dds/ddsi/ddsi_plist.h"
#include "dds__init.h"
@@ -181,10 +182,13 @@ static struct ddsi_tkmap_instance *dds__builtin_get_tkmap_entry (const struct dd
struct dds_domain *domain = vdomain;
struct ddsi_tkmap_instance *tk;
struct ddsi_serdata *sd;
- struct ddsi_keyhash kh;
- memcpy (&kh, guid, sizeof (kh));
- /* any random builtin topic will do (provided it has a GUID for a key), because what matters is the "class" of the topic, not the actual topic; also, this is called early in the initialisation of the entity with this GUID, which simply causes serdata_from_keyhash to create a key-only serdata because the key lookup fails. */
- sd = ddsi_serdata_from_keyhash (domain->builtin_participant_topic, &kh);
+ union { ddsi_guid_t guid; struct ddsi_keyhash keyhash; } x;
+ x.guid = nn_hton_guid (*guid);
+ /* any random builtin topic will do (provided it has a GUID for a key), because what matters is the "class"
+ of the topic, not the actual topic; also, this is called early in the initialisation of the entity with
+ this GUID, which simply causes serdata_from_keyhash to create a key-only serdata because the key lookup
+ fails. */
+ sd = ddsi_serdata_from_keyhash (domain->builtin_participant_topic, &x.keyhash);
tk = ddsi_tkmap_find (domain->gv.m_tkmap, sd, true);
ddsi_serdata_unref (sd);
return tk;
@@ -196,7 +200,7 @@ struct ddsi_serdata *dds__builtin_make_sample (const struct entity_common *e, dd
struct dds_domain *dom = e->gv->builtin_topic_interface->arg;
struct ddsi_sertopic *topic = NULL;
struct ddsi_serdata *serdata;
- struct ddsi_keyhash keyhash;
+ union { ddsi_guid_t guid; struct ddsi_keyhash keyhash; } x;
switch (e->kind)
{
case EK_PARTICIPANT:
@@ -213,8 +217,8 @@ struct ddsi_serdata *dds__builtin_make_sample (const struct entity_common *e, dd
break;
}
assert (topic != NULL);
- memcpy (&keyhash, &e->guid, sizeof (keyhash));
- serdata = ddsi_serdata_from_keyhash (topic, &keyhash);
+ x.guid = nn_hton_guid (e->guid);
+ serdata = ddsi_serdata_from_keyhash (topic, &x.keyhash);
serdata->timestamp = timestamp;
serdata->statusinfo = alive ? 0 : NN_STATUSINFO_DISPOSE | NN_STATUSINFO_UNREGISTER;
return serdata;
diff --git a/src/core/ddsc/src/dds_serdata_builtintopic.c b/src/core/ddsc/src/dds_serdata_builtintopic.c
index 614a56641d..9991e8bfaa 100644
--- a/src/core/ddsc/src/dds_serdata_builtintopic.c
+++ b/src/core/ddsc/src/dds_serdata_builtintopic.c
@@ -130,10 +130,12 @@ static struct ddsi_serdata *ddsi_serdata_builtin_from_keyhash (const struct ddsi
{
/* FIXME: not quite elegant to manage the creation of a serdata for a built-in topic via this function, but I also find it quite unelegant to let from_sample read straight from the underlying internal entity, and to_sample convert to the external format ... I could claim the internal entity is the "serialised form", but that forces wrapping it in a fragchain in one way or another, which, though possible, is also a bit lacking in elegance. */
const struct ddsi_sertopic_builtintopic *tp = (const struct ddsi_sertopic_builtintopic *)tpcmn;
- /* keyhash must in host format (which the GUIDs always are internally) */
- struct entity_common *entity = entidx_lookup_guid_untyped (tp->c.gv->entity_index, (const ddsi_guid_t *) keyhash->value);
- struct ddsi_serdata_builtintopic *d = serdata_builtin_new(tp, entity ? SDK_DATA : SDK_KEY);
- memcpy (&d->key, keyhash->value, sizeof (d->key));
+ union { ddsi_guid_t guid; ddsi_keyhash_t keyhash; } x;
+ x.keyhash = *keyhash;
+ x.guid = nn_ntoh_guid (x.guid);
+ struct entity_common *entity = entidx_lookup_guid_untyped (tp->c.gv->entity_index, &x.guid);
+ struct ddsi_serdata_builtintopic *d = serdata_builtin_new (tp, entity ? SDK_DATA : SDK_KEY);
+ d->key = x.guid;
if (entity)
{
ddsrt_mutex_lock (&entity->qos_lock);
From 2ad84efb146a6942f8d79811684fa2fe40f89904 Mon Sep 17 00:00:00 2001
From: Erik Boasson
Date: Thu, 30 Apr 2020 09:30:14 +0200
Subject: [PATCH 13/36] Implement "from_sample" for built-in topics
One cannot create writers for built-in topics, therefore one generally
does not create samples for them. However, one can lookup an instance
handle from a sample with just a key value in it, and so the function is
needed.
Signed-off-by: Erik Boasson
---
src/core/ddsc/src/dds_serdata_builtintopic.c | 37 +++++++++++++++++++-
1 file changed, 36 insertions(+), 1 deletion(-)
diff --git a/src/core/ddsc/src/dds_serdata_builtintopic.c b/src/core/ddsc/src/dds_serdata_builtintopic.c
index 9991e8bfaa..cc0728513e 100644
--- a/src/core/ddsc/src/dds_serdata_builtintopic.c
+++ b/src/core/ddsc/src/dds_serdata_builtintopic.c
@@ -171,6 +171,41 @@ static struct ddsi_serdata *ddsi_serdata_builtin_from_keyhash (const struct ddsi
return fix_serdata_builtin(d, tp->c.serdata_basehash);
}
+static struct ddsi_serdata *ddsi_serdata_builtin_from_sample (const struct ddsi_sertopic *tpcmn, enum ddsi_serdata_kind kind, const void *sample)
+{
+ const struct ddsi_sertopic_builtintopic *tp = (const struct ddsi_sertopic_builtintopic *)tpcmn;
+ union {
+ dds_guid_t extguid;
+ ddsi_keyhash_t keyhash;
+ } x;
+
+ /* no-one should be trying to convert user-provided data into a built-in topic sample, but converting
+ a key is something that can be necessary, e.g., dds_lookup_instance depends on it */
+ if (kind != SDK_KEY)
+ return NULL;
+
+ /* memset x (even though it is entirely superfluous) so we can leave out a default case from the
+ switch (ensuring at least some compilers will warn when more types are added) without getting
+ warnings from any compiler */
+ memset (&x, 0, sizeof (x));
+ switch (tp->type)
+ {
+ case DSBT_PARTICIPANT: {
+ const dds_builtintopic_participant_t *s = sample;
+ x.extguid = s->key;
+ break;
+ }
+ case DSBT_READER:
+ case DSBT_WRITER: {
+ const dds_builtintopic_endpoint_t *s = sample;
+ x.extguid = s->key;
+ break;
+ }
+ }
+
+ return ddsi_serdata_from_keyhash (tpcmn, &x.keyhash);
+}
+
static struct ddsi_serdata *serdata_builtin_to_topicless (const struct ddsi_serdata *serdata_common)
{
/* All built-in ones are currently topicless */
@@ -291,7 +326,7 @@ const struct ddsi_serdata_ops ddsi_serdata_ops_builtintopic = {
.from_ser = 0,
.from_ser_iov = 0,
.from_keyhash = ddsi_serdata_builtin_from_keyhash,
- .from_sample = 0,
+ .from_sample = ddsi_serdata_builtin_from_sample,
.to_ser = serdata_builtin_to_ser,
.to_sample = serdata_builtin_to_sample,
.to_ser_ref = serdata_builtin_to_ser_ref,
From f03f6961b884c5020d36dfb92ec2e13f76a730d4 Mon Sep 17 00:00:00 2001
From: Erik Boasson
Date: Wed, 29 Apr 2020 20:10:14 +0200
Subject: [PATCH 14/36] Rework listener tests
A few failures to signal DATA_AVAILABLE (as well as some where it was
signalled unnecessarily) were discovered while refactoring the RHC
despite the tests all passing. Clearly the tests were inadequate.
The enormous amount of boilerplate in the tests prompted a small rewrite
to a programmable listener invocation tester that one simply feeds a
noise-like one-liner in a string. This trades the boilerplate for
somewhat inscrutable code.
Signed-off-by: Erik Boasson
---
src/core/ddsc/tests/listener.c | 2259 ++++++++++++++++----------------
1 file changed, 1127 insertions(+), 1132 deletions(-)
diff --git a/src/core/ddsc/tests/listener.c b/src/core/ddsc/tests/listener.c
index 0ab0108070..6738cf76e4 100644
--- a/src/core/ddsc/tests/listener.c
+++ b/src/core/ddsc/tests/listener.c
@@ -1,5 +1,5 @@
/*
- * Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
+ * Copyright(c) 2006 to 2020 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
@@ -9,1254 +9,1249 @@
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
-#include "dds/dds.h"
+#include
-#include "dds/ddsrt/cdtors.h"
+#include "dds/dds.h"
#include "dds/ddsrt/misc.h"
-#include "dds/ddsrt/process.h"
#include "dds/ddsrt/sync.h"
-#include "dds/ddsrt/threads.h"
-
+#include "dds/ddsrt/heap.h"
+#include "dds/ddsrt/string.h"
+#include "dds/ddsrt/environ.h"
#include "test_common.h"
-/****************************************************************************
- * TODO: Add DDS_INCONSISTENT_TOPIC_STATUS test
- * TODO: Add DDS_OFFERED/REQUESTED_DEADLINE_MISSED_STATUS test
- * TODO: Add DDS_LIVELINESS_LOST_STATUS test
- * TODO: Check DDS_REQUESTED_INCOMPATIBLE_QOS_STATUS intermittent fail (total_count != 1)
- ****************************************************************************/
-
-/****************************************************************************
- * Convenience test macros.
- ****************************************************************************/
-#define ASSERT_CALLBACK_EQUAL(fntype, listener, expected) \
- do { \
- dds_on_##fntype##_fn cb; \
- dds_lget_##fntype(listener, &cb); \
- CU_ASSERT_EQUAL(cb, expected); \
- } while (0)
-
-#define STR(fntype) #fntype##_cb
-
-#define TEST_GET_SET(listener, fntype, cb) \
- do { \
- dds_on_##fntype##_fn dummy = NULL; \
- /* Initially expect DDS_LUNSET on a newly created listener */ \
- ASSERT_CALLBACK_EQUAL(fntype, listener, DDS_LUNSET); \
- /* Using listener or callback NULL, shouldn't crash and be noop */ \
- dds_lset_##fntype(NULL, NULL); \
- dds_lget_##fntype(NULL, NULL); \
- dds_lget_##fntype(listener, NULL); \
- dds_lget_##fntype(NULL, &dummy); \
- CU_ASSERT_EQUAL_FATAL(dummy, NULL); \
- /* Set to NULL, get to confirm it succeeds */ \
- dds_lset_##fntype(listener, NULL); \
- ASSERT_CALLBACK_EQUAL(fntype, listener, NULL); \
- /* Set to a proper cb method, get to confirm it succeeds */ \
- dds_lset_##fntype(listener, cb); \
- ASSERT_CALLBACK_EQUAL(fntype, listener, cb); \
- } while (0)
-
-
-
-/****************************************************************************
- * Test globals.
- ****************************************************************************/
-static dds_entity_t g_participant = 0;
-static dds_entity_t g_subscriber = 0;
-static dds_entity_t g_publisher = 0;
-static dds_entity_t g_topic = 0;
-static dds_entity_t g_writer = 0;
-static dds_entity_t g_reader = 0;
-
-static dds_listener_t *g_listener = NULL;
-static dds_qos_t *g_qos = NULL;
-static ddsrt_mutex_t g_mutex;
-static ddsrt_cond_t g_cond;
-
-
-
-/****************************************************************************
- * Callback stuff.
- ****************************************************************************/
-static uint32_t cb_called = 0;
-static dds_entity_t cb_topic = 0;
-static dds_entity_t cb_writer = 0;
-static dds_entity_t cb_reader = 0;
-static dds_entity_t cb_subscriber = 0;
-
-static dds_inconsistent_topic_status_t cb_inconsistent_topic_status;
-static dds_liveliness_lost_status_t cb_liveliness_lost_status;
-static dds_offered_deadline_missed_status_t cb_offered_deadline_missed_status;
-static dds_offered_incompatible_qos_status_t cb_offered_incompatible_qos_status;
-static dds_sample_lost_status_t cb_sample_lost_status;
-static dds_sample_rejected_status_t cb_sample_rejected_status;
-static dds_liveliness_changed_status_t cb_liveliness_changed_status;
-static dds_requested_deadline_missed_status_t cb_requested_deadline_missed_status;
-static dds_requested_incompatible_qos_status_t cb_requested_incompatible_qos_status;
-static dds_publication_matched_status_t cb_publication_matched_status;
-static dds_subscription_matched_status_t cb_subscription_matched_status;
-
-
-static void
-inconsistent_topic_cb(
- dds_entity_t topic,
- const dds_inconsistent_topic_status_t status, void* arg)
+static ddsrt_mutex_t g_mutex;
+static ddsrt_cond_t g_cond;
+static uint32_t cb_called;
+static dds_entity_t cb_topic, cb_writer, cb_reader, cb_subscriber;
+
+#define DEFINE_STATUS_CALLBACK(name, NAME, kind) \
+ static dds_##name##_status_t cb_##name##_status; \
+ static void name##_cb (dds_entity_t kind, const dds_##name##_status_t status, void *arg) \
+ { \
+ (void) arg; \
+ ddsrt_mutex_lock (&g_mutex); \
+ cb_##kind = kind; \
+ cb_##name##_status = status; \
+ cb_called |= DDS_##NAME##_STATUS; \
+ ddsrt_cond_broadcast (&g_cond); \
+ ddsrt_mutex_unlock (&g_mutex); \
+ }
+
+DEFINE_STATUS_CALLBACK (inconsistent_topic, INCONSISTENT_TOPIC, topic)
+DEFINE_STATUS_CALLBACK (liveliness_changed, LIVELINESS_CHANGED, reader)
+DEFINE_STATUS_CALLBACK (liveliness_lost, LIVELINESS_LOST, writer)
+DEFINE_STATUS_CALLBACK (offered_deadline_missed, OFFERED_DEADLINE_MISSED, writer)
+DEFINE_STATUS_CALLBACK (offered_incompatible_qos, OFFERED_INCOMPATIBLE_QOS, writer)
+DEFINE_STATUS_CALLBACK (publication_matched, PUBLICATION_MATCHED, writer)
+DEFINE_STATUS_CALLBACK (requested_deadline_missed, REQUESTED_DEADLINE_MISSED, reader)
+DEFINE_STATUS_CALLBACK (requested_incompatible_qos, REQUESTED_INCOMPATIBLE_QOS, reader)
+DEFINE_STATUS_CALLBACK (sample_lost, SAMPLE_LOST, reader)
+DEFINE_STATUS_CALLBACK (sample_rejected, SAMPLE_REJECTED, reader)
+DEFINE_STATUS_CALLBACK (subscription_matched, SUBSCRIPTION_MATCHED, reader)
+
+static void data_on_readers_cb (dds_entity_t subscriber, void *arg)
{
- (void)arg;
- ddsrt_mutex_lock(&g_mutex);
- cb_topic = topic;
- cb_inconsistent_topic_status = status;
- cb_called |= DDS_INCONSISTENT_TOPIC_STATUS;
- ddsrt_cond_broadcast(&g_cond);
- ddsrt_mutex_unlock(&g_mutex);
+ (void) arg;
+ ddsrt_mutex_lock (&g_mutex);
+ cb_subscriber = subscriber;
+ cb_called |= DDS_DATA_ON_READERS_STATUS;
+ ddsrt_cond_broadcast (&g_cond);
+ ddsrt_mutex_unlock (&g_mutex);
}
-static void
-liveliness_lost_cb(
- dds_entity_t writer,
- const dds_liveliness_lost_status_t status,
- void* arg)
+static void data_available_cb (dds_entity_t reader, void *arg)
{
- (void)arg;
- ddsrt_mutex_lock(&g_mutex);
- cb_writer = writer;
- cb_liveliness_lost_status = status;
- cb_called |= DDS_LIVELINESS_LOST_STATUS;
- ddsrt_cond_broadcast(&g_cond);
- ddsrt_mutex_unlock(&g_mutex);
+ (void)arg;
+ ddsrt_mutex_lock (&g_mutex);
+ cb_reader = reader;
+ cb_called |= DDS_DATA_AVAILABLE_STATUS;
+ ddsrt_cond_broadcast (&g_cond);
+ ddsrt_mutex_unlock (&g_mutex);
}
-static void
-offered_deadline_missed_cb(
- dds_entity_t writer,
- const dds_offered_deadline_missed_status_t status,
- void* arg)
+static void dummy_data_on_readers_cb (dds_entity_t subscriber, void *arg)
{
- (void)arg;
- ddsrt_mutex_lock(&g_mutex);
- cb_writer = writer;
- cb_offered_deadline_missed_status = status;
- cb_called |= DDS_OFFERED_DEADLINE_MISSED_STATUS;
- ddsrt_cond_broadcast(&g_cond);
- ddsrt_mutex_unlock(&g_mutex);
+ (void)subscriber;
+ (void)arg;
}
-static void
-offered_incompatible_qos_cb(
- dds_entity_t writer,
- const dds_offered_incompatible_qos_status_t status,
- void* arg)
+static void dummy_data_available_cb (dds_entity_t reader, void *arg)
{
- (void)arg;
- ddsrt_mutex_lock(&g_mutex);
- cb_writer = writer;
- cb_offered_incompatible_qos_status = status;
- cb_called |= DDS_OFFERED_INCOMPATIBLE_QOS_STATUS;
- ddsrt_cond_broadcast(&g_cond);
- ddsrt_mutex_unlock(&g_mutex);
+ (void)reader;
+ (void)arg;
}
-static void
-data_on_readers_cb(
- dds_entity_t subscriber,
- void* arg)
+static void dummy_subscription_matched_cb (dds_entity_t reader, const dds_subscription_matched_status_t status, void *arg)
{
- (void)arg;
- ddsrt_mutex_lock(&g_mutex);
- cb_subscriber = subscriber;
- cb_called |= DDS_DATA_ON_READERS_STATUS;
- ddsrt_cond_broadcast(&g_cond);
- ddsrt_mutex_unlock(&g_mutex);
+ (void)reader;
+ (void)status;
+ (void)arg;
}
-static void
-sample_lost_cb(
- dds_entity_t reader,
- const dds_sample_lost_status_t status,
- void* arg)
+static void dummy_liveliness_changed_cb (dds_entity_t reader, const dds_liveliness_changed_status_t status, void *arg)
{
- (void)arg;
- ddsrt_mutex_lock(&g_mutex);
- cb_reader = reader;
- cb_sample_lost_status = status;
- cb_called |= DDS_SAMPLE_LOST_STATUS;
- ddsrt_cond_broadcast(&g_cond);
- ddsrt_mutex_unlock(&g_mutex);
+ (void)reader;
+ (void)status;
+ (void)arg;
}
-static void
-data_available_cb(
- dds_entity_t reader,
- void* arg)
+static void dummy_cb (void)
{
- (void)arg;
- ddsrt_mutex_lock(&g_mutex);
- cb_reader = reader;
- cb_called |= DDS_DATA_AVAILABLE_STATUS;
- ddsrt_cond_broadcast(&g_cond);
- ddsrt_mutex_unlock(&g_mutex);
+ // Used as a listener function in checking merging of listeners,
+ // and for that purpose, casting it to whatever function type is
+ // required is ok. It is not supposed to ever be called.
+ abort ();
}
-static void
-sample_rejected_cb(
- dds_entity_t reader,
- const dds_sample_rejected_status_t status,
- void* arg)
-{
- (void)arg;
- ddsrt_mutex_lock(&g_mutex);
- cb_reader = reader;
- cb_sample_rejected_status = status;
- cb_called |= DDS_SAMPLE_REJECTED_STATUS;
- ddsrt_cond_broadcast(&g_cond);
- ddsrt_mutex_unlock(&g_mutex);
-}
+#undef DEFINE_STATUS_CALLBACK
-static void
-liveliness_changed_cb(
- dds_entity_t reader,
- const dds_liveliness_changed_status_t status,
- void* arg)
-{
- (void)arg;
- ddsrt_mutex_lock(&g_mutex);
- cb_reader = reader;
- cb_liveliness_changed_status = status;
- cb_called |= DDS_LIVELINESS_CHANGED_STATUS;
- ddsrt_cond_broadcast(&g_cond);
- ddsrt_mutex_unlock(&g_mutex);
-}
+/**************************************************
+ **** ****
+ **** create/delete/get/set/copy/merge/reset ****
+ **** ****
+ **************************************************/
-static void
-requested_deadline_missed_cb(
- dds_entity_t reader,
- const dds_requested_deadline_missed_status_t status,
- void* arg)
+static void set_all_const (dds_listener_t *l, void (*c) (void))
{
- (void)arg;
- ddsrt_mutex_lock(&g_mutex);
- cb_reader = reader;
- cb_requested_deadline_missed_status = status;
- cb_called |= DDS_REQUESTED_DEADLINE_MISSED_STATUS;
- ddsrt_cond_broadcast(&g_cond);
- ddsrt_mutex_unlock(&g_mutex);
+ dds_lset_data_available (l, (dds_on_data_available_fn) c);
+ dds_lset_data_on_readers (l, (dds_on_data_on_readers_fn) c);
+ dds_lset_inconsistent_topic (l, (dds_on_inconsistent_topic_fn) c);
+ dds_lset_liveliness_changed (l, (dds_on_liveliness_changed_fn) c);
+ dds_lset_liveliness_lost (l, (dds_on_liveliness_lost_fn) c);
+ dds_lset_offered_deadline_missed (l, (dds_on_offered_deadline_missed_fn) c);
+ dds_lset_offered_incompatible_qos (l, (dds_on_offered_incompatible_qos_fn) c);
+ dds_lset_publication_matched (l, (dds_on_publication_matched_fn) c);
+ dds_lset_requested_deadline_missed (l, (dds_on_requested_deadline_missed_fn) c);
+ dds_lset_requested_incompatible_qos (l, (dds_on_requested_incompatible_qos_fn) c);
+ dds_lset_sample_lost (l, (dds_on_sample_lost_fn) c);
+ dds_lset_sample_rejected (l, (dds_on_sample_rejected_fn) c);
+ dds_lset_subscription_matched (l, (dds_on_subscription_matched_fn) c);
}
-static void
-requested_incompatible_qos_cb(
- dds_entity_t reader,
- const dds_requested_incompatible_qos_status_t status,
- void* arg)
+static void set_all (dds_listener_t *l)
{
- (void)arg;
- ddsrt_mutex_lock(&g_mutex);
- cb_reader = reader;
- cb_requested_incompatible_qos_status = status;
- cb_called |= DDS_REQUESTED_INCOMPATIBLE_QOS_STATUS;
- ddsrt_cond_broadcast(&g_cond);
- ddsrt_mutex_unlock(&g_mutex);
+ dds_lset_data_available (l, data_available_cb);
+ dds_lset_data_on_readers (l, data_on_readers_cb);
+ dds_lset_inconsistent_topic (l, inconsistent_topic_cb);
+ dds_lset_liveliness_changed (l, liveliness_changed_cb);
+ dds_lset_liveliness_lost (l, liveliness_lost_cb);
+ dds_lset_offered_deadline_missed (l, offered_deadline_missed_cb);
+ dds_lset_offered_incompatible_qos (l, offered_incompatible_qos_cb);
+ dds_lset_publication_matched (l, publication_matched_cb);
+ dds_lset_requested_deadline_missed (l, requested_deadline_missed_cb);
+ dds_lset_requested_incompatible_qos (l, requested_incompatible_qos_cb);
+ dds_lset_sample_lost (l, sample_lost_cb);
+ dds_lset_sample_rejected (l, sample_rejected_cb);
+ dds_lset_subscription_matched (l, subscription_matched_cb);
}
-static void
-publication_matched_cb(
- dds_entity_t writer,
- const dds_publication_matched_status_t status,
- void* arg)
-{
- (void)arg;
- ddsrt_mutex_lock(&g_mutex);
- cb_writer = writer;
- cb_publication_matched_status = status;
- cb_called |= DDS_PUBLICATION_MATCHED_STATUS;
- ddsrt_cond_broadcast(&g_cond);
- ddsrt_mutex_unlock(&g_mutex);
-}
+#define ASSERT_CALLBACK_EQUAL(fntype, listener, expected) \
+ do { \
+ dds_on_##fntype##_fn cb; \
+ dds_lget_##fntype(listener, &cb); \
+ CU_ASSERT_EQUAL(cb, expected); \
+ } while (0)
-static void
-subscription_matched_cb(
- dds_entity_t reader,
- const dds_subscription_matched_status_t status,
- void* arg)
+static void check_all_const (const dds_listener_t *l, void (*c) (void))
{
- (void)arg;
- ddsrt_mutex_lock(&g_mutex);
- cb_reader = reader;
- cb_subscription_matched_status = status;
- cb_called |= DDS_SUBSCRIPTION_MATCHED_STATUS;
- ddsrt_cond_broadcast(&g_cond);
- ddsrt_mutex_unlock(&g_mutex);
+ ASSERT_CALLBACK_EQUAL (data_available, l, (dds_on_data_available_fn) c);
+ ASSERT_CALLBACK_EQUAL (data_on_readers, l, (dds_on_data_on_readers_fn) c);
+ ASSERT_CALLBACK_EQUAL (inconsistent_topic, l, (dds_on_inconsistent_topic_fn) c);
+ ASSERT_CALLBACK_EQUAL (liveliness_changed, l, (dds_on_liveliness_changed_fn) c);
+ ASSERT_CALLBACK_EQUAL (liveliness_lost, l, (dds_on_liveliness_lost_fn) c);
+ ASSERT_CALLBACK_EQUAL (offered_deadline_missed, l, (dds_on_offered_deadline_missed_fn) c);
+ ASSERT_CALLBACK_EQUAL (offered_incompatible_qos, l, (dds_on_offered_incompatible_qos_fn) c);
+ ASSERT_CALLBACK_EQUAL (publication_matched, l, (dds_on_publication_matched_fn) c);
+ ASSERT_CALLBACK_EQUAL (requested_deadline_missed, l, (dds_on_requested_deadline_missed_fn) c);
+ ASSERT_CALLBACK_EQUAL (requested_incompatible_qos, l, (dds_on_requested_incompatible_qos_fn) c);
+ ASSERT_CALLBACK_EQUAL (sample_lost, l, (dds_on_sample_lost_fn) c);
+ ASSERT_CALLBACK_EQUAL (sample_rejected, l, (dds_on_sample_rejected_fn) c);
+ ASSERT_CALLBACK_EQUAL (subscription_matched, l, (dds_on_subscription_matched_fn) c);
}
-static void
-callback_dummy(void)
+static void check_all (const dds_listener_t *l)
{
+ ASSERT_CALLBACK_EQUAL (data_available, l, data_available_cb);
+ ASSERT_CALLBACK_EQUAL (data_on_readers, l, data_on_readers_cb);
+ ASSERT_CALLBACK_EQUAL (inconsistent_topic, l, inconsistent_topic_cb);
+ ASSERT_CALLBACK_EQUAL (liveliness_changed, l, liveliness_changed_cb);
+ ASSERT_CALLBACK_EQUAL (liveliness_lost, l, liveliness_lost_cb);
+ ASSERT_CALLBACK_EQUAL (offered_deadline_missed, l, offered_deadline_missed_cb);
+ ASSERT_CALLBACK_EQUAL (offered_incompatible_qos, l, offered_incompatible_qos_cb);
+ ASSERT_CALLBACK_EQUAL (publication_matched, l, publication_matched_cb);
+ ASSERT_CALLBACK_EQUAL (requested_deadline_missed, l, requested_deadline_missed_cb);
+ ASSERT_CALLBACK_EQUAL (requested_incompatible_qos, l, requested_incompatible_qos_cb);
+ ASSERT_CALLBACK_EQUAL (sample_lost, l, sample_lost_cb);
+ ASSERT_CALLBACK_EQUAL (sample_rejected, l, sample_rejected_cb);
+ ASSERT_CALLBACK_EQUAL (subscription_matched, l, subscription_matched_cb);
}
-static uint32_t
-waitfor_cb(uint32_t expected)
+CU_Test (ddsc_listener, create_and_delete)
{
- dds_time_t timeout = 5 * DDS_NSECS_IN_SEC;
- bool signalled = true;
- ddsrt_mutex_lock(&g_mutex);
- while (((cb_called & expected) != expected) && (signalled)) {
- signalled = ddsrt_cond_waitfor(&g_cond, &g_mutex, timeout);
- }
- ddsrt_mutex_unlock(&g_mutex);
- return cb_called;
-}
-
+ dds_listener_t *listener = dds_create_listener (NULL);
+ CU_ASSERT_PTR_NOT_NULL_FATAL (listener);
+ check_all_const (listener, 0);
+ dds_delete_listener (listener);
+ // check delete_listeners handles a null pointer gracefully
+ dds_delete_listener (NULL);
+}
-/****************************************************************************
- * Test initializations and teardowns.
- ****************************************************************************/
-
-static void
-init_triggering_base(void)
+CU_Test (ddsc_listener, reset)
{
- char name[100];
-
- ddsrt_init();
+ dds_listener_t *listener = dds_create_listener (NULL);
+ CU_ASSERT_PTR_NOT_NULL_FATAL (listener);
- ddsrt_mutex_init(&g_mutex);
- ddsrt_cond_init(&g_cond);
+ set_all (listener);
- g_participant = dds_create_participant(DDS_DOMAIN_DEFAULT, NULL, NULL);
- CU_ASSERT_FATAL(g_participant > 0);
+ // all callbacks should revert to default after reset
+ dds_reset_listener (listener);
+ check_all_const (listener, 0);
+ dds_delete_listener (listener);
- g_subscriber = dds_create_subscriber(g_participant, NULL, NULL);
- CU_ASSERT_FATAL(g_subscriber > 0);
-
- g_publisher = dds_create_publisher(g_participant, NULL, NULL);
- CU_ASSERT_FATAL(g_publisher > 0);
+ // check reset_listeners handles a null pointer gracefully
+ dds_reset_listener (NULL);
+}
- g_topic = dds_create_topic(g_participant, &RoundTripModule_DataType_desc, create_unique_topic_name("ddsc_listener_test", name, 100), NULL, NULL);
- CU_ASSERT_FATAL(g_topic > 0);
+CU_Test (ddsc_listener, copy)
+{
+ dds_listener_t *listener1 = dds_create_listener (NULL);
+ CU_ASSERT_PTR_NOT_NULL_FATAL (listener1);
+ set_all (listener1);
+
+ dds_listener_t *listener2 = dds_create_listener (NULL);
+ CU_ASSERT_PTR_NOT_NULL_FATAL (listener2);
+ dds_copy_listener (listener2, listener1);
+ check_all (listener2);
+
+ // Calling copy with NULL should not crash and be noops
+ dds_copy_listener (listener2, NULL);
+ dds_copy_listener (NULL, listener1);
+ dds_copy_listener (NULL, NULL);
+
+ dds_delete_listener (listener1);
+ dds_delete_listener (listener2);
+}
- g_listener = dds_create_listener(NULL);
- CU_ASSERT_PTR_NOT_NULL_FATAL(g_listener);
+CU_Test (ddsc_listener, merge)
+{
+ dds_listener_t *listener1 = dds_create_listener (NULL);
+ CU_ASSERT_PTR_NOT_NULL_FATAL (listener1);
+ set_all (listener1);
+
+ // Merging listener1 into empty listener2 be like a copy
+ dds_listener_t *listener2 = dds_create_listener (NULL);
+ CU_ASSERT_PTR_NOT_NULL_FATAL (listener2);
+ dds_merge_listener (listener2, listener1);
+ check_all (listener2);
+
+ // Merging listener into a full listener2 should not overwrite anything
+ set_all_const (listener2, dummy_cb);
+ dds_merge_listener (listener2, listener1);
+ check_all_const (listener2, dummy_cb);
+
+ // Using NULLs shouldn't crash and be noops
+ dds_merge_listener (listener2, NULL);
+ dds_merge_listener (NULL, listener1);
+ dds_merge_listener (NULL, NULL);
+
+ dds_delete_listener (listener1);
+ dds_delete_listener (listener2);
+}
- g_qos = dds_create_qos();
- CU_ASSERT_PTR_NOT_NULL_FATAL(g_qos);
- dds_qset_reliability(g_qos, DDS_RELIABILITY_RELIABLE, DDS_SECS(1));
- dds_qset_history(g_qos, DDS_HISTORY_KEEP_ALL, 0);
+CU_Test(ddsc_listener, getters_setters)
+{
+ // test all individual cb get/set methods
+ dds_listener_t *listener = dds_create_listener (NULL);
+ CU_ASSERT_PTR_NOT_NULL_FATAL (listener);
- cb_called = 0;
+#define TEST_GET_SET(listener, fntype, cb) \
+ do { \
+ dds_on_##fntype##_fn dummy = NULL; \
+ /* Initially expect DDS_LUNSET on a newly created listener */ \
+ ASSERT_CALLBACK_EQUAL (fntype, listener, 0); \
+ /* Using listener or callback NULL, shouldn't crash and be noop */ \
+ dds_lset_##fntype (NULL, NULL); \
+ dds_lget_##fntype (NULL, NULL); \
+ dds_lget_##fntype (listener, NULL); \
+ dds_lget_##fntype (NULL, &dummy); \
+ CU_ASSERT_EQUAL_FATAL (dummy, NULL); \
+ /* Set to NULL, get to confirm it succeeds */ \
+ dds_lset_##fntype (listener, NULL); \
+ ASSERT_CALLBACK_EQUAL (fntype, listener, NULL); \
+ /* Set to a proper cb method, get to confirm it succeeds */ \
+ dds_lset_##fntype (listener, cb); \
+ ASSERT_CALLBACK_EQUAL (fntype, listener, cb); \
+ } while (0)
+ TEST_GET_SET (listener, data_available, data_available_cb);
+ TEST_GET_SET (listener, data_on_readers, data_on_readers_cb);
+ TEST_GET_SET (listener, inconsistent_topic, inconsistent_topic_cb);
+ TEST_GET_SET (listener, liveliness_changed, liveliness_changed_cb);
+ TEST_GET_SET (listener, liveliness_lost, liveliness_lost_cb);
+ TEST_GET_SET (listener, offered_deadline_missed, offered_deadline_missed_cb);
+ TEST_GET_SET (listener, offered_incompatible_qos, offered_incompatible_qos_cb);
+ TEST_GET_SET (listener, publication_matched, publication_matched_cb);
+ TEST_GET_SET (listener, requested_deadline_missed, requested_deadline_missed_cb);
+ TEST_GET_SET (listener, requested_incompatible_qos, requested_incompatible_qos_cb);
+ TEST_GET_SET (listener, sample_lost, sample_lost_cb);
+ TEST_GET_SET (listener, sample_rejected, sample_rejected_cb);
+ TEST_GET_SET (listener, subscription_matched, subscription_matched_cb);
+#undef TEST_GET_SET
+
+ dds_delete_listener (listener);
}
-static void
-init_triggering_test(void)
+#undef ASSERT_CALLBACK_EQUAL
+
+/**************************************************
+ **** ****
+ **** programmable listener checker ****
+ **** ****
+ **************************************************/
+
+// These had better match the corresponding type definitions!
+// n uint32_t ...count
+// c int32_t ...count_change
+// I instance handle of a data instance
+// P uint32_t QoS policy ID
+// E instance handle of an entity
+// R sample_rejected_status_kind
+static const struct {
+ size_t size; // size of status struct
+ const char *desc; // description of status struct
+ uint32_t mask; // status mask, bit in "cb_called"
+ const dds_entity_t *cb_entity; // which cb_... entity to look at
+ const void *cb_status; // cb_..._status to look at
+} lldesc[] = {
+ { 0, NULL, DDS_DATA_AVAILABLE_STATUS, &cb_reader, NULL }, // data available
+ { 0, NULL, DDS_DATA_ON_READERS_STATUS, &cb_subscriber, NULL }, // data on readers
+ { sizeof (dds_inconsistent_topic_status_t), "nc", DDS_INCONSISTENT_TOPIC_STATUS, &cb_topic, &cb_inconsistent_topic_status },
+ { sizeof (dds_liveliness_changed_status_t), "nnccE", DDS_LIVELINESS_CHANGED_STATUS, &cb_reader, &cb_liveliness_changed_status },
+ { sizeof (dds_liveliness_lost_status_t), "nc", DDS_LIVELINESS_LOST_STATUS, &cb_writer, &cb_liveliness_lost_status },
+ { sizeof (dds_offered_deadline_missed_status_t), "ncI", DDS_OFFERED_DEADLINE_MISSED_STATUS, &cb_writer, &cb_offered_deadline_missed_status },
+ { sizeof (dds_offered_incompatible_qos_status_t), "ncP", DDS_OFFERED_INCOMPATIBLE_QOS_STATUS, &cb_writer, &cb_offered_incompatible_qos_status },
+ { sizeof (dds_publication_matched_status_t), "ncncE", DDS_PUBLICATION_MATCHED_STATUS, &cb_writer, &cb_publication_matched_status },
+ { sizeof (dds_requested_deadline_missed_status_t), "ncI", DDS_REQUESTED_DEADLINE_MISSED_STATUS, &cb_reader, &cb_requested_deadline_missed_status },
+ { sizeof (dds_requested_incompatible_qos_status_t), "ncP", DDS_REQUESTED_INCOMPATIBLE_QOS_STATUS, &cb_reader, &cb_requested_incompatible_qos_status },
+ { sizeof (dds_sample_lost_status_t), "nc", DDS_SAMPLE_LOST_STATUS, &cb_reader, &cb_sample_lost_status },
+ { sizeof (dds_sample_rejected_status_t), "ncRI", DDS_SAMPLE_REJECTED_STATUS, &cb_reader, &cb_sample_rejected_status },
+ { sizeof (dds_subscription_matched_status_t), "ncncE", DDS_SUBSCRIPTION_MATCHED_STATUS, &cb_reader, &cb_subscription_matched_status }
+};
+
+static const void *advance (const void *status, size_t *off, char code)
{
- uint32_t triggered;
-
- /* Initialize base. */
- init_triggering_base();
-
- /* Set QoS Policies that'll help us test various status callbacks. */
- dds_qset_destination_order(g_qos, DDS_DESTINATIONORDER_BY_SOURCE_TIMESTAMP);
- dds_qset_reliability(g_qos, DDS_RELIABILITY_BEST_EFFORT, DDS_MSECS(100));
- dds_qset_resource_limits(g_qos, 1, 1, 1);
-
- /* Use these to be sure reader and writer know each other. */
- dds_lset_publication_matched(g_listener, publication_matched_cb);
- dds_lset_subscription_matched(g_listener, subscription_matched_cb);
- dds_lset_liveliness_changed(g_listener, liveliness_changed_cb);
-
- /* Create reader and writer with proper listeners. */
- g_writer = dds_create_writer(g_publisher, g_topic, g_qos, g_listener);
- CU_ASSERT(g_writer > 0);
- g_reader = dds_create_reader(g_subscriber, g_topic, g_qos, g_listener);
- CU_ASSERT(g_reader > 0);
-
- /* Sync. */
- triggered = waitfor_cb(DDS_PUBLICATION_MATCHED_STATUS | DDS_SUBSCRIPTION_MATCHED_STATUS | DDS_LIVELINESS_CHANGED_STATUS);
- CU_ASSERT_EQUAL_FATAL(triggered & DDS_LIVELINESS_CHANGED_STATUS, DDS_LIVELINESS_CHANGED_STATUS);
- CU_ASSERT_EQUAL_FATAL(triggered & DDS_PUBLICATION_MATCHED_STATUS, DDS_PUBLICATION_MATCHED_STATUS);
- CU_ASSERT_EQUAL_FATAL(triggered & DDS_SUBSCRIPTION_MATCHED_STATUS, DDS_SUBSCRIPTION_MATCHED_STATUS);
+#define alignof(type_) offsetof (struct { char c; type_ d; }, d)
+ size_t align = 1, size = 1;
+ switch (code)
+ {
+ case 'n': case 'c': case 'P':
+ align = alignof (uint32_t); size = sizeof (uint32_t);
+ break;
+ case 'E': case 'I':
+ align = alignof (dds_instance_handle_t); size = sizeof (dds_instance_handle_t);
+ break;
+ case 'R':
+ align = alignof (dds_sample_rejected_status_kind); size = sizeof (dds_sample_rejected_status_kind);
+ break;
+ default:
+ abort ();
+ }
+#undef alignof
+ *off = (*off + align - 1) & ~(align - 1);
+ const void *p = (const char *) status + *off;
+ *off += size;
+ return p;
}
-static void
-fini_triggering_base(void)
+static void get_status (int ll, dds_entity_t ent, void *status)
{
- dds_delete_qos(g_qos);
- dds_delete_listener(g_listener);
- dds_delete(g_participant);
- ddsrt_cond_destroy(&g_cond);
- ddsrt_mutex_destroy(&g_mutex);
- ddsrt_fini();
+ dds_return_t ret;
+ switch (ll)
+ {
+ case 2: ret = dds_get_inconsistent_topic_status (ent, status); break;
+ case 3: ret = dds_get_liveliness_changed_status (ent, status); break;
+ case 4: ret = dds_get_liveliness_lost_status (ent, status); break;
+ case 5: ret = dds_get_offered_deadline_missed_status (ent, status); break;
+ case 6: ret = dds_get_offered_incompatible_qos_status (ent, status); break;
+ case 7: ret = dds_get_publication_matched_status (ent, status); break;
+ case 8: ret = dds_get_requested_deadline_missed_status (ent, status); break;
+ case 9: ret = dds_get_requested_incompatible_qos_status (ent, status); break;
+ case 10: ret = dds_get_sample_lost_status (ent, status); break;
+ case 11: ret = dds_get_sample_rejected_status (ent, status); break;
+ case 12: ret = dds_get_subscription_matched_status (ent, status); break;
+ default: abort ();
+ }
+ CU_ASSERT_FATAL (ret == 0);
}
-static void
-fini_triggering_test(void)
+static void assert_status_change_fields_are_0 (int ll, dds_entity_t ent)
{
- dds_delete(g_reader);
- if (g_writer)
- dds_delete(g_writer);
- fini_triggering_base();
+ if (lldesc[ll].desc)
+ {
+ const char *d = lldesc[ll].desc;
+ void *status = malloc (lldesc[ll].size);
+ get_status (ll, ent, status);
+ size_t off = 0;
+ while (*d)
+ {
+ const uint32_t *p = advance (status, &off, *d);
+ if (*d == 'c')
+ CU_ASSERT_FATAL (*p == 0);
+ d++;
+ }
+ assert (off <= lldesc[ll].size);
+ free (status);
+ }
}
-
-/****************************************************************************
- * API tests
- ****************************************************************************/
-CU_Test(ddsc_listener, create_and_delete)
+static int getentity (const char *tok, bool *isbang, bool *ishash)
{
- /* Verify create doesn't return null */
- dds_listener_t *listener;
- listener = dds_create_listener(NULL);
- CU_ASSERT_PTR_NOT_NULL_FATAL(listener);
-
- /* Check default cb's are set */
- ASSERT_CALLBACK_EQUAL(inconsistent_topic, listener, DDS_LUNSET);
- ASSERT_CALLBACK_EQUAL(liveliness_lost, listener, DDS_LUNSET);
- ASSERT_CALLBACK_EQUAL(offered_deadline_missed, listener, DDS_LUNSET);
- ASSERT_CALLBACK_EQUAL(offered_incompatible_qos, listener, DDS_LUNSET);
- ASSERT_CALLBACK_EQUAL(data_on_readers, listener, DDS_LUNSET);
- ASSERT_CALLBACK_EQUAL(sample_lost, listener, DDS_LUNSET);
- ASSERT_CALLBACK_EQUAL(sample_rejected, listener, DDS_LUNSET);
- ASSERT_CALLBACK_EQUAL(liveliness_changed, listener, DDS_LUNSET);
- ASSERT_CALLBACK_EQUAL(requested_deadline_missed, listener, DDS_LUNSET);
- ASSERT_CALLBACK_EQUAL(requested_incompatible_qos, listener, DDS_LUNSET);
- ASSERT_CALLBACK_EQUAL(publication_matched, listener, DDS_LUNSET);
- ASSERT_CALLBACK_EQUAL(subscription_matched, listener, DDS_LUNSET);
- ASSERT_CALLBACK_EQUAL(data_available, listener, DDS_LUNSET);
-
- dds_delete_listener(listener);
- DDSRT_WARNING_MSVC_OFF(6387); /* Disable SAL warning on intentional misuse of the API */
- dds_delete_listener(NULL);
- DDSRT_WARNING_MSVC_ON(6387);
+ static const char *known = "PRWrstwxy";
+ const char *p;
+ if (isbang)
+ *isbang = false;
+ if (ishash)
+ *ishash = false;
+ if ((p = strchr (known, *tok)) == NULL)
+ return -1;
+ int ent = (int) (p - known);
+ if (*++tok == 0)
+ return ent;
+ if (*tok == '\'')
+ {
+ ent += (int) strlen (known);
+ tok++;
+ }
+ while (*tok == '!' || *tok == '#')
+ {
+ if (strchr (known + 3, *p) == NULL)
+ return -1; // only readers, writers
+ if (*tok == '!' && isbang)
+ *isbang = true;
+ else if (*tok == '#' && ishash)
+ *ishash = true;
+ tok++;
+ }
+ return (*tok == 0) ? ent : -1;
}
-CU_Test(ddsc_listener, reset)
+static int getlistener (const char *tok, bool *isbang)
{
- dds_listener_t *listener;
- listener = dds_create_listener(NULL);
- CU_ASSERT_PTR_NOT_NULL_FATAL(listener);
-
- /* Set a listener cb to a non-default value */
- dds_lset_data_available(listener, NULL);
- ASSERT_CALLBACK_EQUAL(data_available, listener, NULL);
-
- /* Listener cb should revert to default after reset */
- dds_reset_listener(listener);
- ASSERT_CALLBACK_EQUAL(data_available, listener, DDS_LUNSET);
-
- /* Resetting a NULL listener should not crash */
- dds_reset_listener(NULL);
-
- dds_delete_listener(listener);
+ // note: sort order is on full name (so sample rejected precedes subscription matched)
+ static const char *ls[] = {
+ "da", "dor", "it", "lc", "ll", "odm", "oiq", "pm", "rdm", "riq", "sl", "sr", "sm"
+ };
+ if (isbang)
+ *isbang = false;
+ for (size_t i = 0; i < sizeof (ls) / sizeof (*ls); i++)
+ {
+ size_t n = strlen (ls[i]);
+ if (strncmp (tok, ls[i], n) == 0 && (tok[n] == 0 || tok[n+1] == ','))
+ {
+ if (isbang)
+ *isbang = (tok[n] == '!');
+ return (int) i;
+ }
+ }
+ return -1;
}
-CU_Test(ddsc_listener, copy)
+struct ents {
+ dds_entity_t es[2 * 9];
+ dds_entity_t tps[2];
+ dds_entity_t doms[2];
+ dds_instance_handle_t esi[2 * 9];
+ // built-in topic readers for cross-referencing instance handles
+ dds_entity_t pubrd[2];
+ dds_entity_t subrd[2];
+};
+
+static void make_participant (struct ents *es, const char *topicname, int ent, const dds_qos_t *qos, dds_listener_t *list)
{
- dds_listener_t *listener1 = NULL, *listener2 = NULL;
- listener1 = dds_create_listener(NULL);
- listener2 = dds_create_listener(NULL);
- CU_ASSERT_PTR_NOT_NULL_FATAL(listener1);
- CU_ASSERT_PTR_NOT_NULL_FATAL(listener2);
-
- /* Set some listener1 callbacks to non-default values */
- dds_lset_data_available(listener1, NULL);
- dds_lset_sample_lost(listener1, sample_lost_cb);
- ASSERT_CALLBACK_EQUAL(data_available, listener1, NULL);
- ASSERT_CALLBACK_EQUAL(sample_lost, listener1, sample_lost_cb);
- ASSERT_CALLBACK_EQUAL(data_available, listener2, DDS_LUNSET);
- ASSERT_CALLBACK_EQUAL(sample_lost, listener2, DDS_LUNSET);
-
- /* Cb's should be copied to listener2 */
- dds_copy_listener(listener2, listener1);
- ASSERT_CALLBACK_EQUAL(data_available, listener1, NULL);
- ASSERT_CALLBACK_EQUAL(data_available, listener2, NULL);
- ASSERT_CALLBACK_EQUAL(sample_lost, listener1, sample_lost_cb);
- ASSERT_CALLBACK_EQUAL(sample_lost, listener2, sample_lost_cb);
-
- /* Calling copy with NULL should not crash and be noops. */
- DDSRT_WARNING_MSVC_OFF(6387); /* Disable SAL warning on intentional misuse of the API */
- dds_copy_listener(listener2, NULL);
- dds_copy_listener(NULL, listener1);
- dds_copy_listener(NULL, NULL);
- DDSRT_WARNING_MSVC_ON(6387);
-
- dds_delete_listener(listener1);
- dds_delete_listener(listener2);
+ const dds_domainid_t domid = (ent < 9) ? 0 : 1;
+ char *conf = ddsrt_expand_envvars ("${CYCLONEDDS_URI}${CYCLONEDDS_URI:+,}0", domid);
+ printf ("create domain %"PRIu32, domid);
+ fflush (stdout);
+ es->doms[domid] = dds_create_domain (domid, conf);
+ CU_ASSERT_FATAL (es->doms[domid] > 0);
+ ddsrt_free (conf);
+ printf (" create participant P%s", (ent < 9) ? "" : "'");
+ fflush (stdout);
+ es->es[ent] = dds_create_participant (domid, NULL, list);
+ CU_ASSERT_FATAL (es->es[ent] > 0);
+ es->tps[domid] = dds_create_topic (es->es[ent], &Space_Type1_desc, topicname, qos, NULL);
+ CU_ASSERT_FATAL (es->tps[domid] > 0);
+
+ // Create the built-in topic readers with a dummy listener to avoid any event (data available comes to mind)
+ // from propagating to the normal data available listener, in case it has been set on the participant.
+ //
+ // - dummy_cb aborts when it is invoked, but all reader-related listeners that can possibly trigger are set
+ // separately (incompatible qos, deadline missed, sample lost and sample rejected are all impossible by
+ // construction)
+ // - regarding data_on_readers: Cyclone handles listeners installed on an ancestor by *inheriting* them,
+ // rather than by walking up ancestor chain. Setting data_on_readers on the reader therefore overrides the
+ // listener set on the subscriber. It is a nice feature!
+ dds_listener_t *dummylist = dds_create_listener (NULL);
+ set_all_const (dummylist, dummy_cb);
+ dds_lset_data_available (dummylist, dummy_data_available_cb);
+ dds_lset_data_on_readers (dummylist, dummy_data_on_readers_cb);
+ dds_lset_subscription_matched (dummylist, dummy_subscription_matched_cb);
+ dds_lset_liveliness_changed (dummylist, dummy_liveliness_changed_cb);
+ es->pubrd[domid] = dds_create_reader (es->es[ent], DDS_BUILTIN_TOPIC_DCPSPUBLICATION, NULL, dummylist);
+ CU_ASSERT_FATAL (es->pubrd[domid] > 0);
+ es->subrd[domid] = dds_create_reader (es->es[ent], DDS_BUILTIN_TOPIC_DCPSSUBSCRIPTION, NULL, dummylist);
+ CU_ASSERT_FATAL (es->subrd[domid] > 0);
+ dds_delete_listener (dummylist);
+ printf ("pubrd %"PRId32" subrd %"PRId32" sub %"PRId32"\n", es->pubrd[domid], es->subrd[domid], dds_get_parent (es->pubrd[domid]));
}
-CU_Test(ddsc_listener, merge)
+static void make_entity1 (struct ents *es, const char *topicname, int ent, bool isbang, bool ishash, const dds_qos_t *qos, dds_qos_t *rwqos, dds_listener_t *list)
{
- dds_listener_t *listener1 = NULL, *listener2 = NULL;
- listener1 = dds_create_listener(NULL);
- listener2 = dds_create_listener(NULL);
- CU_ASSERT_PTR_NOT_NULL_FATAL(listener1);
- CU_ASSERT_PTR_NOT_NULL_FATAL(listener2);
-
- /* Set all listener1 callbacks to non-default values */
- dds_lset_inconsistent_topic (listener1, inconsistent_topic_cb);
- dds_lset_liveliness_lost (listener1, liveliness_lost_cb);
- dds_lset_offered_deadline_missed (listener1, offered_deadline_missed_cb);
- dds_lset_offered_incompatible_qos (listener1, offered_incompatible_qos_cb);
- dds_lset_data_on_readers (listener1, data_on_readers_cb);
- dds_lset_sample_lost (listener1, sample_lost_cb);
- dds_lset_data_available (listener1, data_available_cb);
- dds_lset_sample_rejected (listener1, sample_rejected_cb);
- dds_lset_liveliness_changed (listener1, liveliness_changed_cb);
- dds_lset_requested_deadline_missed (listener1, requested_deadline_missed_cb);
- dds_lset_requested_incompatible_qos (listener1, requested_incompatible_qos_cb);
- dds_lset_publication_matched (listener1, publication_matched_cb);
- dds_lset_subscription_matched (listener1, subscription_matched_cb);
-
- /* Merging listener1 into empty listener2 should act a bit like a copy. */
- dds_merge_listener(listener2, listener1);
- ASSERT_CALLBACK_EQUAL(inconsistent_topic, listener2, inconsistent_topic_cb);
- ASSERT_CALLBACK_EQUAL(liveliness_lost, listener2, liveliness_lost_cb);
- ASSERT_CALLBACK_EQUAL(offered_deadline_missed, listener2, offered_deadline_missed_cb);
- ASSERT_CALLBACK_EQUAL(offered_incompatible_qos, listener2, offered_incompatible_qos_cb);
- ASSERT_CALLBACK_EQUAL(data_on_readers, listener2, data_on_readers_cb);
- ASSERT_CALLBACK_EQUAL(sample_lost, listener2, sample_lost_cb);
- ASSERT_CALLBACK_EQUAL(data_available, listener2, data_available_cb);
- ASSERT_CALLBACK_EQUAL(sample_rejected, listener2, sample_rejected_cb);
- ASSERT_CALLBACK_EQUAL(liveliness_changed, listener2, liveliness_changed_cb);
- ASSERT_CALLBACK_EQUAL(requested_deadline_missed, listener2, requested_deadline_missed_cb);
- ASSERT_CALLBACK_EQUAL(requested_incompatible_qos, listener2, requested_incompatible_qos_cb);
- ASSERT_CALLBACK_EQUAL(publication_matched, listener2, publication_matched_cb);
- ASSERT_CALLBACK_EQUAL(subscription_matched, listener2, subscription_matched_cb);
-
- /* Merging listener into a full listener2 should act as a noop. */
- dds_lset_inconsistent_topic (listener2, (dds_on_inconsistent_topic_fn)callback_dummy);
- dds_lset_liveliness_lost (listener2, (dds_on_liveliness_lost_fn)callback_dummy);
- dds_lset_offered_deadline_missed (listener2, (dds_on_offered_deadline_missed_fn)callback_dummy);
- dds_lset_offered_incompatible_qos (listener2, (dds_on_offered_incompatible_qos_fn)callback_dummy);
- dds_lset_data_on_readers (listener2, (dds_on_data_on_readers_fn)callback_dummy);
- dds_lset_sample_lost (listener2, (dds_on_sample_lost_fn)callback_dummy);
- dds_lset_data_available (listener2, (dds_on_data_available_fn)callback_dummy);
- dds_lset_sample_rejected (listener2, (dds_on_sample_rejected_fn)callback_dummy);
- dds_lset_liveliness_changed (listener2, (dds_on_liveliness_changed_fn)callback_dummy);
- dds_lset_requested_deadline_missed (listener2, (dds_on_requested_deadline_missed_fn)callback_dummy);
- dds_lset_requested_incompatible_qos (listener2, (dds_on_requested_incompatible_qos_fn)callback_dummy);
- dds_lset_publication_matched (listener2, (dds_on_publication_matched_fn)callback_dummy);
- dds_lset_subscription_matched (listener2, (dds_on_subscription_matched_fn)callback_dummy);
- dds_merge_listener(listener2, listener1);
- ASSERT_CALLBACK_EQUAL(inconsistent_topic, listener2, (dds_on_inconsistent_topic_fn)callback_dummy);
- ASSERT_CALLBACK_EQUAL(liveliness_lost, listener2, (dds_on_liveliness_lost_fn)callback_dummy);
- ASSERT_CALLBACK_EQUAL(offered_deadline_missed, listener2, (dds_on_offered_deadline_missed_fn)callback_dummy);
- ASSERT_CALLBACK_EQUAL(offered_incompatible_qos, listener2, (dds_on_offered_incompatible_qos_fn)callback_dummy);
- ASSERT_CALLBACK_EQUAL(data_on_readers, listener2, (dds_on_data_on_readers_fn)callback_dummy);
- ASSERT_CALLBACK_EQUAL(sample_lost, listener2, (dds_on_sample_lost_fn)callback_dummy);
- ASSERT_CALLBACK_EQUAL(data_available, listener2, (dds_on_data_available_fn)callback_dummy);
- ASSERT_CALLBACK_EQUAL(sample_rejected, listener2, (dds_on_sample_rejected_fn)callback_dummy);
- ASSERT_CALLBACK_EQUAL(liveliness_changed, listener2, (dds_on_liveliness_changed_fn)callback_dummy);
- ASSERT_CALLBACK_EQUAL(requested_deadline_missed, listener2, (dds_on_requested_deadline_missed_fn)callback_dummy);
- ASSERT_CALLBACK_EQUAL(requested_incompatible_qos, listener2, (dds_on_requested_incompatible_qos_fn)callback_dummy);
- ASSERT_CALLBACK_EQUAL(publication_matched, listener2, (dds_on_publication_matched_fn)callback_dummy);
- ASSERT_CALLBACK_EQUAL(subscription_matched, listener2, (dds_on_subscription_matched_fn)callback_dummy);
-
- /* Using NULLs shouldn't crash and be noops. */
- dds_merge_listener(listener2, NULL);
- dds_merge_listener(NULL, listener1);
- dds_merge_listener(NULL, NULL);
-
- dds_delete_listener(listener1);
- dds_delete_listener(listener2);
+ dds_return_t ret;
+ switch (ent)
+ {
+ case 0: case 9:
+ make_participant (es, topicname, ent, qos, list);
+ break;
+ case 1: case 10:
+ if (es->es[ent-1] == 0)
+ {
+ printf ("[");
+ make_entity1 (es, topicname, ent-1, false, false, qos, rwqos, NULL);
+ printf ("] ");
+ }
+ printf ("create subscriber R%s", (ent < 9) ? "" : "'");
+ fflush (stdout);
+ es->es[ent] = dds_create_subscriber (es->es[ent-1], NULL, list);
+ break;
+ case 2: case 11:
+ if (es->es[ent-2] == 0)
+ {
+ printf ("[");
+ make_entity1 (es, topicname, ent-2, false, false, qos, rwqos, NULL);
+ printf ("] ");
+ }
+ printf ("create publisher W%s", (ent < 9) ? "" : "'");
+ fflush (stdout);
+ es->es[ent] = dds_create_publisher (es->es[ent-2], NULL, list);
+ break;
+ case 3: case 4: case 5: case 12: case 13: case 14:
+ if (es->es[ent < 9 ? 1 : 10] == 0)
+ {
+ printf ("[");
+ make_entity1 (es, topicname, ent < 9 ? 1 : 10, false, false, qos, rwqos, NULL);
+ printf ("] ");
+ }
+ printf ("create %s reader %c%s", isbang ? "best-effort" : "reliable", 'r' + (ent < 9 ? ent-3 : ent-12), (ent < 9) ? "" : "'");
+ fflush (stdout);
+ dds_reset_qos (rwqos);
+ if (isbang)
+ dds_qset_reliability (rwqos, DDS_RELIABILITY_BEST_EFFORT, DDS_MSECS (100));
+ if (ishash)
+ dds_qset_resource_limits (rwqos, 1, DDS_LENGTH_UNLIMITED, DDS_LENGTH_UNLIMITED);
+ es->es[ent] = dds_create_reader (es->es[ent < 9 ? 1 : 10], es->tps[ent < 9 ? 0 : 1], rwqos, list);
+ break;
+ case 6: case 7: case 8: case 15: case 16: case 17:
+ if (es->es[ent < 9 ? 2 : 11] == 0)
+ {
+ printf ("[");
+ make_entity1 (es, topicname, ent < 9 ? 2 : 11, false, false, qos, rwqos, NULL);
+ printf ("] ");
+ }
+ printf ("create %s writer %c%s", isbang ? "best-effort" : "reliable", 'w' + (ent < 9 ? ent-6 : ent-15), (ent < 9) ? "" : "'");
+ fflush (stdout);
+ dds_reset_qos (rwqos);
+ if (isbang)
+ dds_qset_reliability (rwqos, DDS_RELIABILITY_BEST_EFFORT, DDS_MSECS (100));
+ if (ishash)
+ dds_qset_resource_limits (rwqos, 1, DDS_LENGTH_UNLIMITED, DDS_LENGTH_UNLIMITED);
+ es->es[ent] = dds_create_writer (es->es[ent < 9 ? 2 : 11], es->tps[ent < 9 ? 0 : 1], rwqos, list);
+ break;
+ default:
+ abort ();
+ }
+ printf (" = %"PRId32, es->es[ent]);
+ fflush (stdout);
+ CU_ASSERT_FATAL (es->es[ent] > 0);
+ ret = dds_get_instance_handle (es->es[ent], &es->esi[ent]);
+ //printf (" %"PRIx64, es->esi[ent]);
+ //fflush (stdout);
+ CU_ASSERT_FATAL (ret == 0);
}
-CU_Test(ddsc_listener, getters_setters)
+static void make_entity (struct ents *es, const char *topicname, int ent, bool isbang, bool ishash, const dds_qos_t *qos, dds_qos_t *rwqos, dds_listener_t *list)
{
- /* test all individual cb get/set methods */
- dds_listener_t *listener = dds_create_listener(NULL);
- CU_ASSERT_PTR_NOT_NULL_FATAL(listener);
-
- DDSRT_WARNING_MSVC_OFF(6387); /* Disable SAL warning on intentional misuse of the API */ \
- TEST_GET_SET(listener, inconsistent_topic, inconsistent_topic_cb);
- TEST_GET_SET(listener, liveliness_lost, liveliness_lost_cb);
- TEST_GET_SET(listener, offered_deadline_missed, offered_deadline_missed_cb);
- TEST_GET_SET(listener, offered_incompatible_qos, offered_incompatible_qos_cb);
- TEST_GET_SET(listener, data_on_readers, data_on_readers_cb);
- TEST_GET_SET(listener, sample_lost, sample_lost_cb);
- TEST_GET_SET(listener, sample_rejected, sample_rejected_cb);
- TEST_GET_SET(listener, liveliness_changed, liveliness_changed_cb);
- TEST_GET_SET(listener, requested_deadline_missed, requested_deadline_missed_cb);
- TEST_GET_SET(listener, requested_incompatible_qos, requested_incompatible_qos_cb);
- TEST_GET_SET(listener, publication_matched, publication_matched_cb);
- TEST_GET_SET(listener, subscription_matched, subscription_matched_cb);
- TEST_GET_SET(listener, data_available, data_available_cb);
- DDSRT_WARNING_MSVC_ON(6387);
-
- dds_delete_listener(listener);
+ make_entity1 (es, topicname, ent, isbang, ishash, qos, rwqos, list);
+ printf ("\n");
}
+static char *strsep_noempty (char **cursor, const char *sep)
+{
+ char *tok;
+ while ((tok = ddsrt_strsep (cursor, sep)) != NULL && *tok == 0) { }
+ return tok;
+}
-
-/****************************************************************************
- * Triggering tests
- ****************************************************************************/
-CU_Test(ddsc_listener, propagation, .init=init_triggering_base, .fini=fini_triggering_base)
+static dds_instance_handle_t lookup_insthandle (const struct ents *es, int ent, int ent1)
{
- dds_listener_t *listener_par = NULL;
- dds_listener_t *listener_pub = NULL;
- dds_listener_t *listener_sub = NULL;
- uint32_t triggered;
+ // if both are in the same domain, it's easy
+ if (ent / 9 == ent1 / 9)
+ return es->esi[ent1];
+ else
+ {
+ // if they aren't ... find GUID from instance handle in the one domain,
+ // then find instance handle for GUID in the other
+ dds_entity_t rd1 = 0, rd2 = 0;
+ switch (ent1)
+ {
+ case 3: case 4: case 5: rd1 = es->subrd[0]; rd2 = es->subrd[1]; break;
+ case 12: case 13: case 14: rd1 = es->subrd[1]; rd2 = es->subrd[0]; break;
+ case 6: case 7: case 8: rd1 = es->pubrd[0]; rd2 = es->pubrd[1]; break;
+ case 15: case 16: case 17: rd1 = es->pubrd[1]; rd2 = es->pubrd[0]; break;
+ default: abort ();
+ }
+
dds_return_t ret;
- RoundTripModule_DataType sample;
- memset (&sample, 0, sizeof (sample));
-
- /* Let participant be interested in data. */
- listener_par = dds_create_listener(NULL);
- CU_ASSERT_PTR_NOT_NULL_FATAL(listener_par);
- dds_lset_data_on_readers(listener_par, data_on_readers_cb);
- ret = dds_set_listener(g_participant, listener_par);
- CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
- dds_delete_listener(listener_par);
-
- /* Let publisher be interested in publication matched. */
- listener_pub = dds_create_listener(NULL);
- CU_ASSERT_PTR_NOT_NULL_FATAL(listener_pub);
- dds_lset_publication_matched(listener_pub, publication_matched_cb);
- ret = dds_set_listener(g_publisher, listener_pub);
- CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
- dds_delete_listener(listener_pub);
-
- /* Let subscriber be interested in subscription matched. */
- listener_sub = dds_create_listener(NULL);
- CU_ASSERT_PTR_NOT_NULL_FATAL(listener_pub);
- dds_lset_subscription_matched(listener_sub, subscription_matched_cb);
- ret = dds_set_listener(g_subscriber, listener_sub);
- CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
- dds_delete_listener(listener_sub);
-
- /* Create reader and writer without listeners. */
- g_reader = dds_create_reader(g_subscriber, g_topic, g_qos, NULL);
- CU_ASSERT_FATAL(g_reader > 0);
- g_writer = dds_create_writer(g_publisher, g_topic, g_qos, NULL);
- CU_ASSERT_FATAL(g_writer > 0);
-
- /* Publication and Subscription should be matched. */
- triggered = waitfor_cb(DDS_PUBLICATION_MATCHED_STATUS | DDS_SUBSCRIPTION_MATCHED_STATUS);
- CU_ASSERT_EQUAL_FATAL(triggered & DDS_SUBSCRIPTION_MATCHED_STATUS, DDS_SUBSCRIPTION_MATCHED_STATUS);
- CU_ASSERT_EQUAL_FATAL(triggered & DDS_PUBLICATION_MATCHED_STATUS, DDS_PUBLICATION_MATCHED_STATUS);
- CU_ASSERT_EQUAL_FATAL(cb_writer, g_writer);
- CU_ASSERT_EQUAL_FATAL(cb_reader, g_reader);
-
- /* Write sample. */
- ret = dds_write(g_writer, &sample);
- CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
-
- /* Data on readers should be triggered with the right status. */
- triggered = waitfor_cb(DDS_DATA_ON_READERS_STATUS);
- CU_ASSERT_EQUAL_FATAL(triggered & DDS_DATA_ON_READERS_STATUS, DDS_DATA_ON_READERS_STATUS);
- CU_ASSERT_EQUAL_FATAL(cb_subscriber, g_subscriber);
- CU_ASSERT_NOT_EQUAL_FATAL(triggered & DDS_DATA_AVAILABLE_STATUS, DDS_DATA_AVAILABLE_STATUS);
-
- dds_delete(g_writer);
- dds_delete(g_reader);
+ dds_builtintopic_endpoint_t keysample;
+ //printf ("(in %"PRId32" %"PRIx64" -> ", rd1, es->esi[ent1]);
+ //fflush (stdout);
+ ret = dds_instance_get_key (rd1, es->esi[ent1], &keysample);
+ CU_ASSERT_FATAL (ret == 0);
+ // In principle, only key fields are set in sample returned by get_key;
+ // in the case of a built-in topic that is extended to the participant
+ // key. The qos and topic/type names should not be set, and there is no
+ // (therefore) memory allocated for the sample.
+ CU_ASSERT_FATAL (keysample.qos == NULL);
+ CU_ASSERT_FATAL (keysample.topic_name == NULL);
+ CU_ASSERT_FATAL (keysample.type_name == NULL);
+ //for (size_t j = 0; j < sizeof (keysample.key.v); j++)
+ // printf ("%s%02x", (j > 0 && j % 4 == 0) ? ":" : "", keysample.key.v[j]);
+ const dds_instance_handle_t ih = dds_lookup_instance (rd2, &keysample);
+ CU_ASSERT_FATAL (ih != 0);
+ //printf (" -> %"PRIx64")", ih);
+ //fflush (stdout);
+ return ih;
+ }
}
-
-CU_Test(ddsc_listener, matched, .init=init_triggering_base, .fini=fini_triggering_base)
+static void checkstatus (int ll, const struct ents *es, int ent, const char *args, const void *status)
{
- uint32_t triggered;
-
- /* We will basically do the same as the 'normal' init_triggering_test() and
- * fini_triggering_test() calls. It's just that we do it in a different
- * order and use the participant iso subscriber and publisher. */
-
- /* We are interested in matched notifications. */
- dds_lset_publication_matched(g_listener, publication_matched_cb);
- dds_lset_subscription_matched(g_listener, subscription_matched_cb);
-
- /* Create reader and writer with proper listeners.
- * The creation order is deliberately different from publication_matched and subscription_matched. */
- g_reader = dds_create_reader(g_participant, g_topic, g_qos, g_listener);
- CU_ASSERT_FATAL(g_reader > 0);
- g_writer = dds_create_writer(g_participant, g_topic, g_qos, g_listener);
- CU_ASSERT_FATAL(g_writer > 0);
-
- /* Both matched should be triggered on the right entities. */
- triggered = waitfor_cb(DDS_PUBLICATION_MATCHED_STATUS | DDS_SUBSCRIPTION_MATCHED_STATUS);
- CU_ASSERT_EQUAL_FATAL(triggered & DDS_SUBSCRIPTION_MATCHED_STATUS, DDS_SUBSCRIPTION_MATCHED_STATUS);
- CU_ASSERT_EQUAL_FATAL(triggered & DDS_PUBLICATION_MATCHED_STATUS, DDS_PUBLICATION_MATCHED_STATUS);
- CU_ASSERT_EQUAL_FATAL(cb_writer, g_writer);
- CU_ASSERT_EQUAL_FATAL(cb_reader, g_reader);
-
- dds_delete(g_writer);
- dds_delete(g_reader);
+ DDSRT_WARNING_MSVC_OFF(4996); // use of sscanf triggers a warning
+ if (*args == 0)
+ return;
+ if (*args++ != '(')
+ abort ();
+ assert (lldesc[ll].desc != NULL);
+ const char *d = lldesc[ll].desc;
+ const char *sep = "(";
+ size_t off = 0;
+ while (*d)
+ {
+ const void *p = advance (status, &off, *d);
+ char str[32];
+ unsigned u;
+ int i, pos = -1;
+ switch (*d)
+ {
+ case 'n':
+ if (sscanf (args, "%u%n", &u, &pos) != 1 || (args[pos] != ',' && args[pos] != ')'))
+ abort ();
+ printf ("%s%"PRIu32" %u", sep, *(uint32_t *)p, u); fflush (stdout);
+ CU_ASSERT_FATAL (*(uint32_t *)p == u);
+ break;
+ case 'c':
+ if (sscanf (args, "%d%n", &i, &pos) != 1 || (args[pos] != ',' && args[pos] != ')'))
+ abort ();
+ printf ("%s%"PRId32" %d", sep, *(int32_t *)p, i); fflush (stdout);
+ CU_ASSERT_FATAL (*(int32_t *)p == i);
+ break;
+ case 'P': // policy id: currently fixed at reliability
+ pos = -1; // not actually consuming an argument
+ printf ("%s%"PRIu32" %d", sep, *(uint32_t *)p, (int) DDS_RELIABILITY_QOS_POLICY_ID); fflush (stdout);
+ CU_ASSERT_FATAL (*(uint32_t *)p == (uint32_t) DDS_RELIABILITY_QOS_POLICY_ID);
+ break;
+ case 'R':
+ if (sscanf (args, "%31[^,)]%n", str, &pos) != 1 || (args[pos] != ',' && args[pos] != ')'))
+ abort ();
+ if (strcmp (str, "i") == 0)
+ i = (int) DDS_REJECTED_BY_INSTANCES_LIMIT;
+ else if (strcmp (str, "s") == 0)
+ i = (int) DDS_REJECTED_BY_SAMPLES_LIMIT;
+ else if (strcmp (str, "spi") == 0)
+ i = (int) DDS_REJECTED_BY_SAMPLES_PER_INSTANCE_LIMIT;
+ else
+ abort ();
+ printf ("%s%d %d", sep, (int) *(dds_sample_rejected_status_kind *)p, i); fflush (stdout);
+ CU_ASSERT_FATAL (*(dds_sample_rejected_status_kind *)p == (dds_sample_rejected_status_kind) i);
+ break;
+ case 'I': // instance handle is too complicated
+ pos = -1; // not actually consuming an argument
+ break;
+ case 'E': {
+ int ent1 = -1;
+ dds_instance_handle_t esi1 = 0;
+ if (sscanf (args, "%31[^,)]%n", str, &pos) != 1 || (args[pos] != ',' && args[pos] != ')'))
+ abort ();
+ if (strcmp (str, "*") != 0 && (ent1 = getentity (str, NULL, NULL)) < 0)
+ abort ();
+ if (ent1 != -1)
+ esi1 = lookup_insthandle (es, ent, ent1);
+ printf ("%s%"PRIx64" %"PRIx64, sep, *(dds_instance_handle_t *)p, esi1); fflush (stdout);
+ CU_ASSERT_FATAL (ent1 == -1 || *(dds_instance_handle_t *)p == esi1);
+ break;
+ }
+ default: abort ();
+ }
+ args += pos + 1;
+ sep = ", ";
+ d++;
+ }
+ printf (")");
+ assert (*args == 0);
+ assert (off <= lldesc[ll].size);
+ DDSRT_WARNING_MSVC_ON(4996);
}
-CU_Test(ddsc_listener, publication_matched, .init=init_triggering_test, .fini=fini_triggering_test)
+/** @brief run a "test" consisting of a sequence of simplish operations
+ *
+ * This operation takes a test description, really a program in a bizarre syntax, and executes it. Any failures,
+ * be it because of error codes coming out of the Cyclone calls or expected values being wrong cause it to fail
+ * the test via CU_ASSERT_FATAL. While it is doing this, it outputs the test steps to stdout including some
+ * actual values. An invalid program is mostly reported by calling abort(). It is geared towards checking for
+ * listener invocations and the effects on statuses.
+ *
+ * Entities in play:
+ *
+ * - participants: P P'
+ * - subscribers: R R'
+ * - publishers: W W'
+ * - readers: r s t r' s' t'
+ * - writers: w x y w' x' y'
+ *
+ * The unprimed ones exist in domain 0, the primed ones in domain 1 (but configured such that it talks to
+ * domain 0), so that network-related listener invocations can be checked as well.
+ *
+ * The first mention of an entity creates it as well as its ancestors. Implicitly created ancestors always have
+ * standard QoS and have no listeners. There is one topic that is created implicitly when the participant is
+ * created.
+ *
+ * Standard QoS is: default + reliable (100ms), by-source-timestamp, keep-all.
+ * The QoS of a reader/writer can be altered at the first mention of it by suffixing its name with "!" and/or "#"
+ * (the apostrophe is part of the name, so w#! or r'! are valid). Those suffixes are ignored if the entity
+ * already exists.
+ *
+ * A program consists of a sequence of operations separated by whitespace, ';' or '/' (there is no meaning to the
+ * separators, they exist to allow visual grouping):
+ *
+ * PROGRAM ::= (OP (\s+|[/;])*)*
+ *
+ * OP ::= (LISTENER)* ENTITY-NAME
+ * if entity ENTITY-NAME does not exist:
+ * creates the entity with the given listeners installed
+ * else
+ * changes the entity's listeners to the specified ones
+ * (see above for the valid ENTITY-NAMEs)
+ * | -ENTITY-NAME
+ * deletes the specified entity
+ * | WRITE-LIKE[fail][@DT] KEY
+ * writes/disposes/unregisters key KEY (an integer), if "fail" is appended, the
+ * expectation is that it fails with a timeout, if @DT is appended, the timestamp is the
+ * start time of the test + s rather than the current time; DT is a floating-point
+ * number
+ * | READ-LIKE[(A,B))]
+ * reads/takes at most 10 samples, counting the number of valid and invalid samples seen
+ * and checking it against A and B if given
+ * | ?LISTENER[(ARGS)]
+ * waits until the specified listener has been invoked on using a flag set
+ * by the listener function, resets the flag and verifies that neither the entity status
+ * bit nor the "change" fields in the various statuses were set
+ * ARGS is used to check the status argument most recently passed to the listener:
+ * it (A,B) verifies count and change match A and B, policy matches RELIABILITY
+ * lc (A,B,C,D,E) verifies that alive and not-alive counts match A and B, that
+ * alive and not-alive changes match C and D and that the last handle matches
+ * E if an entity name (ignored if E = "*")
+ * ll (A,B) verifies count and change match A and B
+ * odm (A,B) verifies count and change match A and B, last handle is ignored
+ * oiq (A,B) verifies that total count and change match A and B and that the
+ * mismatching QoS is reliability (the only one that can for now)
+ * pm (A,B,C,D,E) verifies that total count and change match A and B, that
+ * current count and change match C and D and that the last handle matches E
+ * if an entity name (ignored if E = "*")
+ * rdm see odm
+ * riq see oiq
+ * sl (A,B) verifies that total count and change match A and B
+ * sr (A,B,C) verifies total count and change match A and B, and that the reason
+ * matches C (one of "s" for samples, "i" for instances, "spi" for samples
+ * per instance)
+ * sm see pm
+ * | ?!LISTENER
+ * (not listener) tests that LISTENER has not been invoked since last reset
+ * | sleep D
+ * delay program execution for D s (D is a floating-point number)
+ * WRITE-LIKE ::= wr write
+ * | wrdisp write-dispose
+ * | disp dispose
+ * | unreg unregister
+ * READ-LIKE ::= read dds_read (so any state)
+ * | take dds_take (so any state)
+ * LISTENER ::= da data available (acts on a reader)
+ * | dor data on readers (acts on a subcsriber)
+ * | it incompatible topic (acts on a topic)
+ * | lc liveliness changed (acts on a reader)
+ * | ll liveliness lost (acts on a writer)
+ * | odm offered deadline missed (acts on a writer)
+ * | oiq offered incompatible QoS (acts on a writer)
+ * | pm publication matched (acts on a writer)
+ * | rdm requested deadline missed (acts on a reader)
+ * | riq requested incompatible QoS (acts on a reader)
+ * | sl sample lost (acts on a reader)
+ * | sr sample rejected (acts on a reader)
+ * | sm subscription matched (acts on a reader)
+ *
+ * All entities share the listeners with their global state. Only the latest invocation is visible.
+ *
+ * @param[in] ops Program to execute.
+ */
+static void dotest (const char *ops)
{
- dds_publication_matched_status_t publication_matched;
- dds_instance_handle_t reader_hdl;
- dds_return_t ret;
- uint32_t triggered;
- uint32_t status;
-
- /* Get reader handle that should be part of the status. */
- ret = dds_get_instance_handle(g_reader, &reader_hdl);
- CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
-
- /* Publication matched should be triggered with the right status. */
- triggered = waitfor_cb(DDS_PUBLICATION_MATCHED_STATUS);
- CU_ASSERT_EQUAL_FATAL(triggered & DDS_PUBLICATION_MATCHED_STATUS, DDS_PUBLICATION_MATCHED_STATUS);
- CU_ASSERT_EQUAL_FATAL(cb_writer, g_writer);
- CU_ASSERT_EQUAL_FATAL(cb_publication_matched_status.current_count, 1);
- CU_ASSERT_EQUAL_FATAL(cb_publication_matched_status.current_count_change, 1);
- CU_ASSERT_EQUAL_FATAL(cb_publication_matched_status.total_count, 1);
- CU_ASSERT_EQUAL_FATAL(cb_publication_matched_status.total_count_change, 1);
- CU_ASSERT_EQUAL_FATAL(cb_publication_matched_status.last_subscription_handle, reader_hdl);
-
- /* The listener should have swallowed the status. */
- ret = dds_read_status(g_writer, &status, DDS_PUBLICATION_MATCHED_STATUS);
- CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
- CU_ASSERT_EQUAL_FATAL(status, 0);
-
- /* The listener should have reset the count_change. */
- ret = dds_get_publication_matched_status(g_writer, &publication_matched);
- CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
- CU_ASSERT_EQUAL_FATAL(publication_matched.current_count, 1);
- CU_ASSERT_EQUAL_FATAL(publication_matched.current_count_change, 0);
- CU_ASSERT_EQUAL_FATAL(publication_matched.total_count, 1);
- CU_ASSERT_EQUAL_FATAL(publication_matched.total_count_change, 0);
- CU_ASSERT_EQUAL_FATAL(publication_matched.last_subscription_handle, reader_hdl);
-
- /* Reset the trigger flags. */
- ddsrt_mutex_lock(&g_mutex);
- cb_called = 0;
- ddsrt_mutex_unlock(&g_mutex);
-
- /* Un-match the publication by deleting the reader. */
- dds_delete(g_reader);
-
- /* Publication matched should be triggered with the right status. */
- triggered = waitfor_cb(DDS_PUBLICATION_MATCHED_STATUS);
- CU_ASSERT_EQUAL_FATAL(triggered & DDS_PUBLICATION_MATCHED_STATUS, DDS_PUBLICATION_MATCHED_STATUS);
- CU_ASSERT_EQUAL_FATAL(cb_writer, g_writer);
- CU_ASSERT_EQUAL_FATAL(cb_publication_matched_status.current_count, 0);
- CU_ASSERT_EQUAL_FATAL(cb_publication_matched_status.current_count_change, -1);
- CU_ASSERT_EQUAL_FATAL(cb_publication_matched_status.total_count, 1);
- CU_ASSERT_EQUAL_FATAL(cb_publication_matched_status.total_count_change, 0);
- CU_ASSERT_EQUAL_FATAL(cb_publication_matched_status.last_subscription_handle, reader_hdl);
-
- /* The listener should have reset the count_change. */
- ret = dds_get_publication_matched_status(g_writer, &publication_matched);
- CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
- CU_ASSERT_EQUAL_FATAL(publication_matched.current_count, 0);
- CU_ASSERT_EQUAL_FATAL(publication_matched.current_count_change, 0);
- CU_ASSERT_EQUAL_FATAL(publication_matched.total_count, 1);
- CU_ASSERT_EQUAL_FATAL(publication_matched.total_count_change, 0);
- CU_ASSERT_EQUAL_FATAL(publication_matched.last_subscription_handle, reader_hdl);
+ DDSRT_WARNING_MSVC_OFF(4996); // use of sscanf triggers a warning
+ static const char *sep = " /;\n\t\r\v";
+ char *opscopy = ddsrt_strdup (ops), *cursor = opscopy, *tok;
+ struct ents es;
+ dds_return_t ret;
+ Space_Type1 sample;
+ char topicname[100];
+ dds_qos_t *qos = dds_create_qos (), *rwqos = dds_create_qos ();
+ dds_listener_t *list = dds_create_listener (NULL);
+ const dds_time_t tref = dds_time ();
+ CU_ASSERT_FATAL (qos != NULL);
+ CU_ASSERT_FATAL (rwqos != NULL);
+ CU_ASSERT_FATAL (list != NULL);
+ dds_qset_reliability (qos, DDS_RELIABILITY_RELIABLE, DDS_MSECS (100));
+ dds_qset_destination_order (qos, DDS_DESTINATIONORDER_BY_SOURCE_TIMESTAMP);
+ dds_qset_history (qos, DDS_HISTORY_KEEP_ALL, 0);
+ memset (&es, 0, sizeof (es));
+ memset (&sample, 0, sizeof (sample));
+
+ ddsrt_mutex_init (&g_mutex);
+ ddsrt_cond_init (&g_cond);
+ ddsrt_mutex_lock (&g_mutex);
+ cb_called = 0;
+ ddsrt_mutex_unlock (&g_mutex);
+
+ create_unique_topic_name ("ddsc_listener_test", topicname, 100);
+ printf ("dotest: %s\n", ops);
+ printf ("topic: %s\n", topicname);
+ while ((tok = strsep_noempty (&cursor, sep)) != NULL)
+ {
+ int ent, ll;
+ bool isbang, ishash;
+ if ((ent = getentity (tok, &isbang, &ishash)) >= 0)
+ {
+ make_entity (&es, topicname, ent, isbang, ishash, qos, rwqos, NULL);
+ }
+ else if (*tok == '-' && (ent = getentity (tok + 1, NULL, NULL)) >= 0)
+ {
+ // delete deliberately leaves the instance handle in place for checking
+ // the publication/subscription handle in subscription matched/publication
+ // matched for a lost match
+ printf ("delete %"PRId32"\n", es.es[ent]);
+ ret = dds_delete (es.es[ent]);
+ CU_ASSERT_FATAL (ret == 0);
+ es.es[ent] = 0;
+ }
+ else if ((ll = getlistener (tok, &isbang)) >= 0)
+ {
+ printf ("set listener:");
+ dds_reset_listener (list);
+ do {
+ printf (" %s", tok);
+ switch (ll)
+ {
+ case 0: dds_lset_data_available (list, isbang ? 0 : data_available_cb); break;
+ case 1: dds_lset_data_on_readers (list, isbang ? 0 : data_on_readers_cb); break;
+ case 2: dds_lset_inconsistent_topic (list, isbang ? 0: inconsistent_topic_cb); break;
+ case 3: dds_lset_liveliness_changed (list, isbang ? 0 : liveliness_changed_cb); break;
+ case 4: dds_lset_liveliness_lost (list, isbang ? 0 : liveliness_lost_cb); break;
+ case 5: dds_lset_offered_deadline_missed (list, isbang ? 0 : offered_deadline_missed_cb); break;
+ case 6: dds_lset_offered_incompatible_qos (list, isbang ? 0 : offered_incompatible_qos_cb); break;
+ case 7: dds_lset_publication_matched (list, isbang ? 0 : publication_matched_cb); break;
+ case 8: dds_lset_requested_deadline_missed (list, isbang ? 0 : requested_deadline_missed_cb); break;
+ case 9: dds_lset_requested_incompatible_qos (list, isbang ? 0 : requested_incompatible_qos_cb); break;
+ case 10: dds_lset_sample_lost (list, isbang ? 0 : sample_lost_cb); break;
+ case 11: dds_lset_sample_rejected (list, isbang ? 0 : sample_rejected_cb); break;
+ case 12: dds_lset_subscription_matched (list, isbang ? 0 : subscription_matched_cb); break;
+ default: abort ();
+ }
+ } while ((tok = strsep_noempty (&cursor, sep)) != NULL && (ll = getlistener (tok, &isbang)) >= 0);
+ if (tok == NULL || (ent = getentity (tok, &isbang, &ishash)) < 0)
+ abort ();
+ if (es.es[ent] == 0)
+ {
+ printf (" for ");
+ make_entity (&es, topicname, ent, isbang, ishash, qos, rwqos, list);
+ }
+ else
+ {
+ dds_listener_t *tmplist = dds_create_listener (NULL);
+ CU_ASSERT_FATAL (tmplist != NULL);
+ ret = dds_get_listener (es.es[ent], tmplist);
+ CU_ASSERT_FATAL (ret == 0);
+ dds_merge_listener (list, tmplist);
+ dds_delete_listener (tmplist);
+ printf (" on entity %"PRId32"\n", es.es[ent]);
+ ret = dds_set_listener (es.es[ent], list);
+ CU_ASSERT_FATAL (ret == 0);
+ }
+ }
+ else if (strncmp (tok, "wr", 2) == 0 || strncmp (tok, "disp", 4) == 0 || strncmp (tok, "unreg", 5) == 0)
+ {
+ dds_return_t (*fn) (dds_entity_t wr, const void *sample, dds_time_t ts) = 0;
+ double dt = 0.0;
+ dds_time_t ts = dds_time ();
+ char *cmd = tok;
+ bool expectfail = false;
+ int off, pos, key;
+ if ((tok = strsep_noempty (&cursor, sep)) == NULL)
+ abort ();
+ if (sscanf (tok, "%d%n", &key, &pos) != 1 || tok[pos] != 0)
+ abort ();
+ if ((tok = strsep_noempty (&cursor, sep)) == NULL || (ent = getentity (tok, &isbang, &ishash)) < 0)
+ abort ();
+ if (es.es[ent] == 0)
+ make_entity (&es, topicname, ent, isbang, ishash, qos, rwqos, NULL);
+ switch (cmd[0])
+ {
+ case 'w':
+ if (strncmp (cmd + 2, "disp", 4) == 0) {
+ off = 6; fn = dds_writedispose_ts;
+ } else {
+ off = 2; fn = dds_write_ts;
+ }
+ break;
+ case 'd': off = 4; fn = dds_dispose_ts; break;
+ case 'u': off = 5; fn = dds_unregister_instance_ts; break;
+ default: abort ();
+ }
+ if (strncmp (cmd + off, "fail", 4) == 0)
+ {
+ expectfail = true;
+ off += 4;
+ }
+ if (cmd[off] == '@')
+ {
+ if (sscanf (cmd + off, "@%lf%n", &dt, &pos) != 1 || cmd[off + pos] != 0)
+ abort ();
+ ts = tref + (dds_time_t) (dt * 1e9);
+ }
+ sample.long_1 = key;
+ printf ("entity %"PRId32": %*.*s@%"PRId64".%09"PRId64" %d\n", es.es[ent], off, off, cmd, ts / DDS_NSECS_IN_SEC, ts % DDS_NSECS_IN_SEC, key);
+ ret = fn (es.es[ent], &sample, ts);
+ if (expectfail) {
+ CU_ASSERT_FATAL (ret == DDS_RETCODE_TIMEOUT);
+ } else {
+ CU_ASSERT_FATAL (ret == 0);
+ }
+ }
+ else if (strncmp (tok, "take", 4) == 0 || strncmp(tok, "read", 4) == 0)
+ {
+ char *args = (tok[4] ? tok + 4 : NULL);
+ int exp_nvalid = -1, exp_ninvalid = -1, pos;
+ dds_return_t (*fn) (dds_entity_t, void **buf, dds_sample_info_t *, size_t, uint32_t);
+ fn = (strncmp (tok, "take", 4) == 0) ? dds_take : dds_read;
+ assert (args == NULL || *args == '(');
+ if (args && (sscanf (args, "(%d,%d)%n", &exp_nvalid, &exp_ninvalid, &pos) != 2 || args[pos] != 0))
+ abort ();
+ if ((tok = strsep_noempty (&cursor, sep)) == NULL || (ent = getentity (tok, &isbang, &ishash)) < 0)
+ abort ();
+ if (es.es[ent] == 0)
+ make_entity (&es, topicname, ent, isbang, ishash, qos, rwqos, NULL);
+ printf ("entity %"PRId32": %s", es.es[ent], (fn == dds_take) ? "take" : "read");
+ fflush (stdout);
+ void *raw[10] = { NULL };
+ dds_sample_info_t si[10];
+ const uint32_t maxs = (uint32_t) (sizeof (raw) / sizeof (raw[0]));
+ int count[2] = { 0, 0 };
+ ret = fn (es.es[ent], raw, si, maxs, maxs);
+ CU_ASSERT_FATAL (ret >= 0);
+ for (int32_t i = 0; i < ret; i++)
+ count[si[i].valid_data]++;
+ ret = dds_return_loan (es.es[ent], raw, ret);
+ CU_ASSERT_FATAL (ret == 0);
+ printf (" valid %d %d invalid %d %d\n", count[1], exp_nvalid, count[0], exp_ninvalid);
+ if (exp_nvalid >= 0)
+ CU_ASSERT_FATAL (count[1] == exp_nvalid);
+ if (exp_ninvalid >= 0)
+ CU_ASSERT_FATAL (count[0] == exp_ninvalid);
+ }
+ else if (tok[0] == '?')
+ {
+ const bool expectclear = (tok[1] == '!');
+ const char *llname = tok + (expectclear ? 2 : 1);
+ char *checkargs;
+ if ((checkargs = strchr (llname, '(')) != NULL)
+ *checkargs = 0; // clear so getlistener groks the input
+ if ((ll = getlistener (llname, NULL)) < 0)
+ abort ();
+ if (expectclear)
+ {
+ printf ("listener %s: check not called", llname);
+ fflush (stdout);
+ ddsrt_mutex_lock (&g_mutex);
+ printf (" cb_called %"PRIx32" %s\n", cb_called, (cb_called & lldesc[ll].mask) == 0 ? "ok" : "fail");
+ CU_ASSERT_FATAL ((cb_called & lldesc[ll].mask) == 0);
+ ddsrt_mutex_unlock (&g_mutex);
+ }
+ else
+ {
+ bool signalled = true;
+ uint32_t status;
+ if ((tok = strsep_noempty (&cursor, sep)) == NULL || (ent = getentity (tok, &isbang, &ishash)) < 0)
+ abort ();
+ if (es.es[ent] == 0)
+ make_entity (&es, topicname, ent, isbang, ishash, qos, rwqos, NULL);
+ if ((size_t) ll >= sizeof (lldesc) / sizeof (*lldesc))
+ abort ();
+ printf ("listener %s: check called for entity %"PRId32, llname, es.es[ent]);
+ fflush (stdout);
+ ddsrt_mutex_lock (&g_mutex);
+ while ((cb_called & lldesc[ll].mask) == 0 && signalled)
+ signalled = ddsrt_cond_waitfor (&g_cond, &g_mutex, DDS_SECS (5));
+ printf (" cb_called %"PRIx32" (%s)", cb_called, (cb_called & lldesc[ll].mask) != 0 ? "ok" : "fail");
+ fflush (stdout);
+ CU_ASSERT_FATAL ((cb_called & lldesc[ll].mask) != 0);
+ printf (" cb_entity %"PRId32" %"PRId32" (%s)", *lldesc[ll].cb_entity, es.es[ent], (*lldesc[ll].cb_entity == es.es[ent]) ? "ok" : "fail");
+ fflush (stdout);
+ CU_ASSERT_FATAL (*lldesc[ll].cb_entity == es.es[ent]);
+ if (!(es.doms[0] && es.doms[1]))
+ {
+ // FIXME: two domains: listener invocation happens on another thread and we can observe non-0 "change" fields
+ // they get updated, listener gets invoked, then they get reset -- pretty sure it is allowed by the spec, but
+ // not quite elegant
+ assert_status_change_fields_are_0 (ll, es.es[ent]);
+ }
+ if (checkargs && lldesc[ll].cb_status)
+ {
+ *checkargs = '('; // restore ( so checkargs function gets a more sensible input
+ checkstatus (ll, &es, ent, checkargs, lldesc[ll].cb_status);
+ }
+ printf ("\n");
+ cb_called &= ~lldesc[ll].mask;
+ ddsrt_mutex_unlock (&g_mutex);
+ ret = dds_get_status_changes (es.es[ent], &status);
+ CU_ASSERT_FATAL (ret == 0);
+ CU_ASSERT_FATAL ((status & lldesc[ll].mask) == 0);
+ }
+ }
+ else if (strcmp (tok, "sleep") == 0)
+ {
+ if ((tok = strsep_noempty (&cursor, sep)) == NULL)
+ abort ();
+ double d; int pos;
+ if (sscanf (tok, "%lf%n", &d, &pos) != 1 || tok[pos] != 0)
+ abort ();
+ printf ("sleep %fs\n", d);
+ dds_sleepfor ((dds_duration_t) (d * 1e9));
+ }
+ else
+ {
+ printf ("tok '%s': unrecognized\n", tok);
+ abort ();
+ }
+ }
+
+ dds_delete_listener (list);
+ dds_delete_qos (rwqos);
+ dds_delete_qos (qos);
+ // prevent any listeners from being invoked so we can safely delete the
+ // mutex and the condition variable -- must do this going down the
+ // hierarchy, or listeners may remain set through inheritance
+ for (size_t i = 0; i < sizeof (es.es) / sizeof (es.es[0]); i++)
+ {
+ if (es.es[i])
+ {
+ ret = dds_set_listener (es.es[i], NULL);
+ CU_ASSERT_FATAL (ret == 0);
+ }
+ }
+ ddsrt_mutex_destroy (&g_mutex);
+ ddsrt_cond_destroy (&g_cond);
+ for (size_t i = 0; i < sizeof (es.doms) / sizeof (es.doms[0]); i++)
+ {
+ if (es.doms[i])
+ {
+ ret = dds_delete (es.doms[i]);
+ CU_ASSERT_FATAL (ret == 0);
+ }
+ }
+ ddsrt_free (opscopy);
+ DDSRT_WARNING_MSVC_ON(4996);
}
-CU_Test(ddsc_listener, subscription_matched, .init=init_triggering_test, .fini=fini_triggering_test)
+/**************************************************
+ **** ****
+ **** listener invocation checks ****
+ **** ****
+ **************************************************/
+
+CU_Test (ddsc_listener, propagation)
{
- dds_subscription_matched_status_t subscription_matched;
- dds_instance_handle_t writer_hdl;
- dds_return_t ret;
- uint32_t triggered;
- uint32_t status;
-
- /* Get writer handle that should be part of the status. */
- ret = dds_get_instance_handle(g_writer, &writer_hdl);
- CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
-
- /* Subscription matched should be triggered with the right status. */
- triggered = waitfor_cb(DDS_SUBSCRIPTION_MATCHED_STATUS);
- CU_ASSERT_EQUAL_FATAL(triggered & DDS_SUBSCRIPTION_MATCHED_STATUS, DDS_SUBSCRIPTION_MATCHED_STATUS);
- CU_ASSERT_EQUAL_FATAL(cb_reader, g_reader);
- CU_ASSERT_EQUAL_FATAL(cb_subscription_matched_status.current_count, 1);
- CU_ASSERT_EQUAL_FATAL(cb_subscription_matched_status.current_count_change, 1);
- CU_ASSERT_EQUAL_FATAL(cb_subscription_matched_status.total_count, 1);
- CU_ASSERT_EQUAL_FATAL(cb_subscription_matched_status.total_count_change, 1);
- CU_ASSERT_EQUAL_FATAL(cb_subscription_matched_status.last_publication_handle, writer_hdl);
-
- /* The listener should have swallowed the status. */
- ret = dds_read_status(g_reader, &status, DDS_SUBSCRIPTION_MATCHED_STATUS);
- CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
- CU_ASSERT_EQUAL_FATAL(status, 0);
-
- /* The listener should have reset the count_change. */
- ret = dds_get_subscription_matched_status(g_reader, &subscription_matched);
- CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
- CU_ASSERT_EQUAL_FATAL(subscription_matched.current_count, 1);
- CU_ASSERT_EQUAL_FATAL(subscription_matched.current_count_change, 0);
- CU_ASSERT_EQUAL_FATAL(subscription_matched.total_count, 1);
- CU_ASSERT_EQUAL_FATAL(subscription_matched.total_count_change, 0);
- CU_ASSERT_EQUAL_FATAL(subscription_matched.last_publication_handle, writer_hdl);
-
- /* Reset the trigger flags. */
- ddsrt_mutex_lock(&g_mutex);
- cb_called = 0;
- ddsrt_mutex_unlock(&g_mutex);
-
- /* Un-match the subscription by deleting the writer. */
- dds_delete(g_writer);
-
- /* Subscription matched should be triggered with the right status. */
- triggered = waitfor_cb(DDS_SUBSCRIPTION_MATCHED_STATUS);
- CU_ASSERT_EQUAL_FATAL(triggered & DDS_SUBSCRIPTION_MATCHED_STATUS, DDS_SUBSCRIPTION_MATCHED_STATUS);
- CU_ASSERT_EQUAL_FATAL(cb_reader, g_reader);
- CU_ASSERT_EQUAL_FATAL(cb_subscription_matched_status.current_count, 0);
- CU_ASSERT_EQUAL_FATAL(cb_subscription_matched_status.current_count_change, -1);
- CU_ASSERT_EQUAL_FATAL(cb_subscription_matched_status.total_count, 1);
- CU_ASSERT_EQUAL_FATAL(cb_subscription_matched_status.total_count_change, 0);
- CU_ASSERT_EQUAL_FATAL(cb_subscription_matched_status.last_publication_handle, writer_hdl);
-
- /* The listener should have reset the count_change. */
- ret = dds_get_subscription_matched_status(g_reader, &subscription_matched);
- CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
- CU_ASSERT_EQUAL_FATAL(subscription_matched.current_count, 0);
- CU_ASSERT_EQUAL_FATAL(subscription_matched.current_count_change, 0);
- CU_ASSERT_EQUAL_FATAL(subscription_matched.total_count, 1);
- CU_ASSERT_EQUAL_FATAL(subscription_matched.total_count_change, 0);
- CU_ASSERT_EQUAL_FATAL(subscription_matched.last_publication_handle, writer_hdl);
+ // data-on-readers set on a participant at creation time must not trigger for
+ // the readers for DCPSPublication and DCPSSubscription: those events must be
+ // invisible for the test logic to work reliably. Installing a dummy listener
+ // for it on the reader should prevent that from happening
+ dotest ("da dor lc sm P ; ?!dor ?!da ?!sm ?!lc");
+ // writing data should trigger data-available unless data-on-readers is set
+ dotest ("da lc sm P ; r ; wr 0 w ; ?da r ?sm r ?lc r");
+ dotest ("da dor lc sm P ; r ; wr 0 w ; ?!da ; ?dor R ?sm r ?lc r");
+ // setting listeners after entity creation should work, too
+ dotest ("P W R ; dor P pm W sm R ; r w ; ?sm r ?pm w ; wr 0 w ; ?dor R ; ?!da");
}
-CU_Test(ddsc_listener, incompatible_qos, .init=init_triggering_base, .fini=fini_triggering_base)
+CU_Test (ddsc_listener, matched)
{
- dds_offered_incompatible_qos_status_t offered_incompatible_qos;
- dds_requested_incompatible_qos_status_t requested_incompatible_qos;
- dds_return_t ret;
- uint32_t triggered;
- uint32_t status;
-
- /* We are interested in incompatible qos notifications. */
- dds_lset_offered_incompatible_qos(g_listener, offered_incompatible_qos_cb);
- dds_lset_requested_incompatible_qos(g_listener, requested_incompatible_qos_cb);
-
- /* Create reader and writer with proper listeners.
- * But create reader with persistent durability to get incompatible qos. */
- g_writer = dds_create_writer(g_participant, g_topic, g_qos, g_listener);
- CU_ASSERT_FATAL(g_writer > 0);
- dds_qset_durability (g_qos, DDS_DURABILITY_PERSISTENT);
- g_reader = dds_create_reader(g_participant, g_topic, g_qos, g_listener);
- CU_ASSERT_FATAL(g_reader > 0);
-
- /* Incompatible QoS should be triggered with the right status. */
- triggered = waitfor_cb(DDS_OFFERED_INCOMPATIBLE_QOS_STATUS | DDS_REQUESTED_INCOMPATIBLE_QOS_STATUS);
- CU_ASSERT_EQUAL_FATAL(triggered & DDS_OFFERED_INCOMPATIBLE_QOS_STATUS, DDS_OFFERED_INCOMPATIBLE_QOS_STATUS);
- CU_ASSERT_EQUAL_FATAL(triggered & DDS_REQUESTED_INCOMPATIBLE_QOS_STATUS, DDS_REQUESTED_INCOMPATIBLE_QOS_STATUS);
- CU_ASSERT_EQUAL_FATAL(cb_reader, g_reader);
- CU_ASSERT_EQUAL_FATAL(cb_writer, g_writer);
- CU_ASSERT_EQUAL_FATAL(cb_offered_incompatible_qos_status.total_count, 1);
- CU_ASSERT_EQUAL_FATAL(cb_offered_incompatible_qos_status.total_count_change, 1);
- CU_ASSERT_EQUAL_FATAL(cb_offered_incompatible_qos_status.last_policy_id, DDS_DURABILITY_QOS_POLICY_ID);
- CU_ASSERT_EQUAL_FATAL(cb_requested_incompatible_qos_status.total_count, 1);
- CU_ASSERT_EQUAL_FATAL(cb_requested_incompatible_qos_status.total_count_change, 1);
- CU_ASSERT_EQUAL_FATAL(cb_requested_incompatible_qos_status.last_policy_id, DDS_DURABILITY_QOS_POLICY_ID);
-
- /* The listener should have swallowed the status. */
- ret = dds_read_status(g_writer, &status, DDS_OFFERED_INCOMPATIBLE_QOS_STATUS);
- CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
- CU_ASSERT_EQUAL_FATAL(status, 0);
- ret = dds_read_status(g_reader, &status, DDS_REQUESTED_INCOMPATIBLE_QOS_STATUS);
- CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
- CU_ASSERT_EQUAL_FATAL(status, 0);
-
- /* The listener should have reset the count_change. */
- ret = dds_get_offered_incompatible_qos_status(g_writer, &offered_incompatible_qos);
- CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
- ret = dds_get_requested_incompatible_qos_status(g_reader, &requested_incompatible_qos);
- CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
- CU_ASSERT_EQUAL_FATAL(offered_incompatible_qos.total_count, 1);
- CU_ASSERT_EQUAL_FATAL(offered_incompatible_qos.total_count_change, 0);
- CU_ASSERT_EQUAL_FATAL(offered_incompatible_qos.last_policy_id, DDS_DURABILITY_QOS_POLICY_ID);
- CU_ASSERT_EQUAL_FATAL(requested_incompatible_qos.total_count, 1);
- CU_ASSERT_EQUAL_FATAL(requested_incompatible_qos.total_count_change, 0);
- CU_ASSERT_EQUAL_FATAL(requested_incompatible_qos.last_policy_id, DDS_DURABILITY_QOS_POLICY_ID);
-
- dds_delete(g_writer);
- dds_delete(g_reader);
+ // publication & subscription matched must both trigger; note: reader/writer matching inside
+ // a process is synchronous, no need to check everywhere
+ dotest ("sm r pm w ?pm w ?sm r");
+ // across the network it should work just as well (matching happens on different threads for
+ // remote & local entity creation, so it is meaningfully different test)
+ dotest ("sm r pm w' ?pm w' ?sm r");
}
-CU_Test(ddsc_listener, data_available, .init=init_triggering_test, .fini=fini_triggering_test)
+CU_Test (ddsc_listener, publication_matched)
{
- dds_return_t ret;
- uint32_t triggered;
- uint32_t status;
- RoundTripModule_DataType sample;
- memset (&sample, 0, sizeof (sample));
-
- /* We are interested in data available notifications. */
- dds_lset_data_available(g_listener, data_available_cb);
- ret = dds_set_listener(g_reader, g_listener);
- CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
-
- /* Write sample. */
- ret = dds_write(g_writer, &sample);
- CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
-
- /* Data available should be triggered with the right status. */
- triggered = waitfor_cb(DDS_DATA_AVAILABLE_STATUS);
- CU_ASSERT_EQUAL_FATAL(triggered & DDS_DATA_AVAILABLE_STATUS, DDS_DATA_AVAILABLE_STATUS);
- CU_ASSERT_EQUAL_FATAL(cb_reader, g_reader);
-
- /* The listener should have swallowed the status. */
- ret = dds_read_status(g_subscriber, &status, DDS_DATA_ON_READERS_STATUS);
- CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
- CU_ASSERT_EQUAL_FATAL(status, 0);
- ret = dds_read_status(g_reader, &status, DDS_DATA_AVAILABLE_STATUS);
- CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
- CU_ASSERT_EQUAL_FATAL(status, 0);
-
- /* Deleting the writer causes unregisters (or dispose+unregister), and those
- should trigger DATA_AVAILABLE as well */
- ddsrt_mutex_lock(&g_mutex);
- cb_called = 0;
- cb_reader = 0;
- ddsrt_mutex_unlock(&g_mutex);
- ret = dds_delete (g_writer);
- CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
- g_writer = 0;
- triggered = waitfor_cb(DDS_DATA_AVAILABLE_STATUS);
- CU_ASSERT_EQUAL_FATAL(triggered & DDS_DATA_AVAILABLE_STATUS, DDS_DATA_AVAILABLE_STATUS);
- CU_ASSERT_EQUAL_FATAL(cb_reader, g_reader);
-
- /* The listener should have swallowed the status. */
- ret = dds_read_status(g_subscriber, &status, DDS_DATA_ON_READERS_STATUS);
- CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
- CU_ASSERT_EQUAL_FATAL(status, 0);
- ret = dds_read_status(g_reader, &status, DDS_DATA_AVAILABLE_STATUS);
- CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
- CU_ASSERT_EQUAL_FATAL(status, 0);
+ // regardless of order of creation, the writer should see one reader come & then go
+ dotest ("sm r pm w ; ?pm(1,1,1,1,r) w ?sm r ; -r ; ?pm(1,0,0,-1,r) w");
+ dotest ("pm w sm r ; ?pm(1,1,1,1,r) w ?sm r ; -r ; ?pm(1,0,0,-1,r) w");
+
+ // regardless of order of creation, the writer should see one reader come & then go, also
+ // when a second reader introduced
+ dotest ("sm r pm w ; ?pm(1,1,1,1,r) w ?sm r ; t ?pm(2,1,2,1,t) w ; -r ; ?pm(2,0,1,-1,r) w");
+ dotest ("pm w sm r ; ?pm(1,1,1,1,r) w ?sm r ; t ?pm(2,1,2,1,t) w ; -t ; ?pm(2,0,1,-1,t) w");
+
+ // same with 2 domains
+ dotest ("sm r pm w' ; ?pm(1,1,1,1,r) w' ?sm r ; -r ; ?pm(1,0,0,-1,r) w'");
+ dotest ("pm w sm r' ; ?pm(1,1,1,1,r') w ?sm r' ; -r' ; ?pm(1,0,0,-1,r') w");
+
+ dotest ("sm r pm w' ; ?pm(1,1,1,1,r) w' ?sm r ; t ?pm(2,1,2,1,t) w' ; -r ; ?pm(2,0,1,-1,r) w'");
+ dotest ("pm w sm r' ; ?pm(1,1,1,1,r') w ?sm r' ; t ?pm(2,1,2,1,t) w ; -t ; ?pm(2,0,1,-1,t) w");
+ dotest ("sm r pm w' ; ?pm(1,1,1,1,r) w' ?sm r ; t' ?pm(2,1,2,1,t') w' ; -r ; ?pm(2,0,1,-1,r) w'");
+ dotest ("pm w sm r' ; ?pm(1,1,1,1,r') w ?sm r' ; t' ?pm(2,1,2,1,t') w ; -t' ; ?pm(2,0,1,-1,t') w");
}
-CU_Test(ddsc_listener, data_available_delete_writer, .init=init_triggering_test, .fini=fini_triggering_test)
+CU_Test (ddsc_listener, subscription_matched)
{
- dds_return_t ret;
- uint32_t triggered;
- uint32_t status;
- RoundTripModule_DataType sample;
- memset (&sample, 0, sizeof (sample));
-
- /* We are interested in data available notifications. */
- dds_lset_data_available(g_listener, data_available_cb);
- ret = dds_set_listener(g_reader, g_listener);
- CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
-
- /* Write sample, wait for the listener to swallow the status. */
- ret = dds_write(g_writer, &sample);
- CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
- triggered = waitfor_cb(DDS_DATA_AVAILABLE_STATUS);
- CU_ASSERT_EQUAL_FATAL(triggered & DDS_DATA_AVAILABLE_STATUS, DDS_DATA_AVAILABLE_STATUS);
- CU_ASSERT_EQUAL_FATAL(cb_reader, g_reader);
-
- /* Deleting the writer must trigger DATA_AVAILABLE as well */
- ddsrt_mutex_lock(&g_mutex);
- cb_called = 0;
- cb_reader = 0;
- ddsrt_mutex_unlock(&g_mutex);
- ret = dds_delete (g_writer);
- CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
- g_writer = 0;
- triggered = waitfor_cb(DDS_DATA_AVAILABLE_STATUS);
- CU_ASSERT_EQUAL_FATAL(triggered & DDS_DATA_AVAILABLE_STATUS, DDS_DATA_AVAILABLE_STATUS);
- CU_ASSERT_EQUAL_FATAL(cb_reader, g_reader);
-
- /* The listener should have swallowed the status. */
- ret = dds_read_status(g_subscriber, &status, DDS_DATA_ON_READERS_STATUS);
- CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
- CU_ASSERT_EQUAL_FATAL(status, 0);
- ret = dds_read_status(g_reader, &status, DDS_DATA_AVAILABLE_STATUS);
- CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
- CU_ASSERT_EQUAL_FATAL(status, 0);
+ // regardless of order of creation, the reader should see one writer come & then go
+ dotest ("sm r pm w ; ?pm w ?sm(1,1,1,1,w) r ; -w ; ?sm(1,0,0,-1,w) r");
+ dotest ("pm w sm r ; ?pm w ?sm(1,1,1,1,w) r ; -w ; ?sm(1,0,0,-1,w) r");
+
+ // regardless of order of creation, the reader should see one writer come & then go, also
+ // when a second writer is introduced
+ dotest ("sm r pm w ; ?pm w ?sm(1,1,1,1,w) r ; x ?sm(2,1,2,1,x) r ; -w ; ?sm(2,0,1,-1,w) r");
+ dotest ("pm w sm r ; ?pm w ?sm(1,1,1,1,w) r ; x ?sm(2,1,2,1,x) r ; -x ; ?sm(2,0,1,-1,x) r");
+
+ // same with 2 domains
+ dotest ("sm r pm w' ; ?pm w' ?sm(1,1,1,1,w') r ; -w' ; ?sm(1,0,0,-1,w') r");
+ dotest ("pm w sm r' ; ?pm w ?sm(1,1,1,1,w) r' ; -w ; ?sm(1,0,0,-1,w) r'");
+
+ dotest ("sm r pm w' ; ?pm w' ?sm(1,1,1,1,w') r ; x ?sm(2,1,2,1,x) r ; -w' ; ?sm(2,0,1,-1,w') r");
+ dotest ("pm w sm r' ; ?pm w ?sm(1,1,1,1,w) r' ; x ?sm(2,1,2,1,x) r' ; -x ; ?sm(2,0,1,-1,x) r'");
+ dotest ("sm r pm w' ; ?pm w' ?sm(1,1,1,1,w') r ; x' ?sm(2,1,2,1,x') r ; -w' ; ?sm(2,0,1,-1,w') r");
+ dotest ("pm w sm r' ; ?pm w ?sm(1,1,1,1,w) r' ; x' ?sm(2,1,2,1,x') r' ; -x' ; ?sm(2,0,1,-1,x') r'");
}
-CU_Test(ddsc_listener, data_available_delete_writer_disposed, .init=init_triggering_test, .fini=fini_triggering_test)
+CU_Test (ddsc_listener, incompatible_qos)
{
- dds_return_t ret;
- uint32_t triggered;
- uint32_t status;
- RoundTripModule_DataType sample;
- memset (&sample, 0, sizeof (sample));
-
- /* We are interested in data available notifications. */
- dds_lset_data_available(g_listener, data_available_cb);
- ret = dds_set_listener(g_reader, g_listener);
- CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
-
- /* Write & dispose sample and take it so that the instance is empty & disposed. Then deleting
- the writer should silently drop the instance. */
- ret = dds_write(g_writer, &sample);
- CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
- ret = dds_dispose(g_writer, &sample);
- CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
- triggered = waitfor_cb(DDS_DATA_AVAILABLE_STATUS);
- CU_ASSERT_EQUAL_FATAL(triggered & DDS_DATA_AVAILABLE_STATUS, DDS_DATA_AVAILABLE_STATUS);
- CU_ASSERT_EQUAL_FATAL(cb_reader, g_reader);
-
- /* Take all data so that the instance becomes empty & disposed */
- do {
- void *sampleptr = &sample;
- dds_sample_info_t info;
- ret = dds_take (g_reader, &sampleptr, &info, 1, 1);
- } while (ret > 0);
-
- /* Deleting the writer should not trigger DATA_AVAILABLE with all instances empty & disposed */
- ddsrt_mutex_lock(&g_mutex);
- cb_called = 0;
- cb_reader = 0;
- ddsrt_mutex_unlock(&g_mutex);
- ret = dds_delete (g_writer);
- CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
- g_writer = 0;
- ddsrt_mutex_lock(&g_mutex);
- CU_ASSERT_EQUAL_FATAL(cb_called & DDS_DATA_AVAILABLE_STATUS_ID, 0);
- ddsrt_mutex_unlock(&g_mutex);
-
- /* The listener should have swallowed the status. */
- ret = dds_read_status(g_subscriber, &status, DDS_DATA_ON_READERS_STATUS);
- CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
- CU_ASSERT_EQUAL_FATAL(status, 0);
- ret = dds_read_status(g_reader, &status, DDS_DATA_AVAILABLE_STATUS);
- CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
- CU_ASSERT_EQUAL_FATAL(status, 0);
+ // best-effort writer & reliable reader: both must trigger incompatible QoS event
+ dotest ("oiq w! riq r ; ?oiq(1,1) w ?riq(1,1) r");
+ dotest ("riq r oiq w! ; ?oiq(1,1) w ?riq(1,1) r");
}
-CU_Test(ddsc_listener, data_on_readers, .init=init_triggering_test, .fini=fini_triggering_test)
+CU_Test (ddsc_listener, data_available)
{
- dds_return_t ret;
- uint32_t triggered;
- uint32_t status;
- RoundTripModule_DataType sample;
- memset (&sample, 0, sizeof (sample));
-
- /* We are interested in data available notifications. */
- dds_lset_data_on_readers(g_listener, data_on_readers_cb);
- ret = dds_set_listener(g_subscriber, g_listener);
- CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
-
- /* Setting data available notifications should not 'sabotage' the on_readers call. */
- dds_lset_data_available(g_listener, data_available_cb);
- ret = dds_set_listener(g_reader, g_listener);
- CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
-
- /* Write sample. */
- ret = dds_write(g_writer, &sample);
- CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
-
- /* Data on readers should be triggered with the right status. */
- triggered = waitfor_cb(DDS_DATA_ON_READERS_STATUS);
- CU_ASSERT_EQUAL_FATAL(triggered & DDS_DATA_ON_READERS_STATUS, DDS_DATA_ON_READERS_STATUS);
- CU_ASSERT_EQUAL_FATAL(cb_subscriber, g_subscriber);
- CU_ASSERT_NOT_EQUAL_FATAL(triggered & DDS_DATA_AVAILABLE_STATUS, DDS_DATA_AVAILABLE_STATUS);
-
- /* The listener should have swallowed the status. */
- ret = dds_read_status(g_subscriber, &status, DDS_DATA_ON_READERS_STATUS);
- CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
- CU_ASSERT_EQUAL_FATAL(status, 0);
- ret = dds_read_status(g_reader, &status, DDS_DATA_AVAILABLE_STATUS);
- CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
- CU_ASSERT_EQUAL_FATAL(status, 0);
+ // data available on reader
+ dotest ("da sm r pm w ?pm w ?sm r wr 0 w ?da r ?!dor");
+ // data available set on subscriber
+ dotest ("da R sm r pm w ?pm w ?sm r wr 0 w ?da r ?!dor");
+ // data available set on participant
+ dotest ("da P sm r pm w ?pm w ?sm r wr 0 w ?da r ?!dor");
}
-
-CU_Test(ddsc_listener, sample_lost, .init=init_triggering_test, .fini=fini_triggering_test)
+CU_Test (ddsc_listener, data_available_delete_writer)
{
- dds_sample_lost_status_t sample_lost;
- dds_return_t ret;
- uint32_t triggered;
- dds_time_t the_past;
- uint32_t status;
- RoundTripModule_DataType sample;
- memset (&sample, 0, sizeof (sample));
-
- /* Get a time that should be historic on all platforms.*/
- the_past = dds_time() - 1000000;
-
- /* We are interested in sample lost notifications. */
- dds_lset_sample_lost(g_listener, sample_lost_cb);
- ret = dds_set_listener(g_reader, g_listener);
- CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
-
- /* Write first sample with current timestamp. */
- ret = dds_write_ts(g_writer, &sample, dds_time());
- CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
-
- /* Write second sample with older timestamp. */
- ret = dds_write_ts(g_writer, &sample, the_past);
- CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
-
- /* Sample lost should be triggered with the right status. */
- triggered = waitfor_cb(DDS_SAMPLE_LOST_STATUS);
- CU_ASSERT_EQUAL_FATAL(triggered & DDS_SAMPLE_LOST_STATUS, DDS_SAMPLE_LOST_STATUS);
- CU_ASSERT_EQUAL_FATAL(cb_reader, g_reader);
- CU_ASSERT_EQUAL_FATAL(cb_sample_lost_status.total_count, 1);
- CU_ASSERT_EQUAL_FATAL(cb_sample_lost_status.total_count_change, 1);
-
- /* The listener should have swallowed the status. */
- ret = dds_read_status(g_reader, &status, DDS_SAMPLE_LOST_STATUS);
- CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
- CU_ASSERT_EQUAL_FATAL(status, 0);
-
- /* The listener should have reset the count_change. */
- ret = dds_get_sample_lost_status(g_reader, &sample_lost);
- CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
- CU_ASSERT_EQUAL_FATAL(sample_lost.total_count, 1);
- CU_ASSERT_EQUAL_FATAL(sample_lost.total_count_change, 0);
+ // unmatching a writer that didn't read anything has no visible effect on RHC
+ // subscription-matched event is generated synchronously, so "?sm r" doesn't
+ // really add anything (it'd be different if there are two domain instances)
+ dotest ("da sm r w ; -w ?sm r ?!da ; take(0,0) r");
+ // after writing: auto-dispose should always trigger data available, an invalid
+ // sample needs to show up if there isn't an unread sample to use instead
+ dotest ("da r w ; wr 0 w ?da r ; -w ?da r ; take(1,0) r");
+ dotest ("da r w ; wr 0 w ?da r ; read(1,0) r ; -w ?da r ; take(1,1) r");
+ dotest ("da r w ; wr 0 w ?da r ; take(1,0) r ; -w ?da r ; take(0,1) r");
+ // same with two writers (no point in doing this also with two domains)
+ dotest ("da r w x ; -w ?!da -x ?!da ; take(0,0) r");
+ dotest ("da r w x ; wr 0 w ?da r ; -x ?!da ; -w ?da r ; take(1,0) r");
+ dotest ("da r w x ; wr 0 w ?da r ; -w ?da r ; take(1,0) r ; -x ?!da ; take(0,0) r");
+ dotest ("da r w x ; wr 0 w wr 0 x ?da r ; -w ?!da ; take(2,0) r ; -x ?da r ; take(0,1) r");
+ dotest ("da r w x ; wr 0 w wr 0 x ?da r ; read(2,0) r ; -w ?!da -x ?da r ; take(2,1) r");
+ dotest ("da r w x ; wr 0 w wr 0 x ?da r ; read(2,0) r ; -x ?!da -w ?da r ; take(2,1) r");
+ dotest ("da r w x ; wr 0 w read(1,0) r ; wr 0 x ?da r ; -w ?!da -x ?da r ; take(2,0) r");
+ dotest ("da r w x ; wr 0 w read(1,0) r ; wr 0 x ?da r ; -x ?!da -w ?da r ; take(2,0) r");
+ dotest ("da r w x ; wr 0 w read(1,0) r ; wr 0 x ?da r ; read(2,0) r ; -w ?!da -x ?da r ; take(2,1) r");
+ dotest ("da r w x ; wr 0 w read(1,0) r ; wr 0 x ?da r ; read(2,0) r ; -x ?!da -w ?da r ; take(2,1) r");
+ dotest ("da r w x ; wr 0 w wr 0 x ?da r ; take(2,0) r ; -w ?!da -x ?da r ; take(0,1) r");
+ dotest ("da r w x ; wr 0 w wr 0 x ?da r ; take(2,0) r ; -x ?!da -w ?da r ; take(0,1) r");
}
-CU_Test(ddsc_listener, sample_rejected, .init=init_triggering_test, .fini=fini_triggering_test)
+CU_Test (ddsc_listener, data_available_delete_writer_disposed)
{
- dds_sample_rejected_status_t sample_rejected;
- dds_return_t ret;
- uint32_t triggered;
- uint32_t status;
- RoundTripModule_DataType sample;
- memset (&sample, 0, sizeof (sample));
-
- /* We are interested in sample rejected notifications. */
- dds_lset_sample_rejected(g_listener, sample_rejected_cb);
- ret = dds_set_listener(g_reader, g_listener);
- CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
-
- /* Write more than resource limits set by the reader. */
- ret = dds_write(g_writer, &sample);
- CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
- ret = dds_write(g_writer, &sample);
- CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
- ret = dds_write(g_writer, &sample);
- CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
-
- /* Sample lost should be triggered with the right status. */
- triggered = waitfor_cb(DDS_SAMPLE_REJECTED_STATUS);
- CU_ASSERT_EQUAL_FATAL(triggered & DDS_SAMPLE_REJECTED_STATUS, DDS_SAMPLE_REJECTED_STATUS);
- CU_ASSERT_EQUAL_FATAL(cb_reader, g_reader);
- CU_ASSERT_EQUAL_FATAL(cb_sample_rejected_status.total_count, 2);
- CU_ASSERT_EQUAL_FATAL(cb_sample_rejected_status.total_count_change, 1);
- CU_ASSERT_EQUAL_FATAL(cb_sample_rejected_status.last_reason, DDS_REJECTED_BY_SAMPLES_LIMIT);
-
- /* The listener should have swallowed the status. */
- ret = dds_read_status(g_reader, &status, DDS_SAMPLE_REJECTED_STATUS);
- CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
- CU_ASSERT_EQUAL_FATAL(status, 0);
-
- /* The listener should have reset the count_change. */
- ret = dds_get_sample_rejected_status(g_reader, &sample_rejected);
- CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
- CU_ASSERT_EQUAL_FATAL(sample_rejected.total_count, 2);
- CU_ASSERT_EQUAL_FATAL(sample_rejected.total_count_change, 0);
- CU_ASSERT_EQUAL_FATAL(cb_sample_rejected_status.last_reason, DDS_REJECTED_BY_SAMPLES_LIMIT);
+ // same as data_available_delete_writer, but now with the instance disposed first
+ dotest ("da r w ; wr 0 w disp 0 w ?da r ; -w ?!da");
+ dotest ("da r w ; wr 0 w disp 0 w ?da r ; read(1,0) r ; -w ?!da");
+ dotest ("da r w ; wr 0 w disp 0 w ?da r ; take(1,0) r ; -w ?!da");
+
+ dotest ("da r w x ; wr 0 w ?da r ; read(1,0) r ; disp 0 w ?da r ; read(1,1) r ; -w ?!da -x ?!da");
+ dotest ("da r w x ; wr 0 w ?da r ; take(1,0) r ; disp 0 w ?da r ; take(0,1) r ; -w ?!da -x ?!da");
+ dotest ("da r w x ; wr 0 w ?da r ; read(1,0) r ; disp 0 w ?da r ; read(1,1) r ; -x ?!da -w ?!da");
+ dotest ("da r w x ; wr 0 w ?da r ; take(1,0) r ; disp 0 w ?da r ; take(0,1) r ; -x ?!da -w ?!da");
+
+ dotest ("da r w x ; wr 0 w ?da r ; read(1,0) r ; disp 0 x ?da r ; read(1,1) r ; -w ?!da -x ?!da");
+ dotest ("da r w x ; wr 0 w ?da r ; take(1,0) r ; disp 0 x ?da r ; take(0,1) r ; -w ?!da -x ?!da");
+ dotest ("da r w x ; wr 0 w ?da r ; read(1,0) r ; disp 0 x ?da r ; read(1,1) r ; -x ?!da -w ?!da");
+ dotest ("da r w x ; wr 0 w ?da r ; take(1,0) r ; disp 0 x ?da r ; take(0,1) r ; -x ?!da -w ?!da");
}
-CU_Test(ddsc_listener, liveliness_changed, .init=init_triggering_test, .fini=fini_triggering_base)
+CU_Test (ddsc_listener, data_on_readers)
{
- dds_liveliness_changed_status_t liveliness_changed;
- dds_instance_handle_t writer_hdl;
- dds_return_t ret;
- uint32_t triggered;
- uint32_t status;
-
- /* The init_triggering_test_byliveliness set our interest in liveliness. */
-
- /* Get writer handle that should be part of the status. */
- ret = dds_get_instance_handle(g_writer, &writer_hdl);
- CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
-
- /* Liveliness changed should be triggered with the right status. */
- triggered = waitfor_cb(DDS_LIVELINESS_CHANGED_STATUS);
- CU_ASSERT_EQUAL_FATAL(triggered & DDS_LIVELINESS_CHANGED_STATUS, DDS_LIVELINESS_CHANGED_STATUS);
- CU_ASSERT_EQUAL_FATAL(cb_reader, g_reader);
- CU_ASSERT_EQUAL_FATAL(cb_liveliness_changed_status.alive_count, 1);
- CU_ASSERT_EQUAL_FATAL(cb_liveliness_changed_status.alive_count_change, 1);
- CU_ASSERT_EQUAL_FATAL(cb_liveliness_changed_status.not_alive_count, 0);
- CU_ASSERT_EQUAL_FATAL(cb_liveliness_changed_status.not_alive_count_change, 0);
- CU_ASSERT_EQUAL_FATAL(cb_liveliness_changed_status.last_publication_handle, writer_hdl);
-
- /* The listener should have swallowed the status. */
- ret = dds_read_status(g_reader, &status, DDS_LIVELINESS_CHANGED_STATUS);
- CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
- CU_ASSERT_EQUAL_FATAL(status, 0);
-
- /* The listener should have reset the count_change. */
- ret = dds_get_liveliness_changed_status(g_reader, &liveliness_changed);
- CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
- CU_ASSERT_EQUAL_FATAL(liveliness_changed.alive_count, 1);
- CU_ASSERT_EQUAL_FATAL(liveliness_changed.alive_count_change, 0);
- CU_ASSERT_EQUAL_FATAL(liveliness_changed.not_alive_count, 0);
- CU_ASSERT_EQUAL_FATAL(liveliness_changed.not_alive_count_change, 0);
- CU_ASSERT_EQUAL_FATAL(liveliness_changed.last_publication_handle, writer_hdl);
-
- /* Reset the trigger flags. */
- ddsrt_mutex_lock(&g_mutex);
- cb_called = 0;
- ddsrt_mutex_unlock(&g_mutex);
-
- /* Change liveliness again by deleting the writer. */
- dds_delete(g_writer);
-
- /* Liveliness changed should be triggered with the right status. */
- triggered = waitfor_cb(DDS_LIVELINESS_CHANGED_STATUS);
- CU_ASSERT_EQUAL_FATAL(triggered & DDS_LIVELINESS_CHANGED_STATUS, DDS_LIVELINESS_CHANGED_STATUS);
- CU_ASSERT_EQUAL_FATAL(cb_reader, g_reader);
- CU_ASSERT_EQUAL_FATAL(cb_liveliness_changed_status.alive_count, 0);
- CU_ASSERT_EQUAL_FATAL(cb_liveliness_changed_status.alive_count_change, -1);
- CU_ASSERT_EQUAL_FATAL(cb_liveliness_changed_status.not_alive_count, 0);
- CU_ASSERT_EQUAL_FATAL(cb_liveliness_changed_status.not_alive_count_change, 0);
- CU_ASSERT_EQUAL_FATAL(cb_liveliness_changed_status.last_publication_handle, writer_hdl);
-
- /* The listener should have reset the count_change. */
- ret = dds_get_liveliness_changed_status(g_reader, &liveliness_changed);
- CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
- CU_ASSERT_EQUAL_FATAL(liveliness_changed.alive_count, 0);
- CU_ASSERT_EQUAL_FATAL(liveliness_changed.alive_count_change, 0);
- CU_ASSERT_EQUAL_FATAL(liveliness_changed.not_alive_count, 0);
- CU_ASSERT_EQUAL_FATAL(liveliness_changed.not_alive_count_change, 0);
- CU_ASSERT_EQUAL_FATAL(liveliness_changed.last_publication_handle, writer_hdl);
+ // data on readers wins from data available
+ dotest ("dor R da r ; wr 0 w ; ?dor R ?!da");
+ dotest ("dor P da r ; wr 0 w ; ?dor R ?!da");
}
-#if 0
-/* This is basically the same as the Lite test, but inconsistent topic is not triggered.
- * That is actually what I would expect, because the code doesn't seem to be the way
- * to go to test for inconsistent topic. */
-Test(ddsc_listener, inconsistent_topic, .init=init_triggering_base, .fini=fini_triggering_base)
+CU_Test (ddsc_listener, sample_lost)
{
- dds_entity_t wr_topic;
- dds_entity_t rd_topic;
- dds_entity_t writer;
- dds_entity_t reader;
- uint32_t triggered;
-
- os_osInit();
-
- ddsrt_mutex_init(&g_mutex);
- ddsrt_cond_init(&g_cond);
-
- g_qos = dds_create_qos();
- cr_assert_not_null(g_qos, "Failed to create prerequisite g_qos");
-
- g_listener = dds_create_listener(NULL);
- cr_assert_not_null(g_listener, "Failed to create prerequisite g_listener");
-
- g_participant = dds_create_participant(DDS_DOMAIN_DEFAULT, NULL, NULL);
- cr_assert_gt(g_participant, 0, "Failed to create prerequisite g_participant");
-
- /* We are interested in inconsistent topics. */
- dds_lset_inconsistent_topic(g_listener, inconsistent_topic_cb);
-
- wr_topic = dds_create_topic(g_participant, &RoundTripModule_DataType_desc, "WRITER_TOPIC", NULL, g_listener);
- cr_assert_gt(g_topic, 0, "Failed to create prerequisite wr_topic");
-
- rd_topic = dds_create_topic(g_participant, &RoundTripModule_DataType_desc, "READER_TOPIC", NULL, g_listener);
- cr_assert_gt(g_topic, 0, "Failed to create prerequisite rd_topic");
-
- /* Create reader and writer. */
- writer = dds_create_writer(g_participant, g_topic, NULL, NULL);
- cr_assert_gt(writer, 0, "Failed to create prerequisite writer");
- dds_qset_reliability (g_qos, DDS_RELIABILITY_RELIABLE, DDS_SECS (1));
- dds_qset_history (g_qos, DDS_HISTORY_KEEP_ALL, 0);
- reader = dds_create_reader(g_subscriber, g_topic, g_qos, NULL);
- cr_assert_gt(reader, 0, "Failed to create prerequisite reader");
-
- /* Inconsistent topic should be triggered with the right status. */
- triggered = waitfor_cb(DDS_INCONSISTENT_TOPIC_STATUS);
- cr_assert_eq(triggered & DDS_INCONSISTENT_TOPIC_STATUS, DDS_INCONSISTENT_TOPIC_STATUS, "DDS_INCONSISTENT_TOPIC_STATUS not triggered");
-
- dds_delete(reader);
- dds_delete(writer);
- dds_delete(rd_topic);
- dds_delete(wr_topic);
- dds_delete(g_participant);
+ // FIXME: figure out what really constitutes a "lost sample"
+ dotest ("sl r ; wr@0 0 w ?!sl ; wr@-1 0 w ?sl(1,1) r");
+}
- dds_delete_listener(g_listener);
- dds_delete_qos(g_qos);
+CU_Test (ddsc_listener, sample_rejected)
+{
+ // FIXME: rejection counts with retries?
+ // reliable: expect timeout on the write when max samples has been reached
+ // invalid samples don't count towards resource limits, so dispose should
+ // not be blocked
+ dotest ("sr r# ; wr 0 w wrfail 0 w wrfail 0 w ; ?sr r");
+ dotest ("sr r# ; wr 0 w wrfail 0 w ; read(1,0) r ; disp 0 w ; read(1,1) r ; ?sr r");
+
+ // best-effort: writes should succeed despite not delivering the data adding
+ // the data in the RHC, also check number of samples rejected
+ dotest ("sr r#! ; wr 0 w! wr 0 w wr 0 w ; ?sr(2,1,s) r");
+ dotest ("sr r#! ; wr 0 w! wr 0 w ; read(1,0) r ; disp 0 w ; read(1,1) r ; ?sr(1,1,s) r");
}
-#endif
+CU_Test (ddsc_listener, liveliness_changed)
+{
+ // liveliness changed should trigger along with matching
+ dotest ("pm w lc sm r ; ?pm w ?sm r ; ?lc(1,0,1,0,w) r ; -w ; ?lc(0,0,-1,0,w) r");
+ dotest ("pm w lc sm r' ; ?pm w ?sm r' ; ?lc(1,0,1,0,w) r' ; -w ; ?lc(0,0,-1,0,w) r'");
+}
From ad86b1f1f02969782462d19eaf29feea72c2b68c Mon Sep 17 00:00:00 2001
From: Erik Boasson
Date: Wed, 6 May 2020 21:31:58 +0200
Subject: [PATCH 15/36] Properly pair entity_pin/mutex_lock
Coverity has difficulty observering that dds_entity_pin /
ddsrt_mutex_lock / dds_entity_unlock is correct. It is perhaps a bit
confusing, so change it.
Signed-off-by: Erik Boasson
---
src/core/ddsc/src/dds_topic.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/core/ddsc/src/dds_topic.c b/src/core/ddsc/src/dds_topic.c
index a07b4e3e87..0f48cbd9b0 100644
--- a/src/core/ddsc/src/dds_topic.c
+++ b/src/core/ddsc/src/dds_topic.c
@@ -359,7 +359,8 @@ dds_entity_t dds_create_topic_impl (dds_entity_t participant, struct ddsi_sertop
hdl = create_topic_pp_locked (pp, ktp, (sertopic_registered->ops == &ddsi_sertopic_ops_builtintopic), sertopic_registered, listener, sedp_plist);
ddsi_sertopic_unref (*sertopic);
*sertopic = sertopic_registered;
- dds_participant_unlock (pp);
+ ddsrt_mutex_unlock (&pp->m_entity.m_mutex);
+ dds_entity_unpin (&pp->m_entity);
GVTRACE ("dds_create_topic_generic: new topic %"PRId32"\n", hdl);
return hdl;
From fe5dbb9e7ee248ddc350c6b9a52ab7c671c43210 Mon Sep 17 00:00:00 2001
From: Erik Boasson
Date: Wed, 6 May 2020 21:34:21 +0200
Subject: [PATCH 16/36] Fix conversion of user_data to security plugin
The security plugins currently use the standardized representations of
octet sequences, unlike the DDSI stack's internal representation. A
shallow copy is therefore not simply a memcpy.
Signed-off-by: Erik Boasson
---
src/core/ddsi/src/ddsi_security_util.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/core/ddsi/src/ddsi_security_util.c b/src/core/ddsi/src/ddsi_security_util.c
index 11f39b5ca3..65959bc800 100644
--- a/src/core/ddsi/src/ddsi_security_util.c
+++ b/src/core/ddsi/src/ddsi_security_util.c
@@ -517,7 +517,7 @@ q_omg_shallow_copy_ParticipantBuiltinTopicDataSecure(
/* Copy the DDS_Security_OctetSeq content (length, pointer, etc), not the buffer content. */
if (plist->qos.present & QP_USER_DATA)
- memcpy(&(dst->user_data.value), &(plist->qos.user_data.value), sizeof(DDS_Security_OctetSeq));
+ g_omg_shallow_copy_octSeq(&dst->user_data.value, &plist->qos.user_data);
/* Tokens are actually DataHolders. */
if (plist->present & PP_IDENTITY_TOKEN)
q_omg_shallow_copyin_DataHolder(&(dst->identity_token), &(plist->identity_token));
From 4ce1e395070d014b4f72a97c32f0cb30f73e1f8d Mon Sep 17 00:00:00 2001
From: Erik Boasson
Date: Wed, 6 May 2020 21:35:55 +0200
Subject: [PATCH 17/36] Fix leak when inserting p2p message in WHC fails
Signed-off-by: Erik Boasson
---
src/core/ddsi/src/q_transmit.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/src/core/ddsi/src/q_transmit.c b/src/core/ddsi/src/q_transmit.c
index 2b27d5413d..4a52fdbf4b 100644
--- a/src/core/ddsi/src/q_transmit.c
+++ b/src/core/ddsi/src/q_transmit.c
@@ -1212,6 +1212,10 @@ int write_sample_p2p_wrlock_held(struct writer *wr, seqno_t seq, struct ddsi_pli
if (wr->heartbeat_xevent)
writer_hbcontrol_note_asyncwrite(wr, tnow);
}
+ else if (gap)
+ {
+ nn_xmsg_free (gap);
+ }
prd_is_deleting:
return r;
From 5ba08644923ea4cc2eb702f9f63c158677cc8bf2 Mon Sep 17 00:00:00 2001
From: Erik Boasson
Date: Wed, 6 May 2020 21:37:23 +0200
Subject: [PATCH 18/36] Atomic update next heartbeat time for p2p writers
Sending a heartbeat to all matched readers for the P2P builtin
participant volatile secure writer unlocks the writer before pushing
each individual message out, and so determining the time of the next
heartbeat event before writing and updating it afterwards means the
state may have changed. While this is appears benign, it is better to
do the update atomically.
Signed-off-by: Erik Boasson
---
src/core/ddsi/src/q_xevent.c | 40 ++++++++++++++++++++++++------------
1 file changed, 27 insertions(+), 13 deletions(-)
diff --git a/src/core/ddsi/src/q_xevent.c b/src/core/ddsi/src/q_xevent.c
index b535bbdf94..443a9fc609 100644
--- a/src/core/ddsi/src/q_xevent.c
+++ b/src/core/ddsi/src/q_xevent.c
@@ -620,6 +620,31 @@ static void handle_xevk_entityid (struct nn_xpack *xp, struct xevent_nt *ev)
}
#ifdef DDSI_INCLUDE_SECURITY
+static int send_heartbeat_to_all_readers_check_and_sched (struct xevent *ev, struct writer *wr, const struct whc_state *whcst, ddsrt_mtime_t tnow, ddsrt_mtime_t *t_next)
+{
+ int send;
+ if (!writer_must_have_hb_scheduled (wr, whcst))
+ {
+ wr->hbcontrol.tsched = DDSRT_MTIME_NEVER;
+ send = -1;
+ }
+ else if (!writer_hbcontrol_must_send (wr, whcst, tnow))
+ {
+ wr->hbcontrol.tsched = ddsrt_mtime_add_duration (tnow, writer_hbcontrol_intv (wr, whcst, tnow));
+ send = -1;
+ }
+ else
+ {
+ const int hbansreq = writer_hbcontrol_ack_required (wr, whcst, tnow);
+ wr->hbcontrol.tsched = ddsrt_mtime_add_duration (tnow, writer_hbcontrol_intv (wr, whcst, tnow));
+ send = hbansreq;
+ }
+
+ resched_xevent_if_earlier (ev, wr->hbcontrol.tsched);
+ *t_next = wr->hbcontrol.tsched;
+ return send;
+}
+
static void send_heartbeat_to_all_readers (struct nn_xpack *xp, struct xevent *ev, struct writer *wr, ddsrt_mtime_t tnow)
{
struct whc_state whcst;
@@ -629,17 +654,11 @@ static void send_heartbeat_to_all_readers (struct nn_xpack *xp, struct xevent *e
ddsrt_mutex_lock (&wr->e.lock);
whc_get_state(wr->whc, &whcst);
-
- if (!writer_must_have_hb_scheduled (wr, &whcst))
- t_next = DDSRT_MTIME_NEVER;
- else if (!writer_hbcontrol_must_send (wr, &whcst, tnow))
- t_next = ddsrt_mtime_add_duration (tnow, writer_hbcontrol_intv (wr, &whcst, tnow));
- else
+ const int hbansreq = send_heartbeat_to_all_readers_check_and_sched (ev, wr, &whcst, tnow, &t_next);
+ if (hbansreq >= 0)
{
struct wr_prd_match *m;
struct ddsi_guid last_guid = { .prefix = {.u = {0,0,0}}, .entityid = {0} };
- const int hbansreq = writer_hbcontrol_ack_required (wr, &whcst, tnow);
- t_next = ddsrt_mtime_add_duration (tnow, writer_hbcontrol_intv (wr, &whcst, tnow));
while ((m = ddsrt_avl_lookup_succ (&wr_readers_treedef, &wr->readers, &last_guid)) != NULL)
{
@@ -669,16 +688,11 @@ static void send_heartbeat_to_all_readers (struct nn_xpack *xp, struct xevent *e
count++;
}
}
-
}
}
- resched_xevent_if_earlier (ev, t_next);
- wr->hbcontrol.tsched = t_next;
-
if (count == 0)
{
- (void)resched_xevent_if_earlier (ev, t_next);
ETRACE (wr, "heartbeat(wr "PGUIDFMT") suppressed, resched in %g s (min-ack %"PRId64"%s, avail-seq %"PRId64", xmit %"PRId64")\n",
PGUID (wr->e.guid),
(t_next.v == DDS_NEVER) ? INFINITY : (double)(t_next.v - tnow.v) / 1e9,
From 9ded3e5701fb590f6cf47ea203b2fd346d022432 Mon Sep 17 00:00:00 2001
From: Erik Boasson
Date: Wed, 6 May 2020 21:41:48 +0200
Subject: [PATCH 19/36] Memory leak reading access control configuration
Signed-off-by: Erik Boasson
---
.../access_control/src/access_control.c | 30 +++++++++++--------
1 file changed, 17 insertions(+), 13 deletions(-)
diff --git a/src/security/builtin_plugins/access_control/src/access_control.c b/src/security/builtin_plugins/access_control/src/access_control.c
index df27ea7da1..e9d91e0f12 100644
--- a/src/security/builtin_plugins/access_control/src/access_control.c
+++ b/src/security/builtin_plugins/access_control/src/access_control.c
@@ -2063,23 +2063,27 @@ read_document(
char **doc,
DDS_Security_SecurityException *ex)
{
- bool result = true;
char *data = NULL;
-
switch (DDS_Security_get_conf_item_type(doc_uri, &data))
{
- case DDS_SECURITY_CONFIG_ITEM_PREFIX_DATA:
- *doc = data;
- break;
- case DDS_SECURITY_CONFIG_ITEM_PREFIX_FILE:
- result = read_document_from_file(data, doc, ex);
- ddsrt_free(data);
- break;
- default:
- DDS_Security_Exception_set(ex, DDS_ACCESS_CONTROL_PLUGIN_CONTEXT, DDS_SECURITY_ERR_URI_TYPE_NOT_SUPPORTED_CODE, 0, DDS_SECURITY_ERR_URI_TYPE_NOT_SUPPORTED_MESSAGE, doc_uri);
- return false;
+ case DDS_SECURITY_CONFIG_ITEM_PREFIX_DATA:
+ *doc = data;
+ return true;
+
+ case DDS_SECURITY_CONFIG_ITEM_PREFIX_FILE: {
+ const bool result = read_document_from_file(data, doc, ex);
+ ddsrt_free(data);
+ return result;
+ }
+
+ case DDS_SECURITY_CONFIG_ITEM_PREFIX_PKCS11:
+ case DDS_SECURITY_CONFIG_ITEM_PREFIX_UNKNOWN:
+ DDS_Security_Exception_set(ex, DDS_ACCESS_CONTROL_PLUGIN_CONTEXT, DDS_SECURITY_ERR_URI_TYPE_NOT_SUPPORTED_CODE, 0, DDS_SECURITY_ERR_URI_TYPE_NOT_SUPPORTED_MESSAGE, doc_uri);
+ ddsrt_free(data);
+ return false;
}
- return result;
+ assert (0);
+ return false;
}
static bool
From 74f4246bee4f0f96c56c933cbaa91abd0b4f0b17 Mon Sep 17 00:00:00 2001
From: Erik Boasson
Date: Wed, 6 May 2020 21:44:12 +0200
Subject: [PATCH 20/36] Fix double free if local identity validation fails on
certificate expiry
Signed-off-by: Erik Boasson
---
.../authentication/src/authentication.c | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
diff --git a/src/security/builtin_plugins/authentication/src/authentication.c b/src/security/builtin_plugins/authentication/src/authentication.c
index 316c910044..f17ae4106f 100644
--- a/src/security/builtin_plugins/authentication/src/authentication.c
+++ b/src/security/builtin_plugins/authentication/src/authentication.c
@@ -762,6 +762,12 @@ DDS_Security_ValidationResult_t validate_local_identity(dds_security_authenticat
if (verify_certificate(identityCert, identityCA, ex) != DDS_SECURITY_VALIDATION_OK)
goto err_verification_failed;
+ if ((certExpiry = get_certificate_expiry(identityCert)) == DDS_TIME_INVALID)
+ {
+ DDS_Security_Exception_set(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "Expiry date of the certificate is invalid");
+ goto err_verification_failed;
+ }
+
if (get_adjusted_participant_guid(identityCert, candidate_participant_guid, adjusted_participant_guid, ex) != DDS_SECURITY_VALIDATION_OK)
goto err_adj_guid_failed;
@@ -774,12 +780,7 @@ DDS_Security_ValidationResult_t validate_local_identity(dds_security_authenticat
identity = local_identity_info_new(domain_id, identityCert, identityCA, privateKey, candidate_participant_guid, adjusted_participant_guid);
*local_identity_handle = IDENTITY_HANDLE(identity);
- if ((certExpiry = get_certificate_expiry(identityCert)) == DDS_TIME_INVALID)
- {
- DDS_Security_Exception_set(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "Expiry date of the certificate is invalid");
- goto err_verification_failed;
- }
- else if (certExpiry != DDS_NEVER)
+ if (certExpiry != DDS_NEVER)
add_validity_end_trigger(implementation, *local_identity_handle, certExpiry);
ddsrt_mutex_lock(&implementation->lock);
From 2d0b09c5c4c183f0f824d1447531b7941f9e1b15 Mon Sep 17 00:00:00 2001
From: Erik Boasson
Date: Wed, 6 May 2020 21:45:56 +0200
Subject: [PATCH 21/36] Vet lengths of property lists in crypto deserialization
The memory allocation in deserializing property lists within the crypto
code should not trust the deserialized length and try to allocate that
much memory but should first verify that the length is consistent with
the number of bytes remaining in the input. (Noted by Coverity as use
of tainted data.)
Signed-off-by: Erik Boasson
---
.../core/src/dds_security_serialize.c | 23 ++++++++++++++-----
1 file changed, 17 insertions(+), 6 deletions(-)
diff --git a/src/security/core/src/dds_security_serialize.c b/src/security/core/src/dds_security_serialize.c
index 0e92790afc..7e182df8ce 100644
--- a/src/security/core/src/dds_security_serialize.c
+++ b/src/security/core/src/dds_security_serialize.c
@@ -627,14 +627,20 @@ DDS_Security_Deserialize_PropertySeq(
DDS_Security_Deserializer dser,
DDS_Security_PropertySeq *seq)
{
+ /* A well-formed CDR string is length + content including terminating 0, length is
+ 4 bytes and 4-byte aligned, so the minimum length for a non-empty property
+ sequence is 4+1+(3 pad)+4+1 = 13 bytes. Just use 8 because it is way faster
+ and just as good for checking that the length value isn't completely ridiculous. */
+ const uint32_t minpropsize = (uint32_t) (2 * sizeof (uint32_t));
int r = 1;
uint32_t i;
if (!DDS_Security_Deserialize_uint32_t(dser, &seq->_length)) {
return 0;
- }
-
- if (seq->_length > 0) {
+ } else if (seq->_length > dser->remain / minpropsize) {
+ seq->_length = 0;
+ return 0;
+ } else if (seq->_length > 0) {
seq->_buffer = DDS_Security_PropertySeq_allocbuf(seq->_length);
for (i = 0; i < seq->_length && r; i++) {
r = DDS_Security_Deserialize_Property(dser, &seq->_buffer[i]);
@@ -649,14 +655,19 @@ DDS_Security_Deserialize_BinaryPropertySeq(
DDS_Security_Deserializer dser,
DDS_Security_BinaryPropertySeq *seq)
{
+ /* A well-formed CDR string + a well-formed octet sequence: 4+1+(3 pad)+4 = 12 bytes.
+ Just use 8 because it is way faster and just as good for checking that the length
+ value isn't completely ridiculous. */
+ const uint32_t minpropsize = (uint32_t) (2 * sizeof (uint32_t));
int r = 1;
uint32_t i;
if (!DDS_Security_Deserialize_uint32_t(dser, &seq->_length)) {
return 0;
- }
-
- if (seq->_length > 0) {
+ } else if (seq->_length > dser->remain / minpropsize) {
+ seq->_length = 0;
+ return 0;
+ } else if (seq->_length > 0) {
seq->_buffer = DDS_Security_BinaryPropertySeq_allocbuf(seq->_length);
for (i = 0; i < seq->_length && r; i++) {
r = DDS_Security_Deserialize_BinaryProperty(dser, &seq->_buffer[i]);
From 671c70cc1a2d6ec97bbaba055c7f6c5196e03fca Mon Sep 17 00:00:00 2001
From: Erik Boasson
Date: Thu, 7 May 2020 09:33:25 +0200
Subject: [PATCH 22/36] No reason to prevent rediscovery for 10s by default
Signed-off-by: Erik Boasson
---
docs/manual/options.md | 2 +-
etc/cyclonedds.rnc | 2 +-
etc/cyclonedds.xsd | 2 +-
src/core/ddsi/src/q_config.c | 2 +-
4 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/docs/manual/options.md b/docs/manual/options.md
index 8341f3dee7..f8a7aa5f92 100644
--- a/docs/manual/options.md
+++ b/docs/manual/options.md
@@ -1349,7 +1349,7 @@ is therefore recommended to set it to at least several seconds.
Valid values are finite durations with an explicit unit or the keyword
'inf' for infinity. Recognised units: ns, us, ms, s, min, hr, day.
-The default value is: "10s".
+The default value is: "0s".
#### //CycloneDDS/Domain/Internal/RediscoveryBlacklistDuration[@enforce]
diff --git a/etc/cyclonedds.rnc b/etc/cyclonedds.rnc
index e6ce80d278..0898f32768 100644
--- a/etc/cyclonedds.rnc
+++ b/etc/cyclonedds.rnc
@@ -1113,7 +1113,7 @@ is therefore recommended to set it to at least several seconds.
Valid values are finite durations with an explicit unit or the keyword
'inf' for infinity. Recognised units: ns, us, ms, s, min, hr,
-day.
The default value is: "10s".
""" ] ]
+day.The default value is: "0s".
""" ] ]
element RediscoveryBlacklistDuration {
[ a:documentation [ xml:lang="en" """
This attribute controls whether the configured time during which
diff --git a/etc/cyclonedds.xsd b/etc/cyclonedds.xsd
index cca1c3c429..8214bf13f8 100644
--- a/etc/cyclonedds.xsd
+++ b/etc/cyclonedds.xsd
@@ -1475,7 +1475,7 @@ is therefore recommended to set it to at least several seconds.</p>
<p>Valid values are finite durations with an explicit unit or the keyword
'inf' for infinity. Recognised units: ns, us, ms, s, min, hr,
-day.</p><p>The default value is: "10s".</p>
+day.</p><p>The default value is: "0s".</p>
diff --git a/src/core/ddsi/src/q_config.c b/src/core/ddsi/src/q_config.c
index 6808f3131a..6bfb3de766 100644
--- a/src/core/ddsi/src/q_config.c
+++ b/src/core/ddsi/src/q_config.c
@@ -728,7 +728,7 @@ static const struct cfgelem internal_cfgelems[] = {
BLURB("Do not use.
") },
{ LEAF("SendAsync"), 1, "false", ABSOFF(xpack_send_async), 0, uf_boolean, 0, pf_boolean,
BLURB("This element controls whether the actual sending of packets occurs on the same thread that prepares them, or is done asynchronously by another thread.
") },
- { LEAF_W_ATTRS("RediscoveryBlacklistDuration", rediscovery_blacklist_duration_attrs), 1, "10s", ABSOFF(prune_deleted_ppant.delay), 0, uf_duration_inf, 0, pf_duration,
+ { LEAF_W_ATTRS("RediscoveryBlacklistDuration", rediscovery_blacklist_duration_attrs), 1, "0s", ABSOFF(prune_deleted_ppant.delay), 0, uf_duration_inf, 0, pf_duration,
BLURB("This element controls for how long a remote participant that was previously deleted will remain on a blacklist to prevent rediscovery, giving the software on a node time to perform any cleanup actions it needs to do. To some extent this delay is required internally by DDSI2E, but in the default configuration with the 'enforce' attribute set to false, DDSI2E will reallow rediscovery as soon as it has cleared its internal administration. Setting it to too small a value may result in the entry being pruned from the blacklist before DDSI2E is ready, it is therefore recommended to set it to at least several seconds.
") },
{ LEAF_W_ATTRS("MultipleReceiveThreads", multiple_recv_threads_attrs), 1, "default", ABSOFF(multiple_recv_threads), 0, uf_boolean_default, 0, pf_boolean_default,
BLURB("This element controls whether all traffic is handled by a single receive thread (false) or whether multiple receive threads may be used to improve latency (true). By default it is disabled on Windows because it appears that one cannot count on being able to send packets to oneself, which is necessary to stop the thread during shutdown. Currently multiple receive threads are only used for connectionless transport (e.g., UDP) and ManySocketsMode not set to single (the default).
") },
From 4101cd02e09910cfc1a495bfdd08264238a43222 Mon Sep 17 00:00:00 2001
From: Erik Boasson
Date: Mon, 11 May 2020 16:00:45 +0200
Subject: [PATCH 23/36] Add DDS_STATUS_ID_MAX for ranging over status ids
Signed-off-by: Erik Boasson
---
src/core/ddsc/include/dds/dds.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/core/ddsc/include/dds/dds.h b/src/core/ddsc/include/dds/dds.h
index 8383b06842..2a63fbb487 100644
--- a/src/core/ddsc/include/dds/dds.h
+++ b/src/core/ddsc/include/dds/dds.h
@@ -90,6 +90,7 @@ typedef enum dds_status_id {
DDS_PUBLICATION_MATCHED_STATUS_ID,
DDS_SUBSCRIPTION_MATCHED_STATUS_ID
} dds_status_id_t;
+#define DDS_STATUS_ID_MAX (DDS_SUBSCRIPTION_MATCHED_STATUS_ID)
/** Another topic exists with the same name but with different characteristics. */
#define DDS_INCONSISTENT_TOPIC_STATUS (1u << DDS_INCONSISTENT_TOPIC_STATUS_ID)
From 1494d1185ff86cd2a6bdf18b075bf0c0695b9939 Mon Sep 17 00:00:00 2001
From: Erik Boasson
Date: Mon, 11 May 2020 16:01:48 +0200
Subject: [PATCH 24/36] Merging listeners: only when source is set
This leaves the argument pointer in the destination unchanged, rather
than resetting it to an irrelevant value.
Signed-off-by: Erik Boasson
---
src/core/ddsc/src/dds_listener.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/src/core/ddsc/src/dds_listener.c b/src/core/ddsc/src/dds_listener.c
index eee9ef65ab..ae71ec97fe 100644
--- a/src/core/ddsc/src/dds_listener.c
+++ b/src/core/ddsc/src/dds_listener.c
@@ -90,8 +90,7 @@ void dds_listener_copy(dds_listener_t * __restrict dst, const dds_listener_t * _
static bool dds_combine_listener_merge (uint32_t inherited, void (*dst)(void), void (*src)(void))
{
(void)inherited;
- (void)src;
- return dst == 0;
+ return dst == 0 && src != 0;
}
static bool dds_combine_listener_override_inherited (uint32_t inherited, void (*dst)(void), void (*src)(void))
From f5fd5e6d16e784eae287380a0a0c1a740309688a Mon Sep 17 00:00:00 2001
From: Erik Boasson
Date: Thu, 7 May 2020 09:36:20 +0200
Subject: [PATCH 25/36] Refactor oneliner listener tests
The code for executing one-liner tests might be more generally useful.
Signed-off-by: Erik Boasson
---
src/core/ddsc/tests/CMakeLists.txt | 4 +-
src/core/ddsc/tests/listener.c | 969 ++---------
src/core/ddsc/tests/test_oneliner.c | 1780 +++++++++++++++++++++
src/core/ddsc/tests/test_oneliner.h | 326 ++++
src/core/ddsi/include/dds/ddsi/q_entity.h | 2 +-
5 files changed, 2200 insertions(+), 881 deletions(-)
create mode 100644 src/core/ddsc/tests/test_oneliner.c
create mode 100644 src/core/ddsc/tests/test_oneliner.h
diff --git a/src/core/ddsc/tests/CMakeLists.txt b/src/core/ddsc/tests/CMakeLists.txt
index 0942ce3bc5..bde5eae3a0 100644
--- a/src/core/ddsc/tests/CMakeLists.txt
+++ b/src/core/ddsc/tests/CMakeLists.txt
@@ -59,7 +59,9 @@ set(ddsc_test_sources
"write_various_types.c"
"writer.c"
"test_common.c"
- "test_common.h")
+ "test_common.h"
+ "test_oneliner.c"
+ "test_oneliner.h")
if(ENABLE_LIFESPAN)
list(APPEND ddsc_test_sources "lifespan.c")
diff --git a/src/core/ddsc/tests/listener.c b/src/core/ddsc/tests/listener.c
index 6738cf76e4..2bd411202d 100644
--- a/src/core/ddsc/tests/listener.c
+++ b/src/core/ddsc/tests/listener.c
@@ -18,23 +18,14 @@
#include "dds/ddsrt/string.h"
#include "dds/ddsrt/environ.h"
#include "test_common.h"
-
-static ddsrt_mutex_t g_mutex;
-static ddsrt_cond_t g_cond;
-static uint32_t cb_called;
-static dds_entity_t cb_topic, cb_writer, cb_reader, cb_subscriber;
+#include "test_oneliner.h"
#define DEFINE_STATUS_CALLBACK(name, NAME, kind) \
- static dds_##name##_status_t cb_##name##_status; \
static void name##_cb (dds_entity_t kind, const dds_##name##_status_t status, void *arg) \
{ \
(void) arg; \
- ddsrt_mutex_lock (&g_mutex); \
- cb_##kind = kind; \
- cb_##name##_status = status; \
- cb_called |= DDS_##NAME##_STATUS; \
- ddsrt_cond_broadcast (&g_cond); \
- ddsrt_mutex_unlock (&g_mutex); \
+ (void) kind; \
+ (void) status; \
}
DEFINE_STATUS_CALLBACK (inconsistent_topic, INCONSISTENT_TOPIC, topic)
@@ -51,48 +42,14 @@ DEFINE_STATUS_CALLBACK (subscription_matched, SUBSCRIPTION_MATCHED, reader)
static void data_on_readers_cb (dds_entity_t subscriber, void *arg)
{
+ (void) subscriber;
(void) arg;
- ddsrt_mutex_lock (&g_mutex);
- cb_subscriber = subscriber;
- cb_called |= DDS_DATA_ON_READERS_STATUS;
- ddsrt_cond_broadcast (&g_cond);
- ddsrt_mutex_unlock (&g_mutex);
}
static void data_available_cb (dds_entity_t reader, void *arg)
{
- (void)arg;
- ddsrt_mutex_lock (&g_mutex);
- cb_reader = reader;
- cb_called |= DDS_DATA_AVAILABLE_STATUS;
- ddsrt_cond_broadcast (&g_cond);
- ddsrt_mutex_unlock (&g_mutex);
-}
-
-static void dummy_data_on_readers_cb (dds_entity_t subscriber, void *arg)
-{
- (void)subscriber;
- (void)arg;
-}
-
-static void dummy_data_available_cb (dds_entity_t reader, void *arg)
-{
- (void)reader;
- (void)arg;
-}
-
-static void dummy_subscription_matched_cb (dds_entity_t reader, const dds_subscription_matched_status_t status, void *arg)
-{
- (void)reader;
- (void)status;
- (void)arg;
-}
-
-static void dummy_liveliness_changed_cb (dds_entity_t reader, const dds_liveliness_changed_status_t status, void *arg)
-{
- (void)reader;
- (void)status;
- (void)arg;
+ (void) reader;
+ (void) arg;
}
static void dummy_cb (void)
@@ -303,796 +260,7 @@ CU_Test(ddsc_listener, getters_setters)
#undef ASSERT_CALLBACK_EQUAL
-/**************************************************
- **** ****
- **** programmable listener checker ****
- **** ****
- **************************************************/
-
-// These had better match the corresponding type definitions!
-// n uint32_t ...count
-// c int32_t ...count_change
-// I instance handle of a data instance
-// P uint32_t QoS policy ID
-// E instance handle of an entity
-// R sample_rejected_status_kind
-static const struct {
- size_t size; // size of status struct
- const char *desc; // description of status struct
- uint32_t mask; // status mask, bit in "cb_called"
- const dds_entity_t *cb_entity; // which cb_... entity to look at
- const void *cb_status; // cb_..._status to look at
-} lldesc[] = {
- { 0, NULL, DDS_DATA_AVAILABLE_STATUS, &cb_reader, NULL }, // data available
- { 0, NULL, DDS_DATA_ON_READERS_STATUS, &cb_subscriber, NULL }, // data on readers
- { sizeof (dds_inconsistent_topic_status_t), "nc", DDS_INCONSISTENT_TOPIC_STATUS, &cb_topic, &cb_inconsistent_topic_status },
- { sizeof (dds_liveliness_changed_status_t), "nnccE", DDS_LIVELINESS_CHANGED_STATUS, &cb_reader, &cb_liveliness_changed_status },
- { sizeof (dds_liveliness_lost_status_t), "nc", DDS_LIVELINESS_LOST_STATUS, &cb_writer, &cb_liveliness_lost_status },
- { sizeof (dds_offered_deadline_missed_status_t), "ncI", DDS_OFFERED_DEADLINE_MISSED_STATUS, &cb_writer, &cb_offered_deadline_missed_status },
- { sizeof (dds_offered_incompatible_qos_status_t), "ncP", DDS_OFFERED_INCOMPATIBLE_QOS_STATUS, &cb_writer, &cb_offered_incompatible_qos_status },
- { sizeof (dds_publication_matched_status_t), "ncncE", DDS_PUBLICATION_MATCHED_STATUS, &cb_writer, &cb_publication_matched_status },
- { sizeof (dds_requested_deadline_missed_status_t), "ncI", DDS_REQUESTED_DEADLINE_MISSED_STATUS, &cb_reader, &cb_requested_deadline_missed_status },
- { sizeof (dds_requested_incompatible_qos_status_t), "ncP", DDS_REQUESTED_INCOMPATIBLE_QOS_STATUS, &cb_reader, &cb_requested_incompatible_qos_status },
- { sizeof (dds_sample_lost_status_t), "nc", DDS_SAMPLE_LOST_STATUS, &cb_reader, &cb_sample_lost_status },
- { sizeof (dds_sample_rejected_status_t), "ncRI", DDS_SAMPLE_REJECTED_STATUS, &cb_reader, &cb_sample_rejected_status },
- { sizeof (dds_subscription_matched_status_t), "ncncE", DDS_SUBSCRIPTION_MATCHED_STATUS, &cb_reader, &cb_subscription_matched_status }
-};
-
-static const void *advance (const void *status, size_t *off, char code)
-{
-#define alignof(type_) offsetof (struct { char c; type_ d; }, d)
- size_t align = 1, size = 1;
- switch (code)
- {
- case 'n': case 'c': case 'P':
- align = alignof (uint32_t); size = sizeof (uint32_t);
- break;
- case 'E': case 'I':
- align = alignof (dds_instance_handle_t); size = sizeof (dds_instance_handle_t);
- break;
- case 'R':
- align = alignof (dds_sample_rejected_status_kind); size = sizeof (dds_sample_rejected_status_kind);
- break;
- default:
- abort ();
- }
-#undef alignof
- *off = (*off + align - 1) & ~(align - 1);
- const void *p = (const char *) status + *off;
- *off += size;
- return p;
-}
-
-static void get_status (int ll, dds_entity_t ent, void *status)
-{
- dds_return_t ret;
- switch (ll)
- {
- case 2: ret = dds_get_inconsistent_topic_status (ent, status); break;
- case 3: ret = dds_get_liveliness_changed_status (ent, status); break;
- case 4: ret = dds_get_liveliness_lost_status (ent, status); break;
- case 5: ret = dds_get_offered_deadline_missed_status (ent, status); break;
- case 6: ret = dds_get_offered_incompatible_qos_status (ent, status); break;
- case 7: ret = dds_get_publication_matched_status (ent, status); break;
- case 8: ret = dds_get_requested_deadline_missed_status (ent, status); break;
- case 9: ret = dds_get_requested_incompatible_qos_status (ent, status); break;
- case 10: ret = dds_get_sample_lost_status (ent, status); break;
- case 11: ret = dds_get_sample_rejected_status (ent, status); break;
- case 12: ret = dds_get_subscription_matched_status (ent, status); break;
- default: abort ();
- }
- CU_ASSERT_FATAL (ret == 0);
-}
-
-static void assert_status_change_fields_are_0 (int ll, dds_entity_t ent)
-{
- if (lldesc[ll].desc)
- {
- const char *d = lldesc[ll].desc;
- void *status = malloc (lldesc[ll].size);
- get_status (ll, ent, status);
- size_t off = 0;
- while (*d)
- {
- const uint32_t *p = advance (status, &off, *d);
- if (*d == 'c')
- CU_ASSERT_FATAL (*p == 0);
- d++;
- }
- assert (off <= lldesc[ll].size);
- free (status);
- }
-}
-
-static int getentity (const char *tok, bool *isbang, bool *ishash)
-{
- static const char *known = "PRWrstwxy";
- const char *p;
- if (isbang)
- *isbang = false;
- if (ishash)
- *ishash = false;
- if ((p = strchr (known, *tok)) == NULL)
- return -1;
- int ent = (int) (p - known);
- if (*++tok == 0)
- return ent;
- if (*tok == '\'')
- {
- ent += (int) strlen (known);
- tok++;
- }
- while (*tok == '!' || *tok == '#')
- {
- if (strchr (known + 3, *p) == NULL)
- return -1; // only readers, writers
- if (*tok == '!' && isbang)
- *isbang = true;
- else if (*tok == '#' && ishash)
- *ishash = true;
- tok++;
- }
- return (*tok == 0) ? ent : -1;
-}
-
-static int getlistener (const char *tok, bool *isbang)
-{
- // note: sort order is on full name (so sample rejected precedes subscription matched)
- static const char *ls[] = {
- "da", "dor", "it", "lc", "ll", "odm", "oiq", "pm", "rdm", "riq", "sl", "sr", "sm"
- };
- if (isbang)
- *isbang = false;
- for (size_t i = 0; i < sizeof (ls) / sizeof (*ls); i++)
- {
- size_t n = strlen (ls[i]);
- if (strncmp (tok, ls[i], n) == 0 && (tok[n] == 0 || tok[n+1] == ','))
- {
- if (isbang)
- *isbang = (tok[n] == '!');
- return (int) i;
- }
- }
- return -1;
-}
-
-struct ents {
- dds_entity_t es[2 * 9];
- dds_entity_t tps[2];
- dds_entity_t doms[2];
- dds_instance_handle_t esi[2 * 9];
- // built-in topic readers for cross-referencing instance handles
- dds_entity_t pubrd[2];
- dds_entity_t subrd[2];
-};
-
-static void make_participant (struct ents *es, const char *topicname, int ent, const dds_qos_t *qos, dds_listener_t *list)
-{
- const dds_domainid_t domid = (ent < 9) ? 0 : 1;
- char *conf = ddsrt_expand_envvars ("${CYCLONEDDS_URI}${CYCLONEDDS_URI:+,}0", domid);
- printf ("create domain %"PRIu32, domid);
- fflush (stdout);
- es->doms[domid] = dds_create_domain (domid, conf);
- CU_ASSERT_FATAL (es->doms[domid] > 0);
- ddsrt_free (conf);
- printf (" create participant P%s", (ent < 9) ? "" : "'");
- fflush (stdout);
- es->es[ent] = dds_create_participant (domid, NULL, list);
- CU_ASSERT_FATAL (es->es[ent] > 0);
- es->tps[domid] = dds_create_topic (es->es[ent], &Space_Type1_desc, topicname, qos, NULL);
- CU_ASSERT_FATAL (es->tps[domid] > 0);
-
- // Create the built-in topic readers with a dummy listener to avoid any event (data available comes to mind)
- // from propagating to the normal data available listener, in case it has been set on the participant.
- //
- // - dummy_cb aborts when it is invoked, but all reader-related listeners that can possibly trigger are set
- // separately (incompatible qos, deadline missed, sample lost and sample rejected are all impossible by
- // construction)
- // - regarding data_on_readers: Cyclone handles listeners installed on an ancestor by *inheriting* them,
- // rather than by walking up ancestor chain. Setting data_on_readers on the reader therefore overrides the
- // listener set on the subscriber. It is a nice feature!
- dds_listener_t *dummylist = dds_create_listener (NULL);
- set_all_const (dummylist, dummy_cb);
- dds_lset_data_available (dummylist, dummy_data_available_cb);
- dds_lset_data_on_readers (dummylist, dummy_data_on_readers_cb);
- dds_lset_subscription_matched (dummylist, dummy_subscription_matched_cb);
- dds_lset_liveliness_changed (dummylist, dummy_liveliness_changed_cb);
- es->pubrd[domid] = dds_create_reader (es->es[ent], DDS_BUILTIN_TOPIC_DCPSPUBLICATION, NULL, dummylist);
- CU_ASSERT_FATAL (es->pubrd[domid] > 0);
- es->subrd[domid] = dds_create_reader (es->es[ent], DDS_BUILTIN_TOPIC_DCPSSUBSCRIPTION, NULL, dummylist);
- CU_ASSERT_FATAL (es->subrd[domid] > 0);
- dds_delete_listener (dummylist);
- printf ("pubrd %"PRId32" subrd %"PRId32" sub %"PRId32"\n", es->pubrd[domid], es->subrd[domid], dds_get_parent (es->pubrd[domid]));
-}
-
-static void make_entity1 (struct ents *es, const char *topicname, int ent, bool isbang, bool ishash, const dds_qos_t *qos, dds_qos_t *rwqos, dds_listener_t *list)
-{
- dds_return_t ret;
- switch (ent)
- {
- case 0: case 9:
- make_participant (es, topicname, ent, qos, list);
- break;
- case 1: case 10:
- if (es->es[ent-1] == 0)
- {
- printf ("[");
- make_entity1 (es, topicname, ent-1, false, false, qos, rwqos, NULL);
- printf ("] ");
- }
- printf ("create subscriber R%s", (ent < 9) ? "" : "'");
- fflush (stdout);
- es->es[ent] = dds_create_subscriber (es->es[ent-1], NULL, list);
- break;
- case 2: case 11:
- if (es->es[ent-2] == 0)
- {
- printf ("[");
- make_entity1 (es, topicname, ent-2, false, false, qos, rwqos, NULL);
- printf ("] ");
- }
- printf ("create publisher W%s", (ent < 9) ? "" : "'");
- fflush (stdout);
- es->es[ent] = dds_create_publisher (es->es[ent-2], NULL, list);
- break;
- case 3: case 4: case 5: case 12: case 13: case 14:
- if (es->es[ent < 9 ? 1 : 10] == 0)
- {
- printf ("[");
- make_entity1 (es, topicname, ent < 9 ? 1 : 10, false, false, qos, rwqos, NULL);
- printf ("] ");
- }
- printf ("create %s reader %c%s", isbang ? "best-effort" : "reliable", 'r' + (ent < 9 ? ent-3 : ent-12), (ent < 9) ? "" : "'");
- fflush (stdout);
- dds_reset_qos (rwqos);
- if (isbang)
- dds_qset_reliability (rwqos, DDS_RELIABILITY_BEST_EFFORT, DDS_MSECS (100));
- if (ishash)
- dds_qset_resource_limits (rwqos, 1, DDS_LENGTH_UNLIMITED, DDS_LENGTH_UNLIMITED);
- es->es[ent] = dds_create_reader (es->es[ent < 9 ? 1 : 10], es->tps[ent < 9 ? 0 : 1], rwqos, list);
- break;
- case 6: case 7: case 8: case 15: case 16: case 17:
- if (es->es[ent < 9 ? 2 : 11] == 0)
- {
- printf ("[");
- make_entity1 (es, topicname, ent < 9 ? 2 : 11, false, false, qos, rwqos, NULL);
- printf ("] ");
- }
- printf ("create %s writer %c%s", isbang ? "best-effort" : "reliable", 'w' + (ent < 9 ? ent-6 : ent-15), (ent < 9) ? "" : "'");
- fflush (stdout);
- dds_reset_qos (rwqos);
- if (isbang)
- dds_qset_reliability (rwqos, DDS_RELIABILITY_BEST_EFFORT, DDS_MSECS (100));
- if (ishash)
- dds_qset_resource_limits (rwqos, 1, DDS_LENGTH_UNLIMITED, DDS_LENGTH_UNLIMITED);
- es->es[ent] = dds_create_writer (es->es[ent < 9 ? 2 : 11], es->tps[ent < 9 ? 0 : 1], rwqos, list);
- break;
- default:
- abort ();
- }
- printf (" = %"PRId32, es->es[ent]);
- fflush (stdout);
- CU_ASSERT_FATAL (es->es[ent] > 0);
- ret = dds_get_instance_handle (es->es[ent], &es->esi[ent]);
- //printf (" %"PRIx64, es->esi[ent]);
- //fflush (stdout);
- CU_ASSERT_FATAL (ret == 0);
-}
-
-static void make_entity (struct ents *es, const char *topicname, int ent, bool isbang, bool ishash, const dds_qos_t *qos, dds_qos_t *rwqos, dds_listener_t *list)
-{
- make_entity1 (es, topicname, ent, isbang, ishash, qos, rwqos, list);
- printf ("\n");
-}
-
-static char *strsep_noempty (char **cursor, const char *sep)
-{
- char *tok;
- while ((tok = ddsrt_strsep (cursor, sep)) != NULL && *tok == 0) { }
- return tok;
-}
-
-static dds_instance_handle_t lookup_insthandle (const struct ents *es, int ent, int ent1)
-{
- // if both are in the same domain, it's easy
- if (ent / 9 == ent1 / 9)
- return es->esi[ent1];
- else
- {
- // if they aren't ... find GUID from instance handle in the one domain,
- // then find instance handle for GUID in the other
- dds_entity_t rd1 = 0, rd2 = 0;
- switch (ent1)
- {
- case 3: case 4: case 5: rd1 = es->subrd[0]; rd2 = es->subrd[1]; break;
- case 12: case 13: case 14: rd1 = es->subrd[1]; rd2 = es->subrd[0]; break;
- case 6: case 7: case 8: rd1 = es->pubrd[0]; rd2 = es->pubrd[1]; break;
- case 15: case 16: case 17: rd1 = es->pubrd[1]; rd2 = es->pubrd[0]; break;
- default: abort ();
- }
-
- dds_return_t ret;
- dds_builtintopic_endpoint_t keysample;
- //printf ("(in %"PRId32" %"PRIx64" -> ", rd1, es->esi[ent1]);
- //fflush (stdout);
- ret = dds_instance_get_key (rd1, es->esi[ent1], &keysample);
- CU_ASSERT_FATAL (ret == 0);
- // In principle, only key fields are set in sample returned by get_key;
- // in the case of a built-in topic that is extended to the participant
- // key. The qos and topic/type names should not be set, and there is no
- // (therefore) memory allocated for the sample.
- CU_ASSERT_FATAL (keysample.qos == NULL);
- CU_ASSERT_FATAL (keysample.topic_name == NULL);
- CU_ASSERT_FATAL (keysample.type_name == NULL);
- //for (size_t j = 0; j < sizeof (keysample.key.v); j++)
- // printf ("%s%02x", (j > 0 && j % 4 == 0) ? ":" : "", keysample.key.v[j]);
- const dds_instance_handle_t ih = dds_lookup_instance (rd2, &keysample);
- CU_ASSERT_FATAL (ih != 0);
- //printf (" -> %"PRIx64")", ih);
- //fflush (stdout);
- return ih;
- }
-}
-
-static void checkstatus (int ll, const struct ents *es, int ent, const char *args, const void *status)
-{
- DDSRT_WARNING_MSVC_OFF(4996); // use of sscanf triggers a warning
- if (*args == 0)
- return;
- if (*args++ != '(')
- abort ();
- assert (lldesc[ll].desc != NULL);
- const char *d = lldesc[ll].desc;
- const char *sep = "(";
- size_t off = 0;
- while (*d)
- {
- const void *p = advance (status, &off, *d);
- char str[32];
- unsigned u;
- int i, pos = -1;
- switch (*d)
- {
- case 'n':
- if (sscanf (args, "%u%n", &u, &pos) != 1 || (args[pos] != ',' && args[pos] != ')'))
- abort ();
- printf ("%s%"PRIu32" %u", sep, *(uint32_t *)p, u); fflush (stdout);
- CU_ASSERT_FATAL (*(uint32_t *)p == u);
- break;
- case 'c':
- if (sscanf (args, "%d%n", &i, &pos) != 1 || (args[pos] != ',' && args[pos] != ')'))
- abort ();
- printf ("%s%"PRId32" %d", sep, *(int32_t *)p, i); fflush (stdout);
- CU_ASSERT_FATAL (*(int32_t *)p == i);
- break;
- case 'P': // policy id: currently fixed at reliability
- pos = -1; // not actually consuming an argument
- printf ("%s%"PRIu32" %d", sep, *(uint32_t *)p, (int) DDS_RELIABILITY_QOS_POLICY_ID); fflush (stdout);
- CU_ASSERT_FATAL (*(uint32_t *)p == (uint32_t) DDS_RELIABILITY_QOS_POLICY_ID);
- break;
- case 'R':
- if (sscanf (args, "%31[^,)]%n", str, &pos) != 1 || (args[pos] != ',' && args[pos] != ')'))
- abort ();
- if (strcmp (str, "i") == 0)
- i = (int) DDS_REJECTED_BY_INSTANCES_LIMIT;
- else if (strcmp (str, "s") == 0)
- i = (int) DDS_REJECTED_BY_SAMPLES_LIMIT;
- else if (strcmp (str, "spi") == 0)
- i = (int) DDS_REJECTED_BY_SAMPLES_PER_INSTANCE_LIMIT;
- else
- abort ();
- printf ("%s%d %d", sep, (int) *(dds_sample_rejected_status_kind *)p, i); fflush (stdout);
- CU_ASSERT_FATAL (*(dds_sample_rejected_status_kind *)p == (dds_sample_rejected_status_kind) i);
- break;
- case 'I': // instance handle is too complicated
- pos = -1; // not actually consuming an argument
- break;
- case 'E': {
- int ent1 = -1;
- dds_instance_handle_t esi1 = 0;
- if (sscanf (args, "%31[^,)]%n", str, &pos) != 1 || (args[pos] != ',' && args[pos] != ')'))
- abort ();
- if (strcmp (str, "*") != 0 && (ent1 = getentity (str, NULL, NULL)) < 0)
- abort ();
- if (ent1 != -1)
- esi1 = lookup_insthandle (es, ent, ent1);
- printf ("%s%"PRIx64" %"PRIx64, sep, *(dds_instance_handle_t *)p, esi1); fflush (stdout);
- CU_ASSERT_FATAL (ent1 == -1 || *(dds_instance_handle_t *)p == esi1);
- break;
- }
- default: abort ();
- }
- args += pos + 1;
- sep = ", ";
- d++;
- }
- printf (")");
- assert (*args == 0);
- assert (off <= lldesc[ll].size);
- DDSRT_WARNING_MSVC_ON(4996);
-}
-
-/** @brief run a "test" consisting of a sequence of simplish operations
- *
- * This operation takes a test description, really a program in a bizarre syntax, and executes it. Any failures,
- * be it because of error codes coming out of the Cyclone calls or expected values being wrong cause it to fail
- * the test via CU_ASSERT_FATAL. While it is doing this, it outputs the test steps to stdout including some
- * actual values. An invalid program is mostly reported by calling abort(). It is geared towards checking for
- * listener invocations and the effects on statuses.
- *
- * Entities in play:
- *
- * - participants: P P'
- * - subscribers: R R'
- * - publishers: W W'
- * - readers: r s t r' s' t'
- * - writers: w x y w' x' y'
- *
- * The unprimed ones exist in domain 0, the primed ones in domain 1 (but configured such that it talks to
- * domain 0), so that network-related listener invocations can be checked as well.
- *
- * The first mention of an entity creates it as well as its ancestors. Implicitly created ancestors always have
- * standard QoS and have no listeners. There is one topic that is created implicitly when the participant is
- * created.
- *
- * Standard QoS is: default + reliable (100ms), by-source-timestamp, keep-all.
- * The QoS of a reader/writer can be altered at the first mention of it by suffixing its name with "!" and/or "#"
- * (the apostrophe is part of the name, so w#! or r'! are valid). Those suffixes are ignored if the entity
- * already exists.
- *
- * A program consists of a sequence of operations separated by whitespace, ';' or '/' (there is no meaning to the
- * separators, they exist to allow visual grouping):
- *
- * PROGRAM ::= (OP (\s+|[/;])*)*
- *
- * OP ::= (LISTENER)* ENTITY-NAME
- * if entity ENTITY-NAME does not exist:
- * creates the entity with the given listeners installed
- * else
- * changes the entity's listeners to the specified ones
- * (see above for the valid ENTITY-NAMEs)
- * | -ENTITY-NAME
- * deletes the specified entity
- * | WRITE-LIKE[fail][@DT] KEY
- * writes/disposes/unregisters key KEY (an integer), if "fail" is appended, the
- * expectation is that it fails with a timeout, if @DT is appended, the timestamp is the
- * start time of the test + s rather than the current time; DT is a floating-point
- * number
- * | READ-LIKE[(A,B))]
- * reads/takes at most 10 samples, counting the number of valid and invalid samples seen
- * and checking it against A and B if given
- * | ?LISTENER[(ARGS)]
- * waits until the specified listener has been invoked on using a flag set
- * by the listener function, resets the flag and verifies that neither the entity status
- * bit nor the "change" fields in the various statuses were set
- * ARGS is used to check the status argument most recently passed to the listener:
- * it (A,B) verifies count and change match A and B, policy matches RELIABILITY
- * lc (A,B,C,D,E) verifies that alive and not-alive counts match A and B, that
- * alive and not-alive changes match C and D and that the last handle matches
- * E if an entity name (ignored if E = "*")
- * ll (A,B) verifies count and change match A and B
- * odm (A,B) verifies count and change match A and B, last handle is ignored
- * oiq (A,B) verifies that total count and change match A and B and that the
- * mismatching QoS is reliability (the only one that can for now)
- * pm (A,B,C,D,E) verifies that total count and change match A and B, that
- * current count and change match C and D and that the last handle matches E
- * if an entity name (ignored if E = "*")
- * rdm see odm
- * riq see oiq
- * sl (A,B) verifies that total count and change match A and B
- * sr (A,B,C) verifies total count and change match A and B, and that the reason
- * matches C (one of "s" for samples, "i" for instances, "spi" for samples
- * per instance)
- * sm see pm
- * | ?!LISTENER
- * (not listener) tests that LISTENER has not been invoked since last reset
- * | sleep D
- * delay program execution for D s (D is a floating-point number)
- * WRITE-LIKE ::= wr write
- * | wrdisp write-dispose
- * | disp dispose
- * | unreg unregister
- * READ-LIKE ::= read dds_read (so any state)
- * | take dds_take (so any state)
- * LISTENER ::= da data available (acts on a reader)
- * | dor data on readers (acts on a subcsriber)
- * | it incompatible topic (acts on a topic)
- * | lc liveliness changed (acts on a reader)
- * | ll liveliness lost (acts on a writer)
- * | odm offered deadline missed (acts on a writer)
- * | oiq offered incompatible QoS (acts on a writer)
- * | pm publication matched (acts on a writer)
- * | rdm requested deadline missed (acts on a reader)
- * | riq requested incompatible QoS (acts on a reader)
- * | sl sample lost (acts on a reader)
- * | sr sample rejected (acts on a reader)
- * | sm subscription matched (acts on a reader)
- *
- * All entities share the listeners with their global state. Only the latest invocation is visible.
- *
- * @param[in] ops Program to execute.
- */
-static void dotest (const char *ops)
-{
- DDSRT_WARNING_MSVC_OFF(4996); // use of sscanf triggers a warning
- static const char *sep = " /;\n\t\r\v";
- char *opscopy = ddsrt_strdup (ops), *cursor = opscopy, *tok;
- struct ents es;
- dds_return_t ret;
- Space_Type1 sample;
- char topicname[100];
- dds_qos_t *qos = dds_create_qos (), *rwqos = dds_create_qos ();
- dds_listener_t *list = dds_create_listener (NULL);
- const dds_time_t tref = dds_time ();
- CU_ASSERT_FATAL (qos != NULL);
- CU_ASSERT_FATAL (rwqos != NULL);
- CU_ASSERT_FATAL (list != NULL);
- dds_qset_reliability (qos, DDS_RELIABILITY_RELIABLE, DDS_MSECS (100));
- dds_qset_destination_order (qos, DDS_DESTINATIONORDER_BY_SOURCE_TIMESTAMP);
- dds_qset_history (qos, DDS_HISTORY_KEEP_ALL, 0);
- memset (&es, 0, sizeof (es));
- memset (&sample, 0, sizeof (sample));
-
- ddsrt_mutex_init (&g_mutex);
- ddsrt_cond_init (&g_cond);
- ddsrt_mutex_lock (&g_mutex);
- cb_called = 0;
- ddsrt_mutex_unlock (&g_mutex);
-
- create_unique_topic_name ("ddsc_listener_test", topicname, 100);
- printf ("dotest: %s\n", ops);
- printf ("topic: %s\n", topicname);
- while ((tok = strsep_noempty (&cursor, sep)) != NULL)
- {
- int ent, ll;
- bool isbang, ishash;
- if ((ent = getentity (tok, &isbang, &ishash)) >= 0)
- {
- make_entity (&es, topicname, ent, isbang, ishash, qos, rwqos, NULL);
- }
- else if (*tok == '-' && (ent = getentity (tok + 1, NULL, NULL)) >= 0)
- {
- // delete deliberately leaves the instance handle in place for checking
- // the publication/subscription handle in subscription matched/publication
- // matched for a lost match
- printf ("delete %"PRId32"\n", es.es[ent]);
- ret = dds_delete (es.es[ent]);
- CU_ASSERT_FATAL (ret == 0);
- es.es[ent] = 0;
- }
- else if ((ll = getlistener (tok, &isbang)) >= 0)
- {
- printf ("set listener:");
- dds_reset_listener (list);
- do {
- printf (" %s", tok);
- switch (ll)
- {
- case 0: dds_lset_data_available (list, isbang ? 0 : data_available_cb); break;
- case 1: dds_lset_data_on_readers (list, isbang ? 0 : data_on_readers_cb); break;
- case 2: dds_lset_inconsistent_topic (list, isbang ? 0: inconsistent_topic_cb); break;
- case 3: dds_lset_liveliness_changed (list, isbang ? 0 : liveliness_changed_cb); break;
- case 4: dds_lset_liveliness_lost (list, isbang ? 0 : liveliness_lost_cb); break;
- case 5: dds_lset_offered_deadline_missed (list, isbang ? 0 : offered_deadline_missed_cb); break;
- case 6: dds_lset_offered_incompatible_qos (list, isbang ? 0 : offered_incompatible_qos_cb); break;
- case 7: dds_lset_publication_matched (list, isbang ? 0 : publication_matched_cb); break;
- case 8: dds_lset_requested_deadline_missed (list, isbang ? 0 : requested_deadline_missed_cb); break;
- case 9: dds_lset_requested_incompatible_qos (list, isbang ? 0 : requested_incompatible_qos_cb); break;
- case 10: dds_lset_sample_lost (list, isbang ? 0 : sample_lost_cb); break;
- case 11: dds_lset_sample_rejected (list, isbang ? 0 : sample_rejected_cb); break;
- case 12: dds_lset_subscription_matched (list, isbang ? 0 : subscription_matched_cb); break;
- default: abort ();
- }
- } while ((tok = strsep_noempty (&cursor, sep)) != NULL && (ll = getlistener (tok, &isbang)) >= 0);
- if (tok == NULL || (ent = getentity (tok, &isbang, &ishash)) < 0)
- abort ();
- if (es.es[ent] == 0)
- {
- printf (" for ");
- make_entity (&es, topicname, ent, isbang, ishash, qos, rwqos, list);
- }
- else
- {
- dds_listener_t *tmplist = dds_create_listener (NULL);
- CU_ASSERT_FATAL (tmplist != NULL);
- ret = dds_get_listener (es.es[ent], tmplist);
- CU_ASSERT_FATAL (ret == 0);
- dds_merge_listener (list, tmplist);
- dds_delete_listener (tmplist);
- printf (" on entity %"PRId32"\n", es.es[ent]);
- ret = dds_set_listener (es.es[ent], list);
- CU_ASSERT_FATAL (ret == 0);
- }
- }
- else if (strncmp (tok, "wr", 2) == 0 || strncmp (tok, "disp", 4) == 0 || strncmp (tok, "unreg", 5) == 0)
- {
- dds_return_t (*fn) (dds_entity_t wr, const void *sample, dds_time_t ts) = 0;
- double dt = 0.0;
- dds_time_t ts = dds_time ();
- char *cmd = tok;
- bool expectfail = false;
- int off, pos, key;
- if ((tok = strsep_noempty (&cursor, sep)) == NULL)
- abort ();
- if (sscanf (tok, "%d%n", &key, &pos) != 1 || tok[pos] != 0)
- abort ();
- if ((tok = strsep_noempty (&cursor, sep)) == NULL || (ent = getentity (tok, &isbang, &ishash)) < 0)
- abort ();
- if (es.es[ent] == 0)
- make_entity (&es, topicname, ent, isbang, ishash, qos, rwqos, NULL);
- switch (cmd[0])
- {
- case 'w':
- if (strncmp (cmd + 2, "disp", 4) == 0) {
- off = 6; fn = dds_writedispose_ts;
- } else {
- off = 2; fn = dds_write_ts;
- }
- break;
- case 'd': off = 4; fn = dds_dispose_ts; break;
- case 'u': off = 5; fn = dds_unregister_instance_ts; break;
- default: abort ();
- }
- if (strncmp (cmd + off, "fail", 4) == 0)
- {
- expectfail = true;
- off += 4;
- }
- if (cmd[off] == '@')
- {
- if (sscanf (cmd + off, "@%lf%n", &dt, &pos) != 1 || cmd[off + pos] != 0)
- abort ();
- ts = tref + (dds_time_t) (dt * 1e9);
- }
- sample.long_1 = key;
- printf ("entity %"PRId32": %*.*s@%"PRId64".%09"PRId64" %d\n", es.es[ent], off, off, cmd, ts / DDS_NSECS_IN_SEC, ts % DDS_NSECS_IN_SEC, key);
- ret = fn (es.es[ent], &sample, ts);
- if (expectfail) {
- CU_ASSERT_FATAL (ret == DDS_RETCODE_TIMEOUT);
- } else {
- CU_ASSERT_FATAL (ret == 0);
- }
- }
- else if (strncmp (tok, "take", 4) == 0 || strncmp(tok, "read", 4) == 0)
- {
- char *args = (tok[4] ? tok + 4 : NULL);
- int exp_nvalid = -1, exp_ninvalid = -1, pos;
- dds_return_t (*fn) (dds_entity_t, void **buf, dds_sample_info_t *, size_t, uint32_t);
- fn = (strncmp (tok, "take", 4) == 0) ? dds_take : dds_read;
- assert (args == NULL || *args == '(');
- if (args && (sscanf (args, "(%d,%d)%n", &exp_nvalid, &exp_ninvalid, &pos) != 2 || args[pos] != 0))
- abort ();
- if ((tok = strsep_noempty (&cursor, sep)) == NULL || (ent = getentity (tok, &isbang, &ishash)) < 0)
- abort ();
- if (es.es[ent] == 0)
- make_entity (&es, topicname, ent, isbang, ishash, qos, rwqos, NULL);
- printf ("entity %"PRId32": %s", es.es[ent], (fn == dds_take) ? "take" : "read");
- fflush (stdout);
- void *raw[10] = { NULL };
- dds_sample_info_t si[10];
- const uint32_t maxs = (uint32_t) (sizeof (raw) / sizeof (raw[0]));
- int count[2] = { 0, 0 };
- ret = fn (es.es[ent], raw, si, maxs, maxs);
- CU_ASSERT_FATAL (ret >= 0);
- for (int32_t i = 0; i < ret; i++)
- count[si[i].valid_data]++;
- ret = dds_return_loan (es.es[ent], raw, ret);
- CU_ASSERT_FATAL (ret == 0);
- printf (" valid %d %d invalid %d %d\n", count[1], exp_nvalid, count[0], exp_ninvalid);
- if (exp_nvalid >= 0)
- CU_ASSERT_FATAL (count[1] == exp_nvalid);
- if (exp_ninvalid >= 0)
- CU_ASSERT_FATAL (count[0] == exp_ninvalid);
- }
- else if (tok[0] == '?')
- {
- const bool expectclear = (tok[1] == '!');
- const char *llname = tok + (expectclear ? 2 : 1);
- char *checkargs;
- if ((checkargs = strchr (llname, '(')) != NULL)
- *checkargs = 0; // clear so getlistener groks the input
- if ((ll = getlistener (llname, NULL)) < 0)
- abort ();
- if (expectclear)
- {
- printf ("listener %s: check not called", llname);
- fflush (stdout);
- ddsrt_mutex_lock (&g_mutex);
- printf (" cb_called %"PRIx32" %s\n", cb_called, (cb_called & lldesc[ll].mask) == 0 ? "ok" : "fail");
- CU_ASSERT_FATAL ((cb_called & lldesc[ll].mask) == 0);
- ddsrt_mutex_unlock (&g_mutex);
- }
- else
- {
- bool signalled = true;
- uint32_t status;
- if ((tok = strsep_noempty (&cursor, sep)) == NULL || (ent = getentity (tok, &isbang, &ishash)) < 0)
- abort ();
- if (es.es[ent] == 0)
- make_entity (&es, topicname, ent, isbang, ishash, qos, rwqos, NULL);
- if ((size_t) ll >= sizeof (lldesc) / sizeof (*lldesc))
- abort ();
- printf ("listener %s: check called for entity %"PRId32, llname, es.es[ent]);
- fflush (stdout);
- ddsrt_mutex_lock (&g_mutex);
- while ((cb_called & lldesc[ll].mask) == 0 && signalled)
- signalled = ddsrt_cond_waitfor (&g_cond, &g_mutex, DDS_SECS (5));
- printf (" cb_called %"PRIx32" (%s)", cb_called, (cb_called & lldesc[ll].mask) != 0 ? "ok" : "fail");
- fflush (stdout);
- CU_ASSERT_FATAL ((cb_called & lldesc[ll].mask) != 0);
- printf (" cb_entity %"PRId32" %"PRId32" (%s)", *lldesc[ll].cb_entity, es.es[ent], (*lldesc[ll].cb_entity == es.es[ent]) ? "ok" : "fail");
- fflush (stdout);
- CU_ASSERT_FATAL (*lldesc[ll].cb_entity == es.es[ent]);
- if (!(es.doms[0] && es.doms[1]))
- {
- // FIXME: two domains: listener invocation happens on another thread and we can observe non-0 "change" fields
- // they get updated, listener gets invoked, then they get reset -- pretty sure it is allowed by the spec, but
- // not quite elegant
- assert_status_change_fields_are_0 (ll, es.es[ent]);
- }
- if (checkargs && lldesc[ll].cb_status)
- {
- *checkargs = '('; // restore ( so checkargs function gets a more sensible input
- checkstatus (ll, &es, ent, checkargs, lldesc[ll].cb_status);
- }
- printf ("\n");
- cb_called &= ~lldesc[ll].mask;
- ddsrt_mutex_unlock (&g_mutex);
- ret = dds_get_status_changes (es.es[ent], &status);
- CU_ASSERT_FATAL (ret == 0);
- CU_ASSERT_FATAL ((status & lldesc[ll].mask) == 0);
- }
- }
- else if (strcmp (tok, "sleep") == 0)
- {
- if ((tok = strsep_noempty (&cursor, sep)) == NULL)
- abort ();
- double d; int pos;
- if (sscanf (tok, "%lf%n", &d, &pos) != 1 || tok[pos] != 0)
- abort ();
- printf ("sleep %fs\n", d);
- dds_sleepfor ((dds_duration_t) (d * 1e9));
- }
- else
- {
- printf ("tok '%s': unrecognized\n", tok);
- abort ();
- }
- }
-
- dds_delete_listener (list);
- dds_delete_qos (rwqos);
- dds_delete_qos (qos);
- // prevent any listeners from being invoked so we can safely delete the
- // mutex and the condition variable -- must do this going down the
- // hierarchy, or listeners may remain set through inheritance
- for (size_t i = 0; i < sizeof (es.es) / sizeof (es.es[0]); i++)
- {
- if (es.es[i])
- {
- ret = dds_set_listener (es.es[i], NULL);
- CU_ASSERT_FATAL (ret == 0);
- }
- }
- ddsrt_mutex_destroy (&g_mutex);
- ddsrt_cond_destroy (&g_cond);
- for (size_t i = 0; i < sizeof (es.doms) / sizeof (es.doms[0]); i++)
- {
- if (es.doms[i])
- {
- ret = dds_delete (es.doms[i]);
- CU_ASSERT_FATAL (ret == 0);
- }
- }
- ddsrt_free (opscopy);
- DDSRT_WARNING_MSVC_ON(4996);
-}
-
-/**************************************************
- **** ****
- **** listener invocation checks ****
- **** ****
- **************************************************/
+#define dotest(ops) CU_ASSERT_FATAL (test_oneliner (ops) > 0)
CU_Test (ddsc_listener, propagation)
{
@@ -1102,10 +270,10 @@ CU_Test (ddsc_listener, propagation)
// for it on the reader should prevent that from happening
dotest ("da dor lc sm P ; ?!dor ?!da ?!sm ?!lc");
// writing data should trigger data-available unless data-on-readers is set
- dotest ("da lc sm P ; r ; wr 0 w ; ?da r ?sm r ?lc r");
- dotest ("da dor lc sm P ; r ; wr 0 w ; ?!da ; ?dor R ?sm r ?lc r");
+ dotest ("da lc sm P ; r ; wr w 0 ; ?da r ?sm r ?lc r");
+ dotest ("da dor lc sm P ; r ; wr w 0 ; ?!da ; ?dor R ?sm r ?lc r");
// setting listeners after entity creation should work, too
- dotest ("P W R ; dor P pm W sm R ; r w ; ?sm r ?pm w ; wr 0 w ; ?dor R ; ?!da");
+ dotest ("P W R ; dor P pm W sm R ; r w ; ?sm r ?pm w ; wr w 0 ; ?dor R ; ?!da");
}
CU_Test (ddsc_listener, matched)
@@ -1116,6 +284,47 @@ CU_Test (ddsc_listener, matched)
// across the network it should work just as well (matching happens on different threads for
// remote & local entity creation, so it is meaningfully different test)
dotest ("sm r pm w' ?pm w' ?sm r");
+
+ // Disconnect + reconnect; "deaf P" means the disconnect is asymmetrical: P no longer observes P'
+ // but P' still observes P. If r did not ACK the data before losing connectivity, w' will hold
+ // the data and it will be re-delivered after reconnecting, depending on QoS settings (the "..."
+ // allows for extra samples) and whether the instance was taken or not
+ //
+ // the uncertainty also means we don't really know how many "data available" events there will be
+ // and the "sleep 0.3" simply gives it a bit more time after the first event
+ dotest ("sm da r pm w' ; ?sm r ?pm w' ;"
+ " wr w' 1 ; ?da r take{(1,0,0)} r sleep 1;"
+ " deaf P ; ?sm(1,0,0,-1,w') r ?da r take{d1} r ; wr w' 2 ;"
+ " hearing P ; ?sm(2,1,1,1,w') r ?da r sleep 0.3 take{(2,0,0),...} r ; ?!pm");
+ // same without taking the "dispose" after disconnect
+ // sample 1 will be delivered anew
+ dotest ("sm da r pm w' ; ?sm r ?pm w' ; wr w' 1 ; ?da r take{(1,0,0)} r ;"
+ " deaf P ; ?sm(1,0,0,-1,w') r ?da r ; wr w' 2 ;"
+ " hearing P ; ?sm(2,1,1,1,w') r ?da r sleep 0.3 take{d1,(2,0,0)} r ; ?!pm");
+
+ // if a volatile writer loses the reader temporarily, the data won't show up
+ dotest ("sm da r pm w' ; ?sm r ?pm w' ; wr w' 1 ; ?da r read{(1,0,0)} r ;"
+ " deaf P' ; ?!sm ?!da ?pm(1,0,0,-1,r) w' ; wr w' 2 ;"
+ " hearing P' ; ?!sm ?pm(2,1,1,1,r) w' ?!da ; wr w' 3 ;"
+ " ?da r sleep 0.3 read{s(1,0,0),f(3,0,0)} r");
+ // if a transient-local writer loses the reader temporarily, what data
+ // has been published during the disconnect must still show up; delete
+ // writer, &c. checks nothing else showed up afterward
+ // - first: durability service history depth 1: 2nd write of 2 pushes
+ // the 1st write of it out of the history and only 2 samples arrive
+ // - second: d.s. keep-all: both writes are kept and 3 samples arrive
+ dotest ("sm da r(d=tl) pm w'(d=tl,h=1,ds=0/1) ; ?sm r ?pm w' ;"
+ " wr w' 1 ; ?da r read{(1,0,0)} r ;"
+ " deaf P' ; ?pm(1,0,0,-1,r) w' ; wr w' 2 wr w' 2 ;"
+ " hearing P' ; ?pm(2,1,1,1,r) w' ; wr w' 3 ;"
+ " ?da(2) r read{s(1,0,0),f(2,0,0),f(3,0,0)} r ;"
+ " -w' ?sm r ?da r read(3,3) r");
+ dotest ("sm da r(d=tl) pm w'(d=tl,h=1,ds=0/all) ; ?sm r ?pm w' ;"
+ " wr w' 1 ; ?da r read{(1,0,0)} r ;"
+ " deaf P' ; ?pm(1,0,0,-1,r) w' ; wr w' 2 wr w' 2 ;"
+ " hearing P' ; ?pm(2,1,1,1,r) w' ; wr w' 3 ;"
+ " ?da(3) r read{s(1,0,0),f(2,0,0),f(2,0,0),f(3,0,0)} r ;"
+ " -w' ?sm r ?da r read(4,3) r");
}
CU_Test (ddsc_listener, publication_matched)
@@ -1163,18 +372,20 @@ CU_Test (ddsc_listener, subscription_matched)
CU_Test (ddsc_listener, incompatible_qos)
{
// best-effort writer & reliable reader: both must trigger incompatible QoS event
- dotest ("oiq w! riq r ; ?oiq(1,1) w ?riq(1,1) r");
- dotest ("riq r oiq w! ; ?oiq(1,1) w ?riq(1,1) r");
+ dotest ("oiq w(r=be) riq r ; ?oiq(1,1,r) w ?riq(1,1,r) r");
+ dotest ("riq r oiq w(r=be) ; ?oiq(1,1,r) w ?riq(1,1,r) r");
+ dotest ("oiq w(o=x) riq r ; ?oiq(1,1,o) w ?riq(1,1,o) r");
+ dotest ("riq r oiq w(o=x) ; ?oiq(1,1,o) w ?riq(1,1,o) r");
}
CU_Test (ddsc_listener, data_available)
{
// data available on reader
- dotest ("da sm r pm w ?pm w ?sm r wr 0 w ?da r ?!dor");
+ dotest ("da sm r pm w ?pm w ?sm r wr w 0 ?da r ?!dor");
// data available set on subscriber
- dotest ("da R sm r pm w ?pm w ?sm r wr 0 w ?da r ?!dor");
+ dotest ("da R sm r pm w ?pm w ?sm r wr w 0 ?da r ?!dor");
// data available set on participant
- dotest ("da P sm r pm w ?pm w ?sm r wr 0 w ?da r ?!dor");
+ dotest ("da P sm r pm w ?pm w ?sm r wr w 0 ?da r ?!dor");
}
CU_Test (ddsc_listener, data_available_delete_writer)
@@ -1185,53 +396,53 @@ CU_Test (ddsc_listener, data_available_delete_writer)
dotest ("da sm r w ; -w ?sm r ?!da ; take(0,0) r");
// after writing: auto-dispose should always trigger data available, an invalid
// sample needs to show up if there isn't an unread sample to use instead
- dotest ("da r w ; wr 0 w ?da r ; -w ?da r ; take(1,0) r");
- dotest ("da r w ; wr 0 w ?da r ; read(1,0) r ; -w ?da r ; take(1,1) r");
- dotest ("da r w ; wr 0 w ?da r ; take(1,0) r ; -w ?da r ; take(0,1) r");
+ dotest ("da r w ; wr w 0 ?da r ; -w ?da r ; take(1,0) r");
+ dotest ("da r w ; wr w 0 ?da r ; read(1,0) r ; -w ?da r ; take(1,1) r");
+ dotest ("da r w ; wr w 0 ?da r ; take(1,0) r ; -w ?da r ; take(0,1) r");
// same with two writers (no point in doing this also with two domains)
dotest ("da r w x ; -w ?!da -x ?!da ; take(0,0) r");
- dotest ("da r w x ; wr 0 w ?da r ; -x ?!da ; -w ?da r ; take(1,0) r");
- dotest ("da r w x ; wr 0 w ?da r ; -w ?da r ; take(1,0) r ; -x ?!da ; take(0,0) r");
- dotest ("da r w x ; wr 0 w wr 0 x ?da r ; -w ?!da ; take(2,0) r ; -x ?da r ; take(0,1) r");
- dotest ("da r w x ; wr 0 w wr 0 x ?da r ; read(2,0) r ; -w ?!da -x ?da r ; take(2,1) r");
- dotest ("da r w x ; wr 0 w wr 0 x ?da r ; read(2,0) r ; -x ?!da -w ?da r ; take(2,1) r");
- dotest ("da r w x ; wr 0 w read(1,0) r ; wr 0 x ?da r ; -w ?!da -x ?da r ; take(2,0) r");
- dotest ("da r w x ; wr 0 w read(1,0) r ; wr 0 x ?da r ; -x ?!da -w ?da r ; take(2,0) r");
- dotest ("da r w x ; wr 0 w read(1,0) r ; wr 0 x ?da r ; read(2,0) r ; -w ?!da -x ?da r ; take(2,1) r");
- dotest ("da r w x ; wr 0 w read(1,0) r ; wr 0 x ?da r ; read(2,0) r ; -x ?!da -w ?da r ; take(2,1) r");
- dotest ("da r w x ; wr 0 w wr 0 x ?da r ; take(2,0) r ; -w ?!da -x ?da r ; take(0,1) r");
- dotest ("da r w x ; wr 0 w wr 0 x ?da r ; take(2,0) r ; -x ?!da -w ?da r ; take(0,1) r");
+ dotest ("da r w x ; wr w 0 ?da r ; -x ?!da ; -w ?da r ; take(1,0) r");
+ dotest ("da r w x ; wr w 0 ?da r ; -w ?da r ; take(1,0) r ; -x ?!da ; take(0,0) r");
+ dotest ("da r w x ; wr w 0 wr x 0 ?da r ; -w ?!da ; take(2,0) r ; -x ?da r ; take(0,1) r");
+ dotest ("da r w x ; wr w 0 wr x 0 ?da r ; read(2,0) r ; -w ?!da -x ?da r ; take(2,1) r");
+ dotest ("da r w x ; wr w 0 wr x 0 ?da r ; read(2,0) r ; -x ?!da -w ?da r ; take(2,1) r");
+ dotest ("da r w x ; wr w 0 read(1,0) r ; wr x 0 ?da r ; -w ?!da -x ?da r ; take(2,0) r");
+ dotest ("da r w x ; wr w 0 read(1,0) r ; wr x 0 ?da r ; -x ?!da -w ?da r ; take(2,0) r");
+ dotest ("da r w x ; wr w 0 read(1,0) r ; wr x 0 ?da r ; read(2,0) r ; -w ?!da -x ?da r ; take(2,1) r");
+ dotest ("da r w x ; wr w 0 read(1,0) r ; wr x 0 ?da r ; read(2,0) r ; -x ?!da -w ?da r ; take(2,1) r");
+ dotest ("da r w x ; wr w 0 wr x 0 ?da r ; take(2,0) r ; -w ?!da -x ?da r ; take(0,1) r");
+ dotest ("da r w x ; wr w 0 wr x 0 ?da r ; take(2,0) r ; -x ?!da -w ?da r ; take(0,1) r");
}
CU_Test (ddsc_listener, data_available_delete_writer_disposed)
{
// same as data_available_delete_writer, but now with the instance disposed first
- dotest ("da r w ; wr 0 w disp 0 w ?da r ; -w ?!da");
- dotest ("da r w ; wr 0 w disp 0 w ?da r ; read(1,0) r ; -w ?!da");
- dotest ("da r w ; wr 0 w disp 0 w ?da r ; take(1,0) r ; -w ?!da");
+ dotest ("da r w ; wr w 0 disp w 0 ?da r ; -w ?!da");
+ dotest ("da r w ; wr w 0 disp w 0 ?da r ; read(1,0) r ; -w ?!da");
+ dotest ("da r w ; wr w 0 disp w 0 ?da r ; take(1,0) r ; -w ?!da");
- dotest ("da r w x ; wr 0 w ?da r ; read(1,0) r ; disp 0 w ?da r ; read(1,1) r ; -w ?!da -x ?!da");
- dotest ("da r w x ; wr 0 w ?da r ; take(1,0) r ; disp 0 w ?da r ; take(0,1) r ; -w ?!da -x ?!da");
- dotest ("da r w x ; wr 0 w ?da r ; read(1,0) r ; disp 0 w ?da r ; read(1,1) r ; -x ?!da -w ?!da");
- dotest ("da r w x ; wr 0 w ?da r ; take(1,0) r ; disp 0 w ?da r ; take(0,1) r ; -x ?!da -w ?!da");
+ dotest ("da r w x ; wr w 0 ?da r ; read(1,0) r ; disp w 0 ?da r ; read(1,1) r ; -w ?!da -x ?!da");
+ dotest ("da r w x ; wr w 0 ?da r ; take(1,0) r ; disp w 0 ?da r ; take(0,1) r ; -w ?!da -x ?!da");
+ dotest ("da r w x ; wr w 0 ?da r ; read(1,0) r ; disp w 0 ?da r ; read(1,1) r ; -x ?!da -w ?!da");
+ dotest ("da r w x ; wr w 0 ?da r ; take(1,0) r ; disp w 0 ?da r ; take(0,1) r ; -x ?!da -w ?!da");
- dotest ("da r w x ; wr 0 w ?da r ; read(1,0) r ; disp 0 x ?da r ; read(1,1) r ; -w ?!da -x ?!da");
- dotest ("da r w x ; wr 0 w ?da r ; take(1,0) r ; disp 0 x ?da r ; take(0,1) r ; -w ?!da -x ?!da");
- dotest ("da r w x ; wr 0 w ?da r ; read(1,0) r ; disp 0 x ?da r ; read(1,1) r ; -x ?!da -w ?!da");
- dotest ("da r w x ; wr 0 w ?da r ; take(1,0) r ; disp 0 x ?da r ; take(0,1) r ; -x ?!da -w ?!da");
+ dotest ("da r w x ; wr w 0 ?da r ; read(1,0) r ; disp x 0 ?da r ; read(1,1) r ; -w ?!da -x ?!da");
+ dotest ("da r w x ; wr w 0 ?da r ; take(1,0) r ; disp x 0 ?da r ; take(0,1) r ; -w ?!da -x ?!da");
+ dotest ("da r w x ; wr w 0 ?da r ; read(1,0) r ; disp x 0 ?da r ; read(1,1) r ; -x ?!da -w ?!da");
+ dotest ("da r w x ; wr w 0 ?da r ; take(1,0) r ; disp x 0 ?da r ; take(0,1) r ; -x ?!da -w ?!da");
}
CU_Test (ddsc_listener, data_on_readers)
{
// data on readers wins from data available
- dotest ("dor R da r ; wr 0 w ; ?dor R ?!da");
- dotest ("dor P da r ; wr 0 w ; ?dor R ?!da");
+ dotest ("dor R da r ; wr w 0 ; ?dor R ?!da");
+ dotest ("dor P da r ; wr w 0 ; ?dor R ?!da");
}
CU_Test (ddsc_listener, sample_lost)
{
// FIXME: figure out what really constitutes a "lost sample"
- dotest ("sl r ; wr@0 0 w ?!sl ; wr@-1 0 w ?sl(1,1) r");
+ dotest ("sl r ; wr w 0@0 ?!sl ; wr w 0@-1 ?sl(1,1) r");
}
CU_Test (ddsc_listener, sample_rejected)
@@ -1240,13 +451,13 @@ CU_Test (ddsc_listener, sample_rejected)
// reliable: expect timeout on the write when max samples has been reached
// invalid samples don't count towards resource limits, so dispose should
// not be blocked
- dotest ("sr r# ; wr 0 w wrfail 0 w wrfail 0 w ; ?sr r");
- dotest ("sr r# ; wr 0 w wrfail 0 w ; read(1,0) r ; disp 0 w ; read(1,1) r ; ?sr r");
+ dotest ("sr r(rl=1) ; wr w 0 wrfail w 0 wrfail w 0 ; ?sr r");
+ dotest ("sr r(rl=1) ; wr w 0 wrfail w 0 ; read(1,0) r ; disp w 0 ; read(1,1) r ; ?sr r");
// best-effort: writes should succeed despite not delivering the data adding
// the data in the RHC, also check number of samples rejected
- dotest ("sr r#! ; wr 0 w! wr 0 w wr 0 w ; ?sr(2,1,s) r");
- dotest ("sr r#! ; wr 0 w! wr 0 w ; read(1,0) r ; disp 0 w ; read(1,1) r ; ?sr(1,1,s) r");
+ dotest ("sr r(rl=1,r=be) ; wr w(r=be) 0 wr w 0 wr w 0 ; ?sr(2,1,s) r");
+ dotest ("sr r(rl=1,r=be) ; wr w(r=be) 0 wr w 0 ; read(1,0) r ; disp w 0 ; read(1,1) r ; ?sr(1,1,s) r");
}
CU_Test (ddsc_listener, liveliness_changed)
diff --git a/src/core/ddsc/tests/test_oneliner.c b/src/core/ddsc/tests/test_oneliner.c
new file mode 100644
index 0000000000..255400ae98
--- /dev/null
+++ b/src/core/ddsc/tests/test_oneliner.c
@@ -0,0 +1,1780 @@
+/*
+ * Copyright(c) 2020 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
+#include
+#include
+#include
+#include
+
+#include "dds/dds.h"
+#include "dds/ddsrt/misc.h"
+#include "dds/ddsrt/sync.h"
+#include "dds/ddsrt/heap.h"
+#include "dds/ddsrt/strtod.h"
+#include "dds/ddsrt/string.h"
+#include "dds/ddsrt/environ.h"
+
+#include "dds__types.h"
+#include "dds__entity.h"
+#include "dds/ddsi/q_lease.h"
+#include "dds/ddsi/q_xevent.h"
+#include "dds/ddsi/ddsi_entity_index.h"
+
+#include "test_common.h"
+#include "test_oneliner.h"
+
+#define MAXDOMS (sizeof (((struct oneliner_ctx){.result=0}).doms) / sizeof (((struct oneliner_ctx){.result=0}).doms[0]))
+
+static const char knownentities[] = "PRWrstwxy";
+typedef struct { char n[MAXDOMS + 1]; } entname_t;
+
+#define DEFINE_STATUS_CALLBACK(name, NAME, kind) \
+ static void name##_cb (dds_entity_t kind, const dds_##name##_status_t status, void *arg) \
+ { \
+ struct oneliner_cb *cb = arg; \
+ ddsrt_mutex_lock (&cb->ctx->g_mutex); \
+ cb->cb_##kind = kind; \
+ cb->cb_##name##_status = status; \
+ cb->cb_called[DDS_##NAME##_STATUS_ID]++; \
+ ddsrt_cond_broadcast (&cb->ctx->g_cond); \
+ ddsrt_mutex_unlock (&cb->ctx->g_mutex); \
+ }
+
+DEFINE_STATUS_CALLBACK (inconsistent_topic, INCONSISTENT_TOPIC, topic)
+DEFINE_STATUS_CALLBACK (liveliness_changed, LIVELINESS_CHANGED, reader)
+DEFINE_STATUS_CALLBACK (liveliness_lost, LIVELINESS_LOST, writer)
+DEFINE_STATUS_CALLBACK (offered_deadline_missed, OFFERED_DEADLINE_MISSED, writer)
+DEFINE_STATUS_CALLBACK (offered_incompatible_qos, OFFERED_INCOMPATIBLE_QOS, writer)
+DEFINE_STATUS_CALLBACK (publication_matched, PUBLICATION_MATCHED, writer)
+DEFINE_STATUS_CALLBACK (requested_deadline_missed, REQUESTED_DEADLINE_MISSED, reader)
+DEFINE_STATUS_CALLBACK (requested_incompatible_qos, REQUESTED_INCOMPATIBLE_QOS, reader)
+DEFINE_STATUS_CALLBACK (sample_lost, SAMPLE_LOST, reader)
+DEFINE_STATUS_CALLBACK (sample_rejected, SAMPLE_REJECTED, reader)
+DEFINE_STATUS_CALLBACK (subscription_matched, SUBSCRIPTION_MATCHED, reader)
+
+static void data_on_readers_cb (dds_entity_t subscriber, void *arg)
+{
+ struct oneliner_cb *cb = arg;
+ ddsrt_mutex_lock (&cb->ctx->g_mutex);
+ cb->cb_subscriber = subscriber;
+ cb->cb_called[DDS_DATA_ON_READERS_STATUS_ID]++;
+ ddsrt_cond_broadcast (&cb->ctx->g_cond);
+ ddsrt_mutex_unlock (&cb->ctx->g_mutex);
+}
+
+static void data_available_cb (dds_entity_t reader, void *arg)
+{
+ struct oneliner_cb *cb = arg;
+ ddsrt_mutex_lock (&cb->ctx->g_mutex);
+ cb->cb_reader = reader;
+ cb->cb_called[DDS_DATA_AVAILABLE_STATUS_ID]++;
+ ddsrt_cond_broadcast (&cb->ctx->g_cond);
+ ddsrt_mutex_unlock (&cb->ctx->g_mutex);
+}
+
+static void dummy_data_on_readers_cb (dds_entity_t subscriber, void *arg)
+{
+ (void)subscriber;
+ (void)arg;
+}
+
+static void dummy_data_available_cb (dds_entity_t reader, void *arg)
+{
+ (void)reader;
+ (void)arg;
+}
+
+static void dummy_subscription_matched_cb (dds_entity_t reader, const dds_subscription_matched_status_t status, void *arg)
+{
+ (void)reader;
+ (void)status;
+ (void)arg;
+}
+
+static void dummy_liveliness_changed_cb (dds_entity_t reader, const dds_liveliness_changed_status_t status, void *arg)
+{
+ (void)reader;
+ (void)status;
+ (void)arg;
+}
+
+static void dummy_cb (void)
+{
+ // Used as a listener function in checking merging of listeners,
+ // and for that purpose, casting it to whatever function type is
+ // required is ok. It is not supposed to ever be called.
+ abort ();
+}
+
+#undef DEFINE_STATUS_CALLBACK
+
+// These had better match the corresponding type definitions!
+// n uint32_t ...count
+// c int32_t ...count_change
+// I instance handle of a data instance
+// P uint32_t QoS policy ID
+// E instance handle of an entity
+// R sample_rejected_status_kind
+static const struct {
+ const char *name;
+ size_t size; // size of status struct
+ const char *desc; // description of status struct
+ dds_status_id_t id; // status id, entry in "cb_called"
+ size_t cb_entity_off; // which cb_... entity to look at
+ size_t cb_status_off; // cb_..._status to look at
+} lldesc[] = {
+#define S0(abbrev, NAME, entity) \
+ { abbrev, 0, NULL, DDS_##NAME##_STATUS_ID, offsetof (struct oneliner_cb, cb_##entity), 0 }
+#define S(abbrev, name, NAME, desc, entity) \
+ { abbrev, sizeof (dds_##name##_status_t), desc, DDS_##NAME##_STATUS_ID, offsetof (struct oneliner_cb, cb_##entity), offsetof (struct oneliner_cb, cb_##name##_status) }
+ S0 ("da", DATA_AVAILABLE, reader),
+ S0 ("dor", DATA_ON_READERS, subscriber),
+ S ("it", inconsistent_topic, INCONSISTENT_TOPIC, "nc", topic),
+ S ("lc", liveliness_changed, LIVELINESS_CHANGED, "nnccE", reader),
+ S ("ll", liveliness_lost, LIVELINESS_LOST, "nc", writer),
+ S ("odm", offered_deadline_missed, OFFERED_DEADLINE_MISSED, "ncI", writer),
+ S ("oiq", offered_incompatible_qos, OFFERED_INCOMPATIBLE_QOS, "ncP", writer),
+ S ("pm", publication_matched, PUBLICATION_MATCHED, "ncncE", writer),
+ S ("rdm", requested_deadline_missed, REQUESTED_DEADLINE_MISSED, "ncI", reader),
+ S ("riq", requested_incompatible_qos, REQUESTED_INCOMPATIBLE_QOS, "ncP", reader),
+ S ("sl", sample_lost, SAMPLE_LOST, "nc", reader),
+ S ("sr", sample_rejected, SAMPLE_REJECTED, "ncRI", reader),
+ S ("sm", subscription_matched, SUBSCRIPTION_MATCHED, "ncncE", reader)
+#undef S
+#undef S0
+};
+
+
+static const void *advance (const void *status, size_t *off, char code)
+{
+#define alignof(type_) offsetof (struct { char c; type_ d; }, d)
+ size_t align = 1, size = 1;
+ switch (code)
+ {
+ case 'n': case 'c': case 'P':
+ align = alignof (uint32_t); size = sizeof (uint32_t);
+ break;
+ case 'E': case 'I':
+ align = alignof (dds_instance_handle_t); size = sizeof (dds_instance_handle_t);
+ break;
+ case 'R':
+ align = alignof (dds_sample_rejected_status_kind); size = sizeof (dds_sample_rejected_status_kind);
+ break;
+ default:
+ abort ();
+ }
+#undef alignof
+ *off = (*off + align - 1) & ~(align - 1);
+ const void *p = (const char *) status + *off;
+ *off += size;
+ return p;
+}
+
+static dds_return_t get_status (int ll, dds_entity_t ent, void *status)
+{
+ dds_return_t ret;
+ switch (ll)
+ {
+ case 2: ret = dds_get_inconsistent_topic_status (ent, status); break;
+ case 3: ret = dds_get_liveliness_changed_status (ent, status); break;
+ case 4: ret = dds_get_liveliness_lost_status (ent, status); break;
+ case 5: ret = dds_get_offered_deadline_missed_status (ent, status); break;
+ case 6: ret = dds_get_offered_incompatible_qos_status (ent, status); break;
+ case 7: ret = dds_get_publication_matched_status (ent, status); break;
+ case 8: ret = dds_get_requested_deadline_missed_status (ent, status); break;
+ case 9: ret = dds_get_requested_incompatible_qos_status (ent, status); break;
+ case 10: ret = dds_get_sample_lost_status (ent, status); break;
+ case 11: ret = dds_get_sample_rejected_status (ent, status); break;
+ case 12: ret = dds_get_subscription_matched_status (ent, status); break;
+ default: return -1;
+ }
+ return (ret == 0);
+}
+
+static dds_return_t check_status_change_fields_are_0 (int ll, dds_entity_t ent)
+{
+ if (lldesc[ll].desc)
+ {
+ const char *d = lldesc[ll].desc;
+ void *status = malloc (lldesc[ll].size);
+ dds_return_t ret;
+ if ((ret = get_status (ll, ent, status)) <= 0)
+ {
+ free (status);
+ return ret;
+ }
+ size_t off = 0;
+ while (*d)
+ {
+ const uint32_t *p = advance (status, &off, *d);
+ if (*d == 'c' && *p != 0)
+ {
+ free (status);
+ return 0;
+ }
+ d++;
+ }
+ assert (off <= lldesc[ll].size);
+ free (status);
+ }
+ return 1;
+}
+
+#define TOK_END -1
+#define TOK_NAME -2
+#define TOK_INT -3
+#define TOK_DURATION -4
+#define TOK_TIMESTAMP -5
+#define TOK_ELLIPSIS -6
+#define TOK_INVALID -7
+
+static int setresult (struct oneliner_ctx *ctx, int result, const char *msg, ...) ddsrt_attribute_format((printf, 3, 4));
+static void error (struct oneliner_ctx *ctx, const char *msg, ...) ddsrt_attribute_format((printf, 2, 3));
+static void error_dds (struct oneliner_ctx *ctx, dds_return_t ret, const char *msg, ...) ddsrt_attribute_format((printf, 3, 4));
+static void testfail (struct oneliner_ctx *ctx, const char *msg, ...) ddsrt_attribute_format((printf, 2, 3));
+
+static void vsetresult (struct oneliner_ctx *ctx, int result, const char *msg, va_list ap)
+{
+ assert (result <= 0);
+ ctx->result = result;
+ vsnprintf (ctx->msg, sizeof (ctx->msg), msg, ap);
+}
+
+static int setresult (struct oneliner_ctx *ctx, int result, const char *msg, ...)
+{
+ va_list ap;
+ va_start (ap, msg);
+ vsetresult (ctx, result, msg, ap);
+ va_end (ap);
+ return result;
+}
+
+static void error (struct oneliner_ctx *ctx, const char *msg, ...)
+{
+ va_list ap;
+ va_start (ap, msg);
+ vsetresult (ctx, -1, msg, ap);
+ va_end (ap);
+ longjmp (ctx->jb, 1);
+}
+
+static void error_dds (struct oneliner_ctx *ctx, dds_return_t ret, const char *msg, ...)
+{
+ va_list ap;
+ va_start (ap, msg);
+ vsetresult (ctx, -1, msg, ap);
+ va_end (ap);
+ size_t n = strlen (ctx->msg);
+ if (n < sizeof (ctx->msg))
+ snprintf (ctx->msg + n, sizeof (ctx->msg) - n, " (%s)", dds_strretcode (ret));
+ longjmp (ctx->jb, 1);
+}
+
+static void testfail (struct oneliner_ctx *ctx, const char *msg, ...)
+{
+ va_list ap;
+ va_start (ap, msg);
+ vsetresult (ctx, 0, msg, ap);
+ va_end (ap);
+ longjmp (ctx->jb, 1);
+}
+
+static void advancetok (struct oneliner_lex *l)
+{
+ while (isspace ((unsigned char) *l->inp))
+ l->inp++;
+}
+
+static int issymchar0 (char c)
+{
+ return isalpha ((unsigned char) c) || c == '_';
+}
+
+static int issymchar (char c)
+{
+ return isalnum ((unsigned char) c) || c == '_' || c == '\'';
+}
+
+static bool lookingatnum (const struct oneliner_lex *l)
+{
+ return (isdigit ((unsigned char) l->inp[(l->inp[0] == '-')]));
+}
+
+static int nexttok_dur (struct oneliner_lex *l, union oneliner_tokval *v, bool expecting_duration)
+{
+ advancetok (l);
+ if (l->inp[0] == 0)
+ {
+ l->tok = TOK_END;
+ }
+ else if (strncmp (l->inp, "...", 3) == 0)
+ {
+ l->inp += 3;
+ l->tok = TOK_ELLIPSIS;
+ }
+ else if (!expecting_duration && lookingatnum (l))
+ {
+ char *endp;
+ // strtol: [0-9]+ ; endp = l->inp if no digits present
+ l->v.i = (int) strtol (l->inp, &endp, 10);
+ l->inp = endp;
+ if (v) *v = l->v;
+ l->tok = TOK_INT;
+ }
+ else if (l->inp[0] == '@' || (expecting_duration && lookingatnum (l)))
+ {
+ const int ists = (l->inp[0] == '@');
+ char *endp;
+ if (!ists && strncmp (l->inp + ists, "inf", 3) == 0 && !issymchar (l->inp[ists + 3]))
+ {
+ l->inp += ists + 3;
+ l->v.d = DDS_INFINITY;
+ }
+ else
+ {
+ double d;
+ if (ddsrt_strtod (l->inp + ists, &endp, &d) != DDS_RETCODE_OK)
+ return false;
+ if (!ists && d < 0)
+ return false;
+ if (d >= (double) (INT64_MAX / DDS_NSECS_IN_SEC))
+ l->v.d = DDS_INFINITY;
+ else if (d >= 0)
+ l->v.d = (int64_t) (d * 1e9 + 0.5);
+ else
+ l->v.d = -(int64_t) (-d * 1e9 + 0.5);
+ if (ists)
+ l->v.d += l->tref;
+ l->inp = endp;
+ }
+ if (v) *v = l->v;
+ l->tok = ists ? TOK_TIMESTAMP : TOK_DURATION;
+ }
+ else if (issymchar0 (l->inp[0]))
+ {
+ int p = 0;
+ while (issymchar (l->inp[p]))
+ {
+ if (p == (int) sizeof (l->v.n))
+ return TOK_INVALID;
+ l->v.n[p] = l->inp[p];
+ p++;
+ }
+ l->v.n[p] = 0;
+ l->inp += p;
+ if (v) *v = l->v;
+ l->tok = TOK_NAME;
+ }
+ else
+ {
+ l->tok = *l->inp++;
+ }
+ return l->tok;
+}
+
+static int nexttok (struct oneliner_lex *l, union oneliner_tokval *v)
+{
+ return nexttok_dur (l, v, false);
+}
+
+static int peektok (const struct oneliner_lex *l, union oneliner_tokval *v)
+{
+ struct oneliner_lex l1 = *l;
+ return nexttok (&l1, v);
+}
+
+static bool nexttok_if (struct oneliner_lex *l, int tok)
+{
+ if (peektok (l, NULL) != tok)
+ return false;
+ nexttok (l, NULL);
+ return true;
+}
+
+static bool nexttok_int (struct oneliner_lex *l, int *dst)
+{
+ if (peektok (l, NULL) != TOK_INT)
+ return false;
+ (void) nexttok (l, NULL);
+ *dst = l->v.i;
+ return true;
+}
+
+struct kvarg {
+ const char *k;
+ size_t klen;
+ int v;
+ bool (*arg) (struct oneliner_lex *l, void *dst); // *inp unchanged when false
+ void (*def) (void *dst);
+};
+
+static void def_kvarg_int0 (void *dst) { *(int *)dst = 0; }
+static void def_kvarg_int1 (void *dst) { *(int *)dst = 1; }
+static void def_kvarg_dur_inf (void *dst) { *(dds_duration_t *)dst = DDS_INFINITY; }
+static void def_kvarg_dur_100ms (void *dst) { *(dds_duration_t *)dst = DDS_MSECS (100); }
+
+static bool read_kvarg_int (struct oneliner_lex *l, void *dst)
+{
+ return nexttok_int (l, dst);
+}
+
+static bool read_kvarg_posint (struct oneliner_lex *l, void *dst)
+{
+ return nexttok_int (l, dst) && l->v.i > 0;
+}
+
+static bool read_kvarg_dur (struct oneliner_lex *l, void *dst)
+{
+ dds_duration_t *x = dst;
+ struct oneliner_lex l1 = *l;
+ if (nexttok_dur (&l1, NULL, true) != TOK_DURATION)
+ return false;
+ *x = l1.v.d;
+ *l = l1;
+ return true;
+}
+
+static bool read_kvarg_3len (struct oneliner_lex *l, void *dst)
+{
+ struct oneliner_lex l1 = *l;
+ int *x = dst, i = 0;
+ x[0] = x[1] = x[2] = DDS_LENGTH_UNLIMITED;
+ do {
+ if (!nexttok_int (&l1, &x[i]) || (x[i] <= 0 && x[i] != DDS_LENGTH_UNLIMITED))
+ return false;
+ } while (++i < 3 && nexttok_if (&l1, '/'));
+ *l = l1;
+ return true;
+}
+
+static bool read_kvarg (const struct kvarg *ks, size_t sizeof_ks, struct oneliner_lex *l, int *v, void *arg)
+{
+ // l points at name, *inp is , or ) terminated; *l unchanged when false
+ const struct kvarg *kend = ks + sizeof_ks / sizeof (*ks);
+ struct oneliner_lex l1 = *l;
+ advancetok (&l1);
+ for (const struct kvarg *k = ks; k < kend; k++)
+ {
+ assert (strlen (k->k) == k->klen);
+ *v = k->v;
+ if (k->klen == 0)
+ {
+ assert (k->arg != 0 && k->def == 0);
+ struct oneliner_lex l2 = l1;
+ if (k->arg (&l2, arg) && (peektok (&l2, NULL) == ',' || peektok (&l2, NULL) == ')'))
+ {
+ *l = l2;
+ return true;
+ }
+ }
+ else if (strncmp (l1.inp, k->k, k->klen) != 0)
+ {
+ continue;
+ }
+ else
+ {
+ /* skip symbol */
+ struct oneliner_lex l2 = l1;
+ l2.inp += k->klen;
+ if (peektok (&l2, NULL) == ',' || peektok (&l2, NULL) == ')')
+ {
+ if (k->arg == 0 || k->def != 0)
+ {
+ if (k->def) k->def (arg);
+ *l = l2;
+ return true;
+ }
+ }
+ else if (k->arg != 0 && nexttok (&l2, NULL) == ':')
+ {
+ if (k->arg (&l2, arg) && (peektok (&l2, NULL) == ',' || peektok (&l2, NULL) == ')'))
+ {
+ *l = l2;
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+static bool qos_durability (struct oneliner_lex *l, dds_qos_t *q)
+{
+ static const struct kvarg ks[] = {
+ { "v", 1, (int) DDS_DURABILITY_VOLATILE },
+ { "tl", 2, (int) DDS_DURABILITY_TRANSIENT_LOCAL },
+ { "t", 1, (int) DDS_DURABILITY_TRANSIENT },
+ { "p", 1, (int) DDS_DURABILITY_PERSISTENT }
+ };
+ int v;
+ if (!read_kvarg (ks, sizeof ks, l, &v, NULL))
+ return false;
+ dds_qset_durability (q, (dds_durability_kind_t) v);
+ return true;
+}
+
+static const struct kvarg ks_history[] = {
+ { "all", 3, (int) DDS_HISTORY_KEEP_ALL, .def = def_kvarg_int1 },
+ { "", 0, (int) DDS_HISTORY_KEEP_LAST, .arg = read_kvarg_posint }
+};
+
+static bool qos_history (struct oneliner_lex *l, dds_qos_t *q)
+{
+ int v, x = 1;
+ if (!read_kvarg (ks_history, sizeof ks_history, l, &v, &x))
+ return false;
+ dds_qset_history (q, (dds_history_kind_t) v, x);
+ return true;
+}
+
+static bool qos_destination_order (struct oneliner_lex *l, dds_qos_t *q)
+{
+ static const struct kvarg ks[] = {
+ { "r", 1, (int) DDS_DESTINATIONORDER_BY_RECEPTION_TIMESTAMP },
+ { "s", 1, (int) DDS_DESTINATIONORDER_BY_SOURCE_TIMESTAMP }
+ };
+ int v;
+ if (!read_kvarg (ks, sizeof ks, l, &v, NULL))
+ return false;
+ dds_qset_destination_order (q, (dds_destination_order_kind_t) v);
+ return true;
+}
+
+static bool qos_ownership (struct oneliner_lex *l, dds_qos_t *q)
+{
+ static const struct kvarg ks[] = {
+ { "s", 1, (int) DDS_OWNERSHIP_SHARED, .def = def_kvarg_int0 },
+ { "x", 1, (int) DDS_OWNERSHIP_EXCLUSIVE, .arg = read_kvarg_int, .def = def_kvarg_int0 }
+ };
+ int v, x;
+ if (!read_kvarg (ks, sizeof ks, l, &v, &x))
+ return false;
+ dds_qset_ownership (q, (dds_ownership_kind_t) v);
+ dds_qset_ownership_strength (q, x);
+ return true;
+}
+
+static bool qos_transport_priority (struct oneliner_lex *l, dds_qos_t *q)
+{
+ static const struct kvarg k = { "", 0, 0, .arg = read_kvarg_int };
+ int v, x;
+ if (!read_kvarg (&k, sizeof k, l, &v, &x))
+ return false;
+ dds_qset_transport_priority (q, x);
+ return true;
+}
+
+static bool qos_reliability (struct oneliner_lex *l, dds_qos_t *q)
+{
+ static const struct kvarg ks[] = {
+ { "be", 2, (int) DDS_RELIABILITY_BEST_EFFORT, .def = def_kvarg_dur_100ms },
+ { "r", 1, (int) DDS_RELIABILITY_RELIABLE, .def = def_kvarg_dur_100ms, .arg = read_kvarg_dur }
+ };
+ int v;
+ dds_duration_t x;
+ if (!read_kvarg (ks, sizeof ks, l, &v, &x))
+ return false;
+ dds_qset_reliability (q, (dds_reliability_kind_t) v, x);
+ return true;
+}
+
+static bool qos_liveliness (struct oneliner_lex *l, dds_qos_t *q)
+{
+ static const struct kvarg ks[] = {
+ { "a", 1, (int) DDS_LIVELINESS_AUTOMATIC, .def = def_kvarg_dur_inf, .arg = read_kvarg_dur },
+ { "p", 1, (int) DDS_LIVELINESS_MANUAL_BY_PARTICIPANT, .arg = read_kvarg_dur },
+ { "w", 1, (int) DDS_LIVELINESS_MANUAL_BY_TOPIC, .arg = read_kvarg_dur }
+ };
+ int v;
+ dds_duration_t x;
+ if (!read_kvarg (ks, sizeof ks, l, &v, &x))
+ return false;
+ dds_qset_liveliness (q, (dds_liveliness_kind_t) v, x);
+ return true;
+}
+
+static bool qos_simple_duration (struct oneliner_lex *l, dds_qos_t *q, void (*set) (dds_qos_t * __restrict q, dds_duration_t dur))
+{
+ static const struct kvarg k = { "", 0, 0, .arg = read_kvarg_dur };
+ int v;
+ dds_duration_t x;
+ if (!read_kvarg (&k, sizeof k, l, &v, &x))
+ return false;
+ set (q, x);
+ return true;
+}
+
+static bool qos_latency_budget (struct oneliner_lex *l, dds_qos_t *q)
+{
+ return qos_simple_duration (l, q, dds_qset_latency_budget);
+}
+
+static bool qos_deadline (struct oneliner_lex *l, dds_qos_t *q)
+{
+ return qos_simple_duration (l, q, dds_qset_deadline);
+}
+
+static bool qos_lifespan (struct oneliner_lex *l, dds_qos_t *q)
+{
+ return qos_simple_duration (l, q, dds_qset_lifespan);
+}
+
+static bool qos_resource_limits (struct oneliner_lex *l, dds_qos_t *q)
+{
+ int rl[3];
+ if (!read_kvarg_3len (l, rl))
+ return false;
+ dds_qset_resource_limits (q, rl[0], rl[1], rl[2]);
+ return true;
+}
+
+static bool qos_durability_service (struct oneliner_lex *l, dds_qos_t *q)
+{
+ struct oneliner_lex l1 = *l;
+ dds_duration_t scd;
+ int hk = DDS_HISTORY_KEEP_LAST, hd = 1, rl[3];
+ if (!read_kvarg_dur (&l1, &scd))
+ return false;
+ if (peektok (&l1, NULL) == '/')
+ {
+ (void) nexttok (&l1, NULL);
+ if (!read_kvarg (ks_history, sizeof ks_history, &l1, &hk, &hd))
+ return false;
+ }
+ if (peektok (&l1, NULL) != '/')
+ rl[0] = rl[1] = rl[2] = DDS_LENGTH_UNLIMITED;
+ else
+ {
+ (void) nexttok (&l1, NULL);
+ if (!read_kvarg_3len (&l1, rl))
+ return false;
+ }
+ dds_qset_durability_service (q, scd, (dds_history_kind_t) hk, hd, rl[0], rl[1], rl[2]);
+ *l = l1;
+ return true;
+}
+
+static bool qos_presentation (struct oneliner_lex *l, dds_qos_t *q)
+{
+ static const struct kvarg ks[] = {
+ { "i", 1, (int) DDS_PRESENTATION_INSTANCE, .def = def_kvarg_int0 },
+ { "t", 1, (int) DDS_PRESENTATION_TOPIC, .def = def_kvarg_int1 },
+ { "g", 1, (int) DDS_PRESENTATION_GROUP, .def = def_kvarg_int1 }
+ };
+ int v, x;
+ if (!read_kvarg (ks, sizeof ks, l, &v, &x))
+ return false;
+ dds_qset_presentation (q, (dds_presentation_access_scope_kind_t) v, x, 0);
+ return true;
+}
+
+static bool qos_autodispose_unregistered_instances (struct oneliner_lex *l, dds_qos_t *q)
+{
+ static const struct kvarg ks[] = {
+ { "y", 1, 1 },
+ { "n", 1, 0 }
+ };
+ int v;
+ if (!read_kvarg (ks, sizeof ks, l, &v, NULL))
+ return false;
+ dds_qset_writer_data_lifecycle (q, !!v);
+ return true;
+}
+
+static const struct {
+ char *abbrev;
+ size_t n;
+ bool (*fn) (struct oneliner_lex *l, dds_qos_t *q);
+ dds_qos_policy_id_t id;
+} qostab[] = {
+ { "ll", 2, qos_liveliness, DDS_LIVELINESS_QOS_POLICY_ID },
+ { "d", 1, qos_durability, DDS_DURABILITY_QOS_POLICY_ID },
+ { "dl", 2, qos_deadline, DDS_DEADLINE_QOS_POLICY_ID },
+ { "h", 1, qos_history, DDS_HISTORY_QOS_POLICY_ID },
+ { "lb", 2, qos_latency_budget, DDS_LATENCYBUDGET_QOS_POLICY_ID },
+ { "ls", 2, qos_lifespan, DDS_LIFESPAN_QOS_POLICY_ID },
+ { "do", 2, qos_destination_order, DDS_DESTINATIONORDER_QOS_POLICY_ID },
+ { "o", 1, qos_ownership, DDS_OWNERSHIP_QOS_POLICY_ID },
+ { "tp", 2, qos_transport_priority, DDS_OWNERSHIPSTRENGTH_QOS_POLICY_ID },
+ { "p", 1, qos_presentation, DDS_PRESENTATION_QOS_POLICY_ID },
+ { "r", 1, qos_reliability, DDS_RELIABILITY_QOS_POLICY_ID },
+ { "rl", 2, qos_resource_limits, DDS_RESOURCELIMITS_QOS_POLICY_ID },
+ { "ds", 2, qos_durability_service, DDS_DURABILITYSERVICE_QOS_POLICY_ID },
+ { "ad", 2, qos_autodispose_unregistered_instances, DDS_WRITERDATALIFECYCLE_QOS_POLICY_ID }
+};
+
+static bool setqos (struct oneliner_lex *l, dds_qos_t *q)
+{
+ struct oneliner_lex l1 = *l;
+ dds_reset_qos (q);
+ // no whitespace between name & QoS
+ if (*l1.inp != '(')
+ return true;
+ nexttok (&l1, NULL); // eat '('
+ do {
+ size_t i;
+ union oneliner_tokval name;
+ if (nexttok (&l1, &name) != TOK_NAME || nexttok (&l1, NULL) != '=')
+ return false;
+ for (i = 0; i < sizeof (qostab) / sizeof (qostab[0]); i++)
+ {
+ assert (strlen (qostab[i].abbrev) == qostab[i].n);
+ if (strcmp (name.n, qostab[i].abbrev) == 0)
+ break;
+ }
+ if (i == sizeof (qostab) / sizeof (qostab[0]))
+ return false;
+ if (!qostab[i].fn (&l1, q))
+ return false;
+ } while (nexttok_if (&l1, ','));
+ if (nexttok (&l1, NULL) != ')')
+ return false;
+ *l = l1;
+ return true;
+}
+
+static int parse_entity1 (struct oneliner_lex *l, dds_qos_t *qos)
+{
+ struct oneliner_lex l1 = *l;
+ if (nexttok (&l1, NULL) != TOK_NAME)
+ return -1;
+ const char *p;
+ if ((p = strchr (knownentities, l1.v.n[0])) == NULL)
+ return -1;
+ int ent = (int) (p - knownentities);
+ int i;
+ for (i = 1; l1.v.n[i] == '\''; i++)
+ ent += (int) sizeof (knownentities) - 1;
+ if (l1.v.n[i] != 0)
+ return -1;
+ if (ent / 9 >= (int) MAXDOMS)
+ return -1;
+ if (!setqos (&l1, qos))
+ return -1;
+ *l = l1;
+ return ent;
+}
+
+static int parse_entity (struct oneliner_ctx *ctx)
+{
+ return parse_entity1 (&ctx->l, ctx->rwqos);
+}
+
+static int parse_listener1 (struct oneliner_lex *l)
+{
+ struct oneliner_lex l1 = *l;
+ size_t i;
+ if (nexttok (&l1, NULL) != TOK_NAME)
+ return -1;
+ for (i = 0; i < sizeof (lldesc) / sizeof (lldesc[0]); i++)
+ if (strcmp (l1.v.n, lldesc[i].name) == 0)
+ break;
+ if (i == sizeof (lldesc) / sizeof (lldesc[0]))
+ return -1;
+ *l = l1;
+ return (int) i;
+}
+
+static int parse_listener (struct oneliner_ctx *ctx)
+{
+ return parse_listener1 (&ctx->l);
+}
+
+static const char *getentname (entname_t *name, int ent)
+{
+ DDSRT_STATIC_ASSERT (sizeof (knownentities) == 10);
+ DDSRT_STATIC_ASSERT (MAXDOMS == 3);
+ name->n[0] = knownentities[ent % 9];
+ const int dom = ent / 9;
+ int i;
+ for (i = 1; i <= dom; i++)
+ name->n[i] = '\'';
+ name->n[i] = 0;
+ return name->n;
+}
+
+static void make_participant (struct oneliner_ctx *ctx, int ent, dds_listener_t *list)
+{
+ const dds_domainid_t domid = (dds_domainid_t) (ent / 9);
+ char *conf = ddsrt_expand_envvars ("${CYCLONEDDS_URI}${CYCLONEDDS_URI:+,}0", domid);
+ entname_t name;
+ printf ("create domain %"PRIu32, domid);
+ fflush (stdout);
+ if ((ctx->doms[domid] = dds_create_domain (domid, conf)) <= 0)
+ error_dds (ctx, ctx->doms[domid], "make_participant: create domain %"PRIu32" failed", domid);
+ ddsrt_free (conf);
+ printf (" create participant %s", getentname (&name, ent));
+ fflush (stdout);
+ if ((ctx->es[ent] = dds_create_participant (domid, NULL, list)) <= 0)
+ error_dds (ctx, ctx->es[ent], "make_participant: create participant failed in domain %"PRIu32, domid);
+ if ((ctx->tps[domid] = dds_create_topic (ctx->es[ent], &Space_Type1_desc, ctx->topicname, ctx->qos, NULL)) <= 0)
+ error_dds (ctx, ctx->tps[domid], "make_participant: create topic failed in domain %"PRIu32, domid);
+
+ // Create the built-in topic readers with a dummy listener to avoid any event (data available comes to mind)
+ // from propagating to the normal data available listener, in case it has been set on the participant.
+ //
+ // - dummy_cb aborts when it is invoked, but all reader-related listeners that can possibly trigger are set
+ // separately (incompatible qos, deadline missed, sample lost and sample rejected are all impossible by
+ // construction)
+ // - regarding data_on_readers: Cyclone handles listeners installed on an ancestor by *inheriting* them,
+ // rather than by walking up ancestor chain. Setting data_on_readers on the reader therefore overrides the
+ // listener set on the subscriber. It is a nice feature!
+ dds_listener_t *dummylist = dds_create_listener (ctx);
+ dds_lset_data_available (dummylist, dummy_data_available_cb);
+ dds_lset_data_on_readers (dummylist, dummy_data_on_readers_cb);
+ dds_lset_inconsistent_topic (dummylist, (dds_on_inconsistent_topic_fn) dummy_cb);
+ dds_lset_liveliness_changed (dummylist, dummy_liveliness_changed_cb);
+ dds_lset_liveliness_lost (dummylist, (dds_on_liveliness_lost_fn) dummy_cb);
+ dds_lset_offered_deadline_missed (dummylist, (dds_on_offered_deadline_missed_fn) dummy_cb);
+ dds_lset_offered_incompatible_qos (dummylist, (dds_on_offered_incompatible_qos_fn) dummy_cb);
+ dds_lset_publication_matched (dummylist, (dds_on_publication_matched_fn) dummy_cb);
+ dds_lset_requested_deadline_missed (dummylist, (dds_on_requested_deadline_missed_fn) dummy_cb);
+ dds_lset_requested_incompatible_qos (dummylist, (dds_on_requested_incompatible_qos_fn) dummy_cb);
+ dds_lset_sample_lost (dummylist, (dds_on_sample_lost_fn) dummy_cb);
+ dds_lset_sample_rejected (dummylist, (dds_on_sample_rejected_fn) dummy_cb);
+ dds_lset_subscription_matched (dummylist, dummy_subscription_matched_cb);
+ if ((ctx->pubrd[domid] = dds_create_reader (ctx->es[ent], DDS_BUILTIN_TOPIC_DCPSPUBLICATION, NULL, dummylist)) <= 0)
+ error_dds (ctx, ctx->pubrd[domid], "make_participant: create DCPSPublication reader in domain %"PRIu32, domid);
+ if ((ctx->subrd[domid] = dds_create_reader (ctx->es[ent], DDS_BUILTIN_TOPIC_DCPSSUBSCRIPTION, NULL, dummylist)) <= 0)
+ error_dds (ctx, ctx->subrd[domid], "make_participant: create DCPSSubscription reader in domain %"PRIu32, domid);
+ dds_delete_listener (dummylist);
+ //printf ("pubrd %"PRId32" subrd %"PRId32" sub %"PRId32"\n", es->pubrd[domid], es->subrd[domid], dds_get_parent (es->pubrd[domid]));
+}
+
+static void make_entity1 (struct oneliner_ctx *ctx, int ent, dds_listener_t *list)
+{
+ entname_t wrname;
+ dds_return_t ret;
+ int domid = ent / 9;
+ int ent1 = ent % 9;
+ switch (ent1)
+ {
+ case 0:
+ make_participant (ctx, ent, list);
+ break;
+ case 1:
+ if (ctx->es[ent-1] == 0)
+ {
+ printf ("[");
+ make_entity1 (ctx, ent-1, NULL);
+ printf ("] ");
+ }
+ printf ("create subscriber %s", getentname (&wrname, ent));
+ fflush (stdout);
+ ctx->es[ent] = dds_create_subscriber (ctx->es[ent-1], NULL, list);
+ break;
+ case 2:
+ if (ctx->es[ent-2] == 0)
+ {
+ printf ("[");
+ make_entity1 (ctx, ent-2, NULL);
+ printf ("] ");
+ }
+ printf ("create publisher %s", getentname (&wrname, ent));
+ fflush (stdout);
+ ctx->es[ent] = dds_create_publisher (ctx->es[ent-2], NULL, list);
+ break;
+ case 3: case 4: case 5:
+ if (ctx->es[9*domid+1] == 0)
+ {
+ printf ("[");
+ make_entity1 (ctx, 9*domid+1, NULL);
+ printf ("] ");
+ }
+ printf ("create reader %s", getentname (&wrname, ent));
+ fflush (stdout);
+ ctx->es[ent] = dds_create_reader (ctx->es[9*domid+1], ctx->tps[domid], ctx->rwqos, list);
+ break;
+ case 6: case 7: case 8:
+ if (ctx->es[9*domid+2] == 0)
+ {
+ printf ("[");
+ make_entity1 (ctx, 9*domid+2, NULL);
+ printf ("] ");
+ }
+ printf ("create writer %s", getentname (&wrname, ent));
+ fflush (stdout);
+ ctx->es[ent] = dds_create_writer (ctx->es[9*domid+2], ctx->tps[domid], ctx->rwqos, list);
+ break;
+ default:
+ abort ();
+ }
+ printf (" = %"PRId32, ctx->es[ent]);
+ fflush (stdout);
+ if (ctx->es[ent] <= 0)
+ error_dds (ctx, ctx->es[ent], "create entity %d failed", ent);
+ if ((ret = dds_get_instance_handle (ctx->es[ent], &ctx->esi[ent])) != 0)
+ error_dds (ctx, ret, "get instance handle for entity %"PRId32" failed", ctx->es[ent]);
+ //printf (" %"PRIx64, es->esi[ent]);
+ //fflush (stdout);
+}
+
+static void make_entity (struct oneliner_ctx *ctx, int ent, dds_listener_t *list)
+{
+ make_entity1 (ctx, ent, list);
+ printf ("\n");
+}
+
+static void setlistener (struct oneliner_ctx *ctx, struct oneliner_lex *l, int ll, int ent)
+{
+ printf ("set listener:");
+ dds_return_t ret;
+ int dom = ent / 9;
+ dds_listener_t *list = ctx->cb[dom].list;
+ dds_reset_listener (list);
+ do {
+ printf (" %s", lldesc[ll].name);
+ switch (ll)
+ {
+ case 0: dds_lset_data_available (list, data_available_cb); break;
+ case 1: dds_lset_data_on_readers (list, data_on_readers_cb); break;
+ case 2: dds_lset_inconsistent_topic (list, inconsistent_topic_cb); break;
+ case 3: dds_lset_liveliness_changed (list, liveliness_changed_cb); break;
+ case 4: dds_lset_liveliness_lost (list, liveliness_lost_cb); break;
+ case 5: dds_lset_offered_deadline_missed (list, offered_deadline_missed_cb); break;
+ case 6: dds_lset_offered_incompatible_qos (list, offered_incompatible_qos_cb); break;
+ case 7: dds_lset_publication_matched (list, publication_matched_cb); break;
+ case 8: dds_lset_requested_deadline_missed (list, requested_deadline_missed_cb); break;
+ case 9: dds_lset_requested_incompatible_qos (list, requested_incompatible_qos_cb); break;
+ case 10: dds_lset_sample_lost (list, sample_lost_cb); break;
+ case 11: dds_lset_sample_rejected (list, sample_rejected_cb); break;
+ case 12: dds_lset_subscription_matched (list, subscription_matched_cb); break;
+ default: abort ();
+ }
+ } while (l && (ll = parse_listener1 (l)) >= 0);
+ if (ctx->es[ent] == 0)
+ {
+ printf (" for ");
+ make_entity (ctx, ent, list);
+ }
+ else
+ {
+ dds_listener_t *tmplist = dds_create_listener (&ctx->cb[dom]);
+ if ((ret = dds_get_listener (ctx->es[ent], tmplist)) != 0)
+ {
+ dds_delete_listener (tmplist);
+ error_dds (ctx, ret, "set listener: dds_get_listener failed on %"PRId32, ctx->es[ent]);
+ }
+ dds_merge_listener (list, tmplist);
+ dds_delete_listener (tmplist);
+ printf (" on entity %"PRId32"\n", ctx->es[ent]);
+ if ((ret = dds_set_listener (ctx->es[ent], list)) != 0)
+ error_dds (ctx, ret, "set listener: dds_set_listener failed on %"PRId32, ctx->es[ent]);
+ }
+}
+
+static dds_instance_handle_t lookup_insthandle (const struct oneliner_ctx *ctx, int ent, int ent1)
+{
+ // if both are in the same domain, it's easy
+ if (ent / 9 == ent1 / 9)
+ return ctx->esi[ent1];
+ else
+ {
+ // if they aren't ... find GUID from instance handle in the one domain,
+ // then find instance handle for GUID in the other
+ dds_entity_t rd1 = 0, rd2 = 0;
+ switch (ent1 % 9)
+ {
+ case 3: case 4: case 5: rd1 = ctx->subrd[ent1/9]; rd2 = ctx->subrd[ent/9]; break;
+ case 6: case 7: case 8: rd1 = ctx->pubrd[ent1/9]; rd2 = ctx->pubrd[ent/9]; break;
+ default: return 0;
+ }
+
+ dds_builtintopic_endpoint_t keysample;
+ //printf ("(in %"PRId32" %"PRIx64" -> ", rd1, es->esi[ent1]);
+ //fflush (stdout);
+ if (dds_instance_get_key (rd1, ctx->esi[ent1], &keysample) != 0)
+ return 0;
+ // In principle, only key fields are set in sample returned by get_key;
+ // in the case of a built-in topic that is extended to the participant
+ // key. The qos and topic/type names should not be set, and there is no
+ // (therefore) memory allocated for the sample.
+ assert (keysample.qos == NULL);
+ assert (keysample.topic_name == NULL);
+ assert (keysample.type_name == NULL);
+ //for (size_t j = 0; j < sizeof (keysample.key.v); j++)
+ // printf ("%s%02x", (j > 0 && j % 4 == 0) ? ":" : "", keysample.key.v[j]);
+ const dds_instance_handle_t ih = dds_lookup_instance (rd2, &keysample);
+ //printf (" -> %"PRIx64")", ih);
+ //fflush (stdout);
+ return ih;
+ }
+}
+
+static void print_timestamp (struct oneliner_ctx *ctx, dds_time_t ts)
+{
+ dds_time_t dt = ts - ctx->l.tref;
+ if ((dt % DDS_NSECS_IN_SEC) == 0)
+ printf ("@%"PRId64, dt / DDS_NSECS_IN_SEC);
+ else
+ {
+ unsigned frac = (unsigned) (dt % DDS_NSECS_IN_SEC);
+ int digs = 9;
+ while ((frac % 10) == 0)
+ {
+ digs--;
+ frac /= 10;
+ }
+ printf ("@%"PRId64".%0*u", dt / DDS_NSECS_IN_SEC, digs, frac);
+ }
+}
+
+static bool parse_sample_value (struct oneliner_ctx *ctx, Space_Type1 *s, bool *valid_data, int def)
+{
+ s->long_1 = s->long_2 = s->long_3 = def;
+ if (nexttok (&ctx->l, NULL) == TOK_INT) // key value (invalid sample)
+ {
+ if (ctx->l.v.i < 0)
+ return false;
+ s->long_1 = ctx->l.v.i;
+ *valid_data = false;
+ return true;
+ }
+ else if (ctx->l.tok == '(')
+ {
+ if (nexttok (&ctx->l, NULL) != TOK_INT || ctx->l.v.i < 0)
+ return false;
+ s->long_1 = ctx->l.v.i;
+ if (nexttok (&ctx->l, NULL) != ',' || nexttok (&ctx->l, NULL) != TOK_INT || ctx->l.v.i < 0)
+ return false;
+ s->long_2 = ctx->l.v.i;
+ if (nexttok (&ctx->l, NULL) != ',' || nexttok (&ctx->l, NULL) != TOK_INT || ctx->l.v.i < 0)
+ return false;
+ s->long_3 = ctx->l.v.i;
+ *valid_data = true;
+ return nexttok (&ctx->l, NULL) == ')';
+ }
+ else
+ {
+ return false;
+ }
+}
+
+struct doreadlike_sample {
+ uint32_t state;
+ bool valid_data;
+ dds_time_t ts;
+ int wrent;
+ dds_instance_handle_t wrih;
+ Space_Type1 data;
+};
+
+static bool wrname_from_pubhandle (const struct oneliner_ctx *ctx, int ent, dds_instance_handle_t pubhandle, entname_t *wrname)
+{
+ dds_builtintopic_endpoint_t inf, inf1;
+ if (dds_instance_get_key (ctx->pubrd[ent/9], pubhandle, &inf) != 0)
+ return false;
+ for (int j = 0; j < (int) (sizeof (ctx->doms) / sizeof (ctx->doms[0])); j++)
+ {
+ for (int k = 6; k < 9; k++)
+ {
+ if (ctx->esi[9*j+k] != 0)
+ {
+ if (dds_instance_get_key (ctx->pubrd[j], ctx->esi[9*j+k], &inf1) != 0)
+ return false;
+ if (memcmp (&inf.key, &inf1.key, sizeof (inf.key)) == 0)
+ {
+ getentname (wrname, 9*j+k);
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+static bool doreadlike_parse_sample (struct oneliner_ctx *ctx, struct doreadlike_sample *s)
+{
+ static const char *statechars = "fsaudno";
+ static const uint32_t statemap[] = {
+ DDS_NOT_READ_SAMPLE_STATE, DDS_READ_SAMPLE_STATE,
+ DDS_ALIVE_INSTANCE_STATE, DDS_NOT_ALIVE_NO_WRITERS_INSTANCE_STATE, DDS_NOT_ALIVE_DISPOSED_INSTANCE_STATE,
+ DDS_NEW_VIEW_STATE, DDS_NOT_NEW_VIEW_STATE
+ };
+ // syntax: [state]k[pubhandle][@ts] or [state](k,l,m)[pubhandle][@ts]
+ // the first is an invalid sample, the second a valid one, the third says anything goes
+ // state is a combination of: sample state (F,S fresh/stale), instance state (A,U,D), view state (N,O)
+ // unspecified: don't care
+ s->state = 0;
+ s->ts = -1;
+ s->wrent = -1;
+ s->wrih = 0;
+ struct oneliner_lex l1 = ctx->l;
+ if (nexttok_if (&ctx->l, TOK_NAME))
+ {
+ char *inp1 = ctx->l.v.n;
+ char *p;
+ while (*inp1 && (p = strchr (statechars, *inp1)) != NULL)
+ {
+ s->state |= statemap[(int) (p - statechars)];
+ inp1++;
+ }
+ if (*inp1 == 0)
+ ;
+ else if (!isdigit (*inp1))
+ return false;
+ else // rewind input to digit
+ ctx->l.inp = l1.inp + (inp1 - ctx->l.v.n);
+ }
+ // missing states: allow everything
+ if ((s->state & (statemap[0] | statemap[1])) == 0)
+ s->state |= statemap[0] | statemap[1];
+ if ((s->state & (statemap[2] | statemap[3] | statemap[4])) == 0)
+ s->state |= statemap[2] | statemap[3] | statemap[4];
+ if ((s->state & (statemap[5] | statemap[6])) == 0)
+ s->state |= statemap[5] | statemap[6];
+ if (!parse_sample_value (ctx, &s->data, &s->valid_data, -1))
+ return false;
+ s->wrent = parse_entity1 (&ctx->l, NULL);
+ if (nexttok_if (&ctx->l, TOK_TIMESTAMP))
+ s->ts = ctx->l.v.d;
+ return true;
+}
+
+static bool doreadlike_ismatch (const dds_sample_info_t *si, const Space_Type1 *s, const struct doreadlike_sample *exp)
+{
+ return (si->valid_data == exp->valid_data &&
+ (si->sample_state & exp->state) != 0 &&
+ (si->instance_state & exp->state) != 0 &&
+ (si->view_state & exp->state) != 0 &&
+ (exp->data.long_1 < 0 || s->long_1 == exp->data.long_1) &&
+ (!exp->valid_data || exp->data.long_2 < 0 || s->long_2 == exp->data.long_2) &&
+ (!exp->valid_data || exp->data.long_3 < 0 || s->long_3 == exp->data.long_3) &&
+ (exp->ts < 0 || si->source_timestamp == exp->ts) &&
+ (exp->wrent < 0 || si->publication_handle == exp->wrih));
+}
+
+static bool doreadlike_matchstep (const dds_sample_info_t *si, const Space_Type1 *s, const struct doreadlike_sample *exp, int nexp, bool ellipsis, unsigned *tomatch, int *cursor, dds_instance_handle_t *lastih, int *matchidx)
+{
+ if (si->instance_handle != *lastih)
+ {
+ *lastih = si->instance_handle;
+ *cursor = -1;
+ for (int m = 0; m < nexp; m++)
+ {
+ if ((*tomatch & (1u << m)) && s->long_1 == exp[m].data.long_1)
+ {
+ *cursor = m;
+ break;
+ }
+ }
+ }
+ if (*cursor < 0 || *cursor >= nexp)
+ {
+ *matchidx = ellipsis ? nexp : -1;
+ return ellipsis;
+ }
+ else if (doreadlike_ismatch (si, s, &exp[*cursor]))
+ {
+ *matchidx = *cursor;
+ *tomatch &= ~(1u << *cursor);
+ (*cursor)++;
+ return true;
+ }
+ else if (ellipsis)
+ {
+ *matchidx = nexp;
+ return true;
+ }
+ else
+ {
+ *matchidx = -1;
+ return false;
+ }
+}
+
+static void doreadlike (struct oneliner_ctx *ctx, const char *name, dds_return_t (*fn) (dds_entity_t, void **buf, dds_sample_info_t *, size_t, uint32_t))
+{
+#define MAXN 10
+ struct doreadlike_sample exp[MAXN];
+ int nexp = 0;
+ bool ellipsis = false;
+ int exp_nvalid = -1, exp_ninvalid = -1;
+ int ent;
+ switch (peektok (&ctx->l, NULL))
+ {
+ default: // no expectations
+ ellipsis = true;
+ break;
+ case '(': // (# valid, # invalid)
+ nexttok (&ctx->l, NULL);
+ if (!(nexttok_int (&ctx->l, &exp_nvalid) && nexttok_if (&ctx->l, ',') && nexttok_int (&ctx->l, &exp_ninvalid) && nexttok_if (&ctx->l, ')')))
+ error (ctx, "%s: expecting (NINVALID, NVALID)", name);
+ ellipsis = true;
+ break;
+ case '{':
+ nexttok (&ctx->l, NULL);
+ if (!nexttok_if (&ctx->l, '}'))
+ {
+ do {
+ if (nexttok_if (&ctx->l, TOK_ELLIPSIS)) {
+ ellipsis = true; break;
+ } else if (nexp == MAXN) {
+ error (ctx, "%s: too many samples specified", name);
+ } else if (!doreadlike_parse_sample (ctx, &exp[nexp++])) {
+ error (ctx, "%s: expecting sample", name);
+ }
+ } while (nexttok_if (&ctx->l, ','));
+ if (!nexttok_if (&ctx->l, '}'))
+ error (ctx, "%s: expecting '}'", name);
+ }
+ break;
+ }
+ if ((ent = parse_entity1 (&ctx->l, NULL)) < 0)
+ error (ctx, "%s: entity required", name);
+
+ for (int i = 0; i < nexp; i++)
+ {
+ if (exp[i].wrent >= 0 && (exp[i].wrih = lookup_insthandle (ctx, ent, exp[i].wrent)) == 0)
+ error (ctx, "%s: instance lookup failed", name);
+ }
+
+ printf ("entity %"PRId32": %s: ", ctx->es[ent], (fn == dds_take) ? "take" : "read");
+ fflush (stdout);
+ Space_Type1 data[MAXN];
+ void *raw[MAXN];
+ for (int i = 0; i < MAXN; i++)
+ raw[i] = &data[i];
+ int matchidx[MAXN];
+ dds_sample_info_t si[MAXN];
+ DDSRT_STATIC_ASSERT (MAXN < CHAR_BIT * sizeof (unsigned));
+ const uint32_t maxs = (uint32_t) (sizeof (raw) / sizeof (raw[0]));
+ const int32_t n = fn (ctx->es[ent], raw, si, maxs, maxs);
+ if (n < 0)
+ error_dds (ctx, n, "%s: failed on %"PRId32, name, ctx->es[ent]);
+ unsigned tomatch = (1u << nexp) - 1; // used to track result entries matched by spec
+ dds_instance_handle_t lastih = 0;
+ int cursor = -1;
+ int count[2] = { 0, 0 };
+ bool matchok = true;
+ printf ("{");
+ for (int i = 0; i < n; i++)
+ {
+ const Space_Type1 *s = raw[i];
+ entname_t wrname;
+ count[si[i].valid_data]++;
+ printf ("%s%c%c%c",
+ (i > 0) ? "," : "",
+ (si[i].sample_state == DDS_NOT_READ_SAMPLE_STATE) ? 'f' : 's',
+ (si[i].instance_state == DDS_ALIVE_INSTANCE_STATE) ? 'a' : (si[i].instance_state == DDS_NOT_ALIVE_NO_WRITERS_INSTANCE_STATE) ? 'u' : 'd',
+ (si[i].view_state == DDS_NEW_VIEW_STATE) ? 'n' : 'o');
+ if (si[i].valid_data)
+ printf ("(%"PRId32",%"PRId32",%"PRId32")", s->long_1, s->long_2, s->long_3);
+ else
+ printf ("%"PRId32, s->long_1);
+ if (!wrname_from_pubhandle (ctx, ent, si[i].publication_handle, &wrname))
+ error (ctx, "%s: unknown publication handle received", name);
+ printf ("%s", wrname.n);
+ print_timestamp (ctx, si[i].source_timestamp);
+ if (!doreadlike_matchstep (&si[i], s, exp, nexp, ellipsis, &tomatch, &cursor, &lastih, &matchidx[i]))
+ matchok = false;
+ }
+ printf ("}:");
+ for (int i = 0; i < n; i++)
+ printf (" %d", matchidx[i]);
+ if (tomatch != 0)
+ {
+ printf (" (samples missing)");
+ matchok = false;
+ }
+ printf (" valid %d %d invalid %d %d", count[1], exp_nvalid, count[0], exp_ninvalid);
+ if (exp_nvalid >= 0 && (count[1] != exp_nvalid))
+ matchok = false;
+ if (exp_ninvalid >= 0 && (count[0] != exp_ninvalid))
+ matchok = false;
+ printf ("\n");
+ fflush (stdout);
+ if (!matchok)
+ testfail (ctx, "%s: mismatch between actual and expected set\n", name);
+#undef MAXN
+}
+
+static void dotake (struct oneliner_ctx *ctx) { doreadlike (ctx, "take", dds_take); }
+static void doread (struct oneliner_ctx *ctx) { doreadlike (ctx, "read", dds_read); }
+
+static void dowritelike (struct oneliner_ctx *ctx, const char *name, bool fail, dds_return_t (*fn) (dds_entity_t wr, const void *sample, dds_time_t ts))
+{
+ dds_return_t ret;
+ dds_time_t ts = dds_time ();
+ bool valid_data;
+ int ent;
+ Space_Type1 sample;
+ if ((ent = parse_entity (ctx)) < 0)
+ error (ctx, "%s: expecting entity", name);
+ if (ctx->es[ent] == 0)
+ make_entity (ctx, ent, NULL);
+ if (!parse_sample_value (ctx, &sample, &valid_data, 0))
+ error (ctx, "%s: expecting sample value", name);
+ if (nexttok_if (&ctx->l, TOK_TIMESTAMP))
+ ts = ctx->l.v.d;
+ printf ("entity %"PRId32": %s (%"PRId32",%"PRId32",%"PRId32")", ctx->es[ent], name, sample.long_1, sample.long_2, sample.long_3);
+ print_timestamp (ctx, ts);
+ printf ("\n");
+ ret = fn (ctx->es[ent], &sample, ts);
+ if (!fail)
+ {
+ if (ret != 0)
+ error_dds (ctx, ret, "%s: failed", name);
+ }
+ else
+ {
+ if (ret == 0)
+ testfail (ctx, "%s: succeeded unexpectedly", name);
+ else if (ret != DDS_RETCODE_TIMEOUT)
+ error_dds (ctx, ret, "%s: failed", name);
+ }
+}
+
+static void dowr (struct oneliner_ctx *ctx) { dowritelike (ctx, "wr", false, dds_write_ts); }
+static void dowrfail (struct oneliner_ctx *ctx) { dowritelike (ctx, "wrfail", true, dds_write_ts); }
+static void dowrdisp (struct oneliner_ctx *ctx) { dowritelike (ctx, "wrdisp", false, dds_writedispose_ts); }
+static void dowrdispfail (struct oneliner_ctx *ctx) { dowritelike (ctx, "wrdispfail", true, dds_writedispose_ts); }
+static void dodisp (struct oneliner_ctx *ctx) { dowritelike (ctx, "disp", false, dds_dispose_ts); }
+static void dodispfail (struct oneliner_ctx *ctx) { dowritelike (ctx, "dispfail", true, dds_dispose_ts); }
+static void dounreg (struct oneliner_ctx *ctx) { dowritelike (ctx, "unreg", false, dds_unregister_instance_ts); }
+static void dounregfail (struct oneliner_ctx *ctx) { dowritelike (ctx, "unregfail", true, dds_unregister_instance_ts); }
+
+static int checkstatus (struct oneliner_ctx *ctx, int ll, int ent, struct oneliner_lex *argl, const void *status)
+{
+ assert (lldesc[ll].desc != NULL);
+ const char *d = lldesc[ll].desc;
+ int field = 0;
+ const char *sep = "(";
+ size_t off = 0;
+ if (nexttok (argl, NULL) != '(')
+ abort ();
+ while (*d)
+ {
+ const void *p = advance (status, &off, *d);
+ int i;
+ switch (*d)
+ {
+ case 'n':
+ if (!nexttok_int (argl, &i) || i < 0)
+ return setresult (ctx, -1, "checkstatus: field %d expecting non-negative integer", field);
+ printf ("%s%"PRIu32" %d", sep, *(uint32_t *)p, i); fflush (stdout);
+ if (*(uint32_t *)p != (uint32_t)i)
+ return setresult (ctx, 0, "checkstatus: field %d has actual %"PRIu32" expected %d", field, *(uint32_t *)p, i);
+ break;
+ case 'c':
+ if (!nexttok_int (argl, &i))
+ return setresult (ctx, -1, "checkstatus: field %d expecting integer", field);
+ printf ("%s%"PRId32" %d", sep, *(int32_t *)p, i); fflush (stdout);
+ if (*(int32_t *)p != i)
+ return setresult (ctx, 0, "checkstatus: field %d has actual %"PRId32" expected %d", field, *(int32_t *)p, i);
+ break;
+ case 'P':
+ if (nexttok (argl, NULL) != TOK_NAME)
+ return setresult (ctx, -1, "checkstatus: field %d expecting policy name", field);
+ size_t polidx;
+ for (polidx = 0; polidx < sizeof (qostab) / sizeof (qostab[0]); polidx++)
+ if (strcmp (argl->v.n, qostab[polidx].abbrev) == 0)
+ break;
+ if (polidx == sizeof (qostab) / sizeof (qostab[0]))
+ return setresult (ctx, -1, "checkstatus: field %d expecting policy name", field);
+ printf ("%s%"PRIu32" %"PRIu32, sep, *(uint32_t *)p, (uint32_t) qostab[polidx].id); fflush (stdout);
+ if (*(uint32_t *)p != (uint32_t) qostab[polidx].id)
+ return setresult (ctx, 0, "checkstatus: field %d has actual %"PRIu32" expected %d", field, *(uint32_t *)p, (int) qostab[polidx].id);
+ break;
+ case 'R':
+ if (nexttok (argl, NULL) != TOK_NAME)
+ return setresult (ctx, -1, "checkstatus: field %d expecting reason", field);
+ if (strcmp (argl->v.n, "i") == 0) i = (int) DDS_REJECTED_BY_INSTANCES_LIMIT;
+ else if (strcmp (argl->v.n, "s") == 0) i = (int) DDS_REJECTED_BY_SAMPLES_LIMIT;
+ else if (strcmp (argl->v.n, "spi") == 0) i = (int) DDS_REJECTED_BY_SAMPLES_PER_INSTANCE_LIMIT;
+ else return setresult (ctx, -1, "checkstatus: field %d expecting reason", field);
+ printf ("%s%d %d", sep, (int) *(dds_sample_rejected_status_kind *)p, i); fflush (stdout);
+ if (*(dds_sample_rejected_status_kind *)p != (dds_sample_rejected_status_kind) i)
+ return setresult (ctx, 0, "checkstatus: field %d has actual %d expected %d", field, (int) (*(dds_sample_rejected_status_kind *)p), i);
+ break;
+ case 'I': // instance handle is too complicated
+ break;
+ case 'E': {
+ int ent1 = -1;
+ dds_instance_handle_t esi1 = 0;
+ if (nexttok_if (argl, '*'))
+ ent1 = -1;
+ else if ((ent1 = parse_entity1 (argl, NULL)) < 0)
+ return setresult (ctx, -1, "checkstatus: field %d expecting * or entity name", field);
+ else if ((esi1 = lookup_insthandle (ctx, ent, ent1)) == 0)
+ return setresult (ctx, -1, "checkstatus: field %d instance handle lookup failed", field);
+ printf ("%s%"PRIx64" %"PRIx64, sep, *(dds_instance_handle_t *)p, esi1); fflush (stdout);
+ if (ent1 >= 0 && *(dds_instance_handle_t *)p != esi1)
+ return setresult (ctx, 0, "checkstatus: field %d has actual %"PRIx64" expected %"PRIx64, field, *(dds_instance_handle_t *)p, esi1);
+ break;
+ }
+ default:
+ return DDS_RETCODE_BAD_PARAMETER;
+ }
+ sep = ", ";
+ if (*d != 'I')
+ field++;
+ ++d;
+ if (*d && *d != 'I' && !nexttok_if (argl, ','))
+ return setresult (ctx, -1, "checkstatus: field %d expecting ','", field);
+ }
+ printf (")");
+ if (!nexttok_if (argl, ')'))
+ return setresult (ctx, -1, "checkstatus: field %d expecting ')'", field);
+ assert (off <= lldesc[ll].size);
+ return 1;
+}
+
+static void checklistener (struct oneliner_ctx *ctx, int ll, int ent, struct oneliner_lex *argl)
+{
+ bool signalled = true;
+ uint32_t min_cnt = 1, max_cnt = UINT32_MAX;
+ uint32_t status;
+ const int dom = ent / 9;
+ dds_return_t ret;
+ printf ("listener %s: check called for entity %"PRId32, lldesc[ll].name, ctx->es[ent]);
+ fflush (stdout);
+ if (argl && lldesc[ll].cb_status_off == 0)
+ {
+ // those that don't have a status can check the number of invocations
+ int cnt = -1;
+ if (!(nexttok_if (argl, '(') && nexttok_int (argl, &cnt) && nexttok_if (argl, ')')))
+ error (ctx, "listener %s: expecting (COUNT)", lldesc[ll].name);
+ if (cnt < 0)
+ error (ctx, "listener %s: invocation count must be at least 0", lldesc[ll].name);
+ min_cnt = max_cnt = (uint32_t) cnt;
+ }
+ ddsrt_mutex_lock (&ctx->g_mutex);
+ bool cnt_ok = (ctx->cb[dom].cb_called[lldesc[ll].id] >= min_cnt && ctx->cb[dom].cb_called[lldesc[ll].id] <= max_cnt);
+ while (ctx->cb[dom].cb_called[lldesc[ll].id] < min_cnt && signalled)
+ {
+ signalled = ddsrt_cond_waitfor (&ctx->g_cond, &ctx->g_mutex, DDS_SECS (5));
+ cnt_ok = (ctx->cb[dom].cb_called[lldesc[ll].id] >= min_cnt && ctx->cb[dom].cb_called[lldesc[ll].id] <= max_cnt);
+ }
+ printf (" cb_called %"PRIu32" (%s)", ctx->cb[dom].cb_called[lldesc[ll].id], cnt_ok ? "ok" : "fail");
+ fflush (stdout);
+ if (!cnt_ok)
+ {
+ ddsrt_mutex_unlock (&ctx->g_mutex);
+ testfail (ctx, "listener %s: not invoked [%"PRIu32",%"PRIu32"] times", lldesc[ll].name, min_cnt, max_cnt);
+ }
+ dds_entity_t * const cb_entity = (dds_entity_t *) ((char *) &ctx->cb[dom] + lldesc[ll].cb_entity_off);
+ printf (" cb_entity %"PRId32" %"PRId32" (%s)", *cb_entity, ctx->es[ent], (*cb_entity == ctx->es[ent]) ? "ok" : "fail");
+ fflush (stdout);
+ if (*cb_entity != ctx->es[ent])
+ {
+ ddsrt_mutex_unlock (&ctx->g_mutex);
+ testfail (ctx, "listener %s: invoked on %"PRId32" instead of %"PRId32, lldesc[ll].name, *cb_entity, ctx->es[ent]);
+ }
+ if (!(ctx->doms[0] && ctx->doms[1]))
+ {
+ // FIXME: two domains: listener invocation happens on another thread and we can observe non-0 "change" fields
+ // they get updated, listener gets invoked, then they get reset -- pretty sure it is allowed by the spec, but
+ // not quite elegant
+ if ((ret = check_status_change_fields_are_0 (ll, ctx->es[ent])) <= 0)
+ {
+ ddsrt_mutex_unlock (&ctx->g_mutex);
+ if (ret == 0)
+ testfail (ctx, "listener %s: status contains non-zero change fields", lldesc[ll].name);
+ else if (ret < 0)
+ error_dds (ctx, ret, "listener %s: get entity status failed", lldesc[ll].name);
+ }
+ }
+ if (argl && lldesc[ll].cb_status_off != 0)
+ {
+ void *cb_status = (char *) &ctx->cb[dom] + lldesc[ll].cb_status_off;
+ if (checkstatus (ctx, ll, ent, argl, cb_status) <= 0)
+ {
+ ddsrt_mutex_unlock (&ctx->g_mutex);
+ longjmp (ctx->jb, 1);
+ }
+ }
+ printf ("\n");
+ ctx->cb[dom].cb_called[lldesc[ll].id] = 0;
+ ddsrt_mutex_unlock (&ctx->g_mutex);
+ if ((ret = dds_get_status_changes (ctx->es[ent], &status)) != 0)
+ error_dds (ctx, ret, "listener %s: dds_get_status_change on %"PRId32, lldesc[ll].name, ctx->es[ent]);
+ if ((status & (1u << lldesc[ll].id)) != 0)
+ testfail (ctx, "listener %s: status mask not cleared", lldesc[ll].name);
+}
+
+static void dochecklistener (struct oneliner_ctx *ctx)
+{
+ const bool expectclear = nexttok_if (&ctx->l, '!');
+ const int ll = parse_listener (ctx);
+ if (ll < 0)
+ error (ctx, "check listener: requires listener name");
+ else if (expectclear)
+ {
+ printf ("listener %s: check not called", lldesc[ll].name);
+ fflush (stdout);
+ ddsrt_mutex_lock (&ctx->g_mutex);
+ bool ret = true;
+ for (int i = 0; i < (int) (sizeof (ctx->doms) / sizeof (ctx->doms[0])); i++)
+ {
+ printf (" cb_called %"PRIu32" %s\n", ctx->cb[i].cb_called[lldesc[ll].id], ctx->cb[i].cb_called[lldesc[ll].id] == 0 ? "ok" : "fail");
+ if (ctx->cb[i].cb_called[lldesc[ll].id] != 0)
+ ret = false;
+ }
+ ddsrt_mutex_unlock (&ctx->g_mutex);
+ if (!ret)
+ testfail (ctx, "callback %s invoked unexpectedly", lldesc[ll].name);
+ }
+ else
+ {
+ struct oneliner_lex l1 = ctx->l;
+ // no whitespace between name and args
+ const bool have_args = (*ctx->l.inp == '(');
+ if (have_args)
+ {
+ // skip args: we need the entity before we can interpret them
+ int tok;
+ while ((tok = nexttok (&ctx->l, NULL)) != EOF && tok != ')')
+ ;
+ }
+ const int ent = parse_entity (ctx);
+ if (ent < 0)
+ error (ctx, "check listener: requires an entity");
+ if (ctx->es[ent] == 0)
+ setlistener (ctx, NULL, ll, ent);
+ checklistener (ctx, ll, ent, have_args ? &l1 : NULL);
+ }
+}
+
+static void dodelete (struct oneliner_ctx *ctx)
+{
+ dds_return_t ret;
+ int ent;
+ if ((ent = parse_entity (ctx)) < 0)
+ error (ctx, "delete: requires entity");
+ if ((ret = dds_delete (ctx->es[ent])) != 0)
+ error_dds (ctx, ret, "delete: failed on %"PRId32, ctx->es[ent]);
+ ctx->es[ent] = 0;
+}
+
+static void dodeaf (struct oneliner_ctx *ctx)
+{
+ dds_return_t ret;
+ entname_t name;
+ int ent;
+ if ((ent = parse_entity (ctx)) < 0 || (ent % 9) != 0)
+ error (ctx, "deaf: requires participant");
+ printf ("deaf: %s\n", getentname (&name, ent));
+ if ((ret = dds_domain_set_deafmute (ctx->es[ent], true, false, DDS_INFINITY)) != 0)
+ error_dds (ctx, ret, "deaf: dds_domain_set_deafmute failed on %"PRId32, ctx->es[ent]);
+ // speed up the process by forcing lease expiry
+ dds_entity *x, *xprime;
+ if ((ret = dds_entity_pin (ctx->es[ent], &x)) < 0)
+ error_dds (ctx, ret, "deaf: pin participant failed %"PRId32, ctx->es[ent]);
+ for (int i = 0; i < (int) (sizeof (ctx->doms) / sizeof (ctx->doms[0])); i++)
+ {
+ if (i == ent / 9 || ctx->es[9*i] == 0)
+ continue;
+ if ((ret = dds_entity_pin (ctx->es[9*i], &xprime)) < 0)
+ {
+ dds_entity_unpin (x);
+ error_dds (ctx, ret, "deaf: pin counterpart participant failed %"PRId32, ctx->es[9*i]);
+ }
+ thread_state_awake (lookup_thread_state (), &x->m_domain->gv);
+ delete_proxy_participant_by_guid (&x->m_domain->gv, &xprime->m_guid, ddsrt_time_wallclock (), true);
+ thread_state_asleep (lookup_thread_state ());
+ dds_entity_unpin (xprime);
+ }
+ dds_entity_unpin (x);
+}
+
+static void dohearing (struct oneliner_ctx *ctx)
+{
+ dds_return_t ret;
+ entname_t name;
+ int ent;
+ if ((ent = parse_entity (ctx)) < 0 || (ent % 9) != 0)
+ error (ctx, "hearing: requires participant");
+ printf ("hearing: %s\n", getentname (&name, ent));
+ if ((ret = dds_domain_set_deafmute (ctx->es[ent], false, false, DDS_INFINITY)) != 0)
+ error_dds (ctx, ret, "hearing: dds_domain_set_deafmute failed %"PRId32, ctx->es[ent]);
+ // speed up the process by forcing SPDP publication on the remote
+ for (int i = 0; i < (int) (sizeof (ctx->doms) / sizeof (ctx->doms[0])); i++)
+ {
+ if (i == ent / 9 || ctx->es[9*i] == 0)
+ continue;
+ dds_entity *xprime;
+ struct participant *pp;
+ if ((ret = dds_entity_pin (ctx->es[9*i], &xprime)) < 0)
+ error_dds (ctx, ret, "hearing: pin counterpart participant failed %"PRId32, ctx->es[9*i]);
+ thread_state_awake (lookup_thread_state (), &xprime->m_domain->gv);
+ if ((pp = entidx_lookup_participant_guid (xprime->m_domain->gv.entity_index, &xprime->m_guid)) != NULL)
+ resched_xevent_if_earlier (pp->spdp_xevent, ddsrt_mtime_add_duration (ddsrt_time_monotonic (), DDS_MSECS (100)));
+ thread_state_asleep (lookup_thread_state ());
+ dds_entity_unpin (xprime);
+ }
+}
+
+static void dosleep (struct oneliner_ctx *ctx)
+{
+ if (nexttok_dur (&ctx->l, NULL, true) != TOK_DURATION)
+ error (ctx, "sleep: invalid duration");
+ dds_sleepfor (ctx->l.v.d);
+}
+
+static void dispatchcmd (struct oneliner_ctx *ctx)
+{
+ static const struct {
+ const char *name;
+ void (*fn) (struct oneliner_ctx *ct);
+ } cs[] = {
+ { "-", dodelete },
+ { "?", dochecklistener },
+ { "wr", dowr },
+ { "wrdisp", dowrdisp },
+ { "disp", dodisp },
+ { "unreg", dounreg },
+ { "wrfail", dowrfail },
+ { "wrdispfail", dowrdispfail },
+ { "dispfail", dodispfail },
+ { "unregfail", dounregfail },
+ { "take", dotake },
+ { "read", doread },
+ { "deaf", dodeaf },
+ { "hearing", dohearing },
+ { "sleep", dosleep }
+ };
+ size_t i;
+ if (ctx->l.tok > 0)
+ {
+ // convert single-character token to string
+ ctx->l.v.n[0] = (char) ctx->l.tok;
+ ctx->l.v.n[1] = 0;
+ }
+ for (i = 0; i < sizeof (cs) / sizeof (cs[0]); i++)
+ if (strcmp (ctx->l.v.n, cs[i].name) == 0)
+ break;
+ if (i == sizeof (cs) / sizeof (cs[0]))
+ error (ctx, "%s: unknown command", ctx->l.v.n);
+ cs[i].fn (ctx);
+}
+
+static void dosetlistener (struct oneliner_ctx *ctx, int ll)
+{
+ int ent;
+ struct oneliner_lex l1 = ctx->l;
+ // scan past listener names to get at the entity, which we need
+ // to get the right listener object (and hence argument)
+ while (parse_listener1 (&ctx->l) >= 0)
+ ;
+ if ((ent = parse_entity (ctx)) < 0)
+ error (ctx, "set listener: entity required");
+ setlistener (ctx, &l1, ll, ent);
+}
+
+static void test_oneliner_step1 (struct oneliner_ctx *ctx)
+{
+ while (peektok (&ctx->l, NULL) != TOK_END)
+ {
+ int ent, ll;
+ if (nexttok_if (&ctx->l, ';'))
+ ; // skip ;s
+ else if ((ent = parse_entity (ctx)) >= 0)
+ make_entity (ctx, ent, NULL);
+ else if ((ll = parse_listener (ctx)) >= 0)
+ dosetlistener (ctx, ll);
+ else if (nexttok (&ctx->l, NULL) == TOK_NAME || ctx->l.tok > 0)
+ dispatchcmd (ctx);
+ else
+ error (ctx, "unexpected token %d", ctx->l.tok);
+ }
+}
+
+void test_oneliner_init (struct oneliner_ctx *ctx)
+{
+ dds_qos_t *qos = dds_create_qos ();
+ dds_qset_reliability (qos, DDS_RELIABILITY_RELIABLE, DDS_MSECS (100));
+ dds_qset_destination_order (qos, DDS_DESTINATIONORDER_BY_SOURCE_TIMESTAMP);
+ dds_qset_history (qos, DDS_HISTORY_KEEP_ALL, 0);
+
+ *ctx = (struct oneliner_ctx) {
+ .l = { .tref = dds_time () },
+ .qos = qos,
+ .rwqos = dds_create_qos (),
+ .result = 1,
+ .cb = {
+ [0] = { .ctx = ctx, .list = dds_create_listener (&ctx->cb[0]) },
+ [1] = { .ctx = ctx, .list = dds_create_listener (&ctx->cb[1]) },
+ [2] = { .ctx = ctx, .list = dds_create_listener (&ctx->cb[2]) }
+ }
+ };
+
+ ddsrt_mutex_init (&ctx->g_mutex);
+ ddsrt_cond_init (&ctx->g_cond);
+
+ create_unique_topic_name ("ddsc_listener_test", ctx->topicname, sizeof (ctx->topicname));
+}
+
+int test_oneliner_step (struct oneliner_ctx *ctx, const char *ops)
+{
+ if (ctx->result > 0 && setjmp (ctx->jb) == 0)
+ {
+ ctx->l.inp = ops;
+ test_oneliner_step1 (ctx);
+ }
+ return ctx->result;
+}
+
+const char *test_oneliner_message (const struct oneliner_ctx *ctx)
+{
+ return ctx->msg;
+}
+
+int test_oneliner_fini (struct oneliner_ctx *ctx)
+{
+ for (size_t i = 0; i < sizeof (ctx->cb) / sizeof (ctx->cb[0]); i++)
+ dds_delete_listener (ctx->cb[i].list);
+ dds_delete_qos (ctx->rwqos);
+ dds_delete_qos ((dds_qos_t *) ctx->qos);
+ // prevent any listeners from being invoked so we can safely delete the
+ // mutex and the condition variable -- must do this going down the
+ // hierarchy, or listeners may remain set through inheritance
+ dds_return_t ret;
+ for (size_t i = 0; i < sizeof (ctx->es) / sizeof (ctx->es[0]); i++)
+ if (ctx->es[i] && (ret = dds_set_listener (ctx->es[i], NULL)) != 0)
+ setresult (ctx, ret, "terminate: reset listener failed on %"PRId32, ctx->es[i]);
+ if (ctx->result == 0)
+ {
+ printf ("\n");
+ for (int i = 0; i < (int) (sizeof (ctx->doms) / sizeof (ctx->doms[0])); i++)
+ {
+ for (int j = 3; j <= 5; j++)
+ {
+ if (ctx->es[9*i + j])
+ {
+ const char *inp_orig = ctx->l.inp;
+ entname_t n;
+ ctx->l.inp = getentname (&n, 9*i + j);
+ doreadlike (ctx, "read", dds_read);
+ ctx->l.inp = inp_orig;
+ }
+ }
+ }
+ }
+ ddsrt_mutex_destroy (&ctx->g_mutex);
+ ddsrt_cond_destroy (&ctx->g_cond);
+ for (size_t i = 0; i < sizeof (ctx->doms) / sizeof (ctx->doms[0]); i++)
+ if (ctx->doms[i] && (ret = dds_delete (ctx->doms[i])) != 0)
+ setresult (ctx, ret, "terminate: delete domain on %"PRId32, ctx->doms[i]);
+ return ctx->result;
+}
+
+int test_oneliner (const char *ops)
+{
+ struct oneliner_ctx ctx;
+ printf ("dotest: %s\n", ops);
+ test_oneliner_init (&ctx);
+ test_oneliner_step (&ctx, ops);
+ if (test_oneliner_fini (&ctx) <= 0)
+ fprintf (stderr, "FAIL: %s\n", test_oneliner_message (&ctx));
+ return ctx.result;
+}
diff --git a/src/core/ddsc/tests/test_oneliner.h b/src/core/ddsc/tests/test_oneliner.h
new file mode 100644
index 0000000000..1a8b75e0d0
--- /dev/null
+++ b/src/core/ddsc/tests/test_oneliner.h
@@ -0,0 +1,326 @@
+/*
+ * Copyright(c) 2020 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
+ */
+#ifndef _TEST_ONELINER_H_
+#define _TEST_ONELINER_H_
+
+#include
+#include
+
+#include "dds/dds.h"
+#include "dds/ddsrt/sync.h"
+
+/** @brief run a "test" consisting of a sequence of simplish operations
+ *
+ * This operation takes a test description, really a program in a bizarre syntax, and
+ * executes it. Any failures, be it because of error codes coming out of the Cyclone
+ * calls or expected values being wrong cause it to fail the test via CU_ASSERT_FATAL.
+ * While it is doing this, it outputs the test steps to stdout including some actual
+ * values. An invalid program is mostly reported by calling abort(). It is geared towards
+ * checking for listener invocations and the effects on statuses.
+ *
+ * Entities in play:
+ *
+ * - participants: P P' P''
+ * - subscribers: R R' R''
+ * - publishers: W W' W''
+ * - readers: r s t r' s' t' r'' s'' t''
+ * - writers: w x y w' x' y' w'' x'' y''
+ *
+ * The unprimed ones exist in domain 0, the primed ones in domain 1 (but configured such
+ * that it talks to domain 0), and the double-primed ones in domain 2 (again configured such
+ * that it talks to domain 0) so that network-related listener invocations can be checked
+ * as well.
+ *
+ * The first mention of an entity creates it as well as its ancestors. Implicitly created
+ * ancestors always have standard QoS and have no listeners. There is one topic that is
+ * created implicitly when the participant is created.
+ *
+ * Standard QoS is: default + reliable (100ms), by-source-timestamp, keep-all. The QoS of
+ * a reader/writer can be overridden at the first mention of it (i.e., when it is created)
+ * by appending a list of QoS overrides between parentheses.
+ *
+ * A program consists of a sequence of operations separated by whitespace, ';' or '/'
+ * (there is no meaning to the separators, they exist to allow visual grouping):
+ *
+ * PROGRAM ::= (OP (\s+|;)*)*
+ *
+ * OP ::= (LISTENER)* ENTITY-NAME[(QOS[,QOS[,QOS...]])]
+ *
+ * If entity ENTITY-NAME does not exist:
+ * creates the entity with the given listeners installed
+ * QOS can be used to override the standard QoS
+ * else
+ * changes the entity's listeners to the specified ones
+ * (see above for the valid ENTITY-NAMEs)
+ *
+ * | -ENTITY-NAME
+ *
+ * Deletes the specified entity
+ *
+ * | WRITE-LIKE[fail] ENTITY-NAME K[@DT]
+ * | WRITE-LIKE[fail] ENTITY-NAME (K,X,Y)[@DT]
+ *
+ * Writes/disposes/unregisters (K,0,0) (first form) or (K,X,Y). If
+ * "fail" is appended, the expectation is that it fails with a
+ * timeout, if @DT is appended, the timestamp is the start time of
+ * the test + s rather than the current time; DT is a
+ * floating-point number
+ *
+ * | READ-LIKE ENTITY-NAME
+ * | READ-LIKE(A,B) ENTITY-NAME
+ * | READ-LIKE{[S1[,S2[,S3...]][,...]} ENTITY-NAME
+ *
+ * Reads/takes at most 10 samples. The second form counts the
+ * number of valid and invalid samples seen and checks them against
+ * A and B.
+ *
+ * In the third form, the exact result set is given by the sample
+ * Si, which is a comma-separated list of samples:
+ *
+ * [STATE]K[ENTITY-NAME][@DT]
+ * [STATE](K,X,Y)[ENTITY-NAME][@DT]
+ *
+ * The first form is an invalid sample with only the (integer) key
+ * value K, the second form also specifies the two (integer)
+ * attribute fields.
+ *
+ * STATE specifies allowed sample (f - not-read (fresh), s - read
+ * (stale)), instance (a - alive, u - no-writers (unregistered) d -
+ * disposed) and view states (n - new, o - old). If no sample state
+ * is specified, all sample states are allowed, &c.
+ *
+ * ENTITY-NAME is the name of the publishing writer expected in the
+ * publication_handle. Not specifying a writer means any writer is
+ * ok. DT is the timestamp in the same manner as the write-like
+ * operations. Not specifying a timestamp means any timestamp is
+ * ok.
+ *
+ * If the expected set ends up with "..." there may be other samples
+ * in the result as well.
+ *
+ * | ?LISTENER[(ARGS)] ENTITY-NAME
+ *
+ * Waits until the specified listener has been invoked on using a flag set by the listener function, resets the flag
+ * and verifies that neither the entity status bit nor the "change"
+ * fields in the various statuses were set.
+ *
+ * ARGS is used to check the status argument most recently passed to
+ * the listener:
+ *
+ * da(A) verifies that it has been invoked A times
+ * dor(A) see da
+ * it(A,B) verifies count and change match A and B, policy
+ * matches RELIABILITY
+ * lc(A,B,C,D,E) verifies that alive and not-alive counts match A
+ * and B, that alive and not-alive changes match C and D
+ * and that the last handle matches E if an entity name
+ * (ignored if E = "*")
+ * ll (A,B) verifies count and change match A and B
+ * odm (A,B) verifies count and change match A and B, last handle
+ * is ignored
+ * oiq (A,B,C) verifies that total count and change match A and B
+ * and that the mismatching QoS is C (using the same
+ * abbreviations as used for defining QoS on entity
+ * creation)
+ * pm (A,B,C,D,E) verifies that total count and change match A and
+ * B, that current count and change match C and D and
+ * that the last handle matches E if an entity name
+ * (ignored if E = "*")
+ * rdm see odm
+ * riq see oiq
+ * sl (A,B) verifies that total count and change match A and B
+ * sr (A,B,C) verifies total count and change match A and B, and
+ * that the reason matches C (one of "s" for samples,
+ * "i" for instances, "spi" for samples per instance)
+ * sm see pm
+ *
+ * | ?!LISTENER
+ *
+ * (Not listener) tests that LISTENER has not been invoked since
+ * last reset
+ *
+ * | sleep D
+ *
+ * Delay program execution for D s (D is a floating-point number)
+ *
+ * | deaf ENTITY-NAME
+ * | hearing ENTITY-NAME
+ *
+ * Makes the domain wherein the specified entity exists deaf,
+ * respectively restoring hearing. The entity must be either P or
+ * P' and both must exist. Plays some tricks to speed up lease
+ * expiry and reconnection (like forcibly deleting a proxy
+ * participant or triggering the publication of SPDP packets).
+ *
+ * WRITE-LIKE ::= wr write
+ * | wrdisp write-dispose
+ * | disp dispose
+ * | unreg unregister
+ *
+ * READ-LIKE ::= read dds_read (so any state)
+ * | take dds_take (so any state)
+ *
+ * LISTENER ::= da data available (acts on a reader)
+ * | dor data on readers (acts on a subcsriber)
+ * | it incompatible topic (acts on a topic)
+ * | lc liveliness changed (acts on a reader)
+ * | ll liveliness lost (acts on a writer)
+ * | odm offered deadline missed (acts on a writer)
+ * | oiq offered incompatible QoS (acts on a writer)
+ * | pm publication matched (acts on a writer)
+ * | rdm requested deadline missed (acts on a reader)
+ * | riq requested incompatible QoS (acts on a reader)
+ * | sl sample lost (acts on a reader)
+ * | sr sample rejected (acts on a reader)
+ * | sm subscription matched (acts on a reader)
+ *
+ * QOS ::= ad={y|n} auto-dispose unregistered instances
+ * | d={v|tl|t|p} durability
+ * | dl={inf|DT} deadline (infinite or DT seconds)
+ * | ds=DT/H/RL durability service: cleanup delay, history,
+ * resource limits
+ * | do={r|s} by-reception or by-source destination order
+ * | h={N|all} history keep-last-N or keep-all
+ * | lb={inf|DT} latency budget
+ * | ll={a[:DT]|p:DT|w:DT} liveliness (automatic, manual by
+ * participant, manual by topic)
+ * | ls={inf|DT} lifespan
+ * | o={s|x[:N]} ownership shared or exclusive (strength N)
+ * | p={i|t|g} presentation: instance, coherent-topic or
+ * coherent-group
+ * | r={be|r[:DT]} best-effort or reliable (with max blocking time)
+ * | rl=N[/N[/N]] resource limits (sample, instances, samples per
+ * instance; "inf" is allowed, ommitted ones are
+ * unlimited)
+ * | tp=N transport-priority
+ * | ud=... user data (with escape sequences and hex/octal
+ * input allowed)
+ *
+ * All entities share the listeners with their global state. Only the latest invocation is visible.
+ *
+ * @param[in] ops Program to execute.
+ *
+ * @return > 0 success, 0 failure, < 0 invalid input
+ */
+int test_oneliner (const char *ops);
+
+union oneliner_tokval {
+ int i;
+ int64_t d;
+ char n[32];
+};
+
+struct oneliner_lex {
+ const char *inp;
+ dds_time_t tref;
+ int tok;
+ union oneliner_tokval v;
+};
+
+struct oneliner_ctx;
+
+struct oneliner_cb {
+ struct oneliner_ctx *ctx;
+ dds_listener_t *list;
+ uint32_t cb_called[DDS_STATUS_ID_MAX + 1];
+ dds_entity_t cb_topic, cb_writer, cb_reader, cb_subscriber;
+ dds_inconsistent_topic_status_t cb_inconsistent_topic_status;
+ dds_liveliness_changed_status_t cb_liveliness_changed_status;
+ dds_liveliness_lost_status_t cb_liveliness_lost_status;
+ dds_offered_deadline_missed_status_t cb_offered_deadline_missed_status;
+ dds_offered_incompatible_qos_status_t cb_offered_incompatible_qos_status;
+ dds_publication_matched_status_t cb_publication_matched_status;
+ dds_requested_deadline_missed_status_t cb_requested_deadline_missed_status;
+ dds_requested_incompatible_qos_status_t cb_requested_incompatible_qos_status;
+ dds_sample_lost_status_t cb_sample_lost_status;
+ dds_sample_rejected_status_t cb_sample_rejected_status;
+ dds_subscription_matched_status_t cb_subscription_matched_status;
+};
+
+struct oneliner_ctx {
+ struct oneliner_lex l;
+
+ dds_entity_t es[3 * 9];
+ dds_entity_t tps[3];
+ dds_entity_t doms[3];
+ dds_instance_handle_t esi[3 * 9];
+ // built-in topic readers for cross-referencing instance handles
+ dds_entity_t pubrd[3];
+ dds_entity_t subrd[3];
+ // topic name used for data
+ char topicname[100];
+
+ const dds_qos_t *qos;
+ dds_qos_t *rwqos;
+
+ int result;
+ char msg[256];
+
+ jmp_buf jb;
+
+ ddsrt_mutex_t g_mutex;
+ ddsrt_cond_t g_cond;
+ struct oneliner_cb cb[3];
+};
+
+/** @brief Initialize a "oneliner test" context
+ *
+ * @param[out] ctx context to initialize
+ */
+void test_oneliner_init (struct oneliner_ctx *ctx);
+
+/** @brief Run a sequence of operations in an initialized context
+ *
+ * If the context indicates a preceding step has failed, this is a
+ * no-op and the previous result is propagated to the return value.
+ *
+ * @param[in,out] ctx context to operate in
+ * @param[in] ops sequence of operations to execute (@ref test_oneliner)
+ *
+ * @return integer indicating success or failure
+ *
+ * @retval 1 success
+ * @retval 0 test failure
+ * @retval <0 syntax error unexpected error
+ */
+int test_oneliner_step (struct oneliner_ctx *ctx, const char *ops);
+
+/** @brief Get a pointer to the error message from a "oneliner test"
+ *
+ * If a preceding step has failed, this returns a pointer to a message
+ * containing some information about the failure. If no error
+ * occurred, the message is meaningless.
+ *
+ *
+ * @param[in] ctx context to retrieve message from
+ *
+ * @return pointer to null-terminated string aliasing a string in ctx
+ */
+const char *test_oneliner_message (const struct oneliner_ctx *ctx);
+
+/** @brief Deinitialize a "oneliner test" context
+ *
+ * This releases all resources used by the context.
+ *
+ * @param[in,out] ctx context to operate in
+ *
+ * @return integer indicating success or failure in any of the
+ * preceding steps. If no steps were taken, the result is success.
+ *
+ * @retval 1 success
+ * @retval 0 test failure
+ * @retval <0 syntax error unexpected error
+ */
+int test_oneliner_fini (struct oneliner_ctx *ctx);
+
+#endif
diff --git a/src/core/ddsi/include/dds/ddsi/q_entity.h b/src/core/ddsi/include/dds/ddsi/q_entity.h
index 9d2765e48c..14f59f8267 100644
--- a/src/core/ddsi/include/dds/ddsi/q_entity.h
+++ b/src/core/ddsi/include/dds/ddsi/q_entity.h
@@ -696,7 +696,7 @@ int writer_set_notalive (struct writer *wr, bool notify);
#define CF_PROXYPP_NO_SPDP (1 << 2)
void new_proxy_participant (struct ddsi_domaingv *gv, const struct ddsi_guid *guid, uint32_t bes, const struct ddsi_guid *privileged_pp_guid, struct addrset *as_default, struct addrset *as_meta, const struct ddsi_plist *plist, dds_duration_t tlease_dur, nn_vendorid_t vendor, unsigned custom_flags, ddsrt_wctime_t timestamp, seqno_t seq);
-int delete_proxy_participant_by_guid (struct ddsi_domaingv *gv, const struct ddsi_guid *guid, ddsrt_wctime_t timestamp, int isimplicit);
+DDS_EXPORT int delete_proxy_participant_by_guid (struct ddsi_domaingv *gv, const struct ddsi_guid *guid, ddsrt_wctime_t timestamp, int isimplicit);
int update_proxy_participant_plist_locked (struct proxy_participant *proxypp, seqno_t seq, const struct ddsi_plist *datap, ddsrt_wctime_t timestamp);
int update_proxy_participant_plist (struct proxy_participant *proxypp, seqno_t seq, const struct ddsi_plist *datap, ddsrt_wctime_t timestamp);
From 6d60047d6282c75d77d7ceb7f78f08e58968b3f8 Mon Sep 17 00:00:00 2001
From: Erik Boasson
Date: Thu, 7 May 2020 14:43:20 +0200
Subject: [PATCH 26/36] Instance to ALIVE also for rejected samples
In particular, this means instances published by a transient-local
writer will go back to ALIVE following a disconnect and reconnect.
Signed-off-by: Erik Boasson
---
src/core/ddsc/src/dds_rhc_default.c | 33 +++++++++++++++++++++--------
src/core/ddsc/tests/listener.c | 12 ++++++++---
2 files changed, 33 insertions(+), 12 deletions(-)
diff --git a/src/core/ddsc/src/dds_rhc_default.c b/src/core/ddsc/src/dds_rhc_default.c
index 9195cd9a3b..4a56d322c6 100644
--- a/src/core/ddsc/src/dds_rhc_default.c
+++ b/src/core/ddsc/src/dds_rhc_default.c
@@ -1034,7 +1034,7 @@ static void drop_instance_noupdate_no_writers (struct dds_rhc_default *__restric
*instptr = NULL;
}
-static void dds_rhc_register (struct dds_rhc_default *rhc, struct rhc_instance *inst, uint64_t wr_iid, bool autodispose, bool iid_update, bool * __restrict nda)
+static void dds_rhc_register (struct dds_rhc_default *rhc, struct rhc_instance *inst, uint64_t wr_iid, bool autodispose, bool sample_accepted, bool * __restrict nda)
{
const uint64_t inst_wr_iid = inst->wr_iid_islive ? inst->wr_iid : 0;
@@ -1061,12 +1061,16 @@ static void dds_rhc_register (struct dds_rhc_default *rhc, struct rhc_instance *
/* Currently no writers at all */
assert (!inst->wr_iid_islive);
- /* to avoid wr_iid update when register is called for sample rejected */
- if (iid_update)
- {
- inst->wr_iid = wr_iid;
+ /* When registering a writer based on a rejected sample and causing
+ the instance to transition from not-alive to alive, we do want
+ to generate an invalid sample with the id of the newly registered
+ (or re-registered) writer, but we don't want inst_accepts_sample
+ to be affected (it was "too old" in the ordering). wr_iid_islive
+ determines whether wr_iid is meaningful, so setting wr_iid while
+ leaving wr_iid_islive false gets us the desired behaviour. */
+ inst->wr_iid = wr_iid;
+ if (sample_accepted)
inst->wr_iid_islive = 1;
- }
inst->wrcount++;
inst->no_writers_gen++;
inst->autodispose = autodispose;
@@ -1108,7 +1112,7 @@ static void dds_rhc_register (struct dds_rhc_default *rhc, struct rhc_instance *
TRACE ("restore");
}
/* to avoid wr_iid update when register is called for sample rejected */
- if (iid_update)
+ if (sample_accepted)
{
inst->wr_iid = wr_iid;
inst->wr_iid_islive = 1;
@@ -1136,7 +1140,6 @@ static void dds_rhc_register (struct dds_rhc_default *rhc, struct rhc_instance *
inst->wrcount++;
if (autodispose)
inst->autodispose = 1;
- *nda = true;
}
else
{
@@ -1145,7 +1148,7 @@ static void dds_rhc_register (struct dds_rhc_default *rhc, struct rhc_instance *
assert (inst->wrcount >= 2);
/* the most recent writer gets the fast path */
/* to avoid wr_iid update when register is called for sample rejected */
- if (iid_update)
+ if (sample_accepted)
{
inst->wr_iid = wr_iid;
inst->wr_iid_islive = 1;
@@ -1568,7 +1571,19 @@ static bool dds_rhc_default_store (struct ddsi_rhc * __restrict rhc_common, cons
get_trigger_info_pre (&pre, inst);
if (has_data || is_dispose)
+ {
dds_rhc_register (rhc, inst, wr_iid, wrinfo->auto_dispose, false, ¬ify_data_available);
+ if (notify_data_available)
+ {
+ if (inst->latest == NULL || inst->latest->isread)
+ {
+ const bool was_empty = inst_is_empty (inst);
+ inst_set_invsample (rhc, inst, &trig_qc, ¬ify_data_available);
+ if (was_empty)
+ account_for_empty_to_nonempty_transition (rhc, inst);
+ }
+ }
+ }
/* notify sample lost */
cb_data.raw_status_id = (int) DDS_SAMPLE_LOST_STATUS_ID;
diff --git a/src/core/ddsc/tests/listener.c b/src/core/ddsc/tests/listener.c
index 2bd411202d..378f0b344f 100644
--- a/src/core/ddsc/tests/listener.c
+++ b/src/core/ddsc/tests/listener.c
@@ -380,12 +380,18 @@ CU_Test (ddsc_listener, incompatible_qos)
CU_Test (ddsc_listener, data_available)
{
- // data available on reader
+ // data available on reader (+ absence of data-on-readers)
dotest ("da sm r pm w ?pm w ?sm r wr w 0 ?da r ?!dor");
- // data available set on subscriber
+ // data available set on subscriber (+ absence of data-on-readers)
dotest ("da R sm r pm w ?pm w ?sm r wr w 0 ?da r ?!dor");
- // data available set on participant
+ // data available set on participant (+ absence of data-on-readers)
dotest ("da P sm r pm w ?pm w ?sm r wr w 0 ?da r ?!dor");
+
+ // non-auto-dispose, transient-local: disconnect => no_writers, reconnect => alive (using invalid samples)
+ // the invalid sample has the source time stamp of the latest update -- one wonders whether that is wise?
+ dotest ("da r(d=tl) ?pm w'(d=tl,ad=n) ; wr w' (1,2,3)@1.1 ?da r read{fan(1,2,3)w'} r ;"
+ " deaf P ; ?da r read{suo(1,2,3)w'@1.1,fuo1w'@1.1} r ;"
+ " hearing P ; ?da r read{sao(1,2,3)w'@1.1,fao1w'@1.1} r");
}
CU_Test (ddsc_listener, data_available_delete_writer)
From 4fe819ad8b6d8b33b0698fc7ae4b29bddf9a0767 Mon Sep 17 00:00:00 2001
From: Erik Boasson
Date: Mon, 11 May 2020 14:08:12 +0200
Subject: [PATCH 27/36] Remove dead stores triggering clang warnings
Signed-off-by: Erik Boasson
---
src/core/ddsc/src/dds_matched.c | 6 ++----
src/core/ddsc/src/dds_write.c | 3 +--
src/core/ddsi/src/ddsi_mcgroup.c | 2 +-
src/core/ddsi/src/ddsi_security_omg.c | 6 ++----
src/core/ddsi/src/ddsi_udp.c | 6 +++---
src/core/ddsi/src/q_config.c | 3 +--
src/ddsrt/src/sync/posix/sync.c | 8 ++------
7 files changed, 12 insertions(+), 22 deletions(-)
diff --git a/src/core/ddsc/src/dds_matched.c b/src/core/ddsc/src/dds_matched.c
index d2cc6a97c0..f10ab4c2f7 100644
--- a/src/core/ddsc/src/dds_matched.c
+++ b/src/core/ddsc/src/dds_matched.c
@@ -144,8 +144,7 @@ static dds_builtintopic_endpoint_t *make_builtintopic_endpoint (const ddsi_guid_
dds_builtintopic_endpoint_t *dds_get_matched_subscription_data (dds_entity_t writer, dds_instance_handle_t ih)
{
dds_writer *wr;
- dds_return_t rc;
- if ((rc = dds_writer_lock (writer, &wr)) != DDS_RETCODE_OK)
+ if (dds_writer_lock (writer, &wr))
return NULL;
else
{
@@ -187,8 +186,7 @@ dds_builtintopic_endpoint_t *dds_get_matched_subscription_data (dds_entity_t wri
dds_builtintopic_endpoint_t *dds_get_matched_publication_data (dds_entity_t reader, dds_instance_handle_t ih)
{
dds_reader *rd;
- dds_return_t rc;
- if ((rc = dds_reader_lock (reader, &rd)) != DDS_RETCODE_OK)
+ if (dds_reader_lock (reader, &rd))
return NULL;
else
{
diff --git a/src/core/ddsc/src/dds_write.c b/src/core/ddsc/src/dds_write.c
index a2b978bfb8..cc0b1e8b7b 100644
--- a/src/core/ddsc/src/dds_write.c
+++ b/src/core/ddsc/src/dds_write.c
@@ -267,8 +267,7 @@ void dds_write_flush (dds_entity_t writer)
{
struct thread_state1 * const ts1 = lookup_thread_state ();
dds_writer *wr;
- dds_return_t rc;
- if ((rc = dds_writer_lock (writer, &wr)) == DDS_RETCODE_OK)
+ if (dds_writer_lock (writer, &wr) == DDS_RETCODE_OK)
{
thread_state_awake (ts1, &wr->m_entity.m_domain->gv);
nn_xpack_send (wr->m_xp, true);
diff --git a/src/core/ddsi/src/ddsi_mcgroup.c b/src/core/ddsi/src/ddsi_mcgroup.c
index cd494447cd..b01b33e198 100644
--- a/src/core/ddsi/src/ddsi_mcgroup.c
+++ b/src/core/ddsi/src/ddsi_mcgroup.c
@@ -203,7 +203,7 @@ static int joinleave_mcgroups (const struct ddsi_domaingv *gv, ddsi_tran_conn_t
{
if (gv->recvips_mode == RECVIPS_MODE_ALL || interface_in_recvips_p (gv->recvips, &gv->interfaces[i]))
{
- if ((rc = joinleave_mcgroup (conn, join, srcloc, mcloc, &gv->interfaces[i])) < 0)
+ if (joinleave_mcgroup (conn, join, srcloc, mcloc, &gv->interfaces[i]) < 0)
fails++;
else
oks++;
diff --git a/src/core/ddsi/src/ddsi_security_omg.c b/src/core/ddsi/src/ddsi_security_omg.c
index 9ef815aff7..92c952920b 100644
--- a/src/core/ddsi/src/ddsi_security_omg.c
+++ b/src/core/ddsi/src/ddsi_security_omg.c
@@ -2250,7 +2250,6 @@ static bool q_omg_security_register_remote_writer_match(struct proxy_writer *pwr
struct dds_security_context *sc = q_omg_security_get_secure_context(pp);
DDS_Security_SecurityException exception = DDS_SECURITY_EXCEPTION_INIT;
struct proxypp_pp_match *proxypp_match;
- struct rd_pwr_match *match;
bool send_tokens = false;
bool allowed = false;
@@ -2258,7 +2257,7 @@ static bool q_omg_security_register_remote_writer_match(struct proxy_writer *pwr
return false;
ddsrt_mutex_lock(&rd->e.lock);
- if ((match = ddsrt_avl_lookup (&rd_writers_treedef, &rd->writers, &pwr->e.guid)) != NULL)
+ if (ddsrt_avl_lookup (&rd_writers_treedef, &rd->writers, &pwr->e.guid) != NULL)
allowed = true;
else if (rd->e.guid.entityid.u == NN_ENTITYID_P2P_BUILTIN_PARTICIPANT_VOLATILE_SECURE_READER)
{
@@ -2584,7 +2583,6 @@ static bool q_omg_security_register_remote_reader_match(struct proxy_reader *prd
struct dds_security_context *sc = q_omg_security_get_secure_context(pp);
DDS_Security_SecurityException exception = DDS_SECURITY_EXCEPTION_INIT;
struct proxypp_pp_match *proxypp_match;
- struct wr_prd_match *match;
bool send_tokens = false;
bool allowed = false;
@@ -2592,7 +2590,7 @@ static bool q_omg_security_register_remote_reader_match(struct proxy_reader *prd
return false;
ddsrt_mutex_lock(&wr->e.lock);
- if ((match = ddsrt_avl_lookup (&wr_readers_treedef, &wr->readers, &prd->e.guid)) != NULL)
+ if (ddsrt_avl_lookup (&wr_readers_treedef, &wr->readers, &prd->e.guid) != NULL)
allowed = true;
else if (wr->e.guid.entityid.u == NN_ENTITYID_P2P_BUILTIN_PARTICIPANT_VOLATILE_SECURE_WRITER || !wr->sec_attr->attr.is_submessage_protected)
{
diff --git a/src/core/ddsi/src/ddsi_udp.c b/src/core/ddsi/src/ddsi_udp.c
index 65d10fd941..d67929dfc0 100644
--- a/src/core/ddsi/src/ddsi_udp.c
+++ b/src/core/ddsi/src/ddsi_udp.c
@@ -477,11 +477,11 @@ static dds_return_t ddsi_udp_create_conn (ddsi_tran_conn_t *conn_out, ddsi_tran_
}
}
- if ((rc = set_rcvbuf (gv, sock, &gv->config.socket_min_rcvbuf_size)) != DDS_RETCODE_OK)
+ if (set_rcvbuf (gv, sock, &gv->config.socket_min_rcvbuf_size) != DDS_RETCODE_OK)
goto fail_w_socket;
- if ((rc = set_sndbuf (gv, sock, gv->config.socket_min_sndbuf_size)) != DDS_RETCODE_OK)
+ if (set_sndbuf (gv, sock, gv->config.socket_min_sndbuf_size) != DDS_RETCODE_OK)
goto fail_w_socket;
- if (gv->config.dontRoute && (rc = set_dont_route (gv, sock, ipv6)) != DDS_RETCODE_OK)
+ if (gv->config.dontRoute && set_dont_route (gv, sock, ipv6) != DDS_RETCODE_OK)
goto fail_w_socket;
if ((rc = ddsrt_bind (sock, &socketname.a, ddsrt_sockaddr_get_size (&socketname.a))) != DDS_RETCODE_OK)
diff --git a/src/core/ddsi/src/q_config.c b/src/core/ddsi/src/q_config.c
index 6bfb3de766..e54c7e62a3 100644
--- a/src/core/ddsi/src/q_config.c
+++ b/src/core/ddsi/src/q_config.c
@@ -2246,14 +2246,13 @@ static int set_defaults (struct cfgst *cfgst, void *parent, int isattr, struct c
int ok = 1;
for (const struct cfgelem *ce = cfgelem; ce && ce->name; ce++)
{
- struct cfgst_node *n;
struct cfgst_nodekey key;
key.e = ce;
key.p = parent;
cfgst_push (cfgst, isattr, ce, parent);
if (ce->multiplicity <= 1)
{
- if ((n = ddsrt_avl_lookup (&cfgst_found_treedef, &cfgst->found, &key)) == NULL)
+ if (ddsrt_avl_lookup (&cfgst_found_treedef, &cfgst->found, &key) == NULL)
{
if (ce->update)
{
diff --git a/src/ddsrt/src/sync/posix/sync.c b/src/ddsrt/src/sync/posix/sync.c
index a7f92e8fa5..7dcd203c32 100644
--- a/src/ddsrt/src/sync/posix/sync.c
+++ b/src/ddsrt/src/sync/posix/sync.c
@@ -159,22 +159,18 @@ ddsrt_cond_broadcast (ddsrt_cond_t *cond)
void
ddsrt_rwlock_init (ddsrt_rwlock_t *rwlock)
{
- int err = 0;
-
assert(rwlock != NULL);
/* process-shared attribute is set to PTHREAD_PROCESS_PRIVATE by default */
- if ((err = pthread_rwlock_init(&rwlock->rwlock, NULL)) != 0)
+ if (pthread_rwlock_init(&rwlock->rwlock, NULL) != 0)
abort();
}
void
ddsrt_rwlock_destroy (ddsrt_rwlock_t *rwlock)
{
- int err;
-
assert(rwlock != NULL);
- if ((err = pthread_rwlock_destroy (&rwlock->rwlock)) != 0)
+ if (pthread_rwlock_destroy (&rwlock->rwlock) != 0)
abort();
}
From 097065f4f24792c58c79b55ce7306819f813acef Mon Sep 17 00:00:00 2001
From: Erik Boasson
Date: Mon, 11 May 2020 14:08:30 +0200
Subject: [PATCH 28/36] Assertions to help clang static analyzer
Signed-off-by: Erik Boasson
---
src/core/ddsc/tests/loan.c | 4 ++++
src/core/ddsi/src/ddsi_serdata_pserop.c | 1 +
2 files changed, 5 insertions(+)
diff --git a/src/core/ddsc/tests/loan.c b/src/core/ddsc/tests/loan.c
index 5e12d84d31..2b822c7bef 100644
--- a/src/core/ddsc/tests/loan.c
+++ b/src/core/ddsc/tests/loan.c
@@ -110,6 +110,7 @@ CU_Test (ddsc_loan, success, .init = create_entities, .fini = delete_entities)
CU_ASSERT_FATAL (result == DDS_RETCODE_OK);
/* return resets buf[0] (so that it picks up the loan the next time) and zeros the data */
CU_ASSERT_FATAL (ptrs[0] == NULL);
+ assert (ptr0copy != NULL); /* clang static analyzer */
CU_ASSERT_FATAL (memcmp (ptr0copy, zeros, sizeof (s)) == 0);
/* read 3, return: should work fine, causes realloc */
@@ -121,6 +122,7 @@ CU_Test (ddsc_loan, success, .init = create_entities, .fini = delete_entities)
result = dds_return_loan (reader, ptrs, n);
CU_ASSERT_FATAL (result == DDS_RETCODE_OK);
CU_ASSERT_FATAL (ptrs[0] == NULL);
+ assert (ptr0copy != NULL); /* clang static analyzer */
CU_ASSERT_FATAL (memcmp (ptr0copy, zeros, 3 * sizeof (s)) == 0);
/* read 1 using loan, expecting to get the same address (no realloc needed), defer return.
@@ -145,6 +147,7 @@ CU_Test (ddsc_loan, success, .init = create_entities, .fini = delete_entities)
{
const struct RoundTripModule_DataType *a = ptrs[0];
const struct RoundTripModule_DataType *b = ptrs2[0];
+ assert (a != NULL && b != NULL); /* clang static analyzer */
CU_ASSERT_FATAL (a->payload._length == b->payload._length);
CU_ASSERT_FATAL (a->payload._buffer != b->payload._buffer);
CU_ASSERT_FATAL (a->payload._buffer[0] == b->payload._buffer[0]);
@@ -164,6 +167,7 @@ CU_Test (ddsc_loan, success, .init = create_entities, .fini = delete_entities)
//This should be a use-after-free
//CU_ASSERT_FATAL (memcmp (ptr0copy, zeros, sizeof (s)) == 0);
+ (void) ptr0copy;
}
CU_Test (ddsc_loan, take_cleanup, .init = create_entities, .fini = delete_entities)
diff --git a/src/core/ddsi/src/ddsi_serdata_pserop.c b/src/core/ddsi/src/ddsi_serdata_pserop.c
index a3df6d2c36..1796497799 100644
--- a/src/core/ddsi/src/ddsi_serdata_pserop.c
+++ b/src/core/ddsi/src/ddsi_serdata_pserop.c
@@ -213,6 +213,7 @@ static struct ddsi_serdata *serdata_pserop_from_sample (const struct ddsi_sertop
const size_t size4 = (size + 3) & ~(size_t)3;
struct ddsi_serdata_pserop *d = serdata_pserop_new (tp, kind, size4, &header);
assert (tp->ops_key == NULL || (size >= 16 && tp->memsize >= 16));
+ assert (d->data != NULL); // clang static analyzer
memcpy (d->data, data, size);
memset (d->data + size, 0, size4 - size);
d->pos = (uint32_t) size;
From 80087f450fdf5c5a007989bf7e741ee97ab7ec4e Mon Sep 17 00:00:00 2001
From: Erik Boasson
Date: Mon, 11 May 2020 14:13:06 +0200
Subject: [PATCH 29/36] Fix documentation comment
Signed-off-by: Erik Boasson
---
.../ddsi/include/dds/ddsi/ddsi_security_omg.h | 19 ++++++-------------
1 file changed, 6 insertions(+), 13 deletions(-)
diff --git a/src/core/ddsi/include/dds/ddsi/ddsi_security_omg.h b/src/core/ddsi/include/dds/ddsi/ddsi_security_omg.h
index 460a4ad84e..6eeb9eb48d 100644
--- a/src/core/ddsi/include/dds/ddsi/ddsi_security_omg.h
+++ b/src/core/ddsi/include/dds/ddsi/ddsi_security_omg.h
@@ -485,22 +485,15 @@ bool q_omg_security_is_remote_rtps_protected(const struct proxy_participant *pro
void set_proxy_participant_security_info(struct proxy_participant *proxypp, const ddsi_plist_t *plist);
/**
- * @brief Check if the participant and the proxy participant
- * have compatible security info settings.
- *
- * Associated with a secure participant is the ParticipantSecurityInfo parameter.
- * This parameter contains the setting of the security attributes and the associated
- * plugin security attributes of the secure participant.
- * This function will check if the received ParticipantSecurityInfo parameter is
- * compatible with the local ParticipantSecurityInfo parameter.
+ * @brief Determine if the messages, related to the given remote
+ * entity, are RTPS protected or not.
*
- * @param[in] pp The participant.
- * @param[in] proxypp The proxy participant.
+ * @param[in] pp The participant.
+ * @param[in] entityid ID of the entity to check.
*
* @returns bool
- * @retval true The participant and the proxy participant have compatible
- * security info settings.
- * @retval false Otherwise.
+ * @retval true The entity messages are RTPS protected.
+ * @retval false The entity messages are not RTPS protected.
*/
bool q_omg_security_is_local_rtps_protected(const struct participant *pp, ddsi_entityid_t entityid);
From 10980ec837c6c818a81b94b2efa5b601e4a230c6 Mon Sep 17 00:00:00 2001
From: Erik Boasson
Date: Mon, 11 May 2020 14:20:36 +0200
Subject: [PATCH 30/36] Use C99 flex array member instead of length-1 array
Signed-off-by: Erik Boasson
---
.../builtin_plugins/cryptographic/src/crypto_transform.c | 6 +++---
.../src/encode_datareader_submessage_utests.c | 2 +-
.../src/encode_datawriter_submessage_utests.c | 2 +-
3 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/src/security/builtin_plugins/cryptographic/src/crypto_transform.c b/src/security/builtin_plugins/cryptographic/src/crypto_transform.c
index a2f6893968..1fd32b92c0 100644
--- a/src/security/builtin_plugins/cryptographic/src/crypto_transform.c
+++ b/src/security/builtin_plugins/cryptographic/src/crypto_transform.c
@@ -59,7 +59,7 @@ struct crypto_header
struct crypto_contents
{
uint32_t _length;
- unsigned char _data[1];
+ unsigned char _data[];
};
struct crypto_contents_ref
@@ -71,7 +71,7 @@ struct crypto_contents_ref
struct receiver_specific_mac_seq
{
uint32_t _length;
- struct receiver_specific_mac _buffer[1];
+ struct receiver_specific_mac _buffer[];
};
struct crypto_footer
@@ -83,7 +83,7 @@ struct crypto_footer
struct encrypted_data
{
uint32_t length;
- unsigned char data[1];
+ unsigned char data[];
};
/*
diff --git a/src/security/builtin_plugins/tests/encode_datareader_submessage/src/encode_datareader_submessage_utests.c b/src/security/builtin_plugins/tests/encode_datareader_submessage/src/encode_datareader_submessage_utests.c
index 30bacaac8c..2962aca864 100644
--- a/src/security/builtin_plugins/tests/encode_datareader_submessage/src/encode_datareader_submessage_utests.c
+++ b/src/security/builtin_plugins/tests/encode_datareader_submessage/src/encode_datareader_submessage_utests.c
@@ -77,7 +77,7 @@ struct receiver_specific_mac
struct encrypted_data
{
uint32_t length;
- unsigned char data[1];
+ unsigned char data[];
};
struct seq_number
diff --git a/src/security/builtin_plugins/tests/encode_datawriter_submessage/src/encode_datawriter_submessage_utests.c b/src/security/builtin_plugins/tests/encode_datawriter_submessage/src/encode_datawriter_submessage_utests.c
index a1866414f5..30a4ce4e90 100644
--- a/src/security/builtin_plugins/tests/encode_datawriter_submessage/src/encode_datawriter_submessage_utests.c
+++ b/src/security/builtin_plugins/tests/encode_datawriter_submessage/src/encode_datawriter_submessage_utests.c
@@ -83,7 +83,7 @@ struct receiver_specific_mac
struct encrypted_data
{
uint32_t length;
- unsigned char data[1];
+ unsigned char data[];
};
static void reset_exception(DDS_Security_SecurityException *ex)
From ce7d41ef2f922347715a459d971c6caa7452f3d2 Mon Sep 17 00:00:00 2001
From: Erik Boasson
Date: Tue, 12 May 2020 09:07:08 +0200
Subject: [PATCH 31/36] Make wait_for_acks implementation capable of waiting
for one reader
The dds_wait_for_acks function follows the DCPS specification and allows
waiting for all matching readers to have acknowledged all data written
prior to that point. This commit leaves the API unchanged but extends
the implementation to make it possible to wait until a specific reader
has acknowledged everything, as this is a useful device in testing with
deliberate one-way disconnections using dds_domain_set_deafmute.
Signed-off-by: Erik Boasson
---
src/core/ddsc/src/dds__writer.h | 2 +-
src/core/ddsc/src/dds_publisher.c | 2 +-
src/core/ddsc/src/dds_writer.c | 4 ++--
src/core/ddsi/include/dds/ddsi/q_entity.h | 2 +-
src/core/ddsi/src/q_entity.c | 24 ++++++++++++++++++-----
5 files changed, 24 insertions(+), 10 deletions(-)
diff --git a/src/core/ddsc/src/dds__writer.h b/src/core/ddsc/src/dds__writer.h
index 69ff7e1b68..111e93d14e 100644
--- a/src/core/ddsc/src/dds__writer.h
+++ b/src/core/ddsc/src/dds__writer.h
@@ -23,7 +23,7 @@ DEFINE_ENTITY_LOCK_UNLOCK(inline, dds_writer, DDS_KIND_WRITER)
struct status_cb_data;
void dds_writer_status_cb (void *entity, const struct status_cb_data * data);
-dds_return_t dds__writer_wait_for_acks (struct dds_writer *wr, dds_time_t abstimeout);
+DDS_EXPORT dds_return_t dds__writer_wait_for_acks (struct dds_writer *wr, ddsi_guid_t *rdguid, dds_time_t abstimeout);
#if defined (__cplusplus)
}
diff --git a/src/core/ddsc/src/dds_publisher.c b/src/core/ddsc/src/dds_publisher.c
index 00ea5203ac..ca17999bbb 100644
--- a/src/core/ddsc/src/dds_publisher.c
+++ b/src/core/ddsc/src/dds_publisher.c
@@ -114,7 +114,7 @@ dds_return_t dds_wait_for_acks (dds_entity_t publisher_or_writer, dds_duration_t
return DDS_RETCODE_UNSUPPORTED;
case DDS_KIND_WRITER:
- ret = dds__writer_wait_for_acks ((struct dds_writer *) p_or_w_ent, abstimeout);
+ ret = dds__writer_wait_for_acks ((struct dds_writer *) p_or_w_ent, NULL, abstimeout);
dds_entity_unpin (p_or_w_ent);
return ret;
diff --git a/src/core/ddsc/src/dds_writer.c b/src/core/ddsc/src/dds_writer.c
index bebebcc01b..cad0492832 100644
--- a/src/core/ddsc/src/dds_writer.c
+++ b/src/core/ddsc/src/dds_writer.c
@@ -410,14 +410,14 @@ dds_entity_t dds_get_publisher (dds_entity_t writer)
}
}
-dds_return_t dds__writer_wait_for_acks (struct dds_writer *wr, dds_time_t abstimeout)
+dds_return_t dds__writer_wait_for_acks (struct dds_writer *wr, ddsi_guid_t *rdguid, dds_time_t abstimeout)
{
/* during lifetime of the writer m_wr is constant, it is only during deletion that it
gets erased at some point */
if (wr->m_wr == NULL)
return DDS_RETCODE_OK;
else
- return writer_wait_for_acks (wr->m_wr, abstimeout);
+ return writer_wait_for_acks (wr->m_wr, rdguid, abstimeout);
}
DDS_GET_STATUS(writer, publication_matched, PUBLICATION_MATCHED, total_count_change, current_count_change)
diff --git a/src/core/ddsi/include/dds/ddsi/q_entity.h b/src/core/ddsi/include/dds/ddsi/q_entity.h
index 14f59f8267..289ab842cb 100644
--- a/src/core/ddsi/include/dds/ddsi/q_entity.h
+++ b/src/core/ddsi/include/dds/ddsi/q_entity.h
@@ -655,7 +655,7 @@ seqno_t writer_max_drop_seq (const struct writer *wr);
int writer_must_have_hb_scheduled (const struct writer *wr, const struct whc_state *whcst);
void writer_set_retransmitting (struct writer *wr);
void writer_clear_retransmitting (struct writer *wr);
-dds_return_t writer_wait_for_acks (struct writer *wr, dds_time_t abstimeout);
+dds_return_t writer_wait_for_acks (struct writer *wr, const ddsi_guid_t *rdguid, dds_time_t abstimeout);
dds_return_t unblock_throttled_writer (struct ddsi_domaingv *gv, const struct ddsi_guid *guid);
dds_return_t delete_writer (struct ddsi_domaingv *gv, const struct ddsi_guid *guid);
diff --git a/src/core/ddsi/src/q_entity.c b/src/core/ddsi/src/q_entity.c
index 4fb30cd61c..9a8b5efcad 100644
--- a/src/core/ddsi/src/q_entity.c
+++ b/src/core/ddsi/src/q_entity.c
@@ -3969,16 +3969,30 @@ dds_return_t unblock_throttled_writer (struct ddsi_domaingv *gv, const struct dd
return 0;
}
-dds_return_t writer_wait_for_acks (struct writer *wr, dds_time_t abstimeout)
+dds_return_t writer_wait_for_acks (struct writer *wr, const ddsi_guid_t *rdguid, dds_time_t abstimeout)
{
dds_return_t rc;
seqno_t ref_seq;
ddsrt_mutex_lock (&wr->e.lock);
ref_seq = wr->seq;
- while (wr->state == WRST_OPERATIONAL && ref_seq > writer_max_drop_seq (wr))
- if (!ddsrt_cond_waituntil (&wr->throttle_cond, &wr->e.lock, abstimeout))
- break;
- rc = (ref_seq <= writer_max_drop_seq (wr)) ? DDS_RETCODE_OK : DDS_RETCODE_TIMEOUT;
+ if (rdguid == NULL)
+ {
+ while (wr->state == WRST_OPERATIONAL && ref_seq > writer_max_drop_seq (wr))
+ if (!ddsrt_cond_waituntil (&wr->throttle_cond, &wr->e.lock, abstimeout))
+ break;
+ rc = (ref_seq <= writer_max_drop_seq (wr)) ? DDS_RETCODE_OK : DDS_RETCODE_TIMEOUT;
+ }
+ else
+ {
+ struct wr_prd_match *m = ddsrt_avl_lookup (&wr_readers_treedef, &wr->readers, rdguid);
+ while (wr->state == WRST_OPERATIONAL && m && ref_seq > m->seq)
+ {
+ if (!ddsrt_cond_waituntil (&wr->throttle_cond, &wr->e.lock, abstimeout))
+ break;
+ m = ddsrt_avl_lookup (&wr_readers_treedef, &wr->readers, rdguid);
+ }
+ rc = (m == NULL || ref_seq <= m->seq) ? DDS_RETCODE_OK : DDS_RETCODE_TIMEOUT;
+ }
ddsrt_mutex_unlock (&wr->e.lock);
return rc;
}
From d7773e0b69c620e83fd331d2a479accf5ce684b1 Mon Sep 17 00:00:00 2001
From: Erik Boasson
Date: Tue, 12 May 2020 09:22:56 +0200
Subject: [PATCH 32/36] Add wait-for-acknowledgement to oneliner tests
Signed-off-by: Erik Boasson
---
src/core/ddsc/tests/listener.c | 29 ++++-
src/core/ddsc/tests/test_oneliner.c | 136 +++++++++++++++++------
src/core/ddsi/include/dds/ddsi/q_bswap.h | 4 +-
3 files changed, 124 insertions(+), 45 deletions(-)
diff --git a/src/core/ddsc/tests/listener.c b/src/core/ddsc/tests/listener.c
index 378f0b344f..1bc7005d13 100644
--- a/src/core/ddsc/tests/listener.c
+++ b/src/core/ddsc/tests/listener.c
@@ -286,16 +286,33 @@ CU_Test (ddsc_listener, matched)
dotest ("sm r pm w' ?pm w' ?sm r");
// Disconnect + reconnect; "deaf P" means the disconnect is asymmetrical: P no longer observes P'
- // but P' still observes P. If r did not ACK the data before losing connectivity, w' will hold
+ // but P' still observes P. If r did not ack the data before losing connectivity, w' will hold
// the data and it will be re-delivered after reconnecting, depending on QoS settings (the "..."
// allows for extra samples) and whether the instance was taken or not
//
- // the uncertainty also means we don't really know how many "data available" events there will be
- // and the "sleep 0.3" simply gives it a bit more time after the first event
+ // If r did ack the data, w will drop it and it can't be delivered. If there is another r'' that
+ // did not ack, r will still not get the data because the writer determines that it was ack'd
+ // already and it won't retransmit.
+ // FIXME: this differs from what the spec says should happen, maybe it should be changed?
+ // (It is a fall-out from changes to make sure a volatile reader doesn't get historical data, but
+ // it could be handled differently.)
+ //
+ // Waiting for an acknowledgement therefore makes sense (and in the other runs, a 0.3s sleep
+ // kind-a solves the problem of not known exactly how many events there will be: it means at
+ // least one event has been observed, and behaviour of Cyclone in a simple case like this means
+ // the full retransmit request will be replied to with a single packet, and that therefore the
+ // likelihood of the retransmitted data arriving within a window of 0.3s is very high. (Where
+ // 0.3s is an attempt to pick a duration on the long side of what's needed and short enough not
+ // to delay things much.)
+ dotest ("sm da r pm w' ?sm r ?pm w' ;" // matched reader/writer pair
+ " wr w' 1 ; ?da r take{(1,0,0)} r ?ack w' ;" // wait-for-acks => writer drops data
+ " deaf P ; ?sm(1,0,0,-1,w') r ?da r take{d1} r ; wr w' 2 ;" // write lost on "wire"
+ " hearing P ; ?sm(2,1,1,1,w') r ?da r sleep 0.3 take{(2,0,0)} r ; ?!pm");
dotest ("sm da r pm w' ; ?sm r ?pm w' ;"
- " wr w' 1 ; ?da r take{(1,0,0)} r sleep 1;"
- " deaf P ; ?sm(1,0,0,-1,w') r ?da r take{d1} r ; wr w' 2 ;"
- " hearing P ; ?sm(2,1,1,1,w') r ?da r sleep 0.3 take{(2,0,0),...} r ; ?!pm");
+ " r'' ?pm w' deaf P'' ;" // with second reader: reader is deaf so won't ACK
+ " wr w' 1 ; ?da r take{(1,0,0)} r ?ack(r) w' ;" // wait for ack from r' (not r'')
+ " deaf P ; ?sm(1,0,0,-1,w') r ?da r take{d1} r ; wr w' 2 ;" // write lost on "wire"
+ " hearing P ; ?sm(2,1,1,1,w') r ?da r sleep 0.3 take{(2,0,0)} r ; ?!pm");
// same without taking the "dispose" after disconnect
// sample 1 will be delivered anew
dotest ("sm da r pm w' ; ?sm r ?pm w' ; wr w' 1 ; ?da r take{(1,0,0)} r ;"
diff --git a/src/core/ddsc/tests/test_oneliner.c b/src/core/ddsc/tests/test_oneliner.c
index 255400ae98..2b635bdf63 100644
--- a/src/core/ddsc/tests/test_oneliner.c
+++ b/src/core/ddsc/tests/test_oneliner.c
@@ -25,6 +25,8 @@
#include "dds__types.h"
#include "dds__entity.h"
+#include "dds__writer.h"
+#include "dds/ddsi/q_bswap.h"
#include "dds/ddsi/q_lease.h"
#include "dds/ddsi/q_xevent.h"
#include "dds/ddsi/ddsi_entity_index.h"
@@ -1502,46 +1504,106 @@ static void checklistener (struct oneliner_ctx *ctx, int ll, int ent, struct one
testfail (ctx, "listener %s: status mask not cleared", lldesc[ll].name);
}
-static void dochecklistener (struct oneliner_ctx *ctx)
+static void dowaitforack (struct oneliner_ctx *ctx)
{
- const bool expectclear = nexttok_if (&ctx->l, '!');
- const int ll = parse_listener (ctx);
- if (ll < 0)
- error (ctx, "check listener: requires listener name");
- else if (expectclear)
+ dds_return_t ret;
+ int ent, ent1 = -1;
+ union { dds_guid_t x; ddsi_guid_t i; } rdguid;
+ if (*ctx->l.inp == '(') // reader present
{
- printf ("listener %s: check not called", lldesc[ll].name);
- fflush (stdout);
- ddsrt_mutex_lock (&ctx->g_mutex);
- bool ret = true;
- for (int i = 0; i < (int) (sizeof (ctx->doms) / sizeof (ctx->doms[0])); i++)
- {
- printf (" cb_called %"PRIu32" %s\n", ctx->cb[i].cb_called[lldesc[ll].id], ctx->cb[i].cb_called[lldesc[ll].id] == 0 ? "ok" : "fail");
- if (ctx->cb[i].cb_called[lldesc[ll].id] != 0)
- ret = false;
- }
- ddsrt_mutex_unlock (&ctx->g_mutex);
- if (!ret)
- testfail (ctx, "callback %s invoked unexpectedly", lldesc[ll].name);
+ nexttok (&ctx->l, NULL);
+ if ((ent1 = parse_entity (ctx)) < 0)
+ error (ctx, "wait for ack: expecting entity");
+ if ((ent1 % 9) < 3 || (ent1 % 9) > 5 || ctx->es[ent1] == 0)
+ error (ctx, "wait for ack: expecting existing reader as argument");
+ if ((ret = dds_get_guid (ctx->es[ent1], &rdguid.x)) != 0)
+ error_dds (ctx, ret, "wait for ack: failed to get GUID for reader %"PRId32, ctx->es[ent1]);
+ rdguid.i = nn_ntoh_guid (rdguid.i);
+ if (!nexttok_if (&ctx->l, ')'))
+ error (ctx, "wait for ack: expecting ')'");
}
+ if ((ent = parse_entity (ctx)) < 0)
+ error (ctx, "wait for ack: expecting writer");
+ if (ent1 >= 0 && ent / 9 == ent1 / 9)
+ error (ctx, "wait for ack: reader and writer must be in different domains");
+ if (ctx->es[ent] == 0)
+ make_entity (ctx, ent, NULL);
+ printf ("wait for ack %"PRId32" reader %"PRId32"\n", ctx->es[ent], ent1 < 0 ? 0 : ctx->es[ent1]);
+
+ // without a reader argument a simple dds_wait_for_acks (ctx->es[ent], DDS_SECS (5)) suffices
+ struct dds_entity *x;
+ if ((ret = dds_entity_pin (ctx->es[ent], &x)) < 0)
+ error_dds (ctx, ret, "wait for ack: pin entity failed %"PRId32, ctx->es[ent]);
+ if (dds_entity_kind (x) != DDS_KIND_WRITER)
+ error_dds (ctx, ret, "wait for ack: %"PRId32" is not a writer", ctx->es[ent]);
else
+ ret = dds__writer_wait_for_acks ((struct dds_writer *) x, (ent1 < 0) ? NULL : &rdguid.i, dds_time () + DDS_SECS (5));
+ dds_entity_unpin (x);
+ if (ret != 0)
{
- struct oneliner_lex l1 = ctx->l;
- // no whitespace between name and args
- const bool have_args = (*ctx->l.inp == '(');
- if (have_args)
- {
- // skip args: we need the entity before we can interpret them
- int tok;
- while ((tok = nexttok (&ctx->l, NULL)) != EOF && tok != ')')
- ;
- }
- const int ent = parse_entity (ctx);
- if (ent < 0)
- error (ctx, "check listener: requires an entity");
- if (ctx->es[ent] == 0)
- setlistener (ctx, NULL, ll, ent);
- checklistener (ctx, ll, ent, have_args ? &l1 : NULL);
+ if (ret == DDS_RETCODE_TIMEOUT)
+ testfail (ctx, "wait for acks timed out on entity %"PRId32, ctx->es[ent]);
+ else
+ error_dds (ctx, ret, "wait for acks failed on entity %"PRId32, ctx->es[ent]);
+ }
+}
+
+static void dowaitfornolistener (struct oneliner_ctx *ctx, int ll)
+{
+ printf ("listener %s: check not called", lldesc[ll].name);
+ fflush (stdout);
+ ddsrt_mutex_lock (&ctx->g_mutex);
+ bool ret = true;
+ for (int i = 0; i < (int) (sizeof (ctx->doms) / sizeof (ctx->doms[0])); i++)
+ {
+ printf (" %"PRIu32, ctx->cb[i].cb_called[lldesc[ll].id]);
+ if (ctx->cb[i].cb_called[lldesc[ll].id] != 0)
+ ret = false;
+ }
+ printf (" (%s)\n", ret ? "ok" : "fail");
+ ddsrt_mutex_unlock (&ctx->g_mutex);
+ if (!ret)
+ testfail (ctx, "callback %s invoked unexpectedly", lldesc[ll].name);
+}
+
+static void dowaitforlistener (struct oneliner_ctx *ctx, int ll)
+{
+ struct oneliner_lex l1 = ctx->l;
+ // no whitespace between name and args
+ const bool have_args = (*ctx->l.inp == '(');
+ if (have_args)
+ {
+ // skip args: we need the entity before we can interpret them
+ int tok;
+ while ((tok = nexttok (&ctx->l, NULL)) != EOF && tok != ')')
+ ;
+ }
+ const int ent = parse_entity (ctx);
+ if (ent < 0)
+ error (ctx, "check listener: requires an entity");
+ if (ctx->es[ent] == 0)
+ setlistener (ctx, NULL, ll, ent);
+ checklistener (ctx, ll, ent, have_args ? &l1 : NULL);
+}
+
+static void dowait (struct oneliner_ctx *ctx)
+{
+ union oneliner_tokval tokval;
+ if (peektok (&ctx->l, &tokval) == TOK_NAME && strcmp (tokval.n, "ack") == 0)
+ {
+ nexttok (&ctx->l, NULL);
+ dowaitforack (ctx);
+ }
+ else
+ {
+ const bool expectclear = nexttok_if (&ctx->l, '!');
+ const int ll = parse_listener (ctx);
+ if (ll < 0)
+ error (ctx, "check listener: requires listener name");
+ if (expectclear)
+ dowaitfornolistener (ctx, ll);
+ else
+ dowaitforlistener (ctx, ll);
}
}
@@ -1628,7 +1690,7 @@ static void dispatchcmd (struct oneliner_ctx *ctx)
void (*fn) (struct oneliner_ctx *ct);
} cs[] = {
{ "-", dodelete },
- { "?", dochecklistener },
+ { "?", dowait },
{ "wr", dowr },
{ "wrdisp", dowrdisp },
{ "disp", dodisp },
@@ -1744,7 +1806,7 @@ int test_oneliner_fini (struct oneliner_ctx *ctx)
setresult (ctx, ret, "terminate: reset listener failed on %"PRId32, ctx->es[i]);
if (ctx->result == 0)
{
- printf ("\n");
+ printf ("\n-- dumping content of readers after failure --\n");
for (int i = 0; i < (int) (sizeof (ctx->doms) / sizeof (ctx->doms[0])); i++)
{
for (int j = 3; j <= 5; j++)
diff --git a/src/core/ddsi/include/dds/ddsi/q_bswap.h b/src/core/ddsi/include/dds/ddsi/q_bswap.h
index 93a3706ae0..2a2ddedd7c 100644
--- a/src/core/ddsi/include/dds/ddsi/q_bswap.h
+++ b/src/core/ddsi/include/dds/ddsi/q_bswap.h
@@ -33,8 +33,8 @@ ddsi_guid_prefix_t nn_hton_guid_prefix (ddsi_guid_prefix_t p);
ddsi_guid_prefix_t nn_ntoh_guid_prefix (ddsi_guid_prefix_t p);
ddsi_entityid_t nn_hton_entityid (ddsi_entityid_t e);
ddsi_entityid_t nn_ntoh_entityid (ddsi_entityid_t e);
-ddsi_guid_t nn_hton_guid (ddsi_guid_t g);
-ddsi_guid_t nn_ntoh_guid (ddsi_guid_t g);
+DDS_EXPORT ddsi_guid_t nn_hton_guid (ddsi_guid_t g);
+DDS_EXPORT ddsi_guid_t nn_ntoh_guid (ddsi_guid_t g);
void bswap_sequence_number_set_hdr (nn_sequence_number_set_header_t *snset);
void bswap_sequence_number_set_bitmap (nn_sequence_number_set_header_t *snset, uint32_t *bits);
From 5f78e7d322e24195bdc4c1ae1dc858af081addf4 Mon Sep 17 00:00:00 2001
From: Erik Boasson
Date: Fri, 15 May 2020 15:00:12 +0200
Subject: [PATCH 33/36] Ignore backward jumps in computing serializer size
When defining a new topic, typically the serializer instructions that
are usually in constant memory and generated by the IDL compiler are
copied into memory managed by the Cyclone implementation. For this it
needs to compute the size of the serializer, which the IDL compiler
doesn't provide. It does this by effectively dry-running the
program. (Note that it doesn't validate the program.)
All but the JSR operations move the program counter forward, but the JSR
operation can cause it to go backward instead and allows implementing
recursive types (the IDL compiler doesn't support them, but one might
decide to work around that limitation). When dry-running the program,
following a backwards jump can cause a non-terminating loop.
The jump could potentially be to an unexplored address and so ignoring
all backwards jumps potentially means it skips part of the program. As
this is not a validator and the program can always be arranged so that a
following a backwards jump is not relevant to computing the size
correctly, this is reasonable approximation.
Signed-off-by: Erik Boasson
---
src/core/ddsi/src/ddsi_cdrstream.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/core/ddsi/src/ddsi_cdrstream.c b/src/core/ddsi/src/ddsi_cdrstream.c
index 6cdfb80678..95567664d4 100644
--- a/src/core/ddsi/src/ddsi_cdrstream.c
+++ b/src/core/ddsi/src/ddsi_cdrstream.c
@@ -364,7 +364,8 @@ static void dds_stream_countops1 (const uint32_t * __restrict ops, const uint32_
break;
}
case DDS_OP_JSR: {
- dds_stream_countops1 (ops + DDS_OP_JUMP (insn), ops_end);
+ if (DDS_OP_JUMP (insn) > 0)
+ dds_stream_countops1 (ops + DDS_OP_JUMP (insn), ops_end);
ops++;
break;
}
From c6194c0b1cfa8575e1798bb72fa1a9f284239787 Mon Sep 17 00:00:00 2001
From: Erik Boasson
Date: Sat, 16 May 2020 08:38:58 +0200
Subject: [PATCH 34/36] Tweak timeout handling of authentication tests
* Compute the time at which the handshake must have completed from the
initial timeout specification, rather than using it as a timeout for
the individual steps of the handshake
* If the handshake fails because an expected message is not present,
print this, including whether the timeout occured because the message
queue was empty or because the expected message could not be found in
a non-empty queue.
* Replace the 0.5s per-step timeout to a single 2s timeout.
Signed-off-by: Erik Boasson
---
src/security/core/tests/access_control.c | 6 +-
.../tests/common/authentication_wrapper.c | 4 +-
.../tests/common/authentication_wrapper.h | 2 +-
.../core/tests/common/plugin_wrapper_msg_q.c | 27 ++--
.../core/tests/common/plugin_wrapper_msg_q.h | 8 +-
src/security/core/tests/common/test_utils.c | 130 +++++++++++++-----
6 files changed, 122 insertions(+), 55 deletions(-)
diff --git a/src/security/core/tests/access_control.c b/src/security/core/tests/access_control.c
index 261fbdf880..f22dbe53fd 100644
--- a/src/security/core/tests/access_control.c
+++ b/src/security/core/tests/access_control.c
@@ -700,7 +700,7 @@ static void test_encoding_mismatch(
struct Handshake *hs_list;
int nhs;
- validate_handshake (DDS_DOMAINID, false, NULL, &hs_list, &nhs, DDS_MSECS(500));
+ validate_handshake (DDS_DOMAINID, false, NULL, &hs_list, &nhs, DDS_SECS(2));
CU_ASSERT_EQUAL_FATAL (exp_hs_fail, nhs < 1);
handshake_list_fini (hs_list, nhs);
@@ -799,7 +799,7 @@ static void test_readwrite_protection (
if (!exp_pub_pp_fail && !exp_sub_pp_fail)
{
dds_entity_t pub, sub, pub_tp, sub_tp, wr, rd;
- validate_handshake_nofail (DDS_DOMAINID, DDS_MSECS(500));
+ validate_handshake_nofail (DDS_DOMAINID, DDS_SECS(2));
rd_wr_init_fail (g_participant[0], &pub, &pub_tp, &wr, g_participant[1], &sub, &sub_tp, &rd, topic_name, exp_pub_tp_fail, exp_wr_fail, exp_sub_tp_fail, exp_rd_fail);
if (!exp_pub_tp_fail && !exp_wr_fail && !exp_sub_tp_fail && !exp_rd_fail)
sync_writer_to_readers (g_participant[0], wr, exp_sync_fail ? 0 : 1, DDS_SECS(1));
@@ -893,4 +893,4 @@ CU_Test(ddssec_access_control, denied_topic)
dds_delete_qos (qos);
access_control_fini (2, (void * []) { gov_config, gov_topic_rule, sub_rules_xml, grants_pub[0], grants_sub[0], perm_config_pub, perm_config_sub, ca, id1_subj, id2_subj, id1, id2 }, 12);
-}
\ No newline at end of file
+}
diff --git a/src/security/core/tests/common/authentication_wrapper.c b/src/security/core/tests/common/authentication_wrapper.c
index 86290df8ed..693ac11826 100644
--- a/src/security/core/tests/common/authentication_wrapper.c
+++ b/src/security/core/tests/common/authentication_wrapper.c
@@ -446,11 +446,11 @@ static struct dds_security_authentication_impl * get_impl_for_domain(dds_domaini
return NULL;
}
-struct message * test_authentication_plugin_take_msg(dds_domainid_t domain_id, message_kind_t kind, DDS_Security_IdentityHandle lidHandle, DDS_Security_IdentityHandle ridHandle, DDS_Security_IdentityHandle hsHandle, dds_duration_t timeout)
+enum take_message_result test_authentication_plugin_take_msg(dds_domainid_t domain_id, message_kind_t kind, DDS_Security_IdentityHandle lidHandle, DDS_Security_IdentityHandle ridHandle, DDS_Security_IdentityHandle hsHandle, dds_time_t abstimeout, struct message **msg)
{
struct dds_security_authentication_impl *impl = get_impl_for_domain(domain_id);
assert(impl);
- return take_message(&impl->msg_queue, kind, lidHandle, ridHandle, hsHandle, timeout);
+ return take_message(&impl->msg_queue, kind, lidHandle, ridHandle, hsHandle, abstimeout, msg);
}
void test_authentication_plugin_release_msg(struct message *msg)
diff --git a/src/security/core/tests/common/authentication_wrapper.h b/src/security/core/tests/common/authentication_wrapper.h
index f58121d391..939db1080c 100644
--- a/src/security/core/tests/common/authentication_wrapper.h
+++ b/src/security/core/tests/common/authentication_wrapper.h
@@ -33,7 +33,7 @@ SECURITY_EXPORT int finalize_test_authentication_missing_func(void *context);
SECURITY_EXPORT int init_test_authentication_init_error(const char *argument, void **context, struct ddsi_domaingv *gv);
SECURITY_EXPORT int finalize_test_authentication_init_error(void *context);
-SECURITY_EXPORT struct message * test_authentication_plugin_take_msg(dds_domainid_t domain_id, message_kind_t kind, DDS_Security_IdentityHandle lidHandle, DDS_Security_IdentityHandle ridHandle, DDS_Security_IdentityHandle hsHandle, dds_duration_t timeout);
+SECURITY_EXPORT enum take_message_result test_authentication_plugin_take_msg(dds_domainid_t domain_id, message_kind_t kind, DDS_Security_IdentityHandle lidHandle, DDS_Security_IdentityHandle ridHandle, DDS_Security_IdentityHandle hsHandle, dds_time_t abstimeout, struct message **msg);
SECURITY_EXPORT void test_authentication_plugin_release_msg(struct message *msg);
#endif /* SECURITY_CORE_TEST_AUTHENTICATION_WRAPPER_H_ */
diff --git a/src/security/core/tests/common/plugin_wrapper_msg_q.c b/src/security/core/tests/common/plugin_wrapper_msg_q.c
index 3ecad82286..0b03dedbed 100644
--- a/src/security/core/tests/common/plugin_wrapper_msg_q.c
+++ b/src/security/core/tests/common/plugin_wrapper_msg_q.c
@@ -95,25 +95,26 @@ int message_matched(struct message *msg, message_kind_t kind, DDS_Security_Ident
(!hsHandle || msg->hsHandle == hsHandle);
}
-struct message * take_message(struct message_queue *queue, message_kind_t kind, DDS_Security_IdentityHandle lidHandle, DDS_Security_IdentityHandle ridHandle, DDS_Security_IdentityHandle hsHandle, dds_duration_t timeout)
+enum take_message_result take_message(struct message_queue *queue, message_kind_t kind, DDS_Security_IdentityHandle lidHandle, DDS_Security_IdentityHandle ridHandle, DDS_Security_IdentityHandle hsHandle, dds_time_t abstimeout, struct message **msg)
{
- struct message *msg = NULL, *cur, *prev;
- int r = 1;
+ struct message *cur, *prev;
+ enum take_message_result ret = TAKE_MESSAGE_OK;
+ *msg = NULL;
ddsrt_mutex_lock(&queue->lock);
do
{
cur = queue->head;
prev = NULL;
- while (cur && !msg)
+ while (cur && *msg == NULL)
{
if (message_matched(cur, kind, lidHandle, ridHandle, hsHandle))
{
- msg = cur;
+ *msg = cur;
if (prev)
- prev->next = msg->next;
+ prev->next = cur->next;
else
- queue->head = msg->next;
- if (queue->tail == msg)
+ queue->head = cur->next;
+ if (queue->tail == cur)
queue->tail = prev;
}
else
@@ -122,13 +123,13 @@ struct message * take_message(struct message_queue *queue, message_kind_t kind,
cur = cur->next;
}
}
- if (!msg)
+ if (*msg == NULL)
{
- if (!ddsrt_cond_waitfor(&queue->cond, &queue->lock, timeout))
- r = 0;
+ if (!ddsrt_cond_waituntil(&queue->cond, &queue->lock, abstimeout))
+ ret = queue->head ? TAKE_MESSAGE_TIMEOUT_NONEMPTY : TAKE_MESSAGE_TIMEOUT_EMPTY;
}
- } while (r && !msg);
+ } while (ret == TAKE_MESSAGE_OK && *msg == NULL);
ddsrt_mutex_unlock(&queue->lock);
- return msg;
+ return ret;
}
diff --git a/src/security/core/tests/common/plugin_wrapper_msg_q.h b/src/security/core/tests/common/plugin_wrapper_msg_q.h
index 32ec557f5c..c70e5f2138 100644
--- a/src/security/core/tests/common/plugin_wrapper_msg_q.h
+++ b/src/security/core/tests/common/plugin_wrapper_msg_q.h
@@ -46,6 +46,12 @@ struct message_queue {
struct message *tail;
};
+enum take_message_result {
+ TAKE_MESSAGE_OK, /* message found */
+ TAKE_MESSAGE_TIMEOUT_EMPTY, /* no message found, queue is empty */
+ TAKE_MESSAGE_TIMEOUT_NONEMPTY /* no message found, queue is not empty */
+};
+
struct dds_security_authentication_impl;
void insert_message(struct message_queue *queue, struct message *msg);
@@ -56,7 +62,7 @@ void delete_message(struct message *msg);
void init_message_queue(struct message_queue *queue);
void deinit_message_queue(struct message_queue *queue);
int message_matched(struct message *msg, message_kind_t kind, DDS_Security_IdentityHandle lidHandle, DDS_Security_IdentityHandle ridHandle, DDS_Security_IdentityHandle hsHandle);
-struct message * take_message(struct message_queue *queue, message_kind_t kind, DDS_Security_IdentityHandle lidHandle, DDS_Security_IdentityHandle ridHandle, DDS_Security_IdentityHandle hsHandle, dds_duration_t timeout);
+enum take_message_result take_message(struct message_queue *queue, message_kind_t kind, DDS_Security_IdentityHandle lidHandle, DDS_Security_IdentityHandle ridHandle, DDS_Security_IdentityHandle hsHandle, dds_time_t abstimeout, struct message **msg);
#endif /* SECURITY_CORE_PLUGIN_WRAPPER_MSG_Q_H_ */
diff --git a/src/security/core/tests/common/test_utils.c b/src/security/core/tests/common/test_utils.c
index e7e7ed474c..cab9bef85c 100644
--- a/src/security/core/tests/common/test_utils.c
+++ b/src/security/core/tests/common/test_utils.c
@@ -149,58 +149,92 @@ static int find_handshake (DDS_Security_HandshakeHandle handle)
return -1;
}
-static void handle_process_message (dds_domainid_t domain_id, DDS_Security_IdentityHandle handshake, dds_duration_t timeout)
+static void handle_process_message (dds_domainid_t domain_id, DDS_Security_IdentityHandle handshake, dds_time_t abstimeout)
{
struct message *msg;
- if ((msg = test_authentication_plugin_take_msg (domain_id, MESSAGE_KIND_PROCESS_HANDSHAKE, 0, 0, handshake, timeout)))
+ switch (test_authentication_plugin_take_msg (domain_id, MESSAGE_KIND_PROCESS_HANDSHAKE, 0, 0, handshake, abstimeout, &msg))
{
- int idx;
- if ((idx = find_handshake (msg->hsHandle)) >= 0)
- {
- print_test_msg ("set handshake %"PRId64" final result to '%s' (errmsg: %s)\n", msg->hsHandle, get_validation_result_str (msg->result), msg->err_msg);
- handshakeList[idx].finalResult = msg->result;
- handshakeList[idx].err_msg = ddsrt_strdup (msg->err_msg);
+ case TAKE_MESSAGE_OK: {
+ int idx;
+ if ((idx = find_handshake (msg->hsHandle)) >= 0)
+ {
+ print_test_msg ("set handshake %"PRId64" final result to '%s' (errmsg: %s)\n", msg->hsHandle, get_validation_result_str (msg->result), msg->err_msg);
+ handshakeList[idx].finalResult = msg->result;
+ handshakeList[idx].err_msg = ddsrt_strdup (msg->err_msg);
+ }
+ test_authentication_plugin_release_msg (msg);
+ break;
+ }
+ case TAKE_MESSAGE_TIMEOUT_EMPTY: {
+ print_test_msg ("handle_process_message: timed out on empty queue\n");
+ break;
+ }
+ case TAKE_MESSAGE_TIMEOUT_NONEMPTY: {
+ print_test_msg ("handle_process_message: timed out on non-empty queue\n");
+ break;
}
- test_authentication_plugin_release_msg (msg);
}
}
-static void handle_begin_handshake_request (dds_domainid_t domain_id, struct Handshake *hs, DDS_Security_IdentityHandle lid, DDS_Security_IdentityHandle rid, dds_duration_t timeout)
+static void handle_begin_handshake_request (dds_domainid_t domain_id, struct Handshake *hs, DDS_Security_IdentityHandle lid, DDS_Security_IdentityHandle rid, dds_time_t abstimeout)
{
struct message *msg;
print_test_msg ("handle begin handshake request %"PRId64"<->%"PRId64"\n", lid, rid);
- if ((msg = test_authentication_plugin_take_msg (domain_id, MESSAGE_KIND_BEGIN_HANDSHAKE_REQUEST, lid, rid, 0, timeout)))
+ switch (test_authentication_plugin_take_msg (domain_id, MESSAGE_KIND_BEGIN_HANDSHAKE_REQUEST, lid, rid, 0, abstimeout, &msg))
{
- hs->handle = msg->hsHandle;
- hs->handshakeResult = msg->result;
- if (msg->result != DDS_SECURITY_VALIDATION_FAILED)
- handle_process_message (domain_id, msg->hsHandle, timeout);
- else
- hs->err_msg = ddsrt_strdup (msg->err_msg);
- test_authentication_plugin_release_msg (msg);
+ case TAKE_MESSAGE_OK: {
+ hs->handle = msg->hsHandle;
+ hs->handshakeResult = msg->result;
+ if (msg->result != DDS_SECURITY_VALIDATION_FAILED)
+ handle_process_message (domain_id, msg->hsHandle, abstimeout);
+ else
+ hs->err_msg = ddsrt_strdup (msg->err_msg);
+ test_authentication_plugin_release_msg (msg);
+ break;
+ }
+ case TAKE_MESSAGE_TIMEOUT_EMPTY: {
+ print_test_msg ("handle_begin_handshake_request: timed out on empty queue\n");
+ break;
+ }
+ case TAKE_MESSAGE_TIMEOUT_NONEMPTY: {
+ print_test_msg ("handle_begin_handshake_request: timed out on non-empty queue\n");
+ break;
+ }
}
}
-static void handle_begin_handshake_reply (dds_domainid_t domain_id, struct Handshake *hs, DDS_Security_IdentityHandle lid, DDS_Security_IdentityHandle rid, dds_duration_t timeout)
+static void handle_begin_handshake_reply (dds_domainid_t domain_id, struct Handshake *hs, DDS_Security_IdentityHandle lid, DDS_Security_IdentityHandle rid, dds_time_t abstimeout)
{
struct message *msg;
print_test_msg ("handle begin handshake reply %"PRId64"<->%"PRId64"\n", lid, rid);
- if ((msg = test_authentication_plugin_take_msg (domain_id, MESSAGE_KIND_BEGIN_HANDSHAKE_REPLY, lid, rid, 0, timeout)))
+ switch (test_authentication_plugin_take_msg (domain_id, MESSAGE_KIND_BEGIN_HANDSHAKE_REPLY, lid, rid, 0, abstimeout, &msg))
{
- hs->handle = msg->hsHandle;
- hs->handshakeResult = msg->result;
- if (msg->result != DDS_SECURITY_VALIDATION_FAILED)
- handle_process_message (domain_id, msg->hsHandle, timeout);
- else
- hs->err_msg = ddsrt_strdup (msg->err_msg);
- test_authentication_plugin_release_msg (msg);
+ case TAKE_MESSAGE_OK: {
+ hs->handle = msg->hsHandle;
+ hs->handshakeResult = msg->result;
+ if (msg->result != DDS_SECURITY_VALIDATION_FAILED)
+ handle_process_message (domain_id, msg->hsHandle, abstimeout);
+ else
+ hs->err_msg = ddsrt_strdup (msg->err_msg);
+ test_authentication_plugin_release_msg (msg);
+ break;
+ }
+ case TAKE_MESSAGE_TIMEOUT_EMPTY: {
+ print_test_msg ("handle_begin_handshake_reply: timed out on empty queue\n");
+ break;
+ }
+ case TAKE_MESSAGE_TIMEOUT_NONEMPTY: {
+ print_test_msg ("handle_begin_handshake_reply: timed out on non-empty queue\n");
+ break;
+ }
}
}
-static void handle_validate_remote_identity (dds_domainid_t domain_id, DDS_Security_IdentityHandle lid, int count, dds_duration_t timeout)
+static void handle_validate_remote_identity (dds_domainid_t domain_id, DDS_Security_IdentityHandle lid, int count, dds_time_t abstimeout)
{
+ enum take_message_result res = TAKE_MESSAGE_OK;
struct message *msg;
- while (count-- > 0 && (msg = test_authentication_plugin_take_msg (domain_id, MESSAGE_KIND_VALIDATE_REMOTE_IDENTITY, lid, 0, 0, timeout)))
+ while (count-- > 0 && (res = test_authentication_plugin_take_msg (domain_id, MESSAGE_KIND_VALIDATE_REMOTE_IDENTITY, lid, 0, 0, abstimeout, &msg)) == TAKE_MESSAGE_OK)
{
struct Handshake *hs;
add_remote_identity (msg->ridHandle, &msg->rguid);
@@ -208,12 +242,12 @@ static void handle_validate_remote_identity (dds_domainid_t domain_id, DDS_Secur
if (msg->result == DDS_SECURITY_VALIDATION_PENDING_HANDSHAKE_REQUEST)
{
hs->node_type = HSN_REQUESTER;
- handle_begin_handshake_request (domain_id, hs, lid, msg->ridHandle, timeout);
+ handle_begin_handshake_request (domain_id, hs, lid, msg->ridHandle, abstimeout);
}
else if (msg->result == DDS_SECURITY_VALIDATION_PENDING_HANDSHAKE_MESSAGE)
{
hs->node_type = HSN_REPLIER;
- handle_begin_handshake_reply (domain_id, hs, lid, msg->ridHandle, timeout);
+ handle_begin_handshake_reply (domain_id, hs, lid, msg->ridHandle, abstimeout);
}
else
{
@@ -221,11 +255,34 @@ static void handle_validate_remote_identity (dds_domainid_t domain_id, DDS_Secur
}
test_authentication_plugin_release_msg (msg);
}
+
+ switch (res)
+ {
+ case TAKE_MESSAGE_OK:
+ break;
+ case TAKE_MESSAGE_TIMEOUT_EMPTY:
+ print_test_msg ("handle_validate_remote_identity: timed out on empty queue\n");
+ break;
+ case TAKE_MESSAGE_TIMEOUT_NONEMPTY:
+ print_test_msg ("handle_validate_remote_identity: timed out on non-empty queue\n");
+ break;
+ }
}
-static void handle_validate_local_identity (dds_domainid_t domain_id, bool exp_localid_fail, const char * exp_localid_msg, dds_duration_t timeout)
+static void handle_validate_local_identity (dds_domainid_t domain_id, bool exp_localid_fail, const char * exp_localid_msg, dds_time_t abstimeout)
{
- struct message *msg = test_authentication_plugin_take_msg (domain_id, MESSAGE_KIND_VALIDATE_LOCAL_IDENTITY, 0, 0, 0, timeout);
+ struct message *msg;
+ switch (test_authentication_plugin_take_msg (domain_id, MESSAGE_KIND_VALIDATE_LOCAL_IDENTITY, 0, 0, 0, abstimeout, &msg))
+ {
+ case TAKE_MESSAGE_OK:
+ break;
+ case TAKE_MESSAGE_TIMEOUT_EMPTY:
+ print_test_msg ("handle_validate_local_identity: timed out on empty queue\n");
+ break;
+ case TAKE_MESSAGE_TIMEOUT_NONEMPTY:
+ print_test_msg ("handle_validate_local_identity: timed out on non-empty queue\n");
+ break;
+ }
CU_ASSERT_FATAL (msg != NULL);
CU_ASSERT_FATAL ((msg->result == DDS_SECURITY_VALIDATION_OK) != exp_localid_fail);
if (exp_localid_fail && exp_localid_msg)
@@ -234,12 +291,15 @@ static void handle_validate_local_identity (dds_domainid_t domain_id, bool exp_l
CU_ASSERT_FATAL (msg->err_msg && strstr (msg->err_msg, exp_localid_msg) != NULL);
}
else
+ {
add_local_identity (msg->lidHandle, &msg->lguid);
+ }
test_authentication_plugin_release_msg (msg);
}
void validate_handshake (dds_domainid_t domain_id, bool exp_localid_fail, const char * exp_localid_msg, struct Handshake *hs_list[], int *nhs, dds_duration_t timeout)
{
+ dds_time_t abstimeout = dds_time() + timeout;
clear_stores ();
if (nhs)
@@ -247,10 +307,10 @@ void validate_handshake (dds_domainid_t domain_id, bool exp_localid_fail, const
if (hs_list)
*hs_list = NULL;
- handle_validate_local_identity (domain_id, exp_localid_fail, exp_localid_msg, timeout);
+ handle_validate_local_identity (domain_id, exp_localid_fail, exp_localid_msg, abstimeout);
if (!exp_localid_fail)
{
- handle_validate_remote_identity (domain_id, localIdentityList[0].handle, 1, timeout);
+ handle_validate_remote_identity (domain_id, localIdentityList[0].handle, 1, abstimeout);
for (int n = 0; n < numHandshake; n++)
{
struct Handshake *hs = &handshakeList[n];
From 8a86af7983925f55a34cd150feb08d1315cbfc63 Mon Sep 17 00:00:00 2001
From: Erik Boasson
Date: Sat, 16 May 2020 11:12:45 +0200
Subject: [PATCH 35/36] Do not respond to an ignored SPDP message
Signed-off-by: Erik Boasson
---
src/core/ddsi/include/dds/ddsi/q_entity.h | 2 +-
src/core/ddsi/src/q_ddsi_discovery.c | 69 ++++++++++-------------
src/core/ddsi/src/q_entity.c | 5 +-
3 files changed, 33 insertions(+), 43 deletions(-)
diff --git a/src/core/ddsi/include/dds/ddsi/q_entity.h b/src/core/ddsi/include/dds/ddsi/q_entity.h
index 289ab842cb..8a4f22f2ef 100644
--- a/src/core/ddsi/include/dds/ddsi/q_entity.h
+++ b/src/core/ddsi/include/dds/ddsi/q_entity.h
@@ -695,7 +695,7 @@ int writer_set_notalive (struct writer *wr, bool notify);
/* Set when this proxy participant is not to be announced on the built-in topics yet */
#define CF_PROXYPP_NO_SPDP (1 << 2)
-void new_proxy_participant (struct ddsi_domaingv *gv, const struct ddsi_guid *guid, uint32_t bes, const struct ddsi_guid *privileged_pp_guid, struct addrset *as_default, struct addrset *as_meta, const struct ddsi_plist *plist, dds_duration_t tlease_dur, nn_vendorid_t vendor, unsigned custom_flags, ddsrt_wctime_t timestamp, seqno_t seq);
+bool new_proxy_participant (struct ddsi_domaingv *gv, const struct ddsi_guid *guid, uint32_t bes, const struct ddsi_guid *privileged_pp_guid, struct addrset *as_default, struct addrset *as_meta, const struct ddsi_plist *plist, dds_duration_t tlease_dur, nn_vendorid_t vendor, unsigned custom_flags, ddsrt_wctime_t timestamp, seqno_t seq);
DDS_EXPORT int delete_proxy_participant_by_guid (struct ddsi_domaingv *gv, const struct ddsi_guid *guid, ddsrt_wctime_t timestamp, int isimplicit);
int update_proxy_participant_plist_locked (struct proxy_participant *proxypp, seqno_t seq, const struct ddsi_plist *datap, ddsrt_wctime_t timestamp);
diff --git a/src/core/ddsi/src/q_ddsi_discovery.c b/src/core/ddsi/src/q_ddsi_discovery.c
index 1bc06c436b..1f031d2f1d 100644
--- a/src/core/ddsi/src/q_ddsi_discovery.c
+++ b/src/core/ddsi/src/q_ddsi_discovery.c
@@ -764,28 +764,17 @@ static int handle_SPDP_alive (const struct receiver_state *rst, seqno_t seq, dds
maybe_add_pp_as_meta_to_as_disc (gv, as_meta);
- 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
- );
-
- /* 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 (!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");
@@ -795,27 +784,27 @@ static int handle_SPDP_alive (const struct receiver_state *rst, seqno_t seq, dds
{
GVLOGDISC ("directed SPDP packet -> not responding\n");
}
- }
- if (custom_flags & 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 (entidx_lookup_proxy_participant_guid (gv->entity_index, &privileged_pp_guid) == NULL)
+ if (custom_flags & CF_PARTICIPANT_IS_DDSI2)
{
- GVLOGDISC ("make_participants_dependent_on_ddsi2: ddsi2 "PGUIDFMT" is no more, delete "PGUIDFMT"\n",
- PGUID (privileged_pp_guid), PGUID (datap->participant_guid));
- delete_proxy_participant_by_guid (gv, &datap->participant_guid, timestamp, 1);
+ /* 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 (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));
+ delete_proxy_participant_by_guid (gv, &datap->participant_guid, timestamp, 1);
+ }
+ }
+ return 1;
}
- return 1;
}
static void handle_SPDP (const struct receiver_state *rst, ddsi_entityid_t pwr_entityid, seqno_t seq, const struct ddsi_serdata *serdata)
@@ -1102,7 +1091,7 @@ static struct proxy_participant *implicitly_create_proxypp (struct ddsi_domaingv
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;
- new_proxy_participant(gv, ppguid, 0, &privguid, new_addrset(), new_addrset(), &pp_plist, DDS_INFINITY, actual_vendorid, CF_IMPLICITLY_CREATED_PROXYPP, timestamp, seq);
+ (void) new_proxy_participant(gv, ppguid, 0, &privguid, new_addrset(), new_addrset(), &pp_plist, DDS_INFINITY, actual_vendorid, CF_IMPLICITLY_CREATED_PROXYPP, timestamp, seq);
}
else if (ppguid->prefix.u[0] == src_guid_prefix->u[0] && vendor_is_eclipse_or_opensplice (vendorid))
{
diff --git a/src/core/ddsi/src/q_entity.c b/src/core/ddsi/src/q_entity.c
index 9a8b5efcad..5ea7defcf5 100644
--- a/src/core/ddsi/src/q_entity.c
+++ b/src/core/ddsi/src/q_entity.c
@@ -4872,7 +4872,7 @@ static void free_proxy_participant(struct proxy_participant *proxypp)
ddsrt_free (proxypp);
}
-void new_proxy_participant (struct ddsi_domaingv *gv, const struct ddsi_guid *ppguid, uint32_t bes, const struct ddsi_guid *privileged_pp_guid, struct addrset *as_default, struct addrset *as_meta, const ddsi_plist_t *plist, dds_duration_t tlease_dur, nn_vendorid_t vendor, unsigned custom_flags, ddsrt_wctime_t timestamp, seqno_t seq)
+bool new_proxy_participant (struct ddsi_domaingv *gv, const struct ddsi_guid *ppguid, uint32_t bes, const struct ddsi_guid *privileged_pp_guid, struct addrset *as_default, struct addrset *as_meta, const ddsi_plist_t *plist, dds_duration_t tlease_dur, nn_vendorid_t vendor, unsigned custom_flags, ddsrt_wctime_t timestamp, seqno_t seq)
{
/* No locking => iff all participants use unique guids, and sedp
runs on a single thread, it can't go wrong. FIXME, maybe? The
@@ -4978,7 +4978,7 @@ void new_proxy_participant (struct ddsi_domaingv *gv, const struct ddsi_guid *pp
{
GVWARNING ("Remote secure participant "PGUIDFMT" not allowed\n", PGUID (*ppguid));
free_proxy_participant (proxypp);
- return;
+ return false;
}
}
#endif
@@ -5004,6 +5004,7 @@ void new_proxy_participant (struct ddsi_domaingv *gv, const struct ddsi_guid *pp
proxy_participant_create_handshakes (gv, proxypp);
}
#endif
+ return true;
}
int update_proxy_participant_plist_locked (struct proxy_participant *proxypp, seqno_t seq, const struct ddsi_plist *datap, ddsrt_wctime_t timestamp)
From 2ecc6eb99ef7e83febde1b27654c5f76af2896a7 Mon Sep 17 00:00:00 2001
From: Erik Boasson
Date: Sat, 16 May 2020 11:13:58 +0200
Subject: [PATCH 36/36] Tweak timeouts in secure communication tests
* Increase the matching timeout to 5s (there are some hints the failures
on Travis are timing related)
* Replace the relative timeout in the waitset by a timestamp so that it
gives up after the specified timeout regardless of the number of
events that occur
Signed-off-by: Erik Boasson
---
src/security/core/tests/common/test_utils.c | 6 ++++--
src/security/core/tests/secure_communication.c | 2 +-
2 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/src/security/core/tests/common/test_utils.c b/src/security/core/tests/common/test_utils.c
index cab9bef85c..39c0489f81 100644
--- a/src/security/core/tests/common/test_utils.c
+++ b/src/security/core/tests/common/test_utils.c
@@ -390,6 +390,7 @@ void handshake_list_fini (struct Handshake *hs_list, int nhs)
void sync_writer_to_readers (dds_entity_t pp_wr, dds_entity_t wr, uint32_t exp_count, dds_duration_t timeout)
{
+ dds_time_t abstimeout = dds_time() + timeout;
dds_attach_t triggered;
dds_entity_t ws = dds_create_waitset (pp_wr);
CU_ASSERT_FATAL (ws > 0);
@@ -399,7 +400,7 @@ void sync_writer_to_readers (dds_entity_t pp_wr, dds_entity_t wr, uint32_t exp_c
CU_ASSERT_EQUAL_FATAL (ret, DDS_RETCODE_OK);
while (true)
{
- ret = dds_waitset_wait (ws, &triggered, 1, timeout);
+ ret = dds_waitset_wait_until (ws, &triggered, 1, abstimeout);
CU_ASSERT_EQUAL_FATAL (exp_count > 0, ret >= 1);
if (exp_count > 0)
CU_ASSERT_EQUAL_FATAL (wr, (dds_entity_t)(intptr_t) triggered);
@@ -414,6 +415,7 @@ void sync_writer_to_readers (dds_entity_t pp_wr, dds_entity_t wr, uint32_t exp_c
void sync_reader_to_writers (dds_entity_t pp_rd, dds_entity_t rd, uint32_t exp_count, dds_duration_t timeout)
{
+ dds_time_t abstimeout = dds_time() + timeout;
dds_attach_t triggered;
dds_entity_t ws = dds_create_waitset (pp_rd);
CU_ASSERT_FATAL (ws > 0);
@@ -423,7 +425,7 @@ void sync_reader_to_writers (dds_entity_t pp_rd, dds_entity_t rd, uint32_t exp_c
CU_ASSERT_EQUAL_FATAL (ret, DDS_RETCODE_OK);
while (true)
{
- ret = dds_waitset_wait (ws, &triggered, 1, timeout);
+ ret = dds_waitset_wait_until (ws, &triggered, 1, abstimeout);
CU_ASSERT_EQUAL_FATAL (exp_count > 0, ret >= 1);
if (exp_count > 0)
CU_ASSERT_EQUAL_FATAL (rd, (dds_entity_t)(intptr_t) triggered);
diff --git a/src/security/core/tests/secure_communication.c b/src/security/core/tests/secure_communication.c
index 3a3a6ecfb2..2d1720db75 100644
--- a/src/security/core/tests/secure_communication.c
+++ b/src/security/core/tests/secure_communication.c
@@ -254,7 +254,7 @@ static void test_write_read(struct domain_sec_config *domain_config,
for (size_t w = 0; w < n_writers; w++)
{
size_t wr_index = pp_index * n_writers + w;
- sync_writer_to_readers (g_pub_participants[pp_index], writers[wr_index], (uint32_t)(n_sub_domains * n_sub_participants * n_readers), DDS_SECS(2));
+ sync_writer_to_readers (g_pub_participants[pp_index], writers[wr_index], (uint32_t)(n_sub_domains * n_sub_participants * n_readers), DDS_SECS(5));
sample.id = (int32_t) wr_index;
printf("writer %"PRId32" writing sample %d\n", writers[wr_index], sample.id);
ret = dds_write (writers[wr_index], &sample);