/* * (c) Copyright, Real-Time Innovations, 2010-2024. * All rights reserved. * * No duplications, whole or partial, manual or electronic, may be made * without express written permission. Any such copies, or * revisions thereof, must display this notice unaltered. * This code contains trade secrets of Real-Time Innovations, Inc. */ #ifndef metp_dds_h #define metp_dds_h #include "metp/metp_dll.h" #include "ndds/ndds_c.h" #ifdef __cplusplus extern "C" { #endif /*e \defgroup METPModuleIDL rtiddsgen \ingroup RTI ZCSHMEM requires rtiddsgen to generate additional code to handle multiple encapsulations. To enable the additional support, annotate types that require zero copy over shared memory support with \@transfer_mode(SHMEM_REF). For example:

\verbatim @transfer_mode(SHMEM_REF) struct Foo { long id; long data[10000]; }; \endverbatim If the annotation is used on an unsupported IDL type (such as a sequence) as a member, an error indicating unsupported type will be observed during code generation. \n\n @section Supported IDL Types
IDL Construct Support
Modules Supported
Interfaces Ignored
Constants Supported
Basic Data Types Supported
Enums Supported
String Types Not Supported
Wide String Types Not Supported
Struct Types Supported. Inline nested structures, such as the following example, are not supported. \verbatim struct Outer { short outer_short; struct Inner { char inner_char; short inner_short; } outer_nested_inner; }; \endverbatim
Fixed Types Ignored
Union Types Supported
Sequence Types Not Supported.
Array Types Supported
Typedefs Supported
Any Not Supported. rtiddsgen does not ignore them. Do not use this construct in the IDL file.
Value Types Ignored
Exception Types Ignored
Type Code Supported
*/ /*e \defgroup METPModuleAPI ZCSHMEM C and C++ APIs \ingroup RTI */ /*e \defgroup METPModuleAPIC ZCSHMEM C API \ingroup METPModuleAPI */ #ifdef DOXYGEN_DOCUMENTATION_ONLY /*e \ingroup METPModuleAPIC \brief Returns a pointer to a sample of IDL type, Foo. The space allocated to this sample is the maximum size of the IDL type mapped to C.\n\n The following is a description of the different possible states of a sample: free, allocated, in use, and removed.\n\n A sample that has never been allocated is "free". create_data() takes a "free" or "removed" sample and makes it "allocated". write() takes an "allocated" or "removed" sample and puts it "in use". An "in use" sample becomes "removed" when DDS removes it from the writer queue. "Removed" samples are made "free" when the DDS domain participant is deleted.\n\n The recommended approach is to create_data, write, create_data, write, etc. @param self \st_in. Pointer to self. Must not be NULL. @param initialize \st_in. Controls initialization of the sample when it is allocated. If \p initialize is DDS_BOOLEAN_TRUE, a sample will be initialized to zero. If \p initialize is DDS_BOOLEAN_FALSE, a sample with undefined contents is returned. @return Pointer to a sample of type Foo if space is available, NULL otherwise. Returns NULL only if writer_sample_allocation_settings.max_count samples are allocated or in use. writer_sample_allocation_settings is available in shmem_ref_transfer_mode_settings of DataWriter TypeSupportQosPolicy. The pointer returned is aligned to 8-byte boundary. It is guaranteed that the distance between any two pointers returned by FooDataWriter_create_data(..) is at least the maximum size of the IDL type mapped to C.\n\n When zero copy data over shared memory transport is enabled, RTI DDS will allocate shared memory based on the standard DDS_ResourceLimitsQosPolicy for the DataWriter: -# DDS_ResourceLimitsQosPolicy.initial_samples. The initial amount of bytes in shared memory allocated by a DataWriter is at least initial_samples * the maximum size of the IDL type mapped to C. -# DDS_ResourceLimitsQosPolicy.max_samples. The maximum number of bytes allocated in shared memory by a DataWriter is at least (max_samples + max_samples_reserve) * the maximum size of the IDL type mapped to C. -# It is recommended to set initial_samples = max_samples to minimize the number of shared memory segments that are created. -# When the resource-limits are exhausted, no additional samples can be allocated. -# If the ResourceLimitsQosPolicy specified on the DataWriter exceeds available OS system resources, the application may run out of shared memory space for samples. -# If the ResourceLimitsQosPolicy specified on the DataWriter exceeds available OS system resources, FooDataWriter_create_data(..) returns NULL. */ void *FooDataWriter_create_data(DDS_DataWriter *self, DDS_Boolean initialize); /*e \ingroup METPModuleAPIC \brief Called when a sample is no longer in use and is removed (see definitions of "in use" and "removed" in the documentation for FooDataWriter_create_data) and when the DDS_DATA_WRITER_SAMPLE_REMOVED_STATUS bit is set on the FooDataWriterListener mask. Writing a sample already "in use" will return this error: METPMemPool_getBufferState:!legal state @param listener_data \st_in Data associated with the listener when the listener is set @param writer \st_out. Locally created DDS_DataWriter that triggers the listener callback. @param cookie \st_out. Contains the absolute address of the sample no longer in use. You can do the following to get the address:\n \verbatim void *location = NULL; location = *(void**)DDS_OctetSeq_get_contiguous_buffer(&cookie.value); printf("on_sample_removed. location = %p\n", location); \endverbatim */ void FooDataWriterListener_on_sample_removed( void *listener_data, DDS_DataWriter *writer, const struct DDS_Cookie_t *cookie); /*e \defgroup METPModuleAPICPP ZCSHMEM C++ API \ingroup METPModuleAPI */ /*e \ingroup METPModuleAPICPP \brief Returns a pointer to a sample of the calling entity's data type. The space allocated to this sample is the maximum size of the IDL type mapped to C++.\n\n The following is a description of the different possible states of a sample: free, allocated, in use, and removed.\n\n A sample that has never been allocated is "free". create_data() takes a "free" or "removed" sample and makes it "allocated". write() takes an "allocated" or "removed" sample and puts it "in use". An "in use" sample becomes "removed" when DDS removes it from the writer queue. "Removed" samples are made "free" when the DDS domain participant is deleted.\n\n If you have more than one writer in your application, the recommended approach is to create_data, write, create_data, write, etc. If writer 1 calls create_data, write, waits for on_sample_removed, and then writer 2 calls create_data, writer 2 could get the "removed" sample of writer 1. At this point, both writers are working with the same sample, so then when writer 1 makes an update to the sample, writer 2 will unintentionally get that update, causing confusion. However, if writer 1 calls create_data before making the update, it will get a fresh sample different from the one that writer 2 has, avoiding confusion. @param self \st_in. Pointer to self. Must not be NULL. @param initialize \st_in. Controls initialization of the sample when it is allocated. If \p initialize is DDS_BOOLEAN_TRUE, a sample will be initialized to zero. If \p initialize is DDS_BOOLEAN_FALSE, a sample with undefined contents is returned. @return Pointer to a sample of type Foo if space is available, NULL otherwise. Returns NULL only if (the value of the property dds.data_writer.type_plugin.max_samples_reserve + the value of the QoS datawriter_qos.resource_limits.max_samples) samples are allocated or in use. The pointer returned is guaranteed to be aligned with the largest CPU alignment requirement for any type on a specific platform. On the Cell platform, the pointer will be aligned on a 128 byte boundary per IBM performance recommendations. It is guaranteed that the distance between any two pointers returned by FooDataWriter_create_data(..) is at least the maximum size of the IDL type mapped to C++.\n\n When zero copy data over shared memory transport is enabled, RTI DDS will allocate shared memory based on the standard DDS_ResourceLimitsQosPolicy for the DataWriter: -# DDS_ResourceLimitsQosPolicy.initial_samples. The initial amount of bytes in shared memory allocated by a DataWriter is at least initial_samples * the maximum size of the IDL type mapped to C++. -# DDS_ResourceLimitsQosPolicy.max_samples. The maximum number of bytes allocated in shared memory by a DataWriter is at least (max_samples + max_samples_reserve) * the maximum size of the IDL type mapped to C++. -# When the resource-limits are exhausted, no additional samples can be allocated. -# If the ResourceLimitsQosPolicy specified on the DataWriter exceeds available OS system resources, the application may run out of shared memory space for samples. -# If the ResourceLimitsQosPolicy specified on the DataWriter exceeds available OS system resources, FooDataWriter_create_data(..) returns NULL. */ void *FooDataWriter_create_data(DDSDataWriter *self, DDS_Boolean initialize); /*e \ingroup METPModuleAPICPP \brief Called when a sample is no longer in use and is removed (see definitions of "in use" and "removed" in the documentation for FooDataWriter_create_data) and when the DDS_DATA_WRITER_SAMPLE_REMOVED_STATUS bit is set on the FooDataWriterListener mask. Writing a sample already "in use" will return this error: METPMemPool_getBufferState:!legal state @param writer \st_out. Locally created DDS_DataWriter that triggers the listener callback. @param cookie \st_out. Contains the absolute address of the sample no longer in use. You can do the following to get the address:\n \verbatim void *location = NULL; location = *(void**)DDS_OctetSeq_get_contiguous_buffer(&cookie.value); printf("on_sample_removed. location = %p\n", location); \endverbatim */ virtual void FooDataWriterListener_on_sample_removed( DDSDataWriter *writer, const struct DDS_Cookie_t& cookie); #endif /*e \defgroup METPModuleExample Examples \ingroup RTI ZCSHMEM comes with example applications and QoS profiles, which can be found in the following locations: \verbatim $NDDSHOME/example/C/helloWorldMETP - Example HelloWorld in C $NDDSHOME/example/CPP/helloWorldMETP - Example HelloWorld in C++ $NDDSHOME/example/CPP/performance/latencyMETP - Example latency test in C++ $NDDSHOME/example/CPP/performance/throughputMETP - Example throughput test in C++ \endverbatim Running the Examples

To build the applications, use the provided makefiles. Please make sure that NDDSHOME is set. For example, to compile HelloWorld: \verbatim declare -x NDDSHOME= make -f makefile_HelloWorld_x64Linux2.6gcc4.5.1 \endverbatim After running make, you can find the executables in: \verbatim ./objs/x64Linux2.6gcc4.5.1/HelloWorld_publisher ./objs/x64Linux2.6gcc4.5.1/HelloWorld_subscriber \endverbatim All executables have a help option (not all applications support all options): \verbatim ./objs/x64Linux2.6gcc4.5.1/HelloWorld_publisher -h -sleep - Sleep between each write call in ms. Default=1000 -domain - Domain to join. Default=0 -count - Number of samples to send. 0 = infinite (Default) -profile - Name of QoS profile to use -hello - Publish HelloWorld sample -10mb - Publish 10MB sample -50mb - Publish 50MB sample -100mb - Publish 100MB sample -v - Print non-error messages \endverbatim

All the examples follow the general design described below. It is assumed that you are familiar with the different \ndds read and take APIs, as well as loan and return semantics.

All the QoS are written using the XML profile syntax. The following example forms the basis for all examples. This example uses the Multi-Encapsulation Type Support (METP) to publish and subscribe to an IDL type called MyType. IDL

