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;
}