-
-
-.. This work is licensed under a Creative Commons Attribution 4.0 International License.
-.. SPDX-License-Identifier: CC-BY-4.0
-.. CAUTION: this document is generated from source in doc/src/rtd.
-.. To make changes edit the source and recompile the document.
-.. Do NOT make changes directly to .rst or .md files.
-
-
-============================================================================================
-RIC Message Router -- RMR
-============================================================================================
---------------------------------------------------------------------------------------------
-User's Manual
---------------------------------------------------------------------------------------------
-
-Overview
-============================================================================================
-
-The RIC Message Router (RMR) is a library for peer-to-peer communication.
-Applications use the library to send and receive messages where the message
-routing and endpoint selection is based on the message type rather than DNS host
-name-IP port combinations. The library provides the following major features:
-
-
-+ Routing and endpoint selection is based on *message type.*
-
-+ Application is insulated from the underlying transport mechanism and/or protocols.
-
-+ Message distribution (round robin or fanout) is selectable by message type.
-
-+ Route management updates are received and processed asynchronously and without overt application involvement.
-
-
-
-Purpose
---------------------------------------------------------------------------------------------
-
-RMR's main purpose is to provide an application with the
-ability to send and receive messages to/from other peer
-applications with minimal effort on the application's part.
-To achieve this, RMR manages all endpoint information,
-connections, and routing information necessary to establish
-and maintain communication. From the application's point of
-view, all that is required to send a message is to allocate
-(via RMR) a message buffer, add the payload data, and set the
-message type. To receive a message, the application needs
-only to invoke the receive function; when a message arrives a
-message buffer will be returned as the function result.
-
-Message Routing
---------------------------------------------------------------------------------------------
-
-Applications are required to place a message type into a
-message before sending, and may optionally add a subscription
-ID when appropriate. The combination of message type, and
-subscription ID are refered to as the *message key,* and is
-used to match an entry in a routing table which provides the
-possible endpoints expecting to receive messages with the
-matching key.
-
-Round Robin Delivery
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-An endpoint from RMR's perspective is an application to which
-RMR may establish a connection, and expect to send messages
-with one or more defined message keys. Each entry in the
-route table consists of one or more endpoint groups, called
-round robin groups. When a message matches a specific entry,
-the entry's groups are used to select the destination of the
-message. A message is sent once to each group, with messages
-being *balanced* across the endpoints of a group via round
-robin selection. Care should be taken when defining multiple
-groups for a message type as there is extra overhead required
-and thus the overall message latency is somewhat increased.
-
-Routing Table Updates
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Route table information is made available to RMR a static
-file (loaded once), or by updates sent from a separate route
-manager application. If a static table is provided, it is
-loaded during RMR initialization and will remain in use until
-an external process connects and delivers a route table
-update (often referred to as a dynamic update). Dynamic
-updates are listened for in a separate process thread and
-applied automatically; the application does not need to allow
-for, or trigger, updates.
-
-Latency And Throughput
---------------------------------------------------------------------------------------------
-
-While providing insulation from the underlying message
-transport mechanics, RMR must also do so in such a manner
-that message latency and throughput are not impacted. In
-general, the RMR induced overhead, incurred due to the
-process of selecting an endpoint for each message, is minimal
-and should not impact the overall latency or throughput of
-the application. This impact has been measured with test
-applications running on the same physical host and the
-average latency through RMR for a message was on the order of
-0.02 milliseconds.
-
-As an application's throughput increases, it becomes easy for
-the application to overrun the underlying transport mechanism
-(e.g. NNG), consume all available TCP transmit buffers, or
-otherwise find itself in a situation where a send might not
-immediately complete. RMR offers different *modes* which
-allow the application to manage these states based on the
-overall needs of the application. These modes are discussed
-in the *Configuration* section of this document.
-
-General Use
-============================================================================================
-
-To use, the RMR based application simply needs to initialise
-the RMR environment, wait for RMR to have received a routing
-table (become ready), and then invoke either the send or
-receive functions. These steps, and some behind the scenes
-details, are described in the following paragraphs.
-
-Initialisation
---------------------------------------------------------------------------------------------
-
-The RMR function is used to set up the RMR environment and
-must be called before messages can be sent or received. One
-of the few parameters that the application must communicate
-to RMR is the port number that will be used as the listen
-port for new connections. The port number is passed on the
-initialisation function call and a TCP listen socket will be
-opened with this port. If the port is already in use RMR will
-report a failure; the application will need to reinitialise
-with a different port number, abort, or take some other
-action appropriate for the application.
-
-In addition to creating a TCP listen port, RMR will start a
-process thread which will be responsible for receiving
-dynamic updates to the route table. This thread also causes a
-TCP listen port to be opened as it is expected that the
-process which generates route table updates will connect and
-send new information when needed. The route table update port
-is **not** supplied by the application, but is supplied via
-an environment variable as this value is likely determined by
-the mechanism which is starting and configuring the
-application.
-
-The RMR Context
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-On successful initialisation, a void pointer, often called a
-*handle* by some programming languages, is returned to the
-application. This is a reference to the RMR control
-information and must be passed as the first parameter on most
-RMR function calls. RMR refers to this as the context, or
-ctx.
-
-Wait For Ready
---------------------------------------------------------------------------------------------
-
-An application which is only receiving messages does not need
-to wait for RMR to *become ready* after the call to the
-initialization function. However, before the application can
-successfully send a message, RMR must have loaded a route
-table, and the application must wait for RMR to report that
-it has done so. The RMR function will return the value *true*
-(1) when a complete route table has been loaded and can be
-used to determine the endpoint for a send request.
-
-Receiving Messages
---------------------------------------------------------------------------------------------
-
-The process of receiving is fairly straight forward. The
-application invokes the RMR function which will block until a
-message is received. The function returns a pointer to a
-message block which provides all of the details about the
-message. Specifically, the application has access to the
-following information either directly or indirectly:
-
-
-+ The payload (actual data)
-
-+ The total payload length in bytes
-
-+ The number of bytes of the payload which contain valid data
-
-+ The message type and subscription ID values
-
-+ The hostname and IP address of the source of the message (the sender)
-
-+ The transaction ID
-
-+ Tracing data (if provided)
-
-
-
-The Message Payload
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The message payload contains the *raw* data that was sent by
-the peer application. The format will likely depend on the
-message type, and is expected to be known by the application.
-A direct pointer to the payload is available from the message
-buffer (see appendix B for specific message buffer details).
-
-Two payload-related length values are also directly
-available: the total payload length, and the number of bytes
-actually filled with data. The used length is set by the
-caller, and may or not be an accurate value. The total
-payload length is determined when the buffer is created for
-sending, and is the maximum number of bytes that the
-application may modify should the buffer be used to return a
-response.
-
-Message Type and Subscription ID
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The message type and subscription ID are both directly
-available from the message buffer, and are the values which
-were used to by RMR in the sending application to select the
-endpoint. If the application resends the message, as opposed
-to returning the message buffer as a response, the message
-number and/or the subscription ID might need to be changed to
-avoid potential issues[1].
-
-Sender Information
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The source, or sender information, is indirectly available to
-the application via the and functions. The former returns a
-string containing hostname:port, while the string ip:port is
-returned by the latter.
-
-Transaction ID
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The message buffer contains a fixed length set of bytes which
-applications can set to track related messages across the
-application concept of a transaction. RMR will use the
-transaction ID for matching a response message when the
-function is used to send a message.
-
-Trace Information
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-RMR supports the addition of an optional trace information to
-any message. The presence and size is controlled by the
-application, and can vary from message to message if desired.
-The actual contents of the trace information is determined by
-the application; RMR provides only the means to set, extract,
-and obtain a direct reference to the trace bytes. The trace
-data field in a message buffer is discussed in greater detail
-in the *Trace Data* section.
-
-Sending Messages
---------------------------------------------------------------------------------------------
-
-Sending requires only slightly more work on the part of the
-application than receiving a message. The application must
-allocate an RMR message buffer, populate the message payload
-with data, set the message type and length, and optionally
-set the subscription ID. Information such as the source IP
-address, hostname, and port are automatically added to the
-message buffer by RMR, so there is no need for the
-application to worry about these.
-
-Message Buffer Allocation
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The function allocates a *zero copy* buffer and returns a
-pointer to the RMR rmr_mbuf_t structure. The message buffer
-provides direct access to the payload, length, message type
-and subscription ID fields. The buffer must be preallocated
-in order to allow the underlying transport mechanism to
-allocate the payload space from its internal memory pool;
-this eliminates multiple copies as the message is sent, and
-thus is more efficient.
-
-If a message buffer has been received, and the application
-wishes to use the buffer to send a response, or to forward
-the buffer to another application, a new buffer does **not**
-need to be allocated. The application may set the necessary
-information (message type, etc.), and adjust the payload, as
-is necessary and then pass the message buffer to or to be
-sent or returned to the sender.
-
-Populating the Message Buffer
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The application has direct access to several of the message
-buffer fields, and should set them appropriately.
-
-
-
-len
-
- This is the number of bytes that the application placed
- into the payload. Setting length to 0 is allowed, and
- length may be less than the allocated payload size.
-
-
-mtype
-
- The message type that RMR will use to determine the
- endpoint used as the target of the send.
-
-
-sub_id
-
- The subscription ID if the message is to be routed based
- on the combination of message type and subscription ID. If
- no subscription ID is valid for the message, the
- application should set the field with the RMR constant
- RMR_VOID_SUBID.
-
-
-payload
-
- The application should obtain the reference (pointer) to
- the payload from the message buffer and place any data
- into the payload. The application is responsible for
- ensuring that the maximum payload size is not exceeded.
- The application may obtain the maximum size via the
- function.
-
-
-trace data
-
- Optionally, the application may add trace information to
- the message buffer.
-
-
-
-Sending a Message Buffer
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Once the application has populated the necessary bits of a
-message, it may be sent by passing the buffer to the
-function. This function will select an endpoint to receive
-the message, based on message type and subscription ID, and
-will pass the message to the underlying transport mechanism
-for actual transmission on the connection. (Depending on the
-underlying transport mechanism, the actual connection to the
-endpoint may happen at the time of the first message sent to
-the endpoint, and thus the latency of the first send might be
-longer than expected.)
-
-On success, the send function will return a reference to a
-message buffer; the status within that message buffer will
-indicate what the message buffer contains. When the status is
-RMR_OK the reference is to a **new** message buffer for the
-application to use for the next send; the payload size is the
-same as the payload size allocated for the message that was
-just sent. This is a convenience as it eliminates the need
-for the application to call the message allocation function
-at some point in the future, and assumes the application will
-send many messages which will require the same payload
-dimensions.
-
-If the message contains any status other than RMR_OK, then
-the message could **not** be sent, and the reference is to
-the unsent message buffer. The value of the status will
-indicate whether the nature of the failure was transient (
-RMR_ERR_RETRY) or not. Transient failures are likely to be
-successful if the application attempts to send the message at
-a later time. Unfortunately, it is impossible for RMR to know
-the exact transient failure (e.g. connection being
-established, or TCP buffer shortage), and thus it is not
-possible to communicate how long the application should wait
-before attempting to resend, if the application wishes to
-resend the message. (More discussion with respect to message
-retries can be found in the *Handling Failures* section.)
-
-Advanced Usage
-============================================================================================
-
-Several forms of usage fall into a more advanced category and
-are described in the following sections. These include
-blocking call, return to sender and wormhole functions.
-
-The Call Function
---------------------------------------------------------------------------------------------
-
-The RMR function sends a message in the exact same manner as
-the rmr_send_msg() function, with the endpoint selection
-based on the message key. But unlike the send function, will
-block and wait for a response from the application that is
-selected to receive the message. The matching message is
-determined by the transaction ID which the application must
-place into the message buffer prior to invoking. Similarly,
-the responding application must ensure that the same
-transaction ID is placed into the message buffer before
-returning its response.
-
-The return from the call is a message buffer with the
-response message; there is no difference between a message
-buffer returned by the receive function and one returned by
-the function. If a response is not received in a reasonable
-amount of time, a nil message buffer is returned to the
-calling application.
-
-Returning a Response
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Because of the nature of RMR's routing policies, it is
-generally not possible for an application to control exactly
-which endpoint is sent a message. There are cases, such as
-responding to a message delivered via that the application
-must send a message and guarantee that RMR routes it to an
-exact destination. To enable this, RMR provides the return to
-sender, function. Upon receipt of any message, an application
-may alter the payload, and if necessary the message type and
-subscription ID, and pass the altered message buffer to the
-function to return the altered message to the application
-which sent it. When this function is used, RMR will examine
-the message buffer for the source information and use that to
-select the connection on which to write the response.
-
-Multi-threaded Calls
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The basic call mechanism described above is **not** thread
-safe, as it is not possible to guarantee that a response
-message is delivered to the correct thread. The RMR function
-accepts an additional parameter which identifies the calling
-thread in order to ensure that the response is delivered
-properly. In addition, the application must specifically
-initialise the multi-threaded call environment by passing the
-RMRFL_MTCALL flag as an option to the function[2].
-
-One advantage of the multi-threaded call capability in RMR is
-the fact that only the calling thread is blocked. Messages
-received which are not responses to the call are continued to
-be delivered via normal calls.
-
-While the process is blocked waiting for the response, it is
-entirely possible that asynchronous, non-matching, messages
-will arrive. When this happens, RMR will queues the messages
-and return them to the application over the next calls to
-
-Wormholes
---------------------------------------------------------------------------------------------
-
-As was mentioned earlier, the design of RMR is to eliminate
-the need for an application to know a specific endpoint, even
-when a response message is being sent. In some rare cases it
-may be necessary for an application to establish a direct
-connection to an RMR-based application rather than relying on
-message type and subscription ID based routing. The
-*wormhole* functions provide an application with the ability
-to create a direct connection and then to send and receive
-messages across the connection. The following are the RMR
-functions which provide wormhole communications:
-
-
-
-rmr_wh_open
-
- Open a connection to an endpoint. Name or IP address and
- port of the endpoint is supplied. Returns a wormhole ID
- that the application must use when sending a direct
- message.
-
-
-rmr_wh_send_msg
-
- Sends an RMR message buffer to the connected application.
- The message type and subscription ID may be set in the
- message, but RMR will ignore both.
-
-
-rmr_wh_close
-
- Closes the direct connection.
-
-
-
-Handling Failures
-============================================================================================
-
-The vast majority of states reported by RMR are fatal; if
-encountered during setup or initialization, then it is
-unlikely that any message oriented processing should
-continue, and when encountered on a message operation
-continued operation on that message should be abandoned.
-Specifically with regard to message sending, it is very
-likely that the underlying transport mechanism will report a
-*soft,* or transient, failure which might be successful if
-the operation is retried at a later point in time. The
-paragraphs below discuss the methods that an application
-might deal with these soft failures.
-
-Failure Notification
---------------------------------------------------------------------------------------------
-
-When a soft failure is reported, the returned message buffer
-returned by the RMR function will be RMR_ERR_RETRY. These
-types of failures can occur for various reasons; one of two
-reasons is typically the underlying cause:
-
-
-+ The session to the targeted recipient (endpoint) is not connected.
-
-+ The transport mechanism buffer pool is full and cannot accept another buffer.
-
-
-
-Unfortunately, it is not possible for RMR to determine which
-of these two cases is occurring, and equally as unfortunate
-the time to resolve each is different. The first, no
-connection, may require up to a second before a message can
-be accepted, while a rejection because of buffer shortage is
-likely to resolve in less than a millisecond.
-
-Application Response
---------------------------------------------------------------------------------------------
-
-The action which an application takes when a soft failure is
-reported ultimately depends on the nature of the application
-with respect to factors such as tolerance to extended message
-latency, dropped messages, and over all message rate.
-
-RMR Retry Modes
---------------------------------------------------------------------------------------------
-
-In an effort to reduce the workload of an application
-developer, RMR has a default retry policy such that RMR will
-attempt to retransmit a message up to 1000 times when a soft
-failure is reported. These retries generally take less than 1
-millisecond (if all 1000 are attempted) and in most cases
-eliminates nearly all reported soft failures to the
-application. When using this mode, it might allow the
-application to simply treat all bad return values from a send
-attempt as permanent failures.
-
-If an application is so sensitive to any delay in RMR, or the
-underlying transport mechanism, it is possible to set RMR to
-return a failure immediately on any kind of error (permanent
-failures are always reported without retry). In this mode,
-RMR will still set the state in the message buffer to
-RMR_ERR_RETRY, but will **not** make any attempts to resend
-the message. This zero-retry policy is enabled by invoking
-the with a value of 0; this can be done once immediately
-after is invoked.
-
-Regardless of the retry mode which the application sets, it
-will ultimately be up to the application to handle failures
-by queuing the message internally for resend, retrying
-immediately, or dropping the send attempt all together. As
-stated before, only the application can determine how to best
-handle send failures.
-
-Other Failures
---------------------------------------------------------------------------------------------
-
-RMR will return the state of processing for message based
-operations (send/receive) as the status in the message
-buffer. For non-message operations, state is returned to the
-caller as the integer return value for all functions which
-are not expected to return a pointer (e.g. and a brief
-description of their meaning.
-
-
-
-RMR_OK
-
- state is good; operation finished successfully
-
-
-RMR_ERR_BADARG
-
- argument passed to function was unusable
-
-
-RMR_ERR_NOENDPT
-
- send/call could not find an endpoint based on msg type
-
-
-RMR_ERR_EMPTY
-
- msg received had no payload; attempt to send an empty
- message
-
-
-RMR_ERR_NOHDR
-
- message didn't contain a valid header
-
-
-RMR_ERR_SENDFAILED
-
- send failed; errno may contain the transport provider
- reason
-
-
-RMR_ERR_CALLFAILED
-
- unable to send the message for a call function; errno may
- contain the transport provider reason
-
-
-RMR_ERR_NOWHOPEN
-
- no wormholes are open
-
-
-RMR_ERR_WHID
-
- the wormhole id provided was invalid
-
-
-RMR_ERR_OVERFLOW
-
- operation would have busted through a buffer/field size
-
-
-RMR_ERR_RETRY
-
- request (send/call/rts) failed, but caller should retry
- (EAGAIN for wrappers)
-
-
-RMR_ERR_RCVFAILED
-
- receive failed (hard error)
-
-
-RMR_ERR_TIMEOUT
-
- response message not received in a reasonable amount of
- time
-
-
-RMR_ERR_UNSET
-
- the message hasn't been populated with a transport buffer
-
-
-RMR_ERR_TRUNC
-
- length in the received buffer is longer than the size of
- the allocated payload, received message likely truncated
- (length set by sender could be wrong, but we can't know
- that)
-
-
-RMR_ERR_INITFAILED
-
- initialisation of something (probably message) failed
-
-
-RMR_ERR_NOTSUPP
-
- the request is not supported, or RMR was not initialised
- for the request
-
-
-Depending on the underlying transport mechanism, and the
-nature of the call that RMR attempted, the system errno value
-might reflect additional detail about the failure.
-Applications should **not** rely on errno as some transport
-mechanisms do not set it with any consistency.
-
-Configuration and Control
-============================================================================================
-
-With the assumption that most RMR based applications will be
-executed in a containerised environment, there are some
-underlying mechanics which the developer may need to know in
-order to properly provide a configuration specification to
-the container management system. The following paragraphs
-briefly discuss these.
-
-
-TCP Ports
---------------------------------------------------------------------------------------------
-
-RMR requires two (2) TCP listen ports: one for general
-application-to-application communications and one for
-route-table updates. The general communication port is
-specified by the application at the time RMR is initialised.
-The port used to listen for route table updates is likely to
-be a constant port shared by all applications provided they
-are running in separate containers. To that end, the port
-number defaults to 4561, but can be configured with an
-environment variable (see later paragraph in this section).
-
-Host Names
---------------------------------------------------------------------------------------------
-
-RMR is typically host name agnostic. Route table entries may
-contain endpoints defined either by host name or IP address.
-In the container world the concept of a *service name* might
-exist, and likely is different than a host name. RMR's only
-requirement with respect to host names is that a name used on
-a route table entry must be resolvable via the gethostbyname
-system call.
-
-Environment Variables
---------------------------------------------------------------------------------------------
-
-Several environment variables are recognised by RMR which, in
-general, are used to define interfaces and listen ports (e.g.
-the route table update listen port), or debugging
-information. Generally this information is system controlled
-and thus RMR expects this information to be defined in the
-environment rather than provided by the application. The
-following is a list of the environment variables which RMR
-recognises:
-
-
-
-RMR_BIND_IF
-
- The interface to bind to listen ports to. If not defined
- 0.0.0.0 (all interfaces) is assumed.
-
-
-RMR_RTG_SVC
-
- The port RMR will listen on for route manager connections.
- If not defined 4561 is used.
-
-
-RMR_SEED_RT
-
- Where RMR expects to find the name of the seed (static)
- route table. If not defined no static table is read.
-
-
-RMR_RTG_ISRAW
-
- If the value set to 0, RMR expects the route table manager
- messages to be messages with and RMR header. If this is
- not defined messages are assumed to be "raw" (without an
- RMR header.
-
-
-RMR_VCTL_FILE
-
- Provides a file which is used to set the verbose level of
- the route table collection thread. The first line of the
- file is read and expected to contain an integer value to
- set the verbose level. The value may be changed at any
- time and the route table thread will adjust accordingly.
-
-
-RMR_SRC_NAMEONLY
-
- If the value of this variable is greater than 0, RMR will
- not permit the IP address to be sent as the message
- source. Only the host name will be sent as the source in
- the message header.
-
-
-
-Logging
---------------------------------------------------------------------------------------------
-
-RMR does **not** use any logging libraries; any error or
-warning messages are written to standard error. RMR messages
-are written with one of three prefix strings:
-
-
-
-[CRI]
-
- The event is of a critical nature and it is unlikely that
- RMR will continue to operate correctly if at all. It is
- almost certain that immediate action will be needed to
- resolve the issue.
-
-
-[ERR]
-
- The event is not expected and RMR is not able to handle
- it. There is a small chance that continued operation will
- be negatively impacted. Eventual action to diagnose and
- correct the issue will be necessary.
-
-
-[WRN]
-
- The event was not expected by RMR, but can be worked
- round. Normal operation will continue, but it is
- recommended that the cause of the problem be investigated.
-
-
-
-_____________________________________________________________
-
-[1] It is entirely possible to design a routing table, and
-application group, such that the same message type is is
-left unchanged and the message is forwarded by an
-application after updating the payload. This type of
-behaviour is often referred to as service chaining, and can
-be done without any "knowledge" by an application with
-respect to where the message goes next. Service chaining is
-supported by RMR in as much as it allows the message to be
-resent, but the actual complexities of designing and
-implementing service chaining lie with the route table
-generator process.
-
-
-
-[2] There is additional overhead to support multi-threaded
-call as a special listener thread must be used in order to
-deliver responses to the proper application thread.
-
-
-
-
-
-
-Appendix A -- Quick Reference
-============================================================================================
-
-Please refer to the RMR manual pages on the Read the Docs
-site
-
-https://docs.o-ran-sc.org/projects/o-ran-sc-ric-plt-lib-rmr/en/latest/index.html
-
-
-Appendix B -- Message Buffer Details
-============================================================================================
-
-The RMR message buffer is a C structure which is exposed in
-the rmr.h header file. It is used to manage a message
-received from a peer endpoint, or a message that is being
-sent to a peer. Fields include payload length, amount of
-payload actually used, status, and a reference to the
-payload. There are also fields which the application should
-ignore, and could be hidden in the header file, but we chose
-not to. These fields include a reference to the RMR header
-information, and to the underlying transport mechanism
-message struct which may or may not be the same as the RMR
-header reference.
-
-The Structure
---------------------------------------------------------------------------------------------
-
-The following is the C structure. Readers are cautioned to
-examine the rmr.h header file directly; the information here
-may be out of date (old document in some cache), and thus it
-may be incorrect.
-
-
-::
-
- typedef struct {
- int state; // state of processing
- int mtype; // message type
- int len; // length of data in the payload (send or received)
- unsigned char* payload; // transported data
- unsigned char* xaction; // pointer to fixed length transaction id bytes
- int sub_id; // subscription id
- int tp_state; // transport state (errno)
- // these things are off limits to the user application
- void* tp_buf; // underlying transport allocated pointer (e.g. nng message)
- void* header; // internal message header (whole buffer: header+payload)
- unsigned char* id; // if we need an ID in the message separate from the xaction id
- int flags; // various MFL_ (private) flags as needed
- int alloc_len; // the length of the allocated space (hdr+payload)
- void* ring; // ring this buffer should be queued back to
- int rts_fd; // SI fd for return to sender
- int cookie; // cookie to detect user misuse of free'd msg
- } rmr_mbuf_t;
-
-
-
-
-State vs Transport State
---------------------------------------------------------------------------------------------
-
-The state field reflects the state at the time the message
-buffer is returned to the calling application. For a send
-operation, if the state is not RMR_OK then the message buffer
-references the payload that could not be sent, and when the
-state is RMR_OK the buffer references a *fresh* payload that
-the application may fill in.
-
-When the state is not RMR_OK, C programmes may examine the
-global errno value which RMR will have left set, if it was
-set, by the underlying transport mechanism. In some cases,
-wrapper modules are not able to directly access the C-library
-errno value, and to assist with possible transport error
-details, the send and receive operations populate tp_state
-with the value of errno.
-
-Regardless of whether the application makes use of the
-tp_state, or the errno value, it should be noted that the
-underlying transport mechanism may not actually update the
-errno value; in other words: it might not be accurate. In
-addition, RMR populates the tp_state value in the message
-buffer **only** when the state is not RMR_OK.
-
-Field References
---------------------------------------------------------------------------------------------
-
-The transaction field was exposed in the first version of
-RMR, and in hindsight this shouldn't have been done. Rather
-than break any existing code the reference was left, but
-additional fields such as trace data, were not directly
-exposed to the application. The application developer is
-strongly encouraged to use the functions which get and set
-the transaction ID rather than using the pointer directly;
-any data overruns will not be detected if the reference is
-used directly.
-
-In contrast, the payload reference should be used directly by
-the application in the interest of speed and ease of
-programming. The same care to prevent writing more bytes to
-the payload buffer than it can hold must be taken by the
-application. By the nature of the allocation of the payload
-in transport space, RMR is unable to add guard bytes and/or
-test for data overrun.
-
-Actual Transmission
---------------------------------------------------------------------------------------------
-
-When RMR sends the application's message, the message buffer
-is **not** transmitted. The transport buffer (tp_buf) which
-contains the RMR header and application payload is the only
-set of bytes which are transmitted. While it may seem to the
-caller like the function is returning a new message buffer,
-the same struct is reused and only a new transport buffer is
-allocated. The intent is to keep the alloc/free cycles to a
-minimum.
-
-
-Appendix C -- Glossary
-============================================================================================
-
-Many terms in networking can be interpreted with multiple
-meanings, and several terms used in this document are RMR
-specific. The following definitions are the meanings of terms
-used within this document and should help the reader to
-understand the intent of meaning.
-
-
-
-application
-
- A programme which uses RMR to send and/or receive messages
- to/from another RMR based application.
-
-
-Critical error
-
- An error that RMR has encountered which will prevent
- further successful processing by RMR. Critical errors
- usually indicate that the application should abort.
-
-
-Endpoint
-
- An RMR based application that is defined as being capable
- of receiving one or more types of messages (as defined by
- a *message key.*)
-
-
-Environment variable
-
- A key/value pair which is set externally to the
- application, but which is available to the application
- (and referenced libraries) through the getenv system call.
- Environment variables are the main method of communicating
- information such as port numbers to RMR.
-
-
-Error
-
- An abnormal condition that RMR has encountered, but will
- not affect the overall processing by RMR, but may impact
- certain aspects such as the ability to communicate with a
- specific endpoint. Errors generally indicate that
- something, usually external to RMR, must be addressed.
-
-
-Host name
-
- The name of the host as returned by the gethostbyname
- system call. In a containerised environment this might be
- the container or service name depending on how the
- container is started. From RMR's point of view, a host
- name can be used to resolve an *endpoint* definition in a
- *route* table.)
-
-
-IP
-
- Internet protocol. A low level transmission protocol which
- governs the transmission of datagrams across network
- boundaries.
-
-
-Listen socket
-
- A *TCP* socket used to await incoming connection requests.
- Listen sockets are defined by an interface and port number
- combination where the port number is unique for the
- interface.
-
-
-Message
-
- A series of bytes transmitted from the application to
- another RMR based application. A message is comprised of
- RMR specific data (a header), and application data (a
- payload).
-
-
-Message buffer
-
- A data structure used to describe a message which is to be
- sent or has been received. The message buffer includes the
- payload length, message type, message source, and other
- information.
-
-
-Messgae type
-
- A signed integer (0-32000) which identifies the type of
- message being transmitted, and is one of the two
- components of a *routing key.* See *Subscription ID.*
-
-
-Payload
-
- The portion of a message which holds the user data to be
- transmitted to the remote *endpoint.* The payload contents
- are completely application defined.
-
-
-RMR context
-
- A set of information which defines the current state of
- the underlying transport connections that RMR is managing.
- The application will be give a context reference (pointer)
- that is supplied to most RMR functions as the first
- parameter.
-
-
-Round robin
-
- The method of selecting an *endpoint* from a list such
- that all *endpoints* are selected before starting at the
- head of the list.
-
-
-Route table
-
- A series of "rules" which define the possible *endpoints*
- for each *message key.*
-
-
-Route table manager
-
- An application responsible for building a *route table*
- and then distributing it to all applicable RMR based
- applications.
-
-
-Routing
-
- The process of selecting an *endpoint* which will be the
- recipient of a message.
-
-
-Routing key
-
- A combination of *message type* and *subscription ID*
- which RMR uses to select the destination *endpoint* when
- sending a message.
-
-
-Source
-
- The sender of a message.
-
-
-Subscription ID
-
- A signed integer value (0-32000) which identifies the
- subscription characteristic of a message. It is used in
- conjunction with the *message type* to determine the
- *routing key.*
-
-
-Target
-
- The *endpoint* selected to receive a message.
-
-
-TCP
-
- Transmission Control Protocol. A connection based internet
- protocol which provides for lossless packet
- transportation, usually over IP.
-
-
-Thread
-
- Also called a *process thread, or pthread.* This is a
- lightweight process which executes in concurrently with
- the application and shares the same address space. RMR
- uses threads to manage asynchronous functions such as
- route table updates. &Term An optional portion of the
- message buffer that the application may populate with data
- that allows for tracing the progress of the transaction or
- application activity across components. RMR makes no use
- of this data.
-
-
-Transaction ID
-
- A fixed number of bytes in the *message* buffer) which the
- application may populate with information related to the
- transaction. RMR makes use of the transaction ID for
- matching response messages with the &c function is used to
- send a message.
-
-
-Transient failure
-
- An error state that is believed to be short lived and that
- the operation, if retried by the application, might be
- successful. C programmers will recognise this as EAGAIN.
-
-
-Warning
-
- A warning occurs when RMR has encountered something that
- it believes isn't correct, but has a defined work round.
-
-
-Wormhole
-
- A direct connection managed by RMR between the user
- application and a remote, RMR based, application.
-
-
-
-Appendix D -- Code Examples
-============================================================================================
-
-The following snippet of code illustrate some of the basic
-operation of the RMR library. Please refer to the examples
-and test directories in the RMR repository for complete RMR
-based programmes.
-
-Sender Sample
---------------------------------------------------------------------------------------------
-
-The following code segment shows how a message buffer can be
-allocated, populated, and sent. The snippet also illustrates
-how the result from the function is used to send the next
-message. It does not illustrate error and/or retry handling.
-
-
-::
-
- mrc = rmr_init( listen_port, MAX_BUF_SZ, RMRFL_NOFLAGS );
- rmr_set_stimeout( mrc, rmr_retries );
- while( ! rmr_ready( mrc ) ) {
- sleep( 1 );
- }
- sbuf = rmr_alloc_msg( mrc, 256 ); // 1st send buffer
- while( TRUE ) {
- sbuf->len = gen_status( (status_msg *) sbuf->payload );
- sbuf->mtype = STATUS_MSG;
- sbuf->sub_id = RMR_VOID_SUBID; // subscription not used
- sbuf = rmr_send_msg( mrc, sbuf );
- sleep( delay_sec );
- }
- rmr_close( mrc );
-
-
-
-Receiver Sample
---------------------------------------------------------------------------------------------
-
-The receiver code is even simpler than the sender code as it
-does not need to wait for a route table to arrive (only
-senders need to do that), nor does it need to allocate an
-initial buffer. The example assumes that the sender is
-transmitting a zero terminated string as the payload.
-
-
-::
-
- rmr_mbuf_t* rbuf = NULL;
- void* mrc = rmr_init( listen_port, MAX_BUF_SZ, RMRFL_NOFLAGS );
- while( TRUE ) {
- rbuf = rmr_rcv_msg( mrc, rbuf ); // reuse buffer on all but first loop
- if( rbuf == NULL || rbuf->state != RMR_OK ) {
- break;
- }
- fprintf( stdout, "mtype=%d sid=%d pay=%s\\n",
- rbuf->mtype, rbuf->sub_id, rbuf->payload );
- sleep( delay_sec );
- }
- fprintf( stderr, "receive error\\n" );
- rmr_close( mrc );
-
-
-
-Receive and Send Sample
---------------------------------------------------------------------------------------------
-
-The following code snippet receives messages and responds to
-the sender if the message type is odd. The code illustrates
-how the received message may be used to return a message to
-the source. Variable type definitions are omitted for clarity
-and should be obvious.
-
-It should also be noted that things like the message type
-which id returned to the sender (99) is a random value that
-these applications would have agreed on in advance and is
-**not** an RMR definition.
-
-
-::
-
- mrc = rmr_init( listen_port, MAX_BUF_SZ, RMRFL_NOFLAGS );
- rmr_set_stimeout( mrc, 1 ); // allow RMR to retry failed sends for ~1ms
- while( ! rmr_ready( mrc ) ) { // we send, therefore we need a route table
- sleep( 1 );
- }
- mbuf = NULL; // ensure our buffer pointer is nil for 1st call
- while( TRUE ) {
- mbuf = rmr_rcv_msg( mrc, mbuf ); // wait for message
- if( mbuf == NULL || mbuf->state != RMR_OK ) {
- break;
- }
- if( mbuf->mtype % 2 ) { // respond to odd message types
- plen = rmr_payload_size( mbuf ); // max size
- // reset necessary fields in msg
- mbuf->mtype = 99; // response type
- mbuf->sub_id = RMR_VOID_SUBID; // we turn subid off
- mbuf->len = snprintf( mbuf->payload, plen, "pong: %s", get_info() );
- mbuf = rmr_rts_msg( mrc, mbuf ); // return to sender
- if( mbuf == NULL || mbuf->state != RMR_OK ) {
- fprintf( stderr, "return to sender failed\\n" );
- }
- }
- }
- fprintf( stderr, "abort: receive failure\\n" );
- rmr_close( mrc );
-
-
-
+.. This work is licensed under a Creative Commons Attribution 4.0 International License.
+.. SPDX-License-Identifier: CC-BY-4.0
+.. CAUTION: this document is generated from source in doc/src/rtd.
+.. To make changes edit the source and recompile the document.
+.. Do NOT make changes directly to .rst or .md files.
+
+============================================================================================
+User's Guide
+============================================================================================
+--------------------------------------------------------------------------------------------
+RIC Message Router -- RMR
+--------------------------------------------------------------------------------------------
+
+
+Overview
+========
+
+The RIC Message Router (RMR) is a library for peer-to-peer
+communication. Applications use the library to send and
+receive messages where the message routing and endpoint
+selection is based on the message type rather than DNS host
+name-IP port combinations. The library provides the following
+major features:
+
+
+* Routing and endpoint selection is based on *message type.*
+
+* Application is insulated from the underlying transport
+ mechanism and/or protocols.
+
+* Message distribution (round robin or fanout) is selectable
+ by message type.
+
+* Route management updates are received and processed
+ asynchronously and without overt application involvement.
+
+
+
+
+Purpose
+-------
+
+RMR's main purpose is to provide an application with the
+ability to send and receive messages to/from other peer
+applications with minimal effort on the application's part.
+To achieve this, RMR manages all endpoint information,
+connections, and routing information necessary to establish
+and maintain communication. From the application's point of
+view, all that is required to send a message is to allocate
+(via RMR) a message buffer, add the payload data, and set the
+message type. To receive a message, the application needs
+only to invoke the receive function; when a message arrives a
+message buffer will be returned as the function result.
+
+
+Message Routing
+---------------
+
+Applications are required to place a message type into a
+message before sending, and may optionally add a subscription
+ID when appropriate. The combination of message type, and
+subscription ID are refered to as the *message key,* and is
+used to match an entry in a routing table which provides the
+possible endpoints expecting to receive messages with the
+matching key.
+
+
+Round Robin Delivery
+--------------------
+
+An endpoint from RMR's perspective is an application to which
+RMR may establish a connection, and expect to send messages
+with one or more defined message keys. Each entry in the
+route table consists of one or more endpoint groups, called
+round robin groups. When a message matches a specific entry,
+the entry's groups are used to select the destination of the
+message. A message is sent once to each group, with messages
+being *balanced* across the endpoints of a group via round
+robin selection. Care should be taken when defining multiple
+groups for a message type as there is extra overhead required
+and thus the overall message latency is somewhat increased.
+
+
+Routing Table Updates
+---------------------
+
+Route table information is made available to RMR a static
+file (loaded once), or by updates sent from a separate route
+manager application. If a static table is provided, it is
+loaded during RMR initialization and will remain in use until
+an external process connects and delivers a route table
+update (often referred to as a dynamic update). Dynamic
+updates are listened for in a separate process thread and
+applied automatically; the application does not need to allow
+for, or trigger, updates.
+
+
+Latency And Throughput
+----------------------
+
+While providing insulation from the underlying message
+transport mechanics, RMR must also do so in such a manner
+that message latency and throughput are not impacted. In
+general, the RMR induced overhead, incurred due to the
+process of selecting an endpoint for each message, is minimal
+and should not impact the overall latency or throughput of
+the application. This impact has been measured with test
+applications running on the same physical host and the
+average latency through RMR for a message was on the order of
+0.02 milliseconds.
+
+As an application's throughput increases, it becomes easy for
+the application to overrun the underlying transport mechanism
+(e.g. NNG), consume all available TCP transmit buffers, or
+otherwise find itself in a situation where a send might not
+immediately complete. RMR offers different *modes* which
+allow the application to manage these states based on the
+overall needs of the application. These modes are discussed
+in the *Configuration* section of this document.
+
+
+General Use
+===========
+
+To use, the RMR based application simply needs to initialise
+the RMR environment, wait for RMR to have received a routing
+table (become ready), and then invoke either the send or
+receive functions. These steps, and some behind the scenes
+details, are described in the following paragraphs.
+
+
+Initialisation
+--------------
+
+The RMR function ``rmr_init()`` is used to set up the RMR
+environment and must be called before messages can be sent or
+received. One of the few parameters that the application must
+communicate to RMR is the port number that will be used as
+the listen port for new connections. The port number is
+passed on the initialisation function call and a TCP listen
+socket will be opened with this port. If the port is already
+in use RMR will report a failure; the application will need
+to reinitialise with a different port number, abort, or take
+some other action appropriate for the application.
+
+In addition to creating a TCP listen port, RMR will start a
+process thread which will be responsible for receiving
+dynamic updates to the route table. This thread also causes a
+TCP listen port to be opened as it is expected that the
+process which generates route table updates will connect and
+send new information when needed. The route table update port
+is **not** supplied by the application, but is supplied via
+an environment variable as this value is likely determined by
+the mechanism which is starting and configuring the
+application.
+
+
+The RMR Context
+---------------
+
+On successful initialisation, a void pointer, often called a
+*handle* by some programming languages, is returned to the
+application. This is a reference to the RMR control
+information and must be passed as the first parameter on most
+RMR function calls. RMR refers to this as the context, or
+ctx.
+
+
+Wait For Ready
+--------------
+
+An application which is only receiving messages does not need
+to wait for RMR to *become ready* after the call to the
+initialization function. However, before the application can
+successfully send a message, RMR must have loaded a route
+table, and the application must wait for RMR to report that
+it has done so. The RMR function ``rmr_ready()`` will return
+the value *true* (1) when a complete route table has been
+loaded and can be used to determine the endpoint for a send
+request.
+
+
+Receiving Messages
+------------------
+
+The process of receiving is fairly straight forward. The
+application invokes the RMR ``rmr_rcv_msg()`` function which
+will block until a message is received. The function returns
+a pointer to a message block which provides all of the
+details about the message. Specifically, the application has
+access to the following information either directly or
+indirectly:
+
+
+* The payload (actual data)
+
+* The total payload length in bytes
+
+* The number of bytes of the payload which contain valid data
+
+* The message type and subscription ID values
+
+* The hostname and IP address of the source of the message
+ (the sender)
+
+* The transaction ID
+
+* Tracing data (if provided)
+
+
+
+
+The Message Payload
+-------------------
+
+The message payload contains the *raw* data that was sent by
+the peer application. The format will likely depend on the
+message type, and is expected to be known by the application.
+A direct pointer to the payload is available from the message
+buffer (see appendix B for specific message buffer details).
+
+Two payload-related length values are also directly
+available: the total payload length, and the number of bytes
+actually filled with data. The used length is set by the
+caller, and may or not be an accurate value. The total
+payload length is determined when the buffer is created for
+sending, and is the maximum number of bytes that the
+application may modify should the buffer be used to return a
+response.
+
+
+Message Type and Subscription ID
+--------------------------------
+
+The message type and subscription ID are both directly
+available from the message buffer, and are the values which
+were used to by RMR in the sending application to select the
+endpoint. If the application resends the message, as opposed
+to returning the message buffer as a response, the message
+number and/or the subscription ID might need to be changed to
+avoid potential issues[1].
+
+
+Sender Information
+------------------
+
+The source, or sender information, is indirectly available to
+the application via the ``rmr_get_src()`` and
+``rmr_get_ip()`` functions. The former returns a string
+containing ``hostname:port,`` while the string
+``ip:port`` is returned by the latter.
+
+
+Transaction ID
+--------------
+
+The message buffer contains a fixed length set of bytes which
+applications can set to track related messages across the
+application concept of a transaction. RMR will use the
+transaction ID for matching a response message when the
+``rmr_call()`` function is used to send a message.
+
+
+Trace Information
+-----------------
+
+RMR supports the addition of an optional trace information to
+any message. The presence and size is controlled by the
+application, and can vary from message to message if desired.
+The actual contents of the trace information is determined by
+the application; RMR provides only the means to set, extract,
+and obtain a direct reference to the trace bytes. The trace
+data field in a message buffer is discussed in greater detail
+in the *Trace Data* section.
+
+
+Sending Messages
+----------------
+
+Sending requires only slightly more work on the part of the
+application than receiving a message. The application must
+allocate an RMR message buffer, populate the message payload
+with data, set the message type and length, and optionally
+set the subscription ID. Information such as the source IP
+address, hostname, and port are automatically added to the
+message buffer by RMR, so there is no need for the
+application to worry about these.
+
+
+Message Buffer Allocation
+-------------------------
+
+The function ``rmr_msg_alloc()`` allocates a *zero copy*
+buffer and returns a pointer to the RMR ``rmr_mbuf_t``
+structure. The message buffer provides direct access to the
+payload, length, message type and subscription ID fields. The
+buffer must be preallocated in order to allow the underlying
+transport mechanism to allocate the payload space from its
+internal memory pool; this eliminates multiple copies as the
+message is sent, and thus is more efficient.
+
+If a message buffer has been received, and the application
+wishes to use the buffer to send a response, or to forward
+the buffer to another application, a new buffer does **not**
+need to be allocated. The application may set the necessary
+information (message type, etc.), and adjust the payload, as
+is necessary and then pass the message buffer to
+``rmr_send_msg()`` or ``rmr_rts_msg()`` to be sent or
+returned to the sender.
+
+
+Populating the Message Buffer
+-----------------------------
+
+The application has direct access to several of the message
+buffer fields, and should set them appropriately.
+
+
+ .. list-table::
+ :widths: 15,80
+ :header-rows: 0
+ :class: borderless
+
+ * - **len**
+ -
+ This is the number of bytes that the application placed into
+ the payload. Setting length to 0 is allowed, and length may
+ be less than the allocated payload size.
+
+ * - **mtype**
+ -
+ The message type that RMR will use to determine the endpoint
+ used as the target of the send.
+
+ * - **sub_id**
+ -
+ The subscription ID if the message is to be routed based on
+ the combination of message type and subscription ID. If no
+ subscription ID is valid for the message, the application
+ should set the field with the RMR constant
+ ``RMR_VOID_SUBID.``
+
+ * - **payload**
+ -
+ The application should obtain the reference (pointer) to the
+ payload from the message buffer and place any data into the
+ payload. The application is responsible for ensuring that the
+ maximum payload size is not exceeded. The application may
+ obtain the maximum size via the ``rmr_payload_size()``
+ function.
+
+ * - **trace data**
+ -
+ Optionally, the application may add trace information to the
+ message buffer.
+
+
+
+
+
+Sending a Message Buffer
+------------------------
+
+Once the application has populated the necessary bits of a
+message, it may be sent by passing the buffer to the
+``rmr_send_msg()`` function. This function will select an
+endpoint to receive the message, based on message type and
+subscription ID, and will pass the message to the underlying
+transport mechanism for actual transmission on the
+connection. (Depending on the underlying transport mechanism,
+the actual connection to the endpoint may happen at the time
+of the first message sent to the endpoint, and thus the
+latency of the first send might be longer than expected.)
+
+On success, the send function will return a reference to a
+message buffer; the status within that message buffer will
+indicate what the message buffer contains. When the status is
+``RMR_OK`` the reference is to a **new** message buffer for
+the application to use for the next send; the payload size is
+the same as the payload size allocated for the message that
+was just sent. This is a convenience as it eliminates the
+need for the application to call the message allocation
+function at some point in the future, and assumes the
+application will send many messages which will require the
+same payload dimensions.
+
+If the message contains any status other than ``RMR_OK,``
+then the message could **not** be sent, and the reference is
+to the unsent message buffer. The value of the status will
+indicate whether the nature of the failure was transient (
+``RMR_ERR_RETRY``) or not. Transient failures are likely to
+be successful if the application attempts to send the message
+at a later time. Unfortunately, it is impossible for RMR to
+know the exact transient failure (e.g. connection being
+established, or TCP buffer shortage), and thus it is not
+possible to communicate how long the application should wait
+before attempting to resend, if the application wishes to
+resend the message. (More discussion with respect to message
+retries can be found in the *Handling Failures* section.)
+
+
+Advanced Usage
+==============
+
+Several forms of usage fall into a more advanced category and
+are described in the following sections. These include
+blocking call, return to sender and wormhole functions.
+
+
+The Call Function
+-----------------
+
+The RMR function ``rmr_call()`` sends a message in the exact
+same manner as the ``rmr_send_msg()()`` function, with the
+endpoint selection based on the message key. But unlike the
+send function, ``rmr_call()`` will block and wait for a
+response from the application that is selected to receive the
+message. The matching message is determined by the
+transaction ID which the application must place into the
+message buffer prior to invoking ``rmr_call()``. Similarly,
+the responding application must ensure that the same
+transaction ID is placed into the message buffer before
+returning its response.
+
+The return from the call is a message buffer with the
+response message; there is no difference between a message
+buffer returned by the receive function and one returned by
+the ``rmr_call()`` function. If a response is not received in
+a reasonable amount of time, a nil message buffer is returned
+to the calling application.
+
+
+Returning a Response
+--------------------
+
+Because of the nature of RMR's routing policies, it is
+generally not possible for an application to control exactly
+which endpoint is sent a message. There are cases, such as
+responding to a message delivered via ``rmr_call()`` that the
+application must send a message and guarantee that RMR routes
+it to an exact destination. To enable this, RMR provides the
+``rmr_rts_msg(),`` return to sender, function. Upon receipt
+of any message, an application may alter the payload, and if
+necessary the message type and subscription ID, and pass the
+altered message buffer to the ``rmr_rts_msg()`` function to
+return the altered message to the application which sent it.
+When this function is used, RMR will examine the message
+buffer for the source information and use that to select the
+connection on which to write the response.
+
+
+Multi-threaded Calls
+--------------------
+
+The basic call mechanism described above is **not** thread
+safe, as it is not possible to guarantee that a response
+message is delivered to the correct thread. The RMR function
+``rmr_mt_call()`` accepts an additional parameter which
+identifies the calling thread in order to ensure that the
+response is delivered properly. In addition, the application
+must specifically initialise the multi-threaded call
+environment by passing the ``RMRFL_MTCALL`` flag as an option
+to the ``rmr_init()`` function.
+
+One advantage of the multi-threaded call capability in RMR is
+the fact that only the calling thread is blocked. Messages
+received which are not responses to the call are continued to
+be delivered via normal ``rmr_rcv_msg()`` calls.
+
+While the process is blocked waiting for the response, it is
+entirely possible that asynchronous, non-matching, messages
+will arrive. When this happens, RMR will queues the messages
+and return them to the application over the next calls to
+``rmr_rcv_msg().``
+
+
+Wormholes
+---------
+
+As was mentioned earlier, the design of RMR is to eliminate
+the need for an application to know a specific endpoint, even
+when a response message is being sent. In some rare cases it
+may be necessary for an application to establish a direct
+connection to an RMR-based application rather than relying on
+message type and subscription ID based routing. The
+*wormhole* functions provide an application with the ability
+to create a direct connection and then to send and receive
+messages across the connection. The following are the RMR
+functions which provide wormhole communications:
+
+
+ .. list-table::
+ :widths: auto
+ :header-rows: 0
+ :class: borderless
+
+ * - **rmr_wh_open**
+ -
+ Open a connection to an endpoint. Name or IP address and port
+ of the endpoint is supplied. Returns a wormhole ID that the
+ application must use when sending a direct message.
+
+ * - **rmr_wh_send_msg**
+ -
+ Sends an RMR message buffer to the connected application. The
+ message type and subscription ID may be set in the message,
+ but RMR will ignore both.
+
+ * - **rmr_wh_close**
+ -
+ Closes the direct connection.
+
+
+
+
+
+Handling Failures
+=================
+
+The vast majority of states reported by RMR are fatal; if
+encountered during setup or initialization, then it is
+unlikely that any message oriented processing should
+continue, and when encountered on a message operation
+continued operation on that message should be abandoned.
+Specifically with regard to message sending, it is very
+likely that the underlying transport mechanism will report a
+*soft,* or transient, failure which might be successful if
+the operation is retried at a later point in time. The
+paragraphs below discuss the methods that an application
+might deal with these soft failures.
+
+
+Failure Notification
+--------------------
+
+When a soft failure is reported, the returned message buffer
+returned by the RMR function will be ``RMR_ERR_RETRY.`` These
+types of failures can occur for various reasons; one of two
+reasons is typically the underlying cause:
+
+
+* The session to the targeted recipient (endpoint) is not
+ connected.
+
+* The transport mechanism buffer pool is full and cannot
+ accept another buffer.
+
+
+
+Unfortunately, it is not possible for RMR to determine which
+of these two cases is occurring, and equally as unfortunate
+the time to resolve each is different. The first, no
+connection, may require up to a second before a message can
+be accepted, while a rejection because of buffer shortage is
+likely to resolve in less than a millisecond.
+
+
+Application Response
+--------------------
+
+The action which an application takes when a soft failure is
+reported ultimately depends on the nature of the application
+with respect to factors such as tolerance to extended message
+latency, dropped messages, and over all message rate.
+
+
+RMR Retry Modes
+---------------
+
+In an effort to reduce the workload of an application
+developer, RMR has a default retry policy such that RMR will
+attempt to retransmit a message up to 1000 times when a soft
+failure is reported. These retries generally take less than 1
+millisecond (if all 1000 are attempted) and in most cases
+eliminates nearly all reported soft failures to the
+application. When using this mode, it might allow the
+application to simply treat all bad return values from a send
+attempt as permanent failures.
+
+If an application is so sensitive to any delay in RMR, or the
+underlying transport mechanism, it is possible to set RMR to
+return a failure immediately on any kind of error (permanent
+failures are always reported without retry). In this mode,
+RMR will still set the state in the message buffer to
+``RMR_ERR_RETRY,`` but will **not** make any attempts to
+resend the message. This zero-retry policy is enabled by
+invoking the ``rmr_set_stimeout()`` with a value of 0; this
+can be done once immediately after ``rmr_init()`` is invoked.
+
+Regardless of the retry mode which the application sets, it
+will ultimately be up to the application to handle failures
+by queuing the message internally for resend, retrying
+immediately, or dropping the send attempt all together. As
+stated before, only the application can determine how to best
+handle send failures.
+
+
+Other Failures
+--------------
+
+RMR will return the state of processing for message based
+operations (send/receive) as the status in the message
+buffer. For non-message operations, state is returned to the
+caller as the integer return value for all functions which
+are not expected to return a pointer (e.g.
+``rmr_init()``.) The following are the RMR state constants
+and a brief description of their meaning.
+
+
+ .. list-table::
+ :widths: auto
+ :header-rows: 0
+ :class: borderless
+
+ * - **RMR_OK**
+ -
+ state is good; operation finished successfully
+
+ * - **RMR_ERR_BADARG**
+ -
+ argument passed to function was unusable
+
+ * - **RMR_ERR_NOENDPT**
+ -
+ send/call could not find an endpoint based on msg type
+
+ * - **RMR_ERR_EMPTY**
+ -
+ msg received had no payload; attempt to send an empty message
+
+ * - **RMR_ERR_NOHDR**
+ -
+ message didn't contain a valid header
+
+ * - **RMR_ERR_SENDFAILED**
+ -
+ send failed; errno may contain the transport provider reason
+
+ * - **RMR_ERR_CALLFAILED**
+ -
+ unable to send the message for a call function; errno may
+ contain the transport provider reason
+
+ * - **RMR_ERR_NOWHOPEN**
+ -
+ no wormholes are open
+
+ * - **RMR_ERR_WHID**
+ -
+ the wormhole id provided was invalid
+
+ * - **RMR_ERR_OVERFLOW**
+ -
+ operation would have busted through a buffer/field size
+
+ * - **RMR_ERR_RETRY**
+ -
+ request (send/call/rts) failed, but caller should retry
+ (EAGAIN for wrappers)
+
+ * - **RMR_ERR_RCVFAILED**
+ -
+ receive failed (hard error)
+
+ * - **RMR_ERR_TIMEOUT**
+ -
+ response message not received in a reasonable amount of time
+
+ * - **RMR_ERR_UNSET**
+ -
+ the message hasn't been populated with a transport buffer
+
+ * - **RMR_ERR_TRUNC**
+ -
+ length in the received buffer is longer than the size of the
+ allocated payload, received message likely truncated (length
+ set by sender could be wrong, but we can't know that)
+
+ * - **RMR_ERR_INITFAILED**
+ -
+ initialisation of something (probably message) failed
+
+ * - **RMR_ERR_NOTSUPP**
+ -
+ the request is not supported, or RMR was not initialised for
+ the request
+
+
+
+Depending on the underlying transport mechanism, and the
+nature of the call that RMR attempted, the system
+``errno`` value might reflect additional detail about the
+failure. Applications should **not** rely on errno as some
+transport mechanisms do not set it with any consistency.
+
+
+Configuration and Control
+=========================
+
+With the assumption that most RMR based applications will be
+executed in a containerised environment, there are some
+underlying mechanics which the developer may need to know in
+order to properly provide a configuration specification to
+the container management system. The following paragraphs
+briefly discuss these.
+
+
+
+TCP Ports
+---------
+
+RMR requires two (2) TCP listen ports: one for general
+application-to-application communications and one for
+route-table updates. The general communication port is
+specified by the application at the time RMR is initialised.
+The port used to listen for route table updates is likely to
+be a constant port shared by all applications provided they
+are running in separate containers. To that end, the port
+number defaults to 4561, but can be configured with an
+environment variable (see later paragraph in this section).
+
+
+Host Names
+----------
+
+RMR is typically host name agnostic. Route table entries may
+contain endpoints defined either by host name or IP address.
+In the container world the concept of a *service name* might
+exist, and likely is different than a host name. RMR's only
+requirement with respect to host names is that a name used on
+a route table entry must be resolvable via the
+``gethostbyname`` system call.
+
+
+Environment Variables
+---------------------
+
+Several environment variables are recognised by RMR which, in
+general, are used to define interfaces and listen ports (e.g.
+the route table update listen port), or debugging
+information. Generally this information is system controlled
+and thus RMR expects this information to be defined in the
+environment rather than provided by the application. The
+following is a list of the environment variables which RMR
+recognises:
+
+
+ .. list-table::
+ :widths: auto
+ :header-rows: 0
+ :class: borderless
+
+ * - **RMR_ASYNC_CONN**
+ -
+ Allows the async connection mode to be turned off (by setting
+ the value to 0). When set to 1, or missing from the
+ environment, RMR will invoke the connection interface in the
+ transport mechanism using the non-blocking (async) mode. This
+ will likely result in many "soft failures" (retry) until the
+ connection is established, but allows the application to
+ continue unimpeded should the connection be slow to set up.
+
+ * - **RMR_BIND_IF**
+ -
+ This provides the interface that RMR will bind listen ports
+ to, allowing for a single interface to be used rather than
+ listening across all interfaces. This should be the IP
+ address assigned to the interface that RMR should listen on,
+ and if not defined RMR will listen on all interfaces.
+
+ * - **RMR_CTL_PORT**
+ -
+ This variable defines the port that RMR should open for
+ communications with Route Manager, and other RMR control
+ applications. If not defined, the port 4561 is assumed.
+
+ Previously, the ``RMR_RTG_SVC`` (route table generator
+ service port) was used to define this port. However, a future
+ version of Route Manager will require RMR to connect and
+ request tables, thus that variable is now used to supply the
+ Route Manager's well-known address and port.
+
+ To maintain backwards compatibility with the older Route
+ Manager versions, the presence of this variable in the
+ environment will shift RMR's behaviour with respect to the
+ default value used when ``RMR_RTG_SVC`` is **not** defined.
+
+ When ``RMR_CTL_PORT`` is **defined:** RMR assumes that Route
+ Manager requires RMR to connect and request table updates is
+ made, and the default well-known address for Route manager is
+ used (routemgr:4561).
+
+ When ``RMR_CTL_PORT`` is **undefined:** RMR assumes that
+ Route Manager will connect and push table updates, thus the
+ default listen port (4561) is used.
+
+ To avoid any possible misinterpretation and/or incorrect
+ assumptions on the part of RMR, it is recommended that both
+ the ``RMR_CTL_PORT`` and ``RMR_RTG_SVC`` be defined. In the
+ case where both variables are defined, RMR will behave
+ exactly as is communicated with the variable's values.
+
+ * - **RMR_RTREQ_FREQ**
+ -
+ When RMR needs a new route table it will send a request once
+ every ``n`` seconds. The default value for ``n`` is 5, but
+ can be changed if this variable is set prior to invoking the
+ process. Accepted values are between 1 and 300 inclusive.
+
+ * - **RMR_RTG_SVC**
+ -
+ The value of this variable depends on the Route Manager in
+ use.
+
+ When the Route Manager is expecting to connect to an xAPP and
+ push route tables, this variable must indicate the
+ ``port`` which RMR should use to listen for these
+ connections.
+
+ When the Route Manager is expecting RMR to connect and
+ request a table update during initialisation, the variable
+ should be the ``host`` of the Route Manager process.
+
+ The ``RMR_CTL_PORT`` variable (added with the support of
+ sending table update requests to Route manager), controls the
+ behaviour if this variable is not set. See the description of
+ that variable for details.
+
+ * - **RMR_HR_LOG**
+ -
+ By default RMR writes messages to standard error (incorrectly
+ referred to as log messages) in human readable format. If
+ this environment variable is set to 0, the format of standard
+ error messages might be written in some format not easily
+ read by humans. If missing, a value of 1 is assumed.
+
+ * - **RMR_LOG_VLEVEL**
+ -
+ This is a numeric value which corresponds to the verbosity
+ level used to limit messages written to standard error. The
+ lower the number the less chatty RMR functions are during
+ execution. The following is the current relationship between
+ the value set on this variable and the messages written:
+
+
+ .. list-table::
+ :widths: auto
+ :header-rows: 0
+ :class: borderless
+
+ * - **0**
+ -
+ Off; no messages of any sort are written.
+
+ * - **1**
+ -
+ Only critical messages are written (default if this variable
+ does not exist)
+
+ * - **2**
+ -
+ Errors and all messages written with a lower value.
+
+ * - **3**
+ -
+ Warnings and all messages written with a lower value.
+
+ * - **4**
+ -
+ Informational and all messages written with a lower value.
+
+ * - **5**
+ -
+ Debugging mode -- all messages written, however this requires
+ RMR to have been compiled with debugging support enabled.
+
+
+
+ * - **RMR_RTG_ISRAW**
+ -
+ **Deprecated.** Should be set to 1 if the route table
+ generator is sending "plain" messages (not using RMR to send
+ messages), 0 if the RTG is using RMR to send. The default is
+ 1 as we don't expect the RTG to use RMR.
+
+ This variable is only recognised when using the NNG transport
+ library as it is not possible to support NNG "raw"
+ communications with other transport libraries. It is also
+ necessary to match the value of this variable with the
+ capabilities of the Route Manager; at some point in the
+ future RMR will assume that all Route Manager messages will
+ arrive via an RMR connection and will ignore this variable.
+
+ * - **RMR_SEED_RT**
+ -
+ This is used to supply a static route table which can be used
+ for debugging, testing, or if no route table generator
+ process is being used to supply the route table. If not
+ defined, no static table is used and RMR will not report
+ *ready* until a table is received. The static route table may
+ contain both the route table (between newrt start and end
+ records), and the MEID map (between meid_map start and end
+ records).
+
+ * - **RMR_SRC_ID**
+ -
+ This is either the name or IP address which is placed into
+ outbound messages as the message source. This will used when
+ an RMR based application uses the rmr_rts_msg() function to
+ return a response to the sender. If not supplied RMR will use
+ the hostname which in some container environments might not
+ be routable.
+
+ The value of this variable is also used for Route Manager
+ messages which are sent via an RMR connection.
+
+ * - **RMR_STASH_RT**
+ -
+ Names the file where RMR should write the latest update it
+ receives from the source of route tables (generally Route
+ Manager). This is meant to assist with debugging and/or
+ troubleshooting when it is suspected that route information
+ isn't being sent and/or received correctly. If this variable
+ is not given, RMR will save the last update using the
+ ``RMR_SEED_RT`` variable value and adding a ``.stash`` suffix
+ to the filename so as not to overwrite the static table.
+
+ * - **RMR_VCTL_FILE**
+ -
+ This supplies the name of a verbosity control file. The core
+ RMR functions do not produce messages unless there is a
+ critical failure. However, the route table collection thread,
+ not a part of the main message processing component, can
+ write additional messages to standard error. If this variable
+ is set, RMR will extract the verbosity level for these
+ messages (0 is silent) from the first line of the file.
+ Changes to the file are detected and thus the level can be
+ changed dynamically, however RMR will only suss out this
+ variable during initialisation, so it is impossible to enable
+ verbosity after startup.
+
+ * - **RMR_WARNINGS**
+ -
+ If set to 1, RMR will write some warnings which are
+ non-performance impacting. If the variable is not defined, or
+ set to 0, RMR will not write these additional warnings.
+
+
+
+There are other, non-RMR, variables which may exist and are
+used by RMR. These variable names are not under the control
+of RMR, so they are subject to change without potentiallyb
+being reflected in either RMR's code, or this document. The
+following is a list of these environment variables.
+
+
+ .. list-table::
+ :widths: auto
+ :header-rows: 0
+ :class: borderless
+
+ * - **ALARM_MANAGER_SERVICE_NAME**
+ -
+ This is the DNS name, or IP address, of the process which is
+ listening for RMR alarm messages. If this variable is
+ missing, ``service-ricplt-alarmmanager-rmr`` is assumed.
+
+ * - **ALARM_MANAGER_SERVICE_PORT**
+ -
+ This is the port that the alarm manager is using to accept
+ RMR messages. If the environment variable is missing the
+ value ``4560`` is assumed.
+
+
+
+
+Logging and Alarms
+------------------
+
+As with nearly all UNIX libraries, errors, warnings and
+informational messages are written in plain text to the
+standard error device (stderr). All RMR messages are prefixed
+with the current time (in milliseconds past the standard UNIX
+epoch), the process ID, and a severity indicator. RMR
+messages are written with one of three severity strings:
+
+
+ .. list-table::
+ :widths: auto
+ :header-rows: 0
+ :class: borderless
+
+ * - **[CRI]**
+ -
+ The event is of a critical nature and it is unlikely that RMR
+ will continue to operate correctly if at all. It is almost
+ certain that immediate action will be needed to resolve the
+ issue.
+
+ * - **[ERR]**
+ -
+ The event is not expected and RMR is not able to handle it.
+ There is a small chance that continued operation will be
+ negatively impacted. Eventual action to diagnose and correct
+ the issue will be necessary.
+
+ * - **[WRN]**
+ -
+ The event was not expected by RMR, but can be worked round.
+ Normal operation will continue, but it is recommended that
+ the cause of the problem be investigated.
+
+
+
+
+
+Log message supression
+----------------------
+
+For the most part, the *fast path* code in RMR does no
+logging; even when messages are squelched, there is a
+non-zero cosst to check for the setting each time a potential
+message is to be written. To that end, RMRM will log only
+severe errors once initialisation has completed. An exception
+to this policy exists in the route table collection thread.
+The thread of execution which collects route table updates
+does not need to be concerned with performance, and as such
+has the potential to log its actions in a very verbose
+manner. The environment variable `` RMR_VCTL_FILE `` can be
+used to define a file where the desired verbosity level (0 to
+4 where 0 is off) can be placed. If the environment variable
+is not set when the process starts, RMR will assume that the
+file ``/tmp/rmr.v`` will be used. Beginning with version
+4.6.0 this file does **not** need to exist when the process
+is started. To change the verbosity level, the desired value
+is written to the file on the first line.
+
+
+Alarms
+------
+
+The route table colleciton thread is also responsible for
+watching for situations which need to be reported as alarms
+to the platform's alarm management service. When a state
+exists RMR will create and send an alarm (via RMR message) to
+the alarm service, and will send a *clear* message when the
+state no longer exists. Currently RMR will alarm only when
+the application is not removing messages from the receive
+ring quicklye enough causing RMR to drop messages as they are
+received.
+
+
+Notes
+=====
+
+
+ [1] It is entirely possible to design a routing table, and
+ application group, such that the same message type is is
+ left unchanged and the message is forwarded by an
+ application after updating the payload. This type of
+ behaviour is often referred to as service chaining, and can
+ be done without any "knowledge" by an application with
+ respect to where the message goes next. Service chaining is
+ supported by RMR in as much as it allows the message to be
+ resent, but the actual complexities of designing and
+ implementing service chaining lie with the route table
+ generator process.
+
+
+
+
+
+
+Appendix A -- Quick Reference
+=============================
+
+Please refer to the RMR manual pages on the Read the Docs
+site
+
+https://docs.o-ran-sc.org/projects/o-ran-sc-ric-plt-lib-rmr/en/latest/index.html
+
+
+
+Appendix B -- Message Buffer Details
+====================================
+
+The RMR message buffer is a C structure which is exposed in
+the ``rmr.h`` header file. It is used to manage a message
+received from a peer endpoint, or a message that is being
+sent to a peer. Fields include payload length, amount of
+payload actually used, status, and a reference to the
+payload. There are also fields which the application should
+ignore, and could be hidden in the header file, but we chose
+not to. These fields include a reference to the RMR header
+information, and to the underlying transport mechanism
+message struct which may or may not be the same as the RMR
+header reference.
+
+
+The Structure
+-------------
+
+The following is the C structure. Readers are cautioned to
+examine the ``rmr.h`` header file directly; the information
+here may be out of date (old document in some cache), and
+thus it may be incorrect.
+
+
+::
+
+
+ typedef struct {
+ int state; // state of processing
+ int mtype; // message type
+ int len; // length of data in the payload (send or received)
+ unsigned char* payload; // transported data
+ unsigned char* xaction; // pointer to fixed length transaction id bytes
+ int sub_id; // subscription id
+ int tp_state; // transport state (errno)
+
+ // these things are off limits to the user application
+ void* tp_buf; // underlying transport allocated pointer (e.g. nng message)
+ void* header; // internal message header (whole buffer: header+payload)
+ unsigned char* id; // if we need an ID in the message separate from the xaction id
+ int flags; // various MFL_ (private) flags as needed
+ int alloc_len; // the length of the allocated space (hdr+payload)
+ void* ring; // ring this buffer should be queued back to
+ int rts_fd; // SI fd for return to sender
+ int cookie; // cookie to detect user misuse of free'd msg
+ } rmr_mbuf_t;
+
+
+
+
+State vs Transport State
+------------------------
+
+The state field reflects the state at the time the message
+buffer is returned to the calling application. For a send
+operation, if the state is not ``RMR_OK`` then the message
+buffer references the payload that could not be sent, and
+when the state is ``RMR_OK`` the buffer references a *fresh*
+payload that the application may fill in.
+
+When the state is not ``RMR_OK,`` C programmes may examine
+the global ``errno`` value which RMR will have left set, if
+it was set, by the underlying transport mechanism. In some
+cases, wrapper modules are not able to directly access the
+C-library ``errno`` value, and to assist with possible
+transport error details, the send and receive operations
+populate ``tp_state`` with the value of ``errno.``
+
+Regardless of whether the application makes use of the
+``tp_state,`` or the ``errno`` value, it should be noted that
+the underlying transport mechanism may not actually update
+the errno value; in other words: it might not be accurate. In
+addition, RMR populates the ``tp_state`` value in the message
+buffer **only** when the state is not ``RMR_OK.``
+
+
+Field References
+----------------
+
+The transaction field was exposed in the first version of
+RMR, and in hindsight this shouldn't have been done. Rather
+than break any existing code the reference was left, but
+additional fields such as trace data, were not directly
+exposed to the application. The application developer is
+strongly encouraged to use the functions which get and set
+the transaction ID rather than using the pointer directly;
+any data overruns will not be detected if the reference is
+used directly.
+
+In contrast, the payload reference should be used directly by
+the application in the interest of speed and ease of
+programming. The same care to prevent writing more bytes to
+the payload buffer than it can hold must be taken by the
+application. By the nature of the allocation of the payload
+in transport space, RMR is unable to add guard bytes and/or
+test for data overrun.
+
+
+Actual Transmission
+-------------------
+
+When RMR sends the application's message, the message buffer
+is **not** transmitted. The transport buffer (tp_buf) which
+contains the RMR header and application payload is the only
+set of bytes which are transmitted. While it may seem to the
+caller like the function ``rmr_send_msg()`` is returning a
+new message buffer, the same struct is reused and only a new
+transport buffer is allocated. The intent is to keep the
+alloc/free cycles to a minimum.
+
+
+
+Appendix C -- Glossary
+======================
+
+Many terms in networking can be interpreted with multiple
+meanings, and several terms used in various RMR documentation
+are RMR specific. The following definitions are the meanings
+of terms used within RMR documentation and should help the
+reader to understand the intent of meaning.
+
+ .. list-table::
+ :widths: 25,70
+ :header-rows: 0
+ :class: borderless
+
+ * - **application**
+ -
+ A programme which uses RMR to send and/or receive messages
+ to/from another RMR based application.
+
+ * - **Critical error**
+ -
+ An error that RMR has encountered which will prevent further
+ successful processing by RMR. Critical errors usually
+ indicate that the application should abort.
+
+ * - **Endpoint**
+ -
+ An RMR based application that is defined as being capable of
+ receiving one or more types of messages (as defined by a
+ *routing key.*)
+
+ * - **Environment variable**
+ -
+ A key/value pair which is set externally to the application,
+ but which is available to the application (and referenced
+ libraries) through the ``getenv`` system call. Environment
+ variables are the main method of communicating information
+ such as port numbers to RMR.
+
+ * - **Error**
+ -
+ An abnormal condition that RMR has encountered, but will not
+ affect the overall processing by RMR, but may impact certain
+ aspects such as the ability to communicate with a specific
+ endpoint. Errors generally indicate that something, usually
+ external to RMR, must be addressed.
+
+ * - **Host name**
+ -
+ The name of the host as returned by the ``gethostbyname``
+ system call. In a containerised environment this might be the
+ container or service name depending on how the container is
+ started. From RMR's point of view, a host name can be used to
+ resolve an *endpoint* definition in a *route* table.)
+
+ * - **IP**
+ -
+ Internet protocol. A low level transmission protocol which
+ governs the transmission of datagrams across network
+ boundaries.
+
+ * - **Listen socket**
+ -
+ A *TCP* socket used to await incoming connection requests.
+ Listen sockets are defined by an interface and port number
+ combination where the port number is unique for the
+ interface.
+
+ * - **Message**
+ -
+ A series of bytes transmitted from the application to another
+ RMR based application. A message is comprised of RMR specific
+ data (a header), and application data (a payload).
+
+ * - **Message buffer**
+ -
+ A data structure used to describe a message which is to be
+ sent or has been received. The message buffer includes the
+ payload length, message type, message source, and other
+ information.
+
+ * - **Message type**
+ -
+ A signed integer (0-32000) which identifies the type of
+ message being transmitted, and is one of the two components
+ of a *routing key.* See *Subscription ID.*
+
+ * - **Payload**
+ -
+ The portion of a message which holds the user data to be
+ transmitted to the remote *endpoint.* The payload contents
+ are completely application defined.
+
+ * - **RMR context**
+ -
+ A set of information which defines the current state of the
+ underlying transport connections that RMR is managing. The
+ application will be give a context reference (pointer) that
+ is supplied to most RMR functions as the first parameter.
+
+ * - **Round robin**
+ -
+ The method of selecting an *endpoint* from a list such that
+ all *endpoints* are selected before starting at the head of
+ the list.
+
+ * - **Route table**
+ -
+ A series of "rules" which define the possible *endpoints* for
+ each *routing key.*
+
+ * - **Route table manager**
+ -
+ An application responsible for building a *route table* and
+ then distributing it to all applicable RMR based
+ applications.
+
+ * - **Routing**
+ -
+ The process of selecting an *endpoint* which will be the
+ recipient of a message.
+
+ * - **Routing key**
+ -
+ A combination of *message type* and *subscription ID* which
+ RMR uses to select the destination *endpoint* when sending a
+ message.
+
+ * - **Source**
+ -
+ The sender of a message.
+
+ * - **Subscription ID**
+ -
+ A signed integer value (0-32000) which identifies the
+ subscription characteristic of a message. It is used in
+ conjunction with the *message type* to determine the *routing
+ key.*
+
+ * - **Target**
+ -
+ The *endpoint* selected to receive a message.
+
+ * - **TCP**
+ -
+ Transmission Control Protocol. A connection based internet
+ protocol which provides for lossless packet transportation,
+ usually over IP.
+
+ * - **Thread**
+ -
+ Also called a *process thread, or pthread.* This is a
+ lightweight process which executes in concurrently with the
+ application and shares the same address space. RMR uses
+ threads to manage asynchronous functions such as route table
+ updates.
+
+ * - **Trace information**
+ -
+ An optional portion of the message buffer that the
+ application may populate with data that allows for tracing
+ the progress of the transaction or application activity
+ across components. RMR makes no use of this data.
+
+ * - **Transaction ID**
+ -
+ A fixed number of bytes in the *message* buffer) which the
+ application may populate with information related to the
+ transaction. RMR makes use of the transaction ID for matching
+ response messages with the &c function is used to send a
+ message.
+
+ * - **Transient failure**
+ -
+ An error state that is believed to be short lived and that
+ the operation, if retried by the application, might be
+ successful. C programmers will recognise this as
+ ``EAGAIN.``
+
+ * - **Warning**
+ -
+ A warning occurs when RMR has encountered something that it
+ believes isn't correct, but has a defined work round.
+
+ * - **Wormhole**
+ -
+ A direct connection managed by RMR between the user
+ application and a remote, RMR based, application.
+
+
+
+
+
+Appendix D -- Code Examples
+===========================
+
+The following snippet of code illustrate some of the basic
+operation of the RMR library. Please refer to the examples
+and test directories in the RMR repository for complete RMR
+based programmes.
+
+
+Sender Sample
+-------------
+
+The following code segment shows how a message buffer can be
+allocated, populated, and sent. The snippet also illustrates
+how the result from the ``rmr_send_msg()`` function is used
+to send the next message. It does not illustrate error and/or
+retry handling.
+
+
+::
+
+
+ #include <unistd.h>
+ #include <errno.h>
+ #include <string.h>
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <sys/epoll.h>
+ #include <time.h>
+
+ #include <rmr/rmr.h>
+
+ int main( int argc, char** argv ) {
+ void* mrc; // msg router context
+ struct epoll_event events[1]; // list of events to give to epoll
+ struct epoll_event epe; // event definition for event to listen to
+ int ep_fd = -1; // epoll's file des (given to epoll_wait)
+ int rcv_fd; // file des for epoll checks
+ int nready; // number of events ready for receive
+ rmr_mbuf_t* sbuf; // send buffer
+ rmr_mbuf_t* rbuf; // received buffer
+ int count = 0;
+ int rcvd_count = 0;
+ char* listen_port = "43086";
+ int delay = 1000000; // mu-sec delay between messages
+ int mtype = 0;
+ int stats_freq = 100;
+
+ if( argc > 1 ) { // simplistic arg picking
+ listen_port = argv[1];
+ }
+ if( argc > 2 ) {
+ delay = atoi( argv[2] );
+ }
+ if( argc > 3 ) {
+ mtype = atoi( argv[3] );
+ }
+
+ fprintf( stderr, "<DEMO> listen port: %s; mtype: %d; delay: %d\\n",
+ listen_port, mtype, delay );
+
+ if( (mrc = rmr_init( listen_port, 1400, RMRFL_NONE )) == NULL ) {
+ fprintf( stderr, "<DEMO> unable to initialise RMR\\n" );
+ exit( 1 );
+ }
+
+ rcv_fd = rmr_get_rcvfd( mrc ); // set up epoll things, start by getting the FD from RMR
+ if( rcv_fd < 0 ) {
+ fprintf( stderr, "<DEMO> unable to set up polling fd\\n" );
+ exit( 1 );
+ }
+ if( (ep_fd = epoll_create1( 0 )) < 0 ) {
+ fprintf( stderr, "[FAIL] unable to create epoll fd: %d\\n", errno );
+ exit( 1 );
+ }
+ epe.events = EPOLLIN;
+ epe.data.fd = rcv_fd;
+
+ if( epoll_ctl( ep_fd, EPOLL_CTL_ADD, rcv_fd, &epe ) != 0 ) {
+ fprintf( stderr, "[FAIL] epoll_ctl status not 0 : %s\\n", strerror( errno ) );
+ exit( 1 );
+ }
+
+ sbuf = rmr_alloc_msg( mrc, 256 ); // alloc 1st send buf; subsequent bufs alloc on send
+ rbuf = NULL; // don't need to alloc receive buffer
+
+ while( ! rmr_ready( mrc ) ) { // must have route table
+ sleep( 1 ); // wait til we get one
+ }
+ fprintf( stderr, "<DEMO> rmr is ready\\n" );
+
+
+ while( 1 ) { // send messages until the cows come home
+ snprintf( sbuf->payload, 200,
+ "count=%d received= %d ts=%lld %d stand up and cheer!", // create the payload
+ count, rcvd_count, (long long) time( NULL ), rand() );
+
+ sbuf->mtype = mtype; // fill in the message bits
+ sbuf->len = strlen( sbuf->payload ) + 1; // send full ascii-z string
+ sbuf->state = 0;
+ sbuf = rmr_send_msg( mrc, sbuf ); // send & get next buf to fill in
+ while( sbuf->state == RMR_ERR_RETRY ) { // soft failure (device busy?) retry
+ sbuf = rmr_send_msg( mrc, sbuf ); // w/ simple spin that doesn't give up
+ }
+ count++;
+
+ // check to see if anything was received and pull all messages in
+ while( (nready = epoll_wait( ep_fd, events, 1, 0 )) > 0 ) { // 0 is non-blocking
+ if( events[0].data.fd == rcv_fd ) { // waiting on 1 thing, so [0] is ok
+ errno = 0;
+ rbuf = rmr_rcv_msg( mrc, rbuf ); // receive and ignore; just count
+ if( rbuf ) {
+ rcvd_count++;
+ }
+ }
+ }
+
+ if( (count % stats_freq) == 0 ) { // occasional stats out to tty
+ fprintf( stderr, "<DEMO> sent %d received %d\\n", count, rcvd_count );
+ }
+
+ usleep( delay );
+ }
+ }
+
+
+
+
+Receiver Sample
+---------------
+
+The receiver code is even simpler than the sender code as it
+does not need to wait for a route table to arrive (only
+senders need to do that), nor does it need to allocate an
+initial buffer. The example assumes that the sender is
+transmitting a zero terminated string as the payload.
+
+
+::
+
+
+ #include <unistd.h>
+ #include <errno.h>
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <time.h>
+
+ #include <rmr/rmr.h>
+
+
+ int main( int argc, char** argv ) {
+ void* mrc; // msg router context
+ long long total = 0;
+ rmr_mbuf_t* msg = NULL; // message received
+ int stat_freq = 10; // write stats after reciving this many messages
+ int i;
+ char* listen_port = "4560"; // default to what has become the standard RMR port
+ long long count = 0;
+ long long bad = 0;
+ long long empty = 0;
+
+ if( argc > 1 ) {
+ listen_port = argv[1];
+ }
+ if( argc > 2 ) {
+ stat_freq = atoi( argv[2] );
+ }
+ fprintf( stderr, "<DEMO> listening on port: %s\\n", listen_port );
+ fprintf( stderr, "<DEMO> stats will be reported every %d messages\\n", stat_freq );
+
+ mrc = rmr_init( listen_port, RMR_MAX_RCV_BYTES, RMRFL_NONE );
+ if( mrc == NULL ) {
+ fprintf( stderr, "<DEMO> ABORT: unable to initialise RMr\\n" );
+ exit( 1 );
+ }
+
+ while( ! rmr_ready( mrc ) ) { // wait for RMR to get a route table
+ fprintf( stderr, "<DEMO> waiting for ready\\n" );
+ sleep( 3 );
+ }
+ fprintf( stderr, "<DEMO> rmr now shows ready\\n" );
+
+ while( 1 ) { // receive until killed
+ msg = rmr_rcv_msg( mrc, msg ); // block until one arrives
+
+ if( msg ) {
+ if( msg->state == RMR_OK ) {
+ count++; // nothing fancy, just count
+ } else {
+ bad++;
+ }
+ } else {
+ empty++;
+ }
+
+ if( (count % stat_freq) == 0 ) {
+ fprintf( stderr, "<DEMO> total received: %lld; errors: %lld; empty: %lld\\n",
+ count, bad, empty );
+ }
+ }
+ }
+
+
+
+
+Receive and Send Sample
+-----------------------
+
+The following code snippet receives messages and responds to
+the sender if the message type is odd. The code illustrates
+how the received message may be used to return a message to
+the source. Variable type definitions are omitted for clarity
+and should be obvious.
+
+It should also be noted that things like the message type
+which id returned to the sender (99) is a random value that
+these applications would have agreed on in advance and is
+**not** an RMR definition.
+
+
+::
+
+ mrc = rmr_init( listen_port, MAX_BUF_SZ, RMRFL_NOFLAGS );
+ rmr_set_stimeout( mrc, 1 ); // allow RMR to retry failed sends for ~1ms
+
+ while( ! rmr_ready( mrc ) ) { // we send, therefore we need a route table
+ sleep( 1 );
+ }
+
+ mbuf = NULL; // ensure our buffer pointer is nil for 1st call
+
+ while( TRUE ) {
+ mbuf = rmr_rcv_msg( mrc, mbuf ); // wait for message
+
+ if( mbuf == NULL || mbuf->state != RMR_OK ) {
+ break;
+ }
+
+ if( mbuf->mtype % 2 ) { // respond to odd message types
+ plen = rmr_payload_size( mbuf ); // max size
+
+ // reset necessary fields in msg
+ mbuf->mtype = 99; // response type
+ mbuf->sub_id = RMR_VOID_SUBID; // we turn subid off
+ mbuf->len = snprintf( mbuf->payload, plen, "pong: %s", get_info() );
+
+ mbuf = rmr_rts_msg( mrc, mbuf ); // return to sender
+ if( mbuf == NULL || mbuf->state != RMR_OK ) {
+ fprintf( stderr, "return to sender failed\\n" );
+ }
+ }
+ }
+
+ fprintf( stderr, "abort: receive failure\\n" );
+ rmr_close( mrc );
+
+
+