/* $Source: bitbucket.org:berkeleylab/gasnet.git/other/amx/amx_internal.h $ * Description: AMX internal header file * Copyright 2002, Dan Bonachea * Copyright 2018, The Regents of the University of California * Terms of use are as specified in license.txt */ #ifndef _AMX_INTERNAL_H #define _AMX_INTERNAL_H #ifndef _AMX_INTERNAL_FWD_H #error header inclusion error #endif #ifdef __cplusplus extern "C" { #endif // At this point we should have the public header, which includes (N)EBUG indicators and library identification #if !(AMX_DEBUG ^ AMX_NDEBUG) #error incorrext AMX_(N)DEBUG settings #endif #if AMUDP #define AMX_LIB_STR "AMUDP" #elif AMMPI #define AMX_LIB_STR "AMMPI" #else #error missing AMX library identification #endif #include #undef assert #define assert(x) ERROR_use_AMX_assert #define AMX_curloc __FILE__ ":" AMX_STRINGIFY(__LINE__) #if AMX_NDEBUG #define AMX_assert(expr) ((void)0) #else #define AMX_assert(expr) \ (AMX_PREDICT_TRUE(expr) ? (void)0 : \ AMX_FatalErr("Assertion failure at %s %s: %s\n", \ AMX_CURRENT_FUNCTION, AMX_curloc, #expr)) #endif #ifdef gasnett_unreachable #define AMX_unreachable() gasnett_unreachable() #else #define AMX_unreachable() AMX_assert(!"unreachable") #endif // Attributes #if defined(__has_attribute) // introduced around gcc 5.x (2015) #define AMX_HAS_ATTRIBUTE(x) __has_attribute(x) #else #define AMX_HAS_ATTRIBUTE(x) 0 #endif #ifdef HAVE_GASNET_TOOLS #define AMX_FORMAT_PRINTF GASNETT_FORMAT_PRINTF #elif defined (__GNUC__) || AMX_HAS_ATTRIBUTE(__format__) #define AMX_FORMAT_PRINTF(fnname,fmtarg,firstvararg,declarator) \ __attribute__((__format__ (__printf__, fmtarg, firstvararg))) declarator #else #define AMX_FORMAT_PRINTF(fnname,fmtarg,firstvararg,declarator) declarator #endif #ifdef HAVE_GASNET_TOOLS #define AMX_INLINE(fn) GASNETT_INLINE(fn) #elif AMX_DEBUG #define AMX_INLINE(fn) static #elif __cplusplus #define AMX_INLINE(fn) inline #elif __STDC_VERSION__ >= 199901L #define AMX_INLINE(fn) static inline #else #define AMX_INLINE(fn) static #endif #ifdef HAVE_GASNET_TOOLS #define AMX_NEVER_INLINE(fnname,declarator) GASNETT_NEVER_INLINE(fnname,declarator) #else #define AMX_NEVER_INLINE(fnname,declarator) declarator #endif /* ------------------------------------------------------------------------------------ */ AMX_FORMAT_PRINTF(AMX_Err,1,2, extern int AMX_Err(const char *msg, ...)); AMX_FORMAT_PRINTF(AMX_Warn,1,2, extern int AMX_Warn(const char *msg, ...)); AMX_FORMAT_PRINTF(AMX_Info,1,2, extern int AMX_Info(const char *msg, ...)); // verbose debug messages (double-parens) #if AMX_DEBUG_VERBOSE #define AMX_VERBOSE_INFO(args) AMX_Info args #define _AMX_IS_DEBUG_VERBOSE 1 #else #define AMX_VERBOSE_INFO(args) ((void)0) #define _AMX_IS_DEBUG_VERBOSE 0 #endif #if AMX_DEBUG #define AMX_DEBUG_INFO(args) AMX_Info args #define AMX_DEBUG_WARN(args) AMX_Warn args // throttled debug-only warning #define AMX_DEBUG_WARN_TH(msg) do { \ static uint64_t _cnt = 0; \ static uint64_t _mask = 0xFF; \ _cnt++; \ if_pf (_AMX_IS_DEBUG_VERBOSE || (_cnt & _mask) == 0) { \ _mask = (_mask << 1) | 0x1; \ AMX_Warn("%s (%" PRIu64 " occurences)",msg,_cnt); \ } \ } while (0) #else #define AMX_DEBUG_INFO(args) ((void)0) #define AMX_DEBUG_WARN(args) ((void)0) #define AMX_DEBUG_WARN_TH(msg) ((void)0) #endif #ifdef GASNETT_NORETURN GASNETT_NORETURN #endif AMX_FORMAT_PRINTF(AMX_FatalErr,1,2, extern void AMX_FatalErr(const char *msg, ...)); #ifdef GASNETT_NORETURNP GASNETT_NORETURNP(AMX_FatalErr) #endif /* memory allocation */ static void *_AMX_malloc(size_t sz, const char *curloc) { void *ret = malloc(sz); if_pf(!ret) AMX_FatalErr("Failed to malloc(%" PRIuSZ ") at %s", sz, curloc); return ret; } static void *_AMX_calloc(size_t N, size_t S, const char *curloc) { void *ret = calloc(N,S); if_pf(!ret) AMX_FatalErr("Failed to calloc(%" PRIuSZ ",%" PRIuSZ ") at %s", N, S, curloc); return ret; } static void *_AMX_realloc(void *ptr, size_t S, const char *curloc) { void *ret = realloc(ptr,S); if_pf(!ret) AMX_FatalErr("Failed to realloc(%" PRIuSZ ") at %s", S, curloc); return ret; } static void _AMX_free(void *ptr, const char *curloc) { free(ptr); } #ifndef AMX_TENTATIVE_EXTERN #if __cplusplus #define AMX_TENTATIVE_EXTERN extern #elif AMX_HAS_ATTRIBUTE(__common__) // This attribute is notably available in or before: // gcc 4.8.5, clang 4.0.0, Intel 2017.0, PGI 18.4, Xcode 8.2.1 #define AMX_TENTATIVE_EXTERN __attribute__((__common__)) #else #define AMX_TENTATIVE_EXTERN #endif #endif #if AMX_DEBUG // use the gasnet debug malloc functions if a debug libgasnet is linked // This uses tentative definitions which are not supported in C++, // so at least one object in libamx must compile this header in C mode. AMX_TENTATIVE_EXTERN void *(*gasnett_debug_malloc_fn)(size_t sz, const char *curloc); AMX_TENTATIVE_EXTERN void *(*gasnett_debug_calloc_fn)(size_t N, size_t S, const char *curloc); AMX_TENTATIVE_EXTERN void *(*gasnett_debug_realloc_fn)(void *ptr, size_t sz, const char *curloc); AMX_TENTATIVE_EXTERN void (*gasnett_debug_free_fn)(void *ptr, const char *curloc); AMX_TENTATIVE_EXTERN char *(*gasnett_debug_strdup_fn)(const char *s, const char *curloc); AMX_TENTATIVE_EXTERN char *(*gasnett_debug_strndup_fn)(const char *s, size_t sz, const char *curloc); AMX_TENTATIVE_EXTERN void (*gasnett_debug_memcheck_fn)(void *ptr, const char *curloc); AMX_TENTATIVE_EXTERN void (*gasnett_debug_memcheck_one_fn)(const char *curloc); AMX_TENTATIVE_EXTERN void (*gasnett_debug_memcheck_all_fn)(const char *curloc); #define AMX_malloc(sz) \ ( (AMX_PREDICT_FALSE(gasnett_debug_malloc_fn==NULL) ? \ gasnett_debug_malloc_fn = &_AMX_malloc : 0), \ (*gasnett_debug_malloc_fn)(sz, AMX_curloc)) #define AMX_calloc(N,S) \ ( (AMX_PREDICT_FALSE(gasnett_debug_calloc_fn==NULL) ? \ gasnett_debug_calloc_fn = &_AMX_calloc : 0), \ (*gasnett_debug_calloc_fn)((N),(S), AMX_curloc)) #define AMX_realloc(ptr,S) \ ( (AMX_PREDICT_FALSE(gasnett_debug_realloc_fn==NULL) ? \ gasnett_debug_realloc_fn = &_AMX_realloc : 0), \ (*gasnett_debug_realloc_fn)((ptr),(S), AMX_curloc)) #define AMX_free(ptr) \ ( (AMX_PREDICT_FALSE(gasnett_debug_free_fn==NULL) ? \ gasnett_debug_free_fn = &_AMX_free : 0), \ (*gasnett_debug_free_fn)(ptr, AMX_curloc)) #define AMX_strdup(s) \ ( (AMX_PREDICT_FALSE(gasnett_debug_strdup_fn==NULL) ? \ gasnett_debug_strdup_fn = &_AMX_strdup : 0), \ (*gasnett_debug_strdup_fn)(s, AMX_curloc)) #define AMX_strndup(s,sz) \ ( (AMX_PREDICT_FALSE(gasnett_debug_strndup_fn==NULL) ? \ gasnett_debug_strndup_fn = &_AMX_strndup : 0), \ (*gasnett_debug_strndup_fn)(s, sz, AMX_curloc)) #define AMX_memcheck(ptr) do { \ AMX_assert(ptr); \ if (gasnett_debug_memcheck_fn) \ (*gasnett_debug_memcheck_fn)(ptr, AMX_curloc); \ } while (0) #define AMX_memcheck_one() do { \ if (gasnett_debug_memcheck_one_fn) \ (*gasnett_debug_memcheck_one_fn)(AMX_curloc); \ } while (0) #define AMX_memcheck_all() do { \ if (gasnett_debug_memcheck_all_fn) \ (*gasnett_debug_memcheck_all_fn)(AMX_curloc); \ } while (0) #undef malloc #define malloc(x) ERROR_use_AMX_malloc #undef calloc #define calloc(n,s) ERROR_use_AMX_calloc #undef realloc #define realloc(n,s) ERROR_use_AMX_realloc #undef free #define free(x) ERROR_use_AMX_free #undef strdup #define strdup(x) ERROR_use_AMX_strdup #undef strndup #define strndup(x) ERROR_use_AMX_strndup #else #define AMX_malloc(sz) _AMX_malloc((sz),AMX_curloc) #define AMX_calloc(N,S) _AMX_calloc((N),(S),AMX_curloc) #define AMX_realloc(ptr,S) _AMX_realloc((ptr),(S),AMX_curloc) #define AMX_free(ptr) _AMX_free(ptr,AMX_curloc) #define AMX_strdup(s) _AMX_strdup(s,AMX_curloc) #define AMX_strndup(s,sz) _AMX_strndup(s,sz,AMX_curloc) #define AMX_memcheck(ptr) ((void)0) #define AMX_memcheck_one() ((void)0) #define AMX_memcheck_all() ((void)0) #endif // some older OS's (eg Solaris 10) lack strndup, so roll our own static char *_AMX_strdup(const char *s, const char *curloc) { AMX_assert(s); size_t len = strlen(s); char *ret = (char*)AMX_malloc(len+1); if_pf(!ret) AMX_FatalErr("Failed to strdup(%" PRIuSZ ") at %s", strlen(s), curloc); memcpy(ret,s,len); ret[len] = 0; return ret; } static char *_AMX_strndup(const char *s, size_t sz, const char *curloc) { AMX_assert(s); size_t _len = strlen(s); size_t len = MIN(_len,sz); char *ret = (char *)AMX_malloc(len+1); if_pf(!ret) AMX_FatalErr("Failed to strndup(%" PRIuSZ ",%" PRIuSZ ") at %s", strlen(s), sz, curloc); memcpy(ret,s,len); ret[len] = 0; return ret; } /*------------------------------------------------------------------------------------ * Error reporting *------------------------------------------------------------------------------------ */ static const char *AMX_ErrorName(int errval) { switch (errval) { case AM_ERR_NOT_INIT: return "AM_ERR_NOT_INIT"; case AM_ERR_BAD_ARG: return "AM_ERR_BAD_ARG"; case AM_ERR_RESOURCE: return "AM_ERR_RESOURCE"; case AM_ERR_NOT_SENT: return "AM_ERR_NOT_SENT"; case AM_ERR_IN_USE: return "AM_ERR_IN_USE"; default: return "*unknown*"; } } static const char *AMX_ErrorDesc(int errval) { switch (errval) { case AM_ERR_NOT_INIT: return "Active message layer not initialized"; case AM_ERR_BAD_ARG: return "Invalid function parameter passed"; case AM_ERR_RESOURCE: return "Problem with requested resource"; case AM_ERR_NOT_SENT: return "Synchronous message not sent"; case AM_ERR_IN_USE: return "Resource currently in use"; default: return "no description available"; } } //------------------------------------------------------------------------------------ /* macros for returning errors that allow verbose error tracking */ #define AMX_RETURN_ERR(type) do { \ if (AMX_VerboseErrors) AMX_Warn("%s returning an error code: AM_ERR_%s (%s)\n" \ " at %s", \ AMX_CURRENT_FUNCTION, #type, AMX_ErrorDesc(AM_ERR_##type), AMX_curloc); \ return AM_ERR_ ## type; \ } while (0) #define AMX_RETURN_ERRF(type, fromfn) do { \ if (AMX_VerboseErrors) AMX_Warn("%s returning an error code: AM_ERR_%s (%s)\n" \ " from function %s\n" \ " at %s", \ AMX_CURRENT_FUNCTION, #type, AMX_ErrorDesc(AM_ERR_##type), \ #fromfn, AMX_curloc); \ return AM_ERR_ ## type; \ } while (0) #define AMX_RETURN_ERRFR(type, fromfn, reason) do { \ if (AMX_VerboseErrors) AMX_Warn("%s returning an error code: AM_ERR_%s (%s)\n" \ " from function %s\n" \ " at %s\n" \ " reason: %s\n", \ AMX_CURRENT_FUNCTION, #type, AMX_ErrorDesc(AM_ERR_##type), \ #fromfn, AMX_curloc, reason); \ return AM_ERR_ ## type; \ } while (0) /* return a possible error */ #define AMX_RETURN(val) do { \ if_pf (AMX_VerboseErrors && val != AM_OK) \ AMX_Warn("%s returning an error code: %s (%s)\n" \ " at %s", \ AMX_CURRENT_FUNCTION, AMX_ErrorName(val), AMX_ErrorDesc(val), \ AMX_curloc); \ return val; \ } while (0) #ifndef AMX_ENABLE_ERRCHECKS #if GASNETI_ENABLE_ERRCHECKS #define AMX_ENABLE_ERRCHECKS 1 #else #define AMX_ENABLE_ERRCHECKS 0 #endif #endif #if AMX_DEBUG || AMX_ENABLE_ERRCHECKS #define AMX_CHECK_ERR(errcond, type) \ do { if_pf (errcond) AMX_RETURN_ERR(type); } while(0) #define AMX_CHECK_ERRF(errcond, type, fromfn) \ do { if_pf (errcond) AMX_RETURN_ERRF(type, fromfn); } while(0) #define AMX_CHECK_ERRR(errcond, type, reason) \ do { if_pf (errcond) AMX_RETURN_ERRR(type, reason); } while(0) #define AMX_CHECK_ERRFR(errcond, type, fromfn, reason) \ do { if_pf (errcond) AMX_RETURN_ERRFR(type, fromfn, reason); } while(0) #define AMX_CHECK_ERRFRC(errcond, type, fromfn, reason, cleanup) \ do { if_pf (errcond) { { cleanup; } AMX_RETURN_ERRFR(type, fromfn, reason); } } while(0) #else #define AMX_CHECK_ERR(errcond, type) ((void)0) #define AMX_CHECK_ERRF(errcond, type, fromfn) ((void)0) #define AMX_CHECK_ERRR(errcond, type, reason) ((void)0) #define AMX_CHECK_ERRFR(errcond, type, fromfn, reason) ((void)0) #define AMX_CHECK_ERRFRC(errcond, type, fromfn, reason, cleanup) ((void)0) #endif #ifdef AMX_HERE #undef AMX_HERE #define AMX_HERE() AMX_Info("HERE at: %s",AMX_curloc) #else #define AMX_HERE() ((void)0) #endif extern int amx_Initialized; #define AMX_CHECKINIT() AMX_CHECK_ERR((!amx_Initialized),NOT_INIT) extern int AMX_Init(void); extern int AMX_Terminate(void); //------------------------------------------------------------------------------------ // shared variables extern amx_handler_fn_t amx_unused_handler; extern const char *AMX_ProcessLabel; //------------------------------------------------------------------------------------ // handler dispatch typedef void (*amx_returned_handler_fn_t)(int status, op_t opcode, void *token); #define _AMX_EMPTY(a,b,c) #define _AMX_harg(a,b,c) ,int32_t #define _AMX_harg_pass(Nm1,N,Np1) ,_args[Nm1] #define _AMX_Short_handlerfn_typedefN(Nm1,N,Np1) \ typedef void (*AMX_Short_handlerfn_type_##N)(void * AMX_META_DES##N(_AMX_EMPTY,_AMX_harg)); AMX_META_ASC16(_AMX_Short_handlerfn_typedefN,_AMX_Short_handlerfn_typedefN) #define _AMX_Short_RunCaseN(Nm1,N,Np1) \ case N: ((AMX_Short_handlerfn_type_##N)_phandlerfn)(_token AMX_META_ASC##N(_AMX_EMPTY,_AMX_harg_pass)); break; #define AMX_RUN_HANDLER_SHORT(phandlerfn, token, pArgs, numargs) do { \ amx_handler_fn_t const _phandlerfn = (amx_handler_fn_t)phandlerfn; \ void * const _token = (void *)token; \ int32_t const * const _args = (int32_t *)(pArgs); \ AMX_assert(_phandlerfn); AMX_assert(_token); AMX_assert(_args || !numargs); \ switch (numargs) { \ AMX_META_DES16(_AMX_Short_RunCaseN,_AMX_Short_RunCaseN) \ default: AMX_unreachable(); \ } \ } while (0) #define _AMX_MedLong_handlerfn_typedefN(Nm1,N,Np1) \ typedef void (*AMX_MedLong_handlerfn_type_##N)(void *, void *, size_t AMX_META_DES##N(_AMX_EMPTY,_AMX_harg)); AMX_META_ASC16(_AMX_MedLong_handlerfn_typedefN,_AMX_MedLong_handlerfn_typedefN) #define _AMX_MedLong_RunCaseN(Nm1,N,Np1) \ case N: ((AMX_MedLong_handlerfn_type_##N)_phandlerfn)(_token, _pData, _datalen AMX_META_ASC##N(_AMX_EMPTY,_AMX_harg_pass)); break; #define _AMX_RUN_HANDLER_MEDLONG(phandlerfn, token, pArgs, numargs, pData, datalen, extrachecks) do { \ amx_handler_fn_t const _phandlerfn = (amx_handler_fn_t)phandlerfn; \ void * const _token = (void *)token; \ int32_t const * const _args = (int32_t *)(pArgs); \ void * const _pData = (void *)pData; \ size_t const _datalen = datalen; \ AMX_assert(_phandlerfn); AMX_assert(_token); AMX_assert(_args || !numargs); \ AMX_assert(_pData || !_datalen); \ extrachecks; \ switch (numargs) { \ AMX_META_DES16(_AMX_MedLong_RunCaseN,_AMX_MedLong_RunCaseN) \ default: AMX_unreachable(); \ } \ } while (0) #define AMX_RUN_HANDLER_MEDIUM(phandlerfn, token, pArgs, numargs, pData, datalen) \ _AMX_RUN_HANDLER_MEDLONG(phandlerfn, token, pArgs, numargs, pData, datalen, \ AMX_assert(((uintptr_t)pData) % 8 == 0)) // ensure medium align #define AMX_RUN_HANDLER_LONG(phandlerfn, token, pArgs, numargs, pData, datalen) \ _AMX_RUN_HANDLER_MEDLONG(phandlerfn, token, pArgs, numargs, pData, datalen, ((void)0)) //------------------------------------------------------------------------------------ #ifndef AMX_ENV_PREFIX_STR // AMX_ENV_PREFIX_STR is the safest define, to avoid conflicting defns #ifdef AMX_ENV_PREFIX #define AMX_ENV_PREFIX_STR AMX_STRINGIFY(AMX_ENV_PREFIX) #elif defined(AMUDP_ENV_PREFIX) #define AMX_ENV_PREFIX_STR AMX_STRINGIFY(AMUDP_ENV_PREFIX) #elif defined(AMMPI_ENV_PREFIX) #define AMX_ENV_PREFIX_STR AMX_STRINGIFY(AMMPI_ENV_PREFIX) #else #define AMX_ENV_PREFIX_STR AMX_LIB_STR #endif #endif /* return the first environment variable matching one of: AMX_ENV_PREFIX_STR##_##basekey AMX_LIB_STR##_##basekey basekey warn if more than one is set with different values */ extern char *AMX_getenv_prefixed(const char *basekey); /* same with a default value and console reporting in verbose mode */ extern char *AMX_getenv_prefixed_withdefault(const char *basekey, const char *defaultval); extern void AMX_freezeForDebugger(void); //------------------------------------------------------------------------------------ /* *** TIMING *** */ #ifndef _AMX_TICK_T #define _AMX_TICK_T typedef uint64_t amx_tick_t; #endif #if defined(HAVE_GASNET_TOOLS) #define AMX_getMicrosecondTimeStamp() (gasnett_ticks_to_us(gasnett_ticks_now())) #define AMX_getCPUTicks() ((amx_tick_t)gasnett_ticks_now()) #define AMX_ticks2us(ticks) (gasnett_ticks_to_us((gasnett_tick_t)(ticks))) #define AMX_us2ticks(us) \ ((amx_tick_t)(us) * \ (amx_tick_t)(1000000000.0 / gasnett_ticks_to_us((gasnett_tick_t)1000000000))) #define AMX_tickspersec AMX_us2ticks(1000000) #else static int64_t AMX_getMicrosecondTimeStamp(void) { int64_t retval; struct timeval tv; retry: if (gettimeofday(&tv, NULL)) AMX_FatalErr("gettimeofday failed: %s",strerror(errno)); retval = ((int64_t)tv.tv_sec) * 1000000 + tv.tv_usec; #if PLATFORM_OS_UNICOS /* fix an empirically observed bug in UNICOS gettimeofday(), which occasionally returns ridiculously incorrect values */ if_pf(retval < (((int64_t)3) << 48)) goto retry; #endif return retval; } /* Ticks == us for gettimeofday */ #define AMX_getCPUTicks() ((amx_tick_t)AMX_getMicrosecondTimeStamp()) #define AMX_ticks2us(ticks) (ticks) #define AMX_us2ticks(us) ((amx_tick_t)(us)) #define AMX_tickspersec 1000000 #endif //------------------------------------------------------------------------------------ #if PLATFORM_OS_SUPERUX || PLATFORM_OS_NETBSD || \ PLATFORM_OS_BLRTS || PLATFORM_OS_OPENBSD /* these implement sched_yield() in libpthread only, which we may not want */ #define AMX_yield() sleep(0) #elif defined(HAVE_GASNET_TOOLS) #else #endif #ifdef __cplusplus } // extern "C" #endif #endif