Commit bbd5bdc6 authored by Shane Snyder's avatar Shane Snyder

Merge branch 'sds-ssg-updates-2018'

parents 91adeaac 6a1015c1
......@@ -3,7 +3,6 @@ ACLOCAL_AMFLAGS = -I m4
bin_PROGRAMS =
bin_SCRIPTS =
noinst_LTLIBRARIES =
noinst_PROGRAMS =
TESTS =
XFAIL_TESTS =
......@@ -13,30 +12,32 @@ CLEANFILES = $(bin_SCRIPTS)
MAINTAINERCLEANFILES =
EXTRA_DIST =
BUILT_SOURCES =
src_libssg_la_SOURCES =
include_HEADERS = include/ssg.h
if SSG_HAVE_MPI
include_HEADERS += include/ssg-mpi.h
endif
endif
if SSG_HAVE_PMIX
include_HEADERS += include/ssg-pmix.h
endif
noinst_HEADERS = ssg-config.h
lib_LTLIBRARIES = src/libssg.la
TESTS_ENVIRONMENT =
EXTRA_DIST += prepare.sh
AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/src
AM_CFLAGS =
AM_LIBS =
lib_LTLIBRARIES = src/libssg.la
src_libssg_la_SOURCES =
LDADD = src/libssg.la
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = maint/ssg.pc
include Make.rules
include $(top_srcdir)/src/Makefile.subdir
if SSG_USE_SWIM_FD
include $(top_srcdir)/src/swim-fd/Makefile.subdir
endif
include $(top_srcdir)/tests/Makefile.subdir
......@@ -34,6 +34,28 @@ dnl
PKG_PROG_PKG_CONFIG
PKG_CONFIG="pkg-config --static"
# coreutils checks for OSX
AC_ARG_VAR([TIMEOUT], timeout program)
AC_ARG_VAR([MKTEMP], mktemp program)
if test -z "$TIMEOUT" ; then
AC_CHECK_PROGS(TIMEOUT, [timeout gtimeout])
if test -z "$TIMEOUT" ; then
AC_MSG_ERROR([Could not find timeout command (can optionally provide via the TIMEOUT variable)])
fi
else
AC_SUBST([TIMEOUT], ["$TIMEOUT"])
fi
if test -z "$MKTEMP" ; then
AC_CHECK_PROGS(MKTEMP, [mktemp gmktemp])
if test -z "$MKTEMP" ; then
AC_MSG_ERROR([Could not find mktemp command (can optionally provide via the MKTEMP variable)])
fi
else
AC_SUBST([MKTEMP], ["$MKTEMP"])
fi
check_mpi=auto
AC_ARG_ENABLE([mpi],
[ --enable-mpi enable MPI (default: dynamic check)],
......@@ -44,6 +66,16 @@ AC_ARG_ENABLE([mpi],
esac],
[])
check_pmix=auto
AC_ARG_ENABLE([pmix],
[ --enable-pmix enable PMIx (default: dynamic check)],
[ case "${enableval}" in
yes) check_pmix=yes ;;
no) check_pmix=no ;;
*) AC_MSG_ERROR([bad value ${enableval} for --enable-pmix]) ;;
esac],
[])
check_mpi_status=fail
if test "x${check_mpi}" = xauto -o "x${check_mpi}" = xyes ; then
AC_MSG_CHECKING([If MPI programs can be compiled])
......@@ -55,11 +87,26 @@ if test "x${check_mpi}" = xauto -o "x${check_mpi}" = xyes ; then
[AC_MSG_RESULT([no])])
fi
check_pmix_status=fail
if test "x${check_pmix}" = xauto -o "x${check_pmix}" = xyes ; then
AC_MSG_CHECKING([If PMIx programs can be compiled])
AC_LINK_IFELSE(
[AC_LANG_PROGRAM([[#include<pmix.h>]], [[PMIx_Init(NULL, NULL,0);]])],
[AC_DEFINE([SSG_HAVE_PMIX], [1], [Define to 1 if compiled with PMIx support])
AC_MSG_RESULT([yes])
check_pmix_status=success],
[AC_MSG_RESULT([no])])
fi
if test "x${check_mpi_status}" = xfail -a "x${check_mpi}" = xyes; then
AC_MSG_ERROR([MPI requested but unable to be used. Did you specify an MPI compiler?])
fi
if test "x${check_pmix_status}" = xfail -a "x${check_pmix}" = xyes; then
AC_MSG_ERROR([PMIx requested but unable to be used.])
fi
AM_CONDITIONAL([SSG_HAVE_MPI], [test "x${check_mpi_status}" = xsuccess])
AM_CONDITIONAL([SSG_HAVE_PMIX], [test "x${check_pmix_status}" = xsuccess])
PKG_CHECK_MODULES([MARGO],[margo],[],
[AC_MSG_ERROR([Could not find working margo installation!])])
......@@ -67,15 +114,5 @@ LIBS="$MARGO_LIBS $LIBS"
CPPFLAGS="$MARGO_CFLAGS $CPPFLAGS"
CFLAGS="$MARGO_CFLAGS $CFLAGS"
AC_ARG_ENABLE([swim-fd],
[ --enable-swim-fd enable SWIM failure detection (default: disabled)],
[if test "x$enable_swim_fd" = "xyes" ; then
AC_DEFINE(SSG_USE_SWIM_FD, 1, Define to 1 if the SWIM failure detector should be used)
elif test "x$enableval" != "xno" ; then
AC_MSG_ERROR(bad value ${enable_swim_fd} for --enable-swim-fd)
fi],)
AM_CONDITIONAL([SSG_USE_SWIM_FD], [test "x${enable_swim_fd}" = xyes])
AC_CONFIG_FILES([Makefile maint/ssg.pc])
AC_OUTPUT
......@@ -26,7 +26,7 @@ extern "C" {
* @param[in] comm MPI communicator containing group members
* @param[in] update_cb Callback function executed on group membership changes
* @param[in] update_cb_dat User data pointer passed to membership update callback
* @returns SSG group identifier on success, SSG_GROUP_ID_NULL otherwise
* @returns SSG group identifier for created group on success, SSG_GROUP_ID_NULL otherwise
*/
ssg_group_id_t ssg_group_create_mpi(
const char * group_name,
......
/*
* Copyright (c) 2016 UChicago Argonne, LLC
*
* See COPYRIGHT in top-level directory.
*/
#pragma once
#include <pmix.h>
#include <ssg.h>
/** @file ssg-pmix.h
* Scalable Service Groups (SSG) interface
*
* An SSG group create routine based on PMIx.
*/
#ifdef __cplusplus
extern "C" {
#endif
/**
* Creates an SSG group from a given PMIx proc handle.
*
* @param[in] group_name Name of the SSG group
* @param[in] proc PMIx proc handle representing this group member
* @param[in] update_cb Callback function executed on group membership changes
* @param[in] update_cb_dat User data pointer passed to membership update callback
* @returns SSG group identifier for created group on success, SSG_GROUP_ID_NULL otherwise
*/
ssg_group_id_t ssg_group_create_pmix(
const char * group_name,
pmix_proc_t proc,
ssg_membership_update_cb update_cb,
void * update_cb_dat);
#ifdef __cplusplus
}
#endif
......@@ -28,32 +28,29 @@ extern "C" {
#define SSG_FAILURE (-1)
/* opaque SSG group ID type */
typedef struct ssg_group_descriptor *ssg_group_id_t;
#define SSG_GROUP_ID_NULL ((ssg_group_id_t)NULL)
typedef uint64_t ssg_group_id_t;
#define SSG_GROUP_ID_INVALID 0
/* SSG group member ID type */
typedef uint64_t ssg_member_id_t;
#define SSG_MEMBER_ID_INVALID UINT64_MAX
#define SSG_MEMBER_ID_INVALID 0
/* SSG group member update types */
enum ssg_membership_update_type
typedef enum ssg_member_update_type
{
SSG_MEMBER_ADD = 0,
SSG_MEMBER_REMOVE
};
typedef struct ssg_membership_update
{
ssg_member_id_t member;
int type;
} ssg_membership_update_t;
SSG_MEMBER_JOINED = 0,
SSG_MEMBER_LEFT,
SSG_MEMBER_DIED
} ssg_member_update_type_t;
typedef void (*ssg_membership_update_cb)(
ssg_membership_update_t, void *);
void * group_data,
ssg_member_id_t member_id,
ssg_member_update_type_t update_type);
/* HG proc routine prototypes for SSG types */
#define hg_proc_ssg_member_id_t hg_proc_int64_t
hg_return_t hg_proc_ssg_group_id_t(hg_proc_t proc, void *data);
#define hg_proc_ssg_group_id_t hg_proc_uint64_t
#define hg_proc_ssg_member_id_t hg_proc_uint64_t
/***************************************************
*** SSG runtime intialization/shutdown routines ***
......@@ -88,7 +85,7 @@ int ssg_finalize(
* @param[in] group_size Number of group members
* @param[in] update_cb Callback function executed on group membership changes
* @param[in] update_cb_dat User data pointer passed to membership update callback
* @returns SSG group identifier on success, SSG_GROUP_ID_NULL otherwise
* @returns SSG group identifier for created group on success, SSG_GROUP_ID_NULL otherwise
*
* NOTE: The HG address string of the caller of this function must be present in
* the list of address strings given in 'group_addr_strs'. That is, the caller
......@@ -110,7 +107,7 @@ ssg_group_id_t ssg_group_create(
* HG address strings for this group
* @param[in] update_cb Callback function executed on group membership changes
* @param[in] update_cb_dat User data pointer passed to membership update callback
* @returns SSG group identifier on success, SSG_GROUP_ID_NULL otherwise
* @returns SSG group identifier for created group on success, SSG_GROUP_ID_NULL otherwise
*
*
* NOTE: The HG address string of the caller of this function must be present in
......@@ -133,25 +130,50 @@ int ssg_group_destroy(
ssg_group_id_t group_id);
/**
* Attaches a client to an SSG group.
* Adds the calling process to an SSG group.
*
* @param[in] group_id Input SSG group ID
* @param[in] update_cb Callback function executed on group membership changes
* @param[in] update_cb_dat User data pointer passed to membership update callback
* @returns SSG_SUCCESS on success, SSG error code otherwise
*
* NOTE: Use the returned group ID to refer to the group, as the input group ID
* becomes stale after the join is completed.
*/
int ssg_group_join(
ssg_group_id_t group_id,
ssg_membership_update_cb update_cb,
void * update_cb_dat);
/**
* Removes the calling process from an SSG group.
*
* @param[in] group_id SSG group ID
* @returns SSG_SUCCESS on success, SSG error code otherwise
*/
int ssg_group_leave(
ssg_group_id_t group_id);
/**
* Initiates a client's observation of an SSG group.
*
* @param[in] group_id SSG group ID
* @returns SSG_SUCCESS on success, SSG error code otherwise
*
* NOTE: The "client" cannot be a member of the group -- attachment is merely
* NOTE: The "client" cannot be a member of the group -- observation is merely
* a way of making the membership view of an existing SSG group available to
* non-group members.
*/
int ssg_group_attach(
int ssg_group_observe(
ssg_group_id_t group_id);
/**
* Detaches a client from an SSG group.
* Terminates a client's observation of an SSG group.
*
* @param[in] group_id SSG group ID
* @returns SSG_SUCCESS on success, SSG error code otherwise
*/
int ssg_group_detach(
int ssg_group_unobserve(
ssg_group_id_t group_id);
/*********************************
......@@ -161,11 +183,11 @@ int ssg_group_detach(
/**
* Obtains the caller's member ID in the given SSG group.
*
* @param[in] group_id SSG group ID
* @returns caller's group ID on success, SSG_MEMBER_ID_INVALID otherwise
* @param[in] mid Corresponding Margo instance identifier
* @returns caller's member ID on success, SSG_MEMBER_ID_INVALID otherwise
*/
ssg_member_id_t ssg_get_group_self_id(
ssg_group_id_t group_id);
ssg_member_id_t ssg_get_self_id(
margo_instance_id mid);
/**
* Obtains the size of a given SSG group.
......@@ -183,25 +205,58 @@ int ssg_get_group_size(
* @param[in] member_id SSG group member ID
* @returns HG address of given group member on success, HG_ADDR_NULL otherwise
*/
hg_addr_t ssg_get_addr(
hg_addr_t ssg_get_group_member_addr(
ssg_group_id_t group_id,
ssg_member_id_t member_id);
/**
* Duplicates the given SSG group identifier.
* Obtains the rank of the caller in a given SSG group.
*
* @param[in] group_id SSG group ID
* @returns SSG group identifier on success, SSG_GROUP_ID_NULL otherwise
* @returns rank on success, -1 on failure
*/
ssg_group_id_t ssg_group_id_dup(
int ssg_get_group_self_rank(
ssg_group_id_t group_id);
/** Frees the given SSG group identifier.
/**
* Obtains the rank of a member in a given SSG group.
*
* @param[in] group_id SSG group ID
* @param[in] member_id SSG group member ID
* @returns rank on success, -1 otherwise
*/
int ssg_get_group_member_rank(
ssg_group_id_t group_id,
ssg_member_id_t member_id);
/**
* Obtains the SSG member ID of the given group and rank.
*
* @param[in] group_id SSG group ID
* @param[in] rank SSG group rank
* @returns caller's member ID on success, SSG_MEMBER_ID_INVALID otherwise
*/
void ssg_group_id_free(
ssg_group_id_t group_id);
ssg_member_id_t ssg_get_group_member_id_from_rank(
ssg_group_id_t group_id,
int rank);
/**
* Obtains an array of SSG member IDs for a given rank range.
*
* @param[in] group_id SSG group ID
* @param[in] rank_start Rank of range start
* @param[in] rank_end Rank of range end
* @param[in,out] range_ids Buffer to store member IDs of requested range
* @returns number of member IDs returned in range_ids on success, 0 otherwise
*
* NOTE: range_ids must be allocated by caller and must be large enough to hold
* requested range.
*/
int ssg_get_group_member_ids_from_range(
ssg_group_id_t group_id,
int rank_start,
int rank_end,
ssg_member_id_t *range_ids);
/**
* Retrieves the HG address string associated with an SSG group identifier.
......@@ -243,6 +298,7 @@ void ssg_group_id_deserialize(
*
* @param[in] file_name File to store the group ID in
* @param[in] group_id SSG group ID
* @returns SSG_SUCCESS on success, SSG error code otherwise
*/
int ssg_group_id_store(
const char * file_name,
......@@ -253,6 +309,7 @@ int ssg_group_id_store(
*
* @param[in] file_name File to store the group ID in
* @param[out] group_id_p Pointer to store group identifier in
* @returns SSG_SUCCESS on success, SSG error code otherwise
*/
int ssg_group_id_load(
const char * file_name,
......
......@@ -6,6 +6,8 @@
#pragma once
#include "ssg-config.h"
#include <stdint.h>
#include <inttypes.h>
......@@ -15,7 +17,10 @@
#include <margo.h>
#include "ssg.h"
#include "swim-fd/swim-fd.h"
#include "uthash.h"
#include "utlist.h"
#include "utarray.h"
#ifdef __cplusplus
extern "C" {
......@@ -24,14 +29,12 @@ extern "C" {
#define SSG_MAGIC_NR 17321588
/* debug printing macro for SSG */
/* TODO: direct debug output to file? */
/* TODO: how do we debug attachers? */
#ifdef DEBUG
#define SSG_DEBUG(__g, __fmt, ...) do { \
double __now = ABT_get_wtime(); \
fprintf(stdout, "%.6lf <%s:%"PRIu64">: " __fmt, __now, \
__g->name, __g->self_id, ## __VA_ARGS__); \
fflush(stdout); \
fprintf(__g->dbg_log, "%.6lf %20"PRIu64" (%s): " __fmt, __now, \
__g->ssg_inst->self_id, __g->name, ## __VA_ARGS__); \
fflush(__g->dbg_log); \
} while(0)
#else
#define SSG_DEBUG(__g, __fmt, ...) do { \
......@@ -40,80 +43,129 @@ extern "C" {
/* SSG internal dataypes */
typedef struct ssg_member_state
typedef struct ssg_instance
{
char *addr_str;
hg_addr_t addr;
int is_member;
} ssg_member_state_t;
margo_instance_id mid;
char *self_addr_str;
ssg_member_id_t self_id;
struct ssg_group_descriptor *g_desc_table;
#ifdef SSG_HAVE_PMIX
size_t pmix_failure_evhdlr_ref;
#endif
ABT_rwlock lock;
} ssg_instance_t;
/* TODO: associate a version number with a descriptor */
typedef struct ssg_group_descriptor
{
uint64_t magic_nr;
uint64_t name_hash;
ssg_group_id_t g_id;
char *addr_str;
int owner_status;
int ref_count;
union
{
struct ssg_group *g;
struct ssg_observed_group *og;
} g_data;
UT_hash_handle hh;
} ssg_group_descriptor_t;
enum ssg_group_descriptor_owner_status
{
SSG_OWNER_IS_UNASSOCIATED = 0,
SSG_OWNER_IS_MEMBER,
SSG_OWNER_IS_OBSERVER
};
typedef struct ssg_member_state
{
ssg_member_id_t id;
char *addr_str;
hg_addr_t addr;
swim_member_state_t swim_state;
UT_hash_handle hh;
} ssg_member_state_t;
typedef struct ssg_group_view
{
unsigned int size;
ssg_member_state_t *member_states;
ssg_member_state_t *member_map;
UT_array *rank_array;
} ssg_group_view_t;
typedef struct ssg_group
{
char *name;
ssg_group_descriptor_t *descriptor;
ssg_member_id_t self_id;
ssg_instance_t *ssg_inst;
ssg_group_view_t view;
void *fd_ctx; /* failure detector context (currently just SWIM) */
ssg_member_state_t *dead_members;
ssg_group_descriptor_t *descriptor;
swim_context_t *swim_ctx;
ssg_membership_update_cb update_cb;
void *update_cb_dat;
UT_hash_handle hh;
ABT_rwlock lock;
#ifdef DEBUG
FILE *dbg_log;
#endif
} ssg_group_t;
typedef struct ssg_attached_group
typedef struct ssg_observed_group
{
char *name;
ssg_group_descriptor_t *descriptor;
ssg_instance_t *ssg_inst;
ssg_group_view_t view;
UT_hash_handle hh;
} ssg_attached_group_t;
ssg_group_descriptor_t *descriptor;
ABT_rwlock lock;
} ssg_observed_group_t;
typedef struct ssg_instance
typedef struct ssg_member_update
{
margo_instance_id mid;
ssg_group_t *group_table;
ssg_attached_group_t *attached_group_table;
} ssg_instance_t;
enum ssg_group_descriptor_owner_status
{
SSG_OWNER_IS_UNASSOCIATED = 0,
SSG_OWNER_IS_MEMBER,
SSG_OWNER_IS_ATTACHER
};
ssg_member_update_type_t type;
union
{
char *member_addr_str;
ssg_member_id_t member_id;
} u;
} ssg_member_update_t;
/* SSG internal function prototypes */
#define ssg_hashlittle2 hashlittle2
extern void hashlittle2(const void *key, size_t length, uint32_t *pc, uint32_t *pb);
static inline uint64_t ssg_hash64_str(const char * str)
{
uint32_t lower = 0, upper = 0;
uint64_t hash;
ssg_hashlittle2(str, strlen(str), &lower, &upper);
hash = lower + (((uint64_t)upper)<<32);
return hash;
}
void ssg_register_rpcs(
void);
int ssg_group_attach_send(
int ssg_group_join_send(
ssg_group_descriptor_t * group_descriptor,
hg_addr_t group_target_addr,
char ** group_name,
int * group_size,
void ** view_buf);
void ssg_apply_membership_update(
ssg_group_t *g,
ssg_membership_update_t update);
int ssg_group_leave_send(
ssg_group_descriptor_t * group_descriptor,
ssg_member_id_t self_id,
hg_addr_t group_target_addr);
int ssg_group_observe_send(
ssg_group_descriptor_t * group_descriptor,
char ** group_name,
int * group_size,
void ** view_buf);
void ssg_apply_member_updates(
ssg_group_t * g,
ssg_member_update_t * updates,
hg_size_t update_count);
hg_return_t hg_proc_ssg_member_update_t(
hg_proc_t proc, void *data);
static const UT_icd ut_ssg_member_id_t_icd = {sizeof(ssg_member_id_t),NULL,NULL,NULL};
/* XXX: is this right? can this be a global? */
extern ssg_instance_t *ssg_inst;
#ifdef __cplusplus
......
This diff is collapsed.
This diff is collapsed.
......@@ -6,7 +6,13 @@
#pragma once
#include <ssg.h>
#include <abt.h>
#include <margo.h>
#include "ssg.h"
#include "ssg-internal.h"
#include "swim-fd.h"
#include "utlist.h"
#ifdef __cplusplus
extern "C" {
......@@ -18,67 +24,77 @@ extern "C" {
#define SWIM_DEF_SUBGROUP_SIZE 2
#define SWIM_MAX_SUBGROUP_SIZE 5
#define SWIM_MAX_PIGGYBACK_ENTRIES 8
#define SWIM_MAX_PIGGYBACK_TX_COUNT 50
typedef uint8_t swim_member_status_t;
typedef uint32_t swim_member_inc_nr_t;
typedef struct swim_member_update swim_member_update_t;
#define SWIM_MAX_PIGGYBACK_TX_COUNT 5
enum swim_member_status
typedef struct swim_ping_target_list
{
SWIM_MEMBER_ALIVE = 0,
SWIM_MEMBER_SUSPECT,
SWIM_MEMBER_DEAD
};
ssg_member_state_t **targets;
unsigned int nslots;
unsigned int len;
unsigned int dping_ndx;
} swim_ping_target_list_t;
struct swim_member_update
typedef struct swim_member_update
{
ssg_member_id_t id;
swim_member_status_t status;
swim_member_inc_nr_t inc_nr;
};
swim_member_state_t state;
} swim_member_update_t;
/* internal swim context implementation */
struct swim_context
{
/* argobots pool for launching SWIM threads */
ABT_pool prot_pool;
/* SWIM internal state */
ssg_member_id_t ping_target;
swim_member_inc_nr_t ping_target_inc_nr;
int ping_target_acked;
double dping_timeout;
ssg_member_id_t subgroup_members[SWIM_MAX_SUBGROUP_SIZE];
int shutdown_flag;
/* current membership state */
swim_member_inc_nr_t *member_inc_nrs;
void *suspect_list;
void *recent_update_list;
margo_instance_id mid;
/* SWIM protocol parameters */
double prot_period_len;
int prot_susp_timeout;
int prot_subgroup_sz;
/* SWIM protocol internal state */
swim_member_inc_nr_t self_inc_nr;
ssg_member_id_t dping_target_id;
swim_member_inc_nr_t dping_target_inc_nr;
hg_addr_t dping_target_addr;
double dping_timeout;
ssg_member_id_t iping_target_ids[SWIM_MAX_SUBGROUP_SIZE];
hg_addr_t iping_target_addrs[SWIM_MAX_SUBGROUP_SIZE];
int iping_target_ndx;
int ping_target_acked;
int shutdown_flag;
/* list of SWIM ping targets */
swim_ping_target_list_t target_list;
/* list of currently supspected SWIM targets */
void *suspect_list;
/* lists of SWIM and SSG membership updates to gossip */
void *swim_update_list;
void *ssg_update_list;
/* argobots pool for launching SWIM threads */
ABT_pool swim_pool;
/* swim protocol ULT handle */
ABT_thread prot_thread;
/* swim protocol lock */
ABT_rwlock swim_lock;
};
/* SWIM ping function prototypes */
void swim_register_ping_rpcs(
ssg_group_t * g);
void swim_dping_send_ult(
ssg_group_t * group);
void swim_dping_req_send_ult(
void * t_arg);
void swim_iping_send_ult(
void swim_iping_req_send_ult(
void * t_arg);
/* SWIM membership update function prototypes */
void swim_retrieve_membership_updates(
ssg_group_t * g,
/* SWIM update function prototypes */
void swim_retrieve_member_updates(
ssg_group_t * group,
swim_member_update_t * updates,
int update_count);
void swim_apply_membership_updates(
ssg_group_t * g,
hg_size_t *update_count);
void swim_retrieve_ssg_member_updates(
ssg_group_t * group,
ssg_member_update_t * updates,
hg_size_t *update_count);
void swim_apply_member_updates(
ssg_group_t * group,
swim_member_update_t * updates,
int update_count);
hg_size_t update_count);
#ifdef __cplusplus
}
......
This diff is collapsed.
This diff is collapsed.
......@@ -6,6 +6,9 @@
#pragma once
#include <stdint.h>
#include <inttypes.h>
#include "ssg.h"
#include "ssg-internal.h"
......@@ -16,12 +19,56 @@ extern "C" {
/* opaque swim context type */
typedef struct swim_context swim_context_t;
swim_context_t * swim_init(
ssg_group_t * g,
/* swim member specific types */
typedef uint32_t swim_member_inc_nr_t;
typedef enum swim_member_status
{
SWIM_MEMBER_ALIVE = 0,
SWIM_MEMBER_SUSPECT,
SWIM_MEMBER_DEAD
} swim_member_status_t;
/* SWIM state associated with each group member */
typedef struct swim_member_state
{
swim_member_inc_nr_t inc_nr;
swim_member_status_t status;
} swim_member_state_t;
/* forward declarations to work around weird SSG/SWIM circular dependency */