UM C API  6.16
C Structure Usage Patterns

As with most rich C APIs, data structures are used extensively to maintain state information and to pass information into and out of the API.

UM uses two general coding conventions related to data structures:


UM Object Handles (Pointers)

A UM object (context, source, receiver, etc) is represented as a pointer to an internal UM structure. The structure is "opaque"; that is the fields of which are not available to the application.

lbm_context_t *ctx_object; /* Handle (pointer) to context object. */

To create an object, you call the appropriate API, passing it a pointer to an object handle (pointer variable). For example:

err = lbm_context_create(&ctx_object, ...);

The "&ctx_object" indicates that it is an output parameter to the API. The lbm_context_create() API will allocate the internal structure for the object and put the address into the "ctx_object" variable.

In other (not-create) APIs that take an object, you don't pass it a pointer to the variable, you just pass the variable itself. For example:

err = lbm_context_delete(ctx_object);

Note that at no time does the application allocate space for the object data structures. UM allocates and owns the memory. Typically the application is responsible to call an object deletion function (the Topic Object is an exception to this).


Application-Visible UM Structures

There are many data structures defined in the UM header files (lbm.h, lbmaux.h, lbmmon.h, ... see File List for the full list). These structures are used either to pass data into UM, or to return data back to the application.

There are two general use cases by which application-visible structures are passed between UM and the application:


Application Calling UM API

In these cases, the application generally declares a structure in its memory, and passes the address to a UM API. UM will access the structure (either reading it or writing to it). When the API returns, UM does not maintain a reference to the passed-in structure, so the application is free to re-use or deallocate the space.

For example, if sending a message with message properties:

...
memset(&ex_info, 0, sizeof(ex_info));
ex_info.properties = prop_obj;
err = lbm_src_send_ex(src, my_buffer, my_len, LBM_MSG_FLUSH, &ex_info);

When lbm_src_send_ex() returns, UM has copied the contents of ex_info and prop_obj. UM does not maintain a reference to the passed-in structures, so the application is free to re-use or deallocate the space.

There are a few general use cases of data structure passing when calling UM APIs that deserve additional explanation:


Statistics Retrieval

A class of application-visible UM structures are used by the UM statistics reporting APIs. For example, when an application self-monitors, it might periodically call lbm_context_retrieve_stats():

...
err = lbm_context_retrieve_stats(my_ctx, &ctx_stats);

As usual, your application declares the structure in its memory and passes the address to the UM API. In this case it is an output, and UM will fill in the structure. As usual, UM will not maintain a reference to the passed-in structure. Your application is free to re-use or deallocate the space.

Contrast this with the receive-side lbmmon API which is callback based. See lbmmon Callback Example


Configuration Option Structures

A class of application-visible UM structures are used by the UM Configuration APIs. For example, the configuration option source_notification_function (receiver) uses the lbm_rcv_src_notification_func_t_stct structure to pass application create and delete function pointers and an application client data pointer.

lbm_rcv_src_notification_func_t src_notif_functs; /* Not a pointer. */
...
src_notif_functs.create_func = my_src_create_funct;
src_notif_functs.delete_func = my_src_create_funct;
src_notif_functs.clientd = my_state_pointer;
err = lbm_rcv_topic_attr_setopt(rcv_attr, "source_notification_function",
&src_notif_functs, sizeof(src_notif_functs));

Passing the size of the structure allows the API to perform a partial sanity check on the passed-in structure to make sure it is of the expected size.

When the _setopt() function returns, UM has copied the contents of src_notif_functs. UM does not maintain a reference to the passed-in structures, so the application is free to re-use or deallocate the space.


UM Calling Application Callback

UM typically delivers events to the application by callback. Information related to the event is passed to the application's callback function via structure pointers.

The "lbmmon

For example, when you create a receiver object, you pass it the address of your application's receiver callback function.

int rcv_handle_msg(lbm_rcv_t *rcv, lbm_msg_t *msg, void *clientd)
{
switch (msg->type) {
...
}
return 0
}
...
err = lbm_rcv_create(&rcv, ctx, topic, rcv_handle_msg, NULL, NULL);

When a message is received (or any other receiver event needs to be delivered), UM allocates an lbm_msg_t structure and passes a pointer to it to the application's callback lbm_rcv_create(). The application can then de-reference the structure pointer and access the fields. However, the application must not retain a reference to the passed-in structure after the callback returns. UM is free to re-use or deallocate the structure.

As another example of callback, the lbmmon library delivers monitoring data to the monitoring application by callback. For example:

void my_ctx_stats_callback(void *attr, lbm_context_stats_t *ctx_stats,
void *clientd)
{
...
}

In this case, the lbmmon library declares the structure internally and passes the application a pointer to it. The application must not retain a reference to that structure after the callback returns. UM is free to re-use or deallocate the structure.

Contrast this with an application retrieving its own statistics. See Statistics Retrieval.