Communication Functions


Introduction

Functions are provided for buffer management, sending remote service requests, and handler management.

Handlers can be either threaded or non-threaded. A threaded handler executes in a thread created specifically for it, and has no restrictions on what it may do. A non-threaded handler executes in an existing thread, and has some restrictions on what it may do. The following are noteworthy items regarding handler design and behavior:


nexus_sizeof_DATATYPE()

int nexus_sizeof_DATATYPE(int count)

Return the size in bytes required to encode count items of data type DATATYPE by nexus_put_DATATYPE() or nexus_user_put_DATATYPE().

If multiple calls to nexus_sizeof_DATATYPE() are being used to determine the size of a buffer to which multiple puts will be applied, the order of the calls to nexus_sizeof_DATATYPE() does not matter. In addition, nexus_sizeof_DATATYPE(count) is always equal to ( count * nexus_sizeof_DATATYPE(1)).

Valid DATATYPE values are float, double, short, u_short, int, u_int, long, u_long, char, u_char, and byte.


nexus_sizeof_startpoint()

int nexus_sizeof_startpoint(nexus_startpoint_t *sp_array,
                            int count)

Return the size in bytes required to put the first count elements of the startpoint array, sp_array.

The size of a startpoint varies, depending upon the context to which it points and the options which it supports; thus the extra sp_array argument that the normal nexus_sizeof_DATATYPE() subroutine does not need. Startpoints that point to the same context or even the same endpoint may not have the same size.


nexus_buffer_init()

int nexus_buffer_init(nexus_buffer_t *buffer,
                      int buffer_size,
                      int num_direct_puts)

Initialize the message buffer, buffer, with an initial size of buffer_size, and with the ability to have num_direct_puts nexus_direct_put_DATATYPE() calls applied to it.

Return zero if successful, or non-zero otherwise.


nexus_buffer_reset()

int nexus_buffer_reset(nexus_buffer_t *buffer,
                       int buffer_size,
                       int num_direct_puts)

Reset the message buffer, buffer, which was previously initialized with nexus_buffer_init(), to its initial state, with an initial size of buffer_size, and with the ability to have num_direct_puts nexus_direct_put_DATATYPE() calls applied to it.

Return zero if successful, or non-zero otherwise.


nexus_send_rsr()

int nexus_send_rsr(nexus_buffer_t *buffer
                   nexus_startpoint_t *startpoint,
                   int handler_id,
                   nexus_bool_t destroy_buffer
                   nexus_bool_t called_from_nonthread_handler)

Send a remote service request to the endpoint to which startpoint is bound. The RSR will invoke the handler specified by handler_id, which is defined locally to the endpoint. The handler will be invoked in the context which contains the endpoint. It will be passed three arguments: the endpoint to which startpoint is bound, a pointer to a nexus_buffer_t with the same contents as buffer, and a boolean flag telling if the handler is non-threaded. If destroy_buffer is NEXUS_TRUE, then the buffer will be destroyed automatically after sending the messages. Otherwise, if destroy_buffer is NEXUS_FALSE, buffer can continue to be used after this call, and must be destroyed by a subsequent call to nexus_send_rsr() or nexus_buffer_destroy(). The called_from_nonthread_handler argument should be NEXUS_TRUE if this nexus_send_rsr() is called from a non-threaded handler, otherwise NEXUS_FALSE.

A nexus_buffer_t that has been passed to a handler may be forwarded by passing it to this subroutine.

This function returns zero upon successful completion, or non-zero otherwise.

Only one constraint is placed on when handlers for RSRs are executed. Two non-threaded RSR handlers which are both sent to the same context from the same context are executed in sequence in the same order as they were sent. Therefore, if two threads each send a RSR to the same context, they can guarantee the order in which those requests will be handled by guaranteeing the order in which they are sent.


nexus_buffer_destroy()

int nexus_buffer_destroy(nexus_buffer_t *buffer)

Destroy the buffer that was:

  1. initialized by nexus_buffer_init(), but never sent by nexus_send_rsr(), or
  2. not destroyed by nexus_send_rsr(), or
  3. saved by calling nexus_buffer_save(), or
  4. passed to a threaded handler.

A nexus_buffer_t which is passed to a non-threaded handler is automatically freed upon completion of the handler, if it has not been explicitly freed by calling this subroutine.

Options #1 and #2 allow an RSR to be canceled. That is, the RSR does not occur until nexus_send_rsr() is called. If you start packing a buffer but then decide not to send it, the buffer can be freed by destroying it without calling the nexus_send_rsr().

Return zero if successful, or non-zero otherwise.


nexus_check_buffer_size()

int nexus_check_buffer_size(nexus_buffer_t *buffer,
                            int size_needed,
                            int size_increment,
                            int num_direct_puts_needed,
                            int num_direct_puts_increment)

Check the message buffer to see if has at least size_needed bytes remaining for subsequent nexus_put_DATATYPE() calls, and to see if it has room for at least num_direct_puts_needed calls to nexus_direct_put_DATATYPE(). If there are not enough bytes remaining, the buffer's size is increased by size_increment byte increments until there are size_needed bytes available in the buffer. Likewise, if there is not enough room for the direct puts, space is made available in num_direct_puts_increment increments until there is adequate room for the direct puts.