ZCSHMEM requires additonal code to be generated. Use rtiddsgen's -metp option to enable ZCSHMEM functionality. \verbatim MyType.idl struct MyType { long x; long y; octet data[120]; }; rtiddsgen -language C -metp MyType.idl \endverbatim DataWriterQos Enable ZCSHMEM only for DataWriters that communicate using the \ndds zero-copy over shared memory transport. \verbatim dds.data_writer.type_plugin.disable_zcshmem true \endverbatim DataReaderQos Enable ZCSHMEM only for DataReaders that communicate using the \ndds zero-copy over shared memory transport. \verbatim dds.data_reader.type_plugin.disable_zcshmem true \endverbatim Publishing Data \verbatim MyType *sample; MyTypeDataWriter *writer; Create a DDS DomainParticipant, Publisher, and DataWriter and register MyType as normal. while (data_to_send) { sample = MyTypeDataWriter_create_data(writer, DDS_BOOLEAN_TRUE); if (sample == NULL) [ continue; } sample-> = ....; MyTypeDataWriter_write(sample,....); } \endverbatim Subscribing to Data, Zero-Copy \verbatim MyTypeDataReader *reader; MyTypeSeq data_seq = = DDS_SEQUENCE_INITIALIZER; DDS_SampleInfoSeq info_seg = DDS_SEQUENCE_INITIALIZER; DDS_ReturnCode_t retcode; Create a DDS DomainParticipant, Subscriber, and DataReader and register MyType as normal. while (data_to_receive) { retcode = MyTypeDataReader_read/take(reader,&data_seq,&info_seq,.....); if (retcode == DDS_RETCODE_OK) { ... process data ... MyTypeDataReader_return_loan(reader,&data_seq,....); } } \endverbatim Subscribing to Data, Copy \verbatim MyType *sample; MyTypeDataReader *reader; MyTypeSeq data_seq = = DDS_SEQUENCE_INITIALIZER; DDS_SampleInfoSeq info_seg = DDS_SEQUENCE_INITIALIZER; DDS_ReturnCode_t retcode; Create a DDS DomainParticipant, Subscriber, and DataReader and register MyType as normal. sample = MyTypeDataReader_create_data(reader, DDS_BOOLEAN_TRUE); if (sample == NULL) { ... error ... } while (data_to_receive) { retcode = MyTypeDataReader_read/take_next(reader,sample); if (retcode == DDS_RETCODE_OK) { ... process ... } } \endverbatim */ /*i \ingroup METPModuleAPI */ extern METPDllExport DDS_ReturnCode_t DDS_DataWriter_create_metp_data( DDS_DataWriter *self, void **sample, DDS_Boolean *initialize_data); /*i \ingroup METPModuleAPI */ extern METPDllExport DDS_ReturnCode_t DDS_DataWriter_delete_metp_data(DDS_DataWriter *self, void *sample); /*i \ingroup METPModuleAPI */ extern METPDllExport DDS_Boolean DDS_DataWriter_is_metp_writer(DDS_DataWriter *self); /*i \ingroup METPModuleAPI */ extern METPDllExport DDS_ReturnCode_t DDS_DataWriter_write_metp_data( DDS_DataWriter *self, const void *instance_data, const DDS_InstanceHandle_t *handle, struct DDS_WriteParams_t *w_params); /*i \ingroup METPModuleAPI */ extern METPDllExport DDS_ReturnCode_t DDS_DataReader_is_metp_data_consistent( DDS_DataReader *self, DDS_Boolean *is_consistent, const void *sample, const struct DDS_SampleInfo *sample_info); #ifdef __cplusplus } /* extern "C" */ #endif #endif /* metp_dds_h */