.. _section-zero-copy-v2-transport: Zero Copy v2 Transport ====================== The Zero Copy v2 transport enables |rti_me| to share data samples between publishers and subscribers without serializing, transmitting, or deserializing the samples. For an overview of this feature and its utility, see :ref:`section-zero-copy-transfer`. This section outlines the basic steps required to enable the Zero Copy v2 transport in an application. All the example code shown below is taken from a Zero Copy v2 application that you can generate using *rtiddsgen* (see :ref:`example_generation` for more details). Generate example and type support files --------------------------------------- First, identify types that require Zero Copy transfer and annotate them with the ``@transfer_mode(SHMEM_REF)`` annotation. See the example IDL file below: .. code-block:: idl @transfer_mode(SHMEM_REF) struct HelloWorld { @key long id; long data[100]; }; *rtiddsgen* generates additional TypePlugin code when a type is annotated with ``@transfer_mode(SHMEM_REF)`` in the IDL files. This code allows a *DataWriter* and a *DataReader* to communicate using a reference to the sample in shared memory. .. note:: Zero Copy v2 for |me| only supports contiguous data types; this means that fixed-size arrays are supported, but sequences and strings are not. Next, generate the type support and example files with the following command: .. code-block:: console rtiddsgen -micro -example -exampleTemplate zcv2 -language C HelloWorld.idl The generated files will appear in the same directory as the type file. .. _section-initialize-zc-v2-transport: Initialize the Zero Copy v2 transport ------------------------------------- Before the Zero Copy v2 transport can be used, it must be initialized. This must be done before creating a *DomainParticipant* and after registering the *DataReader* and *DataWriter* history plugins. The order is important because the Zero Copy v2 transport will perform actions on the history components during initialization. The following example code from **HelloWorldApplication.c** demonstrates how to initialize the Zero Copy v2 transport with :link_connextmicro_dds_api_c_up_two:`NDDS_Transport_ZeroCopy_initialize() `: .. code-block:: c if (!NDDS_Transport_ZeroCopy_initialize(registry, NULL, NULL)) { printf("failed to initialize zero copy\n"); /* handle error */ } .. _section-register-zc-v2-transport: Register the Zero Copy v2 transport ----------------------------------- The Zero Copy v2 transport needs a notification mechanism to notify *DataReaders* when a *DataWriter* has published data samples. RTI provies a default notification mechanism based on POSIX. You can also implement your own custom notification mechanism, but doing so is beyond the scope of this documentation; for more information, contact support@rti.com. The default provided by RTI is a POSIX implementation of the notification mechanism. This mechanism is based on a monitor implemented in shared memory. In this documentation, we will assume that you are using the default implementation unless otherwise noted. Once the Zero Copy transport is initialized, configure the ``notif`` interface factory with the :link_connextmicro_dds_api_c_up_two:`ZCOPY_NotifInterfaceFactoryProperty ` property. This property has three fields you need to set: * :link_connextmicro_dds_api_c_up_two:`max_samples_per_notif `: The number of samples processesed per notification. By default this value is 1. Note that a high value may starve other threads from progressing. * :link_connextmicro_dds_api_c_up_two:`user_intf `: This is the implementation of your chosen notification mechanism. It is populated automatically if you are using the default implementation. * :link_connextmicro_dds_api_c_up_two:`user_property `: Any properties associated with your chosen notification mechanism. |me| treats this as an opaque pointer. When using the default mechanism provided by RTI, the :link_connextmicro_dds_api_c_up_two:`user_property ` mentioned above is resolved to :link_connextmicro_dds_api_c_up_two:`ZCOPY_NotifMechanismProperty `. Both of these properties are required to configure the transport. See the following example from **HelloWorldApplication.c**: .. code-block:: c struct ZCOPY_NotifInterfaceFactoryProperty notif_prop; struct ZCOPY_NotifMechanismProperty notif_mech_prop; notif_mech_prop.intf_addr = 0; notif_prop.user_property = ¬if_mech_prop; notif_prop.max_samples_per_notif = 1; For more information on these properties, see the :ref:`section-zc_config` section. Finally, call :link_connextmicro_dds_api_c_up_two:`ZCOPY_NotifMechanism_register() ` (a utility function on the default notification mechanism) to register the Zero Copy v2 transport with the default notification mechanism. This makes it available for use. The following example registers a ``notif`` with the name ``NETIO_DEFAULT_NOTIF_NAME``: .. code-block:: c if (!ZCOPY_NotifMechanism_register(registry, NETIO_DEFAULT_NOTIF_NAME, ¬if_prop)) { printf("failed to register notif\n"); goto done; } Enable transports ----------------- With the specific notification mechanism registered, you can enable the Zero Copy and UDP transports for the *DomainParticipant*. Consider the following example code: .. code-block:: c if (!DDS_StringSeq_set_maximum(&dp_qos.transports.enabled_transports, 2)) { printf("failed to set transports.enabled_transports maximum\n"); goto done; } if (!DDS_StringSeq_set_length(&dp_qos.transports.enabled_transports, 2)) { printf("failed to set transports.enabled_transports length\n"); goto done; } /* UDP and Notif are enabled*/ *DDS_StringSeq_get_reference(&dp_qos.transports.enabled_transports, 0) = DDS_String_dup(NETIO_DEFAULT_NOTIF_NAME); *DDS_StringSeq_get_reference(&dp_qos.transports.enabled_transports, 1) = DDS_String_dup(NETIO_DEFAULT_UDP_NAME); /* Discovery takes place over UDP */ DDS_StringSeq_set_maximum(&dp_qos.discovery.enabled_transports, 1); DDS_StringSeq_set_length(&dp_qos.discovery.enabled_transports, 1); *DDS_StringSeq_get_reference(&dp_qos.discovery.enabled_transports, 0) = DDS_String_dup("_udp://"); /* User data uses Notif */ DDS_StringSeq_set_maximum(&dp_qos.user_traffic.enabled_transports, 1); DDS_StringSeq_set_length(&dp_qos.user_traffic.enabled_transports, 1); *DDS_StringSeq_get_reference(&dp_qos.user_traffic.enabled_transports, 0) = DDS_String_dup("notif://"); .. note:: The :ref:`section-udp-transport` or :ref:`section-shared-memory-transport` must be registered while using Zero Copy v2 transfer because DDS Discovery requires one of them in order to function (see :ref:`section-discovery` for more details). Sample management ----------------- When using the Zero Copy v2 transport, each *DataWriter* manages a pool of samples, and the application must obtain samples from this pool using :link_connextmicro_dds_api_c_up_two:`get_loan() `. We can see this in the following example: .. code-block:: c hw_datawriter = HelloWorldDataWriter_narrow(datawriter); retcode = HelloWorldDataWriter_get_loan(hw_datawriter, &sample); if (retcode != DDS_RETCODE_OK) { printf("ERROR: Failed to loan sample\n"); } retcode = HelloWorldDataWriter_write(hw_datawriter, sample, &DDS_HANDLE_NIL); if (retcode != DDS_RETCODE_OK) { printf("ERROR: Failed to write to sample\n"); } As seen above, the *DataWriter* **must** get a loan before each write call; it cannot write a loaned sample multiple times. The *DataWriter* does not need to explicitly return any loan to the pool, since this is managed by the middleware. However, if a loaned sample will not be written, it can be discarded with :link_connextmicro_dds_api_c_up_two:`discard_loan() `. .. warning:: It is not possible to write a sample that has not been obtained with :link_connextmicro_dds_api_c_up_two:`get_loan() `. .. note:: A Zero Copy-enabled *DataWriter* can also send samples using other transports (such as UDPv4) to non-Zero Copy *DataReaders*. When a *DataWriter* uses both the Zero Copy v2 transport and a transport which uses serialized data (such as UDP), the same sample is sent over all transports. This may adversely affect performance, since the sample must be serialized for network transmission even if it is in shared memory. For best performance, you should consider an architecture where a *DataWriter* matches either with Zero Copy-enabled or non Zero Copy-enabled *DataReaders*, but not both. On the *DataReader* side, Zero Copy v2 application code is identical to subscribing applications not using Zero Copy. When a *DataReader* calls :link_connextmicro_dds_api_c_up_two:`read() ` or :link_connextmicro_dds_api_c_up_two:`take() ` and receives samples, it is being given samples that are loaned from the *DataWriter*'s pool. Thus, failing to return the loan when the sample is no longer needed will deplete the available samples in the pool, eventually causing calls to :link_connextmicro_dds_api_c_up_two:`get_loan() ` to fail. .. _section-zc_config: Configuration ------------- |me| Zero Copy v2 includes some properties unique to its functionality. The following properties are always required: - :link_connextmicro_dds_api_c_up_two:`max_samples_per_notif ` - :link_connextmicro_dds_api_c_up_two:`user_intf ` [1]_ - :link_connextmicro_dds_api_c_up_two:`user_property ` [2]_ .. [1] This property is only required if you choose to implement your own notification mechanism and not use the default implementation provided by RTI. .. [2] Resolves to :link_connextmicro_dds_api_c_up_two:`ZCOPY_NotifMechanismProperty ` when using the default notification mechanism. The following properties are required if you are using the default implementation of the notification mechanism for Zero Copy v2. These are essentially a default set of user-defined properties; if you are using your own notification mechanism, you can set your own user-defined properties as needed. - :link_connextmicro_dds_api_c_up_two:`user_property.intf_addr ` - :link_connextmicro_dds_api_c_up_two:`user_property.thread_prop ` - :link_connextmicro_dds_api_c_up_two:`user_property.thread_prop.stack_size ` - :link_connextmicro_dds_api_c_up_two:`user_property.thread_prop.priority ` - :link_connextmicro_dds_api_c_up_two:`user_property.thread_prop.options ` - :link_connextmicro_dds_api_c_up_two:`user_property.max_receive_ports ` - :link_connextmicro_dds_api_c_up_two:`user_property.max_routes ` The following additional properties are only required if you are using your own notification mechanism for Zero Copy v2, not the default implementation. - :link_connextmicro_dds_api_c_up_two:`user_intf.create_instance ` - :link_connextmicro_dds_api_c_up_two:`user_intf.delete_instance ` - :link_connextmicro_dds_api_c_up_two:`user_intf.get_route_table ` - :link_connextmicro_dds_api_c_up_two:`user_intf.reserve_address ` - :link_connextmicro_dds_api_c_up_two:`user_intf.release_address ` - :link_connextmicro_dds_api_c_up_two:`user_intf.resolve_address ` - :link_connextmicro_dds_api_c_up_two:`user_intf.add_route ` - :link_connextmicro_dds_api_c_up_two:`user_intf.delete_route ` - :link_connextmicro_dds_api_c_up_two:`user_intf.bind ` - :link_connextmicro_dds_api_c_up_two:`user_intf.unbind ` - :link_connextmicro_dds_api_c_up_two:`user_intf.send ` - :link_connextmicro_dds_api_c_up_two:`user_intf.notify_recv_port ` - :link_connextmicro_dds_api_c_up_two:`user_intf.create_instance ` .. _section-mult-zero-copy: Using multiple Zero Copy v2 transport instances ----------------------------------------------- The platform-independent Zero Copy v2 transport supports multiple instances, provided that the user-defined, platform-specific implementation of the ``notif`` interface implements a way to uniquely identify each instance. In this case, each Zero Copy v2 transport instance should be registered with uniquely different names and properties. When multiple instances of the Zero Copy v2 transport exist, individual *DataReaders* and *DataWriters* can be configured to use a specific instance of the Zero Copy v2 transport. This configuration is done in the entity's :link_connextmicro_dds_api_c_up_two:`enabled_transports ` QoS configuration. For more information, see :ref:`Transport Registration `.