If the buffer is large enough, or can be resized to be large enough, then this function returns zero. If the buffer is not large enough, but cannot be resized to be large enough (i.e. malloc fails), then this function returns -1. However, if size_increment and num_direct_puts_increment are both zero, and the buffer is not large enough to accommodate size_needed and num_direct_puts_needed, then this function returns -2.

The size_needed argument should be calculated by using
nexus_sizeof_DATATYPE().


nexus_put_DATATYPE()

void nexus_put_DATATYPE(nexus_buffer_t *buffer,
                        DATATYPE *data,
                        int count)

Copy count elements from data, of the given DATATYPE, into buffer. The contents of data may be changed immediately after this call completes. Doing so will not change the contents of buffer.

The behavior of this subroutine is undefined is there if not enough room in buffer for the data.

Valid DATATYPE values are float, double, short, u_short, int, u_int, long, u_long, char, u_char, and byte.


nexus_put_startpoint_transfer()

void nexus_put_startpoint_transfer(nexus_buffer_t *buffer,
                                   nexus_startpoint_t *sp,
                                   int count)

Copy count startpoints sp into buffer, and destroy these startpoints. The contents of sp may be changed immediately after this call completes. Doing so will not change the contents of buffer.

The behavior of this subroutine is undefined is there if not enough room in buffer for the data.


nexus_direct_put_DATATYPE()

void nexus_direct_put_DATATYPE(nexus_buffer_t *buffer,
                               DATATYPE *data,
                               int count)

Add count elements from data, of the given DATATYPE, to buffer as a direct component. The contents of data must not be changed until after the subsequent nexus_send_rsr() is called. Doing so may cause the contents of buffer to change in some unexpected (and undefined) way.

Valid DATATYPE values are float, double, short, u_short, int, u_int, long, u_long, char, u_char, and byte, but not startpoint.

The behavior of this subroutine is undefined is there if not enough room in buffer for the direct data.

Efficiency note: Using the nexus_direct_*() calls allows Nexus to optimize away buffer copies on both the sending are receiving ends in many situations. However, using these calls can be thought of as hints to Nexus; Nexus may optimize away copies, but it is not required to do so. In fact, there are circumstances (for example, if count is small) when it is cheaper to copy the data than to go through the extra work to avoid the copies.


nexus_check_get_DATATYPE()

int nexus_check_get_DATATYPE(nexus_buffer_t *buffer)

Return the number of elements of the specified DATATYPE that exist in buffer before the next direct component or the end of the buffer.

Valid DATATYPE values are float, double, short, u_short, int, u_int, long, u_long, char, u_char, byte, and startpoint.


nexus_get_DATATYPE()

void nexus_get_DATATYPE(nexus_buffer_t *buffer,
                        DATATYPE *dest,
                        int count)

Copy count elements from the message buffer, buffer to location data. Convert the data format if necessary.

The behavior of this subroutine is undefined if count elements do not remain either before the end of the buffer or before the next direct component of the buffer.

Valid DATATYPE values are float, double, short, u_short, int, u_int, long, u_long, char, u_char, byte, and startpint.


nexus_check_direct_get_DATATYPE()

int nexus_check_direct_get_DATATYPE(nexus_buffer_t *buffer)

Return the number of elements of the specified DATATYPE that exist in the next direct component of buffer. All non-direct data in the buffer before the next direct component must have been retrieved by previous calls to nexus_get_DATATYPE() in order to use this function.

If there are no more direct components in the buffer, -1 is returned.

Valid DATATYPE values are float, double, short, u_short, int, u_int, long, u_long, char, u_char, and byte, but not startpoint.


nexus_direct_get_DATATYPE()

int nexus_direct_get_DATATYPE(nexus_buffer_t *buffer,
                              DATATYPE *dest,
                              int count)

Retrieve count elements from the message buffer, buffer, to location data. Convert the data format if necessary.

Calls to these subroutines must precisely match the calls to
nexus_direct_put_DATATYPE() that were used to construct the buffer.

Return zero if successful, or non-zero otherwise.

Valid DATATYPE values are float, double, short, u_short, int, u_int, long, u_long, char, u_char, and byte, but not startpoint.


nexus_buffer_save()

int nexus_buffer_save(nexus_buffer_t *buffer)

The nexus_buffer_t that is passed to a non-threaded handler cannot have a lifetime that extends past the completion of that handler, unless it is saved by calling this subroutine. This subroutine must only be called from within a non-threaded handler with the nexus_buffer_t that was passed to that handler.

It is legal to save a buffer from which some data has already be retrieved using nexus_get_DATATYPE() and nexus_direct_get_DATATYPE() calls. Subsequent calls to these subroutines will continue retrieving data where the previous calls left off.

Return zero if successful, or non-zero otherwise.


nexus_buffer_save_linearly()

int nexus_buffer_save_linearly(nexus_buffer_t *buffer)

This subroutine is like nexus_buffer_save(), except that it linearizes the non-direct and direct components of the buffer into a single non-direct component. All data must be subsequently retrieved from the buffer using the nexus_get_DATATYPE() calls, including those parts that were formerly direct components.

Efficiency note: This subroutine may malloc a new buffer that is large enough to hold both the non-direct and direct components, and copies the non-direct components into this new buffer. Therefore, this subroutine may performs copies which can be avoided by using nexus_buffer_save(). However, if the nexus_buffer_t has no direct components, then this subroutine will not malloc or copy anything.

Return zero if successful, or non-zero otherwise.