.. highlight:: c Threading Model =============== Introduction ------------ This section describes the threading model, the use of critical sections, and how to configure thread parameters in |rti_me|. Please note that the information contained in this document applies to application development using |me|. For information regarding *porting* the |me| thread API to a new OS, please refer to :ref:`porting`. This section includes: - :ref:`thread_arch` - :ref:`thread_model` - :ref:`thread_cs` .. _thread_arch: Architectural Overview ---------------------- |rti_me| consists of a core library and a number of components. The core library provides a porting layer, frequently used data-structures and abstractions, and the DDS API. Components provide additional functionality such as UDP communication, DDS discovery plugins, DDS history caches, etc. .. code-block:: none +-------+ \ | DDS_C | } C API +-------+ / +-------+ +-------+ +------+ +------+ \ | DPSE | | DPDE | | WHSM | | RHSM | | +-------+ +-------+ +------+ +------+ | +-------+ +-------+ +------+ +------+ +-----+ } Optional components | LOOP | | UDP(*)| | RTPS | | DRI | | DWI | | (platform independent) +-------+ +-------+ +------+ +------+ +-----+ | / +-------+ +-------+ +------+ +------+ \ Core Services (always | REDA | | CDR | | DB | | RT | } present, platform +-------+ +-------+ +------+ +------+ / independent) +-----------------------------------+ \ | OSAPI | } Platform dependent module +-----------------------------------+ / (*) The UDP transport relies on a BSD socket API .. _thread_model: Threading Model --------------- |rti_me| is architected in a way that makes it possible to create a port of |me| that uses no threads, for example on platforms with no operating system or where the OS itself creates the threads which calls into |me|. Thus, the following discussion is only applicable to |me| libraries from RTI that runs on operating systems where |me| is allowed to create threads. The platform specific notes provide additional information regarding integration with specific environments. OSAPI Threads ............. The |me| OSAPI layer creates one thread per OS process. This thread manages all the |me| timers, such as deadline and liveliness timers. This thread is created by the |me| OSAPI System when the `OSAPI_System_initialize()`_ function is called. When the |me| DDS API is used `DomainParticipantFactory_get_instance()`_ calls this function once. Configuring OSAPI Threads ''''''''''''''''''''''''' The timer thread is configured through the OSAPI_SystemProperty_ structure and any changes must be made before `OSAPI_System_initialize()`_ is called. In |me|, `DomainParticipantFactory_get_instance()`_ calls `OSAPI_System_initialize()`_. Thus, if it is necessary to change the system timer thread settings, it must be done before `DomainParticipantFactory_get_instance()`_ is called the first time. Please refer to OSAPI_Thread_ for supported thread options. Note that not all options are supported by all platforms. .. code-block:: c struct OSAPI_SystemProperty sys_property = OSAPI_SystemProperty_INITIALIZER; if (!OSAPI_System_get_property(&sys_property)) { /* ERROR */ } /* Please refer to OSAPI_ThreadOptions for possible options */ sys.property.timer_property.thread.options = ....; /* The stack-size is platform dependent, it is passed directly to the OS */ sys.property.timer_property.thread.stack_size = .... /* The priority is platform dependent, it is passed directly to the OS */ sys.property.timer_property.thread.priority = .... if (!OSAPI_System_set_property(&sys_property)) { /* ERROR */ } .. _thread_cs: UDP Transport Threads ..................... Of the components that RTI provides, only the UDP component creates threads. The UDP transport creates one receive thread for each unique UDP receive resource. Thus, two UDP threads are created by default: - A unicast receive thread for discovery data - A unicast receive thread for user-data Additional threads may be created depending on the transport configuration for a *DomainParticipant*, *DataReader* and *DataWriter*. The UDP transport creates threads based on the following criteria: - Each unique unicast port creates a new thread - Each unique multicast address *and* port creates a new thread For example, if a *DataReader* specifies its own multicast receive address a new receive thread will be created. Configuring UDP Receive Threads ''''''''''''''''''''''''''''''' All threads in the UDP transport share the same thread settings. It is important to note that all the UDP properties must be set before the UDP transport is registered. |me| pre-registers the UDP transport with default settings when the DomainParticipantFactory_ is initialized. To change the UDP thread settings, use the following code. .. code-block:: c RT_Registry_T *registry = NULL; DDS_DomainParticipantFactory *factory = NULL; struct UDP_InterfaceFactoryProperty *udp_property = NULL; factory = DDS_DomainParticipantFactory_get_instance(); udp_property = (struct UDP_InterfaceFactoryProperty *) malloc(sizeof(struct UDP_InterfaceFactoryProperty)); *udp_property = UDP_INTERFACE_FACTORY_PROPERTY_DEFAULT; registry = DDS_DomainParticipantFactory_get_registry(factory); if (!RT_Registry_unregister(registry, "_udp", NULL, NULL)) { /* ERROR */ } /* Please refer to OSAPI_ThreadOptions for possible options */ udp_property->recv_thread.options = ...; /* The stack-size is platform dependent, it is passed directly to the OS */ udp_property->recv_thread.stack_size = .... /* The priority is platform dependent, it is passed directly to the OS */ udp_property->recv_thread.priority = .... if (!RT_Registry_register(registry, "_udp", UDP_InterfaceFactory_get_interface(), (struct RT_ComponentFactoryProperty*)udp_property, NULL)) { /* ERROR */ } General Thread Configuration ............................ The |me| architecture consists of a number of components and layers, and each layer and component has its own properties. It is important to remember that the layers and components are configured independently of each other, as opposed to configuring everything through DDS. This design makes it possible to relatively easily swap out one part of the library for another. All threads created based on |me| OSAPI APIs use the same OSAPI_ThreadProperty_ structure. Thread-Safety ------------- All public APIs have a note about thread-safety included in the API reference manuals. It is important that an application does not violate thread-safety guidelines. |rti_me| may create multiple threads, but from an application point of view, there is only a single critical section protecting all DDS resources within a *DomainParticipant*. .. note:: Although |me| may create multiple mutexes, these are used to protect resources in the OSAPI layer, and are thus not relevant when using the public DDS APIs. Calling DDS APIs from listeners and callbacks ............................................. When DDS is executing in a listener, it holds a critical section. Thus it is important to return as quickly as possible to avoid stalling network I/O. There are no deadlock scenarios when a listener calls |me| DDS APIs from the **same** *DomainParticipant* (and contained entities) that the listener was called from, as shown in the diagram below: .. image:: ../images/ThreadSafety-OK.png :align: center :width: 400 .. warning:: It is **not** safe to call DDS APIs from a **different** *DomainParticipant* than the one listener was called from, as shown in the diagram below. This may result in a deadlock situation. .. image:: ../images/ThreadSafety-NOK.png :align: center :width: 800 .. warning:: There are no checks on whether or not an API call will cause problems, such as deleting a participant when processing data in on_data_available_ from a reader within the same participant. Calling DDS APIs from a type-plugin ................................... A user type-plugin that is registered with the *DomainParticipant* is subject to the following rules: - The key kind is constant. - The plugin is constant for a given DDS entity (*Topic*, *DataWriter*, or *DataReader*). - The plugin data must be protected if thread safety is a concern, as it is user data and not protected by |me|. .. note:: A type-plugin generated from an IDL file with the *rtiddsgen* IDL compiler included with |me| will satisfy these rules.