.. _section-zero-copy-v2-transport: Zero Copy v2 Transport ====================== The Zero Copy v2 transport enables |rti_me| to transmit data samples without copying them internally. For an overview of this feature, 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 the `Zero Copy HelloWorld `_ example on GitHub. Generate type support files --------------------------- First, identify types that require Zero Copy transfer and annotate them with the ``@transfer_mode(SHMEM_REF)`` annotation. See the HelloWorld.idl file from the `Zero Copy HelloWorld `_ example: .. 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 type support files with the following command: .. code-block:: console rtiddsgen -micro -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 of these operations 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: .. 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 send notifications from *DataWriters* to *DataReaders*. You can use the reference notification mechanism provided by RTI, or implement your own. 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 reference 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. .. note:: For reference, RTI provides a posix implementation of the notification mechanism. This mechanism is based on a monitor implemented in shared memory. This documentation assumes that you are using the reference implementation. When using the notification mechanism provided by RTI, the :link_connextmicro_dds_api_c_up_two:`user_property ` mentioned above is resolved to ``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 ``ZCOPY_NotifMechanism_register()`` (a utility function on the reference 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("ERROR: Failed to set transports.enabled_transports maximum\n"); goto done; } if (!DDS_StringSeq_set_length(&dp_qos.transports.enabled_transports, 2)) { printf("ERROR: Failed to set transports.enabled_transports length\n"); goto done; } /* UDP and Notification are enabled*/ *DDS_StringSeq_get_reference(&dp_qos.transports.enabled_transports, 0) = DDS_String_dup("notif"); *DDS_StringSeq_get_reference(&dp_qos.transports.enabled_transports, 1) = DDS_String_dup(MY_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 Notification Transport */ 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 UDP transport must be registered while using Zero Copy v2 transfer because it is used during DDS Discovery (see :ref:`section-discovery` for more details). Manage DDS samples ------------------ 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 introduces 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 reference implementation provided by RTI. .. [2] Resolves to ``ZCOPY_NotifMechanismProperty`` when using the reference notification mechanism. The following properties are required if you are using the reference 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. .. list-table:: Default User-Defined Properties for Zero Copy v2 :widths: 30 30 40 :header-rows: 1 * - Property Name - Description - Default * - ``user_property.intf_addr`` - Sets the interface address for this instance. Should be unique for all instances in the system. - 0 * - ``user_property.thread_prop.stack_size`` - Sets the stack size for the receive thread created for receiving notifications. - ``OSAPI_THREAD_PROPERTY_DEFAULT`` * - ``user_property.thread_prop.priority`` - Sets the priority for the receive thread created for receiving notifications. - ``OSAPI_THREAD_PROPERTY_DEFAULT`` * - ``user_property.thread_prop.options`` - Sets any thread options supported by the thread API implementation. - ``OSAPI_THREAD_PROPERTY_DEFAULT`` * - ``user_property.max_receive_ports`` - Sets the maximum number of receive ports that can be opened. Used as part of the ``user_intf.bind`` function. - 2 * - ``user_property.max_routes`` - Sets the maximum number of outgoing routes. Used as part of the ``user_intf.add_route`` function. - 32 The following additional properties are only required if you are using your own notification mechanism for Zero Copy v2, NOT the reference implementation. Refer to the Safety Integration Manual for more information on these properties. .. list-table:: User Interface Properties for Zero Copy v2 :widths: 20 20 60 :header-rows: 1 * - Property Name - Type - Description * - ``user_intf.create_instance`` - NETIO_Interface_T - Creates an instance of the notification mechanism interface with upstream as the owner. * - ``user_intf.delete_instance`` - NETIO_Interface_T - Deletes an instance of the notification mechanism. * - ``user_intf.get_route_table`` - RTI_BOOL - Instructs the notification mechanism interface ``netio_intf`` to return a sequence of address and netmask pairs this interface can send to. * - ``user_intf.reserve_address`` - RTI_BOOL - Instructs the notification mechanism interface specified by ``user_intf`` to setup resources for listening to messages on the address ``src_addr`` and return a ``port_entry_out``. The ``port_entry_out`` will be provided to a ``bind`` call. * - ``user_intf.release_address`` - RTI_BOOL - Instructs the notification mechanism interface specified by ``user_intf`` to release resources for listening to messages on the address ``src_addr``. * - ``user_intf.resolve_address`` - RTI_BOOL - Instructs the notification mechanism interface specified by ``user_intf`` to determine if the address string ``address_string`` is a valid address and return the result in ``address_value``. * - ``user_intf.add_route`` - RTI_BOOL - Instructs the notification mechanism interface specified by ``user_intf`` to add a route from the source to the destination. The ``route_entry_out`` will be provided to you later when calling ``send``. * - ``user_intf.delete_route`` - RTI_BOOL - Instructs the notification mechanism interface specified by ``user_intf`` to remove a route from the source to the destination. * - ``user_intf.bind`` - RTI_BOOL - Instructs the notification mechanism interface specified by ``user_intf`` to start listening for messages on the address specified by ``src_addr`` on the given ``port_entry``. * - ``user_intf.unbind`` - RTI_BOOL - Instructs the notification mechanism interface specified by ``user_intf`` to stop listening for messages on the address specified by ``src_addr``. * - ``user_intf.send`` - RTI_BOOL - Instructs the notification mechanism interface specified by ``user_intf`` to send a notification to the destination using the ``route_entry``. * - ``user_intf.notify_recv_port`` - RTI_BOOL - Instructs the notification mechanism interface specified by ``user_intf`` to notify the receive port specified by ``port_entry``. .. _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 track different interface addresses. 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 `.