Merge branch 'master' into merge6

Signed-off-by: Martin Bremmer <martin.bremmer@adlinktech.com>
This commit is contained in:
Martin Bremmer 2019-12-13 12:59:37 +01:00
commit 660d495746
124 changed files with 5049 additions and 1672 deletions

View file

@ -747,27 +747,50 @@ dds_create_participant(
* @brief Creates a domain with a given configuration
*
* To explicitly create a domain based on a configuration passed as a string.
* Normally, the domain is created implicitly on the first call to
* dds_create_particiant based on the configuration specified throught
* the environment. This function allows to by-pass this behaviour.
*
* It will not be created if a domain with the given domain id already exists.
* This could have been created implicitly by a dds_create_participant().
*
* Please be aware that the given domain_id always takes precedence over the
* configuration.
*
* | domain_id | domain id in config | result
* +-----------+---------------------+----------
* | n | any (or absent) | n, config is used
* | n | m == n | n, config is used
* | n | m != n | n, config is ignored: default
*
* Config models:
* 1: <CycloneDDS>
* <Domain id="X">...</Domain>
* <Domain .../>
* </CycloneDDS>
* where ... is all that can today be set in children of CycloneDDS
* with the exception of the id
* 2: <CycloneDDS>
* <Domain><Id>X</Id></Domain>
* ...
* </CycloneDDS>
* legacy form, domain id must be the first element in the file with
* a value (if nothing has been set previously, it a warning is good
* enough)
*
* Using NULL or "" as config will create a domain with default settings.
*
*
* @param[in] domain The domain to be created. DEFAULT_DOMAIN is not allowed.
* @param[in] config A configuration string containing file names and/or XML fragments representing the configuration.
*
* @returns A return code
* @returns A valid entity handle or an error code.
*
* @retval DDS_RETCODE_OK
* The domain with the domain identifier has been created from
* given configuration string.
* @retval DDS_RETCODE_BAD_PARAMETER
* Illegal value for domain id or the configfile parameter is NULL.
* @retval DDS_PRECONDITION_NOT_MET
* @retval DDS_RETCODE_PRECONDITION_NOT_MET
* The domain already existed and cannot be created again.
* @retval DDS_RETCODE_ERROR
* An internal error has occurred.
*/
DDS_EXPORT dds_return_t
DDS_EXPORT dds_entity_t
dds_create_domain(const dds_domainid_t domain, const char *config);
/**
@ -777,15 +800,10 @@ dds_create_domain(const dds_domainid_t domain, const char *config);
* For instance, it will return the Participant that was used when
* creating a Publisher (when that Publisher was provided here).
*
* When a reader or a writer are created with a partition, then a
* subscriber or publisher respectively are created implicitly. These
* implicit subscribers or publishers will be deleted automatically
* when the reader or writer is deleted. However, when this function
* returns such an implicit entity, it is from there on out considered
* 'explicit'. This means that it isn't deleted automatically anymore.
* The application should explicitly call dds_delete on those entities
* now (or delete the parent participant which will delete all entities
* within its hierarchy).
* When a reader or a writer are created with a participant, then a
* subscriber or publisher are created implicitly.
* This function will return the implicit parent and not the used
* participant.
*
* @param[in] entity Entity from which to get its parent.
*
@ -852,15 +870,10 @@ dds_get_participant(dds_entity_t entity);
* When supplying NULL as list and 0 as size, you can use this to acquire
* the number of children without having to pre-allocate a list.
*
* When a reader or a writer are created with a partition, then a
* subscriber or publisher respectively are created implicitly. These
* implicit subscribers or publishers will be deleted automatically
* when the reader or writer is deleted. However, when this function
* returns such an implicit entity, it is from there on out considered
* 'explicit'. This means that it isn't deleted automatically anymore.
* The application should explicitly call dds_delete on those entities
* now (or delete the parent participant which will delete all entities
* within its hierarchy).
* When a reader or a writer are created with a participant, then a
* subscriber or publisher are created implicitly.
* When used on the participant, this function will return the implicit
* subscriber and/or publisher and not the related reader/writer.
*
* @param[in] entity Entity from which to get its children.
* @param[out] children Pre-allocated array to contain the found children.
@ -869,7 +882,7 @@ dds_get_participant(dds_entity_t entity);
* @returns Number of children or an error code.
*
* @retval >=0
* Number of childer found children (can be larger than 'size').
* Number of found children (can be larger than 'size').
* @retval DDS_RETCODE_ERROR
* An internal error has occurred.
* @retval DDS_RETCODE_BAD_PARAMETER
@ -1200,6 +1213,7 @@ dds_wait_for_acks(dds_entity_t publisher_or_writer, dds_duration_t timeout);
/**
* @brief Creates a new instance of a DDS reader.
*
* When a participant is used to create a reader, an implicit subscriber is created.
* This implicit subscriber will be deleted automatically when the created reader
* is deleted.
*
@ -1226,6 +1240,7 @@ dds_create_reader(
/**
* @brief Creates a new instance of a DDS reader with a custom history cache.
*
* When a participant is used to create a reader, an implicit subscriber is created.
* This implicit subscriber will be deleted automatically when the created reader
* is deleted.
*
@ -1273,6 +1288,7 @@ dds_reader_wait_for_historical_data(
/**
* @brief Creates a new instance of a DDS writer.
*
* When a participant is used to create a writer, an implicit publisher is created.
* This implicit publisher will be deleted automatically when the created writer
* is deleted.
*
@ -1696,12 +1712,28 @@ DDS_EXPORT void
dds_write_flush(dds_entity_t writer);
/**
* @brief Write a CDR serialized value of a data instance
* @brief Write a serialized value of a data instance
*
* This call causes the writer to write the serialized value that is provided
* in the serdata argument.
*
* @param[in] writer The writer entity.
* @param[in] serdata CDR serialized value to be written.
* @param[in] serdata Serialized value to be written.
*
* @returns A dds_return_t indicating success or failure.
*
* @retval DDS_RETCODE_OK
* The writer successfully wrote the serialized value.
* @retval DDS_RETCODE_ERROR
* An internal error has occurred.
* @retval DDS_RETCODE_BAD_PARAMETER
* One of the given arguments is not valid.
* @retval DDS_RETCODE_ILLEGAL_OPERATION
* The operation is invoked on an inappropriate object.
* @retval DDS_RETCODE_ALREADY_DELETED
* The entity has already been deleted.
* @retval DDS_RETCODE_TIMEOUT
* The writer failed to write the serialized value reliably within the specified max_blocking_time.
*/
DDS_EXPORT dds_return_t
dds_writecdr(dds_entity_t writer, struct ddsi_serdata *serdata);
@ -2652,6 +2684,44 @@ dds_take_mask_wl(
uint32_t maxs,
uint32_t mask);
/**
* @brief Access the collection of serialized data values (of same type) and
* sample info from the data reader, readcondition or querycondition.
*
* This call accesses the serialized data from the data reader, readcondition or
* querycondition and makes it available to the application. The serialized data
* is made available through \ref ddsi_serdata structures. Once read the data is
* removed from the reader and cannot be 'read' or 'taken' again.
*
* Return value provides information about the number of samples read, which will
* be <= maxs. Based on the count, the buffer will contain serialized data to be
* read only when valid_data bit in sample info structure is set.
* The buffer required for data values, could be allocated explicitly or can
* use the memory from data reader to prevent copy. In the latter case, buffer and
* sample_info should be returned back, once it is no longer using the data.
*
* @param[in] reader_or_condition Reader, readcondition or querycondition entity.
* @param[out] buf An array of pointers to \ref ddsi_serdata structures that contain
* the serialized data. The pointers can be NULL.
* @param[in] maxs Maximum number of samples to read.
* @param[out] si Pointer to an array of \ref dds_sample_info_t returned for each data value.
* @param[in] mask Filter the data based on dds_sample_state_t|dds_view_state_t|dds_instance_state_t.
*
* @returns A dds_return_t with the number of samples read or an error code.
*
* @retval >=0
* Number of samples read.
* @retval DDS_RETCODE_ERROR
* An internal error has occurred.
* @retval DDS_RETCODE_BAD_PARAMETER
* One of the given arguments is not valid.
* @retval DDS_RETCODE_ILLEGAL_OPERATION
* The operation is invoked on an inappropriate object.
* @retval DDS_RETCODE_ALREADY_DELETED
* The entity has already been deleted.
* @retval DDS_RETCODE_PRECONDITION_NOT_MET
* The precondition for this operation is not met.
*/
DDS_EXPORT dds_return_t
dds_takecdr(
dds_entity_t reader_or_condition,
@ -3244,6 +3314,29 @@ dds_get_matched_publication_data (
dds_entity_t reader,
dds_instance_handle_t ih);
/**
* @brief This operation manually asserts the liveliness of a writer
* or domain participant.
*
* This operation manually asserts the liveliness of a writer
* or domain participant. This is used in combination with the Liveliness
* QoS policy to indicate that the entity remains active. This operation need
* only be used if the liveliness kind in the QoS is either
* DDS_LIVELINESS_MANUAL_BY_PARTICIPANT or DDS_LIVELINESS_MANUAL_BY_TOPIC.
*
* @param[in] entity A domain participant or writer
*
* @returns A dds_return_t indicating success or failure.
*
* @retval DDS_RETCODE_OK
* The operation was successful.
* @retval DDS_RETCODE_ILLEGAL_OPERATION
* The operation is invoked on an inappropriate object.
*/
DDS_EXPORT dds_return_t
dds_assert_liveliness (
dds_entity_t entity);
#if defined (__cplusplus)
}
#endif

View file

@ -18,7 +18,7 @@
extern "C" {
#endif
DDS_EXPORT dds_return_t dds_domain_create_internal (dds_domain **domain_out, dds_domainid_t id, bool use_existing, const char *config) ddsrt_nonnull((1,4));
DDS_EXPORT dds_entity_t dds_domain_create_internal (dds_domain **domain_out, dds_domainid_t id, bool implicit, const char *config) ddsrt_nonnull((1,4));
DDS_EXPORT dds_domain *dds_domain_find_locked (dds_domainid_t id);
#if defined (__cplusplus)

View file

@ -24,6 +24,7 @@ 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);
@ -38,6 +39,12 @@ dds_entity_register_child (
DDS_EXPORT void
dds_entity_add_ref_locked(dds_entity *e);
DDS_EXPORT void
dds_entity_drop_ref(dds_entity *e);
DDS_EXPORT void
dds_entity_unpin_and_drop_ref (dds_entity *e);
#define DEFINE_ENTITY_LOCK_UNLOCK(qualifier_, type_, kind_) \
qualifier_ dds_return_t type_##_lock (dds_entity_t hdl, type_ **x) \
{ \
@ -96,6 +103,8 @@ dds_entity_pin (
dds_entity_t hdl,
dds_entity **eptr);
DDS_EXPORT dds_return_t dds_entity_pin_for_delete (dds_entity_t hdl, bool explicit, dds_entity **eptr);
DDS_EXPORT void dds_entity_unpin (
dds_entity *e);

View file

@ -72,9 +72,11 @@ typedef int32_t dds_handle_t;
/* Closing & closed can be combined, but having two gives a means for enforcing
that close() be called first, then close_wait(), and then delete(). */
#define HDL_FLAG_CLOSING (0x80000000u)
#define HDL_FLAG_CLOSED (0x40000000u)
#define HDL_FLAG_PENDING (0x20000000u)
#define HDL_FLAG_CLOSING (0x80000000u)
#define HDL_FLAG_DELETE_DEFERRED (0x40000000u)
#define HDL_FLAG_PENDING (0x20000000u)
#define HDL_FLAG_IMPLICIT (0x10000000u)
#define HDL_FLAG_ALLOW_CHILDREN (0x08000000u) /* refc counts children */
struct dds_handle_link {
dds_handle_t hdl;
@ -116,7 +118,9 @@ dds_handle_server_fini(void);
*/
DDS_EXPORT dds_handle_t
dds_handle_create(
struct dds_handle_link *link);
struct dds_handle_link *link,
bool implicit,
bool allow_children);
/*
@ -124,7 +128,7 @@ dds_handle_create(
*/
DDS_EXPORT dds_return_t
dds_handle_register_special (
struct dds_handle_link *link, dds_handle_t handle);
struct dds_handle_link *link, bool implicit, bool allow_children, dds_handle_t handle);
DDS_EXPORT void dds_handle_unpend (struct dds_handle_link *link);
@ -181,6 +185,8 @@ DDS_EXPORT void
dds_handle_unpin(
struct dds_handle_link *link);
int32_t dds_handle_pin_for_delete (dds_handle_t hdl, bool explicit, struct dds_handle_link **link);
bool dds_handle_drop_childref_and_pin (struct dds_handle_link *link, bool may_delete_parent);
/*
* Check if the handle is closed.
@ -195,11 +201,15 @@ dds_handle_unpin(
DDS_EXPORT void dds_handle_add_ref (struct dds_handle_link *link);
DDS_EXPORT bool dds_handle_drop_ref (struct dds_handle_link *link);
DDS_EXPORT bool dds_handle_close (struct dds_handle_link *link);
DDS_EXPORT bool dds_handle_unpin_and_drop_ref (struct dds_handle_link *link);
DDS_EXPORT inline bool dds_handle_is_closed (struct dds_handle_link *link) {
return (ddsrt_atomic_ld32 (&link->cnt_flags) & (HDL_FLAG_CLOSED | HDL_FLAG_CLOSING)) != 0;
return (ddsrt_atomic_ld32 (&link->cnt_flags) & HDL_FLAG_CLOSING) != 0;
}
DDS_EXPORT bool dds_handle_is_not_refd (struct dds_handle_link *link);
#if defined (__cplusplus)
}
#endif

View file

@ -21,6 +21,13 @@ extern "C" {
DEFINE_ENTITY_LOCK_UNLOCK(inline, dds_publisher, DDS_KIND_PUBLISHER)
dds_entity_t
dds__create_publisher_l(
struct dds_participant *participant, /* entity-lock must be held */
bool implicit,
const dds_qos_t *qos,
const dds_listener_t *listener);
dds_return_t dds_publisher_begin_coherent (dds_entity_t e);
dds_return_t dds_publisher_end_coherent (dds_entity_t e);

View file

@ -24,6 +24,7 @@ DEFINE_ENTITY_LOCK_UNLOCK(inline, dds_subscriber, DDS_KIND_SUBSCRIBER)
dds_entity_t
dds__create_subscriber_l(
struct dds_participant *participant, /* entity-lock must be held */
bool implicit,
const dds_qos_t *qos,
const dds_listener_t *listener);

View file

@ -245,7 +245,7 @@ typedef struct dds_participant {
typedef struct dds_reader {
struct dds_entity m_entity;
const struct dds_topic *m_topic;
struct dds_topic *m_topic;
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 +265,7 @@ typedef struct dds_reader {
typedef struct dds_writer {
struct dds_entity m_entity;
const struct dds_topic *m_topic;
struct dds_topic *m_topic;
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 )*/

View file

@ -131,7 +131,7 @@ bool dds__validate_builtin_reader_qos (const dds_domain *dom, dds_entity_t topic
static dds_entity_t dds__create_builtin_subscriber (dds_participant *participant)
{
dds_qos_t *qos = dds__create_builtin_qos ();
dds_entity_t sub = dds__create_subscriber_l (participant, qos, NULL);
dds_entity_t sub = dds__create_subscriber_l (participant, false, qos, NULL);
dds_delete_qos (qos);
return sub;
}

View file

@ -48,16 +48,14 @@ static int dds_domain_compare (const void *va, const void *vb)
static const ddsrt_avl_treedef_t dds_domaintree_def = DDSRT_AVL_TREEDEF_INITIALIZER (
offsetof (dds_domain, m_node), offsetof (dds_domain, m_id), dds_domain_compare, 0);
static dds_return_t dds_domain_init (dds_domain *domain, dds_domainid_t domain_id, const char *config)
static dds_entity_t dds_domain_init (dds_domain *domain, dds_domainid_t domain_id, const char *config, bool implicit)
{
dds_return_t ret = DDS_RETCODE_OK;
dds_entity_t domh;
uint32_t len;
dds_entity_t domain_handle;
if ((domain_handle = dds_entity_init (&domain->m_entity, &dds_global.m_entity, DDS_KIND_DOMAIN, NULL, NULL, 0)) < 0)
return domain_handle;
if ((domh = dds_entity_init (&domain->m_entity, &dds_global.m_entity, DDS_KIND_DOMAIN, implicit, NULL, NULL, 0)) < 0)
return domh;
domain->m_entity.m_domain = domain;
domain->m_entity.m_flags |= DDS_ENTITY_IMPLICIT;
domain->m_entity.m_iid = ddsi_iid_gen ();
domain->gv.tstart = now ();
@ -90,7 +88,7 @@ static dds_return_t dds_domain_init (dds_domain *domain, dds_domainid_t domain_i
if (domain->cfgst == NULL)
{
DDS_ILOG (DDS_LC_CONFIG, domain_id, "Failed to parse configuration\n");
ret = DDS_RETCODE_ERROR;
domh = DDS_RETCODE_ERROR;
goto fail_config;
}
@ -100,14 +98,14 @@ static dds_return_t dds_domain_init (dds_domain *domain, dds_domainid_t domain_i
if (rtps_config_prep (&domain->gv, domain->cfgst) != 0)
{
DDS_ILOG (DDS_LC_CONFIG, domain->m_id, "Failed to configure RTPS\n");
ret = DDS_RETCODE_ERROR;
domh = DDS_RETCODE_ERROR;
goto fail_rtps_config;
}
if (rtps_init (&domain->gv) < 0)
{
DDS_ILOG (DDS_LC_CONFIG, domain->m_id, "Failed to initialize RTPS\n");
ret = DDS_RETCODE_ERROR;
domh = DDS_RETCODE_ERROR;
goto fail_rtps_init;
}
@ -122,14 +120,14 @@ static dds_return_t dds_domain_init (dds_domain *domain, dds_domainid_t domain_i
if (dds_global.threadmon == NULL)
{
DDS_ILOG (DDS_LC_CONFIG, domain->m_id, "Failed to create a thread liveliness monitor\n");
ret = DDS_RETCODE_OUT_OF_RESOURCES;
domh = DDS_RETCODE_OUT_OF_RESOURCES;
goto fail_threadmon_new;
}
/* FIXME: thread properties */
if (ddsi_threadmon_start (dds_global.threadmon, "threadmon") < 0)
{
DDS_ILOG (DDS_LC_ERROR, domain->m_id, "Failed to start the thread liveliness monitor\n");
ret = DDS_RETCODE_ERROR;
domh = DDS_RETCODE_ERROR;
goto fail_threadmon_start;
}
}
@ -159,16 +157,17 @@ static dds_return_t dds_domain_init (dds_domain *domain, dds_domainid_t domain_i
if (rtps_start (&domain->gv) < 0)
{
DDS_ILOG (DDS_LC_CONFIG, domain->m_id, "Failed to start RTPS\n");
ret = DDS_RETCODE_ERROR;
domh = DDS_RETCODE_ERROR;
goto fail_rtps_start;
}
if (domain->gv.config.liveliness_monitoring)
ddsi_threadmon_register_domain (dds_global.threadmon, &domain->gv);
dds_entity_init_complete (&domain->m_entity);
return DDS_RETCODE_OK;
return domh;
fail_rtps_start:
dds__builtin_fini (domain);
if (domain->gv.config.liveliness_monitoring && dds_global.threadmon_count == 1)
ddsi_threadmon_stop (dds_global.threadmon);
fail_threadmon_start:
@ -184,7 +183,7 @@ fail_rtps_config:
config_fini (domain->cfgst);
fail_config:
dds_handle_delete (&domain->m_entity.m_hdllink);
return ret;
return domh;
}
dds_domain *dds_domain_find_locked (dds_domainid_t id)
@ -192,37 +191,25 @@ dds_domain *dds_domain_find_locked (dds_domainid_t id)
return ddsrt_avl_lookup (&dds_domaintree_def, &dds_global.m_domains, &id);
}
dds_return_t dds_domain_create_internal (dds_domain **domain_out, dds_domainid_t id, bool use_existing, const char *config)
dds_entity_t dds_domain_create_internal (dds_domain **domain_out, dds_domainid_t id, bool implicit, const char *config)
{
struct dds_domain *dom;
dds_return_t ret;
dds_entity_t domh = DDS_RETCODE_ERROR;
/* FIXME: should perhaps lock parent object just like everywhere */
ddsrt_mutex_lock (&dds_global.m_mutex);
retry:
if (id != DDS_DOMAIN_DEFAULT)
{
if ((dom = dds_domain_find_locked (id)) == NULL)
ret = DDS_RETCODE_NOT_FOUND;
else
ret = DDS_RETCODE_OK;
}
dom = dds_domain_find_locked (id);
else
{
if ((dom = ddsrt_avl_find_min (&dds_domaintree_def, &dds_global.m_domains)) != NULL)
ret = DDS_RETCODE_OK;
else
ret = DDS_RETCODE_NOT_FOUND;
}
dom = ddsrt_avl_find_min (&dds_domaintree_def, &dds_global.m_domains);
switch (ret)
if (dom)
{
case DDS_RETCODE_OK:
if (!use_existing)
{
ret = DDS_RETCODE_PRECONDITION_NOT_MET;
break;
}
if (!implicit)
domh = DDS_RETCODE_PRECONDITION_NOT_MET;
else
{
ddsrt_mutex_lock (&dom->m_entity.m_mutex);
if (dds_handle_is_closed (&dom->m_entity.m_hdllink))
{
@ -230,52 +217,54 @@ dds_return_t dds_domain_create_internal (dds_domain **domain_out, dds_domainid_t
ddsrt_cond_wait (&dds_global.m_cond, &dds_global.m_mutex);
goto retry;
}
else
dds_entity_add_ref_locked (&dom->m_entity);
dds_handle_repin (&dom->m_entity.m_hdllink);
domh = dom->m_entity.m_hdllink.hdl;
ddsrt_mutex_unlock (&dom->m_entity.m_mutex);
*domain_out = dom;
}
}
else
{
dom = dds_alloc (sizeof (*dom));
if ((domh = dds_domain_init (dom, id, config, implicit)) < 0)
dds_free (dom);
else
{
ddsrt_mutex_lock (&dom->m_entity.m_mutex);
ddsrt_avl_insert (&dds_domaintree_def, &dds_global.m_domains, dom);
dds_entity_register_child (&dds_global.m_entity, &dom->m_entity);
if (implicit)
{
dds_entity_add_ref_locked (&dom->m_entity);
ddsrt_mutex_unlock (&dom->m_entity.m_mutex);
*domain_out = dom;
dds_handle_repin (&dom->m_entity.m_hdllink);
}
break;
case DDS_RETCODE_NOT_FOUND:
dom = dds_alloc (sizeof (*dom));
if ((ret = dds_domain_init (dom, id, config)) < 0)
dds_free (dom);
else
{
ddsrt_mutex_lock (&dom->m_entity.m_mutex);
ddsrt_avl_insert (&dds_domaintree_def, &dds_global.m_domains, dom);
dds_entity_register_child (&dds_global.m_entity, &dom->m_entity);
dds_entity_add_ref_locked (&dom->m_entity);
ddsrt_mutex_unlock (&dom->m_entity.m_mutex);
*domain_out = dom;
}
break;
domh = dom->m_entity.m_hdllink.hdl;
ddsrt_mutex_unlock (&dom->m_entity.m_mutex);
*domain_out = dom;
}
}
ddsrt_mutex_unlock (&dds_global.m_mutex);
return ret;
return domh;
}
dds_return_t dds_create_domain(const dds_domainid_t domain, const char *config)
dds_entity_t dds_create_domain (const dds_domainid_t domain, const char *config)
{
dds_domain *dom;
dds_entity_t ret;
if (domain == DDS_DOMAIN_DEFAULT || config == NULL)
if (domain == DDS_DOMAIN_DEFAULT)
return DDS_RETCODE_BAD_PARAMETER;
if (config == NULL)
config = "";
/* Make sure DDS instance is initialized. */
if ((ret = dds_init ()) < 0)
goto err_dds_init;
return ret;
if ((ret = dds_domain_create_internal (&dom, domain, false, config)) < 0)
goto err_domain_create;
return DDS_RETCODE_OK;
err_domain_create:
dds_delete_impl_pinned (&dds_global.m_entity, DIS_EXPLICIT);
err_dds_init:
ret = dds_domain_create_internal (&dom, domain, false, config);
dds_entity_unpin_and_drop_ref (&dds_global.m_entity);
return ret;
}
@ -368,5 +357,5 @@ void dds_write_set_batch (bool enable)
}
}
ddsrt_mutex_unlock (&dds_global.m_mutex);
dds_delete_impl_pinned (&dds_global.m_entity, DIS_EXPLICIT);
dds_entity_unpin_and_drop_ref (&dds_global.m_entity);
}

View file

@ -22,7 +22,9 @@
#include "dds__qos.h"
#include "dds__topic.h"
#include "dds/version.h"
#include "dds/ddsi/ddsi_pmd.h"
#include "dds/ddsi/q_xqos.h"
#include "dds/ddsi/q_transmit.h"
extern inline dds_entity *dds_entity_from_handle_link (struct dds_handle_link *hdllink);
extern inline bool dds_entity_is_enabled (const dds_entity *e);
@ -80,11 +82,34 @@ const ddsrt_avl_treedef_t dds_entity_children_td = DDSRT_AVL_TREEDEF_INITIALIZER
static void dds_entity_observers_signal (dds_entity *observed, uint32_t status);
static void dds_entity_observers_signal_delete (dds_entity *observed);
static dds_return_t dds_delete_impl (dds_entity_t entity, enum delete_impl_state delstate);
static dds_return_t really_delete_pinned_closed_locked (struct dds_entity *e, enum delete_impl_state delstate);
void dds_entity_add_ref_locked (dds_entity *e)
{
dds_handle_add_ref (&e->m_hdllink);
}
void dds_entity_drop_ref (dds_entity *e)
{
if (dds_handle_drop_ref (&e->m_hdllink))
{
dds_return_t ret = dds_delete_impl(e->m_hdllink.hdl, DIS_EXPLICIT);
assert (ret == DDS_RETCODE_OK);
(void) ret;
}
}
void dds_entity_unpin_and_drop_ref (dds_entity *e)
{
if (dds_handle_unpin_and_drop_ref (&e->m_hdllink))
{
dds_return_t ret = dds_delete_impl(e->m_hdllink.hdl, DIS_EXPLICIT);
assert (ret == DDS_RETCODE_OK);
(void) ret;
}
}
static bool entity_has_status (const dds_entity *e)
{
switch (e->m_kind)
@ -110,7 +135,7 @@ static bool entity_has_status (const dds_entity *e)
return false;
}
dds_entity_t dds_entity_init (dds_entity *e, dds_entity *parent, dds_entity_kind_t kind, dds_qos_t *qos, const dds_listener_t *listener, status_mask_t mask)
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;
@ -125,6 +150,8 @@ dds_entity_t dds_entity_init (dds_entity *e, dds_entity *parent, dds_entity_kind
/* TODO: CHAM-96: Implement dynamic enabling of entity. */
e->m_flags |= DDS_ENTITY_ENABLED;
if (implicit)
e->m_flags |= DDS_ENTITY_IMPLICIT;
/* set the status enable based on kind */
if (entity_has_status (e))
@ -162,12 +189,14 @@ dds_entity_t dds_entity_init (dds_entity *e, dds_entity *parent, dds_entity_kind
if (kind == DDS_KIND_CYCLONEDDS)
{
if ((handle = dds_handle_register_special (&e->m_hdllink, DDS_CYCLONEDDS_HANDLE)) <= 0)
if ((handle = dds_handle_register_special (&e->m_hdllink, implicit, true, DDS_CYCLONEDDS_HANDLE)) <= 0)
return (dds_entity_t) handle;
}
else
{
if ((handle = dds_handle_create (&e->m_hdllink)) <= 0)
/* 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)
return (dds_entity_t) handle;
}
@ -182,22 +211,53 @@ void dds_entity_init_complete (dds_entity *entity)
void dds_entity_register_child (dds_entity *parent, dds_entity *child)
{
/* parent must be tracking children in its refc, or children can't be added */
assert (ddsrt_atomic_ld32 (&parent->m_hdllink.cnt_flags) & HDL_FLAG_ALLOW_CHILDREN);
assert (child->m_iid != 0);
assert (ddsrt_avl_lookup (&dds_entity_children_td, &parent->m_children, &child->m_iid) == NULL);
ddsrt_avl_insert (&dds_entity_children_td, &parent->m_children, child);
dds_entity_add_ref_locked (parent);
}
static dds_entity *next_non_topic_child (ddsrt_avl_tree_t *remaining_children)
static dds_entity *get_first_child (ddsrt_avl_tree_t *remaining_children, bool ignore_topics)
{
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))
{
if (dds_entity_kind (e) != DDS_KIND_TOPIC)
if ((!ignore_topics) || (dds_entity_kind(e) != DDS_KIND_TOPIC))
return e;
}
return NULL;
}
static void delete_children(struct dds_entity *parent, bool ignore_topics)
{
dds_entity *child;
dds_return_t ret;
ddsrt_mutex_lock (&parent->m_mutex);
while ((child = get_first_child(&parent->m_children, ignore_topics)) != NULL)
{
dds_entity_t child_handle = child->m_hdllink.hdl;
/* 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);
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))
{
ddsrt_cond_wait (&parent->m_cond, &parent->m_mutex);
}
}
ddsrt_mutex_unlock (&parent->m_mutex);
}
#define TRACE_DELETE 0 /* FIXME: use DDS_LOG for this */
#if TRACE_DELETE
static const char *entity_kindstr (dds_entity_kind_t kind)
@ -227,13 +287,11 @@ static void print_delete (const dds_entity *e, enum delete_impl_state delstate ,
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) & 0xffff, (cm & 0x80000000) ? "closed" : "open",
e->m_hdllink.hdl, cm & 0xfff, (cm >> 12) & 0x7fff, (cm & 0x80000000) ? "closed" : "open",
ddsrt_avl_is_empty (&e->m_children) ? "childless" : "has-children");
}
#endif
static dds_return_t dds_delete_impl (dds_entity_t entity, enum delete_impl_state delstate);
dds_return_t dds_delete (dds_entity_t entity)
{
return dds_delete_impl (entity, DIS_EXPLICIT);
@ -252,53 +310,33 @@ static dds_return_t dds_delete_impl (dds_entity_t entity, enum delete_impl_state
{
dds_entity *e;
dds_return_t ret;
if ((ret = dds_entity_pin (entity, &e)) < 0)
return ret;
else
if ((ret = dds_entity_pin_for_delete (entity, (delstate != DIS_IMPLICIT), &e)) == DDS_RETCODE_OK)
return dds_delete_impl_pinned (e, delstate);
else if (ret == DDS_RETCODE_TRY_AGAIN) /* non-child refs exist */
return DDS_RETCODE_OK;
else
return ret;
}
dds_return_t dds_delete_impl_pinned (dds_entity *e, enum delete_impl_state delstate)
{
dds_entity *child;
dds_return_t ret;
/* Any number of threads pinning it, possibly in delete, or having pinned it and
trying to acquire m_mutex */
ddsrt_mutex_lock (&e->m_mutex);
#if TRACE_DELETE
print_delete (e, delstate, iid);
print_delete (e, delstate, e->m_iid);
#endif
/* If another thread was racing us in delete, it will have set the CLOSING flag
while holding m_mutex and we had better bail out. */
if (dds_handle_is_closed (&e->m_hdllink))
{
dds_entity_unlock (e);
return DDS_RETCODE_OK;
}
assert (dds_handle_is_closed (&e->m_hdllink));
return really_delete_pinned_closed_locked (e, delstate);
}
/* Ignore children calling up to delete an implicit parent if there are still
(or again) children */
if (delstate == DIS_IMPLICIT)
{
if (!((e->m_flags & DDS_ENTITY_IMPLICIT) && ddsrt_avl_is_empty (&e->m_children)))
{
dds_entity_unlock (e);
return DDS_RETCODE_OK;
}
}
/* Drop reference, atomically setting CLOSING if no other references remain.
FIXME: that's not quite right: this is really only for topics. After a call
to delete, the handle ought to become invalid even if the topic stays (and
should perhaps even be revivable via find_topic). */
if (! dds_handle_drop_ref (&e->m_hdllink))
{
dds_entity_unlock (e);
return DDS_RETCODE_OK;
}
static dds_return_t really_delete_pinned_closed_locked (struct dds_entity *e, enum delete_impl_state delstate)
{
dds_return_t ret;
/* No threads pinning it anymore, no need to worry about other threads deleting
it, but there can still be plenty of threads that have it pinned and are
@ -354,24 +392,8 @@ dds_return_t dds_delete_impl_pinned (dds_entity *e, enum delete_impl_state delst
*
* To circumvent the problem. We ignore topics in the first loop.
*/
ddsrt_mutex_lock (&e->m_mutex);
while ((child = next_non_topic_child (&e->m_children)) != NULL)
{
/* FIXME: dds_delete can fail if the child is being deleted in parallel, in which case: wait */
dds_entity_t child_handle = child->m_hdllink.hdl;
ddsrt_mutex_unlock (&e->m_mutex);
(void) dds_delete_impl (child_handle, DIS_FROM_PARENT);
ddsrt_mutex_lock (&e->m_mutex);
}
while ((child = ddsrt_avl_find_min (&dds_entity_children_td, &e->m_children)) != NULL)
{
assert (dds_entity_kind (child) == DDS_KIND_TOPIC);
dds_entity_t child_handle = child->m_hdllink.hdl;
ddsrt_mutex_unlock (&e->m_mutex);
(void) dds_delete_impl (child_handle, DIS_FROM_PARENT);
ddsrt_mutex_lock (&e->m_mutex);
}
ddsrt_mutex_unlock (&e->m_mutex);
delete_children(e, true /* ignore topics */);
delete_children(e, false /* delete topics */);
/* 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
@ -391,15 +413,16 @@ dds_return_t dds_delete_impl_pinned (dds_entity *e, enum delete_impl_state delst
ddsrt_mutex_lock (&p->m_mutex);
assert (ddsrt_avl_lookup (&dds_entity_children_td, &p->m_children, &e->m_iid) != NULL);
ddsrt_avl_delete (&dds_entity_children_td, &p->m_children, e);
if (dds_handle_drop_childref_and_pin (&p->m_hdllink, delstate != DIS_FROM_PARENT))
{
dds_handle_close(&p->m_hdllink);
assert (dds_handle_is_closed (&p->m_hdllink));
assert (dds_handle_is_not_refd (&p->m_hdllink));
assert (ddsrt_avl_is_empty (&p->m_children));
parent_to_delete = p;
}
/* trigger parent in case it is waiting in delete */
ddsrt_cond_broadcast (&p->m_cond);
if (delstate != DIS_FROM_PARENT && (p->m_flags & DDS_ENTITY_IMPLICIT) && ddsrt_avl_is_empty (&p->m_children))
{
if ((ret = dds_entity_pin (p->m_hdllink.hdl, &parent_to_delete)) < 0)
parent_to_delete = NULL;
}
ddsrt_mutex_unlock (&p->m_mutex);
}
@ -1121,6 +1144,19 @@ dds_return_t dds_entity_pin (dds_entity_t hdl, dds_entity **eptr)
}
}
dds_return_t dds_entity_pin_for_delete (dds_entity_t hdl, bool explicit, dds_entity **eptr)
{
dds_return_t hres;
struct dds_handle_link *hdllink;
if ((hres = dds_handle_pin_for_delete (hdl, explicit, &hdllink)) < 0)
return hres;
else
{
*eptr = dds_entity_from_handle_link (hdllink);
return DDS_RETCODE_OK;
}
}
void dds_entity_unpin (dds_entity *e)
{
dds_handle_unpin (&e->m_hdllink);
@ -1349,3 +1385,32 @@ dds_return_t dds_generic_unimplemented_operation (dds_entity_t handle, dds_entit
return dds_generic_unimplemented_operation_manykinds (handle, 1, &kind);
}
dds_return_t dds_assert_liveliness (dds_entity_t entity)
{
dds_return_t rc;
dds_entity *e, *ewr;
if ((rc = dds_entity_pin (entity, &e)) != DDS_RETCODE_OK)
return rc;
switch (dds_entity_kind (e))
{
case DDS_KIND_PARTICIPANT: {
write_pmd_message_guid (&e->m_domain->gv, &e->m_guid, PARTICIPANT_MESSAGE_DATA_KIND_MANUAL_LIVELINESS_UPDATE);
break;
}
case DDS_KIND_WRITER: {
if ((rc = dds_entity_lock (entity, DDS_KIND_WRITER, &ewr)) != DDS_RETCODE_OK)
return rc;
if ((rc = write_hb_liveliness (&e->m_domain->gv, &e->m_guid, ((struct dds_writer *)ewr)->m_xp)) != DDS_RETCODE_OK)
return rc;
dds_entity_unlock (e);
break;
}
default: {
rc = DDS_RETCODE_ILLEGAL_OPERATION;
break;
}
}
dds_entity_unpin (e);
return rc;
}

View file

@ -57,18 +57,18 @@ dds_entity_t dds_create_guardcondition (dds_entity_t owner)
}
dds_guardcond *gcond = dds_alloc (sizeof (*gcond));
dds_entity_t hdl = dds_entity_init (&gcond->m_entity, e, DDS_KIND_COND_GUARD, NULL, NULL, 0);
dds_entity_t hdl = dds_entity_init (&gcond->m_entity, e, DDS_KIND_COND_GUARD, false, NULL, NULL, 0);
gcond->m_entity.m_iid = ddsi_iid_gen ();
dds_entity_register_child (e, &gcond->m_entity);
dds_entity_init_complete (&gcond->m_entity);
dds_entity_unlock (e);
dds_delete_impl_pinned (&dds_global.m_entity, DIS_EXPLICIT);
dds_entity_unpin_and_drop_ref (&dds_global.m_entity);
return hdl;
err_entity_kind:
dds_entity_unlock (e);
err_entity_lock:
dds_delete_impl_pinned (&dds_global.m_entity, DIS_EXPLICIT);
dds_entity_unpin_and_drop_ref (&dds_global.m_entity);
return rc;
}

View file

@ -20,11 +20,36 @@
#include "dds__handles.h"
#include "dds__types.h"
#define HDL_REFCOUNT_MASK (0x0ffff000u)
#define HDL_REFCOUNT_MASK (0x07fff000u)
#define HDL_REFCOUNT_UNIT (0x00001000u)
#define HDL_REFCOUNT_SHIFT 12
#define HDL_PINCOUNT_MASK (0x00000fffu)
/*
"regular" entities other than topics:
- create makes it
- delete deletes it and its children immediately
- explicit domain: additional protection for bootstrapping complications need extra care
implicit entities other than topics (pub, sub, domain, cyclonedds):
- created "spontaneously" as a consequence of creating the writer/reader/participant
- delete of last child causes it to disappear
- explicit delete treated like a delete of a "regular" entity
- domain, cyclonedds: bootstrapping complications require additional protection
topics:
- create makes it
- never has children (so the handle's cnt_flags can have a different meaning)
- readers, writers keep it in existence
- delete deferred until no readers/writers exist
- an attempt at deleting it fails if in "deferred delete" state (or should it simply
return ok while doing nothing?), other operations keep going so, e.g., listeners
remain useful
built-in topics:
- implicit variant of a topic
*/
/* Maximum number of handles is INT32_MAX - 1, but as the allocator relies on a
random generator for finding a free one, the time spent in the dds_handle_create
increases with an increasing number of handles. 16M handles seems likely to be
@ -82,7 +107,7 @@ void dds_handle_server_fini (void)
cf & HDL_PINCOUNT_MASK, (cf & HDL_REFCOUNT_MASK) >> HDL_REFCOUNT_SHIFT,
cf & HDL_FLAG_PENDING ? " pending" : "",
cf & HDL_FLAG_CLOSING ? " closing" : "",
cf & HDL_FLAG_CLOSED ? " closed" : "");
cf & HDL_FLAG_DELETE_DEFERRED ? " delete-deferred" : "");
}
assert (ddsrt_hh_iter_first (handles.ht, &it) == NULL);
#endif
@ -94,9 +119,9 @@ void dds_handle_server_fini (void)
}
static bool hhadd (struct ddsrt_hh *ht, void *elem) { return ddsrt_hh_add (ht, elem); }
static dds_handle_t dds_handle_create_int (struct dds_handle_link *link)
static dds_handle_t dds_handle_create_int (struct dds_handle_link *link, bool implicit, bool refc_counts_children)
{
ddsrt_atomic_st32 (&link->cnt_flags, HDL_FLAG_PENDING | HDL_REFCOUNT_UNIT | 1u);
ddsrt_atomic_st32 (&link->cnt_flags, HDL_FLAG_PENDING | (implicit ? HDL_FLAG_IMPLICIT : HDL_REFCOUNT_UNIT) | (refc_counts_children ? HDL_FLAG_ALLOW_CHILDREN : 0) | 1u);
do {
do {
link->hdl = (int32_t) (ddsrt_random () & INT32_MAX);
@ -105,7 +130,7 @@ static dds_handle_t dds_handle_create_int (struct dds_handle_link *link)
return link->hdl;
}
dds_handle_t dds_handle_create (struct dds_handle_link *link)
dds_handle_t dds_handle_create (struct dds_handle_link *link, bool implicit, bool allow_children)
{
dds_handle_t ret;
ddsrt_mutex_lock (&handles.lock);
@ -117,14 +142,14 @@ dds_handle_t dds_handle_create (struct dds_handle_link *link)
else
{
handles.count++;
ret = dds_handle_create_int (link);
ret = dds_handle_create_int (link, implicit, allow_children);
ddsrt_mutex_unlock (&handles.lock);
assert (ret > 0);
}
return ret;
}
dds_return_t dds_handle_register_special (struct dds_handle_link *link, dds_handle_t handle)
dds_return_t dds_handle_register_special (struct dds_handle_link *link, bool implicit, bool allow_children, dds_handle_t handle)
{
dds_return_t ret;
if (handle <= 0)
@ -138,7 +163,7 @@ dds_return_t dds_handle_register_special (struct dds_handle_link *link, dds_hand
else
{
handles.count++;
ddsrt_atomic_st32 (&link->cnt_flags, HDL_FLAG_PENDING | HDL_REFCOUNT_UNIT | 1u);
ddsrt_atomic_st32 (&link->cnt_flags, HDL_FLAG_PENDING | (implicit ? HDL_FLAG_IMPLICIT : HDL_REFCOUNT_UNIT) | (allow_children ? HDL_FLAG_ALLOW_CHILDREN : 0) | 1u);
link->hdl = handle;
if (hhadd (handles.ht, link))
ret = handle;
@ -155,9 +180,9 @@ void dds_handle_unpend (struct dds_handle_link *link)
#ifndef NDEBUG
uint32_t cf = ddsrt_atomic_ld32 (&link->cnt_flags);
assert ((cf & HDL_FLAG_PENDING));
assert (!(cf & HDL_FLAG_CLOSED));
assert (!(cf & HDL_FLAG_DELETE_DEFERRED));
assert (!(cf & HDL_FLAG_CLOSING));
assert ((cf & HDL_REFCOUNT_MASK) >= HDL_REFCOUNT_UNIT);
assert ((cf & HDL_REFCOUNT_MASK) >= HDL_REFCOUNT_UNIT || (cf & HDL_FLAG_IMPLICIT));
assert ((cf & HDL_PINCOUNT_MASK) >= 1u);
#endif
ddsrt_atomic_and32 (&link->cnt_flags, ~HDL_FLAG_PENDING);
@ -171,7 +196,6 @@ int32_t dds_handle_delete (struct dds_handle_link *link)
if (!(cf & HDL_FLAG_PENDING))
{
assert (cf & HDL_FLAG_CLOSING);
assert (cf & HDL_FLAG_CLOSED);
assert ((cf & HDL_REFCOUNT_MASK) == 0u);
}
assert ((cf & HDL_PINCOUNT_MASK) == 1u);
@ -213,7 +237,7 @@ static int32_t dds_handle_pin_int (dds_handle_t hdl, uint32_t delta, struct dds_
rc = DDS_RETCODE_OK;
do {
cf = ddsrt_atomic_ld32 (&(*link)->cnt_flags);
if (cf & (HDL_FLAG_CLOSED | HDL_FLAG_CLOSING | HDL_FLAG_PENDING))
if (cf & (HDL_FLAG_CLOSING | HDL_FLAG_PENDING))
{
rc = DDS_RETCODE_BAD_PARAMETER;
break;
@ -229,6 +253,169 @@ int32_t dds_handle_pin (dds_handle_t hdl, struct dds_handle_link **link)
return dds_handle_pin_int (hdl, 1u, link);
}
int32_t dds_handle_pin_for_delete (dds_handle_t hdl, bool explicit, struct dds_handle_link **link)
{
struct dds_handle_link dummy = { .hdl = hdl };
int32_t rc;
/* it makes sense to check here for initialization: the first thing any operation
(other than create_participant) does is to call dds_handle_pin on the supplied
entity, so checking here whether the library has been initialised helps avoid
crashes if someone forgets to create a participant (or allows a program to
continue after failing to create one).
One could check that the handle is > 0, but that would catch fewer errors
without any advantages. */
if (handles.ht == NULL)
return DDS_RETCODE_PRECONDITION_NOT_MET;
ddsrt_mutex_lock (&handles.lock);
*link = ddsrt_hh_lookup (handles.ht, &dummy);
if (*link == NULL)
rc = DDS_RETCODE_BAD_PARAMETER;
else
{
uint32_t cf, cf1;
/* Assume success; bail out if the object turns out to be in the process of
being deleted */
do {
cf = ddsrt_atomic_ld32 (&(*link)->cnt_flags);
if (cf & (HDL_FLAG_CLOSING | HDL_FLAG_PENDING))
{
/* Only one can succeed (and if closing is already set, the handle's reference has
already been dropped) */
rc = DDS_RETCODE_BAD_PARAMETER;
break;
}
else if (cf & HDL_FLAG_DELETE_DEFERRED)
{
/* Someone already called delete, but the operation was deferred becauses there are still
outstanding references. This implies that there are no children, because then the
entire hierarchy would simply have been deleted. */
assert (!(cf & HDL_FLAG_ALLOW_CHILDREN));
if (cf & HDL_REFCOUNT_MASK)
{
rc = DDS_RETCODE_ALREADY_DELETED;
break;
}
else
{
/* Refcount reached zero. Pin to allow deletion. */
cf1 = (cf + 1u) | HDL_FLAG_CLOSING;
}
}
else if (explicit)
{
/* Explicit call to dds_delete (either by application or by parent deleting its children) */
if (cf & HDL_FLAG_IMPLICIT)
{
/* Entity is implicit, so handle doesn't hold a reference */
cf1 = (cf + 1u) | HDL_FLAG_CLOSING;
}
else
{
assert ((cf & HDL_REFCOUNT_MASK) > 0);
if ((cf & HDL_REFCOUNT_MASK) == HDL_REFCOUNT_UNIT)
{
/* Last reference is closing. Pin entity and indicate that it is closing. */
cf1 = (cf - HDL_REFCOUNT_UNIT + 1u) | HDL_FLAG_CLOSING;
}
else if (!(cf & HDL_FLAG_ALLOW_CHILDREN))
{
/* The refcnt does not contain children.
* Indicate that the closing of the entity is deferred. */
cf1 = (cf - HDL_REFCOUNT_UNIT) | HDL_FLAG_DELETE_DEFERRED;
}
else
{
/* Entity is explicit, so handle held a reference, refc only counts children as so is not our concern */
cf1 = (cf - HDL_REFCOUNT_UNIT + 1u) | HDL_FLAG_CLOSING;
}
}
}
else
{
/* Implicit call to dds_delete (child invoking delete on its parent) */
if (cf & HDL_FLAG_IMPLICIT)
{
assert ((cf & HDL_REFCOUNT_MASK) > 0);
if ((cf & HDL_REFCOUNT_MASK) == HDL_REFCOUNT_UNIT)
{
/* Last reference is closing. Pin entity and indicate that it is closing. */
cf1 = (cf - HDL_REFCOUNT_UNIT + 1u) | HDL_FLAG_CLOSING;
}
else if (!(cf & HDL_FLAG_ALLOW_CHILDREN))
{
/* The refcnt does not contain children.
* Indicate that the closing of the entity is deferred. */
cf1 = (cf - HDL_REFCOUNT_UNIT) | HDL_FLAG_DELETE_DEFERRED;
}
else
{
/* Just reduce the children refcount by one. */
cf1 = (cf - HDL_REFCOUNT_UNIT);
}
}
else
{
/* Child can't delete an explicit parent */
rc = DDS_RETCODE_ILLEGAL_OPERATION;
break;
}
}
rc = ((cf1 & HDL_REFCOUNT_MASK) == 0 || (cf1 & HDL_FLAG_ALLOW_CHILDREN)) ? DDS_RETCODE_OK : DDS_RETCODE_TRY_AGAIN;
} while (!ddsrt_atomic_cas32 (&(*link)->cnt_flags, cf, cf1));
}
ddsrt_mutex_unlock (&handles.lock);
return rc;
}
bool dds_handle_drop_childref_and_pin (struct dds_handle_link *link, bool may_delete_parent)
{
bool del_parent = false;
ddsrt_mutex_lock (&handles.lock);
uint32_t cf, cf1;
do {
cf = ddsrt_atomic_ld32 (&link->cnt_flags);
if (cf & (HDL_FLAG_CLOSING | HDL_FLAG_PENDING))
{
/* Only one can succeed; child ref still to be removed */
assert ((cf & HDL_REFCOUNT_MASK) > 0);
cf1 = (cf - HDL_REFCOUNT_UNIT);
del_parent = false;
}
else
{
if (cf & HDL_FLAG_IMPLICIT)
{
/* Implicit parent: delete if last ref */
if ((cf & HDL_REFCOUNT_MASK) == HDL_REFCOUNT_UNIT && may_delete_parent)
{
cf1 = (cf - HDL_REFCOUNT_UNIT + 1u);
del_parent = true;
}
else
{
assert ((cf & HDL_REFCOUNT_MASK) > 0);
cf1 = (cf - HDL_REFCOUNT_UNIT);
del_parent = false;
}
}
else
{
/* Child can't delete an explicit parent; child ref still to be removed */
assert ((cf & HDL_REFCOUNT_MASK) > 0);
cf1 = (cf - HDL_REFCOUNT_UNIT);
del_parent = false;
}
}
} while (!ddsrt_atomic_cas32 (&link->cnt_flags, cf, cf1));
ddsrt_mutex_unlock (&handles.lock);
return del_parent;
}
int32_t dds_handle_pin_and_ref (dds_handle_t hdl, struct dds_handle_link **link)
{
return dds_handle_pin_int (hdl, HDL_REFCOUNT_UNIT + 1u, link);
@ -237,7 +424,6 @@ int32_t dds_handle_pin_and_ref (dds_handle_t hdl, struct dds_handle_link **link)
void dds_handle_repin (struct dds_handle_link *link)
{
uint32_t x = ddsrt_atomic_inc32_nv (&link->cnt_flags);
assert (!(x & HDL_FLAG_CLOSED));
(void) x;
}
@ -245,7 +431,6 @@ void dds_handle_unpin (struct dds_handle_link *link)
{
#ifndef NDEBUG
uint32_t cf = ddsrt_atomic_ld32 (&link->cnt_flags);
assert (!(cf & HDL_FLAG_CLOSED));
if (cf & HDL_FLAG_CLOSING)
assert ((cf & HDL_PINCOUNT_MASK) > 1u);
else
@ -266,16 +451,43 @@ void dds_handle_add_ref (struct dds_handle_link *link)
bool dds_handle_drop_ref (struct dds_handle_link *link)
{
assert ((ddsrt_atomic_ld32 (&link->cnt_flags) & HDL_REFCOUNT_MASK) != 0);
uint32_t old, new;
do {
old = ddsrt_atomic_ld32 (&link->cnt_flags);
if ((old & HDL_REFCOUNT_MASK) != HDL_REFCOUNT_UNIT)
new = old - HDL_REFCOUNT_UNIT;
else
new = (old - HDL_REFCOUNT_UNIT) | HDL_FLAG_CLOSING;
assert ((old & HDL_REFCOUNT_MASK) > 0);
new = old - HDL_REFCOUNT_UNIT;
} while (!ddsrt_atomic_cas32 (&link->cnt_flags, old, new));
return (new & HDL_REFCOUNT_MASK) == 0;
ddsrt_mutex_lock (&handles.lock);
if ((new & (HDL_FLAG_CLOSING | HDL_PINCOUNT_MASK)) == (HDL_FLAG_CLOSING | 1u))
{
ddsrt_cond_broadcast (&handles.cond);
}
ddsrt_mutex_unlock (&handles.lock);
return ((new & HDL_REFCOUNT_MASK) == 0);
}
bool dds_handle_unpin_and_drop_ref (struct dds_handle_link *link)
{
uint32_t old, new;
do {
old = ddsrt_atomic_ld32 (&link->cnt_flags);
assert ((old & HDL_REFCOUNT_MASK) > 0);
assert ((old & HDL_PINCOUNT_MASK) > 0);
new = old - HDL_REFCOUNT_UNIT - 1u;
} while (!ddsrt_atomic_cas32 (&link->cnt_flags, old, new));
ddsrt_mutex_lock (&handles.lock);
if ((new & (HDL_FLAG_CLOSING | HDL_PINCOUNT_MASK)) == (HDL_FLAG_CLOSING | 1u))
{
ddsrt_cond_broadcast (&handles.cond);
}
ddsrt_mutex_unlock (&handles.lock);
return ((new & HDL_REFCOUNT_MASK) == 0);
}
bool dds_handle_close (struct dds_handle_link *link)
{
uint32_t old = ddsrt_atomic_or32_ov (&link->cnt_flags, HDL_FLAG_CLOSING);
return (old & HDL_REFCOUNT_MASK) == 0;
}
void dds_handle_close_wait (struct dds_handle_link *link)
@ -283,17 +495,18 @@ void dds_handle_close_wait (struct dds_handle_link *link)
#ifndef NDEBUG
uint32_t cf = ddsrt_atomic_ld32 (&link->cnt_flags);
assert ((cf & HDL_FLAG_CLOSING));
assert (!(cf & HDL_FLAG_CLOSED));
assert ((cf & HDL_REFCOUNT_MASK) == 0u);
assert ((cf & HDL_PINCOUNT_MASK) >= 1u);
#endif
ddsrt_mutex_lock (&handles.lock);
while ((ddsrt_atomic_ld32 (&link->cnt_flags) & HDL_PINCOUNT_MASK) != 1u)
ddsrt_cond_wait (&handles.cond, &handles.lock);
/* only one thread may call close_wait on a given handle */
assert (!(ddsrt_atomic_ld32 (&link->cnt_flags) & HDL_FLAG_CLOSED));
ddsrt_atomic_or32 (&link->cnt_flags, HDL_FLAG_CLOSED);
ddsrt_mutex_unlock (&handles.lock);
}
bool dds_handle_is_not_refd (struct dds_handle_link *link)
{
return ((ddsrt_atomic_ld32 (&link->cnt_flags) & HDL_REFCOUNT_MASK) == 0);
}
extern inline bool dds_handle_is_closed (struct dds_handle_link *link);

View file

@ -119,9 +119,8 @@ dds_return_t dds_init (void)
goto fail_handleserver;
}
dds_entity_init (&dds_global.m_entity, NULL, DDS_KIND_CYCLONEDDS, NULL, NULL, 0);
dds_entity_init (&dds_global.m_entity, NULL, DDS_KIND_CYCLONEDDS, true, NULL, NULL, 0);
dds_global.m_entity.m_iid = ddsi_iid_gen ();
dds_global.m_entity.m_flags = DDS_ENTITY_IMPLICIT;
dds_handle_repin (&dds_global.m_entity.m_hdllink);
dds_entity_add_ref_locked (&dds_global.m_entity);
dds_entity_init_complete (&dds_global.m_entity);

View file

@ -113,7 +113,7 @@ dds_return_t dds_unregister_instance_ts (dds_entity_t writer, const void *data,
return ret;
if (wr->m_entity.m_qos)
dds_qget_writer_data_lifecycle (wr->m_entity.m_qos, &autodispose);
(void) dds_qget_writer_data_lifecycle (wr->m_entity.m_qos, &autodispose);
thread_state_awake (ts1, &wr->m_entity.m_domain->gv);
if (autodispose)
@ -140,7 +140,7 @@ dds_return_t dds_unregister_instance_ih_ts (dds_entity_t writer, dds_instance_ha
return ret;
if (wr->m_entity.m_qos)
dds_qget_writer_data_lifecycle (wr->m_entity.m_qos, &autodispose);
(void) dds_qget_writer_data_lifecycle (wr->m_entity.m_qos, &autodispose);
thread_state_awake (ts1, &wr->m_entity.m_domain->gv);
if (autodispose)

View file

@ -117,7 +117,7 @@ dds_entity_t dds_create_participant (const dds_domainid_t domain, const dds_qos_
}
pp = dds_alloc (sizeof (*pp));
if ((ret = dds_entity_init (&pp->m_entity, &dom->m_entity, DDS_KIND_PARTICIPANT, new_qos, listener, DDS_PARTICIPANT_STATUS_MASK)) < 0)
if ((ret = dds_entity_init (&pp->m_entity, &dom->m_entity, DDS_KIND_PARTICIPANT, false, new_qos, listener, DDS_PARTICIPANT_STATUS_MASK)) < 0)
goto err_entity_init;
pp->m_entity.m_guid = guid;
@ -126,14 +126,14 @@ dds_entity_t dds_create_participant (const dds_domainid_t domain, const dds_qos_
pp->m_builtin_subscriber = 0;
/* Add participant to extent */
ddsrt_mutex_lock (&dds_global.m_entity.m_mutex);
ddsrt_mutex_lock (&dom->m_entity.m_mutex);
dds_entity_register_child (&dom->m_entity, &pp->m_entity);
ddsrt_mutex_unlock (&dds_global.m_entity.m_mutex);
ddsrt_mutex_unlock (&dom->m_entity.m_mutex);
dds_entity_init_complete (&pp->m_entity);
/* drop temporary extra ref to domain, dds_init */
dds_delete (dom->m_entity.m_hdllink.hdl);
dds_delete_impl_pinned (&dds_global.m_entity, DIS_EXPLICIT);
dds_entity_unpin_and_drop_ref (&dom->m_entity);
dds_entity_unpin_and_drop_ref (&dds_global.m_entity);
return ret;
err_entity_init:
@ -141,9 +141,9 @@ err_entity_init:
err_new_participant:
err_qos_validation:
dds_delete_qos (new_qos);
dds_delete (dom->m_entity.m_hdllink.hdl);
dds_entity_unpin_and_drop_ref (&dom->m_entity);
err_domain_create:
dds_delete_impl_pinned (&dds_global.m_entity, DIS_EXPLICIT);
dds_entity_unpin_and_drop_ref (&dds_global.m_entity);
err_dds_init:
return ret;
}
@ -175,6 +175,6 @@ dds_return_t dds_lookup_participant (dds_domainid_t domain_id, dds_entity_t *par
}
}
ddsrt_mutex_unlock (&dds_global.m_mutex);
dds_delete_impl_pinned (&dds_global.m_entity, DIS_EXPLICIT);
dds_entity_unpin_and_drop_ref (&dds_global.m_entity);
return ret;
}

View file

@ -45,17 +45,13 @@ const struct dds_entity_deriver dds_entity_deriver_publisher = {
.validate_status = dds_publisher_status_validate
};
dds_entity_t dds_create_publisher (dds_entity_t participant, const dds_qos_t *qos, const dds_listener_t *listener)
dds_entity_t dds__create_publisher_l (dds_participant *par, bool implicit, const dds_qos_t *qos, const dds_listener_t *listener)
{
dds_participant *par;
dds_publisher *pub;
dds_entity_t hdl;
dds_qos_t *new_qos;
dds_return_t ret;
if ((ret = dds_participant_lock (participant, &par)) != DDS_RETCODE_OK)
return ret;
new_qos = dds_create_qos ();
if (qos)
nn_xqos_mergein_missing (new_qos, qos, DDS_PUBLISHER_QOS_MASK);
@ -67,10 +63,21 @@ dds_entity_t dds_create_publisher (dds_entity_t participant, const dds_qos_t *qo
}
pub = dds_alloc (sizeof (*pub));
hdl = dds_entity_init (&pub->m_entity, &par->m_entity, DDS_KIND_PUBLISHER, new_qos, listener, DDS_PUBLISHER_STATUS_MASK);
hdl = dds_entity_init (&pub->m_entity, &par->m_entity, DDS_KIND_PUBLISHER, implicit, new_qos, listener, DDS_PUBLISHER_STATUS_MASK);
pub->m_entity.m_iid = ddsi_iid_gen ();
dds_entity_register_child (&par->m_entity, &pub->m_entity);
dds_entity_init_complete (&pub->m_entity);
return hdl;
}
dds_entity_t dds_create_publisher (dds_entity_t participant, const dds_qos_t *qos, const dds_listener_t *listener)
{
dds_participant *par;
dds_entity_t hdl;
dds_return_t ret;
if ((ret = dds_participant_lock (participant, &par)) != DDS_RETCODE_OK)
return ret;
hdl = dds__create_publisher_l (par, false, qos, listener);
dds_participant_unlock (par);
return hdl;
}

View file

@ -41,7 +41,7 @@ dds_readcond *dds_create_readcond (dds_reader *rd, dds_entity_kind_t kind, uint3
{
dds_readcond *cond = dds_alloc (sizeof (*cond));
assert ((kind == DDS_KIND_COND_READ && filter == 0) || (kind == DDS_KIND_COND_QUERY && filter != 0));
(void) dds_entity_init (&cond->m_entity, &rd->m_entity, kind, NULL, NULL, 0);
(void) dds_entity_init (&cond->m_entity, &rd->m_entity, kind, false, NULL, NULL, 0);
cond->m_entity.m_iid = ddsi_iid_gen ();
dds_entity_register_child (&rd->m_entity, &cond->m_entity);
cond->m_sample_states = mask & DDS_ANY_SAMPLE_STATE;

View file

@ -62,11 +62,11 @@ static dds_return_t dds_reader_delete (dds_entity *e) ddsrt_nonnull_all;
static dds_return_t dds_reader_delete (dds_entity *e)
{
dds_reader * const rd = (dds_reader *) e;
(void) dds_delete (rd->m_topic->m_entity.m_hdllink.hdl);
dds_free (rd->m_loan);
thread_state_awake (lookup_thread_state (), &e->m_domain->gv);
dds_rhc_free (rd->m_rhc);
thread_state_asleep (lookup_thread_state ());
dds_entity_drop_ref (&rd->m_topic->m_entity);
return DDS_RETCODE_OK;
}
@ -150,6 +150,7 @@ void dds_reader_data_available_cb (struct dds_reader *rd)
rd->m_entity.m_cb_count--;
rd->m_entity.m_cb_pending_count--;
ddsrt_cond_broadcast (&rd->m_entity.m_observers_cond);
ddsrt_mutex_unlock (&rd->m_entity.m_observers_lock);
}
@ -232,16 +233,47 @@ void dds_reader_status_cb (void *ventity, const status_cb_data_t *data)
}
case DDS_LIVELINESS_CHANGED_STATUS_ID: {
struct dds_liveliness_changed_status * const st = vst = &rd->m_liveliness_changed_status;
if (data->add) {
st->alive_count++;
st->alive_count_change++;
if (st->not_alive_count > 0) {
DDSRT_STATIC_ASSERT ((uint32_t) LIVELINESS_CHANGED_ADD_ALIVE == 0 &&
LIVELINESS_CHANGED_ADD_ALIVE < LIVELINESS_CHANGED_ADD_NOT_ALIVE &&
LIVELINESS_CHANGED_ADD_NOT_ALIVE < LIVELINESS_CHANGED_REMOVE_NOT_ALIVE &&
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);
switch ((enum liveliness_changed_data_extra) data->extra)
{
case LIVELINESS_CHANGED_ADD_ALIVE:
st->alive_count++;
st->alive_count_change++;
break;
case LIVELINESS_CHANGED_ADD_NOT_ALIVE:
st->not_alive_count++;
st->not_alive_count_change++;
break;
case LIVELINESS_CHANGED_REMOVE_NOT_ALIVE:
st->not_alive_count--;
}
} else {
st->alive_count--;
st->not_alive_count++;
st->not_alive_count_change++;
st->not_alive_count_change--;
break;
case LIVELINESS_CHANGED_REMOVE_ALIVE:
st->alive_count--;
st->alive_count_change--;
break;
case LIVELINESS_CHANGED_ALIVE_TO_NOT_ALIVE:
st->alive_count--;
st->alive_count_change--;
st->not_alive_count++;
st->not_alive_count_change++;
break;
case LIVELINESS_CHANGED_NOT_ALIVE_TO_ALIVE:
st->not_alive_count--;
st->not_alive_count_change--;
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);
@ -332,36 +364,37 @@ static dds_entity_t dds_create_reader_int (dds_entity_t participant_or_subscribe
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);
break;
default: {
dds_entity *p_or_s;
if ((ret = dds_entity_pin (participant_or_subscriber, &p_or_s)) != DDS_RETCODE_OK)
if ((ret = dds_entity_lock (participant_or_subscriber, DDS_KIND_DONTCARE, &p_or_s)) != DDS_RETCODE_OK)
return ret;
if (dds_entity_kind (p_or_s) == DDS_KIND_PARTICIPANT)
subscriber = dds_create_subscriber (participant_or_subscriber, qos, NULL);
else
subscriber = participant_or_subscriber;
dds_entity_unpin (p_or_s);
switch (dds_entity_kind (p_or_s))
{
case DDS_KIND_SUBSCRIBER:
subscriber = participant_or_subscriber;
sub = (dds_subscriber *) p_or_s;
break;
case DDS_KIND_PARTICIPANT:
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;
break;
default:
dds_entity_unlock (p_or_s);
return DDS_RETCODE_ILLEGAL_OPERATION;
}
internal_topic = false;
t = topic;
break;
}
}
if ((ret = dds_subscriber_lock (subscriber, &sub)) != DDS_RETCODE_OK)
{
reader = ret;
goto err_sub_lock;
}
if (subscriber != participant_or_subscriber && !internal_topic)
{
/* Delete implicit subscriber if reader creation fails */
sub->m_entity.m_flags |= DDS_ENTITY_IMPLICIT;
}
if ((ret = dds_topic_lock (t, &tp)) != DDS_RETCODE_OK)
{
reader = ret;
@ -405,7 +438,7 @@ static dds_entity_t dds_create_reader_int (dds_entity_t participant_or_subscribe
/* 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, rqos, listener, DDS_READER_STATUS_MASK);
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);
@ -431,12 +464,6 @@ static dds_entity_t dds_create_reader_int (dds_entity_t participant_or_subscribe
dds_topic_unlock (tp);
dds_subscriber_unlock (sub);
if (internal_topic)
{
/* If topic is builtin, then the topic entity is local and should be deleted because the application won't. */
dds_delete (t);
}
return reader;
err_bad_qos:
@ -446,9 +473,6 @@ err_tp_lock:
dds_subscriber_unlock (sub);
if ((sub->m_entity.m_flags & DDS_ENTITY_IMPLICIT) != 0)
(void) dds_delete (subscriber);
err_sub_lock:
if (internal_topic)
dds_delete (t);
return reader;
}

View file

@ -1110,7 +1110,7 @@ static int rhc_unregister_isreg_w_sideeffects (struct dds_rhc_default *rhc, cons
if (inst->wrcount == 2 && inst->wr_iid_islive && inst->wr_iid != wr_iid)
{
TRACE (",delreg(remain)");
lwregs_delete (&rhc->registrations, inst->iid, inst->wr_iid);
(void) lwregs_delete (&rhc->registrations, inst->iid, inst->wr_iid);
}
return 1;
}
@ -1627,7 +1627,7 @@ static void dds_rhc_default_unregister_wr (struct dds_rhc_default * __restrict r
}
}
dds_rhc_unregister (rhc, inst, wrinfo, inst->tstamp, &post, &trig_qc);
(void) dds_rhc_unregister (rhc, inst, wrinfo, inst->tstamp, &post, &trig_qc);
TRACE ("\n");
@ -2394,7 +2394,7 @@ static bool dds_rhc_default_add_readcondition (struct dds_rhc_default *rhc, dds_
ddsrt_atomic_st32 (&cond->m_entity.m_status.m_trigger, trigger);
dds_entity_status_signal (&cond->m_entity, DDS_DATA_AVAILABLE_STATUS);
}
TRACE ("add_readcondition(%p, %"PRIx32", %"PRIx32", %"PRIx32") => %p qminv %"PRIx32" ; rhc %"PRIu32" conds\n",
(void *) rhc, cond->m_sample_states, cond->m_view_states,
cond->m_instance_states, (void *) cond, cond->m_qminv, rhc->nconds);

View file

@ -256,24 +256,25 @@ size_t dds_stream_check_optimize (const dds_topic_descriptor_t * __restrict desc
return dds_stream_check_optimize1 (desc);
}
static char *dds_stream_reuse_string (dds_istream_t * __restrict is, char * __restrict str, const uint32_t bound)
static void dds_stream_reuse_string_bound (dds_istream_t * __restrict is, char * __restrict str, const uint32_t bound)
{
const uint32_t length = dds_is_get4 (is);
const void *src = is->m_buffer + is->m_index;
if (bound)
{
/* FIXME: validation now rejects data containing an oversize bounded string,
so this check is superfluous, but perhaps rejecting such a sample is the
wrong thing to do */
assert (str != NULL);
memcpy (str, src, length > bound ? bound : length);
}
else
{
if (str == NULL || strlen (str) + 1 < length)
str = dds_realloc (str, length);
memcpy (str, src, length);
}
/* FIXME: validation now rejects data containing an oversize bounded string,
so this check is superfluous, but perhaps rejecting such a sample is the
wrong thing to do */
assert (str != NULL);
memcpy (str, src, length > bound ? bound : length);
is->m_index += length;
}
static char *dds_stream_reuse_string (dds_istream_t * __restrict is, char * __restrict str)
{
const uint32_t length = dds_is_get4 (is);
const void *src = is->m_buffer + is->m_index;
if (str == NULL || strlen (str) + 1 < length)
str = dds_realloc (str, length);
memcpy (str, src, length);
is->m_index += length;
return str;
}
@ -596,7 +597,7 @@ static const uint32_t *dds_stream_read_seq (dds_istream_t * __restrict is, char
seq->_length = (num <= seq->_maximum) ? num : seq->_maximum;
char **ptr = (char **) seq->_buffer;
for (uint32_t i = 0; i < seq->_length; i++)
ptr[i] = dds_stream_reuse_string (is, ptr[i], 0);
ptr[i] = dds_stream_reuse_string (is, ptr[i]);
for (uint32_t i = seq->_length; i < num; i++)
dds_stream_skip_string (is);
return ops + 2;
@ -607,7 +608,7 @@ static const uint32_t *dds_stream_read_seq (dds_istream_t * __restrict is, char
seq->_length = (num <= seq->_maximum) ? num : seq->_maximum;
char *ptr = (char *) seq->_buffer;
for (uint32_t i = 0; i < seq->_length; i++)
(void) dds_stream_reuse_string (is, ptr + i * elem_size, elem_size);
dds_stream_reuse_string_bound (is, ptr + i * elem_size, elem_size);
for (uint32_t i = seq->_length; i < num; i++)
dds_stream_skip_string (is);
return ops + 3;
@ -641,14 +642,14 @@ static const uint32_t *dds_stream_read_arr (dds_istream_t * __restrict is, char
case DDS_OP_VAL_STR: {
char **ptr = (char **) addr;
for (uint32_t i = 0; i < num; i++)
ptr[i] = dds_stream_reuse_string (is, ptr[i], 0);
ptr[i] = dds_stream_reuse_string (is, ptr[i]);
return ops + 3;
}
case DDS_OP_VAL_BST: {
char *ptr = (char *) addr;
const uint32_t elem_size = ops[4];
for (uint32_t i = 0; i < num; i++)
(void) dds_stream_reuse_string (is, ptr + i * elem_size, elem_size);
dds_stream_reuse_string_bound (is, ptr + i * elem_size, elem_size);
return ops + 5;
}
case DDS_OP_VAL_SEQ: case DDS_OP_VAL_ARR: case DDS_OP_VAL_UNI: case DDS_OP_VAL_STU: {
@ -685,7 +686,7 @@ static const uint32_t *dds_stream_read_uni (dds_istream_t * __restrict is, char
case DDS_OP_VAL_2BY: *((uint16_t *) valaddr) = dds_is_get2 (is); break;
case DDS_OP_VAL_4BY: *((uint32_t *) valaddr) = dds_is_get4 (is); break;
case DDS_OP_VAL_8BY: *((uint64_t *) valaddr) = dds_is_get8 (is); break;
case DDS_OP_VAL_STR: *(char **) valaddr = dds_stream_reuse_string (is, *((char **) valaddr), 0); break;
case DDS_OP_VAL_STR: *(char **) valaddr = dds_stream_reuse_string (is, *((char **) valaddr)); break;
case DDS_OP_VAL_BST: case DDS_OP_VAL_SEQ: case DDS_OP_VAL_ARR: case DDS_OP_VAL_UNI: case DDS_OP_VAL_STU:
dds_stream_read (is, valaddr, jeq_op + DDS_OP_ADR_JSR (jeq_op[0]));
break;
@ -709,8 +710,8 @@ static void dds_stream_read (dds_istream_t * __restrict is, char * __restrict da
case DDS_OP_VAL_2BY: *((uint16_t *) addr) = dds_is_get2 (is); ops += 2; break;
case DDS_OP_VAL_4BY: *((uint32_t *) addr) = dds_is_get4 (is); ops += 2; break;
case DDS_OP_VAL_8BY: *((uint64_t *) addr) = dds_is_get8 (is); ops += 2; break;
case DDS_OP_VAL_STR: *((char **) addr) = dds_stream_reuse_string (is, *((char **) addr), 0); ops += 2; break;
case DDS_OP_VAL_BST: dds_stream_reuse_string (is, (char *) addr, ops[2]); ops += 3; break;
case DDS_OP_VAL_STR: *((char **) addr) = dds_stream_reuse_string (is, *((char **) addr)); ops += 2; break;
case DDS_OP_VAL_BST: dds_stream_reuse_string_bound (is, (char *) addr, ops[2]); ops += 3; break;
case DDS_OP_VAL_SEQ: ops = dds_stream_read_seq (is, addr, ops, insn); break;
case DDS_OP_VAL_ARR: ops = dds_stream_read_arr (is, addr, ops, insn); break;
case DDS_OP_VAL_UNI: ops = dds_stream_read_uni (is, addr, data, ops, insn); break;
@ -1126,8 +1127,8 @@ void dds_stream_read_key (dds_istream_t * __restrict is, char * __restrict sampl
case DDS_OP_VAL_2BY: *((uint16_t *) dst) = dds_is_get2 (is); break;
case DDS_OP_VAL_4BY: *((uint32_t *) dst) = dds_is_get4 (is); break;
case DDS_OP_VAL_8BY: *((uint64_t *) dst) = dds_is_get8 (is); break;
case DDS_OP_VAL_STR: *((char **) dst) = dds_stream_reuse_string (is, *((char **) dst), 0); break;
case DDS_OP_VAL_BST: dds_stream_reuse_string (is, dst, op[2]); break;
case DDS_OP_VAL_STR: *((char **) dst) = dds_stream_reuse_string (is, *((char **) dst)); break;
case DDS_OP_VAL_BST: dds_stream_reuse_string_bound (is, dst, op[2]); break;
case DDS_OP_VAL_ARR:
dds_is_get_bytes (is, dst, op[2], get_type_size (DDS_OP_SUBTYPE (*op)));
break;
@ -1728,16 +1729,16 @@ static const uint32_t *prtf_seq (char * __restrict *buf, size_t *bufsize, dds_is
num = dds_is_get4 (is);
if (num == 0)
{
prtf (buf, bufsize, "{}");
(void) prtf (buf, bufsize, "{}");
return skip_sequence_insns (ops, insn);
}
switch (subtype)
{
case DDS_OP_VAL_1BY: case DDS_OP_VAL_2BY: case DDS_OP_VAL_4BY: case DDS_OP_VAL_8BY:
prtf_simple_array (buf, bufsize, is, num, subtype);
(void) prtf_simple_array (buf, bufsize, is, num, subtype);
return ops + 2;
case DDS_OP_VAL_STR: case DDS_OP_VAL_BST:
prtf_simple_array (buf, bufsize, is, num, subtype);
(void) prtf_simple_array (buf, bufsize, is, num, subtype);
return ops + (subtype == DDS_OP_VAL_STR ? 2 : 3);
case DDS_OP_VAL_SEQ: case DDS_OP_VAL_ARR: case DDS_OP_VAL_UNI: case DDS_OP_VAL_STU: {
const uint32_t jmp = DDS_OP_ADR_JMP (ops[3]);
@ -1745,10 +1746,10 @@ static const uint32_t *prtf_seq (char * __restrict *buf, size_t *bufsize, dds_is
bool cont = prtf (buf, bufsize, "{");
for (uint32_t i = 0; cont && i < num; i++)
{
if (i > 0) prtf (buf, bufsize, ",");
if (i > 0) (void) prtf (buf, bufsize, ",");
cont = dds_stream_print_sample1 (buf, bufsize, is, jsr_ops, subtype == DDS_OP_VAL_STU);
}
prtf (buf, bufsize, "}");
(void) prtf (buf, bufsize, "}");
return ops + (jmp ? jmp : 4); /* FIXME: why would jmp be 0? */
}
}
@ -1762,10 +1763,10 @@ static const uint32_t *prtf_arr (char * __restrict *buf, size_t *bufsize, dds_is
switch (subtype)
{
case DDS_OP_VAL_1BY: case DDS_OP_VAL_2BY: case DDS_OP_VAL_4BY: case DDS_OP_VAL_8BY:
prtf_simple_array (buf, bufsize, is, num, subtype);
(void) prtf_simple_array (buf, bufsize, is, num, subtype);
return ops + 3;
case DDS_OP_VAL_STR: case DDS_OP_VAL_BST:
prtf_simple_array (buf, bufsize, is, num, subtype);
(void) prtf_simple_array (buf, bufsize, is, num, subtype);
return ops + (subtype == DDS_OP_VAL_STR ? 3 : 5);
case DDS_OP_VAL_SEQ: case DDS_OP_VAL_ARR: case DDS_OP_VAL_UNI: case DDS_OP_VAL_STU: {
const uint32_t *jsr_ops = ops + DDS_OP_ADR_JSR (ops[3]);
@ -1773,10 +1774,10 @@ static const uint32_t *prtf_arr (char * __restrict *buf, size_t *bufsize, dds_is
bool cont = prtf (buf, bufsize, "{");
for (uint32_t i = 0; cont && i < num; i++)
{
if (i > 0) prtf (buf, bufsize, ",");
if (i > 0) (void) prtf (buf, bufsize, ",");
cont = dds_stream_print_sample1 (buf, bufsize, is, jsr_ops, subtype == DDS_OP_VAL_STU);
}
prtf (buf, bufsize, "}");
(void) prtf (buf, bufsize, "}");
return ops + (jmp ? jmp : 5);
}
}
@ -1787,7 +1788,7 @@ static const uint32_t *prtf_uni (char * __restrict *buf, size_t *bufsize, dds_is
{
const uint32_t disc = read_union_discriminant (is, DDS_OP_SUBTYPE (insn));
uint32_t const * const jeq_op = find_union_case (ops, disc);
prtf (buf, bufsize, "%"PRIu32":", disc);
(void) prtf (buf, bufsize, "%"PRIu32":", disc);
ops += DDS_OP_ADR_JMP (ops[3]);
if (jeq_op)
{
@ -1796,10 +1797,10 @@ static const uint32_t *prtf_uni (char * __restrict *buf, size_t *bufsize, dds_is
{
case DDS_OP_VAL_1BY: case DDS_OP_VAL_2BY: case DDS_OP_VAL_4BY: case DDS_OP_VAL_8BY:
case DDS_OP_VAL_STR: case DDS_OP_VAL_BST:
prtf_simple (buf, bufsize, is, valtype);
(void) prtf_simple (buf, bufsize, is, valtype);
break;
case DDS_OP_VAL_SEQ: case DDS_OP_VAL_ARR: case DDS_OP_VAL_UNI: case DDS_OP_VAL_STU:
dds_stream_print_sample1 (buf, bufsize, is, jeq_op + DDS_OP_ADR_JSR (jeq_op[0]), valtype == DDS_OP_VAL_STU);
(void) dds_stream_print_sample1 (buf, bufsize, is, jeq_op + DDS_OP_ADR_JSR (jeq_op[0]), valtype == DDS_OP_VAL_STU);
break;
}
}
@ -1812,11 +1813,11 @@ static bool dds_stream_print_sample1 (char * __restrict *buf, size_t * __restric
bool cont = true;
bool needs_comma = false;
if (add_braces)
prtf (buf, bufsize, "{");
(void) prtf (buf, bufsize, "{");
while (cont && (insn = *ops) != DDS_OP_RTS)
{
if (needs_comma)
prtf (buf, bufsize, ",");
(void) prtf (buf, bufsize, ",");
needs_comma = true;
switch (DDS_OP (insn))
{
@ -1859,13 +1860,13 @@ static bool dds_stream_print_sample1 (char * __restrict *buf, size_t * __restric
}
}
if (add_braces)
prtf (buf, bufsize, "}");
(void) prtf (buf, bufsize, "}");
return cont;
}
size_t dds_stream_print_sample (dds_istream_t * __restrict is, const struct ddsi_sertopic_default * __restrict topic, char * __restrict buf, size_t bufsize)
{
dds_stream_print_sample1 (&buf, &bufsize, is, topic->type->m_ops, true);
(void) dds_stream_print_sample1 (&buf, &bufsize, is, topic->type->m_ops, true);
return bufsize;
}
@ -1891,7 +1892,7 @@ size_t dds_stream_print_key (dds_istream_t * __restrict is, const struct ddsi_se
break;
}
}
prtf (&buf, &bufsize, "}");
(void) prtf (&buf, &bufsize, "}");
return bufsize;
}

View file

@ -45,7 +45,7 @@ const struct dds_entity_deriver dds_entity_deriver_subscriber = {
.validate_status = dds_subscriber_status_validate
};
dds_entity_t dds__create_subscriber_l (dds_participant *participant, const dds_qos_t *qos, const dds_listener_t *listener)
dds_entity_t dds__create_subscriber_l (dds_participant *participant, bool implicit, const dds_qos_t *qos, const dds_listener_t *listener)
{
/* participant entity lock must be held */
dds_subscriber *sub;
@ -64,7 +64,7 @@ dds_entity_t dds__create_subscriber_l (dds_participant *participant, const dds_q
}
sub = dds_alloc (sizeof (*sub));
subscriber = dds_entity_init (&sub->m_entity, &participant->m_entity, DDS_KIND_SUBSCRIBER, new_qos, listener, DDS_SUBSCRIBER_STATUS_MASK);
subscriber = dds_entity_init (&sub->m_entity, &participant->m_entity, DDS_KIND_SUBSCRIBER, implicit, new_qos, listener, DDS_SUBSCRIBER_STATUS_MASK);
sub->m_entity.m_iid = ddsi_iid_gen ();
dds_entity_register_child (&participant->m_entity, &sub->m_entity);
dds_entity_init_complete (&sub->m_entity);
@ -78,7 +78,7 @@ dds_entity_t dds_create_subscriber (dds_entity_t participant, const dds_qos_t *q
dds_return_t ret;
if ((ret = dds_participant_lock (participant, &par)) != DDS_RETCODE_OK)
return ret;
hdl = dds__create_subscriber_l (par, qos, listener);
hdl = dds__create_subscriber_l (par, false, qos, listener);
dds_participant_unlock (par);
return hdl;
}

View file

@ -31,6 +31,7 @@
#include "dds/ddsi/ddsi_iid.h"
#include "dds/ddsi/q_plist.h"
#include "dds/ddsi/q_globals.h"
#include "dds__serdata_builtintopic.h"
DECL_ENTITY_LOCK_UNLOCK (extern inline, dds_topic)
@ -135,12 +136,16 @@ static bool dds_find_topic_check_and_add_ref (dds_entity_t participant, dds_enti
ret = false;
else
{
/* FIXME: calling addref is wrong because the Cyclone library has no
knowledge of the reference and hence simply deleting the participant
won't make the ref count drop to 0. On the other hand, the DDS spec
says find_topic (and a second call to create_topic) return a new
proxy that must separately be deleted. */
dds_entity_add_ref_locked (&tp->m_entity);
/* 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);
@ -259,12 +264,7 @@ static dds_return_t create_topic_topic_arbitrary_check_sertopic (dds_entity_t pa
ret = DDS_RETCODE_INCONSISTENT_POLICY;
else
{
/* FIXME: calling addref is wrong because the Cyclone library has no
knowledge of the reference and hence simply deleting the participant
won't make the ref count drop to 0. On the other hand, the DDS spec
says find_topic (and a second call to create_topic) return a new
proxy that must separately be deleted. */
dds_entity_add_ref_locked (&tp->m_entity);
/* See dds_find_topic_check_and_add_ref */
ret = DDS_RETCODE_OK;
}
dds_topic_unlock (tp);
@ -429,7 +429,8 @@ dds_entity_t dds_create_topic_arbitrary (dds_entity_t participant, struct ddsi_s
/* Create topic */
top = dds_alloc (sizeof (*top));
hdl = dds_entity_init (&top->m_entity, &par->m_entity, DDS_KIND_TOPIC, new_qos, listener, DDS_TOPIC_STATUS_MASK);
/* 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;

View file

@ -166,7 +166,7 @@ dds_entity_t dds_create_waitset (dds_entity_t owner)
}
dds_waitset *waitset = dds_alloc (sizeof (*waitset));
dds_entity_t hdl = dds_entity_init (&waitset->m_entity, e, DDS_KIND_WAITSET, NULL, NULL, 0);
dds_entity_t hdl = dds_entity_init (&waitset->m_entity, e, DDS_KIND_WAITSET, false, NULL, NULL, 0);
ddsrt_mutex_init (&waitset->wait_lock);
ddsrt_cond_init (&waitset->wait_cond);
waitset->m_entity.m_iid = ddsi_iid_gen ();
@ -176,13 +176,13 @@ dds_entity_t dds_create_waitset (dds_entity_t owner)
waitset->entities = NULL;
dds_entity_init_complete (&waitset->m_entity);
dds_entity_unlock (e);
dds_delete_impl_pinned (&dds_global.m_entity, DIS_EXPLICIT);
dds_entity_unpin_and_drop_ref (&dds_global.m_entity);
return hdl;
err_entity_kind:
dds_entity_unlock (e);
err_entity_lock:
dds_delete_impl_pinned (&dds_global.m_entity, DIS_EXPLICIT);
dds_entity_unpin_and_drop_ref (&dds_global.m_entity);
return rc;
}

View file

@ -209,13 +209,12 @@ static dds_return_t dds_writer_delete (dds_entity *e) ddsrt_nonnull_all;
static dds_return_t dds_writer_delete (dds_entity *e)
{
dds_writer * const wr = (dds_writer *) e;
dds_return_t ret;
/* FIXME: not freeing WHC here because it is owned by the DDSI entity */
thread_state_awake (lookup_thread_state (), &e->m_domain->gv);
nn_xpack_free (wr->m_xp);
thread_state_asleep (lookup_thread_state ());
ret = dds_delete (wr->m_topic->m_entity.m_hdllink.hdl);
return ret;
dds_entity_drop_ref (&wr->m_topic->m_entity);
return DDS_RETCODE_OK;
}
static dds_return_t dds_writer_qos_set (dds_entity *e, const dds_qos_t *qos, bool enabled)
@ -277,21 +276,27 @@ dds_entity_t dds_create_writer (dds_entity_t participant_or_publisher, dds_entit
{
dds_entity *p_or_p;
if ((rc = dds_entity_pin (participant_or_publisher, &p_or_p)) != DDS_RETCODE_OK)
if ((rc = dds_entity_lock (participant_or_publisher, DDS_KIND_DONTCARE, &p_or_p)) != DDS_RETCODE_OK)
return rc;
if (dds_entity_kind (p_or_p) == DDS_KIND_PARTICIPANT)
publisher = dds_create_publisher (participant_or_publisher, qos, NULL);
else
publisher = participant_or_publisher;
dds_entity_unpin (p_or_p);
switch (dds_entity_kind (p_or_p))
{
case DDS_KIND_PUBLISHER:
publisher = participant_or_publisher;
pub = (dds_publisher *) p_or_p;
break;
case DDS_KIND_PARTICIPANT:
publisher = dds__create_publisher_l ((dds_participant *) p_or_p, true, qos, NULL);
dds_entity_unlock (p_or_p);
if ((rc = dds_publisher_lock (publisher, &pub)) < 0)
return rc;
break;
default:
dds_entity_unlock (p_or_p);
return DDS_RETCODE_ILLEGAL_OPERATION;
}
}
if ((rc = dds_publisher_lock (publisher, &pub)) != DDS_RETCODE_OK)
return rc;
ddsi_tran_conn_t conn = pub->m_entity.m_domain->gv.data_conn_uc;
if (publisher != participant_or_publisher)
pub->m_entity.m_flags |= DDS_ENTITY_IMPLICIT;
if ((rc = dds_topic_lock (topic, &tp)) != DDS_RETCODE_OK)
goto err_tp_lock;
@ -322,7 +327,7 @@ 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, wqos, listener, DDS_WRITER_STATUS_MASK);
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);

View file

@ -21,12 +21,14 @@ set(ddsc_test_sources
"config.c"
"dispose.c"
"domain.c"
"domain_torture.c"
"entity_api.c"
"entity_hierarchy.c"
"entity_status.c"
"err.c"
"instance_get_key.c"
"listener.c"
"liveliness.c"
"participant.c"
"publisher.c"
"qos.c"
@ -55,7 +57,8 @@ add_cunit_executable(cunit_ddsc ${ddsc_test_sources})
target_include_directories(
cunit_ddsc PRIVATE
"$<BUILD_INTERFACE:${CMAKE_BINARY_DIR}/src/include/>"
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../../ddsi/include/>")
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../../ddsc/src>"
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../../ddsi/include>")
target_link_libraries(cunit_ddsc PRIVATE RoundTrip Space TypesArrayKey ddsc)
# Setup environment for config-tests

View file

@ -27,98 +27,62 @@
#define URI_VARIABLE DDS_PROJECT_NAME_NOSPACE_CAPS"_URI"
#define MAX_PARTICIPANTS_VARIABLE "MAX_PARTICIPANTS"
static void config__check_env(
const char * env_variable,
const char * expected_value)
static void config__check_env (const char *env_variable, const char *expected_value)
{
char * env_uri = NULL;
ddsrt_getenv(env_variable, &env_uri);
#if 0
const char * const env_not_set = "Environment variable '%s' isn't set. This needs to be set to '%s' for this test to run.";
const char * const env_not_as_expected = "Environment variable '%s' has an unexpected value: '%s' (expected: '%s')";
#endif
char *env_uri = NULL;
ddsrt_getenv (env_variable, &env_uri);
#ifdef FORCE_ENV
{
bool env_ok;
if (env_uri == NULL)
env_ok = false;
else if (strncmp (env_uri, expected_value, strlen (expected_value)) != 0)
env_ok = false;
else
env_ok = true;
if (!env_ok)
{
bool env_ok;
if ( env_uri == NULL ) {
env_ok = false;
} else if ( strncmp(env_uri, expected_value, strlen(expected_value)) != 0 ) {
env_ok = false;
} else {
env_ok = true;
}
if ( !env_ok ) {
dds_return_t r;
r = ddsrt_setenv(env_variable, expected_value);
CU_ASSERT_EQUAL_FATAL(r, DDS_RETCODE_OK);
}
dds_return_t r = ddsrt_setenv (env_variable, expected_value);
CU_ASSERT_EQUAL_FATAL (r, DDS_RETCODE_OK);
}
}
#else
CU_ASSERT_PTR_NOT_NULL_FATAL(env_uri);
CU_ASSERT_STRING_EQUAL_FATAL(env_uri, expected_value);
CU_ASSERT_PTR_NOT_NULL_FATAL (env_uri);
CU_ASSERT_STRING_EQUAL_FATAL (env_uri, expected_value);
#endif /* FORCE_ENV */
}
CU_Test(ddsc_config, simple_udp, .init = ddsrt_init, .fini = ddsrt_fini) {
dds_entity_t participant;
config__check_env(URI_VARIABLE, CONFIG_ENV_SIMPLE_UDP);
config__check_env(MAX_PARTICIPANTS_VARIABLE, CONFIG_ENV_MAX_PARTICIPANTS);
participant = dds_create_participant(DDS_DOMAIN_DEFAULT, NULL, NULL);
CU_ASSERT_FATAL(participant> 0);
dds_delete(participant);
CU_Test (ddsc_config, simple_udp, .init = ddsrt_init, .fini = ddsrt_fini)
{
dds_entity_t participant;
config__check_env (URI_VARIABLE, CONFIG_ENV_SIMPLE_UDP);
config__check_env (MAX_PARTICIPANTS_VARIABLE, CONFIG_ENV_MAX_PARTICIPANTS);
participant = dds_create_participant (DDS_DOMAIN_DEFAULT, NULL, NULL);
CU_ASSERT_FATAL (participant> 0);
dds_delete (participant);
}
CU_Test(ddsc_config, user_config, .init = ddsrt_init, .fini = ddsrt_fini) {
CU_Test (ddsc_config, user_config, .init = ddsrt_init, .fini = ddsrt_fini)
{
dds_entity_t domain;
domain = dds_create_domain (1,
"<"DDS_PROJECT_NAME"><Domain><Id>any</Id></Domain>"
"<DDSI2E><Internal><MaxParticipants>2</MaxParticipants></Internal></DDSI2E>"
"</"DDS_PROJECT_NAME">");
CU_ASSERT_FATAL (domain > 0);
CU_ASSERT_FATAL(dds_create_domain(1,
"<"DDS_PROJECT_NAME"><Domain><Id>any</Id></Domain>"
"<DDSI2E><Internal><MaxParticipants>2</MaxParticipants></Internal></DDSI2E>"
"</"DDS_PROJECT_NAME">") == DDS_RETCODE_OK);
dds_entity_t participant_1 = dds_create_participant (1, NULL, NULL);
CU_ASSERT_FATAL(participant_1 > 0);
dds_entity_t participant_1;
dds_entity_t participant_2;
dds_entity_t participant_3;
dds_entity_t participant_2 = dds_create_participant (1, NULL, NULL);
CU_ASSERT_FATAL(participant_2 > 0);
participant_1 = dds_create_participant(1, NULL, NULL);
dds_entity_t participant_3 = dds_create_participant (1, NULL, NULL);
CU_ASSERT(participant_3 < 0);
CU_ASSERT_FATAL(participant_1 > 0);
participant_2 = dds_create_participant(1, NULL, NULL);
CU_ASSERT_FATAL(participant_2 > 0);
participant_3 = dds_create_participant(1, NULL, NULL);
CU_ASSERT(participant_3 <= 0);
dds_delete(participant_3);
dds_delete(participant_2);
dds_delete(participant_1);
}
CU_Test(ddsc_config, incorrect_config, .init = ddsrt_init, .fini = ddsrt_fini) {
CU_ASSERT_FATAL(dds_create_domain(1, NULL) == DDS_RETCODE_BAD_PARAMETER);
CU_ASSERT_FATAL(dds_create_domain(1, "<CycloneDDS incorrect XML") != DDS_RETCODE_OK);
CU_ASSERT_FATAL(dds_create_domain(DDS_DOMAIN_DEFAULT,
"<"DDS_PROJECT_NAME"><Domain><Id>any</Id></Domain>"
"<DDSI2E><Internal><MaxParticipants>2</MaxParticipants></Internal></DDSI2E>"
"</"DDS_PROJECT_NAME">") == DDS_RETCODE_BAD_PARAMETER);
CU_ASSERT_FATAL(dds_create_domain(2,
"<"DDS_PROJECT_NAME"><Domain><Id>any</Id></Domain>"
"<DDSI2E><Internal><MaxParticipants>2</MaxParticipants></Internal></DDSI2E>"
"</"DDS_PROJECT_NAME">") == DDS_RETCODE_OK);
CU_ASSERT_FATAL(dds_create_domain(2, "") == DDS_RETCODE_PRECONDITION_NOT_MET);
dds_delete (domain);
}
/*
@ -823,4 +787,5 @@ CU_Test(ddsc_config, security_qos_invalid, .init = ddsrt_init, .fini = ddsrt_fin
#endif
dds_set_log_sink(NULL, NULL);
dds_set_trace_sink(NULL, NULL);
}
}

View file

@ -1,4 +1,6 @@
<?xml version="1.0" encoding="UTF-8" ?>
<?xml-model href="https://raw.githubusercontent.com/eclipse-cyclonedds/cyclonedds/master/etc/cyclonedds.xsd"
schematypens="http://www.w3.org/2001/XMLSchema" ?>
<!--
Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
@ -10,10 +12,10 @@
SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
-->
<CycloneDDS>
<CycloneDDS xmlns="https://cdds.io/config">
<!-- Simple config-file for testing whether a config-file can be picked up
correctly. -->
<Domain id="3">
<Domain Id="3">
<General>
<NetworkInterfaceAddress>127.0.0.1</NetworkInterfaceAddress>
<AllowMulticast>true</AllowMulticast>
@ -28,8 +30,8 @@
</Tracing>
<Internal>
<MaxParticipants>${MAX_PARTICIPANTS}</MaxParticipants>
<HeartbeatInterval max="10 s"> 100 ms </HeartbeatInterval>
<RediscoveryBlacklistDuration></RediscoveryBlacklistDuration>
<HeartbeatInterval>100 ms</HeartbeatInterval>
<RediscoveryBlacklistDuration>10s</RediscoveryBlacklistDuration>
</Internal>
</Domain>
</CycloneDDS>

View file

@ -140,3 +140,150 @@ CU_Test(ddsc_domain, delete_cyclonedds)
rc = dds_get_domainid (pp[0], &did);
CU_ASSERT_FATAL (rc == DDS_RETCODE_PRECONDITION_NOT_MET);
}
CU_Test(ddsc_domain_create, valid)
{
dds_return_t ret;
dds_domainid_t did;
dds_entity_t domain;
domain = dds_create_domain(1, "<"DDS_PROJECT_NAME"><Domain><Id>1</Id></Domain></"DDS_PROJECT_NAME">");
CU_ASSERT_FATAL(domain > 0);
ret = dds_get_domainid (domain, &did);
CU_ASSERT_FATAL(ret == DDS_RETCODE_OK);
CU_ASSERT_FATAL(did == 1);
ret = dds_delete(domain);
CU_ASSERT_FATAL(ret == DDS_RETCODE_OK);
ret = dds_delete(domain);
CU_ASSERT_FATAL(ret != DDS_RETCODE_OK);
}
CU_Test(ddsc_domain_create, mismatch)
{
dds_return_t ret;
dds_domainid_t did;
dds_entity_t domain;
/* The config should have been ignored. */
domain = dds_create_domain(2, "<"DDS_PROJECT_NAME"><Domain><Id>3</Id></Domain></"DDS_PROJECT_NAME">");
CU_ASSERT_FATAL(domain > 0);
ret = dds_get_domainid (domain, &did);
CU_ASSERT_FATAL(ret == DDS_RETCODE_OK);
CU_ASSERT_FATAL(did == 2);
ret = dds_delete(domain);
CU_ASSERT_FATAL(ret == DDS_RETCODE_OK);
}
CU_Test(ddsc_domain_create, empty)
{
dds_return_t ret;
dds_domainid_t did;
dds_entity_t domain;
/* This should create a domain with default settings. */
domain = dds_create_domain(3, "");
CU_ASSERT_FATAL(domain > 0);
ret = dds_get_domainid (domain, &did);
CU_ASSERT_FATAL(ret == DDS_RETCODE_OK);
CU_ASSERT_FATAL(did == 3);
ret = dds_delete(domain);
CU_ASSERT_FATAL(ret == DDS_RETCODE_OK);
}
CU_Test(ddsc_domain_create, null)
{
dds_return_t ret;
dds_domainid_t did;
dds_entity_t domain;
/* This should start create a domain with default settings. */
domain = dds_create_domain(5, NULL);
CU_ASSERT_FATAL(domain > 0);
ret = dds_get_domainid (domain, &did);
CU_ASSERT_FATAL(ret == DDS_RETCODE_OK);
CU_ASSERT_FATAL(did == 5);
ret = dds_delete(domain);
CU_ASSERT_FATAL(ret == DDS_RETCODE_OK);
}
CU_Test(ddsc_domain_create, after_domain)
{
dds_entity_t domain1;
dds_entity_t domain2;
domain1 = dds_create_domain(4, "<"DDS_PROJECT_NAME"><Domain><Id>any</Id></Domain></"DDS_PROJECT_NAME">");
CU_ASSERT_FATAL(domain1 > 0);
domain2 = dds_create_domain(4, "<"DDS_PROJECT_NAME"><Domain><Id>any</Id></Domain></"DDS_PROJECT_NAME">");
CU_ASSERT_FATAL(domain2 == DDS_RETCODE_PRECONDITION_NOT_MET);
dds_delete(domain1);
}
CU_Test(ddsc_domain_create, after_participant)
{
dds_entity_t domain;
dds_entity_t participant;
participant = dds_create_participant (5, NULL, NULL);
CU_ASSERT_FATAL(participant > 0);
domain = dds_create_domain(5, "<"DDS_PROJECT_NAME"><Domain><Id>any</Id></Domain></"DDS_PROJECT_NAME">");
CU_ASSERT_FATAL(domain == DDS_RETCODE_PRECONDITION_NOT_MET);
dds_delete(participant);
}
CU_Test(ddsc_domain_create, diff)
{
dds_return_t ret;
dds_domainid_t did;
dds_entity_t domain1;
dds_entity_t domain2;
domain1 = dds_create_domain(1, "<"DDS_PROJECT_NAME"><Domain><Id>any</Id></Domain></"DDS_PROJECT_NAME">");
CU_ASSERT_FATAL(domain1 > 0);
domain2 = dds_create_domain(2, "<"DDS_PROJECT_NAME"><Domain><Id>any</Id></Domain></"DDS_PROJECT_NAME">");
CU_ASSERT_FATAL(domain2 > 0);
ret = dds_get_domainid (domain1, &did);
CU_ASSERT_FATAL(ret == DDS_RETCODE_OK);
CU_ASSERT_FATAL(did == 1);
ret = dds_get_domainid (domain2, &did);
CU_ASSERT_FATAL(ret == DDS_RETCODE_OK);
CU_ASSERT_FATAL(did == 2);
ret = dds_delete(domain1);
CU_ASSERT_FATAL(ret == DDS_RETCODE_OK);
ret = dds_delete(domain2);
CU_ASSERT_FATAL(ret == DDS_RETCODE_OK);
ret = dds_delete(domain1);
CU_ASSERT_FATAL(ret != DDS_RETCODE_OK);
ret = dds_delete(domain2);
CU_ASSERT_FATAL(ret != DDS_RETCODE_OK);
}
CU_Test(ddsc_domain_create, domain_default)
{
dds_entity_t domain;
domain = dds_create_domain(DDS_DOMAIN_DEFAULT, "<"DDS_PROJECT_NAME"><Domain><Id>any</Id></Domain></"DDS_PROJECT_NAME">");
CU_ASSERT_FATAL(domain == DDS_RETCODE_BAD_PARAMETER);
}
CU_Test(ddsc_domain_create, invalid_xml)
{
dds_entity_t domain;
domain = dds_create_domain(1, "<CycloneDDS incorrect XML");
CU_ASSERT_FATAL(domain == DDS_RETCODE_ERROR);
}

View file

@ -0,0 +1,118 @@
/*
* Copyright(c) 2019 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 "RoundTrip.h"
#include "dds/ddsrt/threads.h"
#include "dds/ddsrt/atomics.h"
#include "dds/ddsrt/time.h"
#define N_THREADS (10)
static const dds_duration_t TEST_DURATION = DDS_SECS(3);
static ddsrt_atomic_uint32_t terminate;
static uint32_t create_participants_thread (void *varg)
{
(void) varg;
while (!ddsrt_atomic_ld32 (&terminate))
{
dds_entity_t par = dds_create_participant (DDS_DOMAIN_DEFAULT, NULL, NULL);
if (par < 0)
{
fprintf (stderr, "dds_create_participant failed: %s\n", dds_strretcode (par));
ddsrt_atomic_st32 (&terminate, 1);
return 1;
}
dds_return_t ret = dds_delete(par);
if (ret != DDS_RETCODE_OK)
{
fprintf (stderr, "dds_delete failed: %s\n", dds_strretcode (ret));
ddsrt_atomic_st32 (&terminate, 1);
return 1;
}
}
return 0;
}
static void participant_creation_torture()
{
dds_return_t rc;
ddsrt_thread_t tids[N_THREADS];
ddsrt_threadattr_t tattr;
ddsrt_threadattr_init (&tattr);
/* Start threads. */
for (size_t i = 0; i < sizeof(tids) / sizeof(*tids); i++)
{
rc = ddsrt_thread_create (&tids[i], "domain_torture_explicit", &tattr, create_participants_thread, 0);
CU_ASSERT_FATAL (rc == DDS_RETCODE_OK);
}
/* Let the threads do the torturing for a while. */
dds_sleepfor(TEST_DURATION);
/* Stop and check threads results. */
ddsrt_atomic_st32 (&terminate, 1);
for (size_t i = 0; i < sizeof (tids) / sizeof (tids[0]); i++)
{
uint32_t retval;
rc = ddsrt_thread_join (tids[i], &retval);
CU_ASSERT_FATAL (rc == DDS_RETCODE_OK);
CU_ASSERT (retval == 0);
}
}
/*
* There are some issues when completely init/deinit the
* library in a torturing way. We really just want to
* check the domain creation/deletion. So, disable this
* test for now.
*/
CU_Test (ddsc_domain, torture_implicit, .disabled=true)
{
/* No explicit domain creation, just start creating and
* deleting participants (that'll create and delete the
* domain implicitly) in a torturing manner. */
participant_creation_torture();
}
CU_Test (ddsc_domain, torture_explicit)
{
dds_return_t rc;
dds_entity_t domain;
/* Create domain explicitly. */
domain = dds_create_domain(1, "");
CU_ASSERT_FATAL (domain > 0);
/* Start creating and deleting participants on the
* explicit domain in a torturing manner. */
participant_creation_torture();
/* Delete domain. */
rc = dds_delete(domain);
CU_ASSERT_FATAL (rc == DDS_RETCODE_OK);
rc = dds_delete(domain);
CU_ASSERT_FATAL (rc != DDS_RETCODE_OK);
}

View file

@ -192,6 +192,10 @@ CU_Test(ddsc_entity_delete, recursive_with_deleted_topic)
ret = dds_delete(g_topic);
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
/* Second call to delete a topic must fail */
ret = dds_delete(g_topic);
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_ALREADY_DELETED);
/* Third, deleting the participant should delete all children of which
* the writer with the last topic reference is one. */
ret = dds_delete(g_participant);
@ -988,6 +992,70 @@ CU_Test(ddsc_entity_get_parent, implicit_subscriber)
}
/*************************************************************************************************/
/*************************************************************************************************/
CU_Test(ddsc_entity_implicit, delete_publisher)
{
dds_entity_t participant;
dds_entity_t writer;
dds_entity_t parent;
dds_entity_t topic;
dds_return_t ret;
char name[100];
participant = dds_create_participant(DDS_DOMAIN_DEFAULT, NULL, NULL);
CU_ASSERT_FATAL(participant > 0);
topic = dds_create_topic(participant, &RoundTripModule_DataType_desc, create_topic_name("ddsc_entity_implicit_delete_publisher", name, 100), NULL, NULL);
CU_ASSERT_FATAL(topic > 0);
writer = dds_create_writer(participant, topic, NULL, NULL);
CU_ASSERT_FATAL(writer > 0);
parent = dds_get_parent(writer);
CU_ASSERT_FATAL(parent > 0);
ret = dds_delete(parent);
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
ret = dds_delete(writer);
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_BAD_PARAMETER);
dds_delete(participant);
}
/*************************************************************************************************/
/*************************************************************************************************/
CU_Test(ddsc_entity_implicit, delete_subscriber)
{
dds_entity_t participant;
dds_entity_t reader;
dds_entity_t parent;
dds_entity_t topic;
dds_return_t ret;
char name[100];
participant = dds_create_participant(DDS_DOMAIN_DEFAULT, NULL, NULL);
CU_ASSERT_FATAL(participant > 0);
topic = dds_create_topic(participant, &RoundTripModule_DataType_desc, create_topic_name("ddsc_entity_implicit_delete_subscriber", name, 100), NULL, NULL);
CU_ASSERT_FATAL(topic > 0);
reader = dds_create_reader(participant, topic, NULL, NULL);
CU_ASSERT_FATAL(reader > 0);
parent = dds_get_parent(reader);
CU_ASSERT_FATAL(parent > 0);
ret = dds_delete(parent);
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
ret = dds_delete(reader);
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_BAD_PARAMETER);
dds_delete(participant);
}
/*************************************************************************************************/
/*************************************************************************************************/
#endif

View file

@ -360,9 +360,9 @@ CU_Test(ddsc_entity, liveliness_changed, .init=init_entity_status, .fini=fini_en
ret = dds_get_liveliness_changed_status (rea, &liveliness_changed);
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
CU_ASSERT_EQUAL_FATAL(liveliness_changed.alive_count, 0);
CU_ASSERT_EQUAL_FATAL(liveliness_changed.alive_count_change, 0);
CU_ASSERT_EQUAL_FATAL(liveliness_changed.not_alive_count, 1);
CU_ASSERT_EQUAL_FATAL(liveliness_changed.not_alive_count_change,1);
CU_ASSERT_EQUAL_FATAL(liveliness_changed.alive_count_change, -1);
CU_ASSERT_EQUAL_FATAL(liveliness_changed.not_alive_count, 0);
CU_ASSERT_EQUAL_FATAL(liveliness_changed.not_alive_count_change,0);
CU_ASSERT_EQUAL_FATAL(liveliness_changed.last_publication_handle, writer_i_hdl);
/* Second call should reset the changed count. */
@ -370,7 +370,7 @@ CU_Test(ddsc_entity, liveliness_changed, .init=init_entity_status, .fini=fini_en
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
CU_ASSERT_EQUAL_FATAL(liveliness_changed.alive_count, 0);
CU_ASSERT_EQUAL_FATAL(liveliness_changed.alive_count_change, 0);
CU_ASSERT_EQUAL_FATAL(liveliness_changed.not_alive_count, 1);
CU_ASSERT_EQUAL_FATAL(liveliness_changed.not_alive_count, 0);
CU_ASSERT_EQUAL_FATAL(liveliness_changed.not_alive_count_change,0);
CU_ASSERT_EQUAL_FATAL(liveliness_changed.last_publication_handle, writer_i_hdl);
}

View file

@ -1196,9 +1196,9 @@ CU_Test(ddsc_listener, liveliness_changed, .init=init_triggering_test, .fini=fin
CU_ASSERT_EQUAL_FATAL(triggered & DDS_LIVELINESS_CHANGED_STATUS, DDS_LIVELINESS_CHANGED_STATUS);
CU_ASSERT_EQUAL_FATAL(cb_reader, g_reader);
CU_ASSERT_EQUAL_FATAL(cb_liveliness_changed_status.alive_count, 0);
CU_ASSERT_EQUAL_FATAL(cb_liveliness_changed_status.alive_count_change, 0);
CU_ASSERT_EQUAL_FATAL(cb_liveliness_changed_status.not_alive_count, 1);
CU_ASSERT_EQUAL_FATAL(cb_liveliness_changed_status.not_alive_count_change, 1);
CU_ASSERT_EQUAL_FATAL(cb_liveliness_changed_status.alive_count_change, -1);
CU_ASSERT_EQUAL_FATAL(cb_liveliness_changed_status.not_alive_count, 0);
CU_ASSERT_EQUAL_FATAL(cb_liveliness_changed_status.not_alive_count_change, 0);
CU_ASSERT_EQUAL_FATAL(cb_liveliness_changed_status.last_publication_handle, writer_hdl);
/* The listener should have reset the count_change. */
@ -1206,7 +1206,7 @@ CU_Test(ddsc_listener, liveliness_changed, .init=init_triggering_test, .fini=fin
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
CU_ASSERT_EQUAL_FATAL(liveliness_changed.alive_count, 0);
CU_ASSERT_EQUAL_FATAL(liveliness_changed.alive_count_change, 0);
CU_ASSERT_EQUAL_FATAL(liveliness_changed.not_alive_count, 1);
CU_ASSERT_EQUAL_FATAL(liveliness_changed.not_alive_count, 0);
CU_ASSERT_EQUAL_FATAL(liveliness_changed.not_alive_count_change, 0);
CU_ASSERT_EQUAL_FATAL(liveliness_changed.last_publication_handle, writer_hdl);
}

View file

@ -0,0 +1,815 @@
/*
* 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/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_liveliness_tests.${CYCLONEDDS_DOMAIN_ID}.${CYCLONEDDS_PID}.log</OutputFile><Verbosity>finest</Verbosity></Tracing><Discovery><ExternalDomainId>0</ExternalDomainId></Discovery>"
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 liveliness_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 liveliness_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);
}
/**
* Gets the current PMD sequence number for the participant. This
* can be used to count the number of PMD messages that is sent by
* the participant.
*/
static seqno_t get_pmd_seqno(dds_entity_t participant)
{
seqno_t seqno;
struct dds_entity *pp_entity;
struct participant *pp;
struct writer *wr;
CU_ASSERT_EQUAL_FATAL(dds_entity_pin(participant, &pp_entity), 0);
thread_state_awake(lookup_thread_state(), &pp_entity->m_domain->gv);
pp = ephash_lookup_participant_guid(pp_entity->m_domain->gv.guid_hash, &pp_entity->m_guid);
wr = get_builtin_writer(pp, NN_ENTITYID_P2P_BUILTIN_PARTICIPANT_MESSAGE_WRITER);
CU_ASSERT_FATAL(wr != NULL);
assert(wr != NULL); /* for Clang's static analyzer */
seqno = wr->seq;
thread_state_asleep(lookup_thread_state());
dds_entity_unpin(pp_entity);
return seqno;
}
/**
* Gets the current PMD interval for the participant
*/
static dds_duration_t get_pmd_interval(dds_entity_t participant)
{
dds_duration_t intv;
struct dds_entity *pp_entity;
struct participant *pp;
CU_ASSERT_EQUAL_FATAL(dds_entity_pin(participant, &pp_entity), 0);
thread_state_awake(lookup_thread_state(), &pp_entity->m_domain->gv);
pp = ephash_lookup_participant_guid(pp_entity->m_domain->gv.guid_hash, &pp_entity->m_guid);
intv = pp_get_pmd_interval(pp);
thread_state_asleep(lookup_thread_state());
dds_entity_unpin(pp_entity);
return intv;
}
/**
* Gets the current lease duration for the participant
*/
static dds_duration_t get_ldur_config(dds_entity_t participant)
{
struct dds_entity *pp_entity;
dds_duration_t ldur;
CU_ASSERT_EQUAL_FATAL(dds_entity_pin(participant, &pp_entity), 0);
ldur = (dds_duration_t)pp_entity->m_domain->gv.config.lease_duration;
dds_entity_unpin(pp_entity);
return ldur;
}
/**
* Test that the correct number of PMD messages is sent for
* the various liveliness kinds.
*/
#define A DDS_LIVELINESS_AUTOMATIC
#define MP DDS_LIVELINESS_MANUAL_BY_PARTICIPANT
#define MT DDS_LIVELINESS_MANUAL_BY_TOPIC
CU_TheoryDataPoints(ddsc_liveliness, pmd_count) = {
CU_DataPoints(dds_liveliness_kind_t, A, A, MP, MT), /* liveliness kind */
CU_DataPoints(uint32_t, 200, 500, 100, 100), /* lease duration */
CU_DataPoints(double, 10, 5, 5, 5), /* delay (n times lease duration) */
};
#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)
{
dds_entity_t pub_topic;
dds_entity_t sub_topic;
dds_entity_t reader;
dds_entity_t writer;
seqno_t start_seqno, end_seqno;
dds_qos_t *rqos;
dds_qos_t *wqos;
dds_entity_t waitset;
dds_attach_t triggered;
uint32_t status;
char name[100];
dds_time_t t;
t = dds_time();
printf("%d.%06d running test: kind %s, lease duration %d, delay %d\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));
/* wait for initial PMD to be sent by the participant */
while (get_pmd_seqno(g_pub_participant) < 1)
dds_sleepfor(DDS_MSECS(50));
/* 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);
/* 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);
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_EQUAL_FATAL(dds_waitset_attach(waitset, reader, reader), DDS_RETCODE_OK);
/* writer */
CU_ASSERT_FATAL((wqos = dds_create_qos()) != NULL);
dds_qset_liveliness(wqos, kind, DDS_MSECS(ldur));
CU_ASSERT_FATAL((writer = dds_create_writer(g_pub_participant, pub_topic, wqos, NULL)) > 0);
dds_delete_qos(wqos);
/* wait for writer to be alive */
CU_ASSERT_EQUAL_FATAL(dds_waitset_wait(waitset, &triggered, 1, DDS_SECS(1)), 1);
CU_ASSERT_EQUAL_FATAL(dds_take_status(reader, &status, DDS_LIVELINESS_CHANGED_STATUS), DDS_RETCODE_OK);
/* check no of PMD messages sent */
start_seqno = get_pmd_seqno(g_pub_participant);
dds_sleepfor(DDS_MSECS((dds_duration_t)(mult * ldur)));
end_seqno = get_pmd_seqno(g_pub_participant);
t = dds_time();
printf("%d.%06d PMD sequence no: start %" PRId64 " -> end %" PRId64 "\n",
(int32_t)(t / DDS_NSECS_IN_SEC), (int32_t)(t % DDS_NSECS_IN_SEC) / 1000,
start_seqno, end_seqno);
/* 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))
if (kind != DDS_LIVELINESS_AUTOMATIC)
CU_ASSERT(get_pmd_seqno(g_pub_participant) - start_seqno < mult)
/* cleanup */
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);
}
/**
* Test that the expected number of proxy writers expires (set to not-alive)
* after a certain delay for various combinations of writers with different
* liveliness kinds.
*/
CU_TheoryDataPoints(ddsc_liveliness, expire_liveliness_kinds) = {
CU_DataPoints(uint32_t, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200), /* lease duration for initial test run (increased for each retry when test fails) */
CU_DataPoints(double, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 2, 2, 2, 2, 2, 2, 2, 2, 2), /* delay (n times lease duration) */
CU_DataPoints(uint32_t, 1, 0, 2, 0, 1, 0, 0, 1, 1, 2, 0, 5, 0, 15, 15), /* number of writers with automatic liveliness */
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)
{
dds_entity_t pub_topic;
dds_entity_t sub_topic;
dds_entity_t reader;
dds_entity_t *writers;
dds_qos_t *rqos, *wqos_auto, *wqos_man_pp, *wqos_man_tp;
dds_entity_t waitset;
dds_attach_t triggered;
struct dds_liveliness_changed_status lstatus;
uint32_t status, n, run = 1, wr_cnt = wr_cnt_auto + wr_cnt_man_pp + wr_cnt_man_tp;
char name[100];
dds_time_t tstart, t;
bool test_finished = false;
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",
(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);
/* 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);
/* 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);
dds_delete_qos(rqos);
CU_ASSERT_EQUAL_FATAL(dds_set_status_mask(reader, DDS_LIVELINESS_CHANGED_STATUS), DDS_RETCODE_OK);
/* writers */
CU_ASSERT_FATAL((wqos_auto = dds_create_qos()) != NULL);
dds_qset_liveliness(wqos_auto, DDS_LIVELINESS_AUTOMATIC, DDS_MSECS(ldur));
CU_ASSERT_FATAL((wqos_man_pp = dds_create_qos()) != NULL);
dds_qset_liveliness(wqos_man_pp, DDS_LIVELINESS_MANUAL_BY_PARTICIPANT, DDS_MSECS(ldur));
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_EQUAL_FATAL(dds_waitset_attach(waitset, reader, reader), DDS_RETCODE_OK);
writers = dds_alloc(wr_cnt * sizeof(dds_entity_t));
for (n = 0; n < wr_cnt; n++)
{
dds_qos_t *wqos;
wqos = n < wr_cnt_auto ? wqos_auto : (n < (wr_cnt_auto + wr_cnt_man_pp) ? wqos_man_pp : wqos_man_tp);
CU_ASSERT_FATAL((writers[n] = dds_create_writer(g_pub_participant, pub_topic, wqos, NULL)) > 0);
CU_ASSERT_EQUAL_FATAL(dds_waitset_wait(waitset, &triggered, 1, DDS_SECS(5)), 1);
CU_ASSERT_EQUAL_FATAL(dds_take_status(reader, &status, DDS_LIVELINESS_CHANGED_STATUS), DDS_RETCODE_OK);
}
dds_delete_qos(wqos_auto);
dds_delete_qos(wqos_man_pp);
dds_delete_qos(wqos_man_tp);
t = dds_time();
if (t - tstart > DDS_MSECS(0.5 * ldur))
{
ldur *= 10 / (run + 1);
printf("%d.%06d failed to create writers in time\n",
(int32_t)(t / DDS_NSECS_IN_SEC), (int32_t)(t % DDS_NSECS_IN_SEC) / 1000);
}
else
{
/* check alive count before proxy writers are expired */
dds_get_liveliness_changed_status(reader, &lstatus);
printf("%d.%06d writers alive: %d\n", (int32_t)(t / DDS_NSECS_IN_SEC), (int32_t)(t % DDS_NSECS_IN_SEC) / 1000, lstatus.alive_count);
CU_ASSERT_EQUAL_FATAL(lstatus.alive_count, wr_cnt);
dds_time_t tstop = tstart + DDS_MSECS((dds_duration_t)(mult * ldur));
uint32_t stopped = 0;
do
{
dds_duration_t w = tstop - dds_time();
CU_ASSERT_FATAL((dds_waitset_wait(waitset, &triggered, 1, w > 0 ? w : 0)) >= 0);
CU_ASSERT_EQUAL_FATAL(dds_get_liveliness_changed_status(reader, &lstatus), DDS_RETCODE_OK);
stopped += (uint32_t)lstatus.not_alive_count_change;
} while (dds_time() < tstop);
t = dds_time();
printf("%d.%06d writers stopped: %u\n",
(int32_t)(t / DDS_NSECS_IN_SEC), (int32_t)(t % DDS_NSECS_IN_SEC) / 1000, stopped);
size_t exp_stopped = mult < 1 ? 0 : (wr_cnt_man_pp + wr_cnt_man_tp);
if (stopped != exp_stopped)
{
ldur *= 10 / (run + 1);
printf("%d.%06d incorrect number of stopped writers\n",
(int32_t)(t / DDS_NSECS_IN_SEC), (int32_t)(t % DDS_NSECS_IN_SEC) / 1000);
}
else
{
/* check alive count */
CU_ASSERT_EQUAL_FATAL(dds_get_liveliness_changed_status(reader, &lstatus), DDS_RETCODE_OK);
CU_ASSERT_EQUAL(lstatus.alive_count, mult < 1 ? wr_cnt : wr_cnt_auto);
test_finished = true;
}
}
/* cleanup */
CU_ASSERT_EQUAL_FATAL(dds_waitset_detach(waitset, reader), DDS_RETCODE_OK);
CU_ASSERT_EQUAL_FATAL(dds_delete(waitset), DDS_RETCODE_OK);
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);
CU_ASSERT_EQUAL_FATAL(dds_delete(pub_topic), DDS_RETCODE_OK);
CU_ASSERT_EQUAL_FATAL(dds_delete(reader), DDS_RETCODE_OK);
if (!test_finished)
{
if (++run > 3)
{
printf("%d.%06d run limit reached, test failed\n", (int32_t)(tstart / DDS_NSECS_IN_SEC), (int32_t)(tstart % DDS_NSECS_IN_SEC) / 1000);
CU_FAIL_FATAL("Run limit reached");
test_finished = true;
continue;
}
else
{
printf("%d.%06d restarting test with ldur %d\n",
(int32_t)(t / DDS_NSECS_IN_SEC), (int32_t)(t % DDS_NSECS_IN_SEC) / 1000, ldur);
}
}
} 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)
{
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_EQUAL_FATAL(dds_waitset_attach(waitset, reader, reader), DDS_RETCODE_OK);
CU_ASSERT_FATAL((wqos = dds_create_qos()) != NULL);
dds_qset_liveliness(wqos, kind, ldur);
CU_ASSERT_FATAL((*writer = dds_create_writer(g_pub_participant, topic, wqos, NULL)) > 0);
dds_delete_qos(wqos);
/* wait for writer to be alive */
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);
CU_ASSERT_EQUAL_FATAL(dds_waitset_detach(waitset, reader), DDS_RETCODE_OK);
CU_ASSERT_EQUAL_FATAL(dds_delete(waitset), DDS_RETCODE_OK);
}
/**
* Test that the correct PMD interval is set for the participant
* based on the lease duration of the writers.
*/
#define MAX_WRITERS 10
CU_Test(ddsc_liveliness, lease_duration, .init = liveliness_init, .fini = liveliness_fini)
{
dds_entity_t pub_topic;
dds_entity_t sub_topic;
dds_entity_t reader;
dds_entity_t writers[MAX_WRITERS];
uint32_t wr_cnt = 0;
char name[100];
dds_qos_t *rqos;
uint32_t n;
/* topics */
create_topic_name("ddsc_liveliness_ldur", 1, 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);
/* 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);
dds_delete_qos(rqos);
CU_ASSERT_EQUAL_FATAL(dds_set_status_mask(reader, DDS_LIVELINESS_CHANGED_STATUS), DDS_RETCODE_OK);
/* check if pmd defaults to configured duration */
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);
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);
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);
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);
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);
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);
CU_ASSERT_EQUAL_FATAL(get_pmd_interval(g_pub_participant), DDS_MSECS(500));
/* cleanup */
for (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);
CU_ASSERT_EQUAL_FATAL(dds_delete(pub_topic), DDS_RETCODE_OK);
CU_ASSERT_EQUAL_FATAL(dds_delete(reader), DDS_RETCODE_OK);
}
#undef MAX_WRITERS
/**
* 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)
{
dds_entity_t pub_topic;
dds_entity_t sub_topic;
dds_entity_t reader;
dds_entity_t writer;
char name[100];
dds_qos_t *rqos, *wqos;
dds_entity_t waitset;
dds_attach_t triggered;
uint32_t status;
dds_duration_t ldur;
/* topics */
create_topic_name("ddsc_liveliness_ldurpwr", 1, 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);
/* 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);
dds_delete_qos(rqos);
CU_ASSERT_EQUAL_FATAL(dds_set_status_mask(reader, DDS_LIVELINESS_CHANGED_STATUS), DDS_RETCODE_OK);
/* writer */
ldur = 1000;
CU_ASSERT_FATAL((wqos = dds_create_qos()) != NULL);
dds_qset_liveliness(wqos, DDS_LIVELINESS_AUTOMATIC, DDS_MSECS(ldur));
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_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);
/* check pwr lease duration in matched publication */
dds_instance_handle_t wrs[1];
CU_ASSERT_EQUAL_FATAL(dds_get_matched_publications(reader, wrs, 1), 1);
dds_builtintopic_endpoint_t *ep;
ep = dds_get_matched_publication_data(reader, wrs[0]);
CU_ASSERT_FATAL(ep != NULL);
assert(ep != NULL); /* for Clang's static analyzer */
CU_ASSERT_EQUAL_FATAL(ep->qos->liveliness.lease_duration, DDS_MSECS(ldur));
dds_delete_qos(ep->qos);
dds_free(ep->topic_name);
dds_free(ep->type_name);
dds_free(ep);
/* cleanup */
dds_delete_qos(wqos);
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);
CU_ASSERT_EQUAL_FATAL(dds_delete(pub_topic), DDS_RETCODE_OK);
CU_ASSERT_EQUAL_FATAL(dds_delete(reader), DDS_RETCODE_OK);
}
/**
* Create a relative large number of writers with liveliness kinds automatic and
* manual-by-participant and with decreasing lease duration, and check that all
* writers become alive. During the writer creation loop, every third writer
* 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)
{
dds_entity_t pub_topic;
dds_entity_t sub_topic;
dds_entity_t reader;
dds_entity_t writers[MAX_WRITERS];
dds_entity_t waitset;
dds_qos_t *wqos;
struct dds_liveliness_changed_status lstatus;
uint32_t alive_writers_auto = 0, alive_writers_man = 0;
char name[100];
dds_qos_t *rqos;
dds_attach_t triggered;
uint32_t n;
Space_Type1 sample = {0, 0, 0};
int64_t ldur = 1000;
/* topics */
create_topic_name("ddsc_liveliness_wr_stress", 1, 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);
/* 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);
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_EQUAL_FATAL(dds_waitset_attach(waitset, reader, reader), DDS_RETCODE_OK);
/* create 1st writer and wait for it to become alive */
CU_ASSERT_FATAL((wqos = dds_create_qos()) != NULL);
dds_qset_liveliness(wqos, DDS_LIVELINESS_MANUAL_BY_PARTICIPANT, DDS_MSECS(ldur));
CU_ASSERT_FATAL((writers[0] = dds_create_writer(g_pub_participant, pub_topic, wqos, NULL)) > 0);
CU_ASSERT_EQUAL_FATAL(dds_waitset_wait(waitset, &triggered, 1, DDS_MSECS(1000)), 1);
alive_writers_man++;
/* create writers */
for (n = 1; n < MAX_WRITERS; n++)
{
dds_qset_liveliness(wqos, n % 2 ? DDS_LIVELINESS_AUTOMATIC : DDS_LIVELINESS_MANUAL_BY_PARTICIPANT, DDS_MSECS(n % 3 ? ldur + n : ldur - n) + ((n % 3) == 2 ? 1 : 0));
CU_ASSERT_FATAL((writers[n] = dds_create_writer(g_pub_participant, pub_topic, wqos, NULL)) > 0);
dds_write(writers[n], &sample);
if (n % 3 == 2)
dds_delete(writers[n]);
else if (n % 2)
alive_writers_auto++;
else
alive_writers_man++;
}
dds_delete_qos(wqos);
printf("alive_writers_auto: %d, alive_writers_man: %d\n", alive_writers_auto, alive_writers_man);
/* wait for auto liveliness writers to become alive and manual-by-pp writers to become not-alive */
do
{
CU_ASSERT_EQUAL_FATAL(dds_get_liveliness_changed_status(reader, &lstatus), DDS_RETCODE_OK);
printf("alive: %d, not-alive: %d\n", lstatus.alive_count, lstatus.not_alive_count);
dds_sleepfor(DDS_MSECS(50));
} while (lstatus.alive_count != alive_writers_auto || lstatus.not_alive_count != alive_writers_man);
/* check that counts are stable after a delay */
dds_sleepfor(DDS_MSECS(ldur / 2));
CU_ASSERT_EQUAL_FATAL(dds_get_liveliness_changed_status(reader, &lstatus), DDS_RETCODE_OK);
CU_ASSERT_FATAL(lstatus.alive_count == alive_writers_auto && lstatus.not_alive_count == alive_writers_man);
/* cleanup remaining writers */
for (n = 0; n < MAX_WRITERS; n++)
{
if (n % 3 != 2)
CU_ASSERT_EQUAL_FATAL(dds_delete(writers[n]), DDS_RETCODE_OK);
}
/* wait for alive_count and not_alive_count to become 0 */
do
{
CU_ASSERT_EQUAL_FATAL(dds_get_liveliness_changed_status(reader, &lstatus), DDS_RETCODE_OK);
printf("alive: %d, not: %d\n", lstatus.alive_count, lstatus.not_alive_count);
dds_sleepfor(DDS_MSECS(ldur / 10));
} while (lstatus.alive_count > 0 || lstatus.not_alive_count > 0);
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);
CU_ASSERT_EQUAL_FATAL(dds_delete(pub_topic), DDS_RETCODE_OK);
}
#undef MAX_WRITERS
/**
* Check the counts in liveliness_changed_status result.
*/
CU_Test(ddsc_liveliness, status_counts, .init = liveliness_init, .fini = liveliness_fini)
{
dds_entity_t pub_topic;
dds_entity_t sub_topic;
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_subscription_matched_status sstatus;
char name[100];
dds_duration_t ldur = DDS_MSECS(500);
Space_Type1 sample = {1, 0, 0};
/* 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);
/* 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);
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_EQUAL_FATAL(dds_waitset_attach(waitset, reader, reader), DDS_RETCODE_OK);
/* writer */
CU_ASSERT_FATAL((wqos = dds_create_qos()) != NULL);
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);
/* 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_subscription_matched_status(reader, &sstatus);
CU_ASSERT_EQUAL_FATAL(sstatus.current_count, 1);
/* 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_subscription_matched_status(reader, &sstatus);
CU_ASSERT_EQUAL_FATAL(sstatus.current_count, 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_subscription_matched_status(reader, &sstatus);
CU_ASSERT_EQUAL_FATAL(sstatus.current_count, 1);
/* 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);
CU_ASSERT_EQUAL_FATAL(dds_delete(pub_topic), DDS_RETCODE_OK);
}
/**
* 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_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)
{
dds_entity_t pub_topic, sub_topic, reader, writers[MAX_WRITERS];
dds_qos_t *rqos;
struct dds_liveliness_changed_status lstatus;
char name[100];
uint32_t ldur = 100, wr_cnt, run = 1, stopped;
dds_time_t tstart, tstop, t;
bool test_finished = false;
do
{
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);
/* 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);
/* 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);
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);
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);
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);
t = dds_time();
if (t - tstart > DDS_MSECS(0.5 * ldur))
{
ldur *= 10 / (run + 1);
printf("%d.%06d failed to create writers with non-automatic liveliness kind in time\n",
(int32_t)(t / DDS_NSECS_IN_SEC), (int32_t)(t % DDS_NSECS_IN_SEC) / 1000);
}
else
{
/* check status counts before proxy writer is expired */
dds_get_liveliness_changed_status(reader, &lstatus);
CU_ASSERT_EQUAL_FATAL(lstatus.alive_count, wr_cnt_auto + wr_cnt_man_pp + wr_cnt_man_tp);
/* delay for more than lease duration and assert liveliness on writers:
all writers (including man-by-pp) should be kept alive */
tstop = dds_time() + 4 * DDS_MSECS(ldur) / 3;
stopped = 0;
do
{
for (size_t n = wr_cnt - wr_cnt_man_tp; n < wr_cnt; n++)
dds_assert_liveliness(writers[n]);
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);
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)
{
ldur *= 10 / (run + 1);
printf("incorrect number of writers alive or stopped writers\n");
}
else
{
/* delay for more than lease duration and assert liveliness on participant:
writers with liveliness man-by-pp should be kept alive, man-by-topic writers
should stop */
tstop = dds_time() + 4 * DDS_MSECS(ldur) / 3;
stopped = 0;
do
{
dds_assert_liveliness(g_pub_participant);
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);
printf("writers alive with dds_assert_liveliness on participant: %d, writers stopped: %d\n", lstatus.alive_count, stopped);
if (lstatus.alive_count != wr_cnt_auto + wr_cnt_man_pp || stopped != wr_cnt_man_tp)
{
ldur *= 10 / (run + 1);
printf("incorrect number of writers alive or stopped writers\n");
}
else
{
test_finished = true;
}
}
}
/* cleanup */
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);
CU_ASSERT_EQUAL_FATAL(dds_delete(pub_topic), DDS_RETCODE_OK);
if (!test_finished)
{
if (++run > 3)
{
CU_FAIL_FATAL("Run limit reached");
test_finished = true;
continue;
}
else
{
printf("restarting test with ldur %d\n", ldur);
}
}
} while (!test_finished);
}
#undef MAX_WRITERS