Merge branch 'master' into security
Signed-off-by: Erik Boasson <eb@ilities.com>
This commit is contained in:
		
						commit
						ad58db0721
					
				
					 158 changed files with 6915 additions and 3361 deletions
				
			
		| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										44
									
								
								src/core/ddsc/include/dds/ddsc/dds_internal_api.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								src/core/ddsc/include/dds/ddsc/dds_internal_api.h
									
										
									
									
									
										Normal 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
 | 
			
		||||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 (
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -18,8 +18,6 @@
 | 
			
		|||
extern "C" {
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
void dds_sample_free_contents (char * data, const uint32_t * ops);
 | 
			
		||||
 | 
			
		||||
#if defined (__cplusplus)
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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" {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
											
										
									
								
							| 
						 | 
				
			
			@ -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;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										484
									
								
								src/core/ddsc/tests/deadline.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										484
									
								
								src/core/ddsc/tests/deadline.c
									
										
									
									
									
										Normal 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);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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");
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										608
									
								
								src/core/ddsc/tests/multi_sertopic.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										608
									
								
								src/core/ddsc/tests/multi_sertopic.c
									
										
									
									
									
										Normal 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);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
									
								
							
							
						
						
									
										279
									
								
								src/core/ddsc/tests/whc.c
									
										
									
									
									
										Normal 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
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue