Commit 03c800f0 authored by Shane Snyder's avatar Shane Snyder
Browse files

refactor dping path for new swim changes

parent 5372d4df
...@@ -125,15 +125,13 @@ int ssg_finalize() ...@@ -125,15 +125,13 @@ int ssg_finalize()
static int ssg_get_swim_dping_target( static int ssg_get_swim_dping_target(
void *group_data, void *group_data,
hg_addr_t *target_addr, swim_dping_target_info_t *target_info);
swim_member_state_t *target_swim_ms);
static void ssg_gen_rand_member_list( static void ssg_gen_rand_member_list(
ssg_group_t *g); ssg_group_t *g);
static int ssg_get_swim_dping_target( static int ssg_get_swim_dping_target(
void *group_data, void *group_data,
hg_addr_t *target_addr, swim_dping_target_info_t *target_info)
swim_member_state_t *target_swim_ms)
{ {
ssg_group_t *g = (ssg_group_t *)group_data; ssg_group_t *g = (ssg_group_t *)group_data;
ssg_member_state_t *target_ms; ssg_member_state_t *target_ms;
...@@ -147,10 +145,9 @@ static int ssg_get_swim_dping_target( ...@@ -147,10 +145,9 @@ static int ssg_get_swim_dping_target(
/* pull random member off head of list and return addr */ /* pull random member off head of list and return addr */
target_ms = g->member_list; target_ms = g->member_list;
LL_DELETE(g->member_list, target_ms); LL_DELETE(g->member_list, target_ms);
*target_addr = target_ms->addr; target_info->id = (swim_member_id_t)target_ms->id;
*target_swim_ms = target_ms->swim_state; target_info->addr = target_ms->addr;
target_info->swim_state = target_ms->swim_state;
printf("%lu: pinging %lu\n", g->self_id, target_ms->id);
return 0; return 0;
} }
...@@ -236,7 +233,8 @@ ssg_group_id_t ssg_group_create( ...@@ -236,7 +233,8 @@ ssg_group_id_t ssg_group_create(
swim_group_mgmt_callbacks_t swim_callbacks = { swim_group_mgmt_callbacks_t swim_callbacks = {
.get_dping_target = &ssg_get_swim_dping_target, .get_dping_target = &ssg_get_swim_dping_target,
}; };
g->swim_ctx = swim_init(ssg_inst->mid, g, swim_callbacks, 1); g->swim_ctx = swim_init(ssg_inst->mid, g, (swim_member_id_t)g->self_id,
swim_callbacks, 1);
if (g->swim_ctx == NULL) goto fini; if (g->swim_ctx == NULL) goto fini;
/* everything successful -- set the output group identifier, which is just /* everything successful -- set the output group identifier, which is just
......
...@@ -23,12 +23,31 @@ extern "C" { ...@@ -23,12 +23,31 @@ extern "C" {
#define SWIM_MAX_PIGGYBACK_ENTRIES 8 #define SWIM_MAX_PIGGYBACK_ENTRIES 8
#define SWIM_MAX_PIGGYBACK_TX_COUNT 50 #define SWIM_MAX_PIGGYBACK_TX_COUNT 50
/* debug printing macro for SSG */
#ifdef DEBUG
#define SWIM_DEBUG(__swim_ctx, __fmt, ...) do { \
double __now = ABT_get_wtime(); \
fprintf(stdout, "%.6lf <%020"PRIu64">: SWIM: " __fmt, __now, \
__swim_ctx->self_id, ## __VA_ARGS__); \
fflush(stdout); \
} while(0)
#else
#define SWIM_DEBUG(__swim_ctx, __fmt, ...) do { \
} while(0)
#endif
/* internal swim context implementation */ /* internal swim context implementation */
struct swim_context struct swim_context
{ {
margo_instance_id mid; margo_instance_id mid;
/* void pointer to user group data */ /* void pointer to user group data */
void *group_data; void *group_data;
/* XXX other state */
swim_member_id_t self_id;
swim_member_inc_nr_t self_inc_nr;
swim_dping_target_info_t dping_target_info;
int dping_target_acked;
double dping_timeout;
/* XXX group mgmt callbacks */ /* XXX group mgmt callbacks */
swim_group_mgmt_callbacks_t swim_callbacks; swim_group_mgmt_callbacks_t swim_callbacks;
/* argobots pool for launching SWIM threads */ /* argobots pool for launching SWIM threads */
...@@ -41,10 +60,6 @@ struct swim_context ...@@ -41,10 +60,6 @@ struct swim_context
int prot_subgroup_sz; int prot_subgroup_sz;
/* SWIM internal state */ /* SWIM internal state */
int shutdown_flag; int shutdown_flag;
hg_addr_t dping_target_addr;
swim_member_state_t dping_target_state;
int dping_target_acked;
double dping_timeout;
hg_addr_t iping_subgroup_addrs[SWIM_MAX_SUBGROUP_SIZE]; hg_addr_t iping_subgroup_addrs[SWIM_MAX_SUBGROUP_SIZE];
#if 0 #if 0
/* current membership state */ /* current membership state */
...@@ -62,12 +77,14 @@ struct swim_member_update ...@@ -62,12 +77,14 @@ struct swim_member_update
swim_member_status_t status; swim_member_status_t status;
swim_member_inc_nr_t inc_nr; swim_member_inc_nr_t inc_nr;
}; };
#endif
/* SWIM ping function prototypes */ /* SWIM ping function prototypes */
void swim_register_ping_rpcs( void swim_register_ping_rpcs(
ssg_group_t * g); swim_context_t * swim_ctx);
void swim_dping_send_ult( void swim_dping_send_ult(
void * t_arg); void * t_arg);
#if 0
void swim_iping_send_ult( void swim_iping_send_ult(
void * t_arg); void * t_arg);
......
...@@ -3,8 +3,6 @@ ...@@ -3,8 +3,6 @@
* *
* See COPYRIGHT in top-level directory. * See COPYRIGHT in top-level directory.
*/ */
#include <ssg-config.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
...@@ -13,32 +11,31 @@ ...@@ -13,32 +11,31 @@
#include <mercury.h> #include <mercury.h>
#include <margo.h> #include <margo.h>
#include "ssg.h"
#include "ssg-internal.h"
#include "swim-fd.h" #include "swim-fd.h"
#include "swim-fd-internal.h" #include "swim-fd-internal.h"
#if 0 /* NOTE: keep these defines in sync with defs in swim.h */
#define hg_proc_swim_member_id_t hg_proc_uint64_t
/* NOTE these defines must be kept in sync with defs in #define hg_proc_swim_member_inc_nr_t hg_proc_uint32_t
* ssg.h & swim-internal.h #define hg_proc_swim_member_status_t hg_proc_uint8_t
*/
#define hg_proc_swim_member_status_t hg_proc_uint8_t
#define hg_proc_swim_member_inc_nr_t hg_proc_uint32_t
#if 0
MERCURY_GEN_STRUCT_PROC(swim_member_update_t, \ MERCURY_GEN_STRUCT_PROC(swim_member_update_t, \
((ssg_member_id_t) (id)) \ ((ssg_member_id_t) (id)) \
((swim_member_status_t) (status)) \ ((swim_member_status_t) (status)) \
((swim_member_inc_nr_t) (inc_nr))); ((swim_member_inc_nr_t) (inc_nr)));
#endif
/* a swim message is the membership information piggybacked (gossiped) /* a swim message is the membership information piggybacked (gossiped)
* on the ping and ack messages generated by the protocol * on the ping and ack messages generated by the protocol
*/ */
typedef struct swim_message_s typedef struct swim_message_s
{ {
ssg_member_id_t source_id; swim_member_id_t source_id;
swim_member_inc_nr_t source_inc_nr; swim_member_inc_nr_t source_inc_nr;
#if 0
swim_member_update_t pb_buf[SWIM_MAX_PIGGYBACK_ENTRIES]; //TODO: can we do dynamic array instead? swim_member_update_t pb_buf[SWIM_MAX_PIGGYBACK_ENTRIES]; //TODO: can we do dynamic array instead?
#endif
} swim_message_t; } swim_message_t;
/* HG encode/decode routines for SWIM RPCs */ /* HG encode/decode routines for SWIM RPCs */
...@@ -47,42 +44,52 @@ static hg_return_t hg_proc_swim_message_t( ...@@ -47,42 +44,52 @@ static hg_return_t hg_proc_swim_message_t(
MERCURY_GEN_PROC(swim_dping_req_t, \ MERCURY_GEN_PROC(swim_dping_req_t, \
((swim_message_t) (msg))); ((swim_message_t) (msg)));
MERCURY_GEN_PROC(swim_dping_resp_t, \ MERCURY_GEN_PROC(swim_dping_resp_t, \
((swim_message_t) (msg))); ((swim_message_t) (msg)));
#if 0
MERCURY_GEN_PROC(swim_iping_req_t, \ MERCURY_GEN_PROC(swim_iping_req_t, \
((ssg_member_id_t) (target_id)) \ ((ssg_member_id_t) (target_id)) \
((swim_message_t) (msg))); ((swim_message_t) (msg)));
MERCURY_GEN_PROC(swim_iping_resp_t, \ MERCURY_GEN_PROC(swim_iping_resp_t, \
((swim_message_t) (msg))); ((swim_message_t) (msg)));
#endif
/* SWIM message pack/unpack prototypes */ /* SWIM message pack/unpack prototypes */
static void swim_pack_message( static void swim_pack_message(
ssg_group_t *g, swim_message_t *msg); swim_context_t *swim_ctx, swim_message_t *msg);
static void swim_unpack_message( static void swim_unpack_message(
ssg_group_t *g, swim_message_t *msg); swim_context_t *swim_ctx, swim_message_t *msg);
DECLARE_MARGO_RPC_HANDLER(swim_dping_recv_ult) DECLARE_MARGO_RPC_HANDLER(swim_dping_recv_ult)
#if 0
DECLARE_MARGO_RPC_HANDLER(swim_iping_recv_ult) DECLARE_MARGO_RPC_HANDLER(swim_iping_recv_ult)
#endif
static hg_id_t swim_dping_rpc_id; static hg_id_t swim_dping_rpc_id;
#if 0
static hg_id_t swim_iping_rpc_id; static hg_id_t swim_iping_rpc_id;
#endif
void swim_register_ping_rpcs( void swim_register_ping_rpcs(
ssg_group_t *g) swim_context_t *swim_ctx)
{ {
assert(swim_ctx != NULL);
/* register RPC handlers for SWIM pings */ /* register RPC handlers for SWIM pings */
swim_dping_rpc_id = MARGO_REGISTER(ssg_inst->mid, "swim_dping", swim_dping_req_t, swim_dping_rpc_id = MARGO_REGISTER(swim_ctx->mid, "swim_dping", swim_dping_req_t,
swim_dping_resp_t, swim_dping_recv_ult); swim_dping_resp_t, swim_dping_recv_ult);
#if 0
swim_iping_rpc_id = MARGO_REGISTER(ssg_inst->mid, "swim_iping", swim_iping_req_t, swim_iping_rpc_id = MARGO_REGISTER(ssg_inst->mid, "swim_iping", swim_iping_req_t,
swim_iping_resp_t, swim_iping_recv_ult); swim_iping_resp_t, swim_iping_recv_ult);
#endif
/* register swim context data structure with each RPC type */ /* register swim context data structure with each RPC type */
/* XXX: this won't work for multiple groups ... */ /* XXX: this won't work for multiple groups ... */
margo_register_data(ssg_inst->mid, swim_dping_rpc_id, g, NULL); margo_register_data(swim_ctx->mid, swim_dping_rpc_id, swim_ctx, NULL);
#if 0
margo_register_data(ssg_inst->mid, swim_iping_rpc_id, g, NULL); margo_register_data(ssg_inst->mid, swim_iping_rpc_id, g, NULL);
#endif
return; return;
} }
...@@ -91,80 +98,76 @@ void swim_register_ping_rpcs( ...@@ -91,80 +98,76 @@ void swim_register_ping_rpcs(
* SWIM direct pings * * SWIM direct pings *
********************************/ ********************************/
/* XXX: just accept a target info? */
static int swim_send_dping( static int swim_send_dping(
ssg_group_t *g, ssg_member_id_t target); swim_context_t *swim_ctx, swim_member_id_t target_id,
hg_addr_t target_addr);
void swim_dping_send_ult( void swim_dping_send_ult(
void *t_arg) void *t_arg)
{ {
ssg_group_t *g = (ssg_group_t *)t_arg; swim_context_t *swim_ctx = (swim_context_t *)t_arg;
swim_context_t *swim_ctx; swim_member_id_t dping_target_id;
ssg_member_id_t target;
int ret; int ret;
assert(g != NULL);
swim_ctx = (swim_context_t *)g->fd_ctx;
assert(swim_ctx != NULL); assert(swim_ctx != NULL);
target = swim_ctx->ping_target; dping_target_id = swim_ctx->dping_target_info.id;
ret = swim_send_dping(g, target); ret = swim_send_dping(swim_ctx, dping_target_id, swim_ctx->dping_target_info.addr);
if (ret == 0) if (ret == 0)
{ {
/* mark this dping req as acked, double checking to make /* mark this dping req as acked, double checking to make
* sure we aren't inadvertently ack'ing a ping request * sure we aren't inadvertently ack'ing a ping request
* for a more recent tick of the protocol * for a more recent tick of the protocol
*/ */
if(swim_ctx->ping_target == target) /* XXX: maybe just use a sequence number? this isn't technically right */
swim_ctx->ping_target_acked = 1; if(swim_ctx->dping_target_info.id == dping_target_id)
swim_ctx->dping_target_acked = 1;
} }
return; return;
} }
static int swim_send_dping( static int swim_send_dping(
ssg_group_t *g, ssg_member_id_t target) swim_context_t *swim_ctx, swim_member_id_t target_id,
hg_addr_t target_addr)
{ {
swim_context_t *swim_ctx = (swim_context_t *)g->fd_ctx;
hg_addr_t target_addr = HG_ADDR_NULL;
hg_handle_t handle; hg_handle_t handle;
swim_dping_req_t dping_req; swim_dping_req_t dping_req;
swim_dping_resp_t dping_resp; swim_dping_resp_t dping_resp;
hg_return_t hret; hg_return_t hret;
int ret = -1; int ret = -1;
target_addr = g->view.member_states[target].addr; assert(swim_ctx != NULL);
if(target_addr == HG_ADDR_NULL)
return(ret);
hret = margo_create(ssg_inst->mid, target_addr, swim_dping_rpc_id, &handle); hret = margo_create(swim_ctx->mid, target_addr, swim_dping_rpc_id, &handle);
if(hret != HG_SUCCESS) if(hret != HG_SUCCESS)
return(ret); return(ret);
SSG_DEBUG(g, "SWIM: send dping req to %d\n", (int)target); SWIM_DEBUG(swim_ctx, "send dping req to %lu\n", target_id);
/* fill the direct ping request with current membership state */ /* fill the direct ping request with current membership state */
swim_pack_message(g, &(dping_req.msg)); swim_pack_message(swim_ctx, &(dping_req.msg));
/* send a direct ping that expires at the end of the protocol period */ /* send a direct ping that expires at the end of the protocol period */
hret = margo_forward_timed(handle, &dping_req, hret = margo_forward_timed(handle, &dping_req, swim_ctx->prot_period_len);
swim_ctx->prot_period_len);
if (hret == HG_SUCCESS) if (hret == HG_SUCCESS)
{ {
hret = margo_get_output(handle, &dping_resp); hret = margo_get_output(handle, &dping_resp);
if(hret != HG_SUCCESS) goto fini; if(hret != HG_SUCCESS) goto fini;
SSG_DEBUG(g, "SWIM: recv dping ack from %d\n", (int)dping_resp.msg.source_id); SWIM_DEBUG(swim_ctx, "recv dping ack from %lu\n", dping_resp.msg.source_id);
assert(dping_resp.msg.source_id == target); assert(dping_resp.msg.source_id == target_id);
/* extract target's membership state from response */ /* extract target's membership state from response */
swim_unpack_message(g, &(dping_resp.msg)); swim_unpack_message(swim_ctx, &(dping_resp.msg));
margo_free_output(handle, &dping_resp); margo_free_output(handle, &dping_resp);
ret = 0; ret = 0;
} }
else if(hret != HG_TIMEOUT) else if(hret != HG_TIMEOUT)
{ {
SSG_DEBUG(g, "SWIM: dping req error from %d (err=%d)\n", (int)target, hret); fprintf(stderr, "SWIM dping req error (err=%d)\n", hret);
} }
fini: fini:
...@@ -174,30 +177,35 @@ fini: ...@@ -174,30 +177,35 @@ fini:
static void swim_dping_recv_ult(hg_handle_t handle) static void swim_dping_recv_ult(hg_handle_t handle)
{ {
ssg_group_t *g;
swim_context_t *swim_ctx; swim_context_t *swim_ctx;
swim_dping_req_t dping_req; swim_dping_req_t dping_req;
swim_dping_resp_t dping_resp; swim_dping_resp_t dping_resp;
const struct hg_info *hgi;
margo_instance_id mid;
hg_return_t hret; hg_return_t hret;
/* get handle info and margo instance */
hgi = margo_get_info(handle);
assert(hgi);
mid = margo_hg_info_get_instance(hgi);
assert(mid != MARGO_INSTANCE_NULL);
/* get ssg & swim state */ /* get ssg & swim state */
g = (ssg_group_t *)margo_registered_data(ssg_inst->mid, swim_dping_rpc_id); swim_ctx = (swim_context_t *)margo_registered_data(mid, swim_dping_rpc_id);
assert(g != NULL);
swim_ctx = (swim_context_t *)g->fd_ctx;
assert(swim_ctx != NULL); assert(swim_ctx != NULL);
hret = margo_get_input(handle, &dping_req); hret = margo_get_input(handle, &dping_req);
if(hret != HG_SUCCESS) goto fini; if(hret != HG_SUCCESS) goto fini;
SSG_DEBUG(g, "SWIM: recv dping req from %d\n", (int)dping_req.msg.source_id); SWIM_DEBUG(swim_ctx, "recv dping req from %lu\n", dping_req.msg.source_id);
/* extract sender's membership state from request */ /* extract sender's membership state from request */
swim_unpack_message(g, &(dping_req.msg)); swim_unpack_message(swim_ctx, &(dping_req.msg));
/* fill the direct ping response with current membership state */ /* fill the direct ping response with current membership state */
swim_pack_message(g, &(dping_resp.msg)); swim_pack_message(swim_ctx, &(dping_resp.msg));
SSG_DEBUG(g, "SWIM: send dping ack to %d\n", (int)dping_req.msg.source_id); SWIM_DEBUG(swim_ctx, "send dping ack to %lu\n", dping_req.msg.source_id);
/* respond to sender of the dping req */ /* respond to sender of the dping req */
margo_respond(handle, &dping_resp); margo_respond(handle, &dping_resp);
...@@ -213,6 +221,7 @@ DEFINE_MARGO_RPC_HANDLER(swim_dping_recv_ult) ...@@ -213,6 +221,7 @@ DEFINE_MARGO_RPC_HANDLER(swim_dping_recv_ult)
* SWIM indirect pings * * SWIM indirect pings *
********************************/ ********************************/
#if 0
void swim_iping_send_ult( void swim_iping_send_ult(
void *t_arg) void *t_arg)
{ {
...@@ -342,30 +351,31 @@ fini: ...@@ -342,30 +351,31 @@ fini:
return; return;
} }
DEFINE_MARGO_RPC_HANDLER(swim_iping_recv_ult) DEFINE_MARGO_RPC_HANDLER(swim_iping_recv_ult)
#endif
/******************************** /********************************
* SWIM ping helpers * * SWIM ping helpers *
********************************/ ********************************/
/* TODO: refactor retrieve/apply api to make this less awkward */ static void swim_pack_message(swim_context_t *swim_ctx, swim_message_t *msg)
static void swim_pack_message(ssg_group_t *g, swim_message_t *msg)
{ {
swim_context_t *swim_ctx = (swim_context_t *)g->fd_ctx;
memset(msg, 0, sizeof(*msg)); memset(msg, 0, sizeof(*msg));
/* fill in self information */ /* fill in self information */
msg->source_id = g->self_id; msg->source_id = swim_ctx->self_id;
msg->source_inc_nr = swim_ctx->member_inc_nrs[g->self_id]; msg->source_inc_nr = swim_ctx->self_inc_nr;
#if 0
/* piggyback a set of membership states on this message */ /* piggyback a set of membership states on this message */
swim_retrieve_membership_updates(g, msg->pb_buf, SWIM_MAX_PIGGYBACK_ENTRIES); swim_retrieve_membership_updates(g, msg->pb_buf, SWIM_MAX_PIGGYBACK_ENTRIES);
#endif
return; return;
} }
static void swim_unpack_message(ssg_group_t *g, swim_message_t *msg) static void swim_unpack_message(swim_context_t *swim_ctx, swim_message_t *msg)
{ {
#if 0
swim_member_update_t sender_update; swim_member_update_t sender_update;
/* apply (implicit) sender update */ /* apply (implicit) sender update */
...@@ -376,6 +386,7 @@ static void swim_unpack_message(ssg_group_t *g, swim_message_t *msg) ...@@ -376,6 +386,7 @@ static void swim_unpack_message(ssg_group_t *g, swim_message_t *msg)
/* update membership status using piggybacked membership updates */ /* update membership status using piggybacked membership updates */
swim_apply_membership_updates(g, msg->pb_buf, SWIM_MAX_PIGGYBACK_ENTRIES); swim_apply_membership_updates(g, msg->pb_buf, SWIM_MAX_PIGGYBACK_ENTRIES);
#endif
return; return;
} }
...@@ -390,7 +401,7 @@ static hg_return_t hg_proc_swim_message_t(hg_proc_t proc, void *data) ...@@ -390,7 +401,7 @@ static hg_return_t hg_proc_swim_message_t(hg_proc_t proc, void *data)
switch(hg_proc_get_op(proc)) switch(hg_proc_get_op(proc))
{ {
case HG_ENCODE: case HG_ENCODE:
hret = hg_proc_ssg_member_id_t(proc, &(msg->source_id)); hret = hg_proc_swim_member_id_t(proc, &(msg->source_id));
if(hret != HG_SUCCESS) if(hret != HG_SUCCESS)
{ {
hret = HG_PROTOCOL_ERROR; hret = HG_PROTOCOL_ERROR;
...@@ -402,6 +413,7 @@ static hg_return_t hg_proc_swim_message_t(hg_proc_t proc, void *data) ...@@ -402,6 +413,7 @@ static hg_return_t hg_proc_swim_message_t(hg_proc_t proc, void *data)
hret = HG_PROTOCOL_ERROR; hret = HG_PROTOCOL_ERROR;
return hret; return hret;
} }
#if 0
for(i = 0; i < SWIM_MAX_PIGGYBACK_ENTRIES; i++) for(i = 0; i < SWIM_MAX_PIGGYBACK_ENTRIES; i++)
{ {
hret = hg_proc_swim_member_update_t(proc, &(msg->pb_buf[i])); hret = hg_proc_swim_member_update_t(proc, &(msg->pb_buf[i]));
...@@ -411,9 +423,10 @@ static hg_return_t hg_proc_swim_message_t(hg_proc_t proc, void *data) ...@@ -411,9 +423,10 @@ static hg_return_t hg_proc_swim_message_t(hg_proc_t proc, void *data)
return hret; return hret;
} }
} }
#endif
break; break;
case HG_DECODE: case HG_DECODE:
hret = hg_proc_ssg_member_id_t(proc, &(msg->source_id)); hret = hg_proc_swim_member_id_t(proc, &(msg->source_id));
if(hret != HG_SUCCESS) if(hret != HG_SUCCESS)
{ {
hret = HG_PROTOCOL_ERROR; hret = HG_PROTOCOL_ERROR;
...@@ -425,6 +438,7 @@ static hg_return_t hg_proc_swim_message_t(hg_proc_t proc, void *data) ...@@ -425,6 +438,7 @@ static hg_return_t hg_proc_swim_message_t(hg_proc_t proc, void *data)
hret = HG_PROTOCOL_ERROR; hret = HG_PROTOCOL_ERROR;
return hret; return hret;
} }
#if 0
for(i = 0; i < SWIM_MAX_PIGGYBACK_ENTRIES; i++) for(i = 0; i < SWIM_MAX_PIGGYBACK_ENTRIES; i++)
{ {
hret = hg_proc_swim_member_update_t(proc, &(msg->pb_buf[i])); hret = hg_proc_swim_member_update_t(proc, &(msg->pb_buf[i]));
...@@ -434,6 +448,7 @@ static hg_return_t hg_proc_swim_message_t(hg_proc_t proc, void *data) ...@@ -434,6 +448,7 @@ static hg_return_t hg_proc_swim_message_t(hg_proc_t proc, void *data)