/* -*- Mode: C; c-basic-offset:4 ; -*- */
/*
 *  (C) 2007 by Argonne National Laboratory.
 *      See COPYRIGHT in top-level directory.
 */

#ifndef PMI2UTIL_H_INCLUDED
#define PMI2UTIL_H_INCLUDED

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

#ifdef NVSHMEMI_WEAK_SYMBOLS
#define NVSHMEMI_ATTR_WEAK __attribute__((weak))
#else
#define NVSHMEMI_ATTR_WEAK
#endif

/* Note: functions with external linkage are annotated with
 * __attribute__((weak)) to allow NVSHMEM to link with an MPI or OpenSHMEM
 * library that also contains the same PMI-2 symbols. */

/* maximum sizes for arrays */
#define PMI2_MAXLINE 1024
#define PMI2_IDSIZE    32

#define TRUE  1
#define FALSE 0

#ifdef DEBUG
    #define PMI2U_printf(x...) do {				\
	char logstr[1024];					\
	snprintf(logstr, 1024, x);      			\
	fprintf(stderr, "[%s (%d): %s] %s\n",			\
		__FILE__, __LINE__, __func__, logstr);		\
    } while (0)
#else
    #define PMI2U_printf(x...)
#endif

#define PMI2U_Assert(a_) do { \
    if (!(a_)) { \
        PMI2U_printf("ASSERT( %s )", #a_); \
    } \
} while (0)

#define PMI2U_ERR_POP(err) do { \
    pmi2_errno = err; \
    PMI2U_printf("err. %d", pmi2_errno); \
    goto fn_fail; \
} while (0)

#define PMI2U_ERR_SETANDJUMP(err, class, x...) do { \
    char errstr[PMI2_MAXLINE * 3]; \
    snprintf(errstr, sizeof(errstr), x);	\
    PMI2U_printf("err. %s", errstr);\
    pmi2_errno = class; \
    goto fn_fail; \
} while (0)

#define PMI2U_ERR_CHKANDJUMP(cond, err, class, x...) do { \
    if (cond) PMI2U_ERR_SETANDJUMP(err, class, x); \
} while (0)

#define PMI2U_CHKMEM_SETERR(rc_, nbytes_, name_) do { \
    PMI2U_printf("ERROR: memory allocation of %lu bytes failed for %s", \
            (long unsigned int) nbytes_, name_); \
    rc_ = PMI2_ERR_NOMEM; \
    goto fn_exit; \
} while(0)

/* Persistent memory that we may want to recover if something goes wrong */
#define PMI2U_CHKMEM_DECL(n_) \
    void* pmi2u_chkmem_stk_[n_] = {0}; \
    int pmi2u_chkmem_stk_sp_= 0; \
    const int pmi2u_chkmem_stk_sz_ = n_

#define PMI2U_CHKMEM_REAP() \
    while (pmi2u_chkmem_stk_sp_ > 0) { \
        free ((void*)( pmi2u_chkmem_stk_[--pmi2u_chkmem_stk_sp_] )); \
    }

#define PMI2U_CHKMEM_COMMIT() pmi2u_chkmem_stk_sp_ = 0

#define PMI2U_CHKMEM_MALLOC(pointer_,type_,nbytes_,rc_,name_) do { \
    pointer_ = (type_)malloc(nbytes_); \
    if (pointer_ && (pmi2u_chkmem_stk_sp_< pmi2u_chkmem_stk_sz_)) { \
        pmi2u_chkmem_stk_[pmi2u_chkmem_stk_sp_++] = pointer_; \
    } else { \
        PMI2U_CHKMEM_SETERR(rc_,nbytes_,name_); \
        goto fn_fail; \
    } \
} while(0)

#define PMI2U_CHKMEM_FREEALL() \
    while (pmi2u_chkmem_stk_sp_ > 0) { \
        free ((void*)( pmi2u_chkmem_stk_[--pmi2u_chkmem_stk_sp_] )); \
}

/* prototypes for PMIU routines */
void PMI2U_Set_rank( int PMI_rank ) NVSHMEMI_ATTR_WEAK;
void PMI2U_SetServer( void ) NVSHMEMI_ATTR_WEAK;
int  PMI2U_readline( int fd, char *buf, int max ) NVSHMEMI_ATTR_WEAK;
int  PMI2U_writeline( int fd, char *buf ) NVSHMEMI_ATTR_WEAK;
int  PMI2U_parse_keyvals( char *st ) NVSHMEMI_ATTR_WEAK;
void PMI2U_dump_keyvals( void ) NVSHMEMI_ATTR_WEAK;
char *PMI2U_getval( const char *keystr, char *valstr, int vallen ) NVSHMEMI_ATTR_WEAK;
void PMI2U_chgval( const char *keystr, char *valstr ) NVSHMEMI_ATTR_WEAK;
int MPIU_Strncpy(char *, const char *, size_t) NVSHMEMI_ATTR_WEAK;

#undef NVSHMEMI_ATTR_WEAK

#endif /* PMI2UTIL_H_INCLUDED */
