#include "src/include/pmix_config.h"
#include "pmix_common.h"
#include <errno.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#ifdef HAVE_STRING_H
# include <string.h>
#endif
#include <pthread.h>
#include <stdio.h>
#include <event.h>
#include "src/include/pmix_globals.h"
#include "src/mca/ptl/base/base.h"
#include "src/util/pmix_argv.h"
#include "src/util/pmix_error.h"
#include "src/util/pmix_output.h"
#include "src/util/pmix_show_help.h"
#include "psensor_heartbeat.h"
#include "src/mca/psensor/base/base.h"
static pmix_status_t heartbeat_start(pmix_peer_t *requestor, pmix_status_t error,
const pmix_info_t *monitor, const pmix_info_t directives[],
size_t ndirs);
static pmix_status_t heartbeat_stop(pmix_peer_t *requestor, char *id);
pmix_psensor_base_module_t pmix_psensor_heartbeat_module = {.start = heartbeat_start,
.stop = heartbeat_stop};
typedef struct {
pmix_list_item_t super;
pmix_peer_t *requestor;
char *id;
bool event_active;
pmix_event_t ev;
pmix_event_t cdev;
struct timeval tv;
uint32_t nbeats;
uint32_t ndrops;
uint32_t nmissed;
pmix_status_t error;
pmix_data_range_t range;
pmix_info_t *info;
size_t ninfo;
bool stopped;
} pmix_heartbeat_trkr_t;
static void ft_constructor(pmix_heartbeat_trkr_t *ft)
{
ft->requestor = NULL;
ft->id = NULL;
ft->event_active = false;
ft->tv.tv_sec = 0;
ft->tv.tv_usec = 0;
ft->nbeats = 0;
ft->ndrops = 0;
ft->nmissed = 0;
ft->error = PMIX_SUCCESS;
ft->range = PMIX_RANGE_NAMESPACE;
ft->info = NULL;
ft->ninfo = 0;
ft->stopped = false;
}
static void ft_destructor(pmix_heartbeat_trkr_t *ft)
{
if (NULL != ft->requestor) {
PMIX_RELEASE(ft->requestor);
}
if (NULL != ft->id) {
free(ft->id);
}
if (ft->event_active) {
pmix_event_del(&ft->ev);
}
if (NULL != ft->info) {
PMIX_INFO_FREE(ft->info, ft->ninfo);
}
}
PMIX_CLASS_INSTANCE(pmix_heartbeat_trkr_t, pmix_list_item_t, ft_constructor, ft_destructor);
typedef struct {
pmix_object_t super;
pmix_event_t ev;
pmix_peer_t *requestor;
char *id;
} heartbeat_caddy_t;
static void cd_con(heartbeat_caddy_t *p)
{
p->requestor = NULL;
p->id = NULL;
}
static void cd_des(heartbeat_caddy_t *p)
{
if (NULL != (p->requestor)) {
PMIX_RELEASE(p->requestor);
}
if (NULL != p->id) {
free(p->id);
}
}
PMIX_CLASS_INSTANCE(heartbeat_caddy_t, pmix_object_t, cd_con, cd_des);
typedef struct {
pmix_object_t super;
pmix_event_t ev;
pmix_peer_t *peer;
} pmix_psensor_beat_t;
static void bcon(pmix_psensor_beat_t *p)
{
p->peer = NULL;
}
static void bdes(pmix_psensor_beat_t *p)
{
if (NULL != p->peer) {
PMIX_RELEASE(p->peer);
}
}
PMIX_CLASS_INSTANCE(pmix_psensor_beat_t, pmix_object_t, bcon, bdes);
static void check_heartbeat(int fd, short dummy, void *arg);
static void add_tracker(int sd, short flags, void *cbdata)
{
pmix_heartbeat_trkr_t *ft = (pmix_heartbeat_trkr_t *) cbdata;
PMIX_ACQUIRE_OBJECT(ft);
PMIX_HIDE_UNUSED_PARAMS(sd, flags);
pmix_list_append(&pmix_mca_psensor_heartbeat_component.trackers, &ft->super);
pmix_event_evtimer_set(pmix_psensor_base.evbase, &ft->ev, check_heartbeat, ft);
pmix_event_evtimer_add(&ft->ev, &ft->tv);
ft->event_active = true;
}
static pmix_status_t heartbeat_start(pmix_peer_t *requestor, pmix_status_t error,
const pmix_info_t *monitor, const pmix_info_t directives[],
size_t ndirs)
{
pmix_heartbeat_trkr_t *ft;
size_t n;
pmix_ptl_posted_recv_t *rcv;
pmix_output_verbose(1, pmix_psensor_base_framework.framework_output,
"[%s:%d] checking heartbeat monitoring for requestor %s:%d",
pmix_globals.myid.nspace, pmix_globals.myid.rank,
requestor->info->pname.nspace, requestor->info->pname.rank);
if (0 != strcmp(monitor->key, PMIX_MONITOR_HEARTBEAT)) {
return PMIX_ERR_TAKE_NEXT_OPTION;
}
ft = PMIX_NEW(pmix_heartbeat_trkr_t);
PMIX_RETAIN(requestor);
ft->requestor = requestor;
ft->error = error;
for (n = 0; n < ndirs; n++) {
if (0 == strcmp(directives[n].key, PMIX_MONITOR_HEARTBEAT_TIME)) {
ft->tv.tv_sec = directives[n].value.data.uint32;
} else if (0 == strcmp(directives[n].key, PMIX_MONITOR_HEARTBEAT_DROPS)) {
ft->ndrops = directives[n].value.data.uint32;
} else if (0 == strcmp(directives[n].key, PMIX_RANGE)) {
ft->range = directives[n].value.data.range;
}
}
if (0 == ft->tv.tv_sec) {
PMIX_RELEASE(ft);
return PMIX_ERR_BAD_PARAM;
}
if (!pmix_mca_psensor_heartbeat_component.recv_active) {
rcv = PMIX_NEW(pmix_ptl_posted_recv_t);
rcv->tag = PMIX_PTL_TAG_HEARTBEAT;
rcv->cbfunc = pmix_psensor_heartbeat_recv_beats;
pmix_list_prepend(&pmix_ptl_base.posted_recvs, &rcv->super);
pmix_mca_psensor_heartbeat_component.recv_active = true;
}
pmix_event_assign(&ft->cdev, pmix_psensor_base.evbase, -1, EV_WRITE, add_tracker, ft);
PMIX_POST_OBJECT(ft);
pmix_event_active(&ft->cdev, EV_WRITE, 1);
return PMIX_SUCCESS;
}
static void del_tracker(int sd, short flags, void *cbdata)
{
heartbeat_caddy_t *cd = (heartbeat_caddy_t *) cbdata;
pmix_heartbeat_trkr_t *ft, *ftnext;
PMIX_ACQUIRE_OBJECT(cd);
PMIX_HIDE_UNUSED_PARAMS(sd, flags);
PMIX_LIST_FOREACH_SAFE (ft, ftnext, &pmix_mca_psensor_heartbeat_component.trackers,
pmix_heartbeat_trkr_t) {
if (ft->requestor != cd->requestor) {
continue;
}
if (NULL == cd->id || (NULL != ft->id && 0 == strcmp(ft->id, cd->id))) {
pmix_list_remove_item(&pmix_mca_psensor_heartbeat_component.trackers, &ft->super);
PMIX_RELEASE(ft);
}
}
PMIX_RELEASE(cd);
}
static pmix_status_t heartbeat_stop(pmix_peer_t *requestor, char *id)
{
heartbeat_caddy_t *cd;
cd = PMIX_NEW(heartbeat_caddy_t);
PMIX_RETAIN(requestor);
cd->requestor = requestor;
if (NULL != id) {
cd->id = strdup(id);
}
pmix_event_assign(&cd->ev, pmix_psensor_base.evbase, -1, EV_WRITE, del_tracker, cd);
PMIX_POST_OBJECT(cd);
pmix_event_active(&cd->ev, EV_WRITE, 1);
return PMIX_SUCCESS;
}
static void opcbfunc(pmix_status_t status, void *cbdata)
{
pmix_heartbeat_trkr_t *ft = (pmix_heartbeat_trkr_t *) cbdata;
PMIX_HIDE_UNUSED_PARAMS(status);
PMIX_RELEASE(ft); }
static void check_heartbeat(int fd, short dummy, void *cbdata)
{
pmix_heartbeat_trkr_t *ft = (pmix_heartbeat_trkr_t *) cbdata;
pmix_status_t rc;
pmix_proc_t source;
PMIX_ACQUIRE_OBJECT(ft);
PMIX_HIDE_UNUSED_PARAMS(fd, dummy);
pmix_output_verbose(1, pmix_psensor_base_framework.framework_output,
"[%s:%d] sensor:check_heartbeat for proc %s:%d", pmix_globals.myid.nspace,
pmix_globals.myid.rank, ft->requestor->info->pname.nspace,
ft->requestor->info->pname.rank);
if (0 == ft->nbeats && !ft->stopped) {
pmix_output_verbose(1, pmix_psensor_base_framework.framework_output,
"[%s:%d] sensor:check_heartbeat failed for proc %s:%d",
pmix_globals.myid.nspace, pmix_globals.myid.rank,
ft->requestor->info->pname.nspace, ft->requestor->info->pname.rank);
pmix_strncpy(source.nspace, ft->requestor->info->pname.nspace, PMIX_MAX_NSLEN);
source.rank = ft->requestor->info->pname.rank;
PMIX_RETAIN(ft);
ft->stopped = true;
rc = PMIx_Notify_event(PMIX_MONITOR_HEARTBEAT_ALERT, &source, ft->range, ft->info,
ft->ninfo, opcbfunc, ft);
if (PMIX_SUCCESS != rc) {
PMIX_ERROR_LOG(rc);
}
} else {
pmix_output_verbose(1, pmix_psensor_base_framework.framework_output,
"[%s:%d] sensor:check_heartbeat detected %d beats for proc %s:%d",
pmix_globals.myid.nspace, pmix_globals.myid.rank, ft->nbeats,
ft->requestor->info->pname.nspace, ft->requestor->info->pname.rank);
}
ft->nbeats = 0;
pmix_event_evtimer_add(&ft->ev, &ft->tv);
}
static void add_beat(int sd, short args, void *cbdata)
{
pmix_psensor_beat_t *b = (pmix_psensor_beat_t *) cbdata;
pmix_heartbeat_trkr_t *ft;
PMIX_ACQUIRE_OBJECT(b);
PMIX_HIDE_UNUSED_PARAMS(sd, args);
PMIX_LIST_FOREACH (ft, &pmix_mca_psensor_heartbeat_component.trackers, pmix_heartbeat_trkr_t) {
if (ft->requestor == b->peer) {
++ft->nbeats;
ft->stopped = false;
break;
}
}
PMIX_RELEASE(b);
}
void pmix_psensor_heartbeat_recv_beats(struct pmix_peer_t *peer, pmix_ptl_hdr_t *hdr,
pmix_buffer_t *buf, void *cbdata)
{
pmix_psensor_beat_t *b;
PMIX_HIDE_UNUSED_PARAMS(hdr, buf, cbdata);
b = PMIX_NEW(pmix_psensor_beat_t);
PMIX_RETAIN(peer);
b->peer = peer;
pmix_event_assign(&b->ev, pmix_psensor_base.evbase, -1, EV_WRITE, add_beat, b);
PMIX_POST_OBJECT(b);
pmix_event_active(&b->ev, EV_WRITE, 1);
}