3. LBM Programming Use

The programs below contain the minimum code and supporting material. Their purpose is to verify that the user's build and run-time environments are set up correctly. They also give a basic introduction to the LBM API.

This source code example is provided by 29West for educational and evaluation purposes only.

3.1. Minimal LBM Source Implementation

This is a source code listing of a minimal source (sender) program. You may find it helpful to download the source code (most browsers let you right-click on the link and use the save link target function, or some variation).

/*file: minsrc.c - minimal source (sender) program.
 *
 * Copyright (c) 2005-2007 29West, Inc.  Permission is granted to licensees to use
 * or alter this software for any purpose, including commercial applications,
 * according to the terms laid out in the Software License Agreement.
 */

#include <stdio.h>

#if defined(_MSC_VER)
/* Windows-only includes */
#include <winsock2.h>
#define SLEEP(s) Sleep((s)*1000)
#else
/* Unix-only includes */
#include <stdlib.h>
#include <unistd.h>
#define SLEEP(s) sleep(s)
#endif

#include <lbm.h>

main()
{
    lbm_context_t *ctx;    /* pointer to context object */
    lbm_topic_t *topic;    /* pointer to topic object */
    lbm_src_t *src;        /* pointer to source (sender) object */
    int lbm_failed;        /* return status of lbm functions */

#if defined(_MSC_VER)
    /* windows-specific code */
    WSADATA wsadata;
    int wsStat = WSAStartup(MAKEWORD(2,2), &wsadata);
    if (wsStat != 0) {printf("line %s: %d\n", __LINE__, wsStat); exit(1);}
#endif

    lbm_failed = lbm_context_create(&ctx, NULL, NULL, NULL);   (1)
    if (lbm_failed) {printf("line %s: %s\n", __LINE__, lbm_errmsg()); exit(1);}

    lbm_failed = lbm_src_topic_alloc(&topic, ctx, "Greeting", NULL);   (2)
    if (lbm_failed) {printf("line %s: %s\n", __LINE__, lbm_errmsg()); exit(1);}

    lbm_failed = lbm_src_create(&src, ctx, topic, NULL, NULL, NULL);   (3)
    if (lbm_failed) {printf("line %s: %s\n", __LINE__, lbm_errmsg()); exit(1);}

    SLEEP(3);   (4)

    lbm_failed = lbm_src_send(src, "Hello!", 6, LBM_MSG_FLUSH | LBM_SRC_BLOCK);   (5)
    if (lbm_failed) {printf("line %s: %s\n", __LINE__, lbm_errmsg()); exit(1);}

    SLEEP(2);   (6)

    /* Finished all sending to this topic, delete the source object. */
    lbm_src_delete(src);

    /* Do not need to delete the topic object - LBM keeps track of topic
     * objects and deletes them as-needed.  */

    /* Finished with all LBM functions, delete the context object. */
    lbm_context_delete(ctx);

#if defined(_MSC_VER)
    WSACleanup();
#endif
}  /* main */

3.1.1. Notes:

(1)
Create a context object. A context is an environment in which LBM functions. Note that the first parameter is a pointer to a pointer variable; lbm_context_create writes the pointer to the context object into "ctx". Also, by passing NULL to the context attribute parameter, the default option values are used.
(2)
Allocate a topic object. A topic object is little more than a string (the topic name). During operation, LBM keeps some state information in the topic object as well. The topic is bound to the containing context, and will also be bound to a source object. Note that the first parameter is a pointer to a pointer variable; lbm_src_topic_alloc writes the pointer to the topic object into "topic". Also, by passing NULL to the source topic attribute, the default option values are used. The string "Greeting" is the topic string.
(3)
Create the source object. A source object is used to send messages. It must be bound to a topic. Note that the first parameter is a pointer to a pointer variable; lbm_src_create writes the pointer to the source object to into "src". Use of the third and fourth parameters is optional but recommended in a production program - some source events can be important to the application. The last parameter is an optional event queue (not used in this example).
(4)
Need to wait for receivers to find us. See LBM application note: doc/AppNotes/delay-before-sending.html for details.
(5)
Send a message to the "Greeting" topic. The flags make sure the call to lbm_src_send doesn't return until the message is sent.
(6)
Even though the message is sent, some transports may need a bit of time to request re-transmission. If the above lbm_src_send call didn't include the flags, some time might also be needed to empty the batching buffer.

3.2. Minimal LBM Receiver Implementation

This is a source code listing of a minimal receiver program. You may find it helpful to download the source code (most browsers let you right-click on the link and use the save link target function, or some variation).

/*file: minrcv.c - minimal receiver program.
 *
 * Copyright (c) 2005-2007 29West, Inc.  Permission is granted to licensees to use
 * or alter this software for any purpose, including commercial applications,
 * according to the terms laid out in the Software License Agreement.
 */

#include <stdio.h>

#if defined(_MSC_VER)
/* Windows-only includes */
#include <winsock2.h>
#define SLEEP(s) Sleep((s)*1000)
#else
/* Unix-only includes */
#include <stdlib.h>
#include <unistd.h>
#define SLEEP(s) sleep(s)
#endif

#include <lbm.h>

/*
 * A global variable is used to communicate from the receiver callback to
 * the main application thread.
 */
int msgs_rcvd = 0;

int app_rcv_callback(lbm_rcv_t *rcv, lbm_msg_t *msg, void *clientd)   (1)
{
    /* There are several different events that can cause the receiver callback
     * to be called.  Decode the event that caused this.  */
    switch (msg->type) {
        case LBM_MSG_DATA:    /* a received message */
            printf("Received %d bytes on topic %s: '%.*s'\n",   (2)
                   msg->len, msg->topic_name, msg->len, msg->data);

            /* Tell main thread that we've received our message. */
            ++ msgs_rcvd;
            break;

        default:    /* unexpected receiver event */
            printf("line %s: %d\n", __LINE__, msg->type);
            exit(1);
    }  /* switch msg->type */

    return 0;
}  /* app_rcv_callback */


main()
{
    lbm_context_t *ctx;    /* pointer to context object */
    lbm_topic_t *topic;    /* pointer to topic object */
    lbm_rcv_t *rcv;        /* pointer to receiver object */
    int lbm_failed;        /* return status of lbm functions */

#if defined(_MSC_VER)
    /* windows-specific code */
    WSADATA wsadata;
    int wsStat = WSAStartup(MAKEWORD(2,2), &wsadata);
    if (wsStat != 0) {printf("line %s: %d\n", __LINE__, wsStat); exit(1);}
#endif

    lbm_failed = lbm_context_create(&ctx, NULL, NULL, NULL);   (3)
    if (lbm_failed) {printf("line %s: %s\n", __LINE__, lbm_errmsg()); exit(1);}

    lbm_failed = lbm_rcv_topic_lookup(&topic, ctx, "Greeting", NULL);   (4)
    if (lbm_failed) {printf("line %s: %s\n", __LINE__, lbm_errmsg()); exit(1);}

    lbm_failed = lbm_rcv_create(&rcv, ctx, topic, app_rcv_callback, NULL, NULL);   (5)
    if (lbm_failed) {printf("line %s: %s\n", __LINE__, lbm_errmsg()); exit(1);}

    while (msgs_rcvd == 0)
        SLEEP(1);

    /* Finished all receiving from this topic, delete the receiver object. */
    lbm_rcv_delete(rcv);

    /* Do not need to delete the topic object - LBM keeps track of topic
     * objects and deletes them as-needed.  */

    /* Finished with all LBM functions, delete the context object. */
    lbm_context_delete(ctx);

#if defined(_MSC_VER)
    WSACleanup();
#endif
}  /* main */

3.2.1. Notes:

(1)
LBM passes received messages to the application by means of a callback. I.e. the LBM context thread reads the network socket, performs its higher-level protocol functions, and then calls an application-level function that was set up during initialization. This callback function has some severe limitations placed upon it. It must execute very quickly; any potentially blocking calls it might make will interfere with the proper execution of the LBM context thread. One common desire is for the receive function to send an LBM message (via lbm_src_send), however this has the potential to produce a deadlock condition. If it is desired for the receive callback function to call LBM or other potentially blocking functions, it is strongly advised to make use of an event queue, which causes the callback to be executed from an application thread. See the example tool lbmrcvq.c for an example of using a receiver event queue.
(2)
Note - printf can block, which is normally a bad idea for a callback (unless an event queue is being used). However, for this minimal application, only one message is expected.
(3)
Create a context object. A context is an environment in which LBM functions. Note that the first parameter is a pointer to a pointer variable; lbm_context_create writes the pointer to the context object into "ctx". Also, by passing NULL to the context attribute parameter, the default option values are used.
(4)
Lookup a topic object. A topic object is little more than a string (the topic name). During operation, LBM keeps some state information in the topic object as well. The topic is bound to the containing context, and will also be bound to a receiver object. Note that the first parameter is a pointer to a pointer variable; lbm_rcv_topic_lookup writes the pointer to the topic object into "topic". Also, by passing NULL to the source topic attribute, the default option values are used. The string "Greeting" is the topic string.
(5)
Create the receiver object and bind it to a topic. Note that the first parameter is a pointer to a pointer variable; lbm_rcv_create writes the pointer to the source object to into "rcv". The second and third parameters are the function and application data pointers. When a message is received, the function is called with the data pointer passed in as its last parameter. The last parameter is an optional event queue (not used in this example).

Copyright 2004 - 2007 29West, Inc. -- 29West Confidential