Man Page: rmr_mt_call

RMR LIBRARY FUNCTIONS

NAME

rmr_mt_call

SYNOPSIS

#include <rmr/rmr.h>

extern rmr_mbuf_t* rmr_mt_call( void* vctx, rmr_mbuf_t* msg, int id, int timeout );

DESCRIPTION

The rmr_mt_call function sends the user application message to a remote endpoint, and waits for a corresponding response message before returning control to the user application. The user application supplies a completed message buffer, as it would for a rmr_send_msg call, but unlike with a send, the buffer returned will have the response from the application that received the message. The thread invoking the rmr_mt_call() will block until a message arrives or until timeout milliseconds has passed; which ever comes first. Using a timeout value of zero (0) will cause the thread to block without a timeout.

The id supplied as the third parameter is an integer in the range of 2 through 255 inclusive. This is a caller defined “thread number” and is used to match the response message with the correct user application thread. If the ID value is not in the proper range, the attempt to make the call will fail.

Messages which are received while waiting for the response are queued on a normal receive queue and will be delivered to the user application with the next invocation of rmr_mt_rcv() or rmr_rvv_msg(). by RMR, and are returned to the user application when rmr_rcv_msg is invoked. These messages are returned in the order received, one per call to rmr_rcv_msg.

The Transaction ID

The user application is responsible for setting the value of the transaction ID field before invoking rmr_mt_call. The transaction ID is a RMR_MAX_XID byte field that is used to match the response message when it arrives. RMR will compare all of the bytes in the field, so the caller must ensure that they are set correctly to avoid missing the response message. The application which returns the response message is also expected to ensure that the return buffer has the matching transaction ID. This can be done transparently if the application uses the rmr_rts_msg() function and does not adjust the transaction ID.

Retries

The send operations in RMR will retry soft send failures until one of three conditions occurs:

  • The message is sent without error

  • The underlying transport reports a hard failure

  • The maximum number of retry loops has been attempted

A retry loop consists of approximately 1000 send attempts without any intervening calls to sleep() or usleep(). The number of retry loops defaults to 1, thus a maximum of 1000 send attempts is performed before returning to the user application. This value can be set at any point after RMR initialisation using the rmr_set_stimeout() function allowing the user application to completely disable retires (set to 0), or to increase the number of retry loops.

Transport Level Blocking

The underlying transport mechanism used to send messages is configured in non-blocking mode. This means that if a message cannot be sent immediately the transport mechanism will not pause with the assumption that the inability to send will clear quickly (within a few milliseconds). This means that when the retry loop is completely disabled (set to 0), that the failure to accept a message for sending by the underlying mechanisms (software or hardware) will be reported immediately to the user application.

It should be noted that depending on the underlying transport mechanism being used, it is extremely likely that retry conditions will happen during normal operations. These are completely out of RMR’s control, and there is nothing that RMR can do to avoid or mitigate these other than by allowing RMR to retry the send operation, and even then it is possible (e.g., during connection reattempts), that a single retry loop is not enough to guarantee a successful send.

RETURN VALUE

The rmr_mt_call function returns a pointer to a message buffer with the state set to reflect the overall state of call processing. If the state is RMR_OK then the buffer contains the response message; otherwise the state indicates the error encountered while attempting to send the message.

If no response message is received when the timeout period has expired, a nil pointer will be returned (NULL).

ERRORS

These values are reflected in the state field of the returned message.

RMR_OK

The call was successful and the message buffer references the response message.

RMR_ERR_BADARG

An argument passed to the function was invalid.

RMR_ERR_CALLFAILED

The call failed and the value of errno, as described below, should be checked for the specific reason.

RMR_ERR_NOENDPT

An endpoint associated with the message type could not be found in the route table.

RMR_ERR_RETRY

The underlying transport mechanism was unable to accept the message for sending. The user application can retry the call operation if appropriate to do so.

The global “variable” errno will be set to one of the following values if the overall call processing was not successful.

ETIMEDOUT

Too many messages were queued before receiving the expected response

ENOBUFS

The queued message ring is full, messages were dropped

EINVAL

A parameter was not valid

EAGAIN

The underlying message system wsa interrupted or the device was busy; the message was not sent, and user application should call this function with the message again.

EXAMPLE

The following code bit shows one way of using the rmr_mt_call function, and illustrates how the transaction ID must be set.

int retries_left = 5;               // max retries on dev not available
static rmr_mbuf_t*  mbuf = NULL;    // response msg
msg_t*  pm;                         // appl message struct (payload)

// get a send buffer and reference the payload
mbuf = rmr_alloc_msg( mr, sizeof( pm->req ) );
pm = (msg_t*) mbuf->payload;

// generate an xaction ID and fill in payload with data and msg type
rmr_bytes2xact( mbuf, xid, RMR_MAX_XID );
snprintf( pm->req, sizeof( pm->req ), "{ \\"req\\": \\"num users\\"}" );
mbuf->mtype = MT_USR_RESP;

msg = rmr_mt_call( mr, msg, my_id, 100 );        // wait up to 100ms
if( ! msg ) {               // probably a timeout and no msg received
    return NULL;            // let errno trickle up
}

if( mbuf->state != RMR_OK ) {
    while( retries_left-- > 0 &&             // loop as long as eagain
           mbuf->state == RMR_ERR_RETRY &&
           (msg = rmr_mt_call( mr, msg )) != NULL &&
           mbuf->state != RMR_OK ) {

        usleep( retry_delay );
    }

    if( mbuf == NULL || mbuf->state != RMR_OK ) {
        rmr_free_msg( mbuf );        // safe if nil
        return NULL;
    }
}

// do something with mbuf

SEE ALSO

rmr_alloc_msg(3), rmr_free_msg(3), rmr_init(3), rmr_mt_rcv(3), rmr_payload_size(3), rmr_send_msg(3), rmr_rcv_msg(3), rmr_rcv_specific(3), rmr_rts_msg(3), rmr_ready(3), rmr_fib(3), rmr_has_str(3), rmr_set_stimeout(3), rmr_tokenise(3), rmr_mk_ring(3), rmr_ring_free(3)