Merge branch 'master' into security

Signed-off-by: Erik Boasson <eb@ilities.com>
This commit is contained in:
Erik Boasson 2020-02-12 16:00:57 +01:00
commit ad58db0721
158 changed files with 6915 additions and 3361 deletions

View file

@ -30,7 +30,6 @@ PREPEND(srcs_ddsc "${CMAKE_CURRENT_LIST_DIR}/src"
dds_topic.c
dds_listener.c
dds_read.c
dds_stream.c
dds_waitset.c
dds_readcond.c
dds_guardcond.c
@ -51,6 +50,7 @@ PREPEND(hdrs_public_ddsc "$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include/dd
ddsc/dds_public_qosdefs.h
ddsc/dds_public_status.h
ddsc/dds_rhc.h
ddsc/dds_internal_api.h
)
PREPEND(hdrs_private_ddsc "${CMAKE_CURRENT_LIST_DIR}/src"
@ -69,7 +69,6 @@ PREPEND(hdrs_private_ddsc "${CMAKE_CURRENT_LIST_DIR}/src"
dds__guardcond.h
dds__reader.h
dds__rhc_default.h
dds__stream.h
dds__subscriber.h
dds__topic.h
dds__types.h

View file

@ -50,6 +50,8 @@ extern "C" {
#endif
struct dds_rhc;
struct ddsi_plist;
struct ddsi_sertopic;
struct ddsi_serdata;
#define DDS_MIN_PSEUDO_HANDLE ((dds_entity_t) 0x7fff0000)
@ -555,7 +557,9 @@ dds_set_enabled_status(dds_entity_t entity, uint32_t mask);
* @param[in] entity Entity on which to get qos.
* @param[out] qos Pointer to the qos structure that returns the set policies.
*
* @returns A dds_return_t indicating success or failure.
* @returns A dds_return_t indicating success or failure. The QoS object will have
* at least all QoS relevant for the entity present and the corresponding dds_qget_...
* will return true.
*
* @retval DDS_RETCODE_OK
* The existing set of QoS policy values applied to the entity
@ -962,7 +966,9 @@ dds_lookup_participant(
* @brief Creates a new topic with default type handling.
*
* The type name for the topic is taken from the generated descriptor. Topic
* matching is done on a combination of topic name and type name.
* matching is done on a combination of topic name and type name. Each successful
* call to dds_create_topic creates a new topic entity sharing the same QoS
* settings with all other topics of the same name.
*
* @param[in] participant Participant on which to create the topic.
* @param[in] descriptor An IDL generated topic descriptor.
@ -970,14 +976,20 @@ dds_lookup_participant(
* @param[in] qos QoS to set on the new topic (can be NULL).
* @param[in] listener Any listener functions associated with the new topic (can be NULL).
*
* @returns A valid topic handle or an error code.
* @returns A valid, unique topic handle or an error code.
*
* @retval >=0
* A valid topic handle.
* A valid unique topic handle.
* @retval DDS_RETCODE_BAD_PARAMETER
* Either participant, descriptor, name or qos is invalid.
* @retval DDS_RETCODE_BAD_PARAMETER
* Either participant, descriptor, name or qos is invalid.
* @retval DDS_RETCODE_INCONSISTENT_POLICY
* QoS mismatch between qos and an existing topic's QoS.
* @retval DDS_RETCODE_PRECONDITION_NOT_MET
* Mismatch between type name in descriptor and pre-existing
* topic's type name.
*/
/* TODO: Check list of retcodes is complete. */
DDS_EXPORT dds_entity_t
dds_create_topic(
dds_entity_t participant,
@ -986,13 +998,17 @@ dds_create_topic(
const dds_qos_t *qos,
const dds_listener_t *listener);
struct ddsi_sertopic;
struct nn_plist;
/**
* @brief Creates a new topic with arbitrary type handling.
*
* The type name for the topic is taken from the provided "sertopic" object. Topic
* matching is done on a combination of topic name and type name.
* matching is done on a combination of topic name and type name. Each successful
* call to dds_create_topic creates a new topic entity sharing the same QoS
* settings with all other topics of the same name.
*
* If sertopic is not yet known in the domain, it is added and its refcount
* incremented; if an equivalent sertopic object is already known, then the known
* one is used instead.
*
* @param[in] participant Participant on which to create the topic.
* @param[in] sertopic Internal description of the topic type (includes name).
@ -1000,21 +1016,27 @@ struct nn_plist;
* @param[in] listener Any listener functions associated with the new topic (can be NULL).
* @param[in] sedp_plist Topic description to be published as part of discovery (if NULL, not published).
*
* @returns A valid topic handle or an error code.
* @returns A valid, unique topic handle or an error code.
*
* @retval >=0
* A valid topic handle.
* A valid unique topic handle.
* @retval DDS_RETCODE_BAD_PARAMETER
* Either participant, descriptor, name or qos is invalid.
* @retval DDS_RETCODE_BAD_PARAMETER
* Either participant, descriptor, name or qos is invalid.
* @retval DDS_RETCODE_INCONSISTENT_POLICY
* QoS mismatch between qos and an existing topic's QoS.
* @retval DDS_RETCODE_PRECONDITION_NOT_MET
* Mismatch between type name in sertopic and pre-existing
* topic's type name.
*/
/* TODO: Check list of retcodes is complete. */
DDS_EXPORT dds_entity_t
dds_create_topic_arbitrary (
dds_entity_t participant,
struct ddsi_sertopic *sertopic,
const dds_qos_t *qos,
const dds_listener_t *listener,
const struct nn_plist *sedp_plist);
const struct ddsi_plist *sedp_plist);
/**
* @brief Finds a named topic.
@ -1030,8 +1052,9 @@ dds_create_topic_arbitrary (
* A valid topic handle.
* @retval DDS_RETCODE_BAD_PARAMETER
* Participant was invalid.
* @retval DDS_RETCODE_PRECONDITION_NOT_MET
* No topic of this name existed yet in the participant
*/
/* TODO: Check list of retcodes is complete. */
DDS_EXPORT dds_entity_t
dds_find_topic(dds_entity_t participant, const char *name);
@ -1047,8 +1070,6 @@ dds_find_topic(dds_entity_t participant, const char *name);
* @retval DDS_RETCODE_OK
* Success.
*/
/* TODO: do we need a convenience version as well that allocates and add a _s suffix to this one? */
/* TODO: Check annotation. Could be _Out_writes_to_(size, return + 1) as well. */
DDS_EXPORT dds_return_t
dds_get_name(dds_entity_t topic, char *name, size_t size);
@ -1324,7 +1345,7 @@ dds_create_writer(
*
* This operation registers an instance with a key value to the data writer and
* returns an instance handle that could be used for successive write & dispose
* operations. When the handle is not allocated, the function will return and
* operations. When the handle is not allocated, the function will return an
* error and the handle will be un-touched.
*
* @param[in] writer The writer to which instance has be associated.
@ -2035,7 +2056,7 @@ dds_waitset_attach(
* @returns A dds_return_t indicating success or failure.
*
* @retval DDS_RETCODE_OK
* Entity attached.
* Entity detached.
* @retval DDS_RETCODE_ERROR
* An internal error has occurred.
* @retval DDS_RETCODE_BAD_PARAMETER
@ -2072,7 +2093,7 @@ dds_waitset_detach(
* @returns A dds_return_t indicating success or failure.
*
* @retval DDS_RETCODE_OK
* Entity attached.
* Trigger value set.
* @retval DDS_RETCODE_ERROR
* An internal error has occurred.
* @retval DDS_RETCODE_BAD_PARAMETER

View file

@ -0,0 +1,44 @@
/*
* 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
* 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
*/
/** @file
* WARNING This file is only needed for the work around for https://github.com/eclipse-cyclonedds/cyclonedds/issues/74
* Do not include this file in an application! Once issue #74 is solved this header file should be removed.
*/
#ifndef DDS_INTERNAL_API_H
#define DDS_INTERNAL_API_H
#if defined (__cplusplus)
extern "C" {
#endif
#define DDS_READ_WITHOUT_LOCK (0xFFFFFFED)
/*
* @private
* dds_reader_lock_samples: Returns number of samples in read cache and locks the
* reader cache to make sure that the samples content doesn't change.
* Because the cache is locked, you should be able to read/take without having to
* lock first. This is done by passing the DDS_READ_WITHOUT_LOCK value to the
* read/take call as maxs. Then the read/take will not lock but still unlock.
*
* CycloneDDS doesn't support a read/take that just returns all
* available samples issue #74. As a work around to support LENGTH_UNLIMITED in C++.
* dds_reader_lock_samples() and DDS_READ_WITHOUT_LOCK are needed.
*/
DDS_EXPORT uint32_t
dds_reader_lock_samples (dds_entity_t entity);
#if defined (__cplusplus)
}
#endif
#endif

View file

@ -112,6 +112,7 @@ typedef enum dds_entity_kind
DDS_KIND_DOMAIN,
DDS_KIND_CYCLONEDDS
} dds_entity_kind_t;
#define DDS_KIND_MAX DDS_KIND_CYCLONEDDS
/* Handles are opaque pointers to implementation types */
typedef uint64_t dds_instance_handle_t;

View file

@ -149,6 +149,8 @@ dds_qset_durability (dds_qos_t * __restrict qos, dds_durability_kind_t kind);
/**
* @brief Set the history policy of a qos structure.
*
* Note that depth is only relevant for keep last. If you want limited history for keep all, use dds_qset_resource_limits().
*
* @param[in,out] qos - Pointer to a dds_qos_t structure that will store the policy
* @param[in] kind - History kind value \ref DCPS_QoS_History
@ -297,7 +299,7 @@ dds_qset_partition1 (
*
* @param[in,out] qos - Pointer to a dds_qos_t structure that will store the policy
* @param[in] kind - Reliability kind
* @param[in] max_blocking_time - Max blocking duration applied when kind is reliable.
* @param[in] max_blocking_time - Max blocking duration applied when kind is reliable. This is how long the writer will block when its history is full.
*/
DDS_EXPORT void
dds_qset_reliability (

View file

@ -18,8 +18,6 @@
extern "C" {
#endif
void dds_sample_free_contents (char * data, const uint32_t * ops);
#if defined (__cplusplus)
}
#endif

View file

@ -86,7 +86,8 @@ DDS_EXPORT void dds_entity_status_signal (dds_entity *e, uint32_t status);
DDS_EXPORT void dds_entity_invoke_listener (const dds_entity *entity, enum dds_status_id which, const void *vst);
DDS_EXPORT dds_participant *dds_entity_participant (dds_entity *e);
DDS_EXPORT dds_participant *dds_entity_participant (const dds_entity *e);
DDS_EXPORT const ddsi_guid_t *dds_entity_participant_guid (const dds_entity *e);
DDS_EXPORT void dds_entity_final_deinit_before_free (dds_entity *e);
DDS_EXPORT bool dds_entity_in_scope (const dds_entity *e, const dds_entity *root);

View file

@ -20,6 +20,8 @@ extern "C" {
DEFINE_ENTITY_LOCK_UNLOCK(inline, dds_participant, DDS_KIND_PARTICIPANT)
extern const ddsrt_avl_treedef_t participant_ktopics_treedef;
#if defined (__cplusplus)
}
#endif

View file

@ -12,7 +12,7 @@
#ifndef _DDS_QOS_H_
#define _DDS_QOS_H_
#include "dds/ddsi/q_xqos.h"
#include "dds/ddsi/ddsi_xqos.h"
#if defined (__cplusplus)
extern "C" {

View file

@ -19,13 +19,18 @@ extern "C" {
struct dds_rhc;
struct dds_reader;
struct ddsi_sertopic;
struct q_globals;
struct ddsi_domaingv;
struct dds_rhc_default;
struct rhc_sample;
DDS_EXPORT struct dds_rhc *dds_rhc_default_new_xchecks (dds_reader *reader, struct q_globals *gv, const struct ddsi_sertopic *topic, bool xchecks);
DDS_EXPORT struct dds_rhc *dds_rhc_default_new_xchecks (dds_reader *reader, struct ddsi_domaingv *gv, const struct ddsi_sertopic *topic, bool xchecks);
DDS_EXPORT struct dds_rhc *dds_rhc_default_new (struct dds_reader *reader, const struct ddsi_sertopic *topic);
#ifdef DDSI_INCLUDE_LIFESPAN
DDS_EXPORT nn_mtime_t dds_rhc_default_sample_expired_cb(void *hc, nn_mtime_t tnow);
#endif
#ifdef DDSI_INCLUDE_DEADLINE_MISSED
DDS_EXPORT nn_mtime_t dds_rhc_default_deadline_missed_cb(void *hc, nn_mtime_t tnow);
#endif
#if defined (__cplusplus)
}

View file

@ -9,10 +9,11 @@
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
#ifndef DDSI_SERDATA_BUILTINTOPIC_H
#define DDSI_SERDATA_BUILTINTOPIC_H
#ifndef DDS__SERDATA_BUILTINTOPIC_H
#define DDS__SERDATA_BUILTINTOPIC_H
#include "dds/ddsi/q_xqos.h"
#include "dds/dds.h"
#include "dds/ddsi/ddsi_xqos.h"
#include "dds/ddsi/ddsi_serdata.h"
#include "dds/ddsi/ddsi_sertopic.h"
@ -33,17 +34,15 @@ enum ddsi_sertopic_builtintopic_type {
DSBT_WRITER
};
struct q_globals;
struct ddsi_sertopic_builtintopic {
struct ddsi_sertopic c;
enum ddsi_sertopic_builtintopic_type type;
struct q_globals *gv;
};
extern const struct ddsi_sertopic_ops ddsi_sertopic_ops_builtintopic;
extern const struct ddsi_serdata_ops ddsi_serdata_ops_builtintopic;
struct ddsi_sertopic *new_sertopic_builtintopic (enum ddsi_sertopic_builtintopic_type type, const char *name, const char *typename, struct q_globals *gv);
struct ddsi_sertopic *new_sertopic_builtintopic (enum ddsi_sertopic_builtintopic_type type, const char *name, const char *typename);
#if defined (__cplusplus)
}

View file

@ -1,88 +0,0 @@
/*
* Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
* v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
#ifndef _DDS_STREAM_H_
#define _DDS_STREAM_H_
#include "dds/ddsi/ddsi_serdata.h"
#include "dds/ddsi/ddsi_serdata_default.h"
#if defined (__cplusplus)
extern "C" {
#endif
typedef struct dds_istream {
const unsigned char *m_buffer;
uint32_t m_size; /* Buffer size */
uint32_t m_index; /* Read/write offset from start of buffer */
} dds_istream_t;
typedef struct dds_ostream {
unsigned char *m_buffer;
uint32_t m_size; /* Buffer size */
uint32_t m_index; /* Read/write offset from start of buffer */
} dds_ostream_t;
typedef struct dds_ostreamBE {
dds_ostream_t x;
} dds_ostreamBE_t;
DDS_EXPORT void dds_ostream_init (dds_ostream_t * __restrict st, uint32_t size);
DDS_EXPORT void dds_ostream_fini (dds_ostream_t * __restrict st);
DDS_EXPORT void dds_ostreamBE_init (dds_ostreamBE_t * __restrict st, uint32_t size);
DDS_EXPORT void dds_ostreamBE_fini (dds_ostreamBE_t * __restrict st);
bool dds_stream_normalize (void * __restrict data, uint32_t size, bool bswap, const struct ddsi_sertopic_default * __restrict topic, bool just_key);
void dds_stream_write_sample (dds_ostream_t * __restrict os, const void * __restrict data, const struct ddsi_sertopic_default * __restrict topic);
void dds_stream_read_sample (dds_istream_t * __restrict is, void * __restrict data, const struct ddsi_sertopic_default * __restrict topic);
size_t dds_stream_check_optimize (const dds_topic_descriptor_t * __restrict desc);
void dds_istream_from_serdata_default (dds_istream_t * __restrict s, const struct ddsi_serdata_default * __restrict d);
void dds_ostream_from_serdata_default (dds_ostream_t * __restrict s, struct ddsi_serdata_default * __restrict d);
void dds_ostream_add_to_serdata_default (dds_ostream_t * __restrict s, struct ddsi_serdata_default ** __restrict d);
void dds_ostreamBE_from_serdata_default (dds_ostreamBE_t * __restrict s, struct ddsi_serdata_default * __restrict d);
void dds_ostreamBE_add_to_serdata_default (dds_ostreamBE_t * __restrict s, struct ddsi_serdata_default ** __restrict d);
void dds_stream_write_key (dds_ostream_t * __restrict os, const char * __restrict sample, const struct ddsi_sertopic_default * __restrict topic);
void dds_stream_write_keyBE (dds_ostreamBE_t * __restrict os, const char * __restrict sample, const struct ddsi_sertopic_default * __restrict topic);
void dds_stream_extract_key_from_data (dds_istream_t * __restrict is, dds_ostream_t * __restrict os, const struct ddsi_sertopic_default * __restrict topic);
void dds_stream_extract_keyBE_from_data (dds_istream_t * __restrict is, dds_ostreamBE_t * __restrict os, const struct ddsi_sertopic_default * __restrict topic);
void dds_stream_extract_keyhash (dds_istream_t * __restrict is, dds_keyhash_t * __restrict kh, const struct ddsi_sertopic_default * __restrict topic, const bool just_key);
void dds_stream_read_key (dds_istream_t * __restrict is, char * __restrict sample, const struct ddsi_sertopic_default * __restrict topic);
size_t dds_stream_print_key (dds_istream_t * __restrict is, const struct ddsi_sertopic_default * __restrict topic, char * __restrict buf, size_t size);
size_t dds_stream_print_sample (dds_istream_t * __restrict is, const struct ddsi_sertopic_default * __restrict topic, char * __restrict buf, size_t size);
/* For marshalling op code handling */
#define DDS_OP_MASK 0xff000000
#define DDS_OP_TYPE_MASK 0x00ff0000
#define DDS_OP_SUBTYPE_MASK 0x0000ff00
#define DDS_OP_JMP_MASK 0x0000ffff
#define DDS_OP_FLAGS_MASK 0x000000ff
#define DDS_JEQ_TYPE_MASK 0x00ff0000
#define DDS_OP(o) ((enum dds_stream_opcode) ((o) & DDS_OP_MASK))
#define DDS_OP_TYPE(o) ((enum dds_stream_typecode) (((o) & DDS_OP_TYPE_MASK) >> 16))
#define DDS_OP_SUBTYPE(o) ((enum dds_stream_typecode) (((o) & DDS_OP_SUBTYPE_MASK) >> 8))
#define DDS_OP_FLAGS(o) ((o) & DDS_OP_FLAGS_MASK)
#define DDS_OP_ADR_JSR(o) ((o) & DDS_OP_JMP_MASK)
#define DDS_OP_JUMP(o) ((int16_t) ((o) & DDS_OP_JMP_MASK))
#define DDS_OP_ADR_JMP(o) ((o) >> 16)
#define DDS_JEQ_TYPE(o) ((enum dds_stream_typecode) (((o) & DDS_JEQ_TYPE_MASK) >> 16))
#if defined (__cplusplus)
}
#endif
#endif

View file

@ -21,9 +21,13 @@ extern "C" {
DEFINE_ENTITY_LOCK_UNLOCK(inline, dds_topic, DDS_KIND_TOPIC)
DDS_EXPORT struct ddsi_sertopic * dds_topic_lookup (dds_domain * domain, const char * name) ddsrt_nonnull_all;
DDS_EXPORT void dds_topic_free (dds_domainid_t domainid, struct ddsi_sertopic * st) ddsrt_nonnull_all;
DDS_EXPORT dds_return_t dds_topic_pin (dds_entity_t handle, struct dds_topic **tp) ddsrt_nonnull_all;
DDS_EXPORT void dds_topic_unpin (struct dds_topic *tp) ddsrt_nonnull_all;
DDS_EXPORT void dds_topic_defer_set_qos (struct dds_topic *tp) ddsrt_nonnull_all;
DDS_EXPORT void dds_topic_allow_set_qos (struct dds_topic *tp) ddsrt_nonnull_all;
#ifndef DDS_TOPIC_INTERN_FILTER_FN_DEFINED
#define DDS_TOPIC_INTERN_FILTER_FN_DEFINED
typedef bool (*dds_topic_intern_filter_fn) (const void * sample, void *ctx);
@ -35,7 +39,7 @@ DDS_EXPORT void dds_topic_set_filter_with_ctx
DDS_EXPORT dds_topic_intern_filter_fn dds_topic_get_filter_with_ctx
(dds_entity_t topic);
DDS_EXPORT dds_entity_t dds_create_topic_impl (dds_entity_t participant, struct ddsi_sertopic *sertopic, const dds_qos_t *qos, const dds_listener_t *listener, const nn_plist_t *sedp_plist);
DDS_EXPORT dds_entity_t dds_create_topic_impl (dds_entity_t participant, struct ddsi_sertopic *sertopic, const dds_qos_t *qos, const dds_listener_t *listener, const ddsi_plist_t *sedp_plist);
#if defined (__cplusplus)
}

View file

@ -17,7 +17,7 @@
#include "dds/dds.h"
#include "dds/ddsrt/sync.h"
#include "dds/ddsi/q_rtps.h"
#include "dds/ddsi/q_globals.h"
#include "dds/ddsi/ddsi_domaingv.h"
#include "dds/ddsrt/avl.h"
#include "dds/ddsi/ddsi_builtin_topic_if.h"
#include "dds__handles.h"
@ -34,6 +34,7 @@ struct dds_writer;
struct dds_publisher;
struct dds_subscriber;
struct dds_topic;
struct dds_ktopic;
struct dds_readcond;
struct dds_guardcond;
struct dds_statuscond;
@ -127,7 +128,7 @@ typedef struct dds_entity {
ddsrt_avl_node_t m_avlnode_child; /* [m_mutex of m_parent] */
ddsrt_avl_tree_t m_children; /* [m_mutex] tree on m_iid using m_avlnode_child */
struct dds_domain *m_domain; /* constant */
dds_qos_t *m_qos; /* [m_mutex] */
dds_qos_t *m_qos; /* [m_mutex]; null for topics (they rely on correpsonding "ktopic") (+waitset,domain,&c.) */
ddsi_guid_t m_guid; /* unique (if not 0) and constant; FIXME: set during creation, but possibly after becoming visible */
dds_instance_handle_t m_iid; /* unique for all time, constant; FIXME: like GUID */
uint32_t m_flags; /* [m_mutex] */
@ -215,7 +216,6 @@ typedef struct dds_domain {
ddsrt_avl_node_t m_node; /* for dds_global.m_domains */
dds_domainid_t m_id;
ddsrt_avl_tree_t m_topics;
struct cfgst *cfgst;
struct ddsi_sertopic *builtin_participant_topic;
@ -227,7 +227,7 @@ typedef struct dds_domain {
struct local_orphan_writer *builtintopic_writer_subscriptions;
struct ddsi_builtin_topic_interface btif;
struct q_globals gv;
struct ddsi_domaingv gv;
} dds_domain;
typedef struct dds_subscriber {
@ -238,14 +238,31 @@ typedef struct dds_publisher {
struct dds_entity m_entity;
} dds_publisher;
typedef struct dds_ktopic {
/* name -> <type_name, QoS> mapping for topics, part of the participant
and protected by the participant's lock (including the actual QoS
setting)
defer_set_qos is used to implement an intentionally unfair single-writer/
multiple-reader lock using the participant's lock & cond var: set_qos
"write-locks" it, create_reader and create_writer "read-lock" it. */
ddsrt_avl_node_t pp_ktopics_avlnode;
uint32_t refc;
uint32_t defer_set_qos; /* set_qos must wait for this to be 0 */
dds_qos_t *qos;
char *name; /* [constant] */
char *type_name; /* [constant] */
} dds_ktopic;
typedef struct dds_participant {
struct dds_entity m_entity;
dds_entity_t m_builtin_subscriber;
ddsrt_avl_tree_t m_ktopics; /* [m_entity.m_mutex] */
} dds_participant;
typedef struct dds_reader {
struct dds_entity m_entity;
struct dds_topic *m_topic;
struct dds_topic *m_topic; /* refc'd, constant, lock(rd) -> lock(tp) allowed */
struct dds_rhc *m_rhc; /* aliases m_rd->rhc with a wider interface, FIXME: but m_rd owns it for resource management */
struct reader *m_rd;
bool m_data_on_readers;
@ -265,7 +282,7 @@ typedef struct dds_reader {
typedef struct dds_writer {
struct dds_entity m_entity;
struct dds_topic *m_topic;
struct dds_topic *m_topic; /* refc'd, constant, lock(wr) -> lock(tp) allowed */
struct nn_xpack *m_xp;
struct writer *m_wr;
struct whc *m_whc; /* FIXME: ownership still with underlying DDSI writer (cos of DDSI built-in writers )*/
@ -287,6 +304,7 @@ typedef bool (*dds_topic_intern_filter_fn) (const void * sample, void *ctx);
typedef struct dds_topic {
struct dds_entity m_entity;
struct ddsi_sertopic *m_stopic;
struct dds_ktopic *m_ktopic; /* refc'd, constant */
dds_topic_intern_filter_fn filter_fn;
void *filter_ctx;

View file

@ -18,8 +18,13 @@
extern "C" {
#endif
struct q_globals;
struct whc *whc_new (struct q_globals *gv, int is_transient_local, uint32_t hdepth, uint32_t tldepth);
struct ddsi_domaingv;
struct whc_writer_info;
struct dds_writer;
struct whc *whc_new (struct ddsi_domaingv *gv, const struct whc_writer_info *wrinfo);
struct whc_writer_info *whc_make_wrinfo (struct dds_writer *wr, const dds_qos_t *qos);
void whc_free_wrinfo (struct whc_writer_info *);
#if defined (__cplusplus)
}

View file

@ -20,6 +20,11 @@ extern "C" {
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);
#if defined (__cplusplus)
}
#endif

View file

@ -13,21 +13,9 @@
#include <string.h>
#include "dds__alloc.h"
#include "dds__stream.h"
#include "dds/ddsrt/heap.h"
#include "dds/ddsi/q_config.h"
/*
#define OP_DEBUG_FREE 1
*/
#if defined OP_DEBUG_FREE
static const char * stream_op_type[11] =
{
NULL, "1Byte", "2Byte", "4Byte", "8Byte", "String",
"BString", "Sequence", "Array", "Union", "Struct"
};
#endif
#include "dds/ddsi/ddsi_cdrstream.h"
static dds_allocator_t dds_allocator_fns = { ddsrt_malloc, ddsrt_realloc, ddsrt_free };
@ -87,287 +75,30 @@ void dds_string_free (char * str)
dds_free (str);
}
void dds_sample_free_contents (char *data, const uint32_t * ops)
static void dds_sample_free_key (void *vsample, const struct dds_topic_descriptor * desc)
{
uint32_t op;
uint32_t type;
uint32_t num;
uint32_t subtype;
char * addr;
while ((op = *ops) != DDS_OP_RTS)
char *sample = vsample;
for (uint32_t i = 0; i < desc->m_nkeys; i++)
{
switch (DDS_OP_MASK & op)
{
case DDS_OP_ADR:
{
type = DDS_OP_TYPE (op);
#ifdef OP_DEBUG_FREE
DDS_TRACE("F-ADR: %s offset %d\n", stream_op_type[type], ops[1]);
#endif
addr = data + ops[1];
ops += 2;
switch (type)
{
case DDS_OP_VAL_1BY:
case DDS_OP_VAL_2BY:
case DDS_OP_VAL_4BY:
case DDS_OP_VAL_8BY:
{
break;
}
case DDS_OP_VAL_STR:
{
#ifdef OP_DEBUG_FREE
DDS_TRACE("F-STR: @ %p %s\n", addr, *((char**) addr));
#endif
dds_free (*((char**) addr));
*((char**) addr) = NULL;
break;
}
case DDS_OP_VAL_SEQ:
{
dds_sequence_t * seq = (dds_sequence_t*) addr;
subtype = DDS_OP_SUBTYPE (op);
num = (seq->_maximum > seq->_length) ? seq->_maximum : seq->_length;
#ifdef OP_DEBUG_FREE
DDS_TRACE("F-SEQ: of %s\n", stream_op_type[subtype]);
#endif
if ((seq->_release && num) || (subtype > DDS_OP_VAL_STR))
{
switch (subtype)
{
case DDS_OP_VAL_1BY:
case DDS_OP_VAL_2BY:
case DDS_OP_VAL_4BY:
case DDS_OP_VAL_8BY:
{
break;
}
case DDS_OP_VAL_BST:
{
ops++;
break;
}
case DDS_OP_VAL_STR:
{
char ** ptr = (char**) seq->_buffer;
while (num--)
{
dds_free (*ptr++);
}
break;
}
default:
{
const uint32_t elem_size = *ops++;
const uint32_t * jsr_ops = ops + DDS_OP_ADR_JSR (*ops) - 3;
const uint32_t jmp = DDS_OP_ADR_JMP (*ops);
char * ptr = (char*) seq->_buffer;
while (num--)
{
dds_sample_free_contents (ptr, jsr_ops);
ptr += elem_size;
}
ops += jmp ? (jmp - 3) : 1;
break;
}
}
}
if (seq->_release)
{
dds_free (seq->_buffer);
seq->_maximum = 0;
seq->_length = 0;
seq->_buffer = NULL;
}
break;
}
case DDS_OP_VAL_ARR:
{
subtype = DDS_OP_SUBTYPE (op);
num = *ops++;
#ifdef OP_DEBUG_FREE
DDS_TRACE("F-ARR: of %s size %d\n", stream_op_type[subtype], num);
#endif
switch (subtype)
{
case DDS_OP_VAL_1BY:
case DDS_OP_VAL_2BY:
case DDS_OP_VAL_4BY:
case DDS_OP_VAL_8BY:
{
break;
}
case DDS_OP_VAL_STR:
{
char ** ptr = (char**) addr;
while (num--)
{
dds_free (*ptr++);
}
break;
}
case DDS_OP_VAL_BST:
{
ops += 2;
break;
}
default:
{
const uint32_t * jsr_ops = ops + DDS_OP_ADR_JSR (*ops) - 3;
const uint32_t jmp = DDS_OP_ADR_JMP (*ops);
const uint32_t elem_size = ops[1];
while (num--)
{
dds_sample_free_contents (addr, jsr_ops);
addr += elem_size;
}
ops += jmp ? (jmp - 3) : 2;
break;
}
}
break;
}
case DDS_OP_VAL_UNI:
{
const bool has_default = op & DDS_OP_FLAG_DEF;
subtype = DDS_OP_SUBTYPE (op);
num = ops[0];
const uint32_t * jeq_op = ops + DDS_OP_ADR_JSR (ops[1]) - 2;
uint32_t disc = 0;
assert (subtype <= DDS_OP_VAL_4BY);
#ifdef OP_DEBUG_FREE
DDS_TRACE("F-UNI: switch %s cases %d\n", stream_op_type[subtype], num);
#endif
/* Get discriminant */
switch (subtype)
{
case DDS_OP_VAL_1BY:
{
disc = *((uint8_t*) addr);
break;
}
case DDS_OP_VAL_2BY:
{
disc = *((uint16_t*) addr);
break;
}
case DDS_OP_VAL_4BY:
{
disc = *((uint32_t*) addr);
break;
}
default: assert (0);
}
/* Free case matching discriminant */
while (num--)
{
assert ((DDS_OP_MASK & jeq_op[0]) == DDS_OP_JEQ);
if ((jeq_op[1] == disc) || (has_default && (num == 0)))
{
subtype = DDS_JEQ_TYPE (jeq_op[0]);
addr = data + jeq_op[2];
switch (subtype)
{
case DDS_OP_VAL_1BY:
case DDS_OP_VAL_2BY:
case DDS_OP_VAL_4BY:
case DDS_OP_VAL_8BY:
case DDS_OP_VAL_BST:
{
break;
}
case DDS_OP_VAL_STR:
{
dds_free (*((char**) addr));
*((char**) addr) = NULL;
break;
}
default:
{
dds_sample_free_contents (addr, jeq_op + DDS_OP_ADR_JSR (jeq_op[0]));
break;
}
}
break;
}
jeq_op += 3;
}
/* Jump to next instruction */
ops += DDS_OP_ADR_JMP (ops[1]) - 2;
break;
}
case DDS_OP_VAL_BST:
{
ops++;
break;
}
default: assert (0);
}
break;
}
case DDS_OP_JSR: /* Implies nested type */
{
#ifdef OP_DEBUG_FREE
DDS_TRACE("F-JSR: %d\n", DDS_OP_JUMP (op));
#endif
dds_sample_free_contents (data, ops + DDS_OP_JUMP (op));
ops++;
break;
}
default: assert (0);
}
}
#ifdef OP_DEBUG_FREE
DDS_TRACE("F-RTS:\n");
#endif
}
static void dds_sample_free_key (char * sample, const struct dds_topic_descriptor * desc)
{
uint32_t i;
const uint32_t * op;
for (i = 0; i < desc->m_nkeys; i++)
{
op = desc->m_ops + desc->m_keys[i].m_index;
const uint32_t *op = desc->m_ops + desc->m_keys[i].m_index;
if (DDS_OP_TYPE (*op) == DDS_OP_VAL_STR)
{
dds_free (*(char**)(sample + op[1]));
}
dds_free (*(char **) (sample + op[1]));
}
}
void dds_sample_free (void * sample, const struct dds_topic_descriptor * desc, dds_free_op_t op)
{
/* external API, so can't replace the dds_topic_decsriptor type ... */
assert (desc);
if (sample)
{
if (op & DDS_FREE_CONTENTS_BIT)
{
dds_sample_free_contents ((char*) sample, desc->m_ops);
}
dds_stream_free_sample (sample, desc->m_ops);
else if (op & DDS_FREE_KEY_BIT)
{
dds_sample_free_key ((char*) sample, desc);
}
dds_sample_free_key (sample, desc);
if (op & DDS_FREE_ALL_BIT)
{
dds_free (sample);
}
}
}

View file

@ -15,8 +15,8 @@
#include "dds/ddsi/q_entity.h"
#include "dds/ddsi/q_thread.h"
#include "dds/ddsi/q_config.h"
#include "dds/ddsi/q_globals.h"
#include "dds/ddsi/q_plist.h" /* for nn_keyhash */
#include "dds/ddsi/ddsi_domaingv.h"
#include "dds/ddsi/ddsi_plist.h"
#include "dds__init.h"
#include "dds__domain.h"
#include "dds__participant.h"
@ -24,6 +24,7 @@
#include "dds__builtin.h"
#include "dds__entity.h"
#include "dds__subscriber.h"
#include "dds__topic.h"
#include "dds__write.h"
#include "dds__writer.h"
#include "dds__whc_builtintopic.h"
@ -242,6 +243,13 @@ static void dds__builtin_write (const struct entity_common *e, nn_wctime_t times
}
}
static void unref_builtin_topics (struct dds_domain *dom)
{
ddsi_sertopic_unref (dom->builtin_participant_topic);
ddsi_sertopic_unref (dom->builtin_reader_topic);
ddsi_sertopic_unref (dom->builtin_writer_topic);
}
void dds__builtin_init (struct dds_domain *dom)
{
dds_qos_t *qos = dds__create_builtin_qos ();
@ -253,9 +261,15 @@ void dds__builtin_init (struct dds_domain *dom)
dom->btif.builtintopic_write = dds__builtin_write;
dom->gv.builtin_topic_interface = &dom->btif;
dom->builtin_participant_topic = new_sertopic_builtintopic (DSBT_PARTICIPANT, "DCPSParticipant", "org::eclipse::cyclonedds::builtin::DCPSParticipant", &dom->gv);
dom->builtin_reader_topic = new_sertopic_builtintopic (DSBT_READER, "DCPSSubscription", "org::eclipse::cyclonedds::builtin::DCPSSubscription", &dom->gv);
dom->builtin_writer_topic = new_sertopic_builtintopic (DSBT_WRITER, "DCPSPublication", "org::eclipse::cyclonedds::builtin::DCPSPublication", &dom->gv);
dom->builtin_participant_topic = new_sertopic_builtintopic (DSBT_PARTICIPANT, "DCPSParticipant", "org::eclipse::cyclonedds::builtin::DCPSParticipant");
dom->builtin_reader_topic = new_sertopic_builtintopic (DSBT_READER, "DCPSSubscription", "org::eclipse::cyclonedds::builtin::DCPSSubscription");
dom->builtin_writer_topic = new_sertopic_builtintopic (DSBT_WRITER, "DCPSPublication", "org::eclipse::cyclonedds::builtin::DCPSPublication");
ddsrt_mutex_lock (&dom->gv.sertopics_lock);
ddsi_sertopic_register_locked (&dom->gv, dom->builtin_participant_topic);
ddsi_sertopic_register_locked (&dom->gv, dom->builtin_reader_topic);
ddsi_sertopic_register_locked (&dom->gv, dom->builtin_writer_topic);
ddsrt_mutex_unlock (&dom->gv.sertopics_lock);
thread_state_awake (lookup_thread_state (), &dom->gv);
const struct entity_index *gh = dom->gv.entity_index;
@ -265,6 +279,11 @@ void dds__builtin_init (struct dds_domain *dom)
thread_state_asleep (lookup_thread_state ());
dds_delete_qos (qos);
/* ddsi_sertopic_init initializes the refcount to 1 and dds_sertopic_register_locked increments
it. All "real" references (such as readers and writers) are also accounted for in the
reference count, so we have an excess reference here. */
unref_builtin_topics (dom);
}
void dds__builtin_fini (struct dds_domain *dom)
@ -275,8 +294,5 @@ void dds__builtin_fini (struct dds_domain *dom)
delete_local_orphan_writer (dom->builtintopic_writer_publications);
delete_local_orphan_writer (dom->builtintopic_writer_subscriptions);
thread_state_asleep (lookup_thread_state ());
ddsi_sertopic_unref (dom->builtin_participant_topic);
ddsi_sertopic_unref (dom->builtin_reader_topic);
ddsi_sertopic_unref (dom->builtin_writer_topic);
unref_builtin_topics (dom);
}

View file

@ -13,6 +13,7 @@
#include "dds/ddsrt/process.h"
#include "dds/ddsrt/heap.h"
#include "dds/ddsrt/hopscotch.h"
#include "dds__init.h"
#include "dds/ddsc/dds_rhc.h"
#include "dds__domain.h"
@ -26,7 +27,7 @@
#include "dds/ddsi/q_entity.h"
#include "dds/ddsi/q_config.h"
#include "dds/ddsi/q_gc.h"
#include "dds/ddsi/q_globals.h"
#include "dds/ddsi/ddsi_domaingv.h"
static dds_return_t dds_domain_free (dds_entity *vdomain);
@ -59,7 +60,6 @@ static dds_entity_t dds_domain_init (dds_domain *domain, dds_domainid_t domain_i
domain->m_entity.m_iid = ddsi_iid_gen ();
domain->gv.tstart = now ();
ddsrt_avl_init (&dds_topictree_def, &domain->m_topics);
/* | domain_id | domain id in config | result
+-----------+---------------------+----------
@ -138,20 +138,9 @@ static dds_entity_t dds_domain_init (dds_domain *domain, dds_domainid_t domain_i
/* Set additional default participant properties */
char progname[50] = "UNKNOWN"; /* FIXME: once retrieving process names is back in */
char hostname[64];
domain->gv.default_local_plist_pp.process_id = (unsigned) ddsrt_getpid();
domain->gv.default_local_plist_pp.present |= PP_PRISMTECH_PROCESS_ID;
domain->gv.default_local_plist_pp.exec_name = dds_string_alloc(32);
(void) snprintf (domain->gv.default_local_plist_pp.exec_name, 32, "CycloneDDS: %u", domain->gv.default_local_plist_pp.process_id);
len = (uint32_t) (13 + strlen (domain->gv.default_local_plist_pp.exec_name));
domain->gv.default_local_plist_pp.present |= PP_PRISMTECH_EXEC_NAME;
if (ddsrt_gethostname (hostname, sizeof (hostname)) == DDS_RETCODE_OK)
{
domain->gv.default_local_plist_pp.node_name = dds_string_dup (hostname);
domain->gv.default_local_plist_pp.present |= PP_PRISMTECH_NODE_NAME;
}
len = (uint32_t) (strlen (progname) + 13);
domain->gv.default_local_plist_pp.entity_name = dds_alloc (len);
(void) snprintf (domain->gv.default_local_plist_pp.entity_name, len, "%s<%u>", progname, domain->gv.default_local_plist_pp.process_id);
(void) snprintf (domain->gv.default_local_plist_pp.entity_name, len, "%s<%u>", progname, (unsigned) ddsrt_getpid ());
domain->gv.default_local_plist_pp.present |= PP_ENTITY_NAME;
if (rtps_start (&domain->gv) < 0)

View file

@ -23,7 +23,7 @@
#include "dds__topic.h"
#include "dds/version.h"
#include "dds/ddsi/ddsi_pmd.h"
#include "dds/ddsi/q_xqos.h"
#include "dds/ddsi/ddsi_xqos.h"
#include "dds/ddsi/q_transmit.h"
extern inline dds_entity *dds_entity_from_handle_link (struct dds_handle_link *hdllink);
@ -135,11 +135,65 @@ static bool entity_has_status (const dds_entity *e)
return false;
}
static bool entity_may_have_children (const dds_entity *e)
{
switch (e->m_kind)
{
case DDS_KIND_TOPIC:
return false;
case DDS_KIND_READER:
case DDS_KIND_WRITER:
case DDS_KIND_PUBLISHER:
case DDS_KIND_SUBSCRIBER:
case DDS_KIND_PARTICIPANT:
case DDS_KIND_COND_READ:
case DDS_KIND_COND_QUERY:
case DDS_KIND_COND_GUARD:
case DDS_KIND_WAITSET:
case DDS_KIND_DOMAIN:
case DDS_KIND_CYCLONEDDS:
break;
case DDS_KIND_DONTCARE:
abort ();
break;
}
return true;
}
#ifndef NDEBUG
static bool entity_kind_has_qos (dds_entity_kind_t kind)
{
switch (kind)
{
case DDS_KIND_READER:
case DDS_KIND_WRITER:
case DDS_KIND_PUBLISHER:
case DDS_KIND_SUBSCRIBER:
case DDS_KIND_PARTICIPANT:
return true;
case DDS_KIND_TOPIC:
case DDS_KIND_COND_READ:
case DDS_KIND_COND_QUERY:
case DDS_KIND_COND_GUARD:
case DDS_KIND_WAITSET:
case DDS_KIND_DOMAIN:
case DDS_KIND_CYCLONEDDS:
break;
case DDS_KIND_DONTCARE:
abort ();
break;
}
return false;
}
#endif
dds_entity_t dds_entity_init (dds_entity *e, dds_entity *parent, dds_entity_kind_t kind, bool implicit, dds_qos_t *qos, const dds_listener_t *listener, status_mask_t mask)
{
dds_handle_t handle;
/* CycloneDDS is at the root of the hierarchy */
assert ((kind == DDS_KIND_CYCLONEDDS) == (parent == NULL));
assert (entity_kind_has_qos (kind) == (qos != NULL));
assert (e);
e->m_kind = kind;
@ -196,7 +250,7 @@ dds_entity_t dds_entity_init (dds_entity *e, dds_entity *parent, dds_entity_kind
{
/* for topics, refc counts readers/writers, for all others, it counts children (this we can get away with
as long as topics can't have children) */
if ((handle = dds_handle_create (&e->m_hdllink, implicit, (kind != DDS_KIND_TOPIC))) <= 0)
if ((handle = dds_handle_create (&e->m_hdllink, implicit, entity_may_have_children (e))) <= 0)
return (dds_entity_t) handle;
}
@ -219,41 +273,40 @@ void dds_entity_register_child (dds_entity *parent, dds_entity *child)
dds_entity_add_ref_locked (parent);
}
static dds_entity *get_first_child (ddsrt_avl_tree_t *remaining_children, bool ignore_topics)
static dds_entity *get_next_child (ddsrt_avl_tree_t *remaining_children, uint32_t allowed_kinds, uint64_t *cursor)
{
ddsrt_avl_iter_t it;
for (dds_entity *e = ddsrt_avl_iter_first (&dds_entity_children_td, remaining_children, &it); e != NULL; e = ddsrt_avl_iter_next (&it))
for (dds_entity *e = ddsrt_avl_iter_succ (&dds_entity_children_td, remaining_children, &it, cursor); e != NULL; e = ddsrt_avl_iter_next (&it))
{
if ((!ignore_topics) || (dds_entity_kind(e) != DDS_KIND_TOPIC))
dds_entity_kind_t kind = dds_entity_kind (e);
if ((1u << (uint32_t) kind) & allowed_kinds)
return e;
}
return NULL;
}
static void delete_children(struct dds_entity *parent, bool ignore_topics)
static void delete_children (struct dds_entity *parent, uint32_t allowed_kinds)
{
dds_entity *child;
dds_return_t ret;
uint64_t cursor = 0;
ddsrt_mutex_lock (&parent->m_mutex);
while ((child = get_first_child(&parent->m_children, ignore_topics)) != NULL)
while ((child = get_next_child (&parent->m_children, allowed_kinds, &cursor)) != NULL)
{
dds_entity_t child_handle = child->m_hdllink.hdl;
cursor = child->m_iid;
/* The child will remove itself from the parent->m_children list. */
ddsrt_mutex_unlock (&parent->m_mutex);
ret = dds_delete_impl (child_handle, DIS_FROM_PARENT);
assert (ret == DDS_RETCODE_OK || ret == DDS_RETCODE_BAD_PARAMETER);
(void) ret;
ddsrt_mutex_lock (&parent->m_mutex);
/* The dds_delete can fail if the child is being deleted in parallel,
* in which case: wait when its not deleted yet.
* The child will trigger the condition after it removed itself from
* the childrens list. */
if ((ret == DDS_RETCODE_BAD_PARAMETER) &&
(get_first_child(&parent->m_children, ignore_topics) == child))
{
/* The dds_delete can fail if the child is being deleted in parallel, in which case:
wait until it is has gone. */
if (ddsrt_avl_lookup (&dds_entity_children_td, &parent->m_children, &cursor) != NULL)
ddsrt_cond_wait (&parent->m_cond, &parent->m_mutex);
}
}
ddsrt_mutex_unlock (&parent->m_mutex);
}
@ -283,12 +336,20 @@ static const char *entity_kindstr (dds_entity_kind_t kind)
static void print_delete (const dds_entity *e, enum delete_impl_state delstate , dds_instance_handle_t iid)
{
unsigned cm = ddsrt_atomic_ld32 (&e->m_hdllink.cnt_flags);
printf ("delete(%p, delstate %s, iid %"PRIx64"): %s%s %d pin %u refc %u %s %s\n",
(void *) e, (delstate == DIS_IMPLICIT) ? "implicit" : (delstate == DIS_EXPLICIT) ? "explicit" : "from_parent", iid,
entity_kindstr (e->m_kind), (e->m_flags & DDS_ENTITY_IMPLICIT) ? " [implicit]" : "",
e->m_hdllink.hdl, cm & 0xfff, (cm >> 12) & 0x7fff, (cm & 0x80000000) ? "closed" : "open",
ddsrt_avl_is_empty (&e->m_children) ? "childless" : "has-children");
if (e)
{
unsigned cm = ddsrt_atomic_ld32 (&e->m_hdllink.cnt_flags);
printf ("delete(%p, delstate %s, iid %"PRIx64"): %s%s %d pin %u refc %u %s %s\n",
(void *) e, (delstate == DIS_IMPLICIT) ? "implicit" : (delstate == DIS_EXPLICIT) ? "explicit" : "from_parent", iid,
entity_kindstr (e->m_kind), (e->m_flags & DDS_ENTITY_IMPLICIT) ? " [implicit]" : "",
e->m_hdllink.hdl, cm & 0xfff, (cm >> 12) & 0x7fff, (cm & 0x80000000) ? "closed" : "open",
ddsrt_avl_is_empty (&e->m_children) ? "childless" : "has-children");
}
else
{
printf ("delete(%p, delstate %s, handle %"PRId64"): pin failed\n",
(void *) e, (delstate == DIS_IMPLICIT) ? "implicit" : (delstate == DIS_EXPLICIT) ? "explicit" : "from_parent", iid);
}
}
#endif
@ -315,7 +376,12 @@ static dds_return_t dds_delete_impl (dds_entity_t entity, enum delete_impl_state
else if (ret == DDS_RETCODE_TRY_AGAIN) /* non-child refs exist */
return DDS_RETCODE_OK;
else
{
#if TRACE_DELETE
print_delete (NULL, delstate, (uint64_t) entity);
#endif
return ret;
}
}
dds_return_t dds_delete_impl_pinned (dds_entity *e, enum delete_impl_state delstate)
@ -392,8 +458,15 @@ static dds_return_t really_delete_pinned_closed_locked (struct dds_entity *e, en
*
* To circumvent the problem. We ignore topics in the first loop.
*/
delete_children(e, true /* ignore topics */);
delete_children(e, false /* delete topics */);
DDSRT_STATIC_ASSERT ((uint32_t) DDS_KIND_MAX < 32);
static const uint32_t disallowed_kinds[] = {
1u << (uint32_t) DDS_KIND_TOPIC,
(uint32_t) 0
};
for (size_t i = 0; i < sizeof (disallowed_kinds) / sizeof (disallowed_kinds[0]); i++)
{
delete_children (e, ~disallowed_kinds[i]);
}
/* The dds_handle_delete will wait until the last active claim on that handle is
released. It is possible that this last release will be done by a thread that was
@ -476,13 +549,20 @@ dds_entity_t dds_get_parent (dds_entity_t entity)
}
}
dds_participant *dds_entity_participant (dds_entity *e)
dds_participant *dds_entity_participant (const dds_entity *e)
{
while (e && dds_entity_kind (e) != DDS_KIND_PARTICIPANT)
e = e->m_parent;
return (dds_participant *) e;
}
const ddsi_guid_t *dds_entity_participant_guid (const dds_entity *e)
{
struct dds_participant const * const pp = dds_entity_participant (e);
assert (pp != NULL);
return &pp->m_entity.m_guid;
}
dds_entity_t dds_get_participant (dds_entity_t entity)
{
dds_entity *e;
@ -577,51 +657,124 @@ dds_return_t dds_get_qos (dds_entity_t entity, dds_qos_t *qos)
ret = DDS_RETCODE_ILLEGAL_OPERATION;
else
{
dds_qos_t *entity_qos;
if (dds_entity_kind (e) != DDS_KIND_TOPIC)
entity_qos = e->m_qos;
else
{
struct dds_topic * const tp = (dds_topic *) e;
struct dds_participant * const pp = dds_entity_participant (e);
ddsrt_mutex_lock (&pp->m_entity.m_mutex);
entity_qos = tp->m_ktopic->qos;
ddsrt_mutex_unlock (&pp->m_entity.m_mutex);
}
dds_reset_qos (qos);
nn_xqos_mergein_missing (qos, e->m_qos, ~(QP_TOPIC_NAME | QP_TYPE_NAME));
ddsi_xqos_mergein_missing (qos, entity_qos, ~(QP_TOPIC_NAME | QP_TYPE_NAME));
ret = DDS_RETCODE_OK;
}
dds_entity_unlock(e);
return ret;
}
static dds_return_t dds_set_qos_locked_impl (dds_entity *e, const dds_qos_t *qos, uint64_t mask)
static dds_return_t dds_set_qos_locked_raw (dds_entity *e, dds_qos_t **e_qos_ptr, bool e_enabled, const dds_qos_t *qos, uint64_t mask, const struct ddsrt_log_cfg *logcfg, dds_return_t (*set_qos) (struct dds_entity *e, const dds_qos_t *qos, bool enabled) ddsrt_nonnull_all)
{
dds_return_t ret;
/* Any attempt to do this on a topic ends up doing it on the ktopic instead, so that there is
but a single QoS for a topic in a participant while there can be multiple definitions of it,
and hence, multiple sertopics. Those are needed for multi-language support. */
dds_qos_t *newqos = dds_create_qos ();
nn_xqos_mergein_missing (newqos, qos, mask);
nn_xqos_mergein_missing (newqos, e->m_qos, ~(uint64_t)0);
if ((ret = nn_xqos_valid (&e->m_domain->gv.logconfig, newqos)) != DDS_RETCODE_OK)
; /* oops ... invalid or inconsistent */
else if (!(e->m_flags & DDS_ENTITY_ENABLED))
; /* do as you please while the entity is not enabled (perhaps we should even allow invalid ones?) */
ddsi_xqos_mergein_missing (newqos, qos, mask);
ddsi_xqos_mergein_missing (newqos, *e_qos_ptr, ~(uint64_t)0);
if ((ret = ddsi_xqos_valid (logcfg, newqos)) != DDS_RETCODE_OK)
{
/* invalid or inconsistent QoS settings */
goto error_or_nochange;
}
else if (!e_enabled)
{
/* do as you please while the entity is not enabled */
}
else
{
const uint64_t delta = nn_xqos_delta (e->m_qos, newqos, ~(uint64_t)0);
if (delta == 0) /* no change */
ret = DDS_RETCODE_OK;
else if (delta & ~QP_CHANGEABLE_MASK)
ret = DDS_RETCODE_IMMUTABLE_POLICY;
else if (delta & (QP_RXO_MASK | QP_PARTITION))
ret = DDS_RETCODE_UNSUPPORTED; /* not yet supporting things that affect matching */
else
const uint64_t delta = ddsi_xqos_delta (*e_qos_ptr, newqos, ~(uint64_t)0);
if (delta == 0)
{
/* yay! */
/* new settings are identical to the old */
goto error_or_nochange;
}
else if (delta & ~QP_CHANGEABLE_MASK)
{
/* not all QoS may be changed according to the spec */
ret = DDS_RETCODE_IMMUTABLE_POLICY;
goto error_or_nochange;
}
else if (delta & (QP_RXO_MASK | QP_PARTITION))
{
/* Cyclone doesn't (yet) support changing QoS that affect matching. Simply re-doing the
matching is easy enough, but the consequences are very weird. E.g., what is the
expectation if a transient-local writer has published data while its partition QoS is set
to A, and then changes its partition to B? Should a reader in B get the data originally
published in A?
One can do the same thing with other RxO QoS settings, e.g., the latency budget setting.
I find that weird, and I'd rather have sane answers to these questions than set up these
traps and pitfalls for people to walk into ...
*/
ret = DDS_RETCODE_UNSUPPORTED;
goto error_or_nochange;
}
}
if (ret != DDS_RETCODE_OK)
dds_delete_qos (newqos);
else if ((ret = dds_entity_deriver_set_qos (e, newqos, e->m_flags & DDS_ENTITY_ENABLED)) != DDS_RETCODE_OK)
dds_delete_qos (newqos);
assert (ret == DDS_RETCODE_OK);
if ((ret = set_qos (e, newqos, e_enabled)) != DDS_RETCODE_OK)
goto error_or_nochange;
else
{
dds_delete_qos (e->m_qos);
e->m_qos = newqos;
dds_delete_qos (*e_qos_ptr);
*e_qos_ptr = newqos;
}
return DDS_RETCODE_OK;
error_or_nochange:
dds_delete_qos (newqos);
return ret;
}
static dds_return_t dds_set_qos_locked_impl (dds_entity *e, const dds_qos_t *qos, uint64_t mask)
{
const struct ddsrt_log_cfg *logcfg = &e->m_domain->gv.logconfig;
dds_entity_kind_t kind = dds_entity_kind (e);
if (kind != DDS_KIND_TOPIC)
{
return dds_set_qos_locked_raw (e, &e->m_qos, (e->m_flags & DDS_ENTITY_ENABLED) != 0, qos, mask, logcfg, dds_entity_deriver_table[kind]->set_qos);
}
else
{
/* Topics must be enabled for now (all are currently, so for now it is not a meaningful limitation):
there can only be a single QoS (or different versions with the same name can have different QoS -
in particular a different value for TOPIC_DATA - and therefore the idea that it is a free-for-all
on the QoS for a disabled entity falls apart for topics.
FIXME: topic should have a QoS object while still disabled */
assert (e->m_flags & DDS_ENTITY_ENABLED);
struct dds_topic * const tp = (struct dds_topic *) e;
struct dds_participant * const pp = dds_entity_participant (e);
struct dds_ktopic * const ktp = tp->m_ktopic;
dds_return_t rc;
ddsrt_mutex_lock (&pp->m_entity.m_mutex);
while (ktp->defer_set_qos != 0)
ddsrt_cond_wait (&pp->m_entity.m_cond, &pp->m_entity.m_mutex);
/* dds_entity_deriver_table[kind]->set_qos had better avoid looking at the entity! */
rc = dds_set_qos_locked_raw (NULL, &ktp->qos, (e->m_flags & DDS_ENTITY_ENABLED) != 0, qos, mask, logcfg, dds_entity_deriver_table[kind]->set_qos);
ddsrt_mutex_unlock (&pp->m_entity.m_mutex);
return rc;
}
}
static void pushdown_pubsub_qos (dds_entity *e)
{
/* e claimed but no mutex held */
@ -650,7 +803,7 @@ static void pushdown_pubsub_qos (dds_entity *e)
ddsrt_mutex_unlock (&e->m_mutex);
}
static void pushdown_topic_qos (dds_entity *e, struct dds_entity *tp)
static void pushdown_topic_qos (dds_entity *e, struct dds_ktopic *ktp)
{
/* on input: both entities claimed but no mutexes held */
enum { NOP, PROP, CHANGE } todo;
@ -658,12 +811,12 @@ static void pushdown_topic_qos (dds_entity *e, struct dds_entity *tp)
{
case DDS_KIND_READER: {
dds_reader *rd = (dds_reader *) e;
todo = (&rd->m_topic->m_entity == tp) ? CHANGE : NOP;
todo = (rd->m_topic->m_ktopic == ktp) ? CHANGE : NOP;
break;
}
case DDS_KIND_WRITER: {
dds_writer *wr = (dds_writer *) e;
todo = (&wr->m_topic->m_entity == tp) ? CHANGE : NOP;
todo = (wr->m_topic->m_ktopic == ktp) ? CHANGE : NOP;
break;
}
default: {
@ -677,10 +830,11 @@ static void pushdown_topic_qos (dds_entity *e, struct dds_entity *tp)
break;
case CHANGE: {
/* may lock topic while holding reader/writer lock */
struct dds_participant * const pp = dds_entity_participant (e);
ddsrt_mutex_lock (&e->m_mutex);
ddsrt_mutex_lock (&tp->m_mutex);
dds_set_qos_locked_impl (e, tp->m_qos, QP_TOPIC_DATA);
ddsrt_mutex_unlock (&tp->m_mutex);
ddsrt_mutex_lock (&pp->m_entity.m_mutex);
dds_set_qos_locked_impl (e, ktp->qos, QP_TOPIC_DATA);
ddsrt_mutex_unlock (&pp->m_entity.m_mutex);
ddsrt_mutex_unlock (&e->m_mutex);
break;
}
@ -697,7 +851,7 @@ static void pushdown_topic_qos (dds_entity *e, struct dds_entity *tp)
assert (x == c);
/* see dds_get_children for why "c" remains valid despite unlocking m_mutex */
ddsrt_mutex_unlock (&e->m_mutex);
pushdown_topic_qos (c, tp);
pushdown_topic_qos (c, ktp);
ddsrt_mutex_lock (&e->m_mutex);
dds_entity_unpin (c);
}
@ -740,7 +894,8 @@ dds_return_t dds_set_qos (dds_entity_t entity, const dds_qos_t *qos)
assert (dds_entity_kind (e->m_parent) == DDS_KIND_PARTICIPANT);
if (dds_entity_pin (e->m_parent->m_hdllink.hdl, &pp) == DDS_RETCODE_OK)
{
pushdown_topic_qos (pp, e);
struct dds_topic *tp = (struct dds_topic *) e;
pushdown_topic_qos (pp, tp->m_ktopic);
dds_entity_unpin (pp);
}
break;

View file

@ -30,7 +30,7 @@
#include "dds/ddsi/q_entity.h"
#include "dds/ddsi/q_config.h"
#include "dds/ddsi/q_gc.h"
#include "dds/ddsi/q_globals.h"
#include "dds/ddsi/ddsi_domaingv.h"
#include "dds/version.h"
static void dds_close (struct dds_entity *e);

View file

@ -21,7 +21,7 @@
#include "dds/ddsi/ddsi_serdata.h"
#include "dds/ddsi/q_entity.h"
#include "dds/ddsi/q_thread.h"
#include "dds/ddsi/q_globals.h"
#include "dds/ddsi/ddsi_domaingv.h"
dds_return_t dds_writedispose (dds_entity_t writer, const void *data)
{

View file

@ -15,7 +15,7 @@
#include "dds/dds.h"
#include "dds/version.h"
#include "dds/ddsi/q_config.h"
#include "dds/ddsi/q_globals.h"
#include "dds/ddsi/ddsi_domaingv.h"
#include "dds/ddsi/q_entity.h"
#include "dds/ddsi/ddsi_entity_index.h"
#include "dds/ddsi/q_thread.h"
@ -135,7 +135,7 @@ static dds_builtintopic_endpoint_t *make_builtintopic_endpoint (const ddsi_guid_
tmp = nn_hton_guid (*ppguid);
memcpy (&ep->participant_key, &tmp, sizeof (ep->participant_key));
ep->qos = dds_create_qos ();
nn_xqos_mergein_missing (ep->qos, qos, ~(QP_TOPIC_NAME | QP_TYPE_NAME));
ddsi_xqos_mergein_missing (ep->qos, qos, ~(QP_TOPIC_NAME | QP_TYPE_NAME));
ep->topic_name = dds_string_dup (qos->topic_name);
ep->type_name = dds_string_dup (qos->type_name);
return ep;

View file

@ -10,14 +10,15 @@
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
#include <assert.h>
#include <string.h>
#include "dds/ddsrt/cdtors.h"
#include "dds/ddsrt/environ.h"
#include "dds/ddsi/q_entity.h"
#include "dds/ddsi/q_thread.h"
#include "dds/ddsi/q_config.h"
#include "dds/ddsi/q_plist.h"
#include "dds/ddsi/q_globals.h"
#include "dds/ddsi/ddsi_plist.h"
#include "dds/ddsi/ddsi_domaingv.h"
#include "dds/ddsi/ddsi_entity_index.h"
#include "dds/version.h"
#include "dds__init.h"
@ -30,6 +31,13 @@ DECL_ENTITY_LOCK_UNLOCK (extern inline, dds_participant)
#define DDS_PARTICIPANT_STATUS_MASK (0u)
static int cmp_ktopic_name (const void *a, const void *b)
{
return strcmp (a, b);
}
const ddsrt_avl_treedef_t participant_ktopics_treedef = DDSRT_AVL_TREEDEF_INITIALIZER_INDKEY(offsetof (struct dds_ktopic, pp_ktopics_avlnode), offsetof (struct dds_ktopic, name), cmp_ktopic_name, 0);
static dds_return_t dds_participant_status_validate (uint32_t mask)
{
return (mask & ~DDS_PARTICIPANT_STATUS_MASK) ? DDS_RETCODE_BAD_PARAMETER : DDS_RETCODE_OK;
@ -42,6 +50,9 @@ static dds_return_t dds_participant_delete (dds_entity *e)
dds_return_t ret;
assert (dds_entity_kind (e) == DDS_KIND_PARTICIPANT);
/* ktopics & topics are children and therefore must all have been deleted by the time we get here */
assert (ddsrt_avl_is_empty (&((struct dds_participant *) e)->m_ktopics));
thread_state_awake (lookup_thread_state (), &e->m_domain->gv);
if ((ret = delete_participant (&e->m_domain->gv, &e->m_guid)) < 0)
DDS_CERROR (&e->m_domain->gv.logconfig, "dds_participant_delete: internal error %"PRId32"\n", ret);
@ -58,8 +69,8 @@ static dds_return_t dds_participant_qos_set (dds_entity *e, const dds_qos_t *qos
thread_state_awake (lookup_thread_state (), &e->m_domain->gv);
if ((pp = entidx_lookup_participant_guid (e->m_domain->gv.entity_index, &e->m_guid)) != NULL)
{
nn_plist_t plist;
nn_plist_init_empty (&plist);
ddsi_plist_t plist;
ddsi_plist_init_empty (&plist);
plist.qos.present = plist.qos.aliased = qos->present;
plist.qos = *qos;
update_participant_plist (pp, &plist);
@ -83,7 +94,7 @@ dds_entity_t dds_create_participant (const dds_domainid_t domain, const dds_qos_
dds_entity_t ret;
ddsi_guid_t guid;
dds_participant * pp;
nn_plist_t plist;
ddsi_plist_t plist;
dds_qos_t *new_qos = NULL;
char *config = "";
@ -98,19 +109,19 @@ dds_entity_t dds_create_participant (const dds_domainid_t domain, const dds_qos_
new_qos = dds_create_qos ();
if (qos != NULL)
nn_xqos_mergein_missing (new_qos, qos, DDS_PARTICIPANT_QOS_MASK);
nn_xqos_mergein_missing (new_qos, &dom->gv.default_local_plist_pp.qos, ~(uint64_t)0);
if ((ret = nn_xqos_valid (&dom->gv.logconfig, new_qos)) < 0)
ddsi_xqos_mergein_missing (new_qos, qos, DDS_PARTICIPANT_QOS_MASK);
ddsi_xqos_mergein_missing (new_qos, &dom->gv.default_local_plist_pp.qos, ~(uint64_t)0);
if ((ret = ddsi_xqos_valid (&dom->gv.logconfig, new_qos)) < 0)
goto err_qos_validation;
/* Translate qos */
nn_plist_init_empty (&plist);
ddsi_plist_init_empty (&plist);
dds_merge_qos (&plist.qos, new_qos);
thread_state_awake (lookup_thread_state (), &dom->gv);
ret = new_participant (&guid, &dom->gv, 0, &plist);
thread_state_asleep (lookup_thread_state ());
nn_plist_fini (&plist);
ddsi_plist_fini (&plist);
if (ret < 0)
{
ret = DDS_RETCODE_ERROR;
@ -125,6 +136,7 @@ dds_entity_t dds_create_participant (const dds_domainid_t domain, const dds_qos_
pp->m_entity.m_iid = get_entity_instance_id (&dom->gv, &guid);
pp->m_entity.m_domain = dom;
pp->m_builtin_subscriber = 0;
ddsrt_avl_init (&participant_ktopics_treedef, &pp->m_ktopics);
/* Add participant to extent */
ddsrt_mutex_lock (&dom->m_entity.m_mutex);

View file

@ -15,10 +15,11 @@
#include "dds__listener.h"
#include "dds__participant.h"
#include "dds__publisher.h"
#include "dds__writer.h"
#include "dds__qos.h"
#include "dds/ddsi/ddsi_iid.h"
#include "dds/ddsi/q_entity.h"
#include "dds/ddsi/q_globals.h"
#include "dds/ddsi/ddsi_domaingv.h"
#include "dds/version.h"
DECL_ENTITY_LOCK_UNLOCK (extern inline, dds_publisher)
@ -54,9 +55,9 @@ dds_entity_t dds__create_publisher_l (dds_participant *par, bool implicit, const
new_qos = dds_create_qos ();
if (qos)
nn_xqos_mergein_missing (new_qos, qos, DDS_PUBLISHER_QOS_MASK);
nn_xqos_mergein_missing (new_qos, &par->m_entity.m_domain->gv.default_xqos_pub, ~(uint64_t)0);
if ((ret = nn_xqos_valid (&par->m_entity.m_domain->gv.logconfig, new_qos)) != DDS_RETCODE_OK)
ddsi_xqos_mergein_missing (new_qos, qos, DDS_PUBLISHER_QOS_MASK);
ddsi_xqos_mergein_missing (new_qos, &par->m_entity.m_domain->gv.default_xqos_pub, ~(uint64_t)0);
if ((ret = ddsi_xqos_valid (&par->m_entity.m_domain->gv.logconfig, new_qos)) != DDS_RETCODE_OK)
{
dds_participant_unlock (par);
return ret;
@ -94,10 +95,33 @@ dds_return_t dds_resume (dds_entity_t publisher)
dds_return_t dds_wait_for_acks (dds_entity_t publisher_or_writer, dds_duration_t timeout)
{
dds_return_t ret;
dds_entity *p_or_w_ent;
if (timeout < 0)
return DDS_RETCODE_BAD_PARAMETER;
static const dds_entity_kind_t kinds[] = { DDS_KIND_WRITER, DDS_KIND_PUBLISHER };
return dds_generic_unimplemented_operation_manykinds (publisher_or_writer, sizeof (kinds) / sizeof (kinds[0]), kinds);
if ((ret = dds_entity_pin (publisher_or_writer, &p_or_w_ent)) < 0)
return ret;
const dds_time_t tnow = dds_time ();
const dds_time_t abstimeout = (DDS_INFINITY - timeout <= tnow) ? DDS_NEVER : (tnow + timeout);
switch (dds_entity_kind (p_or_w_ent))
{
case DDS_KIND_PUBLISHER:
/* FIXME: wait_for_acks on all writers of the same publisher */
dds_entity_unpin (p_or_w_ent);
return DDS_RETCODE_UNSUPPORTED;
case DDS_KIND_WRITER:
ret = dds__writer_wait_for_acks ((struct dds_writer *) p_or_w_ent, abstimeout);
dds_entity_unpin (p_or_w_ent);
return ret;
default:
dds_entity_unpin (p_or_w_ent);
return DDS_RETCODE_ILLEGAL_OPERATION;
}
}
dds_return_t dds_publisher_begin_coherent (dds_entity_t publisher)

View file

@ -15,7 +15,7 @@
#include "dds/dds.h"
#include "dds/ddsrt/heap.h"
#include "dds/ddsrt/string.h"
#include "dds/ddsi/q_plist.h"
#include "dds/ddsi/ddsi_plist.h"
static void dds_qos_data_copy_in (ddsi_octetseq_t *data, const void * __restrict value, size_t sz, bool overwrite)
{
@ -51,7 +51,7 @@ static bool dds_qos_data_copy_out (const ddsi_octetseq_t *data, void **value, si
dds_qos_t *dds_create_qos (void)
{
dds_qos_t *qos = ddsrt_malloc (sizeof (dds_qos_t));
nn_xqos_init_empty (qos);
ddsi_xqos_init_empty (qos);
return qos;
}
@ -64,8 +64,8 @@ void dds_reset_qos (dds_qos_t * __restrict qos)
{
if (qos)
{
nn_xqos_fini (qos);
nn_xqos_init_empty (qos);
ddsi_xqos_fini (qos);
ddsi_xqos_init_empty (qos);
}
}
@ -78,7 +78,7 @@ void dds_delete_qos (dds_qos_t * __restrict qos)
{
if (qos)
{
nn_xqos_fini (qos);
ddsi_xqos_fini (qos);
ddsrt_free (qos);
}
}
@ -92,7 +92,7 @@ dds_return_t dds_copy_qos (dds_qos_t * __restrict dst, const dds_qos_t * __restr
{
if (src == NULL || dst == NULL)
return DDS_RETCODE_BAD_PARAMETER;
nn_xqos_copy (dst, src);
ddsi_xqos_copy (dst, src);
return DDS_RETCODE_OK;
}
@ -105,7 +105,7 @@ void dds_merge_qos (dds_qos_t * __restrict dst, const dds_qos_t * __restrict src
{
/* Copy qos from source to destination unless already set */
if (src != NULL && dst != NULL)
nn_xqos_mergein_missing (dst, src, ~(uint64_t)0);
ddsi_xqos_mergein_missing (dst, src, ~(uint64_t)0);
}
void dds_qos_merge (dds_qos_t * __restrict dst, const dds_qos_t * __restrict src)
@ -121,7 +121,7 @@ bool dds_qos_equal (const dds_qos_t * __restrict a, const dds_qos_t * __restrict
else if (a == NULL || b == NULL)
return false;
else
return nn_xqos_delta (a, b, ~(uint64_t)0) == 0;
return ddsi_xqos_delta (a, b, ~(uint64_t)0) == 0;
}
void dds_qset_userdata (dds_qos_t * __restrict qos, const void * __restrict value, size_t sz)

View file

@ -18,7 +18,7 @@
#include "dds/ddsi/q_thread.h"
#include "dds/ddsi/ddsi_entity_index.h"
#include "dds/ddsi/q_entity.h"
#include "dds/ddsi/q_globals.h"
#include "dds/ddsi/ddsi_domaingv.h"
#include "dds/ddsi/ddsi_sertopic.h"
/*
@ -283,7 +283,7 @@ dds_return_t dds_read_instance_mask (dds_entity_t rd_or_cnd, void **buf, dds_sam
{
lock = false;
/* FIXME: Fix the interface. */
maxs = 100;
maxs = (uint32_t)bufsz;
}
return dds_read_impl (false, rd_or_cnd, buf, bufsz, maxs, si, mask, handle, lock, false);
}
@ -422,7 +422,7 @@ dds_return_t dds_take_instance_mask (dds_entity_t rd_or_cnd, void **buf, dds_sam
{
lock = false;
/* FIXME: Fix the interface. */
maxs = 100;
maxs = (uint32_t)bufsz;
}
return dds_read_impl(true, rd_or_cnd, buf, bufsz, maxs, si, mask, handle, lock, false);
}

View file

@ -14,6 +14,7 @@
#include "dds/dds.h"
#include "dds/version.h"
#include "dds/ddsrt/static_assert.h"
#include "dds__participant.h"
#include "dds__subscriber.h"
#include "dds__reader.h"
#include "dds__listener.h"
@ -25,7 +26,7 @@
#include "dds__qos.h"
#include "dds/ddsi/q_entity.h"
#include "dds/ddsi/q_thread.h"
#include "dds/ddsi/q_globals.h"
#include "dds/ddsi/ddsi_domaingv.h"
#include "dds__builtin.h"
#include "dds/ddsi/ddsi_sertopic.h"
#include "dds/ddsi/ddsi_entity_index.h"
@ -71,9 +72,23 @@ static dds_return_t dds_reader_delete (dds_entity *e)
return DDS_RETCODE_OK;
}
static dds_return_t validate_reader_qos (const dds_qos_t *rqos)
{
#ifndef DDSI_INCLUDE_DEADLINE_MISSED
if (rqos != NULL && (rqos->present & QP_DEADLINE) && rqos->deadline.deadline != DDS_INFINITY)
return DDS_RETCODE_BAD_PARAMETER;
#else
DDSRT_UNUSED_ARG (rqos);
#endif
return DDS_RETCODE_OK;
}
static dds_return_t dds_reader_qos_set (dds_entity *e, const dds_qos_t *qos, bool enabled)
{
/* note: e->m_qos is still the old one to allow for failure here */
dds_return_t ret;
if ((ret = validate_reader_qos(qos)) != DDS_RETCODE_OK)
return ret;
if (enabled)
{
struct reader *rd;
@ -240,9 +255,8 @@ void dds_reader_status_cb (void *ventity, const status_cb_data_t *data)
LIVELINESS_CHANGED_REMOVE_NOT_ALIVE < LIVELINESS_CHANGED_REMOVE_ALIVE &&
LIVELINESS_CHANGED_REMOVE_ALIVE < LIVELINESS_CHANGED_ALIVE_TO_NOT_ALIVE &&
LIVELINESS_CHANGED_ALIVE_TO_NOT_ALIVE < LIVELINESS_CHANGED_NOT_ALIVE_TO_ALIVE &&
LIVELINESS_CHANGED_NOT_ALIVE_TO_ALIVE < LIVELINESS_CHANGED_TWITCH &&
(uint32_t) LIVELINESS_CHANGED_TWITCH < UINT32_MAX);
assert (data->extra <= (uint32_t) LIVELINESS_CHANGED_TWITCH);
(uint32_t) LIVELINESS_CHANGED_NOT_ALIVE_TO_ALIVE < UINT32_MAX);
assert (data->extra <= (uint32_t) LIVELINESS_CHANGED_NOT_ALIVE_TO_ALIVE);
switch ((enum liveliness_changed_data_extra) data->extra)
{
case LIVELINESS_CHANGED_ADD_ALIVE:
@ -273,8 +287,6 @@ void dds_reader_status_cb (void *ventity, const status_cb_data_t *data)
st->alive_count++;
st->alive_count_change++;
break;
case LIVELINESS_CHANGED_TWITCH:
break;
}
st->last_publication_handle = data->handle;
invoke = (lst->on_liveliness_changed != 0);
@ -348,32 +360,34 @@ static dds_entity_t dds_create_reader_int (dds_entity_t participant_or_subscribe
{
dds_qos_t *rqos;
dds_subscriber *sub = NULL;
dds_participant *pp;
dds_entity_t subscriber;
dds_reader *rd;
dds_topic *tp;
dds_entity_t reader;
dds_entity_t t;
dds_return_t ret = DDS_RETCODE_OK;
bool internal_topic;
dds_return_t rc;
dds_entity_t pseudo_topic = 0;
bool created_implicit_sub = false;
switch (topic)
{
case DDS_BUILTIN_TOPIC_DCPSPARTICIPANT:
case DDS_BUILTIN_TOPIC_DCPSTOPIC:
/* not implemented yet */
return DDS_RETCODE_BAD_PARAMETER;
case DDS_BUILTIN_TOPIC_DCPSPARTICIPANT:
case DDS_BUILTIN_TOPIC_DCPSPUBLICATION:
case DDS_BUILTIN_TOPIC_DCPSSUBSCRIPTION:
internal_topic = true;
subscriber = dds__get_builtin_subscriber (participant_or_subscriber);
if ((ret = dds_subscriber_lock (subscriber, &sub)) != DDS_RETCODE_OK)
return ret;
t = dds__get_builtin_topic (subscriber, topic);
/* translate provided pseudo-topic to a real one */
pseudo_topic = topic;
if ((subscriber = dds__get_builtin_subscriber (participant_or_subscriber)) < 0)
return subscriber;
if ((rc = dds_subscriber_lock (subscriber, &sub)) != DDS_RETCODE_OK)
return rc;
topic = dds__get_builtin_topic (subscriber, topic);
break;
default: {
dds_entity *p_or_s;
if ((ret = dds_entity_lock (participant_or_subscriber, DDS_KIND_DONTCARE, &p_or_s)) != DDS_RETCODE_OK)
return ret;
if ((rc = dds_entity_lock (participant_or_subscriber, DDS_KIND_DONTCARE, &p_or_s)) != DDS_RETCODE_OK)
return rc;
switch (dds_entity_kind (p_or_s))
{
case DDS_KIND_SUBSCRIBER:
@ -381,65 +395,70 @@ static dds_entity_t dds_create_reader_int (dds_entity_t participant_or_subscribe
sub = (dds_subscriber *) p_or_s;
break;
case DDS_KIND_PARTICIPANT:
created_implicit_sub = true;
subscriber = dds__create_subscriber_l ((dds_participant *) p_or_s, true, qos, NULL);
dds_entity_unlock (p_or_s);
if ((ret = dds_subscriber_lock (subscriber, &sub)) < 0)
return ret;
if ((rc = dds_subscriber_lock (subscriber, &sub)) < 0)
return rc;
break;
default:
dds_entity_unlock (p_or_s);
return DDS_RETCODE_ILLEGAL_OPERATION;
}
internal_topic = false;
t = topic;
break;
}
}
if ((ret = dds_topic_lock (t, &tp)) != DDS_RETCODE_OK)
{
reader = ret;
goto err_tp_lock;
}
if ((rc = dds_topic_pin (topic, &tp)) < 0)
goto err_pin_topic;
assert (tp->m_stopic);
pp = dds_entity_participant (&sub->m_entity);
if (pp != dds_entity_participant (&tp->m_entity))
if (dds_entity_participant (&sub->m_entity) != dds_entity_participant (&tp->m_entity))
{
reader = DDS_RETCODE_BAD_PARAMETER;
rc = DDS_RETCODE_BAD_PARAMETER;
goto err_pp_mismatch;
}
/* Prevent set_qos on the topic until reader has been created and registered: we can't
allow a TOPIC_DATA change to ccur before the reader has been created because that
change would then not be published in the discovery/built-in topics.
Don't keep the participant (which protects the topic's QoS) locked because that
can cause deadlocks for applications creating a reader/writer from within a
subscription matched listener (whether the restrictions on what one can do in
listeners are reasonable or not, it used to work so it can be broken arbitrarily). */
dds_topic_defer_set_qos (tp);
/* Merge qos from topic and subscriber, dds_copy_qos only fails when it is passed a null
argument, but that isn't the case here */
rqos = dds_create_qos ();
if (qos)
nn_xqos_mergein_missing (rqos, qos, DDS_READER_QOS_MASK);
ddsi_xqos_mergein_missing (rqos, qos, DDS_READER_QOS_MASK);
if (sub->m_entity.m_qos)
nn_xqos_mergein_missing (rqos, sub->m_entity.m_qos, ~(uint64_t)0);
if (tp->m_entity.m_qos)
nn_xqos_mergein_missing (rqos, tp->m_entity.m_qos, ~(uint64_t)0);
nn_xqos_mergein_missing (rqos, &sub->m_entity.m_domain->gv.default_xqos_rd, ~(uint64_t)0);
ddsi_xqos_mergein_missing (rqos, sub->m_entity.m_qos, ~(uint64_t)0);
if (tp->m_ktopic->qos)
ddsi_xqos_mergein_missing (rqos, tp->m_ktopic->qos, ~(uint64_t)0);
ddsi_xqos_mergein_missing (rqos, &sub->m_entity.m_domain->gv.default_xqos_rd, ~(uint64_t)0);
if ((ret = nn_xqos_valid (&sub->m_entity.m_domain->gv.logconfig, rqos)) != DDS_RETCODE_OK)
if ((rc = ddsi_xqos_valid (&sub->m_entity.m_domain->gv.logconfig, rqos)) < 0 ||
(rc = validate_reader_qos(rqos)) != DDS_RETCODE_OK)
{
dds_delete_qos (rqos);
reader = ret;
goto err_bad_qos;
}
/* Additional checks required for built-in topics: we don't want to
run into a resource limit on a built-in topic, it is a needless
complication */
if (internal_topic && !dds__validate_builtin_reader_qos (tp->m_entity.m_domain, topic, rqos))
if (pseudo_topic && !dds__validate_builtin_reader_qos (tp->m_entity.m_domain, pseudo_topic, rqos))
{
dds_delete_qos (rqos);
reader = DDS_RETCODE_INCONSISTENT_POLICY;
rc = DDS_RETCODE_INCONSISTENT_POLICY;
goto err_bad_qos;
}
/* Create reader and associated read cache (if not provided by caller) */
rd = dds_alloc (sizeof (*rd));
reader = dds_entity_init (&rd->m_entity, &sub->m_entity, DDS_KIND_READER, false, rqos, listener, DDS_READER_STATUS_MASK);
struct dds_reader * const rd = dds_alloc (sizeof (*rd));
const dds_entity_t reader = dds_entity_init (&rd->m_entity, &sub->m_entity, DDS_KIND_READER, false, rqos, listener, DDS_READER_STATUS_MASK);
rd->m_sample_rejected_status.last_reason = DDS_NOT_REJECTED;
rd->m_topic = tp;
rd->m_rhc = rhc ? rhc : dds_rhc_default_new (rd, tp->m_stopic);
@ -456,25 +475,27 @@ static dds_entity_t dds_create_reader_int (dds_entity_t participant_or_subscribe
dds_entity_init_complete (&rd->m_entity);
thread_state_awake (lookup_thread_state (), &sub->m_entity.m_domain->gv);
ret = new_reader (&rd->m_rd, &rd->m_entity.m_domain->gv, &rd->m_entity.m_guid, NULL, &pp->m_entity.m_guid, tp->m_stopic, rqos, &rd->m_rhc->common.rhc, dds_reader_status_cb, rd);
assert (ret == DDS_RETCODE_OK); /* FIXME: can be out-of-resources at the very least */
rc = new_reader (&rd->m_rd, &rd->m_entity.m_domain->gv, &rd->m_entity.m_guid, NULL, dds_entity_participant_guid (&sub->m_entity), tp->m_stopic, rqos, &rd->m_rhc->common.rhc, dds_reader_status_cb, rd);
assert (rc == DDS_RETCODE_OK); /* FIXME: can be out-of-resources at the very least */
thread_state_asleep (lookup_thread_state ());
rd->m_entity.m_iid = get_entity_instance_id (&rd->m_entity.m_domain->gv, &rd->m_entity.m_guid);
dds_entity_register_child (&sub->m_entity, &rd->m_entity);
dds_topic_unlock (tp);
dds_topic_allow_set_qos (tp);
dds_topic_unpin (tp);
dds_subscriber_unlock (sub);
return reader;
err_bad_qos:
dds_topic_allow_set_qos (tp);
err_pp_mismatch:
dds_topic_unlock (tp);
err_tp_lock:
dds_topic_unpin (tp);
err_pin_topic:
dds_subscriber_unlock (sub);
if ((sub->m_entity.m_flags & DDS_ENTITY_IMPLICIT) != 0)
if (created_implicit_sub)
(void) dds_delete (subscriber);
return reader;
return rc;
}
void dds_reader_ddsi2direct (dds_entity_t entity, ddsi2direct_directread_cb_t cb, void *cbarg)

View file

@ -30,11 +30,12 @@
#include "dds/ddsi/ddsi_tkmap.h"
#include "dds/ddsrt/hopscotch.h"
#include "dds/ddsrt/avl.h"
#include "dds/ddsrt/circlist.h"
#include "dds/ddsi/ddsi_rhc.h"
#include "dds/ddsi/q_xqos.h"
#include "dds/ddsi/ddsi_xqos.h"
#include "dds/ddsi/q_unused.h"
#include "dds/ddsi/q_config.h"
#include "dds/ddsi/q_globals.h"
#include "dds/ddsi/ddsi_domaingv.h"
#include "dds/ddsi/q_radmin.h" /* sampleinfo */
#include "dds/ddsi/q_entity.h" /* proxy_writer_info */
#include "dds/ddsi/ddsi_serdata.h"
@ -42,6 +43,9 @@
#ifdef DDSI_INCLUDE_LIFESPAN
#include "dds/ddsi/ddsi_lifespan.h"
#endif
#ifdef DDSI_INCLUDE_DEADLINE_MISSED
#include "dds/ddsi/ddsi_deadline.h"
#endif
#include "dds/ddsi/sysdeps.h"
/* INSTANCE MANAGEMENT
@ -269,11 +273,13 @@ struct rhc_instance {
uint32_t disposed_gen; /* bloody generation counters - worst invention of mankind */
uint32_t no_writers_gen; /* __/ */
int32_t strength; /* "current" ownership strength */
ddsi_guid_t wr_guid; /* guid of last writer (if wr_iid != 0 then wr_guid is the corresponding guid, else undef) */
ddsi_guid_t wr_guid; /* guid of last writer (if wr_iid != 0 then wr_guid is the corresponding guid, else undef) */
nn_wctime_t tstamp; /* source time stamp of last update */
struct rhc_instance *next; /* next non-empty instance in arbitrary ordering */
struct rhc_instance *prev;
struct ddsi_tkmap_instance *tk; /* backref into TK for unref'ing */
struct ddsrt_circlist_elem nonempty_list; /* links non-empty instances in arbitrary ordering */
#ifdef DDSI_INCLUDE_DEADLINE_MISSED
struct deadline_elem deadline; /* element in deadline missed administration */
#endif
struct ddsi_tkmap_instance *tk;/* backref into TK for unref'ing */
struct rhc_sample a_sample; /* pre-allocated storage for 1 sample */
};
@ -286,7 +292,7 @@ typedef enum rhc_store_result {
struct dds_rhc_default {
struct dds_rhc common;
struct ddsrt_hh *instances;
struct rhc_instance *nonempty_instances; /* circular, points to most recently added one, NULL if none */
struct ddsrt_circlist nonempty_instances; /* circular, points to most recently added one, NULL if none */
struct lwregs registrations; /* should be a global one (with lock-free lookups) */
/* Instance/Sample maximums from resource limits QoS */
@ -312,7 +318,7 @@ struct dds_rhc_default {
dds_reader *reader; /* reader -- may be NULL (used by rhc_torture) */
struct ddsi_tkmap *tkmap; /* back pointer to tkmap */
struct q_globals *gv; /* globals -- so far only for log config */
struct ddsi_domaingv *gv; /* globals -- so far only for log config */
const struct ddsi_sertopic *topic; /* topic description */
uint32_t history_depth; /* depth, 1 for KEEP_LAST_1, 2**32-1 for KEEP_ALL */
@ -325,6 +331,9 @@ struct dds_rhc_default {
#ifdef DDSI_INCLUDE_LIFESPAN
struct lifespan_adm lifespan; /* Lifespan administration */
#endif
#ifdef DDSI_INCLUDE_DEADLINE_MISSED
struct deadline_adm deadline; /* Deadline missed administration */
#endif
};
struct trigger_info_cmn {
@ -494,57 +503,33 @@ static int instance_iid_eq (const void *va, const void *vb)
static void add_inst_to_nonempty_list (struct dds_rhc_default *rhc, struct rhc_instance *inst)
{
if (rhc->nonempty_instances == NULL)
{
inst->next = inst->prev = inst;
}
else
{
struct rhc_instance * const hd = rhc->nonempty_instances;
#ifndef NDEBUG
{
const struct rhc_instance *x = hd;
do { assert (x != inst); x = x->next; } while (x != hd);
}
#endif
inst->next = hd->next;
inst->prev = hd;
hd->next = inst;
inst->next->prev = inst;
}
rhc->nonempty_instances = inst;
ddsrt_circlist_append (&rhc->nonempty_instances, &inst->nonempty_list);
rhc->n_nonempty_instances++;
}
static void remove_inst_from_nonempty_list (struct dds_rhc_default *rhc, struct rhc_instance *inst)
{
assert (inst_is_empty (inst));
#ifndef NDEBUG
{
const struct rhc_instance *x = rhc->nonempty_instances;
assert (x);
do { if (x == inst) break; x = x->next; } while (x != rhc->nonempty_instances);
assert (x == inst);
}
#endif
if (inst->next == inst)
{
rhc->nonempty_instances = NULL;
}
else
{
struct rhc_instance * const inst_prev = inst->prev;
struct rhc_instance * const inst_next = inst->next;
inst_prev->next = inst_next;
inst_next->prev = inst_prev;
if (rhc->nonempty_instances == inst)
rhc->nonempty_instances = inst_prev;
}
ddsrt_circlist_remove (&rhc->nonempty_instances, &inst->nonempty_list);
assert (rhc->n_nonempty_instances > 0);
rhc->n_nonempty_instances--;
}
static struct rhc_instance *oldest_nonempty_instance (const struct dds_rhc_default *rhc)
{
return DDSRT_FROM_CIRCLIST (struct rhc_instance, nonempty_list, ddsrt_circlist_oldest (&rhc->nonempty_instances));
}
static struct rhc_instance *latest_nonempty_instance (const struct dds_rhc_default *rhc)
{
return DDSRT_FROM_CIRCLIST (struct rhc_instance, nonempty_list, ddsrt_circlist_latest (&rhc->nonempty_instances));
}
static struct rhc_instance *next_nonempty_instance (const struct rhc_instance *inst)
{
return DDSRT_FROM_CIRCLIST (struct rhc_instance, nonempty_list, inst->nonempty_list.next);
}
#ifdef DDSI_INCLUDE_LIFESPAN
static void drop_expired_samples (struct dds_rhc_default *rhc, struct rhc_sample *sample)
{
@ -622,7 +607,37 @@ nn_mtime_t dds_rhc_default_sample_expired_cb(void *hc, nn_mtime_t tnow)
}
#endif /* DDSI_INCLUDE_LIFESPAN */
struct dds_rhc *dds_rhc_default_new_xchecks (dds_reader *reader, struct q_globals *gv, const struct ddsi_sertopic *topic, bool xchecks)
#ifdef DDSI_INCLUDE_DEADLINE_MISSED
nn_mtime_t dds_rhc_default_deadline_missed_cb(void *hc, nn_mtime_t tnow)
{
struct dds_rhc_default *rhc = hc;
void *vinst;
nn_mtime_t tnext;
ddsrt_mutex_lock (&rhc->lock);
while ((tnext = deadline_next_missed_locked (&rhc->deadline, tnow, &vinst)).v == 0)
{
struct rhc_instance *inst = vinst;
deadline_reregister_instance_locked (&rhc->deadline, &inst->deadline, tnow);
inst->wr_iid_islive = 0;
status_cb_data_t cb_data;
cb_data.raw_status_id = (int) DDS_REQUESTED_DEADLINE_MISSED_STATUS_ID;
cb_data.extra = 0;
cb_data.handle = inst->iid;
cb_data.add = true;
ddsrt_mutex_unlock (&rhc->lock);
dds_reader_status_cb (&rhc->reader->m_entity, &cb_data);
ddsrt_mutex_lock (&rhc->lock);
tnow = now_mt ();
}
ddsrt_mutex_unlock (&rhc->lock);
return tnext;
}
#endif /* DDSI_INCLUDE_DEADLINE_MISSED */
struct dds_rhc *dds_rhc_default_new_xchecks (dds_reader *reader, struct ddsi_domaingv *gv, const struct ddsi_sertopic *topic, bool xchecks)
{
struct dds_rhc_default *rhc = ddsrt_malloc (sizeof (*rhc));
memset (rhc, 0, sizeof (*rhc));
@ -631,6 +646,7 @@ struct dds_rhc *dds_rhc_default_new_xchecks (dds_reader *reader, struct q_global
lwregs_init (&rhc->registrations);
ddsrt_mutex_init (&rhc->lock);
rhc->instances = ddsrt_hh_new (1, instance_iid_hash, instance_iid_eq);
ddsrt_circlist_init (&rhc->nonempty_instances);
rhc->topic = topic;
rhc->reader = reader;
rhc->tkmap = gv->m_tkmap;
@ -641,6 +657,11 @@ struct dds_rhc *dds_rhc_default_new_xchecks (dds_reader *reader, struct q_global
lifespan_init (gv, &rhc->lifespan, offsetof(struct dds_rhc_default, lifespan), offsetof(struct rhc_sample, lifespan), dds_rhc_default_sample_expired_cb);
#endif
#ifdef DDSI_INCLUDE_DEADLINE_MISSED
rhc->deadline.dur = (reader != NULL) ? reader->m_entity.m_qos->deadline.deadline : DDS_INFINITY;
deadline_init (gv, &rhc->deadline, offsetof(struct dds_rhc_default, deadline), offsetof(struct rhc_instance, deadline), dds_rhc_default_deadline_missed_cb);
#endif
return &rhc->common;
}
@ -661,6 +682,8 @@ static void dds_rhc_default_set_qos (struct dds_rhc_default * rhc, const dds_qos
rhc->reliable = (qos->reliability.kind == DDS_RELIABILITY_RELIABLE);
assert(qos->history.kind != DDS_HISTORY_KEEP_LAST || qos->history.depth > 0);
rhc->history_depth = (qos->history.kind == DDS_HISTORY_KEEP_LAST) ? (uint32_t)qos->history.depth : ~0u;
/* FIXME: updating deadline duration not yet supported
rhc->deadline.dur = qos->deadline.deadline; */
}
static bool eval_predicate_sample (const struct dds_rhc_default *rhc, const struct ddsi_serdata *sample, bool (*pred) (const void *sample))
@ -758,6 +781,10 @@ 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)
deadline_unregister_instance_locked (&rhc->deadline, &inst->deadline);
#endif
ddsrt_free (inst);
}
@ -812,9 +839,15 @@ static void dds_rhc_default_free (struct dds_rhc_default *rhc)
#ifdef DDSI_INCLUDE_LIFESPAN
dds_rhc_default_sample_expired_cb (rhc, NN_MTIME_NEVER);
lifespan_fini (&rhc->lifespan);
#endif
#ifdef DDSI_INCLUDE_DEADLINE_MISSED
deadline_stop (&rhc->deadline);
#endif
ddsrt_hh_enum (rhc->instances, free_instance_rhc_free_wrap, rhc);
assert (rhc->nonempty_instances == NULL);
assert (ddsrt_circlist_isempty (&rhc->nonempty_instances));
#ifdef DDSI_INCLUDE_DEADLINE_MISSED
deadline_fini (&rhc->deadline);
#endif
ddsrt_hh_free (rhc->instances);
lwregs_fini (&rhc->registrations);
if (rhc->qcond_eval_samplebuf != NULL)
@ -886,7 +919,6 @@ static bool add_sample (struct dds_rhc_default *rhc, struct rhc_instance *inst,
{
/* replace oldest sample; latest points to the latest one, the
list is circular from old -> new, so latest->next is the oldest */
inst_clear_invsample_if_exists (rhc, inst, trig_qc);
assert (inst->latest != NULL);
s = inst->latest->next;
@ -908,7 +940,6 @@ static bool add_sample (struct dds_rhc_default *rhc, struct rhc_instance *inst,
else
{
/* Check if resource max_samples QoS exceeded */
if (rhc->reader && rhc->max_samples != DDS_LENGTH_UNLIMITED && rhc->n_vsamples >= (uint32_t) rhc->max_samples)
{
cb_data->raw_status_id = (int) DDS_SAMPLE_REJECTED_STATUS_ID;
@ -919,7 +950,6 @@ static bool add_sample (struct dds_rhc_default *rhc, struct rhc_instance *inst,
}
/* Check if resource max_samples_per_instance QoS exceeded */
if (rhc->reader && rhc->max_samples_per_instance != DDS_LENGTH_UNLIMITED && inst->nvsamples >= (uint32_t) rhc->max_samples_per_instance)
{
cb_data->raw_status_id = (int) DDS_SAMPLE_REJECTED_STATUS_ID;
@ -930,7 +960,6 @@ static bool add_sample (struct dds_rhc_default *rhc, struct rhc_instance *inst,
}
/* add new latest sample */
s = alloc_sample (inst);
inst_clear_invsample_if_exists (rhc, inst, trig_qc);
if (inst->latest == NULL)
@ -956,6 +985,11 @@ 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)
@ -1317,7 +1351,7 @@ static bool dds_rhc_unregister (struct dds_rhc_default *rhc, struct rhc_instance
return notify_data_available;
}
static struct rhc_instance *alloc_new_instance (const struct dds_rhc_default *rhc, const struct ddsi_writer_info *wrinfo, struct ddsi_serdata *serdata, struct ddsi_tkmap_instance *tk)
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)
{
struct rhc_instance *inst;
@ -1348,6 +1382,11 @@ static struct rhc_instance *alloc_new_instance (const struct dds_rhc_default *rh
}
}
#ifdef DDSI_INCLUDE_DEADLINE_MISSED
if (!inst->isdisposed)
deadline_register_instance_locked (&rhc->deadline, &inst->deadline, now_mt ());
#endif
return inst;
}
@ -1508,8 +1547,6 @@ static bool dds_rhc_default_store (struct dds_rhc_default * __restrict rhc, cons
cb_data.handle = 0;
cb_data.add = true;
goto error_or_nochange;
/* FIXME: deadline (and other) QoS? */
}
else
{
@ -1559,11 +1596,19 @@ static bool dds_rhc_default_store (struct dds_rhc_default * __restrict rhc, 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, now_mt ());
#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);
}
@ -1579,9 +1624,24 @@ static bool dds_rhc_default_store (struct dds_rhc_default * __restrict rhc, cons
/* 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;
inst->isdisposed = old_isdisposed;
if (old_isdisposed)
{
inst->disposed_gen--;
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, now_mt ());
#endif
}
goto error_or_nochange;
}
notify_data_available = true;
@ -1718,6 +1778,9 @@ static void dds_rhc_default_unregister_wr (struct dds_rhc_default * __restrict r
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)
@ -1966,10 +2029,10 @@ static int dds_rhc_read_w_qminv (struct dds_rhc_default *rhc, bool lock, void **
rhc->n_not_alive_no_writers, rhc->n_new, rhc->n_vsamples, rhc->n_invsamples,
rhc->n_vread, rhc->n_invread);
if (rhc->nonempty_instances)
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 = rhc->nonempty_instances->next;
struct rhc_instance * inst = oldest_nonempty_instance (rhc);
struct rhc_instance * const end = inst;
do
{
@ -2055,7 +2118,7 @@ static int dds_rhc_read_w_qminv (struct dds_rhc_default *rhc, bool lock, void **
break;
}
}
inst = inst->next;
inst = next_nonempty_instance (inst);
}
while (inst != end && n < max_samples);
}
@ -2083,14 +2146,14 @@ static int dds_rhc_take_w_qminv (struct dds_rhc_default *rhc, bool lock, void **
rhc->n_not_alive_no_writers, rhc->n_new, rhc->n_vsamples,
rhc->n_invsamples, rhc->n_vread, rhc->n_invread);
if (rhc->nonempty_instances)
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 = rhc->nonempty_instances->next;
struct rhc_instance *inst = oldest_nonempty_instance (rhc);
unsigned n_insts = rhc->n_nonempty_instances;
while (n_insts-- > 0 && n < max_samples)
{
struct rhc_instance * const inst1 = inst->next;
struct rhc_instance * const inst1 = next_nonempty_instance (inst);
iid = inst->iid;
if (handle == DDS_HANDLE_NIL || iid == handle)
{
@ -2238,14 +2301,14 @@ static int dds_rhc_takecdr_w_qminv (struct dds_rhc_default *rhc, bool lock, stru
rhc->n_not_alive_no_writers, rhc->n_new, rhc->n_vsamples,
rhc->n_invsamples, rhc->n_vread, rhc->n_invread);
if (rhc->nonempty_instances)
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 = rhc->nonempty_instances->next;
struct rhc_instance *inst = oldest_nonempty_instance (rhc);
unsigned n_insts = rhc->n_nonempty_instances;
while (n_insts-- > 0 && n < max_samples)
{
struct rhc_instance * const inst1 = inst->next;
struct rhc_instance * const inst1 = next_nonempty_instance (inst);
iid = inst->iid;
if (handle == DDS_HANDLE_NIL || iid == handle)
{
@ -2454,13 +2517,14 @@ static bool dds_rhc_default_add_readcondition (struct dds_rhc_default *rhc, dds_
{
/* Read condition is not cached inside the instances and samples, so it only needs
to be evaluated on the non-empty instances */
if (rhc->nonempty_instances)
if (!ddsrt_circlist_isempty (&rhc->nonempty_instances))
{
struct rhc_instance *inst = rhc->nonempty_instances;
struct rhc_instance *inst = latest_nonempty_instance (rhc);
struct rhc_instance const * const end = inst;
do {
trigger += rhc_get_cond_trigger (inst, cond);
inst = inst->next;
} while (inst != rhc->nonempty_instances);
inst = next_nonempty_instance (inst);
} while (inst != end);
}
}
else
@ -2904,26 +2968,25 @@ static int rhc_check_counts_locked (struct dds_rhc_default *rhc, bool check_cond
{
for (i = 0, rciter = rhc->conds; i < ncheck; i++, rciter = rciter->m_next)
assert (cond_match_count[i] == ddsrt_atomic_ld32 (&rciter->m_entity.m_status.m_trigger));
}
}
if (rhc->n_nonempty_instances == 0)
{
assert (rhc->nonempty_instances == NULL);
assert (ddsrt_circlist_isempty (&rhc->nonempty_instances));
}
else
{
struct rhc_instance *prev, *end;
assert (rhc->nonempty_instances != NULL);
prev = rhc->nonempty_instances->prev;
end = rhc->nonempty_instances;
inst = rhc->nonempty_instances;
assert (!ddsrt_circlist_isempty (&rhc->nonempty_instances));
struct ddsrt_circlist_elem const *prev = rhc->nonempty_instances.latest->prev;
inst = latest_nonempty_instance (rhc);
struct rhc_instance const * const end = inst;
n_nonempty_instances = 0;
do {
assert (!inst_is_empty (inst));
assert (prev->next == inst);
assert (inst->prev == prev);
prev = inst;
inst = inst->next;
assert (prev->next == &inst->nonempty_list);
assert (inst->nonempty_list.prev == prev);
prev = &inst->nonempty_list;
inst = next_nonempty_instance (inst);
n_nonempty_instances++;
} while (inst != end);
assert (rhc->n_nonempty_instances == n_nonempty_instances);

View file

@ -19,8 +19,7 @@
#include "dds/ddsi/q_bswap.h"
#include "dds/ddsi/q_config.h"
#include "dds/ddsi/q_freelist.h"
#include "dds/ddsi/q_plist.h"
#include "dds__stream.h"
#include "dds/ddsi/ddsi_plist.h"
#include "dds__serdata_builtintopic.h"
#include "dds/ddsi/ddsi_tkmap.h"
#include "dds/ddsi/q_entity.h"
@ -60,7 +59,7 @@ static void serdata_builtin_free(struct ddsi_serdata *dcmn)
{
struct ddsi_serdata_builtintopic *d = (struct ddsi_serdata_builtintopic *)dcmn;
if (d->c.kind == SDK_DATA)
nn_xqos_fini (&d->xqos);
ddsi_xqos_fini (&d->xqos);
ddsrt_free (d);
}
@ -73,13 +72,13 @@ static struct ddsi_serdata_builtintopic *serdata_builtin_new(const struct ddsi_s
static void from_entity_pp (struct ddsi_serdata_builtintopic *d, const struct participant *pp)
{
nn_xqos_copy(&d->xqos, &pp->plist->qos);
ddsi_xqos_copy(&d->xqos, &pp->plist->qos);
d->pphandle = pp->e.iid;
}
static void from_entity_proxypp (struct ddsi_serdata_builtintopic *d, const struct proxy_participant *proxypp)
{
nn_xqos_copy(&d->xqos, &proxypp->plist->qos);
ddsi_xqos_copy(&d->xqos, &proxypp->plist->qos);
d->pphandle = proxypp->e.iid;
}
@ -100,14 +99,14 @@ static void set_topic_type_from_sertopic (struct ddsi_serdata_builtintopic *d, c
static void from_entity_rd (struct ddsi_serdata_builtintopic *d, const struct reader *rd)
{
d->pphandle = rd->c.pp->e.iid;
nn_xqos_copy(&d->xqos, rd->xqos);
ddsi_xqos_copy(&d->xqos, rd->xqos);
set_topic_type_from_sertopic(d, rd->topic);
}
static void from_entity_prd (struct ddsi_serdata_builtintopic *d, const struct proxy_reader *prd)
{
d->pphandle = prd->c.proxypp->e.iid;
nn_xqos_copy(&d->xqos, prd->c.xqos);
ddsi_xqos_copy(&d->xqos, prd->c.xqos);
assert (d->xqos.present & QP_TOPIC_NAME);
assert (d->xqos.present & QP_TYPE_NAME);
}
@ -115,14 +114,14 @@ static void from_entity_prd (struct ddsi_serdata_builtintopic *d, const struct p
static void from_entity_wr (struct ddsi_serdata_builtintopic *d, const struct writer *wr)
{
d->pphandle = wr->c.pp->e.iid;
nn_xqos_copy(&d->xqos, wr->xqos);
ddsi_xqos_copy(&d->xqos, wr->xqos);
set_topic_type_from_sertopic(d, wr->topic);
}
static void from_entity_pwr (struct ddsi_serdata_builtintopic *d, const struct proxy_writer *pwr)
{
d->pphandle = pwr->c.proxypp->e.iid;
nn_xqos_copy(&d->xqos, pwr->c.xqos);
ddsi_xqos_copy(&d->xqos, pwr->c.xqos);
assert (d->xqos.present & QP_TOPIC_NAME);
assert (d->xqos.present & QP_TYPE_NAME);
}
@ -132,7 +131,7 @@ 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->gv->entity_index, (const ddsi_guid_t *) keyhash->value);
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));
if (entity)
@ -196,10 +195,10 @@ static dds_qos_t *dds_qos_from_xqos_reuse (dds_qos_t *old, const dds_qos_t *src)
old = ddsrt_malloc (sizeof (*old));
else
{
nn_xqos_fini (old);
ddsi_xqos_fini (old);
}
nn_xqos_init_empty (old);
nn_xqos_mergein_missing (old, src, ~(QP_TOPIC_NAME | QP_TYPE_NAME));
ddsi_xqos_init_empty (old);
ddsi_xqos_mergein_missing (old, src, ~(QP_TOPIC_NAME | QP_TYPE_NAME));
return old;
}
@ -288,6 +287,7 @@ const struct ddsi_serdata_ops ddsi_serdata_ops_builtintopic = {
.eqkey = serdata_builtin_eqkey,
.free = serdata_builtin_free,
.from_ser = 0,
.from_ser_iov = 0,
.from_keyhash = ddsi_serdata_builtin_from_keyhash,
.from_sample = 0,
.to_ser = serdata_builtin_to_ser,

View file

@ -25,12 +25,11 @@
/* FIXME: sertopic /= ddstopic so a lot of stuff needs to be moved here from dds_topic.c and the free function needs to be implemented properly */
struct ddsi_sertopic *new_sertopic_builtintopic (enum ddsi_sertopic_builtintopic_type type, const char *name, const char *typename, struct q_globals *gv)
struct ddsi_sertopic *new_sertopic_builtintopic (enum ddsi_sertopic_builtintopic_type type, const char *name, const char *typename)
{
struct ddsi_sertopic_builtintopic *tp = ddsrt_malloc (sizeof (*tp));
ddsi_sertopic_init (&tp->c, name, typename, &ddsi_sertopic_ops_builtintopic, &ddsi_serdata_ops_builtintopic, false);
tp->type = type;
tp->gv = gv;
return &tp->c;
}
@ -40,6 +39,19 @@ static void sertopic_builtin_free (struct ddsi_sertopic *tp)
ddsrt_free (tp);
}
static bool sertopic_builtin_equal (const struct ddsi_sertopic *acmn, const struct ddsi_sertopic *bcmn)
{
const struct ddsi_sertopic_builtintopic *a = (struct ddsi_sertopic_builtintopic *) acmn;
const struct ddsi_sertopic_builtintopic *b = (struct ddsi_sertopic_builtintopic *) bcmn;
return a->type == b->type;
}
static uint32_t sertopic_builtin_hash (const struct ddsi_sertopic *tpcmn)
{
const struct ddsi_sertopic_builtintopic *tp = (struct ddsi_sertopic_builtintopic *) tpcmn;
return (uint32_t) tp->type;
}
static void free_pp (void *vsample)
{
dds_builtintopic_participant_t *sample = vsample;
@ -131,6 +143,8 @@ static void sertopic_builtin_free_samples (const struct ddsi_sertopic *sertopic_
}
const struct ddsi_sertopic_ops ddsi_sertopic_ops_builtintopic = {
.equal = sertopic_builtin_equal,
.hash = sertopic_builtin_hash,
.free = sertopic_builtin_free,
.zero_samples = sertopic_builtin_zero_samples,
.realloc_samples = sertopic_builtin_realloc_samples,

File diff suppressed because it is too large Load diff

View file

@ -16,7 +16,7 @@
#include "dds__qos.h"
#include "dds/ddsi/ddsi_iid.h"
#include "dds/ddsi/q_entity.h"
#include "dds/ddsi/q_globals.h"
#include "dds/ddsi/ddsi_domaingv.h"
#include "dds/ddsrt/heap.h"
#include "dds/version.h"
@ -55,9 +55,9 @@ dds_entity_t dds__create_subscriber_l (dds_participant *participant, bool implic
new_qos = dds_create_qos ();
if (qos)
nn_xqos_mergein_missing (new_qos, qos, DDS_SUBSCRIBER_QOS_MASK);
nn_xqos_mergein_missing (new_qos, &participant->m_entity.m_domain->gv.default_xqos_sub, ~(uint64_t)0);
if ((ret = nn_xqos_valid (&participant->m_entity.m_domain->gv.logconfig, new_qos)) != DDS_RETCODE_OK)
ddsi_xqos_mergein_missing (new_qos, qos, DDS_SUBSCRIBER_QOS_MASK);
ddsi_xqos_mergein_missing (new_qos, &participant->m_entity.m_domain->gv.default_xqos_sub, ~(uint64_t)0);
if ((ret = ddsi_xqos_valid (&participant->m_entity.m_domain->gv.logconfig, new_qos)) != DDS_RETCODE_OK)
{
dds_delete_qos (new_qos);
return ret;

View file

@ -19,7 +19,6 @@
#include "dds__topic.h"
#include "dds__listener.h"
#include "dds__participant.h"
#include "dds__stream.h"
#include "dds__init.h"
#include "dds__domain.h"
#include "dds__get_status.h"
@ -30,8 +29,9 @@
#include "dds/ddsi/ddsi_sertopic.h"
#include "dds/ddsi/q_ddsi_discovery.h"
#include "dds/ddsi/ddsi_iid.h"
#include "dds/ddsi/q_plist.h"
#include "dds/ddsi/q_globals.h"
#include "dds/ddsi/ddsi_plist.h"
#include "dds/ddsi/ddsi_domaingv.h"
#include "dds/ddsi/ddsi_cdrstream.h"
#include "dds__serdata_builtintopic.h"
DECL_ENTITY_LOCK_UNLOCK (extern inline, dds_topic)
@ -45,15 +45,6 @@ struct topic_sertopic_node {
const struct ddsi_sertopic *st;
};
static int topic_sertopic_node_cmp (const void *va, const void *vb)
{
const struct ddsi_sertopic *a = va;
const struct ddsi_sertopic *b = vb;
return strcmp (a->name, b->name);
}
const ddsrt_avl_treedef_t dds_topictree_def = DDSRT_AVL_TREEDEF_INITIALIZER_INDKEY (offsetof (struct topic_sertopic_node, avlnode), offsetof (struct topic_sertopic_node, st), topic_sertopic_node_cmp, 0);
static bool is_valid_name (const char *name) ddsrt_nonnull_all;
static bool is_valid_name (const char *name)
@ -112,164 +103,88 @@ static void dds_topic_status_cb (struct dds_topic *tp)
}
#endif
struct ddsi_sertopic *dds_topic_lookup (dds_domain *domain, const char *name)
dds_return_t dds_topic_pin (dds_entity_t handle, struct dds_topic **tp)
{
const struct ddsi_sertopic key = { .name = (char *) name };
struct ddsi_sertopic *st;
struct topic_sertopic_node *nst;
ddsrt_mutex_lock (&dds_global.m_mutex);
if ((nst = ddsrt_avl_lookup (&dds_topictree_def, &domain->m_topics, &key)) == NULL)
st = NULL;
else
st = ddsi_sertopic_ref (nst->st);
ddsrt_mutex_unlock (&dds_global.m_mutex);
return st;
}
static bool dds_find_topic_check_and_add_ref (dds_entity_t participant, dds_entity_t topic, const char *name)
{
dds_topic *tp;
if (dds_topic_lock (topic, &tp) != DDS_RETCODE_OK)
return false;
bool ret;
if (dds_entity_participant (&tp->m_entity)->m_entity.m_hdllink.hdl != participant || strcmp (tp->m_stopic->name, name) != 0)
ret = false;
else
{
/* Simply return the same topic, though that is different to the spirit
of the DDS specification, which gives you a unique copy. Giving that
unique copy means there potentially many versions of exactly the same
topic around, and that two entities can be dealing with the same data
even though they have different topics objects (though with the same
name). That I find a confusing model.
As far as I can tell, the only benefit is the ability to set different
listeners on the various copies of the topic. And that seems to be a
really small benefit. */
ret = true;
}
dds_topic_unlock (tp);
return ret;
}
dds_entity_t dds_find_topic (dds_entity_t participant, const char *name)
{
dds_entity *pe;
struct dds_entity *e;
dds_return_t ret;
dds_entity_t topic;
if (name == NULL)
return DDS_RETCODE_BAD_PARAMETER;
/* claim participant handle to guarantee the handle remains valid after
unlocking the participant prior to verifying the found topic still
exists */
if ((ret = dds_entity_pin (participant, &pe)) < 0)
if ((ret = dds_entity_pin (handle, &e)) < 0)
return ret;
if (dds_entity_kind (pe) != DDS_KIND_PARTICIPANT)
if (dds_entity_kind (e) != DDS_KIND_TOPIC)
{
dds_entity_unpin (pe);
dds_entity_unpin (e);
return DDS_RETCODE_ILLEGAL_OPERATION;
}
*tp = (struct dds_topic *) e;
return DDS_RETCODE_OK;
}
do {
dds_participant *p;
topic = DDS_RETCODE_PRECONDITION_NOT_MET;
if ((ret = dds_participant_lock (participant, &p)) == DDS_RETCODE_OK)
{
ddsrt_avl_iter_t it;
for (dds_entity *e = ddsrt_avl_iter_first (&dds_entity_children_td, &p->m_entity.m_children, &it); e != NULL; e = ddsrt_avl_iter_next (&it))
{
if (dds_entity_kind (e) == DDS_KIND_TOPIC && strcmp (((dds_topic *) e)->m_stopic->name, name) == 0)
{
topic = e->m_hdllink.hdl;
break;
}
}
dds_participant_unlock (p);
}
} while (topic > 0 && !dds_find_topic_check_and_add_ref (participant, topic, name));
void dds_topic_unpin (struct dds_topic *tp)
{
dds_entity_unpin (&tp->m_entity);
}
dds_entity_unpin (pe);
return topic;
void dds_topic_defer_set_qos (struct dds_topic *tp)
{
struct dds_ktopic * const ktp = tp->m_ktopic;
struct dds_participant * const pp = dds_entity_participant (&tp->m_entity);
ddsrt_mutex_lock (&pp->m_entity.m_mutex);
++ktp->defer_set_qos;
ddsrt_mutex_unlock (&pp->m_entity.m_mutex);
}
void dds_topic_allow_set_qos (struct dds_topic *tp)
{
struct dds_ktopic * const ktp = tp->m_ktopic;
struct dds_participant * const pp = dds_entity_participant (&tp->m_entity);
ddsrt_mutex_lock (&pp->m_entity.m_mutex);
assert (ktp->defer_set_qos > 0);
if (--ktp->defer_set_qos == 0)
ddsrt_cond_broadcast (&pp->m_entity.m_cond);
ddsrt_mutex_unlock (&pp->m_entity.m_mutex);
}
static dds_return_t dds_topic_delete (dds_entity *e) ddsrt_nonnull_all;
static dds_return_t dds_topic_delete (dds_entity *e)
{
dds_topic *tp = (dds_topic *) e;
dds_domain *domain = tp->m_entity.m_domain;
ddsrt_avl_dpath_t dp;
struct topic_sertopic_node *stn;
ddsrt_mutex_lock (&dds_global.m_mutex);
stn = ddsrt_avl_lookup_dpath (&dds_topictree_def, &domain->m_topics, tp->m_stopic, &dp);
assert (stn != NULL);
if (--stn->refc == 0)
{
ddsrt_avl_delete_dpath (&dds_topictree_def, &domain->m_topics, stn, &dp);
ddsrt_free (stn);
}
struct dds_topic * const tp = (dds_topic *) e;
struct dds_ktopic * const ktp = tp->m_ktopic;
assert (dds_entity_kind (e->m_parent) == DDS_KIND_PARTICIPANT);
dds_participant * const pp = (dds_participant *) e->m_parent;
ddsi_sertopic_unref (tp->m_stopic);
ddsrt_mutex_unlock (&dds_global.m_mutex);
ddsrt_mutex_lock (&pp->m_entity.m_mutex);
if (--ktp->refc == 0)
{
ddsrt_avl_delete (&participant_ktopics_treedef, &pp->m_ktopics, ktp);
dds_delete_qos (ktp->qos);
ddsrt_free (ktp->name);
ddsrt_free (ktp->type_name);
dds_free (ktp);
}
ddsrt_mutex_unlock (&pp->m_entity.m_mutex);
return DDS_RETCODE_OK;
}
static dds_return_t dds_topic_qos_set (dds_entity *e, const dds_qos_t *qos, bool enabled)
{
/* note: e->m_qos is still the old one to allow for failure here */
/* We never actually set the qos of a struct dds_topic and really shouldn't be here,
but the code to check whether set_qos is supported uses the entity's qos_set
function as a proxy. One of the weird things about the topic's set_qos is that
this is called with e == NULL. */
(void) e; (void) qos; (void) enabled;
assert (e == NULL);
return DDS_RETCODE_OK;
}
static bool dupdef_qos_ok (const dds_qos_t *qos, const dds_topic *tp)
static bool dupdef_qos_ok (const dds_qos_t *qos, const dds_ktopic *ktp)
{
if ((qos == NULL) != (tp->m_entity.m_qos == NULL))
if ((qos == NULL) != (ktp->qos == NULL))
return false;
else if (qos == NULL)
return true;
else
return dds_qos_equal (tp->m_entity.m_qos, qos);
}
static bool sertopic_equivalent (const struct ddsi_sertopic *a, const struct ddsi_sertopic *b)
{
if (strcmp (a->name_type_name, b->name_type_name) != 0)
return false;
if (a->serdata_basehash != b->serdata_basehash)
return false;
if (a->ops != b->ops)
return false;
if (a->serdata_ops != b->serdata_ops)
return false;
return true;
}
static dds_return_t create_topic_topic_arbitrary_check_sertopic (dds_entity_t participant, dds_entity_t topic, struct ddsi_sertopic *sertopic, const dds_qos_t *qos)
{
dds_topic *tp;
dds_return_t ret;
if (dds_topic_lock (topic, &tp) < 0)
return DDS_RETCODE_NOT_FOUND;
if (dds_entity_participant (&tp->m_entity)->m_entity.m_hdllink.hdl != participant)
ret = DDS_RETCODE_NOT_FOUND;
else if (!sertopic_equivalent (tp->m_stopic, sertopic))
ret = DDS_RETCODE_PRECONDITION_NOT_MET;
else if (!dupdef_qos_ok (qos, tp))
ret = DDS_RETCODE_INCONSISTENT_POLICY;
else
{
/* See dds_find_topic_check_and_add_ref */
ret = DDS_RETCODE_OK;
}
dds_topic_unlock (tp);
return ret;
return dds_qos_equal (ktp->qos, qos);
}
const struct dds_entity_deriver dds_entity_deriver_topic = {
@ -280,192 +195,192 @@ const struct dds_entity_deriver dds_entity_deriver_topic = {
.validate_status = dds_topic_status_validate
};
dds_entity_t dds_create_topic_impl (dds_entity_t participant, struct ddsi_sertopic *sertopic, const dds_qos_t *qos, const dds_listener_t *listener, const nn_plist_t *sedp_plist)
/**
* @brief Checks whether a ktopic with the same name exists in the participant,
* and if so, whether it's QoS matches or not.
*
* The set of ktopics is stored in the participant, protected by the participant's
* mutex and the internal state of these ktopics (including the QoS) is also
* protected by that mutex.
*
* @param[out] ktp_out matching ktopic if call was successful, or NULL if no
* ktopic with this name exists
* @param[in] pp pinned & locked participant
* @param[in] name topic name to look for
* @param[in] type_name type name the topic must have
* @param[in] new_qos QoS for the new topic (can be NULL)
*
* @returns success + ktopic, success + NULL or error.
*
* @retval DDS_RETCODE_OK
* ktp_out is either NULL (first attempt at creating this topic), or
* the matching ktopic entity
* @retval DDS_RETCODE_INCONSISTENT_POLICY
* a ktopic exists with differing QoS
* @retval DDS_RETCODE_PRECONDITION_NOT_MET
* a ktopic exists with a different type name
*/
static dds_return_t lookup_and_check_ktopic (struct dds_ktopic **ktp_out, dds_participant *pp, const char *name, const char *type_name, const dds_qos_t *new_qos)
{
struct ddsi_domaingv * const gv = &pp->m_entity.m_domain->gv;
struct dds_ktopic *ktp;
if ((ktp = *ktp_out = ddsrt_avl_lookup (&participant_ktopics_treedef, &pp->m_ktopics, name)) == NULL)
{
GVTRACE ("lookup_and_check_ktopic_may_unlock_pp: no such ktopic\n");
return DDS_RETCODE_OK;
}
else if (strcmp (ktp->type_name, type_name) != 0)
{
GVTRACE ("lookup_and_check_ktopic_may_unlock_pp: ktp %p typename %s mismatch\n", (void *) ktp, ktp->type_name);
return DDS_RETCODE_PRECONDITION_NOT_MET;
}
else if (!dupdef_qos_ok (new_qos, ktp))
{
GVTRACE ("lookup_and_check_ktopic_may_unlock_pp: ktp %p qos mismatch\n", (void *) ktp);
return DDS_RETCODE_INCONSISTENT_POLICY;
}
else
{
GVTRACE ("lookup_and_check_ktopic_may_unlock_pp: ktp %p reuse\n", (void *) ktp);
return DDS_RETCODE_OK;
}
}
static dds_entity_t create_topic_pp_locked (struct dds_participant *pp, struct dds_ktopic *ktp, bool implicit, struct ddsi_sertopic *sertopic_registered, const dds_listener_t *listener, const ddsi_plist_t *sedp_plist)
{
dds_entity_t hdl;
dds_topic *tp = dds_alloc (sizeof (*tp));
hdl = dds_entity_init (&tp->m_entity, &pp->m_entity, DDS_KIND_TOPIC, implicit, NULL, listener, DDS_TOPIC_STATUS_MASK);
tp->m_entity.m_iid = ddsi_iid_gen ();
dds_entity_register_child (&pp->m_entity, &tp->m_entity);
tp->m_ktopic = ktp;
tp->m_stopic = sertopic_registered;
/* Publish Topic */
if (sedp_plist)
{
struct participant *ddsi_pp;
ddsi_plist_t plist;
thread_state_awake (lookup_thread_state (), &pp->m_entity.m_domain->gv);
ddsi_pp = entidx_lookup_participant_guid (pp->m_entity.m_domain->gv.entity_index, &pp->m_entity.m_guid);
assert (ddsi_pp);
ddsi_plist_init_empty (&plist);
ddsi_plist_mergein_missing (&plist, sedp_plist, ~(uint64_t)0, ~(uint64_t)0);
ddsi_xqos_mergein_missing (&plist.qos, ktp->qos, ~(uint64_t)0);
sedp_write_topic (ddsi_pp, &plist);
ddsi_plist_fini (&plist);
thread_state_asleep (lookup_thread_state ());
}
dds_entity_init_complete (&tp->m_entity);
return hdl;
}
dds_entity_t dds_create_topic_impl (dds_entity_t participant, struct ddsi_sertopic *sertopic, const dds_qos_t *qos, const dds_listener_t *listener, const ddsi_plist_t *sedp_plist)
{
dds_return_t rc;
dds_participant *par;
dds_entity *par_ent;
dds_topic *top;
dds_participant *pp;
dds_qos_t *new_qos = NULL;
dds_entity_t hdl;
struct participant *ddsi_pp;
struct ddsi_sertopic *sertopic_registered;
if (sertopic == NULL)
return DDS_RETCODE_BAD_PARAMETER;
/* Claim participant handle so we can be sure the handle will not be
reused if we temporarily unlock the participant to check the an
existing topic's compatibility */
if ((rc = dds_entity_pin (participant, &par_ent)) < 0)
return rc;
/* Verify that we've been given a participant, not strictly necessary
because dds_participant_lock below checks it, but this is more
obvious */
if (dds_entity_kind (par_ent) != DDS_KIND_PARTICIPANT)
{
dds_entity_unpin (par_ent);
return DDS_RETCODE_ILLEGAL_OPERATION;
dds_entity *par_ent;
if ((rc = dds_entity_pin (participant, &par_ent)) < 0)
return rc;
if (dds_entity_kind (par_ent) != DDS_KIND_PARTICIPANT)
{
dds_entity_unpin (par_ent);
return DDS_RETCODE_ILLEGAL_OPERATION;
}
pp = (struct dds_participant *) par_ent;
}
new_qos = dds_create_qos ();
if (qos)
nn_xqos_mergein_missing (new_qos, qos, DDS_TOPIC_QOS_MASK);
ddsi_xqos_mergein_missing (new_qos, qos, DDS_TOPIC_QOS_MASK);
/* One would expect this:
*
* nn_xqos_mergein_missing (new_qos, &gv.default_xqos_tp, ~(uint64_t)0);
* ddsi_xqos_mergein_missing (new_qos, &gv.default_xqos_tp, ~(uint64_t)0);
*
* but the crazy defaults of the DDS specification has a default settings
* for reliability that are dependent on the entity type: readers and
* but the crazy defaults of the DDS specification has a default setting
* for reliability that is dependent on the entity type: readers and
* topics default to best-effort, but writers to reliable.
*
* Leaving the topic QoS sparse means a default-default topic QoS of
* best-effort will do "the right thing" and let a writer still default to
* reliable ... (and keep behaviour unchanged) */
if ((rc = nn_xqos_valid (&par_ent->m_domain->gv.logconfig, new_qos)) != DDS_RETCODE_OK)
goto err_invalid_qos;
/* FIXME: just mutex_lock ought to be good enough, but there is the
pesky "closed" check still ... */
if ((rc = dds_participant_lock (participant, &par)) != DDS_RETCODE_OK)
goto err_lock_participant;
bool retry_lookup;
do {
dds_entity_t topic;
/* claim participant handle to guarantee the handle remains valid after
unlocking the participant prior to verifying the found topic still
exists */
topic = DDS_RETCODE_PRECONDITION_NOT_MET;
ddsrt_avl_iter_t it;
for (dds_entity *e = ddsrt_avl_iter_first (&dds_entity_children_td, &par->m_entity.m_children, &it); e != NULL; e = ddsrt_avl_iter_next (&it))
{
if (dds_entity_kind (e) == DDS_KIND_TOPIC && strcmp (((dds_topic *) e)->m_stopic->name, sertopic->name) == 0)
{
topic = e->m_hdllink.hdl;
break;
}
}
if (topic < 0)
{
/* no topic with the name exists; we have locked the participant, and
so we can proceed with creating the topic */
retry_lookup = false;
}
else
{
/* some topic with the same name exists; need to lock the topic to
perform the checks, but locking the topic while holding the
participant lock violates the lock order (child -> parent). So
unlock that participant and check the topic while accounting
for the various scary cases. */
dds_participant_unlock (par);
rc = create_topic_topic_arbitrary_check_sertopic (participant, topic, sertopic, new_qos);
switch (rc)
{
case DDS_RETCODE_OK: /* duplicate definition */
dds_entity_unpin (par_ent);
dds_delete_qos (new_qos);
return topic;
case DDS_RETCODE_NOT_FOUND:
/* either participant is now being deleted, topic was deleted, or
topic was deleted & the handle reused for something else -- so */
retry_lookup = true;
break;
case DDS_RETCODE_PRECONDITION_NOT_MET: /* incompatible sertopic */
case DDS_RETCODE_INCONSISTENT_POLICY: /* different QoS */
/* inconsistent definition */
dds_entity_unpin (par_ent);
dds_delete_qos (new_qos);
return rc;
default:
abort ();
}
if ((rc = dds_participant_lock (participant, &par)) != DDS_RETCODE_OK)
goto err_lock_participant;
}
} while (retry_lookup);
/* FIXME: make this a function
Add sertopic to domain -- but note that it may have been created by another thread
on another participant that is attached to the same domain */
struct ddsi_domaingv * const gv = &pp->m_entity.m_domain->gv;
if ((rc = ddsi_xqos_valid (&gv->logconfig, new_qos)) != DDS_RETCODE_OK)
{
struct dds_domain *domain = par->m_entity.m_domain;
ddsrt_avl_ipath_t ip;
struct topic_sertopic_node *stn;
ddsrt_mutex_lock (&dds_global.m_mutex);
stn = ddsrt_avl_lookup_ipath (&dds_topictree_def, &domain->m_topics, sertopic, &ip);
if (stn == NULL)
{
/* no existing definition: use new */
stn = ddsrt_malloc (sizeof (*stn));
stn->refc = 1;
stn->st = ddsi_sertopic_ref (sertopic);
ddsrt_avl_insert (&dds_topictree_def, &domain->m_topics, stn);
ddsrt_mutex_unlock (&dds_global.m_mutex);
}
else if (sertopic_equivalent (stn->st, sertopic))
{
/* ok -- same definition, so use existing one instead */
sertopic = ddsi_sertopic_ref (stn->st);
stn->refc++;
ddsrt_mutex_unlock (&dds_global.m_mutex);
}
else
{
/* bummer, delete */
ddsrt_mutex_unlock (&dds_global.m_mutex);
rc = DDS_RETCODE_PRECONDITION_NOT_MET;
goto err_sertopic_reuse;
}
dds_delete_qos (new_qos);
dds_entity_unpin (&pp->m_entity);
return rc;
}
/* Create topic */
top = dds_alloc (sizeof (*top));
/* See if we're allowed to create the topic; ktp is returned pinned & locked
so we can be sure it doesn't disappear and its QoS can't change */
GVTRACE ("dds_create_topic_arbitrary (pp %p "PGUIDFMT" sertopic %p reg?%s refc %"PRIu32" %s/%s)\n",
(void *) pp, PGUID (pp->m_entity.m_guid), (void *) sertopic, sertopic->gv ? "yes" : "no",
ddsrt_atomic_ld32 (&sertopic->refc), sertopic->name, sertopic->type_name);
ddsrt_mutex_lock (&pp->m_entity.m_mutex);
struct dds_ktopic *ktp;
if ((rc = lookup_and_check_ktopic (&ktp, pp, sertopic->name, sertopic->type_name, new_qos)) != DDS_RETCODE_OK)
{
GVTRACE ("dds_create_topic_arbitrary: failed after compatibility check: %s\n", dds_strretcode (rc));
dds_participant_unlock (pp);
dds_delete_qos (new_qos);
return rc;
}
/* Create a ktopic if it doesn't exist yet, else reference existing one and delete the
unneeded "new_qos". */
if (ktp == NULL)
{
ktp = dds_alloc (sizeof (*ktp));
ktp->refc = 1;
ktp->defer_set_qos = 0;
ktp->qos = new_qos;
/* have to copy these because the ktopic can outlast any specific sertopic */
ktp->name = ddsrt_strdup (sertopic->name);
ktp->type_name = ddsrt_strdup (sertopic->type_name);
ddsrt_avl_insert (&participant_ktopics_treedef, &pp->m_ktopics, ktp);
GVTRACE ("create_and_lock_ktopic: ktp %p\n", (void *) ktp);
}
else
{
ktp->refc++;
dds_delete_qos (new_qos);
}
/* Sertopic: re-use a previously registered one if possible, else register this one */
{
ddsrt_mutex_lock (&gv->sertopics_lock);
if ((sertopic_registered = ddsi_sertopic_lookup_locked (gv, sertopic)) != NULL)
GVTRACE ("dds_create_topic_arbitrary: reuse sertopic %p\n", (void *) sertopic_registered);
else
{
GVTRACE ("dds_create_topic_arbitrary: register new sertopic %p\n", (void *) sertopic);
ddsi_sertopic_register_locked (gv, sertopic);
sertopic_registered = sertopic;
}
ddsrt_mutex_unlock (&gv->sertopics_lock);
}
/* Create topic referencing ktopic & sertopic_registered */
/* FIXME: setting "implicit" based on sertopic->ops is a hack */
hdl = dds_entity_init (&top->m_entity, &par->m_entity, DDS_KIND_TOPIC, (sertopic->ops == &ddsi_sertopic_ops_builtintopic), new_qos, listener, DDS_TOPIC_STATUS_MASK);
top->m_entity.m_iid = ddsi_iid_gen ();
dds_entity_register_child (&par->m_entity, &top->m_entity);
top->m_stopic = sertopic;
/* Publish Topic */
thread_state_awake (lookup_thread_state (), &par->m_entity.m_domain->gv);
ddsi_pp = entidx_lookup_participant_guid (par->m_entity.m_domain->gv.entity_index, &par->m_entity.m_guid);
assert (ddsi_pp);
if (sedp_plist)
{
nn_plist_t plist;
nn_plist_init_empty (&plist);
nn_plist_mergein_missing (&plist, sedp_plist, ~(uint64_t)0, ~(uint64_t)0);
nn_xqos_mergein_missing (&plist.qos, new_qos, ~(uint64_t)0);
sedp_write_topic (ddsi_pp, &plist);
nn_plist_fini (&plist);
}
thread_state_asleep (lookup_thread_state ());
dds_entity_init_complete (&top->m_entity);
dds_participant_unlock (par);
dds_entity_unpin (par_ent);
hdl = create_topic_pp_locked (pp, ktp, (sertopic_registered->ops == &ddsi_sertopic_ops_builtintopic), sertopic_registered, listener, sedp_plist);
dds_participant_unlock (pp);
GVTRACE ("dds_create_topic_arbitrary: new topic %"PRId32"\n", hdl);
return hdl;
err_sertopic_reuse:
dds_participant_unlock (par);
err_lock_participant:
err_invalid_qos:
dds_delete_qos (new_qos);
dds_entity_unpin (par_ent);
return rc;
}
dds_entity_t dds_create_topic_arbitrary (dds_entity_t participant, struct ddsi_sertopic *sertopic, const dds_qos_t *qos, const dds_listener_t *listener, const nn_plist_t *sedp_plist)
dds_entity_t dds_create_topic_arbitrary (dds_entity_t participant, struct ddsi_sertopic *sertopic, const dds_qos_t *qos, const dds_listener_t *listener, const ddsi_plist_t *sedp_plist)
{
assert(sertopic);
assert(sertopic->name);
@ -477,7 +392,7 @@ dds_entity_t dds_create_topic_arbitrary (dds_entity_t participant, struct ddsi_s
dds_entity_t dds_create_topic (dds_entity_t participant, const dds_topic_descriptor_t *desc, const char *name, const dds_qos_t *qos, const dds_listener_t *listener)
{
struct ddsi_sertopic_default *st;
nn_plist_t plist;
ddsi_plist_t plist;
dds_entity_t hdl;
struct dds_entity *ppent;
dds_return_t ret;
@ -493,17 +408,23 @@ dds_entity_t dds_create_topic (dds_entity_t participant, const dds_topic_descrip
ddsi_sertopic_init (&st->c, name, desc->m_typename, &ddsi_sertopic_ops_default, desc->m_nkeys ? &ddsi_serdata_ops_cdr : &ddsi_serdata_ops_cdr_nokey, (desc->m_nkeys == 0));
st->native_encoding_identifier = (DDSRT_ENDIAN == DDSRT_LITTLE_ENDIAN ? CDR_LE : CDR_BE);
st->serpool = ppent->m_domain->gv.serpool;
st->type = (void*) desc;
st->nkeys = desc->m_nkeys;
st->keys = desc->m_keys;
st->type.m_size = desc->m_size;
st->type.m_align = desc->m_align;
st->type.m_flagset = desc->m_flagset;
st->type.m_nkeys = desc->m_nkeys;
st->type.m_keys = ddsrt_malloc (st->type.m_nkeys * sizeof (*st->type.m_keys));
for (uint32_t i = 0; i < st->type.m_nkeys; i++)
st->type.m_keys[i] = desc->m_keys[i].m_index;
st->type.m_nops = dds_stream_countops (desc->m_ops);
st->type.m_ops = ddsrt_memdup (desc->m_ops, st->type.m_nops * sizeof (*st->type.m_ops));
/* Check if topic cannot be optimised (memcpy marshal) */
if (!(desc->m_flagset & DDS_TOPIC_NO_OPTIMIZE)) {
st->opt_size = dds_stream_check_optimize (desc);
if (!(st->type.m_flagset & DDS_TOPIC_NO_OPTIMIZE)) {
st->opt_size = dds_stream_check_optimize (&st->type);
DDS_CTRACE (&ppent->m_domain->gv.logconfig, "Marshalling for type: %s is %soptimised\n", desc->m_typename, st->opt_size ? "" : "not ");
}
nn_plist_init_empty (&plist);
ddsi_plist_init_empty (&plist);
/* Set Topic meta data (for SEDP publication) */
plist.qos.topic_name = ddsrt_strdup (st->c.name);
plist.qos.type_name = ddsrt_strdup (st->c.type_name);
@ -526,10 +447,51 @@ dds_entity_t dds_create_topic (dds_entity_t participant, const dds_topic_descrip
hdl = dds_create_topic_arbitrary (participant, &st->c, qos, listener, &plist);
ddsi_sertopic_unref (&st->c);
dds_entity_unpin (ppent);
nn_plist_fini (&plist);
ddsi_plist_fini (&plist);
return hdl;
}
dds_entity_t dds_find_topic (dds_entity_t participant, const char *name)
{
dds_participant *pp;
dds_return_t rc;
if (name == NULL)
return DDS_RETCODE_BAD_PARAMETER;
if ((rc = dds_participant_lock (participant, &pp)) < 0)
return rc;
ddsrt_avl_iter_t it;
for (dds_entity *e = ddsrt_avl_iter_first (&dds_entity_children_td, &pp->m_entity.m_children, &it); e != NULL; e = ddsrt_avl_iter_next (&it))
{
if (dds_entity_kind (e) != DDS_KIND_TOPIC)
continue;
struct dds_entity *x;
if (dds_entity_pin (e->m_hdllink.hdl, &x) != DDS_RETCODE_OK)
continue;
struct dds_topic * const tp = (struct dds_topic *) e;
if (x != e || strcmp (tp->m_ktopic->name, name) != 0)
{
dds_entity_unpin (x);
continue;
}
struct ddsi_sertopic * const sertopic = ddsi_sertopic_ref (tp->m_stopic);
struct dds_ktopic * const ktp = tp->m_ktopic;
ktp->refc++;
dds_entity_unpin (x);
dds_entity_t hdl = create_topic_pp_locked (pp, ktp, false, sertopic, NULL, NULL);
dds_participant_unlock (pp);
return hdl;
}
dds_participant_unlock (pp);
return DDS_RETCODE_PRECONDITION_NOT_MET;
}
static bool dds_topic_chaining_filter (const void *sample, void *ctx)
{
dds_topic_filter_fn realf = (dds_topic_filter_fn) ctx;
@ -602,10 +564,10 @@ dds_return_t dds_get_name (dds_entity_t topic, char *name, size_t size)
if (size <= 0 || name == NULL)
return DDS_RETCODE_BAD_PARAMETER;
name[0] = '\0';
if ((ret = dds_topic_lock (topic, &t)) != DDS_RETCODE_OK)
if ((ret = dds_topic_pin (topic, &t)) != DDS_RETCODE_OK)
return ret;
(void) snprintf (name, size, "%s", t->m_stopic->name);
dds_topic_unlock (t);
dds_topic_unpin (t);
return DDS_RETCODE_OK;
}
@ -616,10 +578,10 @@ dds_return_t dds_get_type_name (dds_entity_t topic, char *name, size_t size)
if (size <= 0 || name == NULL)
return DDS_RETCODE_BAD_PARAMETER;
name[0] = '\0';
if ((ret = dds_topic_lock (topic, &t)) != DDS_RETCODE_OK)
if ((ret = dds_topic_pin (topic, &t)) != DDS_RETCODE_OK)
return ret;
(void) snprintf (name, size, "%s", t->m_stopic->type_name);
dds_topic_unlock (t);
dds_topic_unpin (t);
return DDS_RETCODE_OK;
}

View file

@ -22,14 +22,20 @@
#ifdef DDSI_INCLUDE_LIFESPAN
#include "dds/ddsi/ddsi_lifespan.h"
#endif
#ifdef DDSI_INCLUDE_DEADLINE_MISSED
#include "dds/ddsi/ddsi_deadline.h"
#endif
#include "dds/ddsi/q_unused.h"
#include "dds/ddsi/q_config.h"
#include "dds/ddsi/ddsi_tkmap.h"
#include "dds/ddsi/q_time.h"
#include "dds/ddsi/q_rtps.h"
#include "dds/ddsi/q_freelist.h"
#include "dds/ddsi/q_globals.h"
#include "dds/ddsi/ddsi_domaingv.h"
#include "dds/ddsi/q_entity.h"
#include "dds__whc.h"
#include "dds__entity.h"
#include "dds__writer.h"
#define USE_EHH 0
@ -42,7 +48,7 @@ struct whc_node {
seqno_t seq;
uint64_t total_bytes; /* cumulative number of bytes up to and including this node */
size_t size;
struct nn_plist *plist; /* 0 if nothing special */
struct ddsi_plist *plist; /* 0 if nothing special */
unsigned unacked: 1; /* counted in whc::unacked_bytes iff 1 */
unsigned borrowed: 1; /* at most one can borrow it at any time */
nn_mtime_t last_rexmit_ts;
@ -66,6 +72,9 @@ struct whc_idxnode {
seqno_t prune_seq;
struct ddsi_tkmap_instance *tk;
uint32_t headidx;
#ifdef DDSI_INCLUDE_DEADLINE_MISSED
struct deadline_elem deadline; /* list element for deadline missed */
#endif
struct whc_node *hist[];
};
@ -76,6 +85,15 @@ struct whc_seq_entry {
};
#endif
struct whc_writer_info {
dds_writer * writer; /* can be NULL, eg in case of whc for built-in writers */
unsigned is_transient_local: 1;
unsigned has_deadline: 1;
uint32_t hdepth; /* 0 = unlimited */
uint32_t tldepth; /* 0 = disabled/unlimited (no need to maintain an index if KEEP_ALL <=> is_transient_local + tldepth=0) */
uint32_t idxdepth; /* = max (hdepth, tldepth) */
};
struct whc_impl {
struct whc common;
ddsrt_mutex_t lock;
@ -84,13 +102,10 @@ struct whc_impl {
size_t sample_overhead;
uint32_t fragment_size;
uint64_t total_bytes; /* total number of bytes pushed in */
unsigned is_transient_local: 1;
unsigned xchecks: 1;
struct q_globals *gv;
struct ddsi_domaingv *gv;
struct ddsi_tkmap *tkmap;
uint32_t hdepth; /* 0 = unlimited */
uint32_t tldepth; /* 0 = disabled/unlimited (no need to maintain an index if KEEP_ALL <=> is_transient_local + tldepth=0) */
uint32_t idxdepth; /* = max (hdepth, tldepth) */
struct whc_writer_info wrinfo;
seqno_t max_drop_seq; /* samples in whc with seq <= max_drop_seq => transient-local */
struct whc_intvnode *open_intv; /* interval where next sample will go (usually) */
struct whc_node *maxseq_node; /* NULL if empty; if not in open_intv, open_intv is empty */
@ -104,6 +119,9 @@ struct whc_impl {
#ifdef DDSI_INCLUDE_LIFESPAN
struct lifespan_adm lifespan; /* Lifespan administration */
#endif
#ifdef DDSI_INCLUDE_DEADLINE_MISSED
struct deadline_adm deadline; /* Deadline missed administration */
#endif
};
struct whc_sample_iter_impl {
@ -137,7 +155,7 @@ static uint32_t whc_default_remove_acked_messages_full (struct whc_impl *whc, se
static uint32_t whc_default_remove_acked_messages (struct whc *whc, seqno_t max_drop_seq, struct whc_state *whcst, struct whc_node **deferred_free_list);
static void whc_default_free_deferred_free_list (struct whc *whc, struct whc_node *deferred_free_list);
static void whc_default_get_state (const struct whc *whc, struct whc_state *st);
static int whc_default_insert (struct whc *whc, seqno_t max_drop_seq, seqno_t seq, nn_mtime_t exp, struct nn_plist *plist, struct ddsi_serdata *serdata, struct ddsi_tkmap_instance *tk);
static int whc_default_insert (struct whc *whc, seqno_t max_drop_seq, seqno_t seq, nn_mtime_t exp, struct ddsi_plist *plist, struct ddsi_serdata *serdata, struct ddsi_tkmap_instance *tk);
static seqno_t whc_default_next_seq (const struct whc *whc, seqno_t seq);
static bool whc_default_borrow_sample (const struct whc *whc, seqno_t seq, struct whc_borrowed_sample *sample);
static bool whc_default_borrow_sample_key (const struct whc *whc, const struct ddsi_serdata *serdata_key, struct whc_borrowed_sample *sample);
@ -373,45 +391,91 @@ static nn_mtime_t whc_sample_expired_cb(void *hc, nn_mtime_t tnow)
}
#endif
struct whc *whc_new (struct q_globals *gv, int is_transient_local, uint32_t hdepth, uint32_t tldepth)
#ifdef DDSI_INCLUDE_DEADLINE_MISSED
static nn_mtime_t whc_deadline_missed_cb(void *hc, nn_mtime_t tnow)
{
struct whc_impl *whc = hc;
void *vidxnode;
nn_mtime_t tnext;
ddsrt_mutex_lock (&whc->lock);
while ((tnext = deadline_next_missed_locked (&whc->deadline, tnow, &vidxnode)).v == 0)
{
struct whc_idxnode *idxnode = vidxnode;
deadline_reregister_instance_locked (&whc->deadline, &idxnode->deadline, tnow);
status_cb_data_t cb_data;
cb_data.raw_status_id = (int) DDS_OFFERED_DEADLINE_MISSED_STATUS_ID;
cb_data.extra = 0;
cb_data.handle = 0;
cb_data.add = true;
ddsrt_mutex_unlock (&whc->lock);
dds_writer_status_cb (&whc->wrinfo.writer->m_entity, &cb_data);
ddsrt_mutex_lock (&whc->lock);
tnow = now_mt ();
}
ddsrt_mutex_unlock (&whc->lock);
return tnext;
}
#endif
struct whc_writer_info *whc_make_wrinfo (struct dds_writer *wr, const dds_qos_t *qos)
{
struct whc_writer_info *wrinfo = ddsrt_malloc (sizeof (*wrinfo));
wrinfo->writer = wr;
wrinfo->is_transient_local = (qos->durability.kind == DDS_DURABILITY_TRANSIENT_LOCAL);
wrinfo->has_deadline = (qos->deadline.deadline != DDS_INFINITY);
wrinfo->hdepth = (qos->history.kind == DDS_HISTORY_KEEP_ALL) ? 0 : (unsigned) qos->history.depth;
if (!wrinfo->is_transient_local)
wrinfo->tldepth = 0;
else
wrinfo->tldepth = (qos->durability_service.history.kind == DDS_HISTORY_KEEP_ALL) ? 0 : (unsigned) qos->durability_service.history.depth;
wrinfo->idxdepth = wrinfo->hdepth > wrinfo->tldepth ? wrinfo->hdepth : wrinfo->tldepth;
return wrinfo;
}
void whc_free_wrinfo (struct whc_writer_info *wrinfo)
{
ddsrt_free (wrinfo);
}
struct whc *whc_new (struct ddsi_domaingv *gv, const struct whc_writer_info *wrinfo)
{
size_t sample_overhead = 80; /* INFO_TS, DATA (estimate), inline QoS */
struct whc_impl *whc;
struct whc_intvnode *intv;
assert ((hdepth == 0 || tldepth <= hdepth) || is_transient_local);
assert ((wrinfo->hdepth == 0 || wrinfo->tldepth <= wrinfo->hdepth) || wrinfo->is_transient_local);
whc = ddsrt_malloc (sizeof (*whc));
whc->common.ops = &whc_ops;
ddsrt_mutex_init (&whc->lock);
whc->is_transient_local = is_transient_local ? 1 : 0;
whc->xchecks = (gv->config.enabled_xchecks & DDS_XCHECK_WHC) != 0;
whc->gv = gv;
whc->tkmap = gv->m_tkmap;
whc->hdepth = hdepth;
whc->tldepth = tldepth;
whc->idxdepth = hdepth > tldepth ? hdepth : tldepth;
memcpy (&whc->wrinfo, wrinfo, sizeof (*wrinfo));
whc->seq_size = 0;
whc->max_drop_seq = 0;
whc->unacked_bytes = 0;
whc->total_bytes = 0;
whc->sample_overhead = sample_overhead;
whc->fragment_size = gv->config.fragment_size;
whc->idx_hash = ddsrt_hh_new (1, whc_idxnode_hash_key, whc_idxnode_eq_key);
#if USE_EHH
whc->seq_hash = ddsrt_ehh_new (sizeof (struct whc_seq_entry), 32, whc_seq_entry_hash, whc_seq_entry_eq);
#else
whc->seq_hash = ddsrt_hh_new (1, whc_node_hash, whc_node_eq);
#endif
if (whc->idxdepth > 0)
whc->idx_hash = ddsrt_hh_new (1, whc_idxnode_hash_key, whc_idxnode_eq_key);
else
whc->idx_hash = NULL;
#ifdef DDSI_INCLUDE_LIFESPAN
lifespan_init (gv, &whc->lifespan, offsetof(struct whc_impl, lifespan), offsetof(struct whc_node, lifespan), whc_sample_expired_cb);
#endif
#ifdef DDSI_INCLUDE_DEADLINE_MISSED
whc->deadline.dur = (wrinfo->writer != NULL) ? wrinfo->writer->m_entity.m_qos->deadline.deadline : DDS_INFINITY;
deadline_init (gv, &whc->deadline, offsetof(struct whc_impl, deadline), offsetof(struct whc_idxnode, deadline), whc_deadline_missed_cb);
#endif
/* seq interval tree: always has an "open" node */
ddsrt_avl_init (&whc_seq_treedef, &whc->seq);
intv = ddsrt_malloc (sizeof (*intv));
@ -434,7 +498,7 @@ static void free_whc_node_contents (struct whc_node *whcn)
{
ddsi_serdata_unref (whcn->serdata);
if (whcn->plist) {
nn_plist_fini (whcn->plist);
ddsi_plist_fini (whcn->plist);
ddsrt_free (whcn->plist);
}
}
@ -450,14 +514,19 @@ void whc_default_free (struct whc *whc_generic)
lifespan_fini (&whc->lifespan);
#endif
if (whc->idx_hash)
{
struct ddsrt_hh_iter it;
struct whc_idxnode *n;
for (n = ddsrt_hh_iter_first (whc->idx_hash, &it); n != NULL; n = ddsrt_hh_iter_next (&it))
ddsrt_free (n);
ddsrt_hh_free (whc->idx_hash);
}
#ifdef DDSI_INCLUDE_DEADLINE_MISSED
deadline_stop (&whc->deadline);
ddsrt_mutex_lock (&whc->lock);
deadline_clear (&whc->deadline);
ddsrt_mutex_unlock (&whc->lock);
deadline_fini (&whc->deadline);
#endif
struct ddsrt_hh_iter it;
struct whc_idxnode *idxn;
for (idxn = ddsrt_hh_iter_first (whc->idx_hash, &it); idxn != NULL; idxn = ddsrt_hh_iter_next (&it))
ddsrt_free (idxn);
ddsrt_hh_free (whc->idx_hash);
{
struct whc_node *whcn = whc->maxseq_node;
@ -577,31 +646,19 @@ static seqno_t whc_default_next_seq (const struct whc *whc_generic, seqno_t seq)
return nseq;
}
static void delete_one_sample_from_idx (struct whc_impl *whc, struct whc_node *whcn)
static void delete_one_sample_from_idx (struct whc_node *whcn)
{
struct whc_idxnode * const idxn = whcn->idxnode;
assert (idxn != NULL);
assert (idxn->hist[idxn->headidx] != NULL);
assert (idxn->hist[whcn->idxnode_pos] == whcn);
if (whcn->idxnode_pos != idxn->headidx)
idxn->hist[whcn->idxnode_pos] = NULL;
else
{
#ifndef NDEBUG
for (uint32_t i = 0; i < whc->idxdepth; i++)
assert (i == idxn->headidx || idxn->hist[i] == NULL);
#endif
if (!ddsrt_hh_remove (whc->idx_hash, idxn))
assert (0);
ddsi_tkmap_instance_unref (whc->tkmap, idxn->tk);
ddsrt_free (idxn);
}
idxn->hist[whcn->idxnode_pos] = NULL;
whcn->idxnode = NULL;
}
static void free_one_instance_from_idx (struct whc_impl *whc, seqno_t max_drop_seq, struct whc_idxnode *idxn)
{
for (uint32_t i = 0; i < whc->idxdepth; i++)
for (uint32_t i = 0; i < whc->wrinfo.idxdepth; i++)
{
if (idxn->hist[i])
{
@ -622,6 +679,9 @@ static void delete_one_instance_from_idx (struct whc_impl *whc, seqno_t max_drop
{
if (!ddsrt_hh_remove (whc->idx_hash, idxn))
assert (0);
#ifdef DDSI_INCLUDE_DEADLINE_MISSED
deadline_unregister_instance_locked (&whc->deadline, &idxn->deadline);
#endif
free_one_instance_from_idx (whc, max_drop_seq, idxn);
}
@ -631,9 +691,9 @@ static int whcn_in_tlidx (const struct whc_impl *whc, const struct whc_idxnode *
return 0;
else
{
uint32_t d = (idxn->headidx + (pos > idxn->headidx ? whc->idxdepth : 0)) - pos;
assert (d < whc->idxdepth);
return d < whc->tldepth;
uint32_t d = (idxn->headidx + (pos > idxn->headidx ? whc->wrinfo.idxdepth : 0)) - pos;
assert (d < whc->wrinfo.idxdepth);
return d < whc->wrinfo.tldepth;
}
}
@ -649,7 +709,7 @@ static uint32_t whc_default_downgrade_to_volatile (struct whc *whc_generic, stru
ddsrt_mutex_lock (&whc->lock);
check_whc (whc);
if (whc->idxdepth == 0)
if (whc->wrinfo.idxdepth == 0)
{
/* if not maintaining an index at all, this is nonsense */
get_state_locked (whc, st);
@ -657,19 +717,24 @@ static uint32_t whc_default_downgrade_to_volatile (struct whc *whc_generic, stru
return 0;
}
assert (!whc->is_transient_local);
if (whc->tldepth > 0)
assert (!whc->wrinfo.is_transient_local);
if (whc->wrinfo.tldepth > 0)
{
assert (whc->hdepth == 0 || whc->tldepth <= whc->hdepth);
whc->tldepth = 0;
if (whc->hdepth == 0)
assert (whc->wrinfo.hdepth == 0 || whc->wrinfo.tldepth <= whc->wrinfo.hdepth);
whc->wrinfo.tldepth = 0;
if (whc->wrinfo.hdepth == 0)
{
struct ddsrt_hh_iter it;
struct whc_idxnode *n;
for (n = ddsrt_hh_iter_first (whc->idx_hash, &it); n != NULL; n = ddsrt_hh_iter_next (&it))
free_one_instance_from_idx (whc, 0, n);
struct whc_idxnode *idxn;
for (idxn = ddsrt_hh_iter_first (whc->idx_hash, &it); idxn != NULL; idxn = ddsrt_hh_iter_next (&it))
{
#ifdef DDSI_INCLUDE_DEADLINE_MISSED
deadline_unregister_instance_locked (&whc->deadline, &idxn->deadline);
#endif
free_one_instance_from_idx (whc, 0, idxn);
}
ddsrt_hh_free (whc->idx_hash);
whc->idxdepth = 0;
whc->wrinfo.idxdepth = 0;
whc->idx_hash = NULL;
}
}
@ -711,7 +776,7 @@ static void whc_delete_one_intv (struct whc_impl *whc, struct whc_intvnode **p_i
/* If it is in the tlidx, take it out. Transient-local data never gets here */
if (whcn->idxnode)
delete_one_sample_from_idx (whc, whcn);
delete_one_sample_from_idx (whcn);
if (whcn->unacked)
{
assert (whc->unacked_bytes >= whcn->size);
@ -927,15 +992,27 @@ static uint32_t whc_default_remove_acked_messages_full (struct whc_impl *whc, se
struct whc_node deferred_list_head, *last_to_free = &deferred_list_head;
uint32_t ndropped = 0;
if (whc->is_transient_local && whc->tldepth == 0)
whcn = find_nextseq_intv (&intv, whc, whc->max_drop_seq);
if (whc->wrinfo.is_transient_local && whc->wrinfo.tldepth == 0)
{
/* KEEP_ALL on transient local, so we can never ever delete anything */
TRACE (" KEEP_ALL transient-local: do nothing\n");
/* KEEP_ALL on transient local, so we can never ever delete anything, but
we have to ack the data in whc */
TRACE (" KEEP_ALL transient-local: ack data\n");
while (whcn && whcn->seq <= max_drop_seq)
{
if (whcn->unacked)
{
assert (whc->unacked_bytes >= whcn->size);
whc->unacked_bytes -= whcn->size;
whcn->unacked = 0;
}
whcn = whcn->next_seq;
}
whc->max_drop_seq = max_drop_seq;
*deferred_free_list = NULL;
return 0;
}
whcn = find_nextseq_intv (&intv, whc, whc->max_drop_seq);
deferred_list_head.next_seq = NULL;
prev_seq = whcn ? whcn->prev_seq : NULL;
while (whcn && whcn->seq <= max_drop_seq)
@ -982,10 +1059,10 @@ static uint32_t whc_default_remove_acked_messages_full (struct whc_impl *whc, se
the T-L history but that are not anymore. Writing new samples will eventually push these
out, but if the difference is large and the update rate low, it may take a long time.
Thus, we had better prune them. */
if (whc->tldepth > 0 && whc->idxdepth > whc->tldepth)
if (whc->wrinfo.tldepth > 0 && whc->wrinfo.idxdepth > whc->wrinfo.tldepth)
{
assert (whc->hdepth == whc->idxdepth);
TRACE (" idxdepth %"PRIu32" > tldepth %"PRIu32" > 0 -- must prune\n", whc->idxdepth, whc->tldepth);
assert (whc->wrinfo.hdepth == whc->wrinfo.idxdepth);
TRACE (" idxdepth %"PRIu32" > tldepth %"PRIu32" > 0 -- must prune\n", whc->wrinfo.idxdepth, whc->wrinfo.tldepth);
/* Do a second pass over the sequence number range we just processed: this time we only
encounter samples that were retained because of the transient-local durability setting
@ -1010,11 +1087,11 @@ static uint32_t whc_default_remove_acked_messages_full (struct whc_impl *whc, se
idxn->prune_seq = max_drop_seq;
idx = idxn->headidx;
cnt = whc->idxdepth - whc->tldepth;
cnt = whc->wrinfo.idxdepth - whc->wrinfo.tldepth;
while (cnt--)
{
struct whc_node *oldn;
if (++idx == whc->idxdepth)
if (++idx == whc->wrinfo.idxdepth)
idx = 0;
if ((oldn = idxn->hist[idx]) != NULL)
{
@ -1061,12 +1138,16 @@ static uint32_t whc_default_remove_acked_messages (struct whc *whc_generic, seqn
get_state_locked (whc, &tmp);
TRACE ("whc_default_remove_acked_messages(%p max_drop_seq %"PRId64")\n", (void *)whc, max_drop_seq);
TRACE (" whc: [%"PRId64",%"PRId64"] max_drop_seq %"PRId64" h %"PRIu32" tl %"PRIu32"\n",
tmp.min_seq, tmp.max_seq, whc->max_drop_seq, whc->hdepth, whc->tldepth);
tmp.min_seq, tmp.max_seq, whc->max_drop_seq, whc->wrinfo.hdepth, whc->wrinfo.tldepth);
}
check_whc (whc);
if (whc->idxdepth == 0)
/* In case a deadline is set, a sample may be added to whc temporarily, which could be
stored in acked state. The _noidx variant of removing messages assumes that unacked
data exists in whc. So in case of a deadline, the _full variant is used instead,
even when index depth is 0 */
if (whc->wrinfo.idxdepth == 0 && !whc->wrinfo.has_deadline && !whc->wrinfo.is_transient_local)
cnt = whc_default_remove_acked_messages_noidx (whc, max_drop_seq, deferred_free_list);
else
cnt = whc_default_remove_acked_messages_full (whc, max_drop_seq, deferred_free_list);
@ -1075,13 +1156,11 @@ static uint32_t whc_default_remove_acked_messages (struct whc *whc_generic, seqn
return cnt;
}
static struct whc_node *whc_default_insert_seq (struct whc_impl *whc, seqno_t max_drop_seq, seqno_t seq, nn_mtime_t exp, struct nn_plist *plist, struct ddsi_serdata *serdata)
static struct whc_node *whc_default_insert_seq (struct whc_impl *whc, seqno_t max_drop_seq, seqno_t seq, nn_mtime_t exp, struct ddsi_plist *plist, struct ddsi_serdata *serdata)
{
struct whc_node *newn = NULL;
#ifndef DDSI_INCLUDE_LIFESPAN
/* FIXME: the 'exp' arg is used for lifespan, refactor this parameter to a struct 'writer info'
that contains both lifespan and deadline info of the writer */
DDSRT_UNUSED_ARG (exp);
#endif
@ -1149,7 +1228,7 @@ static struct whc_node *whc_default_insert_seq (struct whc_impl *whc, seqno_t ma
return newn;
}
static int whc_default_insert (struct whc *whc_generic, seqno_t max_drop_seq, seqno_t seq, nn_mtime_t exp, struct nn_plist *plist, struct ddsi_serdata *serdata, struct ddsi_tkmap_instance *tk)
static int whc_default_insert (struct whc *whc_generic, seqno_t max_drop_seq, seqno_t seq, nn_mtime_t exp, struct ddsi_plist *plist, struct ddsi_serdata *serdata, struct ddsi_tkmap_instance *tk)
{
struct whc_impl * const whc = (struct whc_impl *)whc_generic;
struct whc_node *newn = NULL;
@ -1172,7 +1251,7 @@ static int whc_default_insert (struct whc *whc_generic, seqno_t max_drop_seq, se
TRACE ("whc_default_insert(%p max_drop_seq %"PRId64" seq %"PRId64" exp %"PRId64" plist %p serdata %p:%"PRIx32")\n",
(void *) whc, max_drop_seq, seq, exp.v, (void *) plist, (void *) serdata, serdata->hash);
TRACE (" whc: [%"PRId64",%"PRId64"] max_drop_seq %"PRId64" h %"PRIu32" tl %"PRIu32"\n",
whcst.min_seq, whcst.max_seq, whc->max_drop_seq, whc->hdepth, whc->tldepth);
whcst.min_seq, whcst.max_seq, whc->max_drop_seq, whc->wrinfo.hdepth, whc->wrinfo.tldepth);
}
assert (max_drop_seq < MAX_SEQ_NUMBER);
@ -1189,7 +1268,7 @@ static int whc_default_insert (struct whc *whc_generic, seqno_t max_drop_seq, se
TRACE (" whcn %p:", (void*)newn);
/* Special case of empty data (such as commit messages) can't go into index, and if we're not maintaining an index, we're done, too */
if (serdata->kind == SDK_EMPTY || whc->idxdepth == 0)
if (serdata->kind == SDK_EMPTY)
{
TRACE (" empty or no hist\n");
ddsrt_mutex_unlock (&whc->lock);
@ -1215,42 +1294,50 @@ static int whc_default_insert (struct whc *whc_generic, seqno_t max_drop_seq, se
}
else
{
struct whc_node *oldn;
if (++idxn->headidx == whc->idxdepth)
idxn->headidx = 0;
if ((oldn = idxn->hist[idxn->headidx]) != NULL)
#ifdef DDSI_INCLUDE_DEADLINE_MISSED
deadline_renew_instance_locked (&whc->deadline, &idxn->deadline);
#endif
if (whc->wrinfo.idxdepth > 0)
{
TRACE (" overwrite whcn %p", (void *)oldn);
oldn->idxnode = NULL;
}
idxn->hist[idxn->headidx] = newn;
newn->idxnode = idxn;
newn->idxnode_pos = idxn->headidx;
if (oldn && (whc->hdepth > 0 || oldn->seq <= max_drop_seq))
{
TRACE (" prune whcn %p", (void *)oldn);
assert (oldn != whc->maxseq_node);
whc_delete_one (whc, oldn);
}
/* Special case for dropping everything beyond T-L history when the new sample is being
auto-acknowledged (for lack of reliable readers), and the keep-last T-L history is
shallower than the keep-last regular history (normal path handles this via pruning in
whc_default_remove_acked_messages, but that never happens when there are no readers). */
if (seq <= max_drop_seq && whc->tldepth > 0 && whc->idxdepth > whc->tldepth)
{
uint32_t pos = idxn->headidx + whc->idxdepth - whc->tldepth;
if (pos >= whc->idxdepth)
pos -= whc->idxdepth;
if ((oldn = idxn->hist[pos]) != NULL)
struct whc_node *oldn;
if (++idxn->headidx == whc->wrinfo.idxdepth)
idxn->headidx = 0;
if ((oldn = idxn->hist[idxn->headidx]) != NULL)
{
TRACE (" prune tl whcn %p", (void *)oldn);
assert (oldn != whc->maxseq_node);
whc_delete_one (whc, oldn);
TRACE (" overwrite whcn %p", (void *)oldn);
oldn->idxnode = NULL;
}
idxn->hist[idxn->headidx] = newn;
newn->idxnode = idxn;
newn->idxnode_pos = idxn->headidx;
if (oldn && (whc->wrinfo.hdepth > 0 || oldn->seq <= max_drop_seq) && whc->wrinfo.tldepth > 0)
{
TRACE (" prune whcn %p", (void *)oldn);
assert (oldn != whc->maxseq_node || whc->wrinfo.has_deadline);
whc_delete_one (whc, oldn);
if (oldn == whc->maxseq_node)
whc->maxseq_node = whc_findmax_procedurally (whc);
}
/* Special case for dropping everything beyond T-L history when the new sample is being
auto-acknowledged (for lack of reliable readers), and the keep-last T-L history is
shallower than the keep-last regular history (normal path handles this via pruning in
whc_default_remove_acked_messages, but that never happens when there are no readers). */
if (seq <= max_drop_seq && whc->wrinfo.tldepth > 0 && whc->wrinfo.idxdepth > whc->wrinfo.tldepth)
{
uint32_t pos = idxn->headidx + whc->wrinfo.idxdepth - whc->wrinfo.tldepth;
if (pos >= whc->wrinfo.idxdepth)
pos -= whc->wrinfo.idxdepth;
if ((oldn = idxn->hist[pos]) != NULL)
{
TRACE (" prune tl whcn %p", (void *)oldn);
assert (oldn != whc->maxseq_node);
whc_delete_one (whc, oldn);
}
}
TRACE ("\n");
}
TRACE ("\n");
}
}
else
@ -1259,20 +1346,26 @@ static int whc_default_insert (struct whc *whc_generic, seqno_t max_drop_seq, se
/* Ignore unregisters, but insert everything else */
if (!(serdata->statusinfo & NN_STATUSINFO_UNREGISTER))
{
idxn = ddsrt_malloc (sizeof (*idxn) + whc->idxdepth * sizeof (idxn->hist[0]));
idxn = ddsrt_malloc (sizeof (*idxn) + whc->wrinfo.idxdepth * sizeof (idxn->hist[0]));
TRACE (" idxn %p", (void *)idxn);
ddsi_tkmap_instance_ref (tk);
idxn->iid = tk->m_iid;
idxn->tk = tk;
idxn->prune_seq = 0;
idxn->headidx = 0;
idxn->hist[0] = newn;
for (uint32_t i = 1; i < whc->idxdepth; i++)
idxn->hist[i] = NULL;
newn->idxnode = idxn;
newn->idxnode_pos = 0;
if (whc->wrinfo.idxdepth > 0)
{
idxn->hist[0] = newn;
for (uint32_t i = 1; i < whc->wrinfo.idxdepth; i++)
idxn->hist[i] = NULL;
newn->idxnode = idxn;
newn->idxnode_pos = 0;
}
if (!ddsrt_hh_add (whc->idx_hash, idxn))
assert (0);
#ifdef DDSI_INCLUDE_DEADLINE_MISSED
deadline_register_instance_locked (&whc->deadline, &idxn->deadline, now_mt ());
#endif
}
else
{
@ -1346,7 +1439,7 @@ static void return_sample_locked (struct whc_impl *whc, struct whc_borrowed_samp
ddsi_serdata_unref (sample->serdata);
if (sample->plist)
{
nn_plist_fini (sample->plist);
ddsi_plist_fini (sample->plist);
ddsrt_free (sample->plist);
}
}

View file

@ -19,7 +19,7 @@
#include "dds/ddsi/q_config.h"
#include "dds/ddsi/ddsi_entity_index.h"
#include "dds/ddsi/q_entity.h"
#include "dds/ddsi/q_globals.h"
#include "dds/ddsi/ddsi_domaingv.h"
#include "dds/ddsi/ddsi_tkmap.h"
#include "dds__serdata_builtintopic.h"
#include "dds__whc_builtintopic.h"
@ -143,7 +143,7 @@ static void bwhc_get_state (const struct whc *whc, struct whc_state *st)
st->unacked_bytes = 0;
}
static int bwhc_insert (struct whc *whc, seqno_t max_drop_seq, seqno_t seq, nn_mtime_t exp, struct nn_plist *plist, struct ddsi_serdata *serdata, struct ddsi_tkmap_instance *tk)
static int bwhc_insert (struct whc *whc, seqno_t max_drop_seq, seqno_t seq, nn_mtime_t exp, struct ddsi_plist *plist, struct ddsi_serdata *serdata, struct ddsi_tkmap_instance *tk)
{
(void)whc;
(void)max_drop_seq;

View file

@ -18,13 +18,14 @@
#include "dds/ddsi/q_xmsg.h"
#include "dds/ddsi/ddsi_rhc.h"
#include "dds/ddsi/ddsi_serdata.h"
#include "dds__stream.h"
#include "dds/ddsi/ddsi_cdrstream.h"
#include "dds/ddsi/q_transmit.h"
#include "dds/ddsi/ddsi_entity_index.h"
#include "dds/ddsi/q_config.h"
#include "dds/ddsi/q_entity.h"
#include "dds/ddsi/q_radmin.h"
#include "dds/ddsi/q_globals.h"
#include "dds/ddsi/ddsi_domaingv.h"
#include "dds/ddsi/ddsi_deliver_locally.h"
dds_return_t dds_write (dds_entity_t writer, const void *data)
{
@ -71,80 +72,103 @@ dds_return_t dds_write_ts (dds_entity_t writer, const void *data, dds_time_t tim
return ret;
}
static dds_return_t try_store (struct ddsi_rhc *rhc, const struct ddsi_writer_info *pwr_info, struct ddsi_serdata *payload, struct ddsi_tkmap_instance *tk, dds_duration_t *max_block_ms)
static struct reader *writer_first_in_sync_reader (struct entity_index *entity_index, struct entity_common *wrcmn, ddsrt_avl_iter_t *it)
{
while (! ddsi_rhc_store (rhc, pwr_info, payload, tk))
assert (wrcmn->kind == EK_WRITER);
struct writer *wr = (struct writer *) wrcmn;
struct wr_rd_match *m = ddsrt_avl_iter_first (&wr_local_readers_treedef, &wr->local_readers, it);
return m ? entidx_lookup_reader_guid (entity_index, &m->rd_guid) : NULL;
}
static struct reader *writer_next_in_sync_reader (struct entity_index *entity_index, ddsrt_avl_iter_t *it)
{
struct wr_rd_match *m = ddsrt_avl_iter_next (it);
return m ? entidx_lookup_reader_guid (entity_index, &m->rd_guid) : NULL;
}
struct local_sourceinfo {
const struct ddsi_sertopic *src_topic;
struct ddsi_serdata *src_payload;
struct ddsi_tkmap_instance *src_tk;
nn_mtime_t timeout;
};
static struct ddsi_serdata *local_make_sample (struct ddsi_tkmap_instance **tk, struct ddsi_domaingv *gv, struct ddsi_sertopic const * const topic, void *vsourceinfo)
{
struct local_sourceinfo *si = vsourceinfo;
if (topic == si->src_topic)
{
if (*max_block_ms > 0)
*tk = si->src_tk;
/* FIXME: see if this pair of refc increments can't be avoided
They're needed because free_sample_after_delivery will always be called, but
in the common case of a local writer and a single sertopic, make_sample doesn't
actually create a sample, and so free_sample_after_delivery doesn't actually
have to free anything */
ddsi_tkmap_instance_ref (si->src_tk);
return ddsi_serdata_ref (si->src_payload);
}
else
{
/* ouch ... convert a serdata from one sertopic to another ... */
ddsrt_iovec_t iov;
uint32_t size = ddsi_serdata_size (si->src_payload);
(void) ddsi_serdata_to_ser_ref (si->src_payload, 0, size, &iov);
struct ddsi_serdata *d = ddsi_serdata_from_ser_iov (topic, si->src_payload->kind, 1, &iov, size);
ddsi_serdata_to_ser_unref (si->src_payload, &iov);
if (d)
{
dds_sleepfor (DDS_HEADBANG_TIMEOUT);
*max_block_ms -= DDS_HEADBANG_TIMEOUT;
d->statusinfo = si->src_payload->statusinfo;
d->timestamp = si->src_payload->timestamp;
*tk = ddsi_tkmap_lookup_instance_ref (gv->m_tkmap, d);
}
else
{
return DDS_RETCODE_TIMEOUT;
DDS_CWARNING (&gv->logconfig, "local: deserialization %s/%s failed in topic type conversion\n", topic->name, topic->type_name);
}
return d;
}
}
static dds_return_t local_on_delivery_failure_fastpath (struct entity_common *source_entity, bool source_entity_locked, struct local_reader_ary *fastpath_rdary, void *vsourceinfo)
{
(void) fastpath_rdary;
(void) source_entity_locked;
assert (source_entity->kind == EK_WRITER);
struct writer *wr = (struct writer *) source_entity;
struct local_sourceinfo *si = vsourceinfo;
nn_mtime_t tnow = now_mt ();
if (si->timeout.v == 0)
si->timeout = add_duration_to_mtime (tnow, wr->xqos->reliability.max_blocking_time);
if (tnow.v >= si->timeout.v)
return DDS_RETCODE_TIMEOUT;
else
{
dds_sleepfor (DDS_HEADBANG_TIMEOUT);
return DDS_RETCODE_OK;
}
return DDS_RETCODE_OK;
}
static dds_return_t deliver_locally (struct writer *wr, struct ddsi_serdata *payload, struct ddsi_tkmap_instance *tk)
{
dds_return_t ret = DDS_RETCODE_OK;
ddsrt_mutex_lock (&wr->rdary.rdary_lock);
if (wr->rdary.fastpath_ok)
{
struct reader ** const rdary = wr->rdary.rdary;
if (rdary[0])
{
dds_duration_t max_block_ms = wr->xqos->reliability.max_blocking_time;
struct ddsi_writer_info pwr_info;
ddsi_make_writer_info (&pwr_info, &wr->e, wr->xqos, payload->statusinfo);
for (uint32_t i = 0; rdary[i]; i++) {
DDS_CTRACE (&wr->e.gv->logconfig, "reader "PGUIDFMT"\n", PGUID (rdary[i]->e.guid));
if ((ret = try_store (rdary[i]->rhc, &pwr_info, payload, tk, &max_block_ms)) != DDS_RETCODE_OK)
break;
}
}
ddsrt_mutex_unlock (&wr->rdary.rdary_lock);
}
else
{
/* When deleting, pwr is no longer accessible via the hash
tables, and consequently, a reader may be deleted without
it being possible to remove it from rdary. The primary
reason rdary exists is to avoid locking the proxy writer
but this is less of an issue when we are deleting it, so
we fall back to using the GUIDs so that we can deliver all
samples we received from it. As writer being deleted any
reliable samples that are rejected are simply discarded. */
ddsrt_avl_iter_t it;
struct pwr_rd_match *m;
struct ddsi_writer_info wrinfo;
const struct entity_index *gh = wr->e.gv->entity_index;
dds_duration_t max_block_ms = wr->xqos->reliability.max_blocking_time;
ddsrt_mutex_unlock (&wr->rdary.rdary_lock);
ddsi_make_writer_info (&wrinfo, &wr->e, wr->xqos, payload->statusinfo);
ddsrt_mutex_lock (&wr->e.lock);
for (m = ddsrt_avl_iter_first (&wr_local_readers_treedef, &wr->local_readers, &it); m != NULL; m = ddsrt_avl_iter_next (&it))
{
struct reader *rd;
if ((rd = entidx_lookup_reader_guid (gh, &m->rd_guid)) != NULL)
{
DDS_CTRACE (&wr->e.gv->logconfig, "reader-via-guid "PGUIDFMT"\n", PGUID (rd->e.guid));
/* Copied the return value ignore from DDSI deliver_user_data () function. */
if ((ret = try_store (rd->rhc, &wrinfo, payload, tk, &max_block_ms)) != DDS_RETCODE_OK)
break;
}
}
ddsrt_mutex_unlock (&wr->e.lock);
}
if (ret == DDS_RETCODE_TIMEOUT)
{
static const struct deliver_locally_ops deliver_locally_ops = {
.makesample = local_make_sample,
.first_reader = writer_first_in_sync_reader,
.next_reader = writer_next_in_sync_reader,
.on_failure_fastpath = local_on_delivery_failure_fastpath
};
struct local_sourceinfo sourceinfo = {
.src_topic = wr->topic,
.src_payload = payload,
.src_tk = tk,
.timeout = { 0 },
};
dds_return_t rc;
struct ddsi_writer_info wrinfo;
ddsi_make_writer_info (&wrinfo, &wr->e, wr->xqos, payload->statusinfo);
rc = deliver_locally_allinsync (wr->e.gv, &wr->e, false, &wr->rdary, &wrinfo, &deliver_locally_ops, &sourceinfo);
if (rc == DDS_RETCODE_TIMEOUT)
DDS_CERROR (&wr->e.gv->logconfig, "The writer could not deliver data on time, probably due to a local reader resources being full\n");
}
return ret;
return rc;
}
dds_return_t dds_write_impl (dds_writer *wr, const void * data, dds_time_t tstamp, dds_write_action action)

View file

@ -15,7 +15,7 @@
#include "dds/version.h"
#include "dds/ddsrt/static_assert.h"
#include "dds/ddsi/q_config.h"
#include "dds/ddsi/q_globals.h"
#include "dds/ddsi/ddsi_domaingv.h"
#include "dds/ddsi/q_entity.h"
#include "dds/ddsi/q_thread.h"
#include "dds/ddsi/q_xmsg.h"
@ -50,9 +50,9 @@ static dds_return_t dds_writer_status_validate (uint32_t mask)
then status conditions is not triggered.
*/
static void dds_writer_status_cb (void *ventity, const status_cb_data_t *data)
void dds_writer_status_cb (void *entity, const struct status_cb_data *data)
{
dds_writer * const wr = ventity;
dds_writer * const wr = entity;
/* When data is NULL, it means that the DDSI reader is deleted. */
if (data == NULL)
@ -181,7 +181,7 @@ static void dds_writer_interrupt (dds_entity *e) ddsrt_nonnull_all;
static void dds_writer_interrupt (dds_entity *e)
{
struct q_globals * const gv = &e->m_domain->gv;
struct ddsi_domaingv * const gv = &e->m_domain->gv;
thread_state_awake (lookup_thread_state (), gv);
unblock_throttled_writer (gv, &e->m_guid);
thread_state_asleep (lookup_thread_state ());
@ -192,7 +192,7 @@ static void dds_writer_close (dds_entity *e) ddsrt_nonnull_all;
static void dds_writer_close (dds_entity *e)
{
struct dds_writer * const wr = (struct dds_writer *) e;
struct q_globals * const gv = &e->m_domain->gv;
struct ddsi_domaingv * const gv = &e->m_domain->gv;
struct thread_state1 * const ts1 = lookup_thread_state ();
thread_state_awake (ts1, gv);
nn_xpack_send (wr->m_xp, false);
@ -223,7 +223,12 @@ static dds_return_t validate_writer_qos (const dds_qos_t *wqos)
#ifndef DDSI_INCLUDE_LIFESPAN
if (wqos != NULL && (wqos->present & QP_LIFESPAN) && wqos->lifespan.duration != DDS_INFINITY)
return DDS_RETCODE_BAD_PARAMETER;
#else
#endif
#ifndef DDSI_INCLUDE_DEADLINE_MISSED
if (wqos != NULL && (wqos->present & QP_DEADLINE) && wqos->deadline.deadline != DDS_INFINITY)
return DDS_RETCODE_BAD_PARAMETER;
#endif
#if defined(DDSI_INCLUDE_LIFESPAN) && defined(DDSI_INCLUDE_DEADLINE_MISSED)
DDSRT_UNUSED_ARG (wqos);
#endif
return DDS_RETCODE_OK;
@ -246,30 +251,6 @@ static dds_return_t dds_writer_qos_set (dds_entity *e, const dds_qos_t *qos, boo
return DDS_RETCODE_OK;
}
static struct whc *make_whc (struct dds_domain *dom, const dds_qos_t *qos)
{
bool handle_as_transient_local;
uint32_t hdepth, tldepth;
/* Construct WHC -- if aggressive_keep_last1 is set, the WHC will
drop all samples for which a later update is available. This
forces it to maintain a tlidx. */
handle_as_transient_local = (qos->durability.kind == DDS_DURABILITY_TRANSIENT_LOCAL);
if (qos->history.kind == DDS_HISTORY_KEEP_ALL)
hdepth = 0;
else
hdepth = (unsigned) qos->history.depth;
if (!handle_as_transient_local)
tldepth = 0;
else
{
if (qos->durability_service.history.kind == DDS_HISTORY_KEEP_ALL)
tldepth = 0;
else
tldepth = (unsigned) qos->durability_service.history.depth;
}
return whc_new (&dom->gv, handle_as_transient_local, hdepth, tldepth);
}
const struct dds_entity_deriver dds_entity_deriver_writer = {
.interrupt = dds_writer_interrupt,
.close = dds_writer_close,
@ -282,12 +263,11 @@ dds_entity_t dds_create_writer (dds_entity_t participant_or_publisher, dds_entit
{
dds_return_t rc;
dds_qos_t *wqos;
dds_writer *wr;
dds_entity_t writer;
dds_publisher *pub = NULL;
dds_participant *pp;
dds_topic *tp;
dds_entity_t publisher;
struct whc_writer_info *wrinfo;
bool created_implicit_pub = false;
{
dds_entity *p_or_p;
@ -304,6 +284,7 @@ dds_entity_t dds_create_writer (dds_entity_t participant_or_publisher, dds_entit
dds_entity_unlock (p_or_p);
if ((rc = dds_publisher_lock (publisher, &pub)) < 0)
return rc;
created_implicit_pub = true;
break;
default:
dds_entity_unlock (p_or_p);
@ -313,28 +294,36 @@ dds_entity_t dds_create_writer (dds_entity_t participant_or_publisher, dds_entit
ddsi_tran_conn_t conn = pub->m_entity.m_domain->gv.data_conn_uc;
if ((rc = dds_topic_lock (topic, &tp)) != DDS_RETCODE_OK)
goto err_tp_lock;
if ((rc = dds_topic_pin (topic, &tp)) != DDS_RETCODE_OK)
goto err_pin_topic;
assert (tp->m_stopic);
pp = dds_entity_participant (&pub->m_entity);
if (pp != dds_entity_participant (&tp->m_entity))
if (dds_entity_participant (&pub->m_entity) != dds_entity_participant (&tp->m_entity))
{
rc = DDS_RETCODE_BAD_PARAMETER;
goto err_pp_mismatch;
}
/* Prevent set_qos on the topic until writer has been created and registered: we can't
allow a TOPIC_DATA change to ccur before the writer has been created because that
change would then not be published in the discovery/built-in topics.
Don't keep the participant (which protects the topic's QoS) locked because that
can cause deadlocks for applications creating a reader/writer from within a
publication matched listener (whether the restrictions on what one can do in
listeners are reasonable or not, it used to work so it can be broken arbitrarily). */
dds_topic_defer_set_qos (tp);
/* Merge Topic & Publisher qos */
wqos = dds_create_qos ();
if (qos)
nn_xqos_mergein_missing (wqos, qos, DDS_WRITER_QOS_MASK);
ddsi_xqos_mergein_missing (wqos, qos, DDS_WRITER_QOS_MASK);
if (pub->m_entity.m_qos)
nn_xqos_mergein_missing (wqos, pub->m_entity.m_qos, ~(uint64_t)0);
if (tp->m_entity.m_qos)
nn_xqos_mergein_missing (wqos, tp->m_entity.m_qos, ~(uint64_t)0);
nn_xqos_mergein_missing (wqos, &pub->m_entity.m_domain->gv.default_xqos_wr, ~(uint64_t)0);
ddsi_xqos_mergein_missing (wqos, pub->m_entity.m_qos, ~(uint64_t)0);
if (tp->m_ktopic->qos)
ddsi_xqos_mergein_missing (wqos, tp->m_ktopic->qos, ~(uint64_t)0);
ddsi_xqos_mergein_missing (wqos, &pub->m_entity.m_domain->gv.default_xqos_wr, ~(uint64_t)0);
if ((rc = nn_xqos_valid (&pub->m_entity.m_domain->gv.logconfig, wqos)) < 0 ||
if ((rc = ddsi_xqos_valid (&pub->m_entity.m_domain->gv.logconfig, wqos)) < 0 ||
(rc = validate_writer_qos(wqos)) != DDS_RETCODE_OK)
{
dds_delete_qos(wqos);
@ -342,17 +331,18 @@ dds_entity_t dds_create_writer (dds_entity_t participant_or_publisher, dds_entit
}
/* Create writer */
wr = dds_alloc (sizeof (*wr));
writer = dds_entity_init (&wr->m_entity, &pub->m_entity, DDS_KIND_WRITER, false, wqos, listener, DDS_WRITER_STATUS_MASK);
struct dds_writer * const wr = dds_alloc (sizeof (*wr));
const dds_entity_t writer = dds_entity_init (&wr->m_entity, &pub->m_entity, DDS_KIND_WRITER, false, wqos, listener, DDS_WRITER_STATUS_MASK);
wr->m_topic = tp;
dds_entity_add_ref_locked (&tp->m_entity);
wr->m_xp = nn_xpack_new (conn, get_bandwidth_limit (wqos->transport_priority), pub->m_entity.m_domain->gv.config.xpack_send_async);
wr->m_whc = make_whc (pub->m_entity.m_domain, wqos);
wrinfo = whc_make_wrinfo (wr, wqos);
wr->m_whc = whc_new (&pub->m_entity.m_domain->gv, wrinfo);
whc_free_wrinfo (wrinfo);
wr->whc_batch = pub->m_entity.m_domain->gv.config.whc_batch;
thread_state_awake (lookup_thread_state (), &pub->m_entity.m_domain->gv);
rc = new_writer (&wr->m_wr, &wr->m_entity.m_domain->gv, &wr->m_entity.m_guid, NULL, &pp->m_entity.m_guid, tp->m_stopic, wqos, wr->m_whc, dds_writer_status_cb, wr);
rc = new_writer (&wr->m_wr, &wr->m_entity.m_domain->gv, &wr->m_entity.m_guid, NULL, dds_entity_participant_guid (&pub->m_entity), tp->m_stopic, wqos, wr->m_whc, dds_writer_status_cb, wr);
assert(rc == DDS_RETCODE_OK);
thread_state_asleep (lookup_thread_state ());
@ -360,16 +350,19 @@ dds_entity_t dds_create_writer (dds_entity_t participant_or_publisher, dds_entit
dds_entity_register_child (&pub->m_entity, &wr->m_entity);
dds_entity_init_complete (&wr->m_entity);
dds_topic_unlock (tp);
dds_topic_allow_set_qos (tp);
dds_topic_unpin (tp);
dds_publisher_unlock (pub);
return writer;
err_bad_qos:
dds_topic_allow_set_qos (tp);
err_pp_mismatch:
dds_topic_unlock (tp);
err_tp_lock:
dds_topic_unpin (tp);
err_pin_topic:
dds_publisher_unlock (pub);
if ((pub->m_entity.m_flags & DDS_ENTITY_IMPLICIT) != 0)
if (created_implicit_pub)
(void) dds_delete (publisher);
return rc;
}
@ -395,6 +388,16 @@ 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)
{
/* 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);
}
DDS_GET_STATUS(writer, publication_matched, PUBLICATION_MATCHED, total_count_change, current_count_change)
DDS_GET_STATUS(writer, liveliness_lost, LIVELINESS_LOST, total_count_change)
DDS_GET_STATUS(writer, offered_deadline_missed, OFFERED_DEADLINE_MISSED, total_count_change)

View file

@ -30,6 +30,7 @@ set(ddsc_test_sources
"instance_get_key.c"
"listener.c"
"liveliness.c"
"multi_sertopic.c"
"participant.c"
"publisher.c"
"qos.c"
@ -51,6 +52,7 @@ set(ddsc_test_sources
"unsupported.c"
"waitset.c"
"waitset_torture.c"
"whc.c"
"write.c"
"write_various_types.c"
"writer.c")
@ -59,6 +61,10 @@ if(ENABLE_LIFESPAN)
list(APPEND ddsc_test_sources "lifespan.c")
endif()
if(ENABLE_DEADLINE_MISSED)
list(APPEND ddsc_test_sources "deadline.c")
endif()
add_cunit_executable(cunit_ddsc ${ddsc_test_sources})
target_include_directories(
cunit_ddsc PRIVATE

View file

@ -22,6 +22,12 @@ module Space {
long long_3;
};
#pragma keylist Type2 long_1
struct Type3 {
long long_1;
long long_2;
long long_3;
};
#pragma keylist Type3
struct simpletypes {
long l;

View file

@ -20,7 +20,7 @@
#include "dds/ddsrt/environ.h"
#include "dds/ddsrt/heap.h"
#include "dds/ddsi/q_misc.h"
#include "dds/ddsi/q_xqos.h"
#include "dds/ddsi/ddsi_xqos.h"
#define FORCE_ENV

View file

@ -0,0 +1,484 @@
/*
* Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
* v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
#include <assert.h>
#include <limits.h>
#include "dds/dds.h"
#include "CUnit/Theory.h"
#include "Space.h"
#include "dds/ddsrt/process.h"
#include "dds/ddsrt/threads.h"
#include "dds/ddsrt/environ.h"
#include "dds/ddsi/ddsi_entity_index.h"
#include "dds/ddsi/q_entity.h"
#include "dds/ddsi/q_whc.h"
#include "dds__entity.h"
#define MAX_RUNS 4
#define WRITER_DEADLINE DDS_MSECS(50)
#define DDS_DOMAINID_PUB 0
#define DDS_DOMAINID_SUB 1
#define DDS_CONFIG_NO_PORT_GAIN "${CYCLONEDDS_URI}${CYCLONEDDS_URI:+,}<Discovery><ExternalDomainId>0</ExternalDomainId></Discovery>"
static dds_entity_t g_domain = 0;
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_qos_t *g_qos;
static dds_entity_t g_remote_domain = 0;
static dds_entity_t g_remote_participant = 0;
static dds_entity_t g_remote_subscriber = 0;
static dds_entity_t g_remote_topic = 0;
static char * create_topic_name(const char *prefix, char *name, size_t size)
{
ddsrt_pid_t pid = ddsrt_getpid();
ddsrt_tid_t tid = ddsrt_gettid();
(void) snprintf(name, size, "%s_pid%"PRIdPID"_tid%"PRIdTID"", prefix, pid, tid);
return name;
}
static void sync_reader_writer(dds_entity_t participant, dds_entity_t reader, dds_entity_t writer)
{
dds_attach_t triggered;
dds_return_t ret;
dds_entity_t waitset_rd = dds_create_waitset(participant);
CU_ASSERT_FATAL(waitset_rd > 0);
dds_entity_t waitset_wr = dds_create_waitset(g_participant);
CU_ASSERT_FATAL(waitset_wr > 0);
/* Sync reader to writer. */
ret = dds_set_status_mask(reader, DDS_SUBSCRIPTION_MATCHED_STATUS);
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
ret = dds_waitset_attach(waitset_rd, reader, reader);
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
ret = dds_waitset_wait(waitset_rd, &triggered, 1, DDS_SECS(1));
CU_ASSERT_EQUAL_FATAL(ret, 1);
CU_ASSERT_EQUAL_FATAL(reader, (dds_entity_t)(intptr_t)triggered);
ret = dds_waitset_detach(waitset_rd, reader);
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
dds_delete(waitset_rd);
/* Sync writer to reader. */
ret = dds_set_status_mask(writer, DDS_PUBLICATION_MATCHED_STATUS);
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
ret = dds_waitset_attach(waitset_wr, writer, writer);
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
ret = dds_waitset_wait(waitset_wr, &triggered, 1, DDS_SECS(1));
CU_ASSERT_EQUAL_FATAL(ret, 1);
CU_ASSERT_EQUAL_FATAL(writer, (dds_entity_t)(intptr_t)triggered);
ret = dds_waitset_detach(waitset_wr, writer);
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
dds_delete(waitset_wr);
}
static dds_entity_t create_and_sync_reader(dds_entity_t participant, dds_entity_t subscriber, dds_entity_t topic, dds_qos_t *qos, dds_entity_t writer)
{
dds_entity_t reader = dds_create_reader(subscriber, topic, qos, NULL);
CU_ASSERT_FATAL(reader > 0);
sync_reader_writer (participant, reader, writer);
dds_return_t ret = dds_set_status_mask(reader, DDS_REQUESTED_DEADLINE_MISSED_STATUS);
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
return reader;
}
static void deadline_init(void)
{
char name[100];
/* Domains for pub and sub use a different domain id, but the portgain setting
* in configuration is 0, so that both domains will map to the same port number.
* This allows to create two domains in a single test process. */
char *conf_pub = ddsrt_expand_envvars(DDS_CONFIG_NO_PORT_GAIN, DDS_DOMAINID_PUB);
char *conf_sub = ddsrt_expand_envvars(DDS_CONFIG_NO_PORT_GAIN, DDS_DOMAINID_SUB);
g_domain = dds_create_domain(DDS_DOMAINID_PUB, conf_pub);
g_remote_domain = dds_create_domain(DDS_DOMAINID_SUB, conf_sub);
dds_free(conf_pub);
dds_free(conf_sub);
g_qos = dds_create_qos();
CU_ASSERT_PTR_NOT_NULL_FATAL(g_qos);
g_participant = dds_create_participant(DDS_DOMAINID_PUB, NULL, NULL);
CU_ASSERT_FATAL(g_participant > 0);
g_remote_participant = dds_create_participant(DDS_DOMAINID_SUB, NULL, NULL);
CU_ASSERT_FATAL(g_remote_participant > 0);
g_subscriber = dds_create_subscriber(g_participant, NULL, NULL);
CU_ASSERT_FATAL(g_subscriber > 0);
g_remote_subscriber = dds_create_subscriber(g_remote_participant, NULL, NULL);
CU_ASSERT_FATAL(g_remote_subscriber > 0);
g_publisher = dds_create_publisher(g_participant, NULL, NULL);
CU_ASSERT_FATAL(g_publisher > 0);
create_topic_name("ddsc_qos_deadline_test", name, sizeof name);
g_topic = dds_create_topic(g_participant, &Space_Type1_desc, name, NULL, NULL);
CU_ASSERT_FATAL(g_topic > 0);
g_remote_topic = dds_create_topic(g_remote_participant, &Space_Type1_desc, name, NULL, NULL);
CU_ASSERT_FATAL(g_remote_topic > 0);
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);
}
static void deadline_fini(void)
{
dds_delete_qos(g_qos);
dds_delete(g_subscriber);
dds_delete(g_remote_subscriber);
dds_delete(g_publisher);
dds_delete(g_topic);
dds_delete(g_participant);
dds_delete(g_remote_participant);
dds_delete(g_domain);
dds_delete(g_remote_domain);
}
static void msg(const char *msg, ...)
{
va_list args;
dds_time_t t;
t = dds_time();
printf("%d.%06d ", (int32_t)(t / DDS_NSECS_IN_SEC), (int32_t)(t % DDS_NSECS_IN_SEC) / 1000);
va_start(args, msg);
vprintf(msg, args);
va_end(args);
printf("\n");
}
static void sleepfor(dds_duration_t sleep_dur)
{
dds_sleepfor (sleep_dur);
msg("after sleeping %"PRId64, sleep_dur);
}
static bool check_missed_deadline_reader(dds_entity_t reader, uint32_t exp_missed_total, int32_t exp_missed_change)
{
struct dds_requested_deadline_missed_status dstatus;
dds_return_t ret = dds_get_requested_deadline_missed_status(reader, &dstatus);
CU_ASSERT_EQUAL_FATAL (ret, DDS_RETCODE_OK);
msg("- check reader total actual %u == expected %u / change actual %d == expected %d", dstatus.total_count, exp_missed_total, dstatus.total_count_change, exp_missed_change);
return dstatus.total_count == exp_missed_total && dstatus.total_count_change == exp_missed_change;
}
static bool check_missed_deadline_writer(dds_entity_t writer, uint32_t exp_missed_total, int32_t exp_missed_change)
{
struct dds_offered_deadline_missed_status dstatus;
dds_return_t ret = dds_get_offered_deadline_missed_status(writer, &dstatus);
CU_ASSERT_EQUAL_FATAL (ret, DDS_RETCODE_OK);
msg("- check writer total actual %u == expected %u / change actual %d == expected %d", dstatus.total_count, exp_missed_total, dstatus.total_count_change, exp_missed_change);
return dstatus.total_count == exp_missed_total && dstatus.total_count_change == exp_missed_change;
}
CU_Test(ddsc_deadline, basic, .init=deadline_init, .fini=deadline_fini)
{
Space_Type1 sample = { 0, 0, 0 };
dds_entity_t reader, reader_remote, reader_dl, reader_dl_remote, writer;
dds_return_t ret;
dds_duration_t deadline_dur = WRITER_DEADLINE;
uint32_t run = 1;
bool test_finished = false;
do
{
msg("deadline test: duration %"PRId64, deadline_dur);
dds_qset_deadline(g_qos, deadline_dur);
writer = dds_create_writer(g_publisher, g_topic, g_qos, NULL);
CU_ASSERT_FATAL(writer > 0);
reader_dl = create_and_sync_reader(g_participant, g_subscriber, g_topic, g_qos, writer);
reader_dl_remote = create_and_sync_reader(g_remote_participant, g_remote_subscriber, g_remote_topic, g_qos, writer);
dds_qset_deadline(g_qos, DDS_INFINITY);
reader = create_and_sync_reader(g_participant, g_subscriber, g_topic, g_qos, writer);
reader_remote = create_and_sync_reader(g_remote_participant, g_remote_subscriber, g_remote_topic, g_qos, writer);
ret = dds_set_status_mask(writer, DDS_OFFERED_DEADLINE_MISSED_STATUS);
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
/* Write first sample */
msg("write sample 1");
ret = dds_write (writer, &sample);
CU_ASSERT_EQUAL_FATAL (ret, DDS_RETCODE_OK);
/* Sleep 0.5 * deadline_dur: expect no missed deadlines for reader and writer */
sleepfor(deadline_dur / 2);
if (!check_missed_deadline_reader(reader, 0, 0) ||
!check_missed_deadline_reader(reader_remote, 0, 0) ||
!check_missed_deadline_reader(reader_dl, 0, 0) ||
!check_missed_deadline_reader(reader_dl_remote, 0, 0) ||
!check_missed_deadline_writer(writer, 0, 0))
deadline_dur *= 10 / (run + 1);
else
{
/* Write another sample */
msg("write sample 2");
ret = dds_write (writer, &sample);
CU_ASSERT_EQUAL_FATAL (ret, DDS_RETCODE_OK);
/* Sleep 0.5 * deadline_dur: expect no missed deadlines for reader and writer */
sleepfor(deadline_dur / 2);
if (!check_missed_deadline_reader(reader, 0, 0) ||
!check_missed_deadline_reader(reader_remote, 0, 0) ||
!check_missed_deadline_reader(reader_dl, 0, 0) ||
!check_missed_deadline_reader(reader_dl_remote, 0, 0) ||
!check_missed_deadline_writer(writer, 0, 0))
deadline_dur *= 10 / (run + 1);
else
{
/* Sleep deadline_dur: expect deadline reader to have 1 missed deadline */
sleepfor(deadline_dur);
if (!check_missed_deadline_reader(reader, 0, 0) ||
!check_missed_deadline_reader(reader_remote, 0, 0) ||
!check_missed_deadline_reader(reader_dl, 1, 1) ||
!check_missed_deadline_reader(reader_dl_remote, 1, 1) ||
!check_missed_deadline_writer(writer, 1, 1))
deadline_dur *= 10 / (run + 1);
else
{
/* Sleep another 2 * deadline_duration: expect 2 new triggers for missed deadline for both reader and writer */
sleepfor(2 * deadline_dur);
if (!check_missed_deadline_reader(reader, 0, 0) ||
!check_missed_deadline_reader(reader_remote, 0, 0) ||
!check_missed_deadline_reader(reader_dl, 3, 2) ||
!check_missed_deadline_reader(reader_dl_remote, 3, 2) ||
!check_missed_deadline_writer(writer, 3, 2))
deadline_dur *= 10 / (run + 1);
else
test_finished = true;
}
}
}
dds_delete(reader);
dds_delete(writer);
if (!test_finished)
{
if (++run > MAX_RUNS)
{
msg("run limit reached, test failed");
CU_FAIL_FATAL("Run limit reached");
test_finished = true;
}
else
{
msg("restarting test with deadline duration %"PRId64, deadline_dur);
sleepfor(deadline_dur);
}
}
} while (!test_finished);
}
#define V DDS_DURABILITY_VOLATILE
#define TL DDS_DURABILITY_TRANSIENT_LOCAL
#define R DDS_RELIABILITY_RELIABLE
#define BE DDS_RELIABILITY_BEST_EFFORT
#define KA DDS_HISTORY_KEEP_ALL
#define KL DDS_HISTORY_KEEP_LAST
CU_TheoryDataPoints(ddsc_deadline, writer_types) = {
CU_DataPoints(dds_durability_kind_t, V, V, V, V, TL, TL, TL, TL),
CU_DataPoints(dds_reliability_kind_t, BE, BE, R, R, BE, BE, R, R),
CU_DataPoints(dds_history_kind_t, KA, KL, KA, KL, KA, KL, KA, KL)
};
#undef V
#undef TL
#undef R
#undef BE
#undef KA
#undef KL
CU_Theory((dds_durability_kind_t dur_kind, dds_reliability_kind_t rel_kind, dds_history_kind_t hist_kind), ddsc_deadline, writer_types, .init = deadline_init, .fini = deadline_fini)
{
Space_Type1 sample = { 0, 0, 0 };
dds_entity_t reader, writer;
dds_qos_t *qos;
dds_return_t ret;
void * samples[1];
dds_sample_info_t info;
Space_Type1 rd_sample;
samples[0] = &rd_sample;
struct dds_offered_deadline_missed_status dstatus;
uint32_t run = 1;
dds_duration_t deadline_dur = WRITER_DEADLINE;
bool test_finished = false;
do
{
msg("deadline test: duration %"PRId64", writer type %d %d %s", deadline_dur, dur_kind, rel_kind, hist_kind == DDS_HISTORY_KEEP_ALL ? "all" : "1");
qos = dds_create_qos();
CU_ASSERT_PTR_NOT_NULL_FATAL(qos);
dds_qset_durability(qos, dur_kind);
dds_qset_reliability(qos, rel_kind, DDS_INFINITY);
dds_qset_history(qos, hist_kind, (hist_kind == DDS_HISTORY_KEEP_ALL) ? 0 : 1);
dds_qset_deadline(qos, deadline_dur);
writer = dds_create_writer(g_publisher, g_topic, qos, NULL);
CU_ASSERT_FATAL(writer > 0);
reader = create_and_sync_reader(g_participant, g_subscriber, g_topic, qos, writer);
/* Set status mask on writer to get offered deadline missed status */
ret = dds_set_status_mask(writer, DDS_OFFERED_DEADLINE_MISSED_STATUS);
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
/* Write sample */
ret = dds_write (writer, &sample);
CU_ASSERT_EQUAL_FATAL (ret, DDS_RETCODE_OK);
/* Take sample */
ret = dds_take (reader, samples, &info, 1, 1);
CU_ASSERT_EQUAL_FATAL (ret, 1);
/* Sleep 2 * deadline_dur: expect missed deadlines for writer */
sleepfor(2 * deadline_dur);
ret = dds_get_offered_deadline_missed_status(writer, &dstatus);
CU_ASSERT_EQUAL_FATAL (ret, DDS_RETCODE_OK);
msg("- check writer total actual %u > 0 / change actual %d > 0", dstatus.total_count, dstatus.total_count_change);
if (dstatus.total_count == 0 || dstatus.total_count_change == 0)
deadline_dur *= 10 / (run + 1);
else
{
uint32_t prev_cnt = dstatus.total_count;
/* Sleep 3 * deadline_dur: expect more missed deadlines for writer */
sleepfor(3 * deadline_dur);
ret = dds_get_offered_deadline_missed_status(writer, &dstatus);
CU_ASSERT_EQUAL_FATAL (ret, DDS_RETCODE_OK);
msg("- check reader total actual %u > expected %u / change actual %d > 0", dstatus.total_count, prev_cnt, dstatus.total_count_change);
if (dstatus.total_count <= prev_cnt || dstatus.total_count_change == 0)
deadline_dur *= 10 / (run + 1);
else
test_finished = true;
}
dds_delete_qos(qos);
dds_delete(reader);
dds_delete(writer);
if (!test_finished)
{
if (++run > MAX_RUNS)
{
msg("run limit reached, test failed");
CU_FAIL_FATAL("Run limit reached");
test_finished = true;
}
else
{
msg("restarting test with deadline duration %"PRId64, deadline_dur);
sleepfor(deadline_dur);
}
}
} while (!test_finished);
}
CU_TheoryDataPoints(ddsc_deadline, instances) = {
CU_DataPoints(int32_t, 1, 10, 10, 100), /* instance count */
CU_DataPoints(uint8_t, 0, 0, 4, 10), /* unregister every n-th instance */
CU_DataPoints(uint8_t, 0, 0, 5, 20), /* dispose every n-th instance */
};
CU_Theory((int32_t n_inst, uint8_t unreg_nth, uint8_t dispose_nth), ddsc_deadline, instances, .init = deadline_init, .fini = deadline_fini, .timeout = 60)
{
Space_Type1 sample = { 0, 0, 0 };
dds_entity_t reader_dl, writer;
dds_return_t ret;
int32_t n, n_unreg, n_dispose, n_alive, run = 1;
bool test_finished = false;
dds_duration_t deadline_dur = WRITER_DEADLINE;
do
{
msg("deadline test: duration %"PRId64", instance count %d, unreg %dth, dispose %dth", deadline_dur, n_inst, unreg_nth, dispose_nth);
dds_qset_deadline(g_qos, deadline_dur);
CU_ASSERT_PTR_NOT_NULL_FATAL(g_qos);
writer = dds_create_writer(g_publisher, g_topic, g_qos, NULL);
CU_ASSERT_FATAL(writer > 0);
reader_dl = create_and_sync_reader(g_participant, g_subscriber, g_topic, g_qos, writer);
/* Write first sample for each instance */
n_unreg = n_dispose = 0;
for (n = 1; n <= n_inst; n++)
{
sample.long_1 = n;
ret = dds_write (writer, &sample);
CU_ASSERT_EQUAL_FATAL (ret, DDS_RETCODE_OK);
if (unreg_nth && n % unreg_nth == 0)
{
ret = dds_unregister_instance (writer, &sample);
CU_ASSERT_EQUAL_FATAL (ret, DDS_RETCODE_OK);
n_unreg++;
}
else if (dispose_nth && n % dispose_nth == 0)
{
ret = dds_dispose (writer, &sample);
CU_ASSERT_EQUAL_FATAL (ret, DDS_RETCODE_OK);
n_dispose++;
}
}
n_alive = n_inst - n_dispose - n_unreg;
/* Sleep deadline_dur + 50% and check missed deadline count */
sleepfor(3 * deadline_dur / 2);
if (!check_missed_deadline_reader(reader_dl, (uint32_t)n_alive, n_alive))
deadline_dur *= 10 / (run + 1);
else
{
/* Sleep another deadline_dur: expect new trigger for missed deadline for all non-disposed instances */
sleepfor(deadline_dur);
if (!check_missed_deadline_reader(reader_dl, 2 * (uint32_t)n_alive, n_alive))
deadline_dur *= 10 / (run + 1);
else
{
/* Write second sample for all (including disposed) instances */
for (n = 1; n <= n_inst; n++)
{
sample.long_1 = n;
ret = dds_write (writer, &sample);
CU_ASSERT_EQUAL_FATAL (ret, DDS_RETCODE_OK);
}
/* Sleep deadline_dur + 25%: expect new trigger for missed deadline for non-disposed instances */
sleepfor(5 * deadline_dur / 4);
if (!check_missed_deadline_reader(reader_dl, 2 * (uint32_t)n_alive + (uint32_t)n_inst, n_inst))
deadline_dur *= 10 / (run + 1);
else
test_finished = true;
}
}
dds_delete(reader_dl);
dds_delete(writer);
if (!test_finished)
{
if (++run > MAX_RUNS)
{
msg("run limit reached, test failed");
CU_FAIL_FATAL("Run limit reached");
test_finished = true;
}
else
{
msg("restarting test with deadline duration %"PRId64, deadline_dur);
sleepfor(deadline_dur);
}
}
} while (!test_finished);
}

View file

@ -153,10 +153,11 @@ CU_TheoryDataPoints(ddsc_liveliness, pmd_count) = {
#undef MT
#undef MP
#undef A
CU_Theory((dds_liveliness_kind_t kind, uint32_t ldur, double mult), ddsc_liveliness, pmd_count, .init = liveliness_init, .fini = liveliness_fini, .timeout = 30)
static void test_pmd_count(dds_liveliness_kind_t kind, uint32_t ldur, double mult, bool remote_reader)
{
dds_entity_t pub_topic;
dds_entity_t sub_topic;
dds_entity_t sub_topic = 0;
dds_entity_t reader;
dds_entity_t writer;
seqno_t start_seqno, end_seqno;
@ -169,9 +170,9 @@ CU_Theory((dds_liveliness_kind_t kind, uint32_t ldur, double mult), ddsc_livelin
dds_time_t t;
t = dds_time();
printf("%d.%06d running test: kind %s, lease duration %d, delay %d\n",
printf("%d.%06d running test: kind %s, lease duration %d, delay %d, %s reader\n",
(int32_t)(t / DDS_NSECS_IN_SEC), (int32_t)(t % DDS_NSECS_IN_SEC) / 1000,
kind == 0 ? "A" : "MP", ldur, (int32_t)(mult * ldur));
kind == 0 ? "A" : "MP", ldur, (int32_t)(mult * ldur), remote_reader ? "remote" : "local");
/* wait for initial PMD to be sent by the participant */
while (get_pmd_seqno(g_pub_participant) < 1)
@ -180,17 +181,18 @@ CU_Theory((dds_liveliness_kind_t kind, uint32_t ldur, double mult), ddsc_livelin
/* topics */
create_topic_name("ddsc_liveliness_pmd_count", g_topic_nr++, name, sizeof name);
CU_ASSERT_FATAL((pub_topic = dds_create_topic(g_pub_participant, &Space_Type1_desc, name, NULL, NULL)) > 0);
CU_ASSERT_FATAL((sub_topic = dds_create_topic(g_sub_participant, &Space_Type1_desc, name, NULL, NULL)) > 0);
if (remote_reader)
CU_ASSERT_FATAL((sub_topic = dds_create_topic(g_sub_participant, &Space_Type1_desc, name, NULL, NULL)) > 0);
/* reader */
CU_ASSERT_FATAL((rqos = dds_create_qos()) != NULL);
dds_qset_liveliness(rqos, DDS_LIVELINESS_AUTOMATIC, DDS_INFINITY);
CU_ASSERT_FATAL((reader = dds_create_reader(g_sub_participant, sub_topic, rqos, NULL)) > 0);
CU_ASSERT_FATAL((reader = dds_create_reader(remote_reader ? g_sub_participant : g_pub_participant, remote_reader ? sub_topic : pub_topic, rqos, NULL)) > 0);
dds_delete_qos(rqos);
CU_ASSERT_EQUAL_FATAL(dds_set_status_mask(reader, DDS_LIVELINESS_CHANGED_STATUS), DDS_RETCODE_OK);
/* waitset on reader */
CU_ASSERT_FATAL((waitset = dds_create_waitset(g_sub_participant)) > 0);
CU_ASSERT_FATAL((waitset = dds_create_waitset(remote_reader ? g_sub_participant : g_pub_participant)) > 0);
CU_ASSERT_EQUAL_FATAL(dds_waitset_attach(waitset, reader, reader), DDS_RETCODE_OK);
/* writer */
@ -216,17 +218,24 @@ CU_Theory((dds_liveliness_kind_t kind, uint32_t ldur, double mult), ddsc_livelin
/* End-start should be mult - 1 under ideal circumstances, but consider the test successful
when at least 50% of the expected PMD's was sent. This checks that the frequency for sending
PMDs was increased when the writer was added. */
CU_ASSERT(end_seqno - start_seqno >= (kind == DDS_LIVELINESS_AUTOMATIC ? (50 * (mult - 1)) / 100 : 0))
CU_ASSERT_FATAL(end_seqno - start_seqno >= (kind == DDS_LIVELINESS_AUTOMATIC ? (50 * (mult - 1)) / 100 : 0))
if (kind != DDS_LIVELINESS_AUTOMATIC)
CU_ASSERT(get_pmd_seqno(g_pub_participant) - start_seqno < mult)
CU_ASSERT_FATAL(get_pmd_seqno(g_pub_participant) - start_seqno < mult)
/* cleanup */
CU_ASSERT_EQUAL_FATAL(dds_delete(sub_topic), DDS_RETCODE_OK);
if (remote_reader)
CU_ASSERT_EQUAL_FATAL(dds_delete(sub_topic), DDS_RETCODE_OK);
CU_ASSERT_EQUAL_FATAL(dds_delete(pub_topic), DDS_RETCODE_OK);
CU_ASSERT_EQUAL_FATAL(dds_delete(writer), DDS_RETCODE_OK);
CU_ASSERT_EQUAL_FATAL(dds_delete(reader), DDS_RETCODE_OK);
}
CU_Theory((dds_liveliness_kind_t kind, uint32_t ldur, double mult), ddsc_liveliness, pmd_count, .init = liveliness_init, .fini = liveliness_fini, .timeout = 30)
{
test_pmd_count(kind, ldur, mult, false);
test_pmd_count(kind, ldur, mult, true);
}
/**
* Test that the expected number of proxy writers expires (set to not-alive)
* after a certain delay for various combinations of writers with different
@ -239,10 +248,11 @@ CU_TheoryDataPoints(ddsc_liveliness, expire_liveliness_kinds) = {
CU_DataPoints(uint32_t, 1, 1, 2, 2, 0, 0, 0, 1, 0, 2, 2, 5, 10, 0, 15), /* number of writers with manual-by-participant liveliness */
CU_DataPoints(uint32_t, 1, 1, 2, 2, 1, 1, 1, 1, 0, 1, 1, 2, 5, 0, 10), /* number of writers with manual-by-topic liveliness */
};
CU_Theory((uint32_t ldur, double mult, uint32_t wr_cnt_auto, uint32_t wr_cnt_man_pp, uint32_t wr_cnt_man_tp), ddsc_liveliness, expire_liveliness_kinds, .init = liveliness_init, .fini = liveliness_fini, .timeout = 120)
static void test_expire_liveliness_kinds(uint32_t ldur, double mult, uint32_t wr_cnt_auto, uint32_t wr_cnt_man_pp, uint32_t wr_cnt_man_tp, bool remote_reader)
{
dds_entity_t pub_topic;
dds_entity_t sub_topic;
dds_entity_t sub_topic = 0;
dds_entity_t reader;
dds_entity_t *writers;
dds_qos_t *rqos, *wqos_auto, *wqos_man_pp, *wqos_man_tp;
@ -257,19 +267,20 @@ CU_Theory((uint32_t ldur, double mult, uint32_t wr_cnt_auto, uint32_t wr_cnt_man
do
{
tstart = dds_time();
printf("%d.%06d running test: lease duration %d, delay %f, auto/man-by-part/man-by-topic %u/%u/%u\n",
printf("%d.%06d running test: lease duration %d, delay %f, auto/man-by-part/man-by-topic %u/%u/%u\n, %s reader",
(int32_t)(tstart / DDS_NSECS_IN_SEC), (int32_t)(tstart % DDS_NSECS_IN_SEC) / 1000,
ldur, mult, wr_cnt_auto, wr_cnt_man_pp, wr_cnt_man_tp);
ldur, mult, wr_cnt_auto, wr_cnt_man_pp, wr_cnt_man_tp, remote_reader ? "remote" : "local");
/* topics */
create_topic_name("ddsc_liveliness_expire_kinds", g_topic_nr++, name, sizeof name);
CU_ASSERT_FATAL((pub_topic = dds_create_topic(g_pub_participant, &Space_Type1_desc, name, NULL, NULL)) > 0);
CU_ASSERT_FATAL((sub_topic = dds_create_topic(g_sub_participant, &Space_Type1_desc, name, NULL, NULL)) > 0);
if (remote_reader)
CU_ASSERT_FATAL((sub_topic = dds_create_topic(g_sub_participant, &Space_Type1_desc, name, NULL, NULL)) > 0);
/* reader */
CU_ASSERT_FATAL((rqos = dds_create_qos()) != NULL);
dds_qset_liveliness(rqos, DDS_LIVELINESS_AUTOMATIC, DDS_INFINITY);
CU_ASSERT_FATAL((reader = dds_create_reader(g_sub_participant, sub_topic, rqos, NULL)) > 0);
CU_ASSERT_FATAL((reader = dds_create_reader(remote_reader ? g_sub_participant : g_pub_participant, remote_reader ? sub_topic : pub_topic, rqos, NULL)) > 0);
dds_delete_qos(rqos);
CU_ASSERT_EQUAL_FATAL(dds_set_status_mask(reader, DDS_LIVELINESS_CHANGED_STATUS), DDS_RETCODE_OK);
@ -281,7 +292,7 @@ CU_Theory((uint32_t ldur, double mult, uint32_t wr_cnt_auto, uint32_t wr_cnt_man
CU_ASSERT_FATAL((wqos_man_tp = dds_create_qos()) != NULL);
dds_qset_liveliness(wqos_man_tp, DDS_LIVELINESS_MANUAL_BY_TOPIC, DDS_MSECS(ldur));
CU_ASSERT_FATAL((waitset = dds_create_waitset(g_sub_participant)) > 0);
CU_ASSERT_FATAL((waitset = dds_create_waitset(remote_reader ? g_sub_participant : g_pub_participant)) > 0);
CU_ASSERT_EQUAL_FATAL(dds_waitset_attach(waitset, reader, reader), DDS_RETCODE_OK);
writers = dds_alloc(wr_cnt * sizeof(dds_entity_t));
@ -347,7 +358,8 @@ CU_Theory((uint32_t ldur, double mult, uint32_t wr_cnt_auto, uint32_t wr_cnt_man
for (n = 0; n < wr_cnt; n++)
CU_ASSERT_EQUAL_FATAL(dds_delete(writers[n]), DDS_RETCODE_OK);
dds_free(writers);
CU_ASSERT_EQUAL_FATAL(dds_delete(sub_topic), DDS_RETCODE_OK);
if (remote_reader)
CU_ASSERT_EQUAL_FATAL(dds_delete(sub_topic), DDS_RETCODE_OK);
CU_ASSERT_EQUAL_FATAL(dds_delete(pub_topic), DDS_RETCODE_OK);
CU_ASSERT_EQUAL_FATAL(dds_delete(reader), DDS_RETCODE_OK);
@ -369,14 +381,21 @@ CU_Theory((uint32_t ldur, double mult, uint32_t wr_cnt_auto, uint32_t wr_cnt_man
} while (!test_finished);
}
static void add_and_check_writer(dds_liveliness_kind_t kind, dds_duration_t ldur, dds_entity_t *writer, dds_entity_t topic, dds_entity_t reader)
CU_Theory((uint32_t ldur, double mult, uint32_t wr_cnt_auto, uint32_t wr_cnt_man_pp, uint32_t wr_cnt_man_tp), ddsc_liveliness, expire_liveliness_kinds, .init = liveliness_init, .fini = liveliness_fini, .timeout = 120)
{
test_expire_liveliness_kinds (ldur, mult, wr_cnt_auto, wr_cnt_man_pp, wr_cnt_man_tp, false);
test_expire_liveliness_kinds (ldur, mult, wr_cnt_auto, wr_cnt_man_pp, wr_cnt_man_tp, true);
}
static void add_and_check_writer(dds_liveliness_kind_t kind, dds_duration_t ldur, dds_entity_t *writer, dds_entity_t topic, dds_entity_t reader, bool remote_reader)
{
dds_entity_t waitset;
dds_qos_t *wqos;
dds_attach_t triggered;
uint32_t status;
CU_ASSERT_FATAL((waitset = dds_create_waitset(g_sub_participant)) > 0);
CU_ASSERT_FATAL((waitset = dds_create_waitset(remote_reader ? g_sub_participant : g_pub_participant)) > 0);
CU_ASSERT_EQUAL_FATAL(dds_waitset_attach(waitset, reader, reader), DDS_RETCODE_OK);
CU_ASSERT_FATAL((wqos = dds_create_qos()) != NULL);
@ -409,7 +428,7 @@ CU_Test(ddsc_liveliness, lease_duration, .init = liveliness_init, .fini = liveli
uint32_t n;
/* topics */
create_topic_name("ddsc_liveliness_ldur", 1, name, sizeof name);
create_topic_name("ddsc_liveliness_ldur", g_topic_nr++, name, sizeof name);
CU_ASSERT_FATAL((pub_topic = dds_create_topic(g_pub_participant, &Space_Type1_desc, name, NULL, NULL)) > 0);
CU_ASSERT_FATAL((sub_topic = dds_create_topic(g_sub_participant, &Space_Type1_desc, name, NULL, NULL)) > 0);
@ -424,22 +443,22 @@ CU_Test(ddsc_liveliness, lease_duration, .init = liveliness_init, .fini = liveli
CU_ASSERT_EQUAL_FATAL(get_pmd_interval(g_pub_participant), get_ldur_config(g_pub_participant));
/* create writers and check pmd interval in publishing participant */
add_and_check_writer(DDS_LIVELINESS_AUTOMATIC, DDS_MSECS(1000), &writers[wr_cnt++], pub_topic, reader);
add_and_check_writer(DDS_LIVELINESS_AUTOMATIC, DDS_MSECS(1000), &writers[wr_cnt++], pub_topic, reader, true);
CU_ASSERT_EQUAL_FATAL(get_pmd_interval(g_pub_participant), DDS_MSECS(1000));
add_and_check_writer(DDS_LIVELINESS_AUTOMATIC, DDS_MSECS(2000), &writers[wr_cnt++], pub_topic, reader);
add_and_check_writer(DDS_LIVELINESS_AUTOMATIC, DDS_MSECS(2000), &writers[wr_cnt++], pub_topic, reader, true);
CU_ASSERT_EQUAL_FATAL(get_pmd_interval(g_pub_participant), DDS_MSECS(1000));
add_and_check_writer(DDS_LIVELINESS_MANUAL_BY_PARTICIPANT, DDS_MSECS(2000), &writers[wr_cnt++], pub_topic, reader);
add_and_check_writer(DDS_LIVELINESS_MANUAL_BY_PARTICIPANT, DDS_MSECS(2000), &writers[wr_cnt++], pub_topic, reader, true);
CU_ASSERT_EQUAL_FATAL(get_pmd_interval(g_pub_participant), DDS_MSECS(1000));
add_and_check_writer(DDS_LIVELINESS_AUTOMATIC, DDS_MSECS(500), &writers[wr_cnt++], pub_topic, reader);
add_and_check_writer(DDS_LIVELINESS_AUTOMATIC, DDS_MSECS(500), &writers[wr_cnt++], pub_topic, reader, true);
CU_ASSERT_EQUAL_FATAL(get_pmd_interval(g_pub_participant), DDS_MSECS(500));
add_and_check_writer(DDS_LIVELINESS_MANUAL_BY_PARTICIPANT, DDS_MSECS(100), &writers[wr_cnt++], pub_topic, reader);
add_and_check_writer(DDS_LIVELINESS_MANUAL_BY_PARTICIPANT, DDS_MSECS(100), &writers[wr_cnt++], pub_topic, reader, true);
CU_ASSERT_EQUAL_FATAL(get_pmd_interval(g_pub_participant), DDS_MSECS(500));
add_and_check_writer(DDS_LIVELINESS_MANUAL_BY_TOPIC, DDS_MSECS(100), &writers[wr_cnt++], pub_topic, reader);
add_and_check_writer(DDS_LIVELINESS_MANUAL_BY_TOPIC, DDS_MSECS(100), &writers[wr_cnt++], pub_topic, reader, true);
CU_ASSERT_EQUAL_FATAL(get_pmd_interval(g_pub_participant), DDS_MSECS(500));
/* cleanup */
@ -454,10 +473,10 @@ CU_Test(ddsc_liveliness, lease_duration, .init = liveliness_init, .fini = liveli
/**
* Check that the correct lease duration is set in the matched
* publications in the readers. */
CU_Test(ddsc_liveliness, lease_duration_pwr, .init = liveliness_init, .fini = liveliness_fini)
static void test_lease_duration_pwr(bool remote_reader)
{
dds_entity_t pub_topic;
dds_entity_t sub_topic;
dds_entity_t sub_topic = 0;
dds_entity_t reader;
dds_entity_t writer;
char name[100];
@ -467,15 +486,18 @@ CU_Test(ddsc_liveliness, lease_duration_pwr, .init = liveliness_init, .fini = li
uint32_t status;
dds_duration_t ldur;
printf("running test lease_duration_pwr: %s reader\n", remote_reader ? "remote" : "local");
/* topics */
create_topic_name("ddsc_liveliness_ldurpwr", 1, name, sizeof name);
create_topic_name("ddsc_liveliness_ldurpwr", g_topic_nr++, name, sizeof name);
CU_ASSERT_FATAL((pub_topic = dds_create_topic(g_pub_participant, &Space_Type1_desc, name, NULL, NULL)) > 0);
CU_ASSERT_FATAL((sub_topic = dds_create_topic(g_sub_participant, &Space_Type1_desc, name, NULL, NULL)) > 0);
if (remote_reader)
CU_ASSERT_FATAL((sub_topic = dds_create_topic(g_sub_participant, &Space_Type1_desc, name, NULL, NULL)) > 0);
/* reader */
CU_ASSERT_FATAL((rqos = dds_create_qos()) != NULL);
dds_qset_liveliness(rqos, DDS_LIVELINESS_AUTOMATIC, DDS_INFINITY);
CU_ASSERT_FATAL((reader = dds_create_reader(g_sub_participant, sub_topic, rqos, NULL)) > 0);
CU_ASSERT_FATAL((reader = dds_create_reader(remote_reader ? g_sub_participant : g_pub_participant, remote_reader ? sub_topic : pub_topic, rqos, NULL)) > 0);
dds_delete_qos(rqos);
CU_ASSERT_EQUAL_FATAL(dds_set_status_mask(reader, DDS_LIVELINESS_CHANGED_STATUS), DDS_RETCODE_OK);
@ -486,7 +508,7 @@ CU_Test(ddsc_liveliness, lease_duration_pwr, .init = liveliness_init, .fini = li
CU_ASSERT_FATAL((writer = dds_create_writer(g_pub_participant, pub_topic, wqos, NULL)) > 0);
/* wait for writer to be alive */
CU_ASSERT_FATAL((waitset = dds_create_waitset(g_sub_participant)) > 0);
CU_ASSERT_FATAL((waitset = dds_create_waitset(remote_reader ? g_sub_participant : g_pub_participant)) > 0);
CU_ASSERT_EQUAL_FATAL(dds_waitset_attach(waitset, reader, reader), DDS_RETCODE_OK);
CU_ASSERT_EQUAL_FATAL(dds_waitset_wait(waitset, &triggered, 1, DDS_MSECS(1000)), 1);
CU_ASSERT_EQUAL_FATAL(dds_take_status(reader, &status, DDS_LIVELINESS_CHANGED_STATUS), DDS_RETCODE_OK);
@ -509,11 +531,18 @@ CU_Test(ddsc_liveliness, lease_duration_pwr, .init = liveliness_init, .fini = li
CU_ASSERT_EQUAL_FATAL(dds_waitset_detach(waitset, reader), DDS_RETCODE_OK);
CU_ASSERT_EQUAL_FATAL(dds_delete(waitset), DDS_RETCODE_OK);
CU_ASSERT_EQUAL_FATAL(dds_delete(writer), DDS_RETCODE_OK);
CU_ASSERT_EQUAL_FATAL(dds_delete(sub_topic), DDS_RETCODE_OK);
if (remote_reader)
CU_ASSERT_EQUAL_FATAL(dds_delete(sub_topic), DDS_RETCODE_OK);
CU_ASSERT_EQUAL_FATAL(dds_delete(pub_topic), DDS_RETCODE_OK);
CU_ASSERT_EQUAL_FATAL(dds_delete(reader), DDS_RETCODE_OK);
}
CU_Test(ddsc_liveliness, lease_duration_pwr, .init = liveliness_init, .fini = liveliness_fini)
{
test_lease_duration_pwr(false);
test_lease_duration_pwr(true);
}
/**
* Create a relative large number of writers with liveliness kinds automatic and
* manual-by-participant and with decreasing lease duration, and check that all
@ -521,10 +550,11 @@ CU_Test(ddsc_liveliness, lease_duration_pwr, .init = liveliness_init, .fini = li
* is deleted immediately after creating.
*/
#define MAX_WRITERS 100
CU_Test(ddsc_liveliness, create_delete_writer_stress, .init = liveliness_init, .fini = liveliness_fini, .timeout = 15)
static void test_create_delete_writer_stress(bool remote_reader)
{
dds_entity_t pub_topic;
dds_entity_t sub_topic;
dds_entity_t sub_topic = 0;
dds_entity_t reader;
dds_entity_t writers[MAX_WRITERS];
dds_entity_t waitset;
@ -538,18 +568,21 @@ CU_Test(ddsc_liveliness, create_delete_writer_stress, .init = liveliness_init, .
Space_Type1 sample = {0, 0, 0};
int64_t ldur = 1000;
printf("running test create_delete_writer_stress: %s reader\n", remote_reader ? "remote" : "local");
/* topics */
create_topic_name("ddsc_liveliness_wr_stress", 1, name, sizeof name);
create_topic_name("ddsc_liveliness_wr_stress", g_topic_nr++, name, sizeof name);
CU_ASSERT_FATAL((pub_topic = dds_create_topic(g_pub_participant, &Space_Type1_desc, name, NULL, NULL)) > 0);
CU_ASSERT_FATAL((sub_topic = dds_create_topic(g_sub_participant, &Space_Type1_desc, name, NULL, NULL)) > 0);
if (remote_reader)
CU_ASSERT_FATAL((sub_topic = dds_create_topic(g_sub_participant, &Space_Type1_desc, name, NULL, NULL)) > 0);
/* reader and waitset */
CU_ASSERT_FATAL((rqos = dds_create_qos()) != NULL);
dds_qset_liveliness(rqos, DDS_LIVELINESS_AUTOMATIC, DDS_INFINITY);
CU_ASSERT_FATAL((reader = dds_create_reader(g_sub_participant, sub_topic, rqos, NULL)) > 0);
CU_ASSERT_FATAL((reader = dds_create_reader(remote_reader ? g_sub_participant : g_pub_participant, remote_reader ? sub_topic : pub_topic, rqos, NULL)) > 0);
dds_delete_qos(rqos);
CU_ASSERT_EQUAL_FATAL(dds_set_status_mask(reader, DDS_LIVELINESS_CHANGED_STATUS), DDS_RETCODE_OK);
CU_ASSERT_FATAL((waitset = dds_create_waitset(g_sub_participant)) > 0);
CU_ASSERT_FATAL((waitset = dds_create_waitset(remote_reader ? g_sub_participant : g_pub_participant)) > 0);
CU_ASSERT_EQUAL_FATAL(dds_waitset_attach(waitset, reader, reader), DDS_RETCODE_OK);
/* create 1st writer and wait for it to become alive */
@ -604,42 +637,53 @@ CU_Test(ddsc_liveliness, create_delete_writer_stress, .init = liveliness_init, .
CU_ASSERT_EQUAL_FATAL(dds_waitset_detach(waitset, reader), DDS_RETCODE_OK);
CU_ASSERT_EQUAL_FATAL(dds_delete(waitset), DDS_RETCODE_OK);
CU_ASSERT_EQUAL_FATAL(dds_delete(reader), DDS_RETCODE_OK);
CU_ASSERT_EQUAL_FATAL(dds_delete(sub_topic), DDS_RETCODE_OK);
if (remote_reader)
CU_ASSERT_EQUAL_FATAL(dds_delete(sub_topic), DDS_RETCODE_OK);
CU_ASSERT_EQUAL_FATAL(dds_delete(pub_topic), DDS_RETCODE_OK);
}
CU_Test(ddsc_liveliness, create_delete_writer_stress, .init = liveliness_init, .fini = liveliness_fini, .timeout = 15)
{
test_create_delete_writer_stress(false);
test_create_delete_writer_stress(true);
}
#undef MAX_WRITERS
/**
* Check the counts in liveliness_changed_status result.
*/
CU_Test(ddsc_liveliness, status_counts, .init = liveliness_init, .fini = liveliness_fini)
static void test_status_counts(bool remote_reader)
{
dds_entity_t pub_topic;
dds_entity_t sub_topic;
dds_entity_t sub_topic = 0;
dds_entity_t reader;
dds_entity_t writer;
dds_entity_t waitset;
dds_qos_t *rqos;
dds_qos_t *wqos;
dds_attach_t triggered;
struct dds_liveliness_changed_status lstatus;
struct dds_liveliness_changed_status lcstatus;
struct dds_liveliness_lost_status llstatus;
struct dds_subscription_matched_status sstatus;
char name[100];
dds_duration_t ldur = DDS_MSECS(500);
Space_Type1 sample = {1, 0, 0};
printf("running test status_counts: %s reader\n", remote_reader ? "remote" : "local");
/* topics */
create_topic_name("ddsc_liveliness_status_counts", g_topic_nr++, name, sizeof name);
CU_ASSERT_FATAL((pub_topic = dds_create_topic(g_pub_participant, &Space_Type1_desc, name, NULL, NULL)) > 0);
CU_ASSERT_FATAL((sub_topic = dds_create_topic(g_sub_participant, &Space_Type1_desc, name, NULL, NULL)) > 0);
if (remote_reader)
CU_ASSERT_FATAL((sub_topic = dds_create_topic(g_sub_participant, &Space_Type1_desc, name, NULL, NULL)) > 0);
/* reader */
CU_ASSERT_FATAL((rqos = dds_create_qos()) != NULL);
dds_qset_liveliness(rqos, DDS_LIVELINESS_AUTOMATIC, DDS_INFINITY);
CU_ASSERT_FATAL((reader = dds_create_reader(g_sub_participant, sub_topic, rqos, NULL)) > 0);
CU_ASSERT_FATAL((reader = dds_create_reader(remote_reader ? g_sub_participant : g_pub_participant, remote_reader ? sub_topic : pub_topic, rqos, NULL)) > 0);
dds_delete_qos(rqos);
CU_ASSERT_EQUAL_FATAL(dds_set_status_mask(reader, DDS_LIVELINESS_CHANGED_STATUS), DDS_RETCODE_OK);
CU_ASSERT_FATAL((waitset = dds_create_waitset(g_sub_participant)) > 0);
CU_ASSERT_FATAL((waitset = dds_create_waitset(remote_reader ? g_sub_participant : g_pub_participant)) > 0);
CU_ASSERT_EQUAL_FATAL(dds_waitset_attach(waitset, reader, reader), DDS_RETCODE_OK);
/* writer */
@ -647,56 +691,72 @@ CU_Test(ddsc_liveliness, status_counts, .init = liveliness_init, .fini = livelin
dds_qset_liveliness(wqos, DDS_LIVELINESS_MANUAL_BY_PARTICIPANT, ldur);
CU_ASSERT_FATAL((writer = dds_create_writer(g_pub_participant, pub_topic, wqos, NULL)) > 0);
dds_delete_qos(wqos);
CU_ASSERT_EQUAL_FATAL(dds_set_status_mask(writer, DDS_LIVELINESS_LOST_STATUS), DDS_RETCODE_OK);
/* wait for writer to be alive */
CU_ASSERT_EQUAL_FATAL(dds_waitset_wait(waitset, &triggered, 1, DDS_SECS(5)), 1);
/* check status counts before proxy writer is expired */
dds_get_liveliness_changed_status(reader, &lstatus);
CU_ASSERT_EQUAL_FATAL(lstatus.alive_count, 1);
dds_get_liveliness_changed_status(reader, &lcstatus);
CU_ASSERT_EQUAL_FATAL(lcstatus.alive_count, 1);
dds_get_subscription_matched_status(reader, &sstatus);
CU_ASSERT_EQUAL_FATAL(sstatus.current_count, 1);
dds_get_liveliness_lost_status(writer, &llstatus);
CU_ASSERT_EQUAL_FATAL(llstatus.total_count, 0);
/* sleep for more than lease duration, writer should be set not-alive but subscription still matched */
dds_sleepfor(ldur + DDS_MSECS(100));
CU_ASSERT_EQUAL_FATAL(dds_waitset_wait(waitset, &triggered, 1, DDS_SECS(5)), 1);
dds_get_liveliness_changed_status(reader, &lstatus);
CU_ASSERT_EQUAL_FATAL(lstatus.alive_count, 0);
dds_get_liveliness_changed_status(reader, &lcstatus);
CU_ASSERT_EQUAL_FATAL(lcstatus.alive_count, 0);
dds_get_subscription_matched_status(reader, &sstatus);
CU_ASSERT_EQUAL_FATAL(sstatus.current_count, 1);
dds_get_liveliness_lost_status(writer, &llstatus);
CU_ASSERT_EQUAL_FATAL(llstatus.total_count, 1);
CU_ASSERT_EQUAL_FATAL(llstatus.total_count_change, 1);
/* write sample and re-check status counts */
dds_write(writer, &sample);
CU_ASSERT_EQUAL_FATAL(dds_waitset_wait(waitset, &triggered, 1, DDS_SECS(5)), 1);
dds_get_liveliness_changed_status(reader, &lstatus);
CU_ASSERT_EQUAL_FATAL(lstatus.alive_count, 1);
dds_get_liveliness_changed_status(reader, &lcstatus);
CU_ASSERT_EQUAL_FATAL(lcstatus.alive_count, 1);
dds_get_subscription_matched_status(reader, &sstatus);
CU_ASSERT_EQUAL_FATAL(sstatus.current_count, 1);
dds_get_liveliness_lost_status(writer, &llstatus);
CU_ASSERT_EQUAL_FATAL(llstatus.total_count_change, 0);
/* cleanup */
CU_ASSERT_EQUAL_FATAL(dds_waitset_detach(waitset, reader), DDS_RETCODE_OK);
CU_ASSERT_EQUAL_FATAL(dds_delete(waitset), DDS_RETCODE_OK);
CU_ASSERT_EQUAL_FATAL(dds_delete(reader), DDS_RETCODE_OK);
CU_ASSERT_EQUAL_FATAL(dds_delete(writer), DDS_RETCODE_OK);
CU_ASSERT_EQUAL_FATAL(dds_delete(sub_topic), DDS_RETCODE_OK);
if (remote_reader)
CU_ASSERT_EQUAL_FATAL(dds_delete(sub_topic), DDS_RETCODE_OK);
CU_ASSERT_EQUAL_FATAL(dds_delete(pub_topic), DDS_RETCODE_OK);
}
CU_Test(ddsc_liveliness, status_counts, .init = liveliness_init, .fini = liveliness_fini)
{
test_status_counts(false);
test_status_counts(true);
}
/**
* Test that dds_assert_liveliness works as expected for liveliness
* kinds manual-by-participant and manual-by-topic.
*/
#define MAX_WRITERS 100
CU_TheoryDataPoints(ddsc_liveliness, assert_liveliness) = {
CU_DataPoints(uint32_t, 1, 0, 0, 1), /* number of writers with automatic liveliness */
CU_DataPoints(uint32_t, 1, 1, 0, 0), /* number of writers with manual-by-participant liveliness */
CU_DataPoints(uint32_t, 1, 1, 1, 2), /* number of writers with manual-by-topic liveliness */
CU_DataPoints(uint32_t, 1, 0, 0, 1, 0, 1, 2), /* number of writers with automatic liveliness */
CU_DataPoints(uint32_t, 0, 1, 0, 1, 1, 0, 2), /* number of writers with manual-by-participant liveliness */
CU_DataPoints(uint32_t, 0, 0, 1, 1, 2, 2, 0), /* number of writers with manual-by-topic liveliness */
};
CU_Theory((uint32_t wr_cnt_auto, uint32_t wr_cnt_man_pp, uint32_t wr_cnt_man_tp), ddsc_liveliness, assert_liveliness, .init = liveliness_init, .fini = liveliness_fini, .timeout = 60)
static void test_assert_liveliness(uint32_t wr_cnt_auto, uint32_t wr_cnt_man_pp, uint32_t wr_cnt_man_tp, bool remote_reader)
{
dds_entity_t pub_topic, sub_topic, reader, writers[MAX_WRITERS];
dds_entity_t pub_topic, sub_topic = 0, reader, writers[MAX_WRITERS];
dds_qos_t *rqos;
struct dds_liveliness_changed_status lstatus;
char name[100];
@ -708,29 +768,30 @@ CU_Theory((uint32_t wr_cnt_auto, uint32_t wr_cnt_man_pp, uint32_t wr_cnt_man_tp)
{
wr_cnt = 0;
assert(wr_cnt_auto + wr_cnt_man_pp + wr_cnt_man_tp < MAX_WRITERS);
printf("running test assert_liveliness: auto/man-by-part/man-by-topic %u/%u/%u with ldur %d\n",
wr_cnt_auto, wr_cnt_man_pp, wr_cnt_man_tp, ldur);
printf("running test assert_liveliness: auto/man-by-part/man-by-topic %u/%u/%u with ldur %d, %s reader\n",
wr_cnt_auto, wr_cnt_man_pp, wr_cnt_man_tp, ldur, remote_reader ? "remote" : "local");
/* topics */
create_topic_name("ddsc_liveliness_assert", g_topic_nr++, name, sizeof name);
CU_ASSERT_FATAL((pub_topic = dds_create_topic(g_pub_participant, &Space_Type1_desc, name, NULL, NULL)) > 0);
CU_ASSERT_FATAL((sub_topic = dds_create_topic(g_sub_participant, &Space_Type1_desc, name, NULL, NULL)) > 0);
if (remote_reader)
CU_ASSERT_FATAL((sub_topic = dds_create_topic(g_sub_participant, &Space_Type1_desc, name, NULL, NULL)) > 0);
/* reader */
CU_ASSERT_FATAL((rqos = dds_create_qos()) != NULL);
dds_qset_liveliness(rqos, DDS_LIVELINESS_AUTOMATIC, DDS_INFINITY);
CU_ASSERT_FATAL((reader = dds_create_reader(g_sub_participant, sub_topic, rqos, NULL)) > 0);
CU_ASSERT_FATAL((reader = dds_create_reader(remote_reader ? g_sub_participant : g_pub_participant, remote_reader ? sub_topic : pub_topic, rqos, NULL)) > 0);
dds_delete_qos(rqos);
CU_ASSERT_EQUAL_FATAL(dds_set_status_mask(reader, DDS_LIVELINESS_CHANGED_STATUS), DDS_RETCODE_OK);
/* writers */
for (size_t n = 0; n < wr_cnt_auto; n++)
add_and_check_writer(DDS_LIVELINESS_AUTOMATIC, DDS_MSECS(ldur), &writers[wr_cnt++], pub_topic, reader);
add_and_check_writer(DDS_LIVELINESS_AUTOMATIC, DDS_MSECS(ldur), &writers[wr_cnt++], pub_topic, reader, remote_reader);
tstart = dds_time();
for (size_t n = 0; n < wr_cnt_man_pp; n++)
add_and_check_writer(DDS_LIVELINESS_MANUAL_BY_PARTICIPANT, DDS_MSECS(ldur), &writers[wr_cnt++], pub_topic, reader);
add_and_check_writer(DDS_LIVELINESS_MANUAL_BY_PARTICIPANT, DDS_MSECS(ldur), &writers[wr_cnt++], pub_topic, reader, remote_reader);
for (size_t n = 0; n < wr_cnt_man_tp; n++)
add_and_check_writer(DDS_LIVELINESS_MANUAL_BY_TOPIC, DDS_MSECS(ldur), &writers[wr_cnt++], pub_topic, reader);
add_and_check_writer(DDS_LIVELINESS_MANUAL_BY_TOPIC, DDS_MSECS(ldur), &writers[wr_cnt++], pub_topic, reader, remote_reader);
t = dds_time();
if (t - tstart > DDS_MSECS(0.5 * ldur))
{
@ -750,13 +811,13 @@ CU_Theory((uint32_t wr_cnt_auto, uint32_t wr_cnt_man_pp, uint32_t wr_cnt_man_tp)
stopped = 0;
do
{
for (size_t n = wr_cnt - wr_cnt_man_tp; n < wr_cnt; n++)
dds_assert_liveliness(writers[n]);
for (size_t n = wr_cnt_auto; n < wr_cnt; n++)
CU_ASSERT_EQUAL_FATAL(dds_assert_liveliness(writers[n]), DDS_RETCODE_OK);
CU_ASSERT_EQUAL_FATAL(dds_get_liveliness_changed_status(reader, &lstatus), DDS_RETCODE_OK);
stopped += (uint32_t)lstatus.not_alive_count_change;
dds_sleepfor(DDS_MSECS(50));
} while (dds_time() < tstop);
dds_get_liveliness_changed_status(reader, &lstatus);
CU_ASSERT_EQUAL_FATAL(dds_get_liveliness_changed_status(reader, &lstatus), DDS_RETCODE_OK);
printf("writers alive with dds_assert_liveliness on all writers: %d, writers stopped: %d\n", lstatus.alive_count, stopped);
if (lstatus.alive_count != wr_cnt_auto + wr_cnt_man_pp + wr_cnt_man_tp || stopped != 0)
{
@ -795,7 +856,8 @@ CU_Theory((uint32_t wr_cnt_auto, uint32_t wr_cnt_man_pp, uint32_t wr_cnt_man_tp)
CU_ASSERT_EQUAL_FATAL(dds_delete(reader), DDS_RETCODE_OK);
for (size_t n = 0; n < wr_cnt; n++)
CU_ASSERT_EQUAL_FATAL(dds_delete(writers[n]), DDS_RETCODE_OK);
CU_ASSERT_EQUAL_FATAL(dds_delete(sub_topic), DDS_RETCODE_OK);
if (remote_reader)
CU_ASSERT_EQUAL_FATAL(dds_delete(sub_topic), DDS_RETCODE_OK);
CU_ASSERT_EQUAL_FATAL(dds_delete(pub_topic), DDS_RETCODE_OK);
if (!test_finished)
@ -813,4 +875,319 @@ CU_Theory((uint32_t wr_cnt_auto, uint32_t wr_cnt_man_pp, uint32_t wr_cnt_man_tp)
}
} while (!test_finished);
}
CU_Theory((uint32_t wr_cnt_auto, uint32_t wr_cnt_man_pp, uint32_t wr_cnt_man_tp), ddsc_liveliness, assert_liveliness, .init = liveliness_init, .fini = liveliness_fini, .timeout = 60)
{
test_assert_liveliness(wr_cnt_auto, wr_cnt_man_pp, wr_cnt_man_tp, false);
test_assert_liveliness(wr_cnt_auto, wr_cnt_man_pp, wr_cnt_man_tp, true);
}
#undef MAX_WRITERS
/**
* Check that manual-by-participant/topic writers with lease duration 0ns and 1ns work.
*/
struct liveliness_changed_state {
ddsrt_mutex_t lock;
dds_instance_handle_t w0_handle;
bool weirdness;
uint32_t w0_alive, w0_not_alive;
};
static void liveliness_changed_listener (dds_entity_t rd, const dds_liveliness_changed_status_t status, void *arg)
{
struct liveliness_changed_state *st = arg;
(void) rd;
ddsrt_mutex_lock (&st->lock);
if (status.last_publication_handle != st->w0_handle)
{
if (st->w0_handle == 0)
{
printf ("liveliness_changed_listener: w0 = %"PRIx64"\n", status.last_publication_handle);
st->w0_handle = status.last_publication_handle;
}
else
{
printf ("liveliness_changed_listener: too many writer handles\n");
st->weirdness = true;
}
}
if (status.alive_count_change != 0 || status.not_alive_count_change != 0)
{
switch (status.alive_count_change)
{
case -1:
break;
case 1:
if (status.last_publication_handle == st->w0_handle)
st->w0_alive++;
else
{
printf ("liveliness_changed_listener: alive_count_change = %d: unrecognized writer\n", status.alive_count_change);
st->weirdness = true;
}
break;
default:
printf ("liveliness_changed_listener: alive_count_change = %d\n", status.alive_count_change);
st->weirdness = true;
}
switch (status.not_alive_count_change)
{
case -1:
break;
case 1:
if (status.last_publication_handle == st->w0_handle)
st->w0_not_alive++;
else
{
printf ("liveliness_changed_listener: not_alive_count_change = %d: unrecognized writer\n", status.not_alive_count_change);
st->weirdness = true;
}
break;
default:
printf ("liveliness_changed_listener: not_alive_count_change = %d\n", status.not_alive_count_change);
st->weirdness = true;
}
}
else
{
printf ("liveliness_changed_listener: alive_count_change = 0 && not_alive_count_change = 0\n");
st->weirdness = true;
}
ddsrt_mutex_unlock (&st->lock);
}
#define STATUS_UNSYNCED 0
#define STATUS_SYNCED 1
#define STATUS_DATA 2
static unsigned get_and_check_status (dds_entity_t reader, dds_entity_t writer_active)
{
struct dds_liveliness_changed_status lstatus;
struct dds_subscription_matched_status sstatus;
struct dds_publication_matched_status pstatus;
uint32_t dstatus;
uint32_t result = STATUS_UNSYNCED;
dds_return_t rc;
rc = dds_get_subscription_matched_status(reader, &sstatus);
CU_ASSERT_FATAL(rc == DDS_RETCODE_OK);
rc = dds_get_liveliness_changed_status(reader, &lstatus);
CU_ASSERT_FATAL(rc == DDS_RETCODE_OK);
rc = dds_take_status(reader, &dstatus, DDS_DATA_AVAILABLE_STATUS);
CU_ASSERT_EQUAL_FATAL(rc, DDS_RETCODE_OK);
rc = dds_get_publication_matched_status(writer_active, &pstatus);
CU_ASSERT_FATAL(rc == DDS_RETCODE_OK);
CU_ASSERT_FATAL(lstatus.alive_count + lstatus.not_alive_count <= 2);
printf ("sub %d | alive %d | not-alive %d | pub %d | data %d\n", (int)sstatus.current_count, (int)lstatus.alive_count, (int)lstatus.not_alive_count, (int)pstatus.current_count, dstatus != 0);
if (dstatus)
result |= STATUS_DATA;
if (sstatus.current_count == 2 && lstatus.not_alive_count == 2 && pstatus.current_count == 1)
result |= STATUS_SYNCED;
return result;
}
static void lease_duration_zero_or_one_impl (dds_duration_t sleep, dds_liveliness_kind_t lkind, dds_duration_t ldur, bool remote_reader)
{
const uint32_t nsamples = (sleep <= DDS_MSECS(10)) ? 50 : 5;
dds_entity_t pub_topic;
dds_entity_t sub_topic = 0;
dds_entity_t reader;
dds_entity_t writer_active; /* writing */
dds_entity_t writer_inactive; /* not writing, liveliness should still toggle */
dds_entity_t waitset;
dds_listener_t *listener;
dds_qos_t *qos;
dds_return_t rc;
struct dds_liveliness_changed_status lstatus;
char name[100];
Space_Type1 sample = {1, 0, 0};
struct liveliness_changed_state listener_state = {
.weirdness = false,
.w0_handle = 0,
.w0_alive = 0,
.w0_not_alive = 0,
};
ddsrt_mutex_init (&listener_state.lock);
waitset = dds_create_waitset(DDS_CYCLONEDDS_HANDLE);
CU_ASSERT_FATAL(waitset > 0);
qos = dds_create_qos();
CU_ASSERT_FATAL(qos != NULL);
dds_qset_reliability(qos, DDS_RELIABILITY_RELIABLE, DDS_INFINITY);
dds_qset_history(qos, DDS_HISTORY_KEEP_ALL, 0);
create_topic_name("ddsc_liveliness_lease_duration_zero", g_topic_nr++, name, sizeof name);
pub_topic = dds_create_topic(g_pub_participant, &Space_Type1_desc, name, qos, NULL);
CU_ASSERT_FATAL(pub_topic > 0);
if (remote_reader)
{
sub_topic = dds_create_topic(g_sub_participant, &Space_Type1_desc, name, qos, NULL);
CU_ASSERT_FATAL(sub_topic > 0);
}
/* reader liveliness is always automatic/infinity */
dds_qset_liveliness(qos, DDS_LIVELINESS_AUTOMATIC, DDS_INFINITY);
reader = dds_create_reader(remote_reader ? g_sub_participant : g_pub_participant, remote_reader ? sub_topic : pub_topic, qos, NULL);
CU_ASSERT_FATAL(reader > 0);
rc = dds_set_status_mask(reader, DDS_LIVELINESS_CHANGED_STATUS | DDS_SUBSCRIPTION_MATCHED_STATUS | DDS_DATA_AVAILABLE_STATUS);
CU_ASSERT_FATAL(rc == DDS_RETCODE_OK);
rc = dds_waitset_attach(waitset, reader, reader);
CU_ASSERT_FATAL(rc == DDS_RETCODE_OK);
/* writer liveliness varies */
dds_qset_liveliness(qos, lkind, ldur);
writer_active = dds_create_writer(g_pub_participant, pub_topic, qos, NULL);
CU_ASSERT_FATAL(writer_active > 0);
writer_inactive = dds_create_writer(g_pub_participant, pub_topic, qos, NULL);
CU_ASSERT_FATAL(writer_inactive > 0);
rc = dds_set_status_mask(writer_active, DDS_PUBLICATION_MATCHED_STATUS);
CU_ASSERT_FATAL(rc == DDS_RETCODE_OK);
rc = dds_waitset_attach(waitset, writer_active, writer_active);
CU_ASSERT_FATAL(rc == DDS_RETCODE_OK);
dds_delete_qos(qos);
/* wait for writers to be discovered and to have lost their liveliness, and for
writer_active to have discovered the reader */
unsigned status = STATUS_UNSYNCED;
bool initial_sample_written = false, initial_sample_received = false;
do
{
status = get_and_check_status (reader, writer_active);
if (status & STATUS_DATA)
initial_sample_received = true;
if (status & STATUS_SYNCED && !initial_sample_written)
{
rc = dds_write(writer_active, &sample);
CU_ASSERT_FATAL(rc == DDS_RETCODE_OK);
initial_sample_written = true;
}
if (status & STATUS_SYNCED && initial_sample_received)
break;
rc = dds_waitset_wait(waitset, NULL, 0, DDS_SECS(5));
if (rc < 1)
{
get_and_check_status (reader, writer_active);
CU_ASSERT_FATAL(rc >= 1);
}
} while (1);
/* switch to using a listener: those allow us to observe all events */
listener = dds_create_listener (&listener_state);
dds_lset_liveliness_changed(listener, liveliness_changed_listener);
rc = dds_set_listener (reader, listener);
CU_ASSERT_FATAL(rc == DDS_RETCODE_OK);
dds_delete_listener (listener);
/* write as fast as possible - we don't expect this to cause the writers
to gain and lose liveliness once for each sample, but it should have
become alive at least once and fall back to not alive afterward */
for (uint32_t i = 0; i < nsamples; i++)
{
rc = dds_write(writer_active, &sample);
CU_ASSERT_FATAL(rc == DDS_RETCODE_OK);
if (sleep && i < nsamples - 1)
dds_sleepfor(sleep);
}
rc = dds_wait_for_acks(writer_active, DDS_SECS(5));
CU_ASSERT_FATAL(rc == DDS_RETCODE_OK);
/* verify the reader received all samples */
void *raw[] = { &sample };
dds_sample_info_t si;
uint32_t cnt = 0;
do
{
rc = dds_waitset_wait(waitset, NULL, 0, DDS_SECS(5));
CU_ASSERT_FATAL(rc >= 1);
while (dds_take(reader, raw, &si, 1, 1) == 1 && si.valid_data)
cnt++;
}
while (cnt < nsamples + 1);
CU_ASSERT_FATAL(cnt == nsamples + 1);
/* transition to not alive is not necessarily immediate */
{
int retries = 100;
rc = dds_get_liveliness_changed_status(reader, &lstatus);
CU_ASSERT_FATAL(rc == DDS_RETCODE_OK);
printf("early liveliness changed status: alive %"PRId32" not-alive %"PRId32"\n", lstatus.alive_count, lstatus.not_alive_count);
ddsrt_mutex_lock (&listener_state.lock);
printf("early w0 %"PRIx64" alive %"PRId32" not-alive %"PRId32"\n", listener_state.w0_handle, listener_state.w0_alive, listener_state.w0_not_alive);
CU_ASSERT_FATAL(!listener_state.weirdness);
CU_ASSERT_FATAL(listener_state.w0_handle != 0);
while (listener_state.w0_not_alive < listener_state.w0_alive && retries-- > 0)
{
ddsrt_mutex_unlock(&listener_state.lock);
dds_sleepfor(DDS_MSECS(10));
rc = dds_get_liveliness_changed_status(reader, &lstatus);
CU_ASSERT_FATAL(rc == DDS_RETCODE_OK);
ddsrt_mutex_lock(&listener_state.lock);
}
printf("late liveliness changed status: alive %"PRId32" not-alive %"PRId32"\n", lstatus.alive_count, lstatus.not_alive_count);
printf("final w0 %"PRIx64" alive %"PRId32" not-alive %"PRId32"\n", listener_state.w0_handle, listener_state.w0_alive, listener_state.w0_not_alive);
CU_ASSERT_FATAL(listener_state.w0_alive == listener_state.w0_not_alive);
uint32_t exp_alive;
if (sleep == 0)
exp_alive = 1; /* if not sleeping, it's ok if the transition happens only once */
else if (sleep <= DDS_MSECS(10))
exp_alive = nsamples / 3; /* if sleeping briefly, expect the a good number of writes to toggle liveliness */
else
exp_alive = nsamples - nsamples / 5; /* if sleeping, expect the vast majority (80%) of the writes to toggle liveliness */
printf("check w0_alive %d >= %d\n", listener_state.w0_alive, exp_alive);
CU_ASSERT_FATAL(listener_state.w0_alive >= exp_alive);
ddsrt_mutex_unlock(&listener_state.lock);
}
/* cleanup */
rc = dds_delete(waitset);
CU_ASSERT_FATAL(rc == DDS_RETCODE_OK);
rc = dds_delete(reader);
CU_ASSERT_FATAL(rc == DDS_RETCODE_OK);
rc = dds_delete(writer_active);
CU_ASSERT_FATAL(rc == DDS_RETCODE_OK);
rc = dds_delete(writer_inactive);
CU_ASSERT_FATAL(rc == DDS_RETCODE_OK);
if (remote_reader)
{
rc = dds_delete(sub_topic);
CU_ASSERT_FATAL(rc == DDS_RETCODE_OK);
}
rc = dds_delete(pub_topic);
CU_ASSERT_FATAL(rc == DDS_RETCODE_OK);
ddsrt_mutex_destroy(&listener_state.lock);
}
CU_Test(ddsc_liveliness, lease_duration_zero_or_one, .init = liveliness_init, .fini = liveliness_fini, .timeout = 30)
{
static const bool remote_rd[] = { false, true };
static const dds_duration_t sleep[] = { 0, DDS_MSECS(10), DDS_MSECS(100) };
static const dds_liveliness_kind_t lkind[] = { DDS_LIVELINESS_MANUAL_BY_PARTICIPANT, DDS_LIVELINESS_MANUAL_BY_TOPIC };
static const dds_duration_t ldur[] = { 0, 1 };
for (size_t remote_rd_idx = 0; remote_rd_idx < sizeof (remote_rd) / sizeof (remote_rd[0]); remote_rd_idx++)
{
for (size_t sleep_idx = 0; sleep_idx < sizeof (sleep) / sizeof (sleep[0]); sleep_idx++)
{
for (size_t lkind_idx = 0; lkind_idx < sizeof (lkind) / sizeof (lkind[0]); lkind_idx++)
{
for (size_t ldur_idx = 0; ldur_idx < sizeof (ldur) / sizeof (ldur[0]); ldur_idx++)
{
bool rrd = remote_rd[remote_rd_idx];
dds_duration_t s = sleep[sleep_idx];
dds_liveliness_kind_t k = lkind[lkind_idx];
dds_duration_t d = ldur[ldur_idx];
printf ("### lease_duration_zero_or_one: sleep = %"PRId64" lkind = %d ldur = %"PRId64" reader = %s\n", s, (int) k, d, rrd ? "remote" : "local");
lease_duration_zero_or_one_impl (s, k, d, rrd);
printf ("\n");
}
}
}
}
}

View file

@ -0,0 +1,608 @@
/*
* Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
* v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
#include <assert.h>
#include <limits.h>
#include "dds/dds.h"
#include "CUnit/Theory.h"
#include "Space.h"
#include "config_env.h"
#include "dds/version.h"
#include "dds__entity.h"
#include "dds/ddsi/q_entity.h"
#include "dds/ddsi/ddsi_serdata.h"
#include "dds/ddsi/ddsi_entity_index.h"
#include "dds/ddsrt/cdtors.h"
#include "dds/ddsrt/misc.h"
#include "dds/ddsrt/process.h"
#include "dds/ddsrt/threads.h"
#include "dds/ddsrt/environ.h"
#include "dds/ddsrt/atomics.h"
#include "dds/ddsrt/time.h"
#define DDS_DOMAINID_PUB 0
#define DDS_DOMAINID_SUB 1
#define DDS_CONFIG_NO_PORT_GAIN "${CYCLONEDDS_URI}${CYCLONEDDS_URI:+,}<Discovery><ExternalDomainId>0</ExternalDomainId></Discovery>"
#define DDS_CONFIG_NO_PORT_GAIN_LOG "${CYCLONEDDS_URI}${CYCLONEDDS_URI:+,}<Tracing><OutputFile>cyclonedds_multi_sertopic_tests.${CYCLONEDDS_DOMAIN_ID}.${CYCLONEDDS_PID}.log</OutputFile><Verbosity>finest</Verbosity></Tracing><Discovery><ExternalDomainId>0</ExternalDomainId></Discovery>"
/* IDL preprocessing is not really friendly towards creating multiple descriptors
for the same type name with different definitions, so we do it by hand. */
struct uint32_seq {
uint32_t _maximum;
uint32_t _length;
uint32_t *_buffer;
bool _release;
};
struct two_uint32 {
uint32_t v[2];
};
struct two_uint32_seq {
uint32_t _maximum;
uint32_t _length;
struct two_uint32 *_buffer;
bool _release;
};
struct type_seq {
struct uint32_seq x;
};
struct type_ary {
uint32_t x[4];
};
struct type_uni {
uint32_t _d;
union
{
struct two_uint32_seq a;
uint32_t b[4];
} _u;
};
static const dds_topic_descriptor_t type_seq_desc =
{
.m_size = sizeof (struct type_seq),
.m_align = sizeof (void *),
.m_flagset = DDS_TOPIC_NO_OPTIMIZE,
.m_nkeys = 0,
.m_typename = "multi_sertopic_type",
.m_keys = NULL,
.m_nops = 2,
.m_ops = (const uint32_t[]) {
DDS_OP_ADR | DDS_OP_TYPE_SEQ | DDS_OP_SUBTYPE_4BY, offsetof (struct type_seq, x),
DDS_OP_RTS
},
.m_meta = "" /* this is on its way out anyway */
};
static const dds_topic_descriptor_t type_ary_desc =
{
.m_size = sizeof (struct type_ary),
.m_align = 4u,
.m_flagset = DDS_TOPIC_NO_OPTIMIZE,
.m_nkeys = 0,
.m_typename = "multi_sertopic_type",
.m_keys = NULL,
.m_nops = 2,
.m_ops = (const uint32_t[]) {
DDS_OP_ADR | DDS_OP_TYPE_ARR | DDS_OP_SUBTYPE_4BY, offsetof (struct type_ary, x), 4,
DDS_OP_RTS
},
.m_meta = "" /* this is on its way out anyway */
};
static const dds_topic_descriptor_t type_uni_desc =
{
.m_size = sizeof (struct type_uni),
.m_align = sizeof (void *),
.m_flagset = DDS_TOPIC_NO_OPTIMIZE | DDS_TOPIC_CONTAINS_UNION,
.m_nkeys = 0,
.m_typename = "multi_sertopic_type",
.m_keys = NULL,
.m_nops = 8,
.m_ops = (const uint32_t[]) {
DDS_OP_ADR | DDS_OP_TYPE_UNI | DDS_OP_SUBTYPE_4BY | DDS_OP_FLAG_DEF, offsetof (struct type_uni, _d), 2u, (23u << 16) + 4u,
DDS_OP_JEQ | DDS_OP_TYPE_SEQ | 6, 3, offsetof (struct type_uni, _u.a),
DDS_OP_JEQ | DDS_OP_TYPE_ARR | 12, 0, offsetof (struct type_uni, _u.b),
DDS_OP_ADR | DDS_OP_TYPE_SEQ | DDS_OP_SUBTYPE_STU, 0u,
sizeof (struct two_uint32), (8u << 16u) + 4u,
DDS_OP_ADR | DDS_OP_TYPE_ARR | DDS_OP_SUBTYPE_4BY, offsetof (struct two_uint32, v), 2,
DDS_OP_RTS,
DDS_OP_RTS,
DDS_OP_ADR | DDS_OP_TYPE_ARR | DDS_OP_SUBTYPE_4BY, 0u, 4,
DDS_OP_RTS,
DDS_OP_RTS
},
.m_meta = "" /* this is on its way out anyway */
};
/* The slow delivery path has a switchover at 4 sertopics (well, today it has ...) so it is better to
to test with > 4 different sertopics. That path (again, today) iterates over GUIDs in increasing
order, and as all readers are created in the participant and the entity ids are strictly
monotonically increasing for the first ~ 16M entities (again, today), creating additional
readers for these topics at the end means that "ary2" is the one that ends up in > 4 case.
Calling takecdr */
static const dds_topic_descriptor_t type_ary1_desc =
{
.m_size = sizeof (struct type_ary),
.m_align = 1u,
.m_flagset = DDS_TOPIC_NO_OPTIMIZE,
.m_nkeys = 0,
.m_typename = "multi_sertopic_type",
.m_keys = NULL,
.m_nops = 2,
.m_ops = (const uint32_t[]) {
DDS_OP_ADR | DDS_OP_TYPE_ARR | DDS_OP_SUBTYPE_1BY, offsetof (struct type_ary, x), 16,
DDS_OP_RTS
},
.m_meta = "" /* this is on its way out anyway */
};
static const dds_topic_descriptor_t type_ary2_desc =
{
.m_size = sizeof (struct type_ary),
.m_align = 2u,
.m_flagset = DDS_TOPIC_NO_OPTIMIZE,
.m_nkeys = 0,
.m_typename = "multi_sertopic_type",
.m_keys = NULL,
.m_nops = 2,
.m_ops = (const uint32_t[]) {
DDS_OP_ADR | DDS_OP_TYPE_ARR | DDS_OP_SUBTYPE_2BY, offsetof (struct type_ary, x), 8,
DDS_OP_RTS
},
.m_meta = "" /* this is on its way out anyway */
};
static uint32_t g_topic_nr = 0;
static dds_entity_t g_pub_domain = 0;
static dds_entity_t g_pub_participant = 0;
static dds_entity_t g_pub_publisher = 0;
static dds_entity_t g_sub_domain = 0;
static dds_entity_t g_sub_participant = 0;
static dds_entity_t g_sub_subscriber = 0;
static char *create_topic_name (const char *prefix, uint32_t nr, char *name, size_t size)
{
/* Get unique g_topic name. */
ddsrt_pid_t pid = ddsrt_getpid();
ddsrt_tid_t tid = ddsrt_gettid();
(void) snprintf (name, size, "%s%d_pid%" PRIdPID "_tid%" PRIdTID "", prefix, nr, pid, tid);
return name;
}
static void multi_sertopic_init (void)
{
/* Domains for pub and sub use a different domain id, but the portgain setting
* in configuration is 0, so that both domains will map to the same port number.
* This allows to create two domains in a single test process. */
char *conf_pub = ddsrt_expand_envvars (DDS_CONFIG_NO_PORT_GAIN, DDS_DOMAINID_PUB);
char *conf_sub = ddsrt_expand_envvars (DDS_CONFIG_NO_PORT_GAIN, DDS_DOMAINID_SUB);
g_pub_domain = dds_create_domain (DDS_DOMAINID_PUB, conf_pub);
g_sub_domain = dds_create_domain (DDS_DOMAINID_SUB, conf_sub);
dds_free (conf_pub);
dds_free (conf_sub);
g_pub_participant = dds_create_participant(DDS_DOMAINID_PUB, NULL, NULL);
CU_ASSERT_FATAL (g_pub_participant > 0);
g_sub_participant = dds_create_participant(DDS_DOMAINID_SUB, NULL, NULL);
CU_ASSERT_FATAL (g_sub_participant > 0);
g_pub_publisher = dds_create_publisher(g_pub_participant, NULL, NULL);
CU_ASSERT_FATAL (g_pub_publisher > 0);
g_sub_subscriber = dds_create_subscriber(g_sub_participant, NULL, NULL);
CU_ASSERT_FATAL (g_sub_subscriber > 0);
}
static void multi_sertopic_fini (void)
{
dds_delete (g_sub_subscriber);
dds_delete (g_pub_publisher);
dds_delete (g_sub_participant);
dds_delete (g_pub_participant);
dds_delete (g_sub_domain);
dds_delete (g_pub_domain);
}
static bool get_and_check_writer_status (size_t nwr, const dds_entity_t *wrs, size_t nrd)
{
dds_return_t rc;
struct dds_publication_matched_status x;
for (size_t i = 0; i < nwr; i++)
{
rc = dds_get_publication_matched_status (wrs[i], &x);
CU_ASSERT_FATAL (rc == DDS_RETCODE_OK);
if (x.current_count != nrd)
return false;
}
return true;
}
static bool get_and_check_reader_status (size_t nrd, const dds_entity_t *rds, size_t nwr)
{
dds_return_t rc;
struct dds_subscription_matched_status x;
for (size_t i = 0; i < nrd; i++)
{
rc = dds_get_subscription_matched_status (rds[i], &x);
CU_ASSERT_FATAL (rc == DDS_RETCODE_OK);
if (x.current_count != nwr)
return false;
}
return true;
}
static void waitfor_or_reset_fastpath (dds_entity_t rdhandle, bool fastpath, size_t nwr)
{
dds_return_t rc;
struct dds_entity *x;
rc = dds_entity_pin (rdhandle, &x);
CU_ASSERT_FATAL (rc == DDS_RETCODE_OK);
CU_ASSERT_FATAL (dds_entity_kind (x) == DDS_KIND_READER);
struct reader * const rd = ((struct dds_reader *) x)->m_rd;
struct rd_pwr_match *m;
ddsi_guid_t cursor;
size_t wrcount = 0;
thread_state_awake (lookup_thread_state (), rd->e.gv);
ddsrt_mutex_lock (&rd->e.lock);
memset (&cursor, 0, sizeof (cursor));
while ((m = ddsrt_avl_lookup_succ (&rd_writers_treedef, &rd->writers, &cursor)) != NULL)
{
cursor = m->pwr_guid;
ddsrt_mutex_unlock (&rd->e.lock);
struct proxy_writer * const pwr = entidx_lookup_proxy_writer_guid (rd->e.gv->entity_index, &cursor);
ddsrt_mutex_lock (&pwr->rdary.rdary_lock);
if (!fastpath)
pwr->rdary.fastpath_ok = false;
else
{
while (!pwr->rdary.fastpath_ok)
{
ddsrt_mutex_unlock (&pwr->rdary.rdary_lock);
dds_sleepfor (DDS_MSECS (10));
ddsrt_mutex_lock (&pwr->rdary.rdary_lock);
}
}
wrcount++;
ddsrt_mutex_unlock (&pwr->rdary.rdary_lock);
ddsrt_mutex_lock (&rd->e.lock);
}
memset (&cursor, 0, sizeof (cursor));
while ((m = ddsrt_avl_lookup_succ (&rd_local_writers_treedef, &rd->local_writers, &cursor)) != NULL)
{
cursor = m->pwr_guid;
ddsrt_mutex_unlock (&rd->e.lock);
struct writer * const wr = entidx_lookup_writer_guid (rd->e.gv->entity_index, &cursor);
ddsrt_mutex_lock (&wr->rdary.rdary_lock);
if (!fastpath)
wr->rdary.fastpath_ok = fastpath;
else
{
while (!wr->rdary.fastpath_ok)
{
ddsrt_mutex_unlock (&wr->rdary.rdary_lock);
dds_sleepfor (DDS_MSECS (10));
ddsrt_mutex_lock (&wr->rdary.rdary_lock);
}
}
wrcount++;
ddsrt_mutex_unlock (&wr->rdary.rdary_lock);
ddsrt_mutex_lock (&rd->e.lock);
}
ddsrt_mutex_unlock (&rd->e.lock);
thread_state_asleep (lookup_thread_state ());
dds_entity_unpin (x);
CU_ASSERT_FATAL (wrcount == nwr);
}
static struct ddsi_sertopic *get_sertopic_from_reader (dds_entity_t reader)
{
/* not refcounting the sertopic: so this presumes it is kept alive for other reasons */
dds_return_t rc;
struct dds_entity *x;
struct dds_reader *rd;
struct ddsi_sertopic *sertopic;
rc = dds_entity_pin (reader, &x);
CU_ASSERT_FATAL (rc == DDS_RETCODE_OK);
CU_ASSERT_FATAL (dds_entity_kind (x) == DDS_KIND_READER);
rd = (struct dds_reader *) x;
sertopic = rd->m_topic->m_stopic;
dds_entity_unpin (x);
return sertopic;
}
static void logsink (void *arg, const dds_log_data_t *msg)
{
ddsrt_atomic_uint32_t *deser_fail = arg;
fputs (msg->message - msg->hdrsize, stderr);
if (strstr (msg->message, "deserialization") && strstr (msg->message, "failed"))
ddsrt_atomic_inc32 (deser_fail);
}
static void ddsc_multi_sertopic_impl (dds_entity_t pp_pub, dds_entity_t pp_sub, bool fastpath)
{
#define SEQ_IDX 0
#define ARY_IDX 1
#define UNI_IDX 2
char name[100];
static const dds_topic_descriptor_t *descs[] = {
&type_seq_desc, &type_ary_desc, &type_uni_desc,
&type_ary1_desc, &type_ary2_desc
};
dds_entity_t pub_topics[3], writers[3];
dds_entity_t sub_topics[5];
dds_entity_t readers[15];
dds_entity_t waitset;
dds_qos_t *qos;
dds_return_t rc;
printf ("multi_sertopic: %s %s\n", (pp_pub == pp_sub) ? "local" : "remote", fastpath ? "fastpath" : "slowpath");
waitset = dds_create_waitset (DDS_CYCLONEDDS_HANDLE);
CU_ASSERT_FATAL (waitset > 0);
qos = dds_create_qos ();
CU_ASSERT_FATAL (qos != NULL);
dds_qset_reliability (qos, DDS_RELIABILITY_RELIABLE, DDS_INFINITY);
dds_qset_destination_order (qos, DDS_DESTINATIONORDER_BY_SOURCE_TIMESTAMP);
dds_qset_history (qos, DDS_HISTORY_KEEP_ALL, 0);
create_topic_name ("ddsc_multi_sertopic_lease_duration_zero", g_topic_nr++, name, sizeof name);
for (size_t i = 0; i < sizeof (pub_topics) / sizeof (pub_topics[0]); i++)
{
pub_topics[i] = dds_create_topic (pp_pub, descs[i], name, qos, NULL);
CU_ASSERT_FATAL (pub_topics[i] > 0);
}
for (size_t i = 0; i < sizeof (writers) / sizeof (writers[0]); i++)
{
writers[i] = dds_create_writer (pp_pub, pub_topics[i], qos, NULL);
CU_ASSERT_FATAL (writers[i] > 0);
}
for (size_t i = 0; i < sizeof (sub_topics) / sizeof (sub_topics[0]); i++)
{
sub_topics[i] = dds_create_topic (pp_sub, descs[i], name, qos, NULL);
CU_ASSERT_FATAL (sub_topics[i] > 0);
}
DDSRT_STATIC_ASSERT (sizeof (readers) >= sizeof (sub_topics));
DDSRT_STATIC_ASSERT ((sizeof (readers) % sizeof (sub_topics)) == 0);
for (size_t i = 0; i < sizeof (sub_topics) / sizeof (sub_topics[0]); i++)
{
readers[i] = dds_create_reader (pp_sub, sub_topics[i], qos, NULL);
CU_ASSERT_FATAL (readers[i] > 0);
}
for (size_t i = sizeof (sub_topics) / sizeof (sub_topics[0]); i < sizeof (readers) / sizeof (readers[0]); i++)
{
const size_t nrd = sizeof (readers) / sizeof (readers[0]);
const size_t ntp = sizeof (sub_topics) / sizeof (sub_topics[0]);
readers[i] = dds_create_reader (pp_sub, sub_topics[(i - ntp) / (nrd / ntp - 1)], qos, NULL);
CU_ASSERT_FATAL (readers[i] > 0);
}
dds_delete_qos (qos);
/* wait for discovery to complete */
for (size_t i = 0; i < sizeof (writers) / sizeof (writers[0]); i++)
{
rc = dds_set_status_mask (writers[i], DDS_PUBLICATION_MATCHED_STATUS);
CU_ASSERT_FATAL (rc == DDS_RETCODE_OK);
rc = dds_waitset_attach (waitset, writers[i], -(dds_attach_t)i - 1);
CU_ASSERT_FATAL (rc == DDS_RETCODE_OK);
}
for (size_t i = 0; i < sizeof (readers) / sizeof (readers[0]); i++)
{
rc = dds_set_status_mask (readers[i], DDS_SUBSCRIPTION_MATCHED_STATUS | DDS_DATA_AVAILABLE_STATUS);
CU_ASSERT_FATAL (rc == DDS_RETCODE_OK);
rc = dds_waitset_attach (waitset, readers[i], (dds_attach_t)i);
CU_ASSERT_FATAL (rc == DDS_RETCODE_OK);
}
printf ("wait for discovery, fastpath_ok; delete & recreate readers\n");
while (!(get_and_check_writer_status (sizeof (writers) / sizeof (writers[0]), writers, sizeof (readers) / sizeof (readers[0])) &&
get_and_check_reader_status (sizeof (readers) / sizeof (readers[0]), readers, sizeof (writers) / sizeof (writers[0]))))
{
rc = dds_waitset_wait (waitset, NULL, 0, DDS_SECS(5));
CU_ASSERT_FATAL (rc >= 1);
}
/* we want to check both the fast path and the slow path ... so first wait
for it to be set on all (proxy) writers, then possibly reset it */
for (size_t i = 0; i < sizeof (readers) / sizeof (readers[0]); i++)
waitfor_or_reset_fastpath (readers[i], true, sizeof (writers) / sizeof (writers[0]));
if (!fastpath)
{
printf ("clear fastpath_ok\n");
for (size_t i = 0; i < sizeof (readers) / sizeof (readers[0]); i++)
waitfor_or_reset_fastpath (readers[i], false, sizeof (writers) / sizeof (writers[0]));
}
/* check the log output for deserialization failures */
ddsrt_atomic_uint32_t deser_fail = DDSRT_ATOMIC_UINT32_INIT (0);
dds_set_log_sink (logsink, &deser_fail);
/* Write one of each type: all of these samples result in the same serialised
form but interpreting the memory layout for type X as-if it were of type Y
wreaks havoc. */
{
struct type_seq s = {
.x = {
._length = 3, ._maximum = 3, ._release = false, ._buffer = (uint32_t[]) { 1, 4, 2 }
}
};
struct type_ary a = {
.x = { 3, 1, 4, 2 }
};
struct type_uni u = {
._d = 3,
._u = { .a = {
._length = 1, ._maximum = 1, ._release = false, ._buffer = (struct two_uint32[]) { { { 4, 2 } } }
} }
};
printf ("writing ...\n");
rc = dds_write_ts (writers[SEQ_IDX], &s, 1);
CU_ASSERT_FATAL (rc == DDS_RETCODE_OK);
rc = dds_write_ts (writers[ARY_IDX], &a, 2);
CU_ASSERT_FATAL (rc == DDS_RETCODE_OK);
rc = dds_write_ts (writers[UNI_IDX], &u, 3);
CU_ASSERT_FATAL (rc == DDS_RETCODE_OK);
/* Also write a sample that can't be deserialised by the other types */
struct type_seq s1 = {
.x = {
._length = 1, ._maximum = 1, ._release = false, ._buffer = (uint32_t[]) { 1 }
}
};
rc = dds_write_ts (writers[SEQ_IDX], &s1, 4);
CU_ASSERT_FATAL (rc == DDS_RETCODE_OK);
}
/* All readers should have received three samples, and those that are of type seq
should have received one extra (whereas the others should cause deserialization
failure warnings) */
printf ("reading\n");
const size_t nexp = ((sizeof (writers) / sizeof (writers[0])) *
(sizeof (readers) / sizeof (readers[0])) +
((sizeof (readers) / sizeof (readers[0])) / (sizeof (sub_topics) / sizeof (sub_topics[0]))));
/* expecting exactly as many deserialization failures as there are topics other than seq */
const size_t nexp_fail = sizeof (sub_topics) / sizeof (sub_topics[0]) - 1;
uint32_t nseen = 0;
while (nseen < nexp)
{
dds_sample_info_t si;
rc = dds_waitset_wait (waitset, NULL, 0, DDS_SECS (5));
CU_ASSERT_FATAL (rc >= 1);
{
struct type_seq s = { .x = { 0 } };
void *raws[] = { &s };
while (dds_take (readers[SEQ_IDX], raws, &si, 1, 1) == 1)
{
if (!si.valid_data)
continue;
printf ("recv: seq %"PRId64"\n", si.source_timestamp);
if (si.source_timestamp == 4)
{
CU_ASSERT_FATAL (s.x._length == 1);
CU_ASSERT_FATAL (s.x._buffer[0] == 1);
}
else
{
CU_ASSERT_FATAL (si.source_timestamp >= 1 && si.source_timestamp <= 3);
CU_ASSERT_FATAL (s.x._length == 3);
CU_ASSERT_FATAL (s.x._buffer[0] == 1);
CU_ASSERT_FATAL (s.x._buffer[1] == 4);
CU_ASSERT_FATAL (s.x._buffer[2] == 2);
}
nseen++;
}
dds_free (s.x._buffer);
}
{
struct type_ary a;
void *rawa[] = { &a };
while (dds_take (readers[ARY_IDX], rawa, &si, 1, 1) == 1)
{
if (!si.valid_data)
continue;
printf ("recv: ary %"PRId64"\n", si.source_timestamp);
CU_ASSERT_FATAL (si.source_timestamp >= 1 && si.source_timestamp <= 3);
CU_ASSERT_FATAL (a.x[0] == 3);
CU_ASSERT_FATAL (a.x[1] == 1);
CU_ASSERT_FATAL (a.x[2] == 4);
CU_ASSERT_FATAL (a.x[3] == 2);
nseen++;
}
}
{
struct type_uni u = { ._u.a = { 0 } };
void *rawu[] = { &u };
while (dds_take (readers[UNI_IDX], rawu, &si, 1, 1) == 1)
{
if (!si.valid_data)
continue;
printf ("recv: uni %"PRId64"\n", si.source_timestamp);
CU_ASSERT_FATAL (si.source_timestamp >= 1 && si.source_timestamp <= 3);
CU_ASSERT_FATAL (u._d == 3);
CU_ASSERT_FATAL (u._u.a._length == 1);
assert (u._u.a._buffer != NULL); /* for Clang static analyzer */
CU_ASSERT_FATAL (u._u.a._buffer[0].v[0] == 4);
CU_ASSERT_FATAL (u._u.a._buffer[0].v[1] == 2);
dds_free (u._u.a._buffer);
u._u.a._buffer = NULL;
nseen++;
}
}
DDSRT_STATIC_ASSERT (((1u << SEQ_IDX) | (1u << ARY_IDX) | (1u << UNI_IDX)) == 7);
for (size_t i = 3; i < sizeof (readers) / sizeof (readers[0]); i++)
{
struct ddsi_serdata *sample;
while (dds_takecdr (readers[i], &sample, 1, &si, DDS_ANY_STATE) == 1)
{
if (!si.valid_data)
continue;
printf ("recv: reader %zu %"PRId64"\n", i, si.source_timestamp);
CU_ASSERT_FATAL (sample->topic == get_sertopic_from_reader (readers[i]));
ddsi_serdata_unref (sample);
nseen++;
}
}
}
CU_ASSERT_FATAL (nseen == nexp);
/* data from remote writers can cause a deserialization failure after all
expected samples have been seen (becasue it is written last); so wait
for them */
while (ddsrt_atomic_ld32 (&deser_fail) < nexp_fail)
dds_sleepfor (DDS_MSECS (10));
CU_ASSERT_FATAL (ddsrt_atomic_ld32 (&deser_fail) == nexp_fail);
/* deleting the waitset is important: it is bound to the library rather than to
a domain and consequently won't be deleted simply because all domains are */
rc = dds_delete (waitset);
CU_ASSERT_FATAL (rc == DDS_RETCODE_OK);
dds_set_log_sink (0, NULL);
}
CU_Test(ddsc_multi_sertopic, local, .init = multi_sertopic_init, .fini = multi_sertopic_fini)
{
ddsc_multi_sertopic_impl (g_pub_participant, g_pub_participant, true);
}
CU_Test(ddsc_multi_sertopic, remote, .init = multi_sertopic_init, .fini = multi_sertopic_fini)
{
ddsc_multi_sertopic_impl (g_pub_participant, g_sub_participant, true);
}
CU_Test(ddsc_multi_sertopic, local_slowpath, .init = multi_sertopic_init, .fini = multi_sertopic_fini)
{
ddsc_multi_sertopic_impl (g_pub_participant, g_pub_participant, false);
}
CU_Test(ddsc_multi_sertopic, remote_slowpath, .init = multi_sertopic_init, .fini = multi_sertopic_fini)
{
ddsc_multi_sertopic_impl (g_pub_participant, g_sub_participant, false);
}

View file

@ -130,8 +130,12 @@ CU_Test(ddsc_topic_create, duplicate, .init=ddsc_topic_init, .fini=ddsc_topic_fi
/* Creating the same topic should succeed. */
topic = dds_create_topic(g_participant, &RoundTripModule_DataType_desc, g_topicRtmDataTypeName, NULL, NULL);
CU_ASSERT_FATAL(topic > 0);
CU_ASSERT_FATAL(topic != g_topicRtmDataType);
ret = dds_delete(topic);
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
/* Old topic entity should remain in existence */
ret = dds_get_parent(g_topicRtmDataType);
CU_ASSERT(ret > 0);
}
/*************************************************************************************************/
@ -201,7 +205,7 @@ CU_Test(ddsc_topic_find, valid, .init=ddsc_topic_init, .fini=ddsc_topic_fini)
dds_return_t ret;
topic = dds_find_topic(g_participant, g_topicRtmDataTypeName);
CU_ASSERT_EQUAL_FATAL(topic, g_topicRtmDataType);
CU_ASSERT_NOT_EQUAL_FATAL(topic, g_topicRtmDataType);
ret = dds_delete(topic);
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);

View file

@ -84,21 +84,6 @@ CU_Test(ddsc_unsupported, dds_begin_end_coherent, .init = setup, .fini = teardow
}
}
CU_Test(ddsc_unsupported, dds_wait_for_acks, .init = setup, .fini = teardown)
{
dds_return_t result;
static struct index_result pars[] = {
{PUB, DDS_RETCODE_UNSUPPORTED},
{WRI, DDS_RETCODE_UNSUPPORTED},
{BAD, DDS_RETCODE_BAD_PARAMETER}
};
for (size_t i=0; i < sizeof (pars) / sizeof (pars[0]);i++) {
result = dds_wait_for_acks(e[pars[i].index], 0);
CU_ASSERT_EQUAL(result, pars[i].exp_res);
}
}
CU_Test(ddsc_unsupported, dds_suspend_resume, .init = setup, .fini = teardown)
{
dds_return_t result;

279
src/core/ddsc/tests/whc.c Normal file
View file

@ -0,0 +1,279 @@
/*
* Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
* v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
#include <assert.h>
#include <limits.h>
#include "dds/dds.h"
#include "CUnit/Theory.h"
#include "Space.h"
#include "dds/ddsrt/process.h"
#include "dds/ddsrt/threads.h"
#include "dds/ddsrt/environ.h"
#include "dds/ddsi/ddsi_entity_index.h"
#include "dds/ddsi/q_entity.h"
#include "dds/ddsi/q_whc.h"
#include "dds__entity.h"
#define DDS_DOMAINID_PUB 0
#define DDS_DOMAINID_SUB 1
#define DDS_CONFIG_NO_PORT_GAIN "${CYCLONEDDS_URI}${CYCLONEDDS_URI:+,}<Discovery><ExternalDomainId>0</ExternalDomainId></Discovery>"
#define DDS_CONFIG_NO_PORT_GAIN_LOG "${CYCLONEDDS_URI}${CYCLONEDDS_URI:+,}<Tracing><OutputFile>cyclonedds_whc_test.${CYCLONEDDS_DOMAIN_ID}.${CYCLONEDDS_PID}.log</OutputFile><Verbosity>finest</Verbosity></Tracing><Discovery><ExternalDomainId>0</ExternalDomainId></Discovery>"
#define SAMPLE_COUNT 5
#define DEADLINE_DURATION DDS_MSECS(1)
static uint32_t g_topic_nr = 0;
static dds_entity_t g_domain = 0;
static dds_entity_t g_participant = 0;
static dds_entity_t g_subscriber = 0;
static dds_entity_t g_publisher = 0;
static dds_qos_t *g_qos;
static dds_entity_t g_remote_domain = 0;
static dds_entity_t g_remote_participant = 0;
static dds_entity_t g_remote_subscriber = 0;
static char *create_topic_name (const char *prefix, uint32_t nr, char *name, size_t size)
{
/* Get unique g_topic name. */
ddsrt_pid_t pid = ddsrt_getpid ();
ddsrt_tid_t tid = ddsrt_gettid ();
(void) snprintf (name, size, "%s%d_pid%" PRIdPID "_tid%" PRIdTID "", prefix, nr, pid, tid);
return name;
}
static void whc_init(void)
{
/* Domains for pub and sub use a different domain id, but the portgain setting
* in configuration is 0, so that both domains will map to the same port number.
* This allows to create two domains in a single test process. */
char *conf_pub = ddsrt_expand_envvars(DDS_CONFIG_NO_PORT_GAIN, DDS_DOMAINID_PUB);
char *conf_sub = ddsrt_expand_envvars(DDS_CONFIG_NO_PORT_GAIN, DDS_DOMAINID_SUB);
g_domain = dds_create_domain(DDS_DOMAINID_PUB, conf_pub);
g_remote_domain = dds_create_domain(DDS_DOMAINID_SUB, conf_sub);
dds_free(conf_pub);
dds_free(conf_sub);
g_qos = dds_create_qos();
CU_ASSERT_PTR_NOT_NULL_FATAL(g_qos);
g_participant = dds_create_participant(DDS_DOMAINID_PUB, NULL, NULL);
CU_ASSERT_FATAL(g_participant > 0);
g_remote_participant = dds_create_participant(DDS_DOMAINID_SUB, NULL, NULL);
CU_ASSERT_FATAL(g_remote_participant > 0);
g_subscriber = dds_create_subscriber(g_participant, NULL, NULL);
CU_ASSERT_FATAL(g_subscriber > 0);
g_remote_subscriber = dds_create_subscriber(g_remote_participant, NULL, NULL);
CU_ASSERT_FATAL(g_remote_subscriber > 0);
g_publisher = dds_create_publisher(g_participant, NULL, NULL);
CU_ASSERT_FATAL(g_publisher > 0);
}
static void whc_fini (void)
{
dds_delete_qos(g_qos);
dds_delete(g_subscriber);
dds_delete(g_remote_subscriber);
dds_delete(g_publisher);
dds_delete(g_participant);
dds_delete(g_remote_participant);
dds_delete(g_domain);
dds_delete(g_remote_domain);
}
static dds_entity_t create_and_sync_reader(dds_entity_t subscriber, dds_entity_t topic, dds_qos_t *qos, dds_entity_t writer)
{
dds_return_t ret;
dds_entity_t reader = dds_create_reader(subscriber, topic, qos, NULL);
CU_ASSERT_FATAL(reader > 0);
while (1)
{
dds_publication_matched_status_t st;
ret = dds_get_publication_matched_status (writer, &st);
CU_ASSERT_FATAL (ret == DDS_RETCODE_OK);
if (st.current_count_change == 1)
break;
dds_sleepfor (DDS_MSECS (1));
}
return reader;
}
static void check_whc_state(dds_entity_t writer, seqno_t exp_min, seqno_t exp_max)
{
struct dds_entity *wr_entity;
struct writer *wr;
struct whc_state whcst;
CU_ASSERT_EQUAL_FATAL(dds_entity_pin(writer, &wr_entity), 0);
thread_state_awake(lookup_thread_state(), &wr_entity->m_domain->gv);
wr = entidx_lookup_writer_guid(wr_entity->m_domain->gv.entity_index, &wr_entity->m_guid);
CU_ASSERT_FATAL(wr != NULL);
assert(wr != NULL); /* for Clang's static analyzer */
whc_get_state(wr->whc, &whcst);
thread_state_asleep(lookup_thread_state());
dds_entity_unpin(wr_entity);
printf(" -- final state: unacked: %zu; min %"PRId64" (exp %"PRId64"); max %"PRId64" (exp %"PRId64")\n", whcst.unacked_bytes, whcst.min_seq, exp_min, whcst.max_seq, exp_max);
CU_ASSERT_EQUAL_FATAL (whcst.unacked_bytes, 0);
CU_ASSERT_EQUAL_FATAL (whcst.min_seq, exp_min);
CU_ASSERT_EQUAL_FATAL (whcst.max_seq, exp_max);
}
#define V DDS_DURABILITY_VOLATILE
#define TL DDS_DURABILITY_TRANSIENT_LOCAL
#define R DDS_RELIABILITY_RELIABLE
#define BE DDS_RELIABILITY_BEST_EFFORT
#define KA DDS_HISTORY_KEEP_ALL
#define KL DDS_HISTORY_KEEP_LAST
static void test_whc_end_state(dds_durability_kind_t d, dds_reliability_kind_t r, dds_history_kind_t h, int32_t hd, dds_history_kind_t dh,
int32_t dhd, bool lrd, bool rrd, int32_t ni, bool k, bool dl)
{
char name[100];
Space_Type1 sample = { 0, 0, 0 };
Space_Type3 sample_keyless = { 0, 0, 0 };
dds_entity_t reader, reader_remote, writer;
dds_entity_t topic;
dds_entity_t remote_topic;
dds_return_t ret;
int32_t s, i;
printf ("test_whc_end_state: %s, %s, %s(%d), durability %s(%d), readers: %u local, %u remote, instances: %u, key %u, deadline %"PRId64"\n",
d == V ? "volatile" : "TL",
r == BE ? "best-effort" : "reliable",
h == KA ? "keep-all" : "keep-last", h == KA ? 0 : hd,
dh == KA ? "keep-all" : "keep-last", dh == KA ? 0 : dhd,
lrd, rrd, ni, k,
dl ? DEADLINE_DURATION : INT64_C(-1));
dds_qset_durability (g_qos, d);
dds_qset_reliability (g_qos, r, DDS_INFINITY);
dds_qset_history (g_qos, h, h == KA ? 0 : hd);
dds_qset_deadline (g_qos, dl ? DEADLINE_DURATION : DDS_INFINITY);
dds_qset_durability_service (g_qos, 0, dh, dh == KA ? 0 : dhd, DDS_LENGTH_UNLIMITED, DDS_LENGTH_UNLIMITED, DDS_LENGTH_UNLIMITED);
create_topic_name ("ddsc_whc_end_state_test", g_topic_nr++, name, sizeof name);
topic = dds_create_topic (g_participant, k ? &Space_Type1_desc : &Space_Type3_desc, name, NULL, NULL);
CU_ASSERT_FATAL(topic > 0);
remote_topic = dds_create_topic (g_remote_participant, k ? &Space_Type1_desc : &Space_Type3_desc, name, NULL, NULL);
CU_ASSERT_FATAL(remote_topic > 0);
writer = dds_create_writer (g_publisher, topic, g_qos, NULL);
CU_ASSERT_FATAL(writer > 0);
ret = dds_set_status_mask(writer, DDS_PUBLICATION_MATCHED_STATUS);
CU_ASSERT_FATAL (ret == DDS_RETCODE_OK)
reader = lrd ? create_and_sync_reader (g_subscriber, topic, g_qos, writer) : 0;
reader_remote = rrd ? create_and_sync_reader (g_remote_subscriber, remote_topic, g_qos, writer) : 0;
for (s = 0; s < SAMPLE_COUNT; s++)
{
if (k)
for (i = 0; i < ni; i++)
{
sample.long_1 = (int32_t)i;
ret = dds_write (writer, &sample);
CU_ASSERT_FATAL (ret == DDS_RETCODE_OK);
}
else
{
ret = dds_write (writer, &sample_keyless);
CU_ASSERT_FATAL (ret == DDS_RETCODE_OK);
}
}
/* delete readers, wait until no matching reader */
if (rrd)
{
ret = dds_delete (reader_remote);
CU_ASSERT_FATAL (ret == DDS_RETCODE_OK);
}
if (lrd)
{
ret = dds_delete (reader);
CU_ASSERT_FATAL (ret == DDS_RETCODE_OK);
}
while (1)
{
dds_publication_matched_status_t st;
ret = dds_get_publication_matched_status (writer, &st);
CU_ASSERT_FATAL (ret == DDS_RETCODE_OK);
if (st.current_count == 0)
break;
dds_sleepfor (DDS_MSECS (1));
}
/* check whc state */
int32_t exp_max = (d == TL) ? ni * SAMPLE_COUNT : -1;
int32_t exp_min = (d == TL) ? ((dh == KA) ? 1 : exp_max - dhd * ni + 1) : -1;
check_whc_state (writer, exp_min, exp_max);
dds_delete (writer);
dds_delete (remote_topic);
dds_delete (topic);
}
#define ARRAY_LEN(A) ((int32_t)(sizeof(A) / sizeof(A[0])))
CU_Test(ddsc_whc, check_end_state, .init=whc_init, .fini=whc_fini, .timeout=30)
{
dds_durability_kind_t dur[] = {V, TL};
dds_reliability_kind_t rel[] = {BE, R};
dds_history_kind_t hist[] = {KA, KL};
dds_history_kind_t dhist[] = {KA, KL};
int32_t hist_depth[] = {1, 3};
int32_t dhist_depth[] = {1, 3};
bool loc_rd[] = {false, true};
bool rem_rd[] = {false, true};
int32_t n_inst[] = {1, 3};
bool keyed[] = {false, true};
#ifdef DDSI_INCLUDE_DEADLINE_MISSED
bool deadline[] = {false, true};
#else
bool deadline[] = {false};
#endif
int32_t i_d, i_r, i_h, i_hd, i_dh, i_dhd, i_lrd, i_rrd, i_ni, i_k, i_dl;
for (i_d = 0; i_d < ARRAY_LEN(dur); i_d++)
for (i_r = 0; i_r < ARRAY_LEN(rel); i_r++)
for (i_h = 0; i_h < ARRAY_LEN(hist); i_h++)
for (i_hd = 0; i_hd < ARRAY_LEN(hist_depth); i_hd++)
for (i_dh = 0; i_dh < ARRAY_LEN(dhist); i_dh++)
for (i_dhd = 0; i_dhd < ARRAY_LEN(dhist_depth); i_dhd++)
for (i_lrd = 0; i_lrd < ARRAY_LEN(loc_rd); i_lrd++)
for (i_rrd = 0; i_rrd < ARRAY_LEN(rem_rd); i_rrd++)
for (i_ni = 0; i_ni < ARRAY_LEN(n_inst); i_ni++)
for (i_k = 0; i_k < ARRAY_LEN(keyed); i_k++)
for (i_dl = 0; i_dl < ARRAY_LEN(deadline); i_dl++)
{
if (rel[i_r] == BE && dur[i_d] == TL)
continue;
else if (hist[i_h] == KA && i_hd > 0)
continue;
else if (dhist[i_dh] == KA && i_dhd > 0)
continue;
else
{
test_whc_end_state (dur[i_d], rel[i_r], hist[i_h], hist_depth[i_hd], dhist[i_dh], dhist_depth[i_dhd],
loc_rd[i_lrd], rem_rd[i_rrd], keyed[i_k] ? n_inst[i_ni] : 1, keyed[i_k], deadline[i_dl]);
}
}
}
#undef ARRAY_LEN
#undef V
#undef TL
#undef R
#undef BE
#undef KA
#undef KL