Passing around a context

The previous tutorial used global static variables to make things like the hg_context and hg_class accessible from within callbacks. Since any good developer would ban such a practice, we will revisit the previous tutorial with local variables instead.

Client

On the client side, we encapsulate a context in the client_data_t structure. By passing a pointer to this structure as third argument of HG_Addr_lookup, we can recover it as callback_info->arg in the callback. This lets us carry the hg_class, hg_context, and hello_rpc_id from main to the lookup_callback function.

client.c (show/hide)

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <mercury.h>

typedef struct {
    hg_class_t*   hg_class;
    hg_context_t* hg_context;
    hg_id_t       hello_rpc_id;
    int           completed;
} client_data_t;

hg_return_t lookup_callback(const struct hg_cb_info *callback_info);

int main(int argc, char** argv)
{
    hg_return_t ret;

    if(argc != 3) {
        printf("Usage: %s <protocol> <server_address>\n",argv[0]);
        printf("Example: %s tcp ofi+tcp://1.2.3.4:1234\n",argv[0]);
        exit(0);
    }

    client_data_t client_data = {
        .hg_class     = NULL,
        .hg_context   = NULL,
        .hello_rpc_id = 0,
        .completed    = 0
    };

    char* protocol = argv[1];
    char* server_address = argv[2];

    client_data.hg_class = HG_Init(protocol, HG_FALSE);
    assert(client_data.hg_class != NULL);

    client_data.hg_context = HG_Context_create(client_data.hg_class);
    assert(client_data.hg_context != NULL);

    client_data.hello_rpc_id = HG_Register_name(client_data.hg_class, "hello", NULL, NULL, NULL);

    HG_Registered_disable_response(client_data.hg_class, client_data.hello_rpc_id, HG_TRUE);

    /* We pass a pointer to the client's data as 3rd argument */
    ret = HG_Addr_lookup(client_data.hg_context, lookup_callback, &client_data, server_address, HG_OP_ID_IGNORE);

    while(!client_data.completed)
    {
        unsigned int count;
        do {
            ret = HG_Trigger(client_data.hg_context, 0, 1, &count);
        } while((ret == HG_SUCCESS) && count && !client_data.completed);
        HG_Progress(client_data.hg_context, 100);
    }

    ret = HG_Context_destroy(client_data.hg_context);
    assert(ret == HG_SUCCESS);

    hg_return_t err = HG_Finalize(client_data.hg_class);
    assert(err == HG_SUCCESS);
    return 0;
}

hg_return_t lookup_callback(const struct hg_cb_info *callback_info)
{
    hg_return_t ret;
    assert(callback_info->ret == 0);

    /* Get the client's data */
    client_data_t* client_data = (client_data_t*)(callback_info->arg);

    hg_addr_t addr = callback_info->info.lookup.addr;

    hg_handle_t handle;
    ret = HG_Create(client_data->hg_context, addr, client_data->hello_rpc_id, &handle);
    assert(ret == HG_SUCCESS);

    ret = HG_Forward(handle, NULL, NULL, NULL);
    assert(ret == HG_SUCCESS);

    ret = HG_Destroy(handle);
    assert(ret == HG_SUCCESS);

    client_data->completed = 1;
    return HG_SUCCESS;
}

Server

On the server side, we encapsulate our information in a server_data_t structure. We use HG_Register_data to attach a pointer to the structure to the RPC handler (the fourth argument, NULL, corresponds to a function to be called to free the pointer when the RPC handler is deregistered. Since our structure is on the stack, we do not need to provide any such function).

Within the hello_world handler, we recover the pointer to our server_data_t structure by using HG_Get_info and HG_Registered_data.

server.c (show/hide)

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <mercury.h>

typedef struct {
    hg_class_t*   hg_class;
    hg_context_t* hg_context;
    int           max_rpcs;
    int           num_rpcs;
} server_data_t;

hg_return_t hello_world(hg_handle_t h);

int main(int argc, char** argv)
{
    hg_return_t ret;

    if(argc != 2) {
        printf("Usage: %s <protocol>\n", argv[0]);
        exit(0);
    }

    server_data_t server_data = {
        .hg_class = NULL,
        .hg_context = NULL,
        .max_rpcs = 4,
        .num_rpcs = 0
    };

    server_data.hg_class = HG_Init(argv[1], HG_TRUE);
    assert(server_data.hg_class != NULL);

    char hostname[128];
    hg_size_t hostname_size = 128;
    hg_addr_t self_addr;
    HG_Addr_self(server_data.hg_class, &self_addr);
    HG_Addr_to_string(server_data.hg_class, hostname, &hostname_size, self_addr);
    printf("Server running at address %s\n",hostname);
    HG_Addr_free(server_data.hg_class, self_addr);

    server_data.hg_context = HG_Context_create(server_data.hg_class);
    assert(server_data.hg_context != NULL);

    hg_id_t rpc_id = HG_Register_name(server_data.hg_class, "hello", NULL, NULL, hello_world);

    /* Register data with the RPC handler */
    HG_Register_data(server_data.hg_class, rpc_id, &server_data, NULL);

    HG_Registered_disable_response(server_data.hg_class, rpc_id, HG_TRUE);

    do
    {
        unsigned int count;
        do {
            ret = HG_Trigger(server_data.hg_context, 0, 1, &count);
        } while((ret == HG_SUCCESS) && count);
        HG_Progress(server_data.hg_context, 100);
    } while(server_data.num_rpcs < server_data.max_rpcs);

    ret = HG_Context_destroy(server_data.hg_context);
    assert(ret == HG_SUCCESS);

    ret = HG_Finalize(server_data.hg_class);
    assert(ret == HG_SUCCESS);

    return 0;
}

/* Implementation of the hello_world RPC. */
hg_return_t hello_world(hg_handle_t h)
{
    hg_return_t ret;

    /* Get the hg_class_t instance from the handle */
    const struct hg_info *info = HG_Get_info(h);
    hg_class_t* hg_class = info->hg_class;
    hg_id_t     rpc_id   = info->id;

    /* Get the data attached to the RPC handle */
    server_data_t* server_data = (server_data_t*)HG_Registered_data(hg_class, rpc_id);

    printf("Hello World!\n");
    server_data->num_rpcs += 1;

    ret = HG_Destroy(h);
    assert(ret == HG_SUCCESS);
    return HG_SUCCESS;
}