diff options
Diffstat (limited to 'src/mercury/include')
42 files changed, 10921 insertions, 0 deletions
diff --git a/src/mercury/include/mercury.h b/src/mercury/include/mercury.h new file mode 100644 index 0000000..9f44012 --- /dev/null +++ b/src/mercury/include/mercury.h @@ -0,0 +1,1060 @@ +/* + * Copyright (C) 2013-2020 Argonne National Laboratory, Department of Energy, + * UChicago Argonne, LLC and The HDF Group. + * All rights reserved. + * + * The full copyright notice, including terms governing use, modification, + * and redistribution, is contained in the COPYING file that can be + * found at the root of the source code distribution tree. + */ + +#ifndef MERCURY_H +#define MERCURY_H + +#include "mercury_header.h" +#include "mercury_types.h" + +#include "mercury_core.h" + +/*************************************/ +/* Public Type and Struct Definition */ +/*************************************/ + +/* See mercury_types.h */ + +/*****************/ +/* Public Macros */ +/*****************/ + +/* See mercury_types.h */ + +/*********************/ +/* Public Prototypes */ +/*********************/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Get Mercury version number. + * + * \param major [OUT] pointer to unsigned integer + * \param minor [OUT] pointer to unsigned integer + * \param patch [OUT] pointer to unsigned integer + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Version_get(unsigned int *major, unsigned int *minor, unsigned int *patch); + +/** + * Convert error return code to string (null terminated). + * + * \param errnum [IN] error return code + * + * \return String + */ +HG_PUBLIC const char *HG_Error_to_string(hg_return_t errnum); + +/** + * Initialize the Mercury layer. + * Must be finalized with HG_Finalize(). + * + * \param na_info_string [IN] host address with port number (e.g., + * "tcp://localhost:3344" or + * "bmi+tcp://localhost:3344") + * \param na_listen [IN] listen for incoming connections + * + * \return Pointer to HG class or NULL in case of failure + */ +HG_PUBLIC hg_class_t *HG_Init(const char *na_info_string, hg_bool_t na_listen); + +/** + * Initialize the Mercury layer with options provided by init_info. + * Must be finalized with HG_Finalize(). + * \remark HG_Init_opt() may become HG_Init() in the future. + * + * \param na_info_string [IN] host address with port number (e.g., + * "tcp://localhost:3344" or + * "bmi+tcp://localhost:3344") + * \param na_listen [IN] listen for incoming connections + * \param hg_init_info [IN] (Optional) HG init info, NULL if no info + * + * \return Pointer to HG class or NULL in case of failure + */ +HG_PUBLIC hg_class_t *HG_Init_opt(const char *na_info_string, hg_bool_t na_listen, + const struct hg_init_info *hg_init_info); + +/** + * Finalize the Mercury layer. + * + * \param hg_class [IN] pointer to HG class + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Finalize(hg_class_t *hg_class); + +/** + * Clean up all temporary files that were created in previous HG instances. + * While temporary resources (e.g., tmp files) are cleaned up on a call + * to HG_Finalize(), this routine gives a chance to programs that terminate + * abnormally to easily clean up those resources. + */ +HG_PUBLIC void HG_Cleanup(void); + +/** + * Set the log level for HG. That setting is valid for all HG classes. + * + * \param level [IN] level string, valid values are: + * "none", "error", "warning", "debug" + */ +HG_PUBLIC void HG_Set_log_level(const char *level); + +/** + * Set the log sub-system for HG. That setting is valid for all HG classes. + * + * \param subsys [IN] string of subsystems, format is: + * subsys1,subsys2,subsys3,etc + * subsystem can be turned off, e.g.: + * ~subsys1 + */ +HG_PUBLIC void HG_Set_log_subsys(const char *subsys); + +/** + * Obtain the name of the given class. + * + * \param hg_class [IN] pointer to HG class + * + * \return the name of the class, or NULL if not a valid class + */ +static HG_INLINE const char *HG_Class_get_name(const hg_class_t *hg_class); + +/** + * Obtain the protocol of the given class. + * + * \param hg_class [IN] pointer to HG class + * + * \return the name of the class's transport, or NULL if not a valid class + */ +static HG_INLINE const char *HG_Class_get_protocol(const hg_class_t *hg_class); + +/** + * Test whether class is listening or not. + * + * \param hg_class [IN] pointer to HG class + * + * \return HG_TRUE if listening or HG_FALSE if not, or not a valid class + */ +static HG_INLINE hg_bool_t HG_Class_is_listening(const hg_class_t *hg_class); + +/** + * Obtain the maximum eager size for sending RPC inputs, for a given class. + * NOTE: This doesn't currently work when using XDR encoding. + * + * \param hg_class [IN] pointer to HG class + * + * \return the maximum size, or 0 if hg_class is not a valid class or XDR is + * being used + */ +static HG_INLINE hg_size_t HG_Class_get_input_eager_size(const hg_class_t *hg_class); + +/** + * Obtain the maximum eager size for sending RPC outputs, for a given class. + * NOTE: This doesn't currently work when using XDR encoding. + * + * \param hg_class [IN] pointer to HG class + * + * \return the maximum size, or 0 if hg_class is not a valid class or XDR is + * being used + */ +static HG_INLINE hg_size_t HG_Class_get_output_eager_size(const hg_class_t *hg_class); + +/** + * Set offset used for serializing / deserializing input. This allows upper + * layers to manually define a reserved space that can be used for the + * definition of custom headers. The actual input is encoded / decoded + * using the defined offset. By default, no offset is set. + * + * \param hg_class [IN] pointer to HG class + * \param offset [IN] offset size + * + * \return HG_SUCCESS or corresponding HG error code + */ +static HG_INLINE hg_return_t HG_Class_set_input_offset(hg_class_t *hg_class, hg_size_t offset); + +/** + * Set offset used for serializing / deserializing output. This allows upper + * layers to manually define a reserved space that can be used for the + * definition of custom headers. The actual output is encoded / decoded + * using the defined offset. By default, no offset is set. + * + * \param hg_class [IN] pointer to HG class + * \param offset [IN] offset size + * + * \return HG_SUCCESS or corresponding HG error code + */ +static HG_INLINE hg_return_t HG_Class_set_output_offset(hg_class_t *hg_class, hg_size_t offset); + +/** + * Associate user data to class. When HG_Finalize() is called, + * free_callback (if defined) is called to free the associated data. + * + * \param hg_class [IN] pointer to HG class + * \param data [IN] pointer to user data + * \param free_callback [IN] pointer to function + * + * \return HG_SUCCESS or corresponding HG error code + */ +static HG_INLINE hg_return_t HG_Class_set_data(hg_class_t *hg_class, void *data, + void (*free_callback)(void *)); + +/** + * Retrieve previously associated data from a given class. + * + * \param hg_class [IN] pointer to HG class + * + * \return Pointer to user data or NULL if not set or any error has occurred + */ +static HG_INLINE void *HG_Class_get_data(const hg_class_t *hg_class); + +/** + * Set callback to be called on HG handle creation. Handles are created + * both on HG_Create() and HG_Context_create() calls. This allows upper layers + * to create and attach data to a handle (using HG_Set_data()) and later + * retrieve it using HG_Get_data(). + * + * \param hg_class [IN] pointer to HG class + * \param callback [IN] pointer to function callback + * \param arg [IN] pointer to data passed to callback + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Class_set_handle_create_callback(hg_class_t *hg_class, + hg_return_t (*callback)(hg_handle_t, void *), + void *arg); + +/** + * Create a new context. Must be destroyed by calling HG_Context_destroy(). + * + * \remark This routine is internally equivalent to: + * - HG_Core_context_create() + * - If listening + * - HG_Core_context_post() with repost set to HG_TRUE + * + * \param hg_class [IN] pointer to HG class + * + * \return Pointer to HG context or NULL in case of failure + */ +HG_PUBLIC hg_context_t *HG_Context_create(hg_class_t *hg_class); + +/** + * Create a new context with a user-defined context identifier. The context + * identifier can be used to route RPC requests to specific contexts by using + * HG_Set_target_id(). + * Context must be destroyed by calling HG_Context_destroy(). + * + * \remark This routine is internally equivalent to: + * - HG_Core_context_create_id() with specified context ID + * - If listening + * - HG_Core_context_post() with repost set to HG_TRUE + * + * \param hg_class [IN] pointer to HG class + * \param id [IN] user-defined context ID + * + * \return Pointer to HG context or NULL in case of failure + */ +HG_PUBLIC hg_context_t *HG_Context_create_id(hg_class_t *hg_class, hg_uint8_t id); + +/** + * Destroy a context created by HG_Context_create(). + * + * \param context [IN] pointer to HG context + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Context_destroy(hg_context_t *context); + +/** + * Retrieve the class used to create the given context. + * + * \param context [IN] pointer to HG context + * + * \return Pointer to associated HG class or NULL if not a valid context + */ +static HG_INLINE hg_class_t *HG_Context_get_class(const hg_context_t *context); + +/** + * Retrieve context ID from context (max value of 255). + * + * \param context [IN] pointer to HG context + * + * \return Non-negative integer (max value of 255) or 0 if no ID has been set + */ +static HG_INLINE hg_uint8_t HG_Context_get_id(const hg_context_t *context); + +/** + * Associate user data to context. When HG_Context_destroy() is called, + * free_callback (if defined) is called to free the associated data. + * + * \param context [IN] pointer to HG context + * \param data [IN] pointer to user data + * \param free_callback [IN] pointer to function + * + * \return HG_SUCCESS or corresponding HG error code + */ +static HG_INLINE hg_return_t HG_Context_set_data(hg_context_t *context, void *data, + void (*free_callback)(void *)); + +/** + * Retrieve previously associated data from a given context. + * + * \param context [IN] pointer to HG context + * + * \return Pointer to user data or NULL if not set or any error has occurred + */ +static HG_INLINE void *HG_Context_get_data(const hg_context_t *context); + +/** + * Dynamically register a function func_name as an RPC as well as the + * RPC callback executed when the RPC request ID associated to func_name is + * received. Associate input and output proc to function ID, so that they can + * be used to serialize and deserialize function parameters. + * + * \param hg_class [IN] pointer to HG class + * \param func_name [IN] unique name associated to function + * \param in_proc_cb [IN] pointer to input proc callback + * \param out_proc_cb [IN] pointer to output proc callback + * \param rpc_cb [IN] RPC callback + * + * \return unique ID associated to the registered function + */ +HG_PUBLIC hg_id_t HG_Register_name(hg_class_t *hg_class, const char *func_name, hg_proc_cb_t in_proc_cb, + hg_proc_cb_t out_proc_cb, hg_rpc_cb_t rpc_cb); + +/* + * Indicate whether HG_Register_name() has been called for the RPC specified by + * func_name. + * + * \param hg_class [IN] pointer to HG class + * \param func_name [IN] function name + * \param id [OUT] registered RPC ID + * \param flag [OUT] pointer to boolean + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Registered_name(hg_class_t *hg_class, const char *func_name, hg_id_t *id, + hg_bool_t *flag); + +/** + * Dynamically register an RPC ID as well as the RPC callback executed when the + * RPC request ID is received. Associate input and output proc to id, so that + * they can be used to serialize and deserialize function parameters. + * + * \param hg_class [IN] pointer to HG class + * \param id [IN] ID to use to register RPC + * \param in_proc_cb [IN] pointer to input proc callback + * \param out_proc_cb [IN] pointer to output proc callback + * \param rpc_cb [IN] RPC callback + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Register(hg_class_t *hg_class, hg_id_t id, hg_proc_cb_t in_proc_cb, + hg_proc_cb_t out_proc_cb, hg_rpc_cb_t rpc_cb); + +/** + * Deregister RPC ID. Further requests with RPC ID will return an error, it + * is therefore up to the user to make sure that all requests for that RPC ID + * have been treated before it is unregistered. + * + * \param hg_class [IN] pointer to HG class + * \param id [IN] registered function ID + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Deregister(hg_class_t *hg_class, hg_id_t id); + +/** + * Indicate whether HG_Register() has been called. + * + * \param hg_class [IN] pointer to HG class + * \param id [IN] function ID + * \param flag [OUT] pointer to boolean + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Registered(hg_class_t *hg_class, hg_id_t id, hg_bool_t *flag); + +/** + * Indicate whether HG_Register() has been called, and if so return pointers + * to proc callback functions for the RPC. + * + * \param hg_class [IN] pointer to HG class + * \param id [IN] function ID + * \param flag [OUT] pointer to boolean + * \param in_proc_cb [OUT] pointer to input encoder cb + * \param out_proc_cb [OUT] pointer to output encoder cb + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Registered_proc_cb(hg_class_t *hg_class, hg_id_t id, hg_bool_t *flag, + hg_proc_cb_t *in_proc_cb, hg_proc_cb_t *out_proc_cb); + +/** + * Register and associate user data to registered function. When HG_Finalize() + * is called, free_callback (if defined) is called to free the registered + * data. + * + * \param hg_class [IN] pointer to HG class + * \param id [IN] registered function ID + * \param data [IN] pointer to data + * \param free_callback [IN] pointer to function + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Register_data(hg_class_t *hg_class, hg_id_t id, void *data, + void (*free_callback)(void *)); + +/** + * Indicate whether HG_Register_data() has been called and return associated + * data. + * + * \param hg_class [IN] pointer to HG class + * \param id [IN] registered function ID + * + * \return Pointer to data or NULL + */ +HG_PUBLIC void *HG_Registered_data(hg_class_t *hg_class, hg_id_t id); + +/** + * Disable response for a given RPC ID. This allows an origin process to send an + * RPC to a target without waiting for a response. The RPC completes locally and + * the callback on the origin is therefore pushed to the completion queue once + * the RPC send is completed. By default, all RPCs expect a response to + * be sent back. + * + * \param hg_class [IN] pointer to HG class + * \param id [IN] registered function ID + * \param disable [IN] boolean (HG_TRUE to disable + * HG_FALSE to re-enable) + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Registered_disable_response(hg_class_t *hg_class, hg_id_t id, hg_bool_t disable); + +/** + * Check if response is disabled for a given RPC ID + * (i.e., HG_Registered_disable_response() has been called for this RPC ID). + * + * \param hg_class [IN] pointer to HG class + * \param id [IN] registered function ID + * \param disabled [OUT] boolean (HG_TRUE if disabled + * HG_FALSE if enabled) + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Registered_disabled_response(hg_class_t *hg_class, hg_id_t id, hg_bool_t *disabled); + +/** + * Lookup an addr from a peer address/name. Addresses need to be + * freed by calling HG_Addr_free(). After completion, user callback is + * placed into a completion queue and can be triggered using HG_Trigger(). + * + * \param context [IN] pointer to context of execution + * \param callback [IN] pointer to function callback + * \param arg [IN] pointer to data passed to callback + * \param name [IN] lookup name + * \param op_id [OUT] pointer to returned operation ID (unused) + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Addr_lookup1(hg_context_t *context, hg_cb_t callback, void *arg, const char *name, + hg_op_id_t *op_id); + +/* This will map to HG_Addr_lookup2() in the future */ +#ifndef HG_Addr_lookup +#define HG_Addr_lookup HG_Addr_lookup1 +#endif + +/** + * Lookup an addr from a peer address/name. Addresses need to be + * freed by calling HG_Addr_free(). + * + * \remark This is the immediate version of HG_Addr_lookup1(). + * + * \param hg_class [IN/OUT] pointer to HG class + * \param name [IN] lookup name + * \param addr [OUT] pointer to abstract address + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Addr_lookup2(hg_class_t *hg_class, const char *name, hg_addr_t *addr); + +/** + * Free the addr. + * + * \param hg_class [IN] pointer to HG class + * \param addr [IN] abstract address + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Addr_free(hg_class_t *hg_class, hg_addr_t addr); + +/** + * Hint that the address is no longer valid. This may happen if the peer is + * no longer responding. This can be used to force removal of the + * peer address from the list of the peers, before freeing it and reclaim + * resources. + * + * \param hg_class [IN] pointer to HG class + * \param addr [IN] abstract address + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Addr_set_remove(hg_class_t *hg_class, hg_addr_t addr); + +/** + * Access self address. Address must be freed with HG_Addr_free(). + * + * \param hg_class [IN] pointer to HG class + * \param addr [OUT] pointer to abstract address + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Addr_self(hg_class_t *hg_class, hg_addr_t *addr); + +/** + * Duplicate an existing HG abstract address. The duplicated address can be + * stored for later use and the origin address be freed safely. The duplicated + * address must be freed with HG_Addr_free(). + * + * \param hg_class [IN] pointer to HG class + * \param addr [IN] abstract address + * \param new_addr [OUT] pointer to abstract address + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Addr_dup(hg_class_t *hg_class, hg_addr_t addr, hg_addr_t *new_addr); + +/** + * Compare two addresses. + * + * \param hg_class [IN] pointer to HG class + * \param addr1 [IN] abstract address + * \param addr2 [IN] abstract address + * + * \return HG_TRUE if addresses are determined to be equal, HG_FALSE otherwise + */ +HG_PUBLIC hg_bool_t HG_Addr_cmp(hg_class_t *hg_class, hg_addr_t addr1, hg_addr_t addr2); + +/** + * Convert an addr to a string (returned string includes the terminating + * null byte '\0'). If buf is NULL, the address is not converted and only + * the required size of the buffer is returned. If the input value passed + * through buf_size is too small, HG_SIZE_ERROR is returned and the buf_size + * output is set to the minimum size required. + * + * \param hg_class [IN] pointer to HG class + * \param buf [IN/OUT] pointer to destination buffer + * \param buf_size [IN/OUT] pointer to buffer size + * \param addr [IN] abstract address + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Addr_to_string(hg_class_t *hg_class, char *buf, hg_size_t *buf_size, hg_addr_t addr); + +/** + * Initiate a new HG RPC using the specified function ID and the local/remote + * target defined by addr. The HG handle created can be used to query input + * and output, as well as issuing the RPC by calling HG_Forward(). + * After completion the handle must be freed using HG_Destroy(). + * + * \param context [IN] pointer to HG context + * \param addr [IN] abstract network address of destination + * \param id [IN] registered function ID + * \param handle [OUT] pointer to HG handle + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Create(hg_context_t *context, hg_addr_t addr, hg_id_t id, hg_handle_t *handle); + +/** + * Destroy HG handle. Decrement reference count, resources associated to the + * handle are freed when the reference count is null. + * + * \param handle [IN] HG handle + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Destroy(hg_handle_t handle); + +/** + * Reset an existing HG handle to make it reusable for RPC forwarding. + * Both target address and RPC ID can be modified at this time. + * Operations on that handle must be completed in order to reset that handle + * safely. + * + * \param handle [IN] HG handle + * \param addr [IN] abstract network address of destination + * \param id [IN] registered function ID + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Reset(hg_handle_t handle, hg_addr_t addr, hg_id_t id); + +/** + * Increment ref count on handle. + * + * \param handle [IN] HG handle + * + * \return HG_SUCCESS or corresponding HG error code + */ +static HG_INLINE hg_return_t HG_Ref_incr(hg_handle_t hg_handle); + +/** + * Retrieve ref count from handle. + * + * \param handle [IN] HG handle + * + * \return Non-negative value or negative if the handle is not valid + */ +static HG_INLINE hg_int32_t HG_Ref_get(hg_handle_t handle); + +/** + * Get info from handle. + * + * \remark Users must call HG_Addr_dup() to safely re-use the addr field. + * + * \param handle [IN] HG handle + * + * \return Pointer to info or NULL in case of failure + */ +static HG_INLINE const struct hg_info *HG_Get_info(hg_handle_t handle); + +/** + * Associate user data to handle. When HG_Destroy() is called, + * free_callback (if defined) is called to free the associated data. + * + * \param handle [IN] HG handle + * \param data [IN] pointer to user data + * \param free_callback [IN] pointer to function + * + * \return HG_SUCCESS or corresponding HG error code + */ +static HG_INLINE hg_return_t HG_Set_data(hg_handle_t handle, void *data, void (*free_callback)(void *)); + +/** + * Retrieve previously associated data from a given handle. + * + * \param handle [IN] HG handle + * + * \return Pointer to user data or NULL if not set or any error has occurred + */ +static HG_INLINE void *HG_Get_data(hg_handle_t handle); + +/** + * Get input from handle (requires registration of input proc to deserialize + * parameters). Input must be freed using HG_Free_input(). + * + * \remark This is equivalent to: + * - HG_Core_get_input() + * - Call hg_proc to deserialize parameters + * + * \param handle [IN] HG handle + * \param in_struct [IN/OUT] pointer to input structure + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Get_input(hg_handle_t handle, void *in_struct); + +/** + * Free resources allocated when deserializing the input. + * User may copy parameters contained in the input structure before calling + * HG_Free_input(). + * + * \param handle [IN] HG handle + * \param in_struct [IN/OUT] pointer to input structure + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Free_input(hg_handle_t handle, void *in_struct); + +/** + * Get output from handle (requires registration of output proc to deserialize + * parameters). Output must be freed using HG_Free_output(). + * + * \remark This is equivalent to: + * - HG_Core_get_output() + * - Call hg_proc to deserialize parameters + * + * + * \param handle [IN] HG handle + * \param out_struct [IN/OUT] pointer to output structure + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Get_output(hg_handle_t handle, void *out_struct); + +/** + * Free resources allocated when deserializing the output. + * User may copy parameters contained in the output structure before calling + * HG_Free_output(). + * + * \param handle [IN] HG handle + * \param out_struct [IN/OUT] pointer to input structure + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Free_output(hg_handle_t handle, void *out_struct); + +/** + * Get raw input buffer from handle that can be used for encoding and decoding + * parameters. + * + * \remark Can be used for manual encoding / decoding when HG proc routines + * cannot be automatically used or there is need for special handling before + * HG_Get_input() can be called, for instance when using a custom header. + * To use proc routines conjunctively, HG_Class_set_input_offset() can be used + * to define the offset at which HG_Forward() / HG_Get_input() will start + * encoding / decoding the input parameters. + * + * \remark in_buf_size argument will be ignored if NULL + * + * \param handle [IN] HG handle + * \param in_buf [OUT] pointer to input buffer + * \param in_buf_size [OUT] pointer to input buffer size + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Get_input_buf(hg_handle_t handle, void **in_buf, hg_size_t *in_buf_size); + +/** + * Get raw output buffer from handle that can be used for encoding and decoding + * parameters. + * + * \remark Can be used for manual encoding / decoding when HG proc routines + * cannot be automatically used or there is need for special handling before + * HG_Get_output() can be called, for instance when using a custom header. + * To use proc routines conjunctively, HG_Class_set_output_offset() can be used + * to define the offset at which HG_Respond() / HG_Get_output() will start + * encoding / decoding the output parameters. + * + * \remark out_buf_size argument will be ignored if NULL + * + * \param handle [IN] HG handle + * \param out_buf [OUT] pointer to output buffer + * \param out_buf_size [OUT] pointer to output buffer size + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Get_output_buf(hg_handle_t handle, void **out_buf, hg_size_t *out_buf_size); + +/** + * Get raw extra input buffer from handle that can be used for encoding and + * decoding parameters. This buffer is only valid if the input payload is large + * enough that it cannot fit into an eager buffer. + * + * \remark NULL pointer will be returned if there is no associated buffer. + * + * \remark in_buf_size argument will be ignored if NULL. + * + * \param handle [IN] HG handle + * \param in_buf [OUT] pointer to input buffer + * \param in_buf_size [OUT] pointer to input buffer size + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Get_input_extra_buf(hg_handle_t handle, void **in_buf, hg_size_t *in_buf_size); + +/** + * Get raw extra output buffer from handle that can be used for encoding and + * decoding parameters. This buffer is only valid if the output payload is large + * enough that it cannot fit into an eager buffer. + * + * \remark NULL pointer will be returned if there is no associated buffer. + * + * \remark out_buf_size argument will be ignored if NULL. + * + * \param handle [IN] HG handle + * \param out_buf [OUT] pointer to output buffer + * \param out_buf_size [OUT] pointer to output buffer size + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Get_output_extra_buf(hg_handle_t handle, void **out_buf, hg_size_t *out_buf_size); + +/** + * Set target context ID that will receive and process the RPC request + * (ID is defined on target context creation, see HG_Context_create_id()). + * + * \param handle [IN] HG handle + * \param id [IN] user-defined target context ID + * + * \return HG_SUCCESS or corresponding HG error code + */ +static HG_INLINE hg_return_t HG_Set_target_id(hg_handle_t handle, hg_uint8_t id); + +/** + * Forward a call to a local/remote target using an existing HG handle. + * Input structure can be passed and parameters serialized using a previously + * registered input proc. After completion, user callback is placed into a + * completion queue and can be triggered using HG_Trigger(). RPC output can + * be queried using HG_Get_output() and freed using HG_Free_output(). + * + * \remark This routine is internally equivalent to: + * - HG_Core_get_input() + * - Call hg_proc to serialize parameters + * - HG_Core_forward() + * + * \param handle [IN] HG handle + * \param callback [IN] pointer to function callback + * \param arg [IN] pointer to data passed to callback + * \param in_struct [IN] pointer to input structure + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Forward(hg_handle_t handle, hg_cb_t callback, void *arg, void *in_struct); + +/** + * Respond back to origin using an existing HG handle. + * Output structure can be passed and parameters serialized using a previously + * registered output proc. After completion, user callback is placed into a + * completion queue and can be triggered using HG_Trigger(). + * + * \remark This routine is internally equivalent to: + * - HG_Core_get_output() + * - Call hg_proc to serialize parameters + * - HG_Core_respond() + * + * \param handle [IN] HG handle + * \param callback [IN] pointer to function callback + * \param arg [IN] pointer to data passed to callback + * \param out_struct [IN] pointer to output structure + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Respond(hg_handle_t handle, hg_cb_t callback, void *arg, void *out_struct); + +/** + * Try to progress RPC execution for at most timeout until timeout is reached or + * any completion has occurred. + * Progress should not be considered as wait, in the sense that it cannot be + * assumed that completion of a specific operation will occur only when + * progress is called. + * + * \param context [IN] pointer to HG context + * \param timeout [IN] timeout (in milliseconds) + * + * \return HG_SUCCESS if any completion has occurred / HG error code otherwise + */ +HG_PUBLIC hg_return_t HG_Progress(hg_context_t *context, unsigned int timeout); + +/** + * Execute at most max_count callbacks. If timeout is non-zero, wait up to + * timeout before returning. Function can return when at least one or more + * callbacks are triggered (at most max_count). + * + * \param context [IN] pointer to HG context + * \param timeout [IN] timeout (in milliseconds) + * \param max_count [IN] maximum number of callbacks triggered + * \param actual_count [IN] actual number of callbacks triggered + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Trigger(hg_context_t *context, unsigned int timeout, unsigned int max_count, + unsigned int *actual_count); + +/** + * Cancel an ongoing operation. + * + * \param handle [IN] HG handle + * + * \return HG_SUCCESS or HG_CANCEL_ERROR or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Cancel(hg_handle_t handle); + +/************************************/ +/* Local Type and Struct Definition */ +/************************************/ + +/* HG class */ +struct hg_class { + hg_core_class_t *core_class; /* Core class */ + hg_size_t in_offset; /* Input offset */ + hg_size_t out_offset; /* Output offset */ +}; + +/* HG context */ +struct hg_context { + hg_core_context_t *core_context; /* Core context */ + hg_class_t * hg_class; /* HG class */ +}; + +/* HG handle */ +struct hg_handle { + struct hg_info info; /* HG info */ + hg_core_handle_t core_handle; /* Core handle */ + void * data; /* User data */ + void (*data_free_callback)(void *); /* User data free callback */ +}; + +/*---------------------------------------------------------------------------*/ +static HG_INLINE const char * +HG_Class_get_name(const hg_class_t *hg_class) +{ + return HG_Core_class_get_name(hg_class->core_class); +} + +/*---------------------------------------------------------------------------*/ +static HG_INLINE const char * +HG_Class_get_protocol(const hg_class_t *hg_class) +{ + return HG_Core_class_get_protocol(hg_class->core_class); +} + +/*---------------------------------------------------------------------------*/ +static HG_INLINE hg_bool_t +HG_Class_is_listening(const hg_class_t *hg_class) +{ + return HG_Core_class_is_listening(hg_class->core_class); +} + +/*---------------------------------------------------------------------------*/ +static HG_INLINE hg_size_t +HG_Class_get_input_eager_size(const hg_class_t *hg_class) +{ + hg_size_t core = HG_Core_class_get_input_eager_size(hg_class->core_class), + header = hg_header_get_size(HG_INPUT); + + return (core > header) ? core - header : 0; +} + +/*---------------------------------------------------------------------------*/ +static HG_INLINE hg_size_t +HG_Class_get_output_eager_size(const hg_class_t *hg_class) +{ + hg_size_t core = HG_Core_class_get_output_eager_size(hg_class->core_class), + header = hg_header_get_size(HG_OUTPUT); + + return (core > header) ? core - header : 0; +} + +/*---------------------------------------------------------------------------*/ +static HG_INLINE hg_return_t +HG_Class_set_input_offset(hg_class_t *hg_class, hg_size_t offset) +{ + /* Extra input header must not be larger than eager size */ + if (offset > HG_Class_get_input_eager_size(hg_class)) + return HG_INVALID_ARG; + + hg_class->in_offset = offset; + + return HG_SUCCESS; +} + +/*---------------------------------------------------------------------------*/ +static HG_INLINE hg_return_t +HG_Class_set_output_offset(hg_class_t *hg_class, hg_size_t offset) +{ + /* Extra output header must not be larger than eager size */ + if (offset > HG_Class_get_output_eager_size(hg_class)) + return HG_INVALID_ARG; + + hg_class->out_offset = offset; + + return HG_SUCCESS; +} + +/*---------------------------------------------------------------------------*/ +static HG_INLINE hg_return_t +HG_Class_set_data(hg_class_t *hg_class, void *data, void (*free_callback)(void *)) +{ + return HG_Core_class_set_data(hg_class->core_class, data, free_callback); +} + +/*---------------------------------------------------------------------------*/ +static HG_INLINE void * +HG_Class_get_data(const hg_class_t *hg_class) +{ + return HG_Core_class_get_data(hg_class->core_class); +} + +/*---------------------------------------------------------------------------*/ +static HG_INLINE hg_class_t * + HG_Context_get_class(const hg_context_t *context) +{ + return context->hg_class; +} + +/*---------------------------------------------------------------------------*/ +static HG_INLINE hg_uint8_t +HG_Context_get_id(const hg_context_t *context) +{ + return HG_Core_context_get_id(context->core_context); +} + +/*---------------------------------------------------------------------------*/ +static HG_INLINE hg_return_t +HG_Context_set_data(hg_context_t *context, void *data, void (*free_callback)(void *)) +{ + return HG_Core_context_set_data(context->core_context, data, free_callback); +} + +/*---------------------------------------------------------------------------*/ +static HG_INLINE void * +HG_Context_get_data(const hg_context_t *context) +{ + return HG_Core_context_get_data(context->core_context); +} + +/*---------------------------------------------------------------------------*/ +static HG_INLINE hg_return_t +HG_Ref_incr(hg_handle_t handle) +{ + return HG_Core_ref_incr(handle->core_handle); +} + +/*---------------------------------------------------------------------------*/ +static HG_INLINE hg_int32_t +HG_Ref_get(hg_handle_t handle) +{ + return HG_Core_ref_get(handle->core_handle); +} + +/*---------------------------------------------------------------------------*/ +static HG_INLINE const struct hg_info * +HG_Get_info(hg_handle_t handle) +{ + return &handle->info; +} + +/*---------------------------------------------------------------------------*/ +static HG_INLINE hg_return_t +HG_Set_data(hg_handle_t handle, void *data, void (*free_callback)(void *)) +{ + handle->data = data; + handle->data_free_callback = free_callback; + + return HG_SUCCESS; +} + +/*---------------------------------------------------------------------------*/ +static HG_INLINE void * +HG_Get_data(hg_handle_t handle) +{ + return handle->data; +} + +/*---------------------------------------------------------------------------*/ +static HG_INLINE hg_return_t +HG_Set_target_id(hg_handle_t handle, hg_uint8_t id) +{ + handle->info.context_id = id; + + return HG_Core_set_target_id(handle->core_handle, id); +} + +#ifdef __cplusplus +} +#endif + +#endif /* MERCURY_H */ diff --git a/src/mercury/include/mercury_atomic.h b/src/mercury/include/mercury_atomic.h new file mode 100644 index 0000000..d5a1417 --- /dev/null +++ b/src/mercury/include/mercury_atomic.h @@ -0,0 +1,625 @@ +/* + * Copyright (C) 2013-2020 Argonne National Laboratory, Department of Energy, + * UChicago Argonne, LLC and The HDF Group. + * All rights reserved. + * + * The full copyright notice, including terms governing use, modification, + * and redistribution, is contained in the COPYING file that can be + * found at the root of the source code distribution tree. + */ + +#ifndef MERCURY_ATOMIC_H +#define MERCURY_ATOMIC_H + +#include "mercury_util_config.h" + +#if defined(_WIN32) +#include <windows.h> +typedef struct { + volatile LONG value; +} hg_atomic_int32_t; +typedef struct { + volatile LONGLONG value; +} hg_atomic_int64_t; +#define HG_ATOMIC_VAR_INIT(x) \ + { \ + (x) \ + } +#elif defined(HG_UTIL_HAS_OPA_PRIMITIVES_H) +#include <opa_primitives.h> +typedef OPA_int_t hg_atomic_int32_t; +typedef OPA_ptr_t hg_atomic_int64_t; /* OPA has only limited 64-bit support */ +#define HG_ATOMIC_VAR_INIT(x) OPA_PTR_T_INITIALIZER(x) +#elif defined(HG_UTIL_HAS_STDATOMIC_H) +#ifndef __cplusplus +#include <stdatomic.h> +typedef atomic_int hg_atomic_int32_t; +#if (HG_UTIL_ATOMIC_LONG_WIDTH == 8) && !defined(__APPLE__) +typedef atomic_long hg_atomic_int64_t; +#else +typedef atomic_llong hg_atomic_int64_t; +#endif +#else +#include <atomic> +typedef std::atomic_int hg_atomic_int32_t; +#if (HG_UTIL_ATOMIC_LONG_WIDTH == 8) && !defined(__APPLE__) +typedef std::atomic_long hg_atomic_int64_t; +#else +typedef std::atomic_llong hg_atomic_int64_t; +#endif +using std::atomic_fetch_add_explicit; +using std::atomic_thread_fence; +using std::memory_order_acq_rel; +using std::memory_order_acquire; +using std::memory_order_release; +#endif +#define HG_ATOMIC_VAR_INIT(x) ATOMIC_VAR_INIT(x) +#elif defined(__APPLE__) +#include <libkern/OSAtomic.h> +typedef struct { + volatile hg_util_int32_t value; +} hg_atomic_int32_t; +typedef struct { + volatile hg_util_int64_t value; +} hg_atomic_int64_t; +#define HG_ATOMIC_VAR_INIT(x) \ + { \ + (x) \ + } +#else +#error "Not supported on this platform." +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Init atomic value (32-bit integer). + * + * \param ptr [OUT] pointer to an atomic32 integer + * \param value [IN] value + */ +static HG_UTIL_INLINE void hg_atomic_init32(hg_atomic_int32_t *ptr, hg_util_int32_t value); + +/** + * Set atomic value (32-bit integer). + * + * \param ptr [OUT] pointer to an atomic32 integer + * \param value [IN] value + */ +static HG_UTIL_INLINE void hg_atomic_set32(hg_atomic_int32_t *ptr, hg_util_int32_t value); + +/** + * Get atomic value (32-bit integer). + * + * \param ptr [OUT] pointer to an atomic32 integer + * + * \return Value of the atomic integer + */ +static HG_UTIL_INLINE hg_util_int32_t hg_atomic_get32(hg_atomic_int32_t *ptr); + +/** + * Increment atomic value (32-bit integer). + * + * \param ptr [IN/OUT] pointer to an atomic32 integer + * + * \return Incremented value + */ +static HG_UTIL_INLINE hg_util_int32_t hg_atomic_incr32(hg_atomic_int32_t *ptr); + +/** + * Decrement atomic value (32-bit integer). + * + * \param ptr [IN/OUT] pointer to an atomic32 integer + * + * \return Decremented value + */ +static HG_UTIL_INLINE hg_util_int32_t hg_atomic_decr32(hg_atomic_int32_t *ptr); + +/** + * OR atomic value (32-bit integer). + * + * \param ptr [IN/OUT] pointer to an atomic32 integer + * \param value [IN] value to OR with + * + * \return Original value + */ +static HG_UTIL_INLINE hg_util_int32_t hg_atomic_or32(hg_atomic_int32_t *ptr, hg_util_int32_t value); + +/** + * XOR atomic value (32-bit integer). + * + * \param ptr [IN/OUT] pointer to an atomic32 integer + * \param value [IN] value to XOR with + * + * \return Original value + */ +static HG_UTIL_INLINE hg_util_int32_t hg_atomic_xor32(hg_atomic_int32_t *ptr, hg_util_int32_t value); + +/** + * AND atomic value (32-bit integer). + * + * \param ptr [IN/OUT] pointer to an atomic32 integer + * \param value [IN] value to AND with + * + * \return Original value + */ +static HG_UTIL_INLINE hg_util_int32_t hg_atomic_and32(hg_atomic_int32_t *ptr, hg_util_int32_t value); + +/** + * Compare and swap values (32-bit integer). + * + * \param ptr [IN/OUT] pointer to an atomic32 integer + * \param compare_value [IN] value to compare to + * \param swap_value [IN] value to swap with if ptr value is equal to + * compare value + * + * \return HG_UTIL_TRUE if swapped or HG_UTIL_FALSE + */ +static HG_UTIL_INLINE hg_util_bool_t hg_atomic_cas32(hg_atomic_int32_t *ptr, hg_util_int32_t compare_value, + hg_util_int32_t swap_value); + +/** + * Init atomic value (64-bit integer). + * + * \param ptr [OUT] pointer to an atomic32 integer + * \param value [IN] value + */ +static HG_UTIL_INLINE void hg_atomic_init64(hg_atomic_int64_t *ptr, hg_util_int64_t value); + +/** + * Set atomic value (64-bit integer). + * + * \param ptr [OUT] pointer to an atomic64 integer + * \param value [IN] value + */ +static HG_UTIL_INLINE void hg_atomic_set64(hg_atomic_int64_t *ptr, hg_util_int64_t value); + +/** + * Get atomic value (64-bit integer). + * + * \param ptr [OUT] pointer to an atomic64 integer + * + * \return Value of the atomic integer + */ +static HG_UTIL_INLINE hg_util_int64_t hg_atomic_get64(hg_atomic_int64_t *ptr); + +/** + * Increment atomic value (64-bit integer). + * + * \param ptr [IN/OUT] pointer to an atomic64 integer + * + * \return Incremented value + */ +static HG_UTIL_INLINE hg_util_int64_t hg_atomic_incr64(hg_atomic_int64_t *ptr); + +/** + * Decrement atomic value (64-bit integer). + * + * \param ptr [IN/OUT] pointer to an atomic64 integer + * + * \return Decremented value + */ +static HG_UTIL_INLINE hg_util_int64_t hg_atomic_decr64(hg_atomic_int64_t *ptr); + +/** + * OR atomic value (64-bit integer). + * + * \param ptr [IN/OUT] pointer to an atomic64 integer + * \param value [IN] value to OR with + * + * \return Original value + */ +static HG_UTIL_INLINE hg_util_int64_t hg_atomic_or64(hg_atomic_int64_t *ptr, hg_util_int64_t value); + +/** + * XOR atomic value (64-bit integer). + * + * \param ptr [IN/OUT] pointer to an atomic64 integer + * \param value [IN] value to XOR with + * + * \return Original value + */ +static HG_UTIL_INLINE hg_util_int64_t hg_atomic_xor64(hg_atomic_int64_t *ptr, hg_util_int64_t value); + +/** + * AND atomic value (64-bit integer). + * + * \param ptr [IN/OUT] pointer to an atomic64 integer + * \param value [IN] value to AND with + * + * \return Original value + */ +static HG_UTIL_INLINE hg_util_int64_t hg_atomic_and64(hg_atomic_int64_t *ptr, hg_util_int64_t value); + +/** + * Compare and swap values (64-bit integer). + * + * \param ptr [IN/OUT] pointer to an atomic64 integer + * \param compare_value [IN] value to compare to + * \param swap_value [IN] value to swap with if ptr value is equal to + * compare value + * + * \return HG_UTIL_TRUE if swapped or HG_UTIL_FALSE + */ +static HG_UTIL_INLINE hg_util_bool_t hg_atomic_cas64(hg_atomic_int64_t *ptr, hg_util_int64_t compare_value, + hg_util_int64_t swap_value); + +/** + * Memory barrier. + * + */ +static HG_UTIL_INLINE void hg_atomic_fence(void); + +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE void +hg_atomic_init32(hg_atomic_int32_t *ptr, hg_util_int32_t value) +{ +#if defined(HG_UTIL_HAS_STDATOMIC_H) && !defined(HG_UTIL_HAS_OPA_PRIMITIVES_H) + atomic_init(ptr, value); +#else + hg_atomic_set32(ptr, value); +#endif +} + +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE void +hg_atomic_set32(hg_atomic_int32_t *ptr, hg_util_int32_t value) +{ +#if defined(_WIN32) + ptr->value = value; +#elif defined(HG_UTIL_HAS_OPA_PRIMITIVES_H) + OPA_store_int(ptr, value); +#elif defined(HG_UTIL_HAS_STDATOMIC_H) + atomic_store_explicit(ptr, value, memory_order_release); +#elif defined(__APPLE__) + ptr->value = value; +#else +#error "Not supported on this platform." +#endif +} + +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE hg_util_int32_t +hg_atomic_get32(hg_atomic_int32_t *ptr) +{ + hg_util_int32_t ret; + +#if defined(_WIN32) + ret = ptr->value; +#elif defined(HG_UTIL_HAS_OPA_PRIMITIVES_H) + ret = OPA_load_int(ptr); +#elif defined(HG_UTIL_HAS_STDATOMIC_H) + ret = atomic_load_explicit(ptr, memory_order_acquire); +#elif defined(__APPLE__) + ret = ptr->value; +#else +#error "Not supported on this platform." +#endif + + return ret; +} + +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE hg_util_int32_t +hg_atomic_incr32(hg_atomic_int32_t *ptr) +{ + hg_util_int32_t ret; + +#if defined(_WIN32) + ret = InterlockedIncrementNoFence(&ptr->value); +#elif defined(HG_UTIL_HAS_OPA_PRIMITIVES_H) + ret = OPA_fetch_and_incr_int(ptr) + 1; +#elif defined(HG_UTIL_HAS_STDATOMIC_H) + ret = atomic_fetch_add_explicit(ptr, 1, memory_order_acq_rel) + 1; +#elif defined(__APPLE__) + ret = OSAtomicIncrement32(&ptr->value); +#else +#error "Not supported on this platform." +#endif + + return ret; +} + +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE hg_util_int32_t +hg_atomic_decr32(hg_atomic_int32_t *ptr) +{ + hg_util_int32_t ret; + +#if defined(_WIN32) + ret = InterlockedDecrementNoFence(&ptr->value); +#elif defined(HG_UTIL_HAS_OPA_PRIMITIVES_H) + ret = OPA_fetch_and_decr_int(ptr) - 1; +#elif defined(HG_UTIL_HAS_STDATOMIC_H) + ret = atomic_fetch_sub_explicit(ptr, 1, memory_order_acq_rel) - 1; +#elif defined(__APPLE__) + ret = OSAtomicDecrement32(&ptr->value); +#else +#error "Not supported on this platform." +#endif + + return ret; +} + +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE hg_util_int32_t +hg_atomic_or32(hg_atomic_int32_t *ptr, hg_util_int32_t value) +{ + hg_util_int32_t ret; + +#if defined(_WIN32) + ret = InterlockedOrNoFence(&ptr->value, value); +#elif defined(HG_UTIL_HAS_STDATOMIC_H) && !defined(HG_UTIL_HAS_OPA_PRIMITIVES_H) + ret = atomic_fetch_or_explicit(ptr, value, memory_order_acq_rel); +#elif defined(__APPLE__) + ret = OSAtomicOr32Orig((uint32_t)value, (volatile uint32_t *)&ptr->value); +#else + do { + ret = hg_atomic_get32(ptr); + } while (!hg_atomic_cas32(ptr, ret, (ret | value))); +#endif + + return ret; +} + +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE hg_util_int32_t +hg_atomic_xor32(hg_atomic_int32_t *ptr, hg_util_int32_t value) +{ + hg_util_int32_t ret; + +#if defined(_WIN32) + ret = InterlockedXorNoFence(&ptr->value, value); +#elif defined(HG_UTIL_HAS_STDATOMIC_H) && !defined(HG_UTIL_HAS_OPA_PRIMITIVES_H) + ret = atomic_fetch_xor_explicit(ptr, value, memory_order_acq_rel); +#elif defined(__APPLE__) + ret = OSAtomicXor32Orig((uint32_t)value, (volatile uint32_t *)&ptr->value); +#else + do { + ret = hg_atomic_get32(ptr); + } while (!hg_atomic_cas32(ptr, ret, (ret ^ value))); +#endif + + return ret; +} + +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE hg_util_int32_t +hg_atomic_and32(hg_atomic_int32_t *ptr, hg_util_int32_t value) +{ + hg_util_int32_t ret; + +#if defined(_WIN32) + ret = InterlockedAndNoFence(&ptr->value, value); +#elif defined(HG_UTIL_HAS_STDATOMIC_H) && !defined(HG_UTIL_HAS_OPA_PRIMITIVES_H) + ret = atomic_fetch_and_explicit(ptr, value, memory_order_acq_rel); +#elif defined(__APPLE__) + ret = OSAtomicAnd32Orig((uint32_t)value, (volatile uint32_t *)&ptr->value); +#else + do { + ret = hg_atomic_get32(ptr); + } while (!hg_atomic_cas32(ptr, ret, (ret & value))); +#endif + + return ret; +} + +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE hg_util_bool_t +hg_atomic_cas32(hg_atomic_int32_t *ptr, hg_util_int32_t compare_value, hg_util_int32_t swap_value) +{ + hg_util_bool_t ret; + +#if defined(_WIN32) + ret = (compare_value == InterlockedCompareExchangeNoFence(&ptr->value, swap_value, compare_value)); +#elif defined(HG_UTIL_HAS_OPA_PRIMITIVES_H) + ret = (hg_util_bool_t)(compare_value == OPA_cas_int(ptr, compare_value, swap_value)); +#elif defined(HG_UTIL_HAS_STDATOMIC_H) + ret = atomic_compare_exchange_strong_explicit(ptr, &compare_value, swap_value, memory_order_acq_rel, + memory_order_acquire); +#elif defined(__APPLE__) + ret = OSAtomicCompareAndSwap32(compare_value, swap_value, &ptr->value); +#else +#error "Not supported on this platform." +#endif + + return ret; +} + +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE void +hg_atomic_init64(hg_atomic_int64_t *ptr, hg_util_int64_t value) +{ +#if defined(HG_UTIL_HAS_STDATOMIC_H) && !defined(HG_UTIL_HAS_OPA_PRIMITIVES_H) + atomic_init(ptr, value); +#else + hg_atomic_set64(ptr, value); +#endif +} + +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE void +hg_atomic_set64(hg_atomic_int64_t *ptr, hg_util_int64_t value) +{ +#if defined(_WIN32) + ptr->value = value; +#elif defined(HG_UTIL_HAS_OPA_PRIMITIVES_H) + OPA_store_ptr(ptr, (void *)value); +#elif defined(HG_UTIL_HAS_STDATOMIC_H) + atomic_store_explicit(ptr, value, memory_order_release); +#elif defined(__APPLE__) + ptr->value = value; +#else +#error "Not supported on this platform." +#endif +} + +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE hg_util_int64_t +hg_atomic_get64(hg_atomic_int64_t *ptr) +{ + hg_util_int64_t ret; + +#if defined(_WIN32) + ret = ptr->value; +#elif defined(HG_UTIL_HAS_OPA_PRIMITIVES_H) + ret = (hg_util_int64_t)OPA_load_ptr(ptr); +#elif defined(HG_UTIL_HAS_STDATOMIC_H) + ret = atomic_load_explicit(ptr, memory_order_acquire); +#elif defined(__APPLE__) + ptr->value = value; +#else +#error "Not supported on this platform." +#endif + + return ret; +} + +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE hg_util_int64_t +hg_atomic_incr64(hg_atomic_int64_t *ptr) +{ + hg_util_int64_t ret; + +#if defined(_WIN32) + ret = InterlockedIncrementNoFence64(&ptr->value); +#elif defined(HG_UTIL_HAS_STDATOMIC_H) && !defined(HG_UTIL_HAS_OPA_PRIMITIVES_H) + ret = atomic_fetch_add_explicit(ptr, 1L, memory_order_acq_rel) + 1; +#elif defined(__APPLE__) + ret = OSAtomicIncrement64(&ptr->value); +#else + do { + ret = hg_atomic_get64(ptr); + } while (!hg_atomic_cas64(ptr, ret, ret + 1)); + ret++; +#endif + + return ret; +} + +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE hg_util_int64_t +hg_atomic_decr64(hg_atomic_int64_t *ptr) +{ + hg_util_int64_t ret; + +#if defined(_WIN32) + ret = InterlockedDecrementNoFence64(&ptr->value); +#elif defined(HG_UTIL_HAS_STDATOMIC_H) && !defined(HG_UTIL_HAS_OPA_PRIMITIVES_H) + ret = atomic_fetch_sub_explicit(ptr, 1L, memory_order_acq_rel) - 1; +#elif defined(__APPLE__) + ret = OSAtomicDecrement64(&ptr->value); +#else + do { + ret = hg_atomic_get64(ptr); + } while (!hg_atomic_cas64(ptr, ret, ret - 1)); + ret--; +#endif + + return ret; +} + +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE hg_util_int64_t +hg_atomic_or64(hg_atomic_int64_t *ptr, hg_util_int64_t value) +{ + hg_util_int64_t ret; + +#if defined(_WIN32) + ret = InterlockedOr64NoFence(&ptr->value, value); +#elif defined(HG_UTIL_HAS_STDATOMIC_H) && !defined(HG_UTIL_HAS_OPA_PRIMITIVES_H) + ret = atomic_fetch_or_explicit(ptr, value, memory_order_acq_rel); +#else + do { + ret = hg_atomic_get64(ptr); + } while (!hg_atomic_cas64(ptr, ret, (ret | value))); +#endif + + return ret; +} + +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE hg_util_int64_t +hg_atomic_xor64(hg_atomic_int64_t *ptr, hg_util_int64_t value) +{ + hg_util_int64_t ret; + +#if defined(_WIN32) + ret = InterlockedXor64NoFence(&ptr->value, value); +#elif defined(HG_UTIL_HAS_STDATOMIC_H) && !defined(HG_UTIL_HAS_OPA_PRIMITIVES_H) + ret = atomic_fetch_xor_explicit(ptr, value, memory_order_acq_rel); +#else + do { + ret = hg_atomic_get64(ptr); + } while (!hg_atomic_cas64(ptr, ret, (ret ^ value))); +#endif + + return ret; +} + +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE hg_util_int64_t +hg_atomic_and64(hg_atomic_int64_t *ptr, hg_util_int64_t value) +{ + hg_util_int64_t ret; + +#if defined(_WIN32) + ret = InterlockedAnd64NoFence(&ptr->value, value); +#elif defined(HG_UTIL_HAS_STDATOMIC_H) && !defined(HG_UTIL_HAS_OPA_PRIMITIVES_H) + ret = atomic_fetch_and_explicit(ptr, value, memory_order_acq_rel); +#else + do { + ret = hg_atomic_get64(ptr); + } while (!hg_atomic_cas64(ptr, ret, (ret & value))); +#endif + + return ret; +} + +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE hg_util_bool_t +hg_atomic_cas64(hg_atomic_int64_t *ptr, hg_util_int64_t compare_value, hg_util_int64_t swap_value) +{ + hg_util_bool_t ret; + +#if defined(_WIN32) + ret = (compare_value == InterlockedCompareExchangeNoFence64(&ptr->value, swap_value, compare_value)); +#elif defined(HG_UTIL_HAS_OPA_PRIMITIVES_H) + ret = (hg_util_bool_t)(compare_value == + (hg_util_int64_t)OPA_cas_ptr(ptr, (void *)compare_value, (void *)swap_value)); +#elif defined(HG_UTIL_HAS_STDATOMIC_H) + ret = atomic_compare_exchange_strong_explicit(ptr, &compare_value, swap_value, memory_order_acq_rel, + memory_order_acquire); +#elif defined(__APPLE__) + ret = OSAtomicCompareAndSwap64(compare_value, swap_value, &ptr->value); +#else +#error "Not supported on this platform." +#endif + + return ret; +} + +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE void +hg_atomic_fence() +{ +#if defined(_WIN32) + MemoryBarrier(); +#elif defined(HG_UTIL_HAS_OPA_PRIMITIVES_H) + OPA_read_write_barrier(); +#elif defined(HG_UTIL_HAS_STDATOMIC_H) + atomic_thread_fence(memory_order_acq_rel); +#elif defined(__APPLE__) + OSMemoryBarrier(); +#else +#error "Not supported on this platform." +#endif +} + +#ifdef __cplusplus +} +#endif + +#endif /* MERCURY_ATOMIC_H */ diff --git a/src/mercury/include/mercury_atomic_queue.h b/src/mercury/include/mercury_atomic_queue.h new file mode 100644 index 0000000..61b5128 --- /dev/null +++ b/src/mercury/include/mercury_atomic_queue.h @@ -0,0 +1,266 @@ +/* + * Copyright (C) 2013-2020 Argonne National Laboratory, Department of Energy, + * UChicago Argonne, LLC and The HDF Group. + * All rights reserved. + * + * The full copyright notice, including terms governing use, modification, + * and redistribution, is contained in the COPYING file that can be + * found at the root of the source code distribution tree. + */ + +/* Implementation derived from: + * https://github.com/freebsd/freebsd/blob/master/sys/sys/buf_ring.h + * + * - + * Copyright (c) 2007-2009 Kip Macy <kmacy@freebsd.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#ifndef MERCURY_ATOMIC_QUEUE_H +#define MERCURY_ATOMIC_QUEUE_H + +#include "mercury_atomic.h" +#include "mercury_mem.h" + +/* For busy loop spinning */ +#ifndef cpu_spinwait +#if defined(_WIN32) +#define cpu_spinwait YieldProcessor +#elif defined(__x86_64__) || defined(__i386__) +#include <immintrin.h> +#define cpu_spinwait _mm_pause +#elif defined(__arm__) +#define cpu_spinwait() __asm__ __volatile__("yield") +#else +#warning "Processor yield is not supported on this architecture." +#define cpu_spinwait(x) +#endif +#endif + +/*************************************/ +/* Public Type and Struct Definition */ +/*************************************/ + +struct hg_atomic_queue { + hg_atomic_int32_t prod_head; + hg_atomic_int32_t prod_tail; + unsigned int prod_size; + unsigned int prod_mask; + hg_util_uint64_t drops; + hg_atomic_int32_t cons_head __attribute__((aligned(HG_MEM_CACHE_LINE_SIZE))); + hg_atomic_int32_t cons_tail; + unsigned int cons_size; + unsigned int cons_mask; + hg_atomic_int64_t ring[] __attribute__((aligned(HG_MEM_CACHE_LINE_SIZE))); +}; + +/*****************/ +/* Public Macros */ +/*****************/ + +/*********************/ +/* Public Prototypes */ +/*********************/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Allocate a new queue that can hold \count elements. + * + * \param count [IN] maximum number of elements + * + * \return pointer to allocated queue or NULL on failure + */ +HG_UTIL_PUBLIC struct hg_atomic_queue *hg_atomic_queue_alloc(unsigned int count); + +/** + * Free an existing queue. + * + * \param hg_atomic_queue [IN] pointer to queue + */ +HG_UTIL_PUBLIC void hg_atomic_queue_free(struct hg_atomic_queue *hg_atomic_queue); + +/** + * Push an entry to the queue. + * + * \param hg_atomic_queue [IN/OUT] pointer to queue + * \param entry [IN] pointer to object + * + * \return Non-negative on success or negative on failure + */ +static HG_UTIL_INLINE int hg_atomic_queue_push(struct hg_atomic_queue *hg_atomic_queue, void *entry); + +/** + * Pop an entry from the queue (multi-consumer). + * + * \param hg_atomic_queue [IN/OUT] pointer to queue + * + * \return Pointer to popped object or NULL if queue is empty + */ +static HG_UTIL_INLINE void *hg_atomic_queue_pop_mc(struct hg_atomic_queue *hg_atomic_queue); + +/** + * Pop an entry from the queue (single consumer). + * + * \param hg_atomic_queue [IN/OUT] pointer to queue + * + * \return Pointer to popped object or NULL if queue is empty + */ +static HG_UTIL_INLINE void *hg_atomic_queue_pop_sc(struct hg_atomic_queue *hg_atomic_queue); + +/** + * Determine whether queue is empty. + * + * \param hg_atomic_queue [IN/OUT] pointer to queue + * + * \return HG_UTIL_TRUE if empty, HG_UTIL_FALSE if not + */ +static HG_UTIL_INLINE hg_util_bool_t hg_atomic_queue_is_empty(struct hg_atomic_queue *hg_atomic_queue); + +/** + * Determine number of entries in a queue. + * + * \param hg_atomic_queue [IN/OUT] pointer to queue + * + * \return Number of entries queued or 0 if none + */ +static HG_UTIL_INLINE unsigned int hg_atomic_queue_count(struct hg_atomic_queue *hg_atomic_queue); + +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE int +hg_atomic_queue_push(struct hg_atomic_queue *hg_atomic_queue, void *entry) +{ + hg_util_int32_t prod_head, prod_next, cons_tail; + + do { + prod_head = hg_atomic_get32(&hg_atomic_queue->prod_head); + prod_next = (prod_head + 1) & (int)hg_atomic_queue->prod_mask; + cons_tail = hg_atomic_get32(&hg_atomic_queue->cons_tail); + + if (prod_next == cons_tail) { + hg_atomic_fence(); + if (prod_head == hg_atomic_get32(&hg_atomic_queue->prod_head) && + cons_tail == hg_atomic_get32(&hg_atomic_queue->cons_tail)) { + hg_atomic_queue->drops++; + /* Full */ + return HG_UTIL_FAIL; + } + continue; + } + } while (!hg_atomic_cas32(&hg_atomic_queue->prod_head, prod_head, prod_next)); + + hg_atomic_set64(&hg_atomic_queue->ring[prod_head], (hg_util_int64_t)entry); + + /* + * If there are other enqueues in progress + * that preceded us, we need to wait for them + * to complete + */ + while (hg_atomic_get32(&hg_atomic_queue->prod_tail) != prod_head) + cpu_spinwait(); + + hg_atomic_set32(&hg_atomic_queue->prod_tail, prod_next); + + return HG_UTIL_SUCCESS; +} + +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE void * +hg_atomic_queue_pop_mc(struct hg_atomic_queue *hg_atomic_queue) +{ + hg_util_int32_t cons_head, cons_next; + void * entry = NULL; + + do { + cons_head = hg_atomic_get32(&hg_atomic_queue->cons_head); + cons_next = (cons_head + 1) & (int)hg_atomic_queue->cons_mask; + + if (cons_head == hg_atomic_get32(&hg_atomic_queue->prod_tail)) + return NULL; + } while (!hg_atomic_cas32(&hg_atomic_queue->cons_head, cons_head, cons_next)); + + entry = (void *)hg_atomic_get64(&hg_atomic_queue->ring[cons_head]); + + /* + * If there are other dequeues in progress + * that preceded us, we need to wait for them + * to complete + */ + while (hg_atomic_get32(&hg_atomic_queue->cons_tail) != cons_head) + cpu_spinwait(); + + hg_atomic_set32(&hg_atomic_queue->cons_tail, cons_next); + + return entry; +} + +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE void * +hg_atomic_queue_pop_sc(struct hg_atomic_queue *hg_atomic_queue) +{ + hg_util_int32_t cons_head, cons_next; + hg_util_int32_t prod_tail; + void * entry = NULL; + + cons_head = hg_atomic_get32(&hg_atomic_queue->cons_head); + prod_tail = hg_atomic_get32(&hg_atomic_queue->prod_tail); + cons_next = (cons_head + 1) & (int)hg_atomic_queue->cons_mask; + + if (cons_head == prod_tail) + /* Empty */ + return NULL; + + hg_atomic_set32(&hg_atomic_queue->cons_head, cons_next); + + entry = (void *)hg_atomic_get64(&hg_atomic_queue->ring[cons_head]); + + hg_atomic_set32(&hg_atomic_queue->cons_tail, cons_next); + + return entry; +} + +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE hg_util_bool_t +hg_atomic_queue_is_empty(struct hg_atomic_queue *hg_atomic_queue) +{ + return (hg_atomic_get32(&hg_atomic_queue->cons_head) == hg_atomic_get32(&hg_atomic_queue->prod_tail)); +} + +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE unsigned int +hg_atomic_queue_count(struct hg_atomic_queue *hg_atomic_queue) +{ + return ((hg_atomic_queue->prod_size + (unsigned int)hg_atomic_get32(&hg_atomic_queue->prod_tail) - + (unsigned int)hg_atomic_get32(&hg_atomic_queue->cons_tail)) & + hg_atomic_queue->prod_mask); +} + +#ifdef __cplusplus +} +#endif + +#endif /* MERCURY_ATOMIC_QUEUE_H */ diff --git a/src/mercury/include/mercury_bulk.h b/src/mercury/include/mercury_bulk.h new file mode 100644 index 0000000..598a842 --- /dev/null +++ b/src/mercury/include/mercury_bulk.h @@ -0,0 +1,336 @@ +/* + * Copyright (C) 2013-2020 Argonne National Laboratory, Department of Energy, + * UChicago Argonne, LLC and The HDF Group. + * All rights reserved. + * + * The full copyright notice, including terms governing use, modification, + * and redistribution, is contained in the COPYING file that can be + * found at the root of the source code distribution tree. + */ + +#ifndef MERCURY_BULK_H +#define MERCURY_BULK_H + +#include "mercury_types.h" + +/*************************************/ +/* Public Type and Struct Definition */ +/*************************************/ + +/*****************/ +/* Public Macros */ +/*****************/ + +/* The memory attributes associated with the bulk handle + * can be defined as read only, write only or read-write */ +#define HG_BULK_READ_ONLY (1 << 0) +#define HG_BULK_WRITE_ONLY (1 << 1) +#define HG_BULK_READWRITE (HG_BULK_READ_ONLY | HG_BULK_WRITE_ONLY) + +/*********************/ +/* Public Prototypes */ +/*********************/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Create an abstract bulk handle from specified memory segments. + * Memory allocated is then freed when HG_Bulk_free() is called. + * \remark If NULL is passed to buf_ptrs, i.e., + * \verbatim HG_Bulk_create(count, NULL, buf_sizes, flags, &handle) \endverbatim + * memory for the missing buf_ptrs array will be internally allocated. + * + * \param hg_class [IN] pointer to HG class + * \param count [IN] number of segments + * \param buf_ptrs [IN] array of pointers + * \param buf_sizes [IN] array of sizes + * \param flags [IN] permission flag: + * - HG_BULK_READWRITE + * - HG_BULK_READ_ONLY + * - HG_BULK_WRITE_ONLY + * \param handle [OUT] pointer to returned abstract bulk handle + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Bulk_create(hg_class_t *hg_class, hg_uint32_t count, void **buf_ptrs, + const hg_size_t *buf_sizes, hg_uint8_t flags, hg_bulk_t *handle); + +/** + * Free bulk handle. + * + * \param handle [IN/OUT] abstract bulk handle + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Bulk_free(hg_bulk_t handle); + +/** + * Increment ref count on bulk handle. + * + * \param handle [IN] abstract bulk handle + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Bulk_ref_incr(hg_bulk_t handle); + +/** + * Bind an existing bulk handle to a local HG context and associate its local + * address. This function can be used to forward and share a bulk handle + * between targets, which would not have direct access to the origin without + * extra RPCs. In that case, the origin address of the bulk handle is embedded + * and serialized/deserialized with HG_Bulk_serialize()/HG_Bulk_deserialize(). + * Users should note that binding a handle adds an extra overhead on + * serialization, therefore it is recommended to use it with care. + * When binding a handle on origin, HG_Bulk_bind_transfer() can be used since + * origin information is embedded in the handle. + * + * Usage example: + * Origin sends an RPC request with a bulk handle attached to target A, target A + * forwards the origin's bulk handle to another target B. When target B receives + * the deserialized bulk handle, it has the address/info required to initiate a + * bulk transfer to/from the origin. + * For that usage, the origin will have called this function to bind the bulk + * handle to its local context, prior to sending the RPC request to target A. + * + * \param context [IN] pointer to HG context + * \param handle [IN] abstract bulk handle + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Bulk_bind(hg_bulk_t handle, hg_context_t *context); + +/** + * Return attached addressing information from a handle that was previously + * bound to a context using HG_Bulk_bind(). + * + * \param handle [IN] abstract bulk handle + * + * \return abstract HG address or HG_ADDR_NULL in case of error + */ +HG_PUBLIC hg_addr_t HG_Bulk_get_addr(hg_bulk_t handle); + +/** + * Return attached context ID from a handle that was previously bound to a + * context using HG_Bulk_bind(). + * + * \param handle [IN] abstract bulk handle + * + * \return valid context ID or 0 by default + */ +HG_PUBLIC hg_uint8_t HG_Bulk_get_context_id(hg_bulk_t handle); + +/** + * Access bulk handle to retrieve memory segments abstracted by handle. + * \remark When using mercury in co-resident mode (i.e., when addr passed is + * self addr), this function allows to avoid copy of bulk data by directly + * accessing pointers from an existing HG bulk handle. + * + * \param handle [IN] abstract bulk handle + * \param offset [IN] bulk offset + * \param size [IN] bulk size + * \param flags [IN] permission flag: + * - HG_BULK_READWRITE + * - HG_BULK_READ_ONLY + * \param max_count [IN] maximum number of segments to be returned + * \param buf_ptrs [IN/OUT] array of buffer pointers + * \param buf_sizes [IN/OUT] array of buffer sizes + * \param actual_count [OUT] actual number of segments returned + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Bulk_access(hg_bulk_t handle, hg_size_t offset, hg_size_t size, hg_uint8_t flags, + hg_uint32_t max_count, void **buf_ptrs, hg_size_t *buf_sizes, + hg_uint32_t *actual_count); + +/** + * Get total size of data abstracted by bulk handle. + * + * \param handle [IN] abstract bulk handle + * + * \return Non-negative value + */ +static HG_INLINE hg_size_t HG_Bulk_get_size(hg_bulk_t handle); + +/** + * Get total number of segments abstracted by bulk handle. + * + * \param handle [IN] abstract bulk handle + * + * \return Non-negative value + */ +static HG_INLINE hg_uint32_t HG_Bulk_get_segment_count(hg_bulk_t handle); + +/** + * Get permission flags set on an existing bulk handle. + * + * \param handle [IN] abstract bulk handle + * + * \return Non-negative value + */ +static HG_INLINE hg_uint8_t HG_Bulk_get_flags(hg_bulk_t handle); + +/** + * Get size required to serialize bulk handle. + * + * \param handle [IN] abstract bulk handle + * \param flags [IN] option flags, valid flags are: + * HG_BULK_SM, HG_BULK_EAGER + * + * \return Non-negative value + */ +HG_PUBLIC hg_size_t HG_Bulk_get_serialize_size(hg_bulk_t handle, unsigned long flags); + +/** + * Serialize bulk handle into a buffer. + * + * \param buf [IN/OUT] pointer to buffer + * \param buf_size [IN] buffer size + * \param flags [IN] option flags, valid flags are: + * HG_BULK_SM, HG_BULK_EAGER + * \param handle [IN] abstract bulk handle + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Bulk_serialize(void *buf, hg_size_t buf_size, unsigned long flags, hg_bulk_t handle); + +/** + * Deserialize bulk handle from an existing buffer. + * + * \param hg_class [IN] pointer to HG class + * \param handle [OUT] abstract bulk handle + * \param buf [IN] pointer to buffer + * \param buf_size [IN] buffer size + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Bulk_deserialize(hg_class_t *hg_class, hg_bulk_t *handle, const void *buf, + hg_size_t buf_size); + +/** + * Transfer data to/from origin using abstract bulk handles and explicit origin + * address information. After completion, user callback is placed into a + * completion queue and can be triggered using HG_Trigger(). + * + * \param context [IN] pointer to HG context + * \param callback [IN] pointer to function callback + * \param arg [IN] pointer to data passed to callback + * \param op [IN] transfer operation: + * - HG_BULK_PUSH + * - HG_BULK_PULL + * \param origin_addr [IN] abstract address of origin + * \param origin_handle [IN] abstract bulk handle + * \param origin_offset [IN] offset + * \param local_handle [IN] abstract bulk handle + * \param local_offset [IN] offset + * \param size [IN] size of data to be transferred + * \param op_id [OUT] pointer to returned operation ID + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Bulk_transfer(hg_context_t *context, hg_cb_t callback, void *arg, hg_bulk_op_t op, + hg_addr_t origin_addr, hg_bulk_t origin_handle, + hg_size_t origin_offset, hg_bulk_t local_handle, + hg_size_t local_offset, hg_size_t size, hg_op_id_t *op_id); + +/** + * Transfer data to/from origin using abstract bulk handles and implicit origin + * information (embedded in the origin handle). After completion, user callback + * is placed into a completion queue and can be triggered using HG_Trigger(). + * + * \param context [IN] pointer to HG context + * \param callback [IN] pointer to function callback + * \param arg [IN] pointer to data passed to callback + * \param op [IN] transfer operation: + * - HG_BULK_PUSH + * - HG_BULK_PULL + * \param origin_handle [IN] abstract bulk handle + * \param origin_offset [IN] offset + * \param local_handle [IN] abstract bulk handle + * \param local_offset [IN] offset + * \param size [IN] size of data to be transferred + * \param op_id [OUT] pointer to returned operation ID + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Bulk_bind_transfer(hg_context_t *context, hg_cb_t callback, void *arg, + hg_bulk_op_t op, hg_bulk_t origin_handle, hg_size_t origin_offset, + hg_bulk_t local_handle, hg_size_t local_offset, hg_size_t size, + hg_op_id_t *op_id); + +/** + * Transfer data to/from origin using abstract bulk handles, explicit origin + * address information and origin context ID (associating the transfer to a + * remote context ID). After completion, user callback is placed into a + * completion queue and can be triggered using HG_Trigger(). + * + * \param context [IN] pointer to HG context + * \param callback [IN] pointer to function callback + * \param arg [IN] pointer to data passed to callback + * \param op [IN] transfer operation: + * - HG_BULK_PUSH + * - HG_BULK_PULL + * \param origin_addr [IN] abstract address of origin + * \param origin_id [IN] context ID of origin + * \param origin_handle [IN] abstract bulk handle + * \param origin_offset [IN] offset + * \param local_handle [IN] abstract bulk handle + * \param local_offset [IN] offset + * \param size [IN] size of data to be transferred + * \param op_id [OUT] pointer to returned operation ID + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Bulk_transfer_id(hg_context_t *context, hg_cb_t callback, void *arg, hg_bulk_op_t op, + hg_addr_t origin_addr, hg_uint8_t origin_id, + hg_bulk_t origin_handle, hg_size_t origin_offset, + hg_bulk_t local_handle, hg_size_t local_offset, hg_size_t size, + hg_op_id_t *op_id); + +/** + * Cancel an ongoing operation. + * + * \param op_id [IN] operation ID + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Bulk_cancel(hg_op_id_t op_id); + +/************************************/ +/* Local Type and Struct Definition */ +/************************************/ + +/* HG bulk descriptor info */ +struct hg_bulk_desc_info { + hg_size_t len; /* Size of region */ + hg_uint32_t segment_count; /* Segment count */ + hg_uint8_t flags; /* Flags of operation access */ +}; + +/*---------------------------------------------------------------------------*/ +static HG_INLINE hg_size_t +HG_Bulk_get_size(hg_bulk_t handle) +{ + return ((struct hg_bulk_desc_info *)handle)->len; +} + +/*---------------------------------------------------------------------------*/ +static HG_INLINE hg_uint32_t +HG_Bulk_get_segment_count(hg_bulk_t handle) +{ + return ((struct hg_bulk_desc_info *)handle)->segment_count; +} + +/*---------------------------------------------------------------------------*/ +static HG_INLINE hg_uint8_t +HG_Bulk_get_flags(hg_bulk_t handle) +{ + return ((struct hg_bulk_desc_info *)handle)->flags; +} + +#ifdef __cplusplus +} +#endif + +#endif /* MERCURY_BULK_H */ diff --git a/src/mercury/include/mercury_config.h b/src/mercury/include/mercury_config.h new file mode 100644 index 0000000..6fe5064 --- /dev/null +++ b/src/mercury/include/mercury_config.h @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2013-2020 Argonne National Laboratory, Department of Energy, + * UChicago Argonne, LLC and The HDF Group. + * All rights reserved. + * + * The full copyright notice, including terms governing use, modification, + * and redistribution, is contained in the COPYING file that can be + * found at the root of the source code distribution tree. + */ + +/* Generated file. Only edit mercury_config.h.in. */ + +#ifndef MERCURY_CONFIG_H +#define MERCURY_CONFIG_H + +/*************************************/ +/* Public Type and Struct Definition */ +/*************************************/ + +/* Type definitions */ +#ifdef _WIN32 +typedef signed __int64 hg_int64_t; +typedef signed __int32 hg_int32_t; +typedef signed __int16 hg_int16_t; +typedef signed __int8 hg_int8_t; +typedef unsigned __int64 hg_uint64_t; +typedef unsigned __int32 hg_uint32_t; +typedef unsigned __int16 hg_uint16_t; +typedef unsigned __int8 hg_uint8_t; +/* Limits on Integer Constants */ +#define UINT64_MAX _UI64_MAX +#else +#include <stddef.h> +#include <stdint.h> +typedef int64_t hg_int64_t; +typedef int32_t hg_int32_t; +typedef int16_t hg_int16_t; +typedef int8_t hg_int8_t; +typedef uint64_t hg_uint64_t; +typedef uint32_t hg_uint32_t; +typedef uint16_t hg_uint16_t; +typedef uint8_t hg_uint8_t; +#endif +typedef hg_uint64_t hg_ptr_t; +typedef hg_uint8_t hg_bool_t; + +/* True / false */ +#define HG_TRUE 1 +#define HG_FALSE 0 + +/*****************/ +/* Public Macros */ +/*****************/ + +/* Reflects major releases of Mercury */ +#define HG_VERSION_MAJOR 2 +/* Reflects any API changes */ +#define HG_VERSION_MINOR 1 +/* Reflects any library code changes */ +#define HG_VERSION_PATCH 0 + +/* Visibility of symbols */ +#if defined(_WIN32) +#define HG_ABI_IMPORT __declspec(dllimport) +#define HG_ABI_EXPORT __declspec(dllexport) +#define HG_ABI_HIDDEN +#elif defined(__GNUC__) && (__GNUC__ >= 4) +#define HG_ABI_IMPORT __attribute__((visibility("default"))) +#define HG_ABI_EXPORT __attribute__((visibility("default"))) +#define HG_ABI_HIDDEN __attribute__((visibility("hidden"))) +#else +#define HG_ABI_IMPORT +#define HG_ABI_EXPORT +#define HG_ABI_HIDDEN +#endif + +/* Inline macro */ +#ifdef _WIN32 +#define HG_INLINE __inline +#else +#define HG_INLINE __inline__ +#endif + +/* Fallthrough macro */ +#if defined(__GNUC__) && (__GNUC__ >= 7) +#define HG_FALLTHROUGH() __attribute__((fallthrough)) +#else +#define HG_FALLTHROUGH() +#endif + +/* Shared libraries */ +/* #undef HG_BUILD_SHARED_LIBS */ +#ifdef HG_BUILD_SHARED_LIBS +#ifdef mercury_EXPORTS +#define HG_PUBLIC HG_ABI_EXPORT +#else +#define HG_PUBLIC HG_ABI_IMPORT +#endif +#define HG_PRIVATE HG_ABI_HIDDEN +#else +#define HG_PUBLIC +#define HG_PRIVATE +#endif + +/* Build Options */ +/* #undef HG_HAS_BOOST */ +/* #undef HG_HAS_CHECKSUMS */ +/* #undef HG_HAS_XDR */ +/* #undef HG_HAS_COLLECT_STATS */ + +/* #undef HG_HAS_DEBUG */ + +#endif /* MERCURY_CONFIG_H */ diff --git a/src/mercury/include/mercury_core.h b/src/mercury/include/mercury_core.h new file mode 100644 index 0000000..3d5c850 --- /dev/null +++ b/src/mercury/include/mercury_core.h @@ -0,0 +1,1074 @@ +/* + * Copyright (C) 2013-2020 Argonne National Laboratory, Department of Energy, + * UChicago Argonne, LLC and The HDF Group. + * All rights reserved. + * + * The full copyright notice, including terms governing use, modification, + * and redistribution, is contained in the COPYING file that can be + * found at the root of the source code distribution tree. + */ + +#ifndef MERCURY_CORE_H +#define MERCURY_CORE_H + +#include "mercury_core_header.h" +#include "mercury_core_types.h" + +#include "na.h" + +/*************************************/ +/* Public Type and Struct Definition */ +/*************************************/ + +typedef struct hg_core_class hg_core_class_t; /* Opaque HG core class */ +typedef struct hg_core_context hg_core_context_t; /* Opaque HG core context */ +typedef struct hg_core_addr * hg_core_addr_t; /* Abstract HG address */ +typedef struct hg_core_handle *hg_core_handle_t; /* Abstract RPC handle */ +typedef struct hg_core_op_id * hg_core_op_id_t; /* Abstract operation id */ + +/* HG info struct */ +struct hg_core_info { + hg_core_class_t * core_class; /* HG core class */ + hg_core_context_t *context; /* HG core context */ + hg_core_addr_t addr; /* HG address at target/origin */ + hg_id_t id; /* RPC ID */ + hg_uint8_t context_id; /* Context ID at target/origin */ +}; + +/* Callback info structs */ +struct hg_core_cb_info_lookup { + hg_core_addr_t addr; /* HG address */ +}; + +struct hg_core_cb_info_forward { + hg_core_handle_t handle; /* HG handle */ +}; + +struct hg_core_cb_info_respond { + hg_core_handle_t handle; /* HG handle */ +}; + +struct hg_core_cb_info { + union { /* Union of callback info structures */ + struct hg_core_cb_info_lookup lookup; + struct hg_core_cb_info_forward forward; + struct hg_core_cb_info_respond respond; + } info; + void * arg; /* User data */ + hg_cb_type_t type; /* Callback type */ + hg_return_t ret; /* Return value */ +}; + +/* RPC / HG callbacks */ +typedef hg_return_t (*hg_core_rpc_cb_t)(hg_core_handle_t handle); +typedef hg_return_t (*hg_core_cb_t)(const struct hg_core_cb_info *callback_info); + +/*****************/ +/* Public Macros */ +/*****************/ + +/* Constant values */ +#define HG_CORE_ADDR_NULL ((hg_core_addr_t)0) +#define HG_CORE_HANDLE_NULL ((hg_core_handle_t)0) +#define HG_CORE_OP_ID_NULL ((hg_core_op_id_t)0) +#define HG_CORE_OP_ID_IGNORE ((hg_core_op_id_t *)1) + +/* Flags */ +#define HG_CORE_MORE_DATA (1 << 0) /* More data required */ +#define HG_CORE_NO_RESPONSE (1 << 1) /* No response required */ + +/*********************/ +/* Public Prototypes */ +/*********************/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Initialize the core Mercury layer. + * Must be finalized with HG_Core_finalize(). + * + * \param na_info_string [IN] host address with port number (e.g., + * "tcp://localhost:3344" or + * "bmi+tcp://localhost:3344") + * \param na_listen [IN] listen for incoming connections + * + * \return Pointer to HG core class or NULL in case of failure + */ +HG_PUBLIC hg_core_class_t *HG_Core_init(const char *na_info_string, hg_bool_t na_listen); + +/** + * Initialize the Mercury layer with options provided by init_info. + * Must be finalized with HG_Core_finalize(). + * \remark HG_Core_init_opt() may become HG_Core_init() in the future. + * + * \param na_info_string [IN] host address with port number (e.g., + * "tcp://localhost:3344" or + * "bmi+tcp://localhost:3344") + * \param na_listen [IN] listen for incoming connections + * \param hg_init_info [IN] (Optional) HG init info, NULL if no info + * + * \return Pointer to HG core class or NULL in case of failure + */ +HG_PUBLIC hg_core_class_t *HG_Core_init_opt(const char *na_info_string, hg_bool_t na_listen, + const struct hg_init_info *hg_init_info); + +/** + * Finalize the Mercury layer. + * + * \param hg_core_class [IN] pointer to HG core class + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Core_finalize(hg_core_class_t *hg_core_class); + +/** + * Clean up all temporary files that were created in previous HG instances. + * While temporary resources (e.g., tmp files) are cleaned up on a call + * to HG_Finalize(), this routine gives a chance to programs that terminate + * abnormally to easily clean up those resources. + */ +HG_PUBLIC void HG_Core_cleanup(void); + +/** + * Set callback that will be triggered when additional data needs to be + * transferred and HG_Core_set_more_data() has been called, usually when the + * eager message size is exceeded. This allows upper layers to manually transfer + * data using bulk transfers for example. The done_callback argument allows the + * upper layer to notify back once the data has been successfully acquired. + * The release callback allows the upper layer to release resources that were + * allocated when acquiring the data. + * + * \param hg_core_class [IN] pointer to HG core class + * \param more_data_acquire_callback [IN] pointer to acquire function callback + * \param more_data_release_callback [IN] pointer to release function callback + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Core_set_more_data_callback( + struct hg_core_class *hg_core_class, + hg_return_t (*more_data_acquire_callback)(hg_core_handle_t, hg_op_t, + hg_return_t (*done_callback)(hg_core_handle_t)), + void (*more_data_release_callback)(hg_core_handle_t)); + +/** + * Obtain the name of the given class. + * + * \param hg_core_class [IN] pointer to HG core class + * + * \return the name of the class, or NULL if not a valid class + */ +static HG_INLINE const char *HG_Core_class_get_name(const hg_core_class_t *hg_core_class); + +/** + * Obtain the protocol of the given class. + * + * \param hg_core_class [IN] pointer to HG core class + * + * \return the protocol of the class, or NULL if not a valid class + */ +static HG_INLINE const char *HG_Core_class_get_protocol(const hg_core_class_t *hg_core_class); + +/** + * Test whether class is listening or not. + * + * \param hg_core_class [IN] pointer to HG core class + * + * \return HG_TRUE if listening or HG_FALSE if not, or not a valid class + */ +static HG_INLINE hg_bool_t HG_Core_class_is_listening(const hg_core_class_t *hg_core_class); + +/** + * Obtain the underlying NA class. + * + * \param hg_core_class [IN] pointer to HG core class + * + * \return Pointer to NA class or NULL if not a valid class + */ +static HG_INLINE na_class_t *HG_Core_class_get_na(const hg_core_class_t *hg_core_class); + +#ifdef NA_HAS_SM +/** + * Obtain the underlying NA SM class. + * + * \param hg_core_class [IN] pointer to HG core class + * + * \return Pointer to NA SM class or NULL if not a valid class + */ +static HG_INLINE na_class_t *HG_Core_class_get_na_sm(const hg_core_class_t *hg_core_class); +#endif + +/** + * Obtain the maximum eager size for sending RPC inputs. + * + * \param hg_core_class [IN] pointer to HG core class + * + * \return the maximum size, or 0 if hg_core_class is not a valid class or + * XDR is being used + */ +static HG_INLINE hg_size_t HG_Core_class_get_input_eager_size(const hg_core_class_t *hg_core_class); + +/** + * Obtain the maximum eager size for sending RPC outputs. + * + * \param hg_core_class [IN] pointer to HG core class + * + * \return the maximum size, or 0 if hg_core_class is not a valid class or XDR + * is being used + */ +static HG_INLINE hg_size_t HG_Core_class_get_output_eager_size(const hg_core_class_t *hg_core_class); + +/** + * Associate user data to class. When HG_Core_finalize() is called, + * free_callback (if defined) is called to free the associated data. + * + * \param hg_core_class [IN] pointer to HG core class + * \param data [IN] pointer to user data + * \param free_callback [IN] pointer to function + * + * \return HG_SUCCESS or corresponding HG error code + */ +static HG_INLINE hg_return_t HG_Core_class_set_data(hg_core_class_t *hg_core_class, void *data, + void (*free_callback)(void *)); + +/** + * Retrieve previously associated data from a given class. + * + * \param hg_core_class [IN] pointer to HG core class + * + * \return Pointer to user data or NULL if not set or any error has occurred + */ +static HG_INLINE void *HG_Core_class_get_data(const hg_core_class_t *hg_core_class); + +/** + * Create a new context. Must be destroyed by calling HG_Core_context_destroy(). + * + * \param hg_core_class [IN] pointer to HG core class + * + * \return Pointer to HG core context or NULL in case of failure + */ +HG_PUBLIC hg_core_context_t *HG_Core_context_create(hg_core_class_t *hg_core_class); + +/** + * Create a new context with a user-defined context identifier. The context + * identifier can be used to route RPC requests to specific contexts by using + * HG_Core_set_target_id(). + * Context must be destroyed by calling HG_Core_context_destroy(). + * + * \param hg_core_class [IN] pointer to HG core class + * \param id [IN] context ID + * + * \return Pointer to HG core context or NULL in case of failure + */ +HG_PUBLIC hg_core_context_t *HG_Core_context_create_id(hg_core_class_t *hg_core_class, hg_uint8_t id); + +/** + * Destroy a context created by HG_Core_context_create(). + * + * \param context [IN] pointer to HG core context + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Core_context_destroy(hg_core_context_t *context); + +/** + * Retrieve the class used to create the given context. + * + * \param context [IN] pointer to HG core context + * + * \return the associated class + */ +static HG_INLINE hg_core_class_t *HG_Core_context_get_class(const hg_core_context_t *context); + +/** + * Retrieve the underlying NA context. + * + * \param context [IN] pointer to HG core context + * + * \return the associated context + */ +static HG_INLINE na_context_t *HG_Core_context_get_na(const hg_core_context_t *context); + +#ifdef NA_HAS_SM +/** + * Retrieve the underlying NA SM context. + * + * \param context [IN] pointer to HG core context + * + * \return the associated context + */ +static HG_INLINE na_context_t *HG_Core_context_get_na_sm(const hg_core_context_t *context); +#endif + +/** + * Retrieve context ID from context. + * + * \param context [IN] pointer to HG core context + * + * \return Non-negative integer (max value of 255) or 0 if no ID has been set + */ +static HG_INLINE hg_uint8_t HG_Core_context_get_id(const hg_core_context_t *context); + +/** + * Associate user data to context. When HG_Core_context_destroy() is called, + * free_callback (if defined) is called to free the associated data. + * + * \param context [IN] pointer to HG core context + * \param data [IN] pointer to user data + * \param free_callback [IN] pointer to function + * + * \return HG_SUCCESS or corresponding HG error code + */ +static HG_INLINE hg_return_t HG_Core_context_set_data(hg_core_context_t *context, void *data, + void (*free_callback)(void *)); + +/** + * Retrieve previously associated data from a given context. + * + * \param context [IN] pointer to HG core context + * + * \return Pointer to user data or NULL if not set or any error has occurred + */ +static HG_INLINE void *HG_Core_context_get_data(const hg_core_context_t *context); + +/** + * Set callback to be called on HG core handle creation. Handles are created + * both on HG_Core_create() and HG_Core_context_post() calls. This allows + * upper layers to create and attach data to a handle (using HG_Core_set_data()) + * and later retrieve it using HG_Core_get_data(). + * + * \param context [IN] pointer to HG core context + * \param callback [IN] pointer to function callback + * \param arg [IN] pointer to data passed to callback + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Core_context_set_handle_create_callback( + hg_core_context_t *context, hg_return_t (*callback)(hg_core_handle_t, void *), void *arg); + +/** + * Post requests associated to context in order to receive incoming RPCs. + * Requests are automatically re-posted after completion until the context is + * destroyed. Additionally a callback can be triggered on HG handle + * creation. This allows upper layers to instantiate data that needs to be + * attached to a handle. Number of requests that are posted can be controlled + * through HG init info. + * + * \param context [IN] pointer to HG core context + * + * \return the associated class + */ +HG_PUBLIC hg_return_t HG_Core_context_post(hg_core_context_t *context); + +/** + * Dynamically register an RPC ID as well as the RPC callback executed + * when the RPC request ID is received. + * + * \param hg_core_class [IN] pointer to HG core class + * \param id [IN] ID to use to register RPC + * \param rpc_cb [IN] RPC callback + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Core_register(hg_core_class_t *hg_core_class, hg_id_t id, hg_core_rpc_cb_t rpc_cb); + +/** + * Deregister RPC ID. Further requests with RPC ID will return an error, it + * is therefore up to the user to make sure that all requests for that RPC ID + * have been treated before it is unregistered. + * + * \param hg_core_class [IN] pointer to HG core class + * \param id [IN] registered function ID + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Core_deregister(hg_core_class_t *hg_core_class, hg_id_t id); + +/** + * Indicate whether HG_Core_register() has been called. + * + * \param hg_core_class [IN] pointer to HG core class + * \param id [IN] function ID + * \param flag [OUT] pointer to boolean + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Core_registered(hg_core_class_t *hg_core_class, hg_id_t id, hg_bool_t *flag); + +/** + * Register and associate user data to registered function. When + * HG_Core_finalize() is called, free_callback (if defined) is called to free + * the registered data. + * + * \param hg_core_class [IN] pointer to HG core class + * \param id [IN] registered function ID + * \param data [IN] pointer to data + * \param free_callback [IN] pointer to function + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Core_register_data(hg_core_class_t *hg_core_class, hg_id_t id, void *data, + void (*free_callback)(void *)); + +/** + * Indicate whether HG_Core_register_data() has been called and return + * associated data. + * + * \param hg_core_class [IN] pointer to HG core class + * \param id [IN] registered function ID + * + * \return Pointer to data or NULL + */ +HG_PUBLIC void *HG_Core_registered_data(hg_core_class_t *hg_core_class, hg_id_t id); + +/** + * Lookup an addr from a peer address/name. Addresses need to be + * freed by calling HG_Core_addr_free(). After completion, user callback is + * placed into a completion queue and can be triggered using HG_Core_trigger(). + * + * \param context [IN] pointer to context of execution + * \param callback [IN] pointer to function callback + * \param arg [IN] pointer to data passed to callback + * \param name [IN] lookup name + * \param op_id [OUT] pointer to returned operation ID (unused) + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Core_addr_lookup1(hg_core_context_t *context, hg_core_cb_t callback, void *arg, + const char *name, hg_core_op_id_t *op_id); + +/** + * Lookup an addr from a peer address/name. Addresses need to be + * freed by calling HG_Core_addr_free(). + * + * \param hg_core_class [IN] pointer to HG core class + * \param name [IN] lookup name + * \param addr [OUT] pointer to abstract address + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Core_addr_lookup2(hg_core_class_t *hg_core_class, const char *name, + hg_core_addr_t *addr); + +/** + * Free the addr from the list of peers. + * + * \param addr [IN] abstract address + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Core_addr_free(hg_core_addr_t addr); + +/** + * Hint that the address is no longer valid. This may happen if the peer is + * no longer responding. This can be used to force removal of the + * peer address from the list of the peers, before freeing it and reclaim + * resources. + * + * \param addr [IN] abstract address + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Core_addr_set_remove(hg_core_addr_t addr); + +/** + * Obtain the underlying NA address from an HG address. + * + * \param addr [IN] abstract address + * + * \return abstract NA addr or NA_ADDR_NULL if not a valid HG address + */ +static HG_INLINE na_addr_t HG_Core_addr_get_na(hg_core_addr_t addr); + +#ifdef NA_HAS_SM +/** + * Obtain the underlying NA SM address from an HG address. + * + * \param addr [IN] abstract address + * + * \return abstract NA addr or NA_ADDR_NULL if not a valid HG address + */ +static HG_INLINE na_addr_t HG_Core_addr_get_na_sm(hg_core_addr_t addr); +#endif + +/** + * Access self address. Address must be freed with HG_Core_addr_free(). + * + * \param hg_core_class [IN] pointer to HG core class + * \param addr [OUT] pointer to abstract address + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Core_addr_self(hg_core_class_t *hg_core_class, hg_core_addr_t *addr); + +/** + * Duplicate an existing HG abstract address. The duplicated address can be + * stored for later use and the origin address be freed safely. The duplicated + * address must be freed with HG_Core_addr_free(). + * + * \param addr [IN] abstract address + * \param new_addr [OUT] pointer to abstract address + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Core_addr_dup(hg_core_addr_t addr, hg_core_addr_t *new_addr); + +/** + * Compare two addresses. + * + * \param addr1 [IN] abstract address + * \param addr2 [IN] abstract address + * + * \return HG_TRUE if addresses are determined to be equal, HG_FALSE otherwise + */ +HG_PUBLIC hg_bool_t HG_Core_addr_cmp(hg_core_addr_t addr1, hg_core_addr_t addr2); + +/** + * Test whether address is self or not. + * + * \param addr [IN] pointer to abstract address + * + * \return HG_TRUE if address is self address, HG_FALSE otherwise + */ +static HG_INLINE hg_bool_t HG_Core_addr_is_self(hg_core_addr_t addr); + +/** + * Convert an addr to a string (returned string includes the terminating + * null byte '\0'). If buf is NULL, the address is not converted and only + * the required size of the buffer is returned. If the input value passed + * through buf_size is too small, HG_SIZE_ERROR is returned and the buf_size + * output is set to the minimum size required. + * + * \param buf [IN/OUT] pointer to destination buffer + * \param buf_size [IN/OUT] pointer to buffer size + * \param addr [IN] abstract address + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Core_addr_to_string(char *buf, hg_size_t *buf_size, hg_core_addr_t addr); + +/** + * Get size required to serialize address. + * + * \param addr [IN] abstract address + * \param flags [IN] optional flags + * + * \return Non-negative value + */ +HG_PUBLIC hg_size_t HG_Core_addr_get_serialize_size(hg_core_addr_t addr, unsigned long flags); + +/** + * Serialize address into a buffer. + * + * \param buf [IN/OUT] pointer to destination buffer + * \param buf_size [IN] pointer to buffer size + * \param flags [IN] optional flags + * \param addr [IN] abstract address + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Core_addr_serialize(void *buf, hg_size_t buf_size, unsigned long flags, + hg_core_addr_t addr); + +/** + * Deserialize address from a buffer. The returned address must be freed with + * HG_Core_addr_free(). + * + * \param hg_core_class [IN] pointer to HG core class + * \param addr [OUT] pointer to abstract address + * \param buf [IN] pointer to buffer used for deserialization + * \param buf_size [IN] buffer size + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Core_addr_deserialize(hg_core_class_t *hg_core_class, hg_core_addr_t *addr, + const void *buf, hg_size_t buf_size); + +/** + * Initiate a new HG RPC using the specified function ID and the local/remote + * target defined by addr. The HG handle created can be used to query input + * and output buffers, as well as issuing the RPC by using HG_Core_forward(). + * After completion the handle must be freed using HG_Core_destroy(). + * + * \param context [IN] pointer to HG core context + * \param addr [IN] target address + * \param id [IN] registered function ID + * \param handle [OUT] pointer to HG handle + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Core_create(hg_core_context_t *context, hg_core_addr_t addr, hg_id_t id, + hg_core_handle_t *handle); + +/** + * Destroy HG handle. Decrement reference count, resources associated to the + * handle are freed when the reference count is null. + * + * \param handle [IN] HG handle + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Core_destroy(hg_core_handle_t handle); + +/** + * Reset an existing HG handle to make it reusable for RPC forwarding. + * Both target address and RPC ID can be modified at this time. + * Operations on that handle must be completed in order to reset that handle + * safely. + * + * \param handle [IN] HG handle + * \param addr [IN] abstract network address of destination + * \param id [IN] registered function ID + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Core_reset(hg_core_handle_t handle, hg_core_addr_t addr, hg_id_t id); + +/** + * Increment ref count on handle. + * + * \param handle [IN] HG handle + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Core_ref_incr(hg_core_handle_t handle); + +/** + * Retrieve ref count from handle. + * + * \param handle [IN] HG handle + * + * \return Non-negative value or negative if the handle is not valid + */ +HG_PUBLIC hg_int32_t HG_Core_ref_get(hg_core_handle_t handle); + +/** + * Allows upper layers to attach data to an existing HG handle. + * The free_callback argument allows allocated resources to be released when + * the handle gets freed. + * + * \param handle [IN] HG handle + * \param data [IN] pointer to user data + * \param free_callback pointer to free function callback + * + * \return HG_SUCCESS or corresponding HG error code + */ +static HG_INLINE hg_return_t HG_Core_set_data(hg_core_handle_t handle, void *data, + void (*free_callback)(void *)); + +/** + * Allows upper layers to retrieve data from an existing HG handle. + * Only valid if HG_Core_set_data() has been previously called. + * + * \param handle [IN] HG handle + * + * \return Pointer to user data or NULL if not set or any error has occurred + */ +static HG_INLINE void *HG_Core_get_data(hg_core_handle_t handle); + +/** + * Get info from handle. + * + * \remark Users must call HG_Core_addr_dup() to safely re-use the addr field. + * + * \param handle [IN] HG handle + * + * \return Pointer to info or NULL in case of failure + */ +static HG_INLINE const struct hg_core_info *HG_Core_get_info(hg_core_handle_t handle); + +/** + * Allows upper layers to retrieve cached RPC data from an existing HG handle. + * Only valid if HG_Core_register_data() has been previously called. + * + * \param handle [IN] HG handle + * + * \return Pointer to user data or NULL if not set or any error has occurred + */ +static HG_INLINE const void *HG_Core_get_rpc_data(hg_core_handle_t handle); + +/** + * Set target context ID that will receive and process the RPC request + * (ID is defined on target context creation, see HG_Core_context_create_id()). + * + * \param handle [IN] HG handle + * \param id [IN] user-defined target context ID + * + * \return HG_SUCCESS or corresponding HG error code + */ +static HG_INLINE hg_return_t HG_Core_set_target_id(hg_core_handle_t handle, hg_uint8_t id); + +/** + * Get input buffer from handle that can be used for serializing/deserializing + * parameters. + * + * \param handle [IN] HG handle + * \param in_buf [OUT] pointer to input buffer + * \param in_buf_size [OUT] pointer to input buffer size + * + * \return HG_SUCCESS or corresponding HG error code + */ +static HG_INLINE hg_return_t HG_Core_get_input(hg_core_handle_t handle, void **in_buf, + hg_size_t *in_buf_size); + +/** + * Get output buffer from handle that can be used for serializing/deserializing + * parameters. + * + * \param handle [IN] HG handle + * \param out_buf [OUT] pointer to output buffer + * \param out_buf_size [OUT] pointer to output buffer size + * + * \return HG_SUCCESS or corresponding HG error code + */ +static HG_INLINE hg_return_t HG_Core_get_output(hg_core_handle_t handle, void **out_buf, + hg_size_t *out_buf_size); + +/** + * Forward a call using an existing HG handle. Input and output buffers can be + * queried from the handle to serialize/deserialize parameters. + * Additionally, a bulk handle can be passed if the size of the input is larger + * than the queried input buffer size. + * After completion, the handle must be freed using HG_Core_destroy(), the user + * callback is placed into a completion queue and can be triggered using + * HG_Core_trigger(). + * + * \param handle [IN] HG handle + * \param callback [IN] pointer to function callback + * \param arg [IN] pointer to data passed to callback + * \param payload_size [IN] size of payload to send + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Core_forward(hg_core_handle_t handle, hg_core_cb_t callback, void *arg, + hg_uint8_t flags, hg_size_t payload_size); + +/** + * Respond back to the origin. The output buffer, which can be used to encode + * the response, must first be queried using HG_Core_get_output(). + * After completion, the user callback is placed into a completion queue and + * can be triggered using HG_Core_trigger(). + * + * \param handle [IN] HG handle + * \param callback [IN] pointer to function callback + * \param arg [IN] pointer to data passed to callback + * \param payload_size [IN] size of payload to send + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Core_respond(hg_core_handle_t handle, hg_core_cb_t callback, void *arg, + hg_uint8_t flags, hg_size_t payload_size); + +/** + * Try to progress RPC execution for at most timeout until timeout is reached or + * any completion has occurred. + * Progress should not be considered as wait, in the sense that it cannot be + * assumed that completion of a specific operation will occur only when + * progress is called. + * + * \param context [IN] pointer to HG core context + * \param timeout [IN] timeout (in milliseconds) + * + * \return HG_SUCCESS if any completion has occurred / HG error code otherwise + */ +HG_PUBLIC hg_return_t HG_Core_progress(hg_core_context_t *context, unsigned int timeout); + +/** + * Execute at most max_count callbacks. If timeout is non-zero, wait up to + * timeout before returning. Function can return when at least one or more + * callbacks are triggered (at most max_count). + * + * \param context [IN] pointer to HG core context + * \param timeout [IN] timeout (in milliseconds) + * \param max_count [IN] maximum number of callbacks triggered + * \param actual_count [IN] actual number of callbacks triggered + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Core_trigger(hg_core_context_t *context, unsigned int timeout, + unsigned int max_count, unsigned int *actual_count); + +/** + * Cancel an ongoing operation. + * + * \param handle [IN] HG handle + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Core_cancel(hg_core_handle_t handle); + +/************************************/ +/* Local Type and Struct Definition */ +/************************************/ + +/* HG core class */ +struct hg_core_class { + na_class_t *na_class; /* NA class */ +#ifdef NA_HAS_SM + na_class_t *na_sm_class; /* NA SM class */ +#endif + void *data; /* User data */ + void (*data_free_callback)(void *); /* User data free callback */ +}; + +/* HG core context */ +struct hg_core_context { + struct hg_core_class *core_class; /* HG core class */ + na_context_t * na_context; /* NA context */ +#ifdef NA_HAS_SM + na_context_t *na_sm_context; /* NA SM context */ +#endif + void *data; /* User data */ + void (*data_free_callback)(void *); /* User data free callback */ + hg_uint8_t id; /* Context ID */ +}; + +/* HG core addr */ +struct hg_core_addr { + struct hg_core_class *core_class; /* HG core class */ + na_addr_t na_addr; /* NA address */ +#ifdef NA_HAS_SM + na_addr_t na_sm_addr; /* NA SM address */ +#endif + hg_bool_t is_self; /* Self address */ +}; + +/* HG core RPC registration info */ +struct hg_core_rpc_info { + hg_core_rpc_cb_t rpc_cb; /* RPC callback */ + void * data; /* User data */ + void (*free_callback)(void *); /* User data free callback */ +}; + +/* HG core handle */ +struct hg_core_handle { + struct hg_core_info info; /* HG info */ + struct hg_core_rpc_info *rpc_info; /* Associated RPC registration info */ + void * data; /* User data */ + void (*data_free_callback)(void *); /* User data free callback */ + void * in_buf; /* Input buffer */ + void * out_buf; /* Output buffer */ + na_size_t in_buf_size; /* Input buffer size */ + na_size_t out_buf_size; /* Output buffer size */ + na_size_t na_in_header_offset; /* Input NA header offset */ + na_size_t na_out_header_offset; /* Output NA header offset */ +}; + +/*---------------------------------------------------------------------------*/ +static HG_INLINE const char * +HG_Core_class_get_name(const hg_core_class_t *hg_core_class) +{ + return NA_Get_class_name(hg_core_class->na_class); +} + +/*---------------------------------------------------------------------------*/ +static HG_INLINE const char * +HG_Core_class_get_protocol(const hg_core_class_t *hg_core_class) +{ + return NA_Get_class_protocol(hg_core_class->na_class); +} + +/*---------------------------------------------------------------------------*/ +static HG_INLINE hg_bool_t +HG_Core_class_is_listening(const hg_core_class_t *hg_core_class) +{ + return NA_Is_listening(hg_core_class->na_class); +} + +/*---------------------------------------------------------------------------*/ +static HG_INLINE na_class_t * + HG_Core_class_get_na(const hg_core_class_t *hg_core_class) +{ + return hg_core_class->na_class; +} + +/*---------------------------------------------------------------------------*/ +#ifdef NA_HAS_SM +static HG_INLINE na_class_t * + HG_Core_class_get_na_sm(const hg_core_class_t *hg_core_class) +{ + return hg_core_class->na_sm_class; +} +#endif + +/*---------------------------------------------------------------------------*/ +static HG_INLINE hg_size_t +HG_Core_class_get_input_eager_size(const hg_core_class_t *hg_core_class) +{ + hg_size_t unexp = NA_Msg_get_max_unexpected_size(hg_core_class->na_class), + header = hg_core_header_request_get_size() + + NA_Msg_get_unexpected_header_size(hg_core_class->na_class); + + return (unexp > header) ? unexp - header : 0; +} + +/*---------------------------------------------------------------------------*/ +static HG_INLINE hg_size_t +HG_Core_class_get_output_eager_size(const hg_core_class_t *hg_core_class) +{ + hg_size_t exp = NA_Msg_get_max_expected_size(hg_core_class->na_class), + header = hg_core_header_response_get_size() + + NA_Msg_get_expected_header_size(hg_core_class->na_class); + + return (exp > header) ? exp - header : 0; +} + +/*---------------------------------------------------------------------------*/ +static HG_INLINE hg_return_t +HG_Core_class_set_data(hg_core_class_t *hg_core_class, void *data, void (*free_callback)(void *)) +{ + hg_core_class->data = data; + hg_core_class->data_free_callback = free_callback; + + return HG_SUCCESS; +} + +/*---------------------------------------------------------------------------*/ +static HG_INLINE void * +HG_Core_class_get_data(const hg_core_class_t *hg_core_class) +{ + return hg_core_class->data; +} + +/*---------------------------------------------------------------------------*/ +static HG_INLINE hg_core_class_t * + HG_Core_context_get_class(const hg_core_context_t *context) +{ + return context->core_class; +} + +/*---------------------------------------------------------------------------*/ +static HG_INLINE na_context_t * + HG_Core_context_get_na(const hg_core_context_t *context) +{ + return context->na_context; +} + +/*---------------------------------------------------------------------------*/ +#ifdef NA_HAS_SM +static HG_INLINE na_context_t * + HG_Core_context_get_na_sm(const hg_core_context_t *context) +{ + return context->na_sm_context; +} +#endif + +/*---------------------------------------------------------------------------*/ +static HG_INLINE hg_uint8_t +HG_Core_context_get_id(const hg_core_context_t *context) +{ + return context->id; +} + +/*---------------------------------------------------------------------------*/ +static HG_INLINE hg_return_t +HG_Core_context_set_data(hg_core_context_t *context, void *data, void (*free_callback)(void *)) +{ + context->data = data; + context->data_free_callback = free_callback; + + return HG_SUCCESS; +} + +/*---------------------------------------------------------------------------*/ +static HG_INLINE void * +HG_Core_context_get_data(const hg_core_context_t *context) +{ + return context->data; +} + +/*---------------------------------------------------------------------------*/ +static HG_INLINE na_addr_t +HG_Core_addr_get_na(hg_core_addr_t addr) +{ + return addr->na_addr; +} + +/*---------------------------------------------------------------------------*/ +#ifdef NA_HAS_SM +static HG_INLINE na_addr_t +HG_Core_addr_get_na_sm(hg_core_addr_t addr) +{ + return addr->na_sm_addr; +} +#endif + +/*---------------------------------------------------------------------------*/ +static HG_INLINE hg_bool_t +HG_Core_addr_is_self(hg_core_addr_t addr) +{ + return addr->is_self; +} + +/*---------------------------------------------------------------------------*/ +static HG_INLINE hg_return_t +HG_Core_set_data(hg_core_handle_t handle, void *data, void (*free_callback)(void *)) +{ + handle->data = data; + handle->data_free_callback = free_callback; + + return HG_SUCCESS; +} + +/*---------------------------------------------------------------------------*/ +static HG_INLINE void * +HG_Core_get_data(hg_core_handle_t handle) +{ + return handle->data; +} + +/*---------------------------------------------------------------------------*/ +static HG_INLINE const struct hg_core_info * +HG_Core_get_info(hg_core_handle_t handle) +{ + return &handle->info; +} + +/*---------------------------------------------------------------------------*/ +static HG_INLINE const void * +HG_Core_get_rpc_data(hg_core_handle_t handle) +{ + return (handle->rpc_info) ? handle->rpc_info->data : NULL; +} + +/*---------------------------------------------------------------------------*/ +static HG_INLINE hg_return_t +HG_Core_set_target_id(hg_core_handle_t handle, hg_uint8_t id) +{ + handle->info.context_id = id; + + return HG_SUCCESS; +} + +/*---------------------------------------------------------------------------*/ +static HG_INLINE hg_return_t +HG_Core_get_input(hg_core_handle_t handle, void **in_buf, hg_size_t *in_buf_size) +{ + hg_size_t header_offset = hg_core_header_request_get_size() + handle->na_in_header_offset; + + /* Space must be left for request header */ + *in_buf = (char *)handle->in_buf + header_offset; + *in_buf_size = handle->in_buf_size - header_offset; + + return HG_SUCCESS; +} + +/*---------------------------------------------------------------------------*/ +static HG_INLINE hg_return_t +HG_Core_get_output(hg_core_handle_t handle, void **out_buf, hg_size_t *out_buf_size) +{ + hg_size_t header_offset = hg_core_header_response_get_size() + handle->na_out_header_offset; + + /* Space must be left for response header */ + *out_buf = (char *)handle->out_buf + header_offset; + *out_buf_size = handle->out_buf_size - header_offset; + + return HG_SUCCESS; +} + +#ifdef __cplusplus +} +#endif + +#endif /* MERCURY_CORE_H */ diff --git a/src/mercury/include/mercury_core_header.h b/src/mercury/include/mercury_core_header.h new file mode 100644 index 0000000..355adfa --- /dev/null +++ b/src/mercury/include/mercury_core_header.h @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2013-2020 Argonne National Laboratory, Department of Energy, + * UChicago Argonne, LLC and The HDF Group. + * All rights reserved. + * + * The full copyright notice, including terms governing use, modification, + * and redistribution, is contained in the COPYING file that can be + * found at the root of the source code distribution tree. + */ + +#ifndef MERCURY_CORE_HEADER_H +#define MERCURY_CORE_HEADER_H + +#include "mercury_core_types.h" + +/*************************************/ +/* Public Type and Struct Definition */ +/*************************************/ + +#if defined(__GNUC__) || defined(_WIN32) +#pragma pack(push, 1) +#else +#warning "Proc header struct padding may not be consistent across platforms." +#endif +#ifdef HG_HAS_CHECKSUMS +union hg_core_header_hash { + hg_uint16_t header; /* Header checksum (16-bits checksum) */ + hg_uint32_t pad; +}; +#endif + +struct hg_core_header_request { + hg_uint8_t hg; /* Mercury identifier */ + hg_uint8_t protocol; /* Version number */ + hg_uint64_t id; /* RPC request identifier */ + hg_uint8_t flags; /* Flags */ + hg_uint8_t cookie; /* Cookie */ + /* 96 bits here */ +#ifdef HG_HAS_CHECKSUMS + union hg_core_header_hash hash; /* Hash */ + /* 128 bits here */ +#endif +}; + +struct hg_core_header_response { + hg_int8_t ret_code; /* Return code */ + hg_uint8_t flags; /* Flags */ + hg_uint16_t cookie; /* Cookie */ + hg_uint64_t pad; /* Pad */ + /* 96 bits here */ +#ifdef HG_HAS_CHECKSUMS + union hg_core_header_hash hash; /* Hash */ + /* 128 bits here */ +#endif +}; +#if defined(__GNUC__) || defined(_WIN32) +#pragma pack(pop) +#endif + +/* Common header struct request/response */ +struct hg_core_header { + union { + struct hg_core_header_request request; + struct hg_core_header_response response; + } msg; +#ifdef HG_HAS_CHECKSUMS + void *checksum; /* Checksum of header */ +#endif +}; + +/* + * 0 HG_CORE_HEADER_SIZE size + * |______________|__________________________| + * | Header | Encoded Data | + * |______________|__________________________| + * + * + * Request: + * mercury byte / protocol version number / rpc id / flags / cookie / checksum + * + * Response: + * flags / return code / cookie / checksum + */ + +/*****************/ +/* Public Macros */ +/*****************/ + +/* Mercury identifier for packets sent */ +#define HG_CORE_IDENTIFIER (('H' << 1) | ('G')) /* 0xD7 */ + +/* Mercury protocol version number */ +#define HG_CORE_PROTOCOL_VERSION 0x05 + +/*********************/ +/* Public Prototypes */ +/*********************/ + +#ifdef __cplusplus +extern "C" { +#endif + +static HG_INLINE size_t hg_core_header_request_get_size(void); +static HG_INLINE size_t hg_core_header_response_get_size(void); + +/** + * Get size reserved for request header (separate user data stored in payload). + * + * \return Non-negative size value + */ +static HG_INLINE size_t +hg_core_header_request_get_size(void) +{ + return sizeof(struct hg_core_header_request); +} + +/** + * Get size reserved for response header (separate user data stored in payload). + * + * \return Non-negative size value + */ +static HG_INLINE size_t +hg_core_header_response_get_size(void) +{ + return sizeof(struct hg_core_header_response); +} + +/** + * Initialize RPC request header. + * + * \param hg_core_header [IN/OUT] pointer to request header structure + * + */ +HG_PRIVATE void hg_core_header_request_init(struct hg_core_header *hg_core_header); + +/** + * Initialize RPC response header. + * + * \param hg_core_header [IN/OUT] pointer to response header structure + * + */ +HG_PRIVATE void hg_core_header_response_init(struct hg_core_header *hg_core_header); + +/** + * Finalize RPC request header. + * + * \param hg_core_header [IN/OUT] pointer to request header structure + * + */ +HG_PRIVATE void hg_core_header_request_finalize(struct hg_core_header *hg_core_header); + +/** + * Finalize RPC response header. + * + * \param hg_core_header [IN/OUT] pointer to response header structure + * + */ +HG_PRIVATE void hg_core_header_response_finalize(struct hg_core_header *hg_core_header); + +/** + * Reset RPC request header. + * + * \param hg_core_header [IN/OUT] pointer to request header structure + * + */ +HG_PRIVATE void hg_core_header_request_reset(struct hg_core_header *hg_core_header); + +/** + * Reset RPC response header. + * + * \param hg_core_header [IN/OUT] pointer to response header structure + * + */ +HG_PRIVATE void hg_core_header_response_reset(struct hg_core_header *hg_core_header); + +/** + * Process private information for sending/receiving RPC request. + * + * \param op [IN] operation type: HG_ENCODE / HG_DECODE + * \param buf [IN/OUT] buffer + * \param buf_size [IN] buffer size + * \param hg_core_header [IN/OUT] pointer to header structure + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PRIVATE hg_return_t hg_core_header_request_proc(hg_proc_op_t op, void *buf, size_t buf_size, + struct hg_core_header *hg_core_header); + +/** + * Process private information for sending/receiving response. + * + * \param op [IN] operation type: HG_ENCODE / HG_DECODE + * \param buf [IN/OUT] buffer + * \param buf_size [IN] buffer size + * \param header [IN/OUT] pointer to header structure + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PRIVATE hg_return_t hg_core_header_response_proc(hg_proc_op_t op, void *buf, size_t buf_size, + struct hg_core_header *hg_core_header); + +/** + * Verify private information from request header. + * + * \param hg_core_header [IN] pointer to request header structure + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PRIVATE hg_return_t hg_core_header_request_verify(const struct hg_core_header *hg_core_header); + +/** + * Verify private information from response header. + * + * \param hg_core_header [IN] pointer to response header structure + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PRIVATE hg_return_t hg_core_header_response_verify(const struct hg_core_header *hg_core_header); + +#ifdef __cplusplus +} +#endif + +#endif /* MERCURY_CORE_HEADER_H */ diff --git a/src/mercury/include/mercury_core_types.h b/src/mercury/include/mercury_core_types.h new file mode 100644 index 0000000..636ab75 --- /dev/null +++ b/src/mercury/include/mercury_core_types.h @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2013-2020 Argonne National Laboratory, Department of Energy, + * UChicago Argonne, LLC and The HDF Group. + * All rights reserved. + * + * The full copyright notice, including terms governing use, modification, + * and redistribution, is contained in the COPYING file that can be + * found at the root of the source code distribution tree. + */ + +#ifndef MERCURY_CORE_TYPES_H +#define MERCURY_CORE_TYPES_H + +#include "mercury_config.h" +#include "na_types.h" + +/*************************************/ +/* Public Type and Struct Definition */ +/*************************************/ + +typedef hg_uint64_t hg_size_t; /* Size */ +typedef hg_uint64_t hg_id_t; /* RPC ID */ + +/** + * HG init info struct + * NB. should be initialized using HG_INIT_INFO_INITIALIZER + */ +struct hg_init_info { + /* NA init info struct, see na_types.h for documentation */ + struct na_init_info na_init_info; + + /* Optional NA class that can be used for initializing an HG class. Using + * that option makes the init string passed to HG_Init() ignored. + * Default is: NULL */ + na_class_t *na_class; + + /* Controls the initial number of requests that are posted on context + * creation when the HG class is initialized with listen set to true. + * A value of zero is equivalent to using the internal default value. + * Default value is: 256 */ + hg_uint32_t request_post_init; + + /* Controls the number of requests that are incrementally posted when the + * initial number of requests is exhausted, a value of 0 means that only the + * initial number of requests will be re-used after they complete. Note that + * if the number of requests that are posted reaches 0, the underlying + * NA transport is responsible for queueing incoming requests. This value is + * used only if \request_post_init is set to a non-zero value. + * Default value is: 256 */ + hg_uint32_t request_post_incr; + + /* Controls whether the NA shared-memory interface should be automatically + * used if/when the RPC target address shares the same node as its origin. + * Default is: false */ + hg_bool_t auto_sm; + + /* Controls whether mercury should _NOT_ attempt to transfer small bulk data + * along with the RPC request. + * Default is: false */ + hg_bool_t no_bulk_eager; + + /* Disable internal loopback interface that enables forwarding of RPC + * requests to self addresses. Doing so will force traffic to be routed + * through NA. For performance reasons, users should be cautious when using + * that option. + * Default is: false */ + hg_bool_t no_loopback; + + /* (Debug) Print stats at exit. + * Default is: false */ + hg_bool_t stats; +}; + +/* Error return codes: + * Functions return 0 for success or corresponding return code */ +#define HG_RETURN_VALUES \ + X(HG_SUCCESS) /*!< operation succeeded */ \ + X(HG_PERMISSION) /*!< operation not permitted */ \ + X(HG_NOENTRY) /*!< no such file or directory */ \ + X(HG_INTERRUPT) /*!< operation interrupted */ \ + X(HG_AGAIN) /*!< operation must be retried */ \ + X(HG_NOMEM) /*!< out of memory */ \ + X(HG_ACCESS) /*!< permission denied */ \ + X(HG_FAULT) /*!< bad address */ \ + X(HG_BUSY) /*!< device or resource busy */ \ + X(HG_EXIST) /*!< entry already exists */ \ + X(HG_NODEV) /*!< no such device */ \ + X(HG_INVALID_ARG) /*!< invalid argument */ \ + X(HG_PROTOCOL_ERROR) /*!< protocol error */ \ + X(HG_OVERFLOW) /*!< value too large */ \ + X(HG_MSGSIZE) /*!< message size too long */ \ + X(HG_PROTONOSUPPORT) /*!< protocol not supported */ \ + X(HG_OPNOTSUPPORTED) /*!< operation not supported on endpoint */ \ + X(HG_ADDRINUSE) /*!< address already in use */ \ + X(HG_ADDRNOTAVAIL) /*!< cannot assign requested address */ \ + X(HG_HOSTUNREACH) /*!< cannot reach host during operation */ \ + X(HG_TIMEOUT) /*!< operation reached timeout */ \ + X(HG_CANCELED) /*!< operation canceled */ \ + X(HG_CHECKSUM_ERROR) /*!< checksum error */ \ + X(HG_NA_ERROR) /*!< generic NA error */ \ + X(HG_OTHER_ERROR) /*!< generic HG error */ \ + X(HG_RETURN_MAX) + +#define X(a) a, +typedef enum hg_return { HG_RETURN_VALUES } hg_return_t; +#undef X + +/* Compat return codes */ +#define HG_INVALID_PARAM HG_INVALID_ARG +#define HG_SIZE_ERROR HG_MSGSIZE +#define HG_NOMEM_ERROR HG_NOMEM +#define HG_NO_MATCH HG_NOENTRY + +/* Callback operation type */ +typedef enum hg_cb_type { + HG_CB_LOOKUP, /*!< lookup callback */ + HG_CB_FORWARD, /*!< forward callback */ + HG_CB_RESPOND, /*!< respond callback */ + HG_CB_BULK /*!< bulk transfer callback */ +} hg_cb_type_t; + +/* Input / output operation type */ +typedef enum { HG_UNDEF, HG_INPUT, HG_OUTPUT } hg_op_t; + +/** + * Encode/decode operations. + */ +typedef enum { + HG_ENCODE, /*!< causes the type to be encoded into the stream */ + HG_DECODE, /*!< causes the type to be extracted from the stream */ + HG_FREE /*!< can be used to release the space allocated by an HG_DECODE + request */ +} hg_proc_op_t; + +/** + * Encode/decode operation flags. + */ +#define HG_CORE_SM (1 << 0) + +/*****************/ +/* Public Macros */ +/*****************/ + +/* Max timeout */ +#define HG_MAX_IDLE_TIME (3600 * 1000) + +/* HG size max */ +#define HG_SIZE_MAX (UINT64_MAX) + +/* HG init info initializer */ +#define HG_INIT_INFO_INITIALIZER \ + { \ + NA_INIT_INFO_INITIALIZER, NULL, 0, 0, HG_FALSE, HG_FALSE, HG_FALSE, HG_FALSE \ + } + +#endif /* MERCURY_CORE_TYPES_H */ diff --git a/src/mercury/include/mercury_dlog.h b/src/mercury/include/mercury_dlog.h new file mode 100644 index 0000000..557b745 --- /dev/null +++ b/src/mercury/include/mercury_dlog.h @@ -0,0 +1,273 @@ +/* + * Copyright (C) 2013-2020 Argonne National Laboratory, Department of Energy, + * UChicago Argonne, LLC and The HDF Group. + * All rights reserved. + * + * The full copyright notice, including terms governing use, modification, + * and redistribution, is contained in the COPYING file that can be + * found at the root of the source code distribution tree. + */ + +#ifndef MERCURY_DLOG_H +#define MERCURY_DLOG_H + +#include "mercury_util_config.h" + +#include "mercury_atomic.h" +#include "mercury_list.h" +#include "mercury_thread_mutex.h" +#include "mercury_time.h" + +#include <stdio.h> + +/*****************/ +/* Public Macros */ +/*****************/ + +/* + * putting a magic number at the front of the dlog allows us to search + * for a dlog in a coredump file after a crash and examine its contents. + */ +#define HG_DLOG_MAGICLEN 16 /* bytes to reserve for magic# */ +#define HG_DLOG_STDMAGIC ">D.LO.G<" /* standard for first 8 bytes */ + +/* + * HG_DLOG_INITIALIZER: initializer for a dlog in a global variable. + * LESIZE is the number of entries in the LE array. use it like this: + * + * #define FOO_NENTS 128 + * struct hg_dlog_entry foo_le[FOO_NENTS]; + * struct hg_dlog foo_dlog = HG_DLOG_INITIALIZER("foo", foo_le, FOO_NENTS, 0); + */ +#define HG_DLOG_INITIALIZER(NAME, LE, LESIZE, LELOOP) \ + { \ + HG_DLOG_STDMAGIC NAME, HG_THREAD_MUTEX_INITIALIZER, HG_LIST_HEAD_INITIALIZER(cnts32), \ + HG_LIST_HEAD_INITIALIZER(cnts64), LE, LESIZE, LELOOP, 0, 0, 0, 0 \ + } + +/*************************************/ +/* Public Type and Struct Definition */ +/*************************************/ + +/* + * hg_dlog_entry: an entry in the dlog + */ +struct hg_dlog_entry { + const char * file; /* file name */ + unsigned int line; /* line number */ + const char * func; /* function name */ + const char * msg; /* entry message (optional) */ + const void * data; /* user data (optional) */ + hg_time_t time; /* time added to log */ +}; + +/* + * hg_dlog_dcount32: 32-bit debug counter in the dlog + */ +struct hg_dlog_dcount32 { + const char * name; /* counter name (short) */ + const char * descr; /* description of counter */ + hg_atomic_int32_t c; /* the counter itself */ + HG_LIST_ENTRY(hg_dlog_dcount32) l; /* linkage */ +}; + +/* + * hg_dlog_dcount64: 64-bit debug counter in the dlog + */ +struct hg_dlog_dcount64 { + const char * name; /* counter name (short) */ + const char * descr; /* description of counter */ + hg_atomic_int64_t c; /* the counter itself */ + HG_LIST_ENTRY(hg_dlog_dcount64) l; /* linkage */ +}; + +/* + * hg_dlog: main structure + */ +struct hg_dlog { + char dlog_magic[HG_DLOG_MAGICLEN]; /* magic number + name */ + hg_thread_mutex_t dlock; /* lock for this data struct */ + + /* counter lists */ + HG_LIST_HEAD(hg_dlog_dcount32) cnts32; /* counter list */ + HG_LIST_HEAD(hg_dlog_dcount64) cnts64; /* counter list */ + + /* log */ + struct hg_dlog_entry *le; /* array of log entries */ + unsigned int lesize; /* size of le[] array */ + int leloop; /* circular buffer? */ + unsigned int lefree; /* next free entry in le[] */ + unsigned int leadds; /* #adds done if < lesize */ + int lestop; /* stop taking new logs */ + + int mallocd; /* allocated with malloc? */ +}; + +/*********************/ +/* Public Prototypes */ +/*********************/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * malloc and return a new dlog + * + * \param name [IN] name of dlog (truncated past 8 bytes) + * \param lesize [IN] number of entries to allocate for log buffer + * \param leloop [IN] set to make log circular (can overwrite old + * entries) + * + * \return the new dlog or NULL on malloc error + */ +HG_UTIL_PUBLIC struct hg_dlog *hg_dlog_alloc(char *name, unsigned int lesize, int leloop); + +/** + * free anything we malloc'd on a dlog. assumes we have the final + * active reference to dlog and it won't be used anymore after this + * call (so no need to lock it). + * + * \param d [IN] the dlog to finalize + */ +HG_UTIL_PUBLIC void hg_dlog_free(struct hg_dlog *d); + +/** + * make a named atomic32 counter in a dlog and return a pointer to + * it. we use the dlock to ensure a counter under a given name only + * gets created once (makes it easy to share a counter across files). + * aborts if unable to alloc counter. use it like this: + * + * hg_atomic_int32_t *foo_count; + * static int init = 0; + * if (init == 0) { + * hg_dlog_mkcount32(dlog, &foo_count, "foocount", "counts of foo"); + * init = 1; + * } + * + * \param d [IN] dlog to create the counter in + * \param cptr [IN/OUT] pointer to use for counter (set to NULL to + * start) + * \param name [IN] short one word name for counter + * \param descr [IN] short description of counter + */ +HG_UTIL_PUBLIC void hg_dlog_mkcount32(struct hg_dlog *d, hg_atomic_int32_t **cptr, const char *name, + const char *descr); + +/** + * make a named atomic64 counter in a dlog and return a pointer to + * it. we use the dlock to ensure a counter under a given name only + * gets created once (makes it easy to share a counter across files). + * aborts if unable to alloc counter. use it like this: + * + * hg_atomic_int64_t *foo_count; + * static int init = 0; + * if (init == 0) { + * hg_dlog_mkcount64(dlog, &foo_count, "foocount", "counts of foo"); + * init = 1; + * } + * + * \param d [IN] dlog to create the counter in + * \param cptr [IN/OUT] pointer to use for counter (set to NULL to + * start) + * \param name [IN] short one word name for counter + * \param descr [IN] short description of counter + */ +HG_UTIL_PUBLIC void hg_dlog_mkcount64(struct hg_dlog *d, hg_atomic_int64_t **cptr, const char *name, + const char *descr); + +/** + * attempt to add a log record to a dlog. the id and msg should point + * to static strings that are valid throughout the life of the program + * (not something that is is on the stack). + * + * \param d [IN] the dlog to add the log record to + * \param file [IN] file entry + * \param line [IN] line entry + * \param func [IN] func entry + * \param msg [IN] log entry message (optional, NULL ok) + * \param data [IN] user data pointer for record (optional, NULL ok) + * + * \return 1 if added, 0 otherwise + */ +static HG_UTIL_INLINE unsigned int hg_dlog_addlog(struct hg_dlog *d, const char *file, unsigned int line, + const char *func, const char *msg, const void *data); + +/** + * set the value of stop for a dlog (to enable/disable logging) + * + * \param d [IN] dlog to set stop in + * \param stop [IN] value of stop to use (1=stop, 0=go) + */ +HG_UTIL_PUBLIC void hg_dlog_setlogstop(struct hg_dlog *d, int stop); + +/** + * reset the log. this does not change the counters (since users + * have direct access to the hg_atomic_int64_t's, we don't need + * an API to change them here). + * + * \param d [IN] dlog to reset + */ +HG_UTIL_PUBLIC void hg_dlog_resetlog(struct hg_dlog *d); + +/** + * dump dlog info to a stream. set trylock if you want to dump even + * if it is locked (e.g. you are crashing and you don't care about + * locking). + * + * \param d [IN] dlog to dump + * \param log_func [IN] log function to use (default printf) + * \param stream [IN] stream to use + * \param trylock [IN] just try to lock (warn if it fails) + */ +HG_UTIL_PUBLIC void hg_dlog_dump(struct hg_dlog *d, int (*log_func)(FILE *, const char *, ...), FILE *stream, + int trylock); + +/** + * dump dlog info to a file. set trylock if you want to dump even + * if it is locked (e.g. you are crashing and you don't care about + * locking). the output file is "base.log" or base-pid.log" depending + * on the value of addpid. + * + * \param d [IN] dlog to dump + * \param base [IN] output file basename + * \param addpid [IN] add pid to output filename + * \param trylock [IN] just try to lock (warn if it fails) + */ +HG_UTIL_PUBLIC void hg_dlog_dump_file(struct hg_dlog *d, const char *base, int addpid, int trylock); + +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE unsigned int +hg_dlog_addlog(struct hg_dlog *d, const char *file, unsigned int line, const char *func, const char *msg, + const void *data) +{ + unsigned int rv = 0; + unsigned int idx; + + hg_thread_mutex_lock(&d->dlock); + if (d->lestop) + goto done; + if (d->leloop == 0 && d->leadds >= d->lesize) + goto done; + idx = d->lefree; + d->lefree = (d->lefree + 1) % d->lesize; + if (d->leadds < d->lesize) + d->leadds++; + d->le[idx].file = file; + d->le[idx].line = line; + d->le[idx].func = func; + d->le[idx].msg = msg; + d->le[idx].data = data; + hg_time_get_current(&d->le[idx].time); + rv = 1; + +done: + hg_thread_mutex_unlock(&d->dlock); + return rv; +} + +#ifdef __cplusplus +} +#endif + +#endif /* MERCURY_DLOG_H */ diff --git a/src/mercury/include/mercury_event.h b/src/mercury/include/mercury_event.h new file mode 100644 index 0000000..8be18a5 --- /dev/null +++ b/src/mercury/include/mercury_event.h @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2013-2020 Argonne National Laboratory, Department of Energy, + * UChicago Argonne, LLC and The HDF Group. + * All rights reserved. + * + * The full copyright notice, including terms governing use, modification, + * and redistribution, is contained in the COPYING file that can be + * found at the root of the source code distribution tree. + */ + +#ifndef MERCURY_EVENT_H +#define MERCURY_EVENT_H + +#include "mercury_util_config.h" + +#ifdef _WIN32 + +#else +#include <errno.h> +#include <string.h> +#include <unistd.h> +#if defined(HG_UTIL_HAS_SYSEVENTFD_H) +#include <sys/eventfd.h> +#ifndef HG_UTIL_HAS_EVENTFD_T +typedef uint64_t eventfd_t; +#endif +#elif defined(HG_UTIL_HAS_SYSEVENT_H) +#include <sys/event.h> +#define HG_EVENT_IDENT 42 /* User-defined ident */ +#endif +#endif + +/** + * Purpose: define an event object that can be used as an event + * wait/notify mechanism. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Create a new event object. + * + * \return file descriptor on success or negative on failure + */ +HG_UTIL_PUBLIC int hg_event_create(void); + +/** + * Destroy an event object. + * + * \param fd [IN] event file descriptor + * + * \return Non-negative on success or negative on failure + */ +HG_UTIL_PUBLIC int hg_event_destroy(int fd); + +/** + * Notify for event. + * + * \param fd [IN] event file descriptor + * + * \return Non-negative on success or negative on failure + */ +static HG_UTIL_INLINE int hg_event_set(int fd); + +/** + * Get event notification. + * + * \param fd [IN] event file descriptor + * \param notified [IN] boolean set to HG_UTIL_TRUE if event received + * + * \return Non-negative on success or negative on failure + */ +static HG_UTIL_INLINE int hg_event_get(int fd, hg_util_bool_t *notified); + +/*---------------------------------------------------------------------------*/ +#if defined(_WIN32) +/* TODO */ +#elif defined(HG_UTIL_HAS_SYSEVENTFD_H) +#ifdef HG_UTIL_HAS_EVENTFD_T +static HG_UTIL_INLINE int +hg_event_set(int fd) +{ + return (eventfd_write(fd, 1) == 0) ? HG_UTIL_SUCCESS : HG_UTIL_FAIL; +} +#else +static HG_UTIL_INLINE int +hg_event_set(int fd) +{ + eventfd_t count = 1; + ssize_t s = write(fd, &count, sizeof(eventfd_t)); + + return (s == sizeof(eventfd_t)) ? HG_UTIL_SUCCESS : HG_UTIL_FAIL; +} +#endif +#elif defined(HG_UTIL_HAS_SYSEVENT_H) +static HG_UTIL_INLINE int +hg_event_set(int fd) +{ + struct kevent kev; + struct timespec timeout = {0, 0}; + int rc; + + EV_SET(&kev, HG_EVENT_IDENT, EVFILT_USER, 0, NOTE_TRIGGER, 0, NULL); + + /* Trigger user-defined event */ + rc = kevent(fd, &kev, 1, NULL, 0, &timeout); + + return (rc == -1) ? HG_UTIL_FAIL : HG_UTIL_SUCCESS; +} +#else +#error "Not supported on this platform." +#endif + +/*---------------------------------------------------------------------------*/ +#if defined(_WIN32) +#elif defined(HG_UTIL_HAS_SYSEVENTFD_H) +#ifdef HG_UTIL_HAS_EVENTFD_T +static HG_UTIL_INLINE int +hg_event_get(int fd, hg_util_bool_t *signaled) +{ + eventfd_t count = 0; + + if ((eventfd_read(fd, &count) == 0) && count) + *signaled = HG_UTIL_TRUE; + else { + if (errno == EAGAIN) + *signaled = HG_UTIL_FALSE; + else + return HG_UTIL_FAIL; + } + + return HG_UTIL_SUCCESS; +} +#else +static HG_UTIL_INLINE int +hg_event_get(int fd, hg_util_bool_t *signaled) +{ + eventfd_t count = 0; + ssize_t s = read(fd, &count, sizeof(eventfd_t)); + if ((s == sizeof(eventfd_t)) && count) + *signaled = HG_UTIL_TRUE; + else { + if (errno == EAGAIN) + *signaled = HG_UTIL_FALSE; + else + return HG_UTIL_FAIL; + } + + return HG_UTIL_SUCCESS; +} +#endif +#elif defined(HG_UTIL_HAS_SYSEVENT_H) +static HG_UTIL_INLINE int +hg_event_get(int fd, hg_util_bool_t *signaled) +{ + struct kevent kev; + int nfds; + struct timespec timeout = {0, 0}; + + /* Check user-defined event */ + nfds = kevent(fd, NULL, 0, &kev, 1, &timeout); + if (nfds == -1) + return HG_UTIL_FAIL; + + *signaled = ((nfds > 0) && (kev.ident == HG_EVENT_IDENT)) ? HG_UTIL_TRUE : HG_UTIL_FALSE; + + return HG_UTIL_SUCCESS; +} +#else +#error "Not supported on this platform." +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* MERCURY_EVENT_H */ diff --git a/src/mercury/include/mercury_hash_string.h b/src/mercury/include/mercury_hash_string.h new file mode 100644 index 0000000..0b136ca --- /dev/null +++ b/src/mercury/include/mercury_hash_string.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2013-2020 Argonne National Laboratory, Department of Energy, + * UChicago Argonne, LLC and The HDF Group. + * All rights reserved. + * + * The full copyright notice, including terms governing use, modification, + * and redistribution, is contained in the COPYING file that can be + * found at the root of the source code distribution tree. + */ + +#ifndef MERCURY_HASH_STRING_H +#define MERCURY_HASH_STRING_H + +#include "mercury_util_config.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Hash function name for unique ID to register. + * + * \param string [IN] string name + * + * \return Non-negative ID that corresponds to string name + */ +static HG_UTIL_INLINE unsigned int +hg_hash_string(const char *string) +{ + /* This is the djb2 string hash function */ + + unsigned int result = 5381; + const unsigned char *p; + + p = (const unsigned char *)string; + + while (*p != '\0') { + result = (result << 5) + result + *p; + ++p; + } + return result; +} + +#ifdef __cplusplus +} +#endif + +#endif /* MERCURY_HASH_STRING_H */ diff --git a/src/mercury/include/mercury_hash_table.h b/src/mercury/include/mercury_hash_table.h new file mode 100644 index 0000000..0063f02 --- /dev/null +++ b/src/mercury/include/mercury_hash_table.h @@ -0,0 +1,242 @@ +/* + +Copyright (c) 2005-2008, Simon Howard + +Permission to use, copy, modify, and/or distribute this software +for any purpose with or without fee is hereby granted, provided +that the above copyright notice and this permission notice appear +in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + */ + +/** + * \file mercury_hash_table.h + * + * \brief Hash table. + * + * A hash table stores a set of values which can be addressed by a + * key. Given the key, the corresponding value can be looked up + * quickly. + * + * To create a hash table, use \ref hg_hash_table_new. To destroy a + * hash table, use \ref hg_hash_table_free. + * + * To insert a value into a hash table, use \ref hg_hash_table_insert. + * + * To remove a value from a hash table, use \ref hg_hash_table_remove. + * + * To look up a value by its key, use \ref hg_hash_table_lookup. + * + * To iterate over all values in a hash table, use + * \ref hg_hash_table_iterate to initialize a \ref hg_hash_table_iter + * structure. Each value can then be read in turn using + * \ref hg_hash_table_iter_next and \ref hg_hash_table_iter_has_more. + */ + +#ifndef HG_HASH_TABLE_H +#define HG_HASH_TABLE_H + +#include "mercury_util_config.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * A hash table structure. + */ + +typedef struct hg_hash_table hg_hash_table_t; + +/** + * Structure used to iterate over a hash table. + */ + +typedef struct hg_hash_table_iter hg_hash_table_iter_t; + +/** + * Internal structure representing an entry in a hash table. + */ + +typedef struct hg_hash_table_entry hg_hash_table_entry_t; + +/** + * A key to look up a value in a \ref hg_hash_table_t. + */ + +typedef void *hg_hash_table_key_t; + +/** + * A value stored in a \ref hg_hash_table_t. + */ + +typedef void *hg_hash_table_value_t; + +/** + * Definition of a \ref hg_hash_table_iter. + */ + +struct hg_hash_table_iter { + hg_hash_table_t * hash_table; + hg_hash_table_entry_t *next_entry; + unsigned int next_chain; +}; + +/** + * A null \ref HashTableValue. + */ + +#define HG_HASH_TABLE_NULL ((void *)0) + +/** + * Hash function used to generate hash values for keys used in a hash + * table. + * + * \param value The value to generate a hash value for. + * \return The hash value. + */ + +typedef unsigned int (*hg_hash_table_hash_func_t)(hg_hash_table_key_t value); + +/** + * Function used to compare two keys for equality. + * + * \return Non-zero if the two keys are equal, zero if the keys are + * not equal. + */ + +typedef int (*hg_hash_table_equal_func_t)(hg_hash_table_key_t value1, hg_hash_table_key_t value2); + +/** + * Type of function used to free keys when entries are removed from a + * hash table. + */ + +typedef void (*hg_hash_table_key_free_func_t)(hg_hash_table_key_t value); + +/** + * Type of function used to free values when entries are removed from a + * hash table. + */ + +typedef void (*hg_hash_table_value_free_func_t)(hg_hash_table_value_t value); + +/** + * Create a new hash table. + * + * \param hash_func Function used to generate hash keys for the + * keys used in the table. + * \param equal_func Function used to test keys used in the table + * for equality. + * \return A new hash table structure, or NULL if it + * was not possible to allocate the new hash + * table. + */ +HG_UTIL_PUBLIC hg_hash_table_t *hg_hash_table_new(hg_hash_table_hash_func_t hash_func, + hg_hash_table_equal_func_t equal_func); + +/** + * Destroy a hash table. + * + * \param hash_table The hash table to destroy. + */ +HG_UTIL_PUBLIC void hg_hash_table_free(hg_hash_table_t *hash_table); + +/** + * Register functions used to free the key and value when an entry is + * removed from a hash table. + * + * \param hash_table The hash table. + * \param key_free_func Function used to free keys. + * \param value_free_func Function used to free values. + */ +HG_UTIL_PUBLIC void hg_hash_table_register_free_functions(hg_hash_table_t * hash_table, + hg_hash_table_key_free_func_t key_free_func, + hg_hash_table_value_free_func_t value_free_func); + +/** + * Insert a value into a hash table, overwriting any existing entry + * using the same key. + * + * \param hash_table The hash table. + * \param key The key for the new value. + * \param value The value to insert. + * \return Non-zero if the value was added successfully, + * or zero if it was not possible to allocate + * memory for the new entry. + */ +HG_UTIL_PUBLIC int hg_hash_table_insert(hg_hash_table_t *hash_table, hg_hash_table_key_t key, + hg_hash_table_value_t value); + +/** + * Look up a value in a hash table by key. + * + * \param hash_table The hash table. + * \param key The key of the value to look up. + * \return The value, or \ref HASH_TABLE_NULL if there + * is no value with that key in the hash table. + */ +HG_UTIL_PUBLIC hg_hash_table_value_t hg_hash_table_lookup(hg_hash_table_t * hash_table, + hg_hash_table_key_t key); + +/** + * Remove a value from a hash table. + * + * \param hash_table The hash table. + * \param key The key of the value to remove. + * \return Non-zero if a key was removed, or zero if the + * specified key was not found in the hash table. + */ +HG_UTIL_PUBLIC int hg_hash_table_remove(hg_hash_table_t *hash_table, hg_hash_table_key_t key); + +/** + * Retrieve the number of entries in a hash table. + * + * \param hash_table The hash table. + * \return The number of entries in the hash table. + */ +HG_UTIL_PUBLIC unsigned int hg_hash_table_num_entries(hg_hash_table_t *hash_table); + +/** + * Initialise a \ref HashTableIterator to iterate over a hash table. + * + * \param hash_table The hash table. + * \param iter Pointer to an iterator structure to + * initialise. + */ +HG_UTIL_PUBLIC void hg_hash_table_iterate(hg_hash_table_t *hash_table, hg_hash_table_iter_t *iter); + +/** + * Determine if there are more keys in the hash table to iterate over. + * + * \param iterator The hash table iterator. + * \return Zero if there are no more values to iterate + * over, non-zero if there are more values to + * iterate over. + */ +HG_UTIL_PUBLIC int hg_hash_table_iter_has_more(hg_hash_table_iter_t *iterator); + +/** + * Using a hash table iterator, retrieve the next key. + * + * \param iterator The hash table iterator. + * \return The next key from the hash table, or + * \ref HG_HASH_TABLE_NULL if there are no more + * keys to iterate over. + */ +HG_UTIL_PUBLIC hg_hash_table_value_t hg_hash_table_iter_next(hg_hash_table_iter_t *iterator); + +#ifdef __cplusplus +} +#endif + +#endif /* HG_HASH_TABLE_H */ diff --git a/src/mercury/include/mercury_header.h b/src/mercury/include/mercury_header.h new file mode 100644 index 0000000..801ec69 --- /dev/null +++ b/src/mercury/include/mercury_header.h @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2013-2020 Argonne National Laboratory, Department of Energy, + * UChicago Argonne, LLC and The HDF Group. + * All rights reserved. + * + * The full copyright notice, including terms governing use, modification, + * and redistribution, is contained in the COPYING file that can be + * found at the root of the source code distribution tree. + */ + +#ifndef MERCURY_HEADER_H +#define MERCURY_HEADER_H + +#include "mercury_core_types.h" + +/*************************************/ +/* Public Type and Struct Definition */ +/*************************************/ + +#if defined(__GNUC__) || defined(_WIN32) +#pragma pack(push, 1) +#else +#warning "Proc header struct padding may not be consistent across platforms." +#endif +#ifdef HG_HAS_CHECKSUMS +struct hg_header_hash { + hg_uint32_t payload; /* Payload checksum (32-bits checksum) */ +}; +#endif + +struct hg_header_input { +#ifdef HG_HAS_CHECKSUMS + struct hg_header_hash hash; /* Hash */ +#else + hg_uint32_t pad; +#endif + /* 160 bits here */ +}; + +struct hg_header_output { +#ifdef HG_HAS_CHECKSUMS + struct hg_header_hash hash; /* Hash */ +#endif + hg_uint32_t pad; + /* 128/64 bits here */ +}; +#if defined(__GNUC__) || defined(_WIN32) +#pragma pack(pop) +#endif + +/* Common header struct input/output */ +struct hg_header { + union { + struct hg_header_input input; + struct hg_header_output output; + } msg; /* Header message */ + hg_op_t op; /* Header operation type */ +}; + +/*****************/ +/* Public Macros */ +/*****************/ + +/*********************/ +/* Public Prototypes */ +/*********************/ + +#ifdef __cplusplus +extern "C" { +#endif + +static HG_INLINE size_t hg_header_get_size(hg_op_t op); + +/** + * Get size reserved for header (separate user data stored in payload). + * + * \return Non-negative size value + */ +static HG_INLINE size_t +hg_header_get_size(hg_op_t op) +{ + hg_size_t ret = 0; + + switch (op) { + case HG_INPUT: + ret = sizeof(struct hg_header_input); + break; + case HG_OUTPUT: + ret = sizeof(struct hg_header_output); + break; + default: + break; + } + + return ret; +} + +/** + * Initialize RPC header. + * + * \param hg_header [IN/OUT] pointer to header structure + * \param op [IN] HG operation type: HG_INPUT / HG_OUTPUT + */ +HG_PRIVATE void hg_header_init(struct hg_header *hg_header, hg_op_t op); + +/** + * Finalize RPC header. + * + * \param hg_header [IN/OUT] pointer to header structure + */ +HG_PRIVATE void hg_header_finalize(struct hg_header *hg_header); + +/** + * Reset RPC header. + * + * \param hg_header [IN/OUT] pointer to header structure + * \param op [IN] HG operation type: HG_INPUT / HG_OUTPUT + */ +HG_PRIVATE void hg_header_reset(struct hg_header *hg_header, hg_op_t op); + +/** + * Process private information for sending/receiving RPC. + * + * \param op [IN] operation type: HG_ENCODE / HG_DECODE + * \param buf [IN/OUT] buffer + * \param buf_size [IN] buffer size + * \param hg_header [IN/OUT] pointer to header structure + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PRIVATE hg_return_t hg_header_proc(hg_proc_op_t op, void *buf, size_t buf_size, + struct hg_header *hg_header); + +#ifdef __cplusplus +} +#endif + +#endif /* MERCURY_HEADER_H */ diff --git a/src/mercury/include/mercury_hl.h b/src/mercury/include/mercury_hl.h new file mode 100644 index 0000000..c6d5b10 --- /dev/null +++ b/src/mercury/include/mercury_hl.h @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2013-2020 Argonne National Laboratory, Department of Energy, + * UChicago Argonne, LLC and The HDF Group. + * All rights reserved. + * + * The full copyright notice, including terms governing use, modification, + * and redistribution, is contained in the COPYING file that can be + * found at the root of the source code distribution tree. + */ + +#ifndef MERCURY_HL_H +#define MERCURY_HL_H + +#include "mercury.h" +#include "mercury_bulk.h" +#include "mercury_request.h" + +/*****************/ +/* Public Macros */ +/*****************/ + +/** + * Define macros so that default classes/contexts can be easily renamed + * if we ever need to. Users should use macros and not global variables + * directly. + */ +#define HG_CLASS_DEFAULT hg_class_default_g +#define HG_CONTEXT_DEFAULT hg_context_default_g +#define HG_REQUEST_CLASS_DEFAULT hg_request_class_default_g + +#ifdef __cplusplus +extern "C" { +#endif + +/********************/ +/* Public Variables */ +/********************/ + +/* HG default */ +extern HG_PUBLIC hg_class_t *HG_CLASS_DEFAULT; +extern HG_PUBLIC hg_context_t *HG_CONTEXT_DEFAULT; +extern HG_PUBLIC hg_request_class_t *HG_REQUEST_CLASS_DEFAULT; + +/*********************/ +/* Public Prototypes */ +/*********************/ + +/** + * Initialize Mercury high-level layer and create default classes/contexts. + * If no info_string is passed, the HG HL layer will attempt to initialize + * NA by using the value contained in the environment variable called + * MERCURY_PORT_NAME. + * \remark HG_Hl_finalize() is registered with atexit() so that default + * classes/contexts are freed at process termination. + * + * \param na_info_string [IN] host address with port number (e.g., + * "tcp://localhost:3344" or + * "bmi+tcp://localhost:3344") + * \param na_listen [IN] listen for incoming connections + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Hl_init(const char *na_info_string, hg_bool_t na_listen); + +/** + * Initialize Mercury high-level layer with options provided by init_info. + * Must be finalized with HG_Hl_finalize(). + * \remark HG_Hl_finalize() is registered with atexit() so that default + * classes/contexts are freed at process termination. + * \remark HG_Hl_init_opt() may become HG_Hl_init() in the future. + * + * \param na_info_string [IN] host address with port number (e.g., + * "tcp://localhost:3344" or + * "bmi+tcp://localhost:3344") + * \param na_listen [IN] listen for incoming connections + * \param hg_init_info [IN] (Optional) HG init info, NULL if no info + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Hl_init_opt(const char *na_info_string, hg_bool_t na_listen, + const struct hg_init_info *hg_init_info); + +/** + * Finalize Mercury high-level layer. + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Hl_finalize(void); + +/** + * Lookup an address and wait for its completion. Address must be freed + * using HG_Addr_free(). + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Hl_addr_lookup_wait(hg_context_t *context, hg_request_class_t *request_class, + const char *name, hg_addr_t *addr, unsigned int timeout); + +/** + * Forward a call and wait for its completion. A HG handle must have been + * previously created. Output can be queried using HG_Get_output() and freed + * using HG_Free_output(). + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Hl_forward_wait(hg_request_class_t *request_class, hg_handle_t handle, + void *in_struct, unsigned int timeout); + +/** + * Initiate a bulk data transfer and wait for its completion. + * + * \param context [IN] pointer to HG context + * \param op [IN] transfer operation: + * - HG_BULK_PUSH + * - HG_BULK_PULL + * \param origin_addr [IN] abstract address of origin + * \param origin_handle [IN] abstract bulk handle + * \param origin_offset [IN] offset + * \param local_handle [IN] abstract bulk handle + * \param local_offset [IN] offset + * \param size [IN] size of data to be transferred + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t HG_Hl_bulk_transfer_wait(hg_context_t *context, hg_request_class_t *request_class, + hg_bulk_op_t op, hg_addr_t origin_addr, + hg_bulk_t origin_handle, hg_size_t origin_offset, + hg_bulk_t local_handle, hg_size_t local_offset, hg_size_t size, + unsigned int timeout); + +#ifdef __cplusplus +} +#endif + +#endif /* MERCURY_HL_H */ diff --git a/src/mercury/include/mercury_hl_macros.h b/src/mercury/include/mercury_hl_macros.h new file mode 100644 index 0000000..6c9135b --- /dev/null +++ b/src/mercury/include/mercury_hl_macros.h @@ -0,0 +1,384 @@ +/* + * Copyright (C) 2013-2020 Argonne National Laboratory, Department of Energy, + * UChicago Argonne, LLC and The HDF Group. + * All rights reserved. + * + * The full copyright notice, including terms governing use, modification, + * and redistribution, is contained in the COPYING file that can be + * found at the root of the source code distribution tree. + */ + +#ifndef MERCURY_HL_MACROS_H +#define MERCURY_HL_MACROS_H + +#include "mercury_hl.h" +#include "mercury_macros.h" + +/** + * The purpose of these macros is to generate boilerplate code in order + * to send and execute HG RPC calls. + * Since these macros make use of the mercury high-level interface, applications + * using these macros must link to the mercury_hl library. + * HG_XXX macros are private macros / MERCURY_XXX are public macros. + * Macros defined in this file are: + * - MERCURY_GEN_LOG_MESSAGE + * - MERCURY_GEN_RPC_STUB + * - MERCURY_GEN_CALLBACK_STUB + */ + +/****************/ +/* Local Macros */ +/****************/ + +/* Return parameter with fixed name */ +#define HG_GEN_RET_PARAM(ret_type) ((ret_type)(ret)) + +/* Generate ((param) (datai)) element */ +#define HG_GEN_PARAM_NAME(r, prefix, i, param) ((param)(BOOST_PP_CAT(prefix, i))) + +/* Generate parameter names and ((type) (name)) sequence */ +#define HG_GEN_PARAM_NAME_SEQ(prefix, type_seq) BOOST_PP_SEQ_FOR_EACH_I(HG_GEN_PARAM_NAME, prefix, type_seq) + +/* Extract parameter (type name) element */ +#define HG_GEN_DECL_FUNC_PARAM(r, is_ref, param) \ + (HG_GEN_GET_TYPE(param) BOOST_PP_IF(is_ref, *, BOOST_PP_EMPTY()) HG_GEN_GET_NAME(param)) + +/* Extract (type name) sequence */ +#define HG_GEN_DECL_FUNC_PARAM_SEQ(is_ref, param_seq) \ + BOOST_PP_SEQ_FOR_EACH(HG_GEN_DECL_FUNC_PARAM, is_ref, param_seq) + +/* Extract function parameter declarations */ +#define HG_GEN_DECL_FUNC_PARAMS(with_input, in_params, extra_in_params, with_output, out_params, \ + extra_out_params) \ + BOOST_PP_SEQ_TO_TUPLE(BOOST_PP_IF( \ + BOOST_PP_OR(with_input, with_output), \ + HG_GEN_DECL_FUNC_PARAM_SEQ(0, in_params) HG_GEN_DECL_FUNC_PARAM_SEQ(0, extra_in_params) \ + HG_GEN_DECL_FUNC_PARAM_SEQ(1, out_params) HG_GEN_DECL_FUNC_PARAM_SEQ(1, extra_out_params), \ + (void))) + +/* Extract parameter (get_name(param)) element */ +#define HG_GEN_FUNC_PARAM(r, is_ref, param) (BOOST_PP_IF(is_ref, &, BOOST_PP_EMPTY()) HG_GEN_GET_NAME(param)) + +/* Extract (name) sequence */ +#define HG_GEN_FUNC_PARAM_SEQ(is_ref, param_seq) BOOST_PP_SEQ_FOR_EACH(HG_GEN_FUNC_PARAM, is_ref, param_seq) + +/* Extract function parameters */ +#define HG_GEN_FUNC_PARAMS(with_input, in_params, extra_in_params, with_output, out_params, \ + extra_out_params) \ + BOOST_PP_SEQ_TO_TUPLE( \ + BOOST_PP_IF(BOOST_PP_OR(with_input, with_output), \ + HG_GEN_FUNC_PARAM_SEQ(0, in_params) HG_GEN_FUNC_PARAM_SEQ(0, extra_in_params) \ + HG_GEN_FUNC_PARAM_SEQ(1, out_params) HG_GEN_FUNC_PARAM_SEQ(1, extra_out_params), \ + ())) + +/* Generate declaration of parameters --> type name; */ +#define HG_GEN_DECL_PARAMS(param_seq) BOOST_PP_SEQ_FOR_EACH(HG_GEN_STRUCT_FIELD, , param_seq) + +/* Assign param to struct field ( e.g., struct_name.param_1 = param_1; ) */ +#define HG_SET_STRUCT_PARAM(r, struct_name, param) \ + struct_name.HG_GEN_GET_NAME(param) = HG_GEN_GET_NAME(param); + +/* Assign param ((type) (name)) sequence to struct_name */ +#define HG_SET_STRUCT_PARAMS(struct_name, params) \ + BOOST_PP_SEQ_FOR_EACH(HG_SET_STRUCT_PARAM, struct_name, params) + +/* Assign struct_name field to param ( e.g., param_1 = struct_name.param_1; ) */ +#define HG_GET_STRUCT_PARAM(r, struct_name, param) \ + HG_GEN_GET_NAME(param) = struct_name.HG_GEN_GET_NAME(param); + +/* Assign struct_name fields to param ((type) (name)) sequence */ +#define HG_GET_STRUCT_PARAMS(struct_name, params) \ + BOOST_PP_SEQ_FOR_EACH(HG_GET_STRUCT_PARAM, struct_name, params) + +/* Assign struct_name field to out param ( e.g., *param_1 = struct_name.param_1; + * ) */ +#define HG_GET_OUT_STRUCT_PARAM(r, struct_name, param) \ + *HG_GEN_GET_NAME(param) = struct_name.HG_GEN_GET_NAME(param); + +/* Assign struct_name fields to out parame ((type) (name)) sequence */ +#define HG_GET_OUT_STRUCT_PARAMS(struct_name, params) \ + BOOST_PP_SEQ_FOR_EACH(HG_GET_OUT_STRUCT_PARAM, struct_name, params) + +/** + * Get/free output boilerplate code + */ + +/* Get output */ +#define HG_GET_OUTPUT(with_ret, ret_fail) \ + hg_ret = HG_Get_output(handle, &out_struct); \ + if (hg_ret != HG_SUCCESS) { \ + BOOST_PP_IF(with_ret, ret = ret_fail;, BOOST_PP_EMPTY()) \ + goto done; \ + } + +/* Free output */ +#define HG_FREE_OUTPUT(with_ret, ret_fail) \ + hg_ret = HG_Free_output(handle, &out_struct); \ + if (hg_ret != HG_SUCCESS) { \ + BOOST_PP_IF(with_ret, ret = ret_fail;, BOOST_PP_EMPTY()) \ + goto done; \ + } + +/** + * Bulk data support boilerplate code + */ + +/* Extra input parameters for bulk data */ +#define HG_BULK_CONST_BUF ((const void *)(bulk_buf)) +#define HG_BULK_BUF ((void *)(bulk_buf)) +#define HG_BULK_COUNT ((hg_uint64_t)(bulk_count)) +#define HG_BULK_EXTRA_IN_PARAM HG_BULK_BUF HG_BULK_COUNT + +/* Bulk handle parameter */ +#define HG_BULK_PARAM ((hg_bulk_t)(bulk_handle)) + +/* Local bulk handle parameter */ +#define HG_BULK_LOCAL_PARAM ((hg_bulk_t)(local_bulk_handle)) + +/* Create bulk handle */ +#define HG_BULK_REGISTER(handle, bulk_handle, with_ret, fail_ret, bulk_read) \ + hg_ret = HG_Bulk_create(HG_Get_info(handle)->hg_bulk_class, 1, \ + &HG_GEN_GET_NAME(BOOST_PP_SEQ_HEAD(HG_BULK_BUF)), \ + &HG_GEN_GET_NAME(BOOST_PP_SEQ_HEAD(HG_BULK_COUNT)), \ + BOOST_PP_IF(bulk_read, HG_BULK_READ_ONLY, HG_BULK_READWRITE), &bulk_handle); \ + if (hg_ret != HG_SUCCESS) { \ + BOOST_PP_IF(with_ret, ret = fail_ret;, BOOST_PP_EMPTY()) \ + goto done; \ + } + +/* Free bulk handle */ +#define HG_BULK_FREE(bulk_handle, with_ret, fail_ret) \ + hg_ret = HG_Bulk_free(bulk_handle); \ + if (hg_ret != HG_SUCCESS) { \ + BOOST_PP_IF(with_ret, ret = fail_ret;, BOOST_PP_EMPTY()) \ + goto done; \ + } + +/* Declare variables required for bulk transfers */ +#define HG_GEN_DECL_BULK_PARAMS HG_GEN_DECL_PARAMS(HG_BULK_PARAM HG_BULK_LOCAL_PARAM HG_BULK_EXTRA_IN_PARAM) + +/* Allocate memory and create local bulk handle */ +#define HG_BULK_LOCAL_ALLOCATE(origin_bulk_handle, local_bulk_handle) \ + HG_GEN_GET_NAME(BOOST_PP_SEQ_HEAD(HG_BULK_COUNT)) = HG_Bulk_get_size(origin_bulk_handle); \ + HG_GEN_GET_NAME(BOOST_PP_SEQ_HEAD(HG_BULK_BUF)) = \ + malloc(HG_GEN_GET_NAME(BOOST_PP_SEQ_HEAD(HG_BULK_COUNT))); \ + HG_Bulk_create(HG_Get_info(handle)->hg_bulk_class, 1, &HG_GEN_GET_NAME(BOOST_PP_SEQ_HEAD(HG_BULK_BUF)), \ + &HG_GEN_GET_NAME(BOOST_PP_SEQ_HEAD(HG_BULK_COUNT)), HG_BULK_READWRITE, \ + &local_bulk_handle); + +/* Free memory and local handle */ +#define HG_BULK_LOCAL_FREE(local_bulk_handle) \ + hg_ret = HG_Bulk_free(local_bulk_handle); \ + if (hg_ret != HG_SUCCESS) { \ + goto done; \ + } \ + free(HG_GEN_GET_NAME(BOOST_PP_SEQ_HEAD(HG_BULK_BUF))); + +/* Transfer bulk data using origin/local bulk handles (pull or push) */ +#define HG_BULK_TRANSFER(handle, origin_bulk_handle, local_bulk_handle, bulk_read) \ + hg_ret = HG_Hl_bulk_transfer_wait( \ + HG_Get_info(handle)->bulk_context, BOOST_PP_IF(bulk_read, HG_BULK_PULL, HG_BULK_PUSH), \ + HG_Get_info(handle)->addr, HG_Get_info(handle)->target_id, origin_bulk_handle, 0, local_bulk_handle, \ + 0, HG_GEN_GET_NAME(BOOST_PP_SEQ_HEAD(HG_BULK_COUNT))); \ + if (hg_ret != HG_SUCCESS) { \ + goto done; \ + } + +/*****************/ +/* Public Macros */ +/*****************/ + +/** + * Advanced BOOST macros: + * - MERCURY_GEN_RPC_STUB + * - MERCURY_GEN_CALLBACK_STUB + */ + +/* Custom function that applications can define for log purposes (none by + * default) */ +#ifndef MERCURY_GEN_LOG_MESSAGE +#define MERCURY_GEN_LOG_MESSAGE(x) +#endif + +/* Booleans for MERCURY_GEN_MACROS */ +#define MERCURY_GEN_FALSE 0 +#define MERCURY_GEN_TRUE 1 + +/* Generate RPC stub */ +#define MERCURY_GEN_RPC_STUB(gen_func_name, func_name, with_ret, ret_type_name, ret_fail, with_input, \ + in_struct_type_name, in_params, with_output, out_struct_type_name, out_params, \ + with_bulk, bulk_read) \ + BOOST_PP_IF(with_ret, ret_type_name, void) \ + gen_func_name HG_GEN_DECL_FUNC_PARAMS(with_input, in_params, \ + BOOST_PP_IF(with_bulk, HG_BULK_EXTRA_IN_PARAM, BOOST_PP_EMPTY()), \ + with_output, out_params, ) \ + { \ + BOOST_PP_IF(with_input, in_struct_type_name in_struct;, BOOST_PP_EMPTY()) \ + BOOST_PP_IF(BOOST_PP_OR(with_output, with_ret), out_struct_type_name out_struct;, BOOST_PP_EMPTY()) \ + BOOST_PP_IF(with_ret, ret_type_name ret;, BOOST_PP_EMPTY()) \ + hg_id_t id; \ + hg_handle_t handle; \ + BOOST_PP_IF(with_bulk, HG_GEN_DECL_PARAMS(HG_BULK_PARAM), BOOST_PP_EMPTY()) \ + hg_bool_t func_registered; \ + hg_return_t hg_ret; \ + \ + /* Init stack if not initialized */ \ + HG_Hl_init(NULL, 0); \ + \ + /* Check whether call has already been registered or not */ \ + HG_Registered_rpc(HG_CLASS_DEFAULT, BOOST_PP_STRINGIZE(func_name), &func_registered, &id); \ + if (!func_registered) { \ + id = MERCURY_REGISTER( \ + HG_CLASS_DEFAULT, BOOST_PP_STRINGIZE(func_name), \ + BOOST_PP_IF(with_input, in_struct_type_name, void), \ + BOOST_PP_IF(BOOST_PP_OR(with_output, with_ret), out_struct_type_name, void), NULL); \ + } \ + \ + /* Create HG handle */ \ + hg_ret = HG_Create(HG_CLASS_DEFAULT, HG_CONTEXT_DEFAULT, NA_ADDR_DEFAULT, id, &handle); \ + if (hg_ret != HG_SUCCESS) { \ + BOOST_PP_IF(with_ret, ret = ret_fail;, BOOST_PP_EMPTY()) \ + goto done; \ + } \ + \ + /* Create bulk handle */ \ + BOOST_PP_IF(with_bulk, \ + HG_BULK_REGISTER(handle, HG_GEN_GET_NAME(BOOST_PP_SEQ_HEAD(HG_BULK_PARAM)), with_ret, \ + ret_fail, bulk_read), \ + BOOST_PP_EMPTY()) \ + \ + /* Fill input structure */ \ + BOOST_PP_IF(with_input, \ + HG_SET_STRUCT_PARAMS(in_struct, \ + in_params BOOST_PP_IF(with_bulk, HG_BULK_PARAM, BOOST_PP_EMPTY())), \ + BOOST_PP_EMPTY()) \ + \ + /* Forward call to default target */ \ + hg_ret = HG_Hl_forward_wait(handle, BOOST_PP_IF(with_input, &in_struct, NULL)); \ + if (hg_ret != HG_SUCCESS) { \ + BOOST_PP_IF(with_ret, ret = ret_fail;, BOOST_PP_EMPTY()) \ + goto done; \ + } \ + \ + /* Free bulk handle */ \ + BOOST_PP_IF(with_bulk, \ + HG_BULK_FREE(HG_GEN_GET_NAME(BOOST_PP_SEQ_HEAD(HG_BULK_PARAM)), with_ret, ret_fail), \ + BOOST_PP_EMPTY()) \ + \ + /* Get output */ \ + BOOST_PP_IF(BOOST_PP_OR(with_output, with_ret), HG_GET_OUTPUT(with_ret, ret_fail), BOOST_PP_EMPTY()) \ + \ + /* Get output parameters */ \ + BOOST_PP_IF(with_ret, HG_GET_STRUCT_PARAMS(out_struct, ((ret_type)(ret))), BOOST_PP_EMPTY()) \ + BOOST_PP_IF(with_output, HG_GET_OUT_STRUCT_PARAMS(out_struct, out_params), BOOST_PP_EMPTY()) \ + \ + /* Free output */ \ + BOOST_PP_IF(BOOST_PP_OR(with_output, with_ret), HG_FREE_OUTPUT(with_ret, ret_fail), \ + BOOST_PP_EMPTY()) \ + \ + /* Destroy handle */ \ + hg_ret = HG_Destroy(handle); \ + if (hg_ret != HG_SUCCESS) { \ + BOOST_PP_IF(with_ret, ret = ret_fail;, BOOST_PP_EMPTY()) \ + goto done; \ + } \ + \ +done: \ + \ + return BOOST_PP_IF(with_ret, ret, BOOST_PP_EMPTY()); \ + } + +/* Generate callback stub */ +#define MERCURY_GEN_CALLBACK_STUB(gen_func_name, func_name, with_ret, ret_type, with_input, \ + in_struct_type_name, in_params, with_output, out_struct_type_name, \ + out_params, with_bulk, bulk_read, with_thread, thread_pool) \ + static BOOST_PP_IF(with_thread, HG_THREAD_RETURN_TYPE BOOST_PP_CAT(gen_func_name, _thread), \ + hg_return_t gen_func_name)(BOOST_PP_IF(with_thread, void *arg, hg_handle_t handle)) \ + { \ + BOOST_PP_IF(with_thread, hg_handle_t handle = (hg_handle_t)arg; \ + hg_thread_ret_t thread_ret = (hg_thread_ret_t)0;, BOOST_PP_EMPTY()) \ + hg_return_t hg_ret = HG_SUCCESS; \ + BOOST_PP_IF(with_input, in_struct_type_name in_struct;, BOOST_PP_EMPTY()) \ + BOOST_PP_IF(BOOST_PP_OR(with_output, with_ret), out_struct_type_name out_struct;, BOOST_PP_EMPTY()) \ + BOOST_PP_IF(with_input, HG_GEN_DECL_PARAMS(in_params), BOOST_PP_EMPTY()) \ + BOOST_PP_IF(with_output, HG_GEN_DECL_PARAMS(out_params), BOOST_PP_EMPTY()) \ + BOOST_PP_IF(with_ret, ret_type ret;, BOOST_PP_EMPTY()) \ + BOOST_PP_IF(with_bulk, HG_GEN_DECL_BULK_PARAMS, BOOST_PP_EMPTY()) \ + \ + /* Get input */ \ + BOOST_PP_IF( \ + with_input, hg_ret = HG_Get_input(handle, &in_struct); \ + if (hg_ret != HG_SUCCESS) { goto done; } \ + \ + /* Get parameters */ \ + HG_GET_STRUCT_PARAMS(in_struct, \ + in_params BOOST_PP_IF(with_bulk, HG_BULK_PARAM, BOOST_PP_EMPTY())), \ + BOOST_PP_EMPTY()) \ + \ + /* Allocate bulk handle */ \ + BOOST_PP_IF(with_bulk, \ + HG_BULK_LOCAL_ALLOCATE(HG_GEN_GET_NAME(BOOST_PP_SEQ_HEAD(HG_BULK_PARAM)), \ + HG_GEN_GET_NAME(BOOST_PP_SEQ_HEAD(HG_BULK_LOCAL_PARAM))), \ + BOOST_PP_EMPTY()) \ + BOOST_PP_IF(with_bulk, \ + BOOST_PP_IF(bulk_read, \ + HG_BULK_TRANSFER(handle, HG_GEN_GET_NAME(BOOST_PP_SEQ_HEAD(HG_BULK_PARAM)), \ + HG_GEN_GET_NAME(BOOST_PP_SEQ_HEAD(HG_BULK_LOCAL_PARAM)), \ + bulk_read), \ + BOOST_PP_EMPTY()), \ + BOOST_PP_EMPTY()) \ + \ + /* Call function */ \ + MERCURY_GEN_LOG_MESSAGE(BOOST_PP_STRINGIZE(func_name)); \ + BOOST_PP_IF(with_ret, ret =, BOOST_PP_EMPTY()) \ + func_name HG_GEN_FUNC_PARAMS(with_input, in_params, \ + BOOST_PP_IF(with_bulk, HG_BULK_EXTRA_IN_PARAM, BOOST_PP_EMPTY()), \ + with_output, out_params, ); \ + \ + BOOST_PP_IF(with_bulk, \ + BOOST_PP_IF(bulk_read, BOOST_PP_EMPTY(), \ + HG_BULK_TRANSFER(handle, HG_GEN_GET_NAME(BOOST_PP_SEQ_HEAD(HG_BULK_PARAM)), \ + HG_GEN_GET_NAME(BOOST_PP_SEQ_HEAD(HG_BULK_LOCAL_PARAM)), \ + bulk_read)), \ + BOOST_PP_EMPTY()) \ + \ + /* Free bulk handle */ \ + BOOST_PP_IF(with_bulk, HG_BULK_LOCAL_FREE(HG_GEN_GET_NAME(BOOST_PP_SEQ_HEAD(HG_BULK_LOCAL_PARAM))), \ + BOOST_PP_EMPTY()) \ + \ + /* Fill output structure */ \ + BOOST_PP_IF(with_ret, HG_SET_STRUCT_PARAMS(out_struct, ((ret_type)(ret))), BOOST_PP_EMPTY()) \ + BOOST_PP_IF(with_output, HG_SET_STRUCT_PARAMS(out_struct, out_params), BOOST_PP_EMPTY()) \ + \ + /* Respond back */ \ + hg_ret = HG_Respond(handle, NULL, NULL, \ + BOOST_PP_IF(BOOST_PP_OR(with_output, with_ret), &out_struct, NULL)); \ + if (hg_ret != HG_SUCCESS) { \ + goto done; \ + } \ + \ + /* Free input */ \ + BOOST_PP_IF( \ + with_input, hg_ret = HG_Free_input(handle, &in_struct); \ + if (hg_ret != HG_SUCCESS) { goto done; }, BOOST_PP_EMPTY()) \ + \ + /* Destroy handle */ \ + hg_ret = HG_Destroy(handle); \ + if (hg_ret != HG_SUCCESS) { \ + goto done; \ + } \ + \ +done: \ + \ + BOOST_PP_IF(with_thread, return thread_ret;, return hg_ret;) \ + } \ + BOOST_PP_IF( \ + with_thread, \ + static hg_return_t gen_func_name(hg_handle_t handle) { \ + hg_return_t ret = HG_SUCCESS; \ + hg_thread_pool_post(thread_pool, &BOOST_PP_CAT(gen_func_name, _thread), handle); \ + return ret; \ + }, \ + BOOST_PP_EMPTY()) + +#endif /* MERCURY_HL_MACROS_H */ diff --git a/src/mercury/include/mercury_list.h b/src/mercury/include/mercury_list.h new file mode 100644 index 0000000..18ce93a --- /dev/null +++ b/src/mercury/include/mercury_list.h @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2013-2020 Argonne National Laboratory, Department of Energy, + * UChicago Argonne, LLC and The HDF Group. + * All rights reserved. + * + * The full copyright notice, including terms governing use, modification, + * and redistribution, is contained in the COPYING file that can be + * found at the root of the source code distribution tree. + */ + +/* Code below is derived from sys/queue.h which follows the below notice: + * + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)queue.h 8.5 (Berkeley) 8/20/94 + */ + +#ifndef MERCURY_LIST_H +#define MERCURY_LIST_H + +#define HG_LIST_HEAD_INITIALIZER(name) \ + { \ + NULL \ + } + +#define HG_LIST_HEAD_INIT(struct_head_name, var_name) \ + struct struct_head_name var_name = HG_LIST_HEAD_INITIALIZER(var_name) + +#define HG_LIST_HEAD_DECL(struct_head_name, struct_entry_name) \ + struct struct_head_name { \ + struct struct_entry_name *head; \ + } + +#define HG_LIST_HEAD(struct_entry_name) \ + struct { \ + struct struct_entry_name *head; \ + } + +#define HG_LIST_ENTRY(struct_entry_name) \ + struct { \ + struct struct_entry_name * next; \ + struct struct_entry_name **prev; \ + } + +#define HG_LIST_INIT(head_ptr) \ + do { \ + (head_ptr)->head = NULL; \ + } while (/*CONSTCOND*/ 0) + +#define HG_LIST_IS_EMPTY(head_ptr) ((head_ptr)->head == NULL) + +#define HG_LIST_FIRST(head_ptr) ((head_ptr)->head) + +#define HG_LIST_NEXT(entry_ptr, entry_field_name) ((entry_ptr)->entry_field_name.next) + +#define HG_LIST_INSERT_AFTER(list_entry_ptr, entry_ptr, entry_field_name) \ + do { \ + if (((entry_ptr)->entry_field_name.next = (list_entry_ptr)->entry_field_name.next) != NULL) \ + (list_entry_ptr)->entry_field_name.next->entry_field_name.prev = \ + &(entry_ptr)->entry_field_name.next; \ + (list_entry_ptr)->entry_field_name.next = (entry_ptr); \ + (entry_ptr)->entry_field_name.prev = &(list_entry_ptr)->entry_field_name.next; \ + } while (/*CONSTCOND*/ 0) + +#define HG_LIST_INSERT_BEFORE(list_entry_ptr, entry_ptr, entry_field_name) \ + do { \ + (entry_ptr)->entry_field_name.prev = (list_entry_ptr)->entry_field_name.prev; \ + (entry_ptr)->entry_field_name.next = (list_entry_ptr); \ + *(list_entry_ptr)->entry_field_name.prev = (entry_ptr); \ + (list_entry_ptr)->entry_field_name.prev = &(entry_ptr)->entry_field_name.next; \ + } while (/*CONSTCOND*/ 0) + +#define HG_LIST_INSERT_HEAD(head_ptr, entry_ptr, entry_field_name) \ + do { \ + if (((entry_ptr)->entry_field_name.next = (head_ptr)->head) != NULL) \ + (head_ptr)->head->entry_field_name.prev = &(entry_ptr)->entry_field_name.next; \ + (head_ptr)->head = (entry_ptr); \ + (entry_ptr)->entry_field_name.prev = &(head_ptr)->head; \ + } while (/*CONSTCOND*/ 0) + +/* TODO would be nice to not have any condition */ +#define HG_LIST_REMOVE(entry_ptr, entry_field_name) \ + do { \ + if ((entry_ptr)->entry_field_name.next != NULL) \ + (entry_ptr)->entry_field_name.next->entry_field_name.prev = (entry_ptr)->entry_field_name.prev; \ + *(entry_ptr)->entry_field_name.prev = (entry_ptr)->entry_field_name.next; \ + } while (/*CONSTCOND*/ 0) + +#define HG_LIST_FOREACH(var, head_ptr, entry_field_name) \ + for ((var) = ((head_ptr)->head); (var); (var) = ((var)->entry_field_name.next)) + +#endif /* MERCURY_LIST_H */ diff --git a/src/mercury/include/mercury_log.h b/src/mercury/include/mercury_log.h new file mode 100644 index 0000000..0e98710 --- /dev/null +++ b/src/mercury/include/mercury_log.h @@ -0,0 +1,393 @@ +/* + * Copyright (C) 2013-2020 Argonne National Laboratory, Department of Energy, + * UChicago Argonne, LLC and The HDF Group. + * All rights reserved. + * + * The full copyright notice, including terms governing use, modification, + * and redistribution, is contained in the COPYING file that can be + * found at the root of the source code distribution tree. + */ + +/* + * Copyright (c) 2004, 2005, 2006, 2007 David Young. All rights reserved. + * + * Copyright (c) 2004 Urbana-Champaign Independent Media Center. + * All rights reserved. + * + * + * Portions of hlog are Copyright (c) David Young. The applicable copyright + * notice and licensing terms are reproduced here: + * + * Copyright (c) 2004, 2005, 2006, 2007 David Young. All rights reserved. + * + * This file contains code contributed by David Young. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY DAVID YOUNG ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DAVID + * YOUNG BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * ----------------------------------------------------------------------------- + * ----------------------------------------------------------------------------- + * + * Portions of hlog are Copyright (c) Urbana-Champaign Independent Media Center. + * The applicable copyright notice and licensing terms are reproduced here: + * + * Copyright (c) 2004 Urbana-Champaign Independent Media Center. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE URBANA-CHAMPAIGN INDEPENDENT + * MEDIA CENTER ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE URBANA-CHAMPAIGN INDEPENDENT + * MEDIA CENTER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MERCURY_LOG_H +#define MERCURY_LOG_H + +#include "mercury_dlog.h" +#include "mercury_queue.h" +#include "mercury_util_config.h" + +#include <stdio.h> + +/*****************/ +/* Public Macros */ +/*****************/ + +/* For compatibility */ +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ < 199901L) +#if defined(__GNUC__) && (__GNUC__ >= 2) +#define __func__ __FUNCTION__ +#else +#define __func__ "<unknown>" +#endif +#elif defined(_WIN32) +#define __func__ __FUNCTION__ +#endif + +/* Cat macro */ +#define HG_UTIL_CAT(x, y) x##y + +/* Stringify macro */ +#define HG_UTIL_STRINGIFY(x) #x + +/* Constructor (used to initialize log outlets) */ +#define HG_UTIL_CONSTRUCTOR __attribute__((constructor)) + +/* Available log levels, additional log levels should be added to that list by + * order of verbosity. Format is: + * - enum type + * - level name + * - default output + * + * error: print error level logs + * warning: print warning level logs + * min_debug: store minimal debug information and defer printing until error + * debug: print debug level logs + */ +#define HG_LOG_LEVELS \ + X(HG_LOG_LEVEL_NONE, "", NULL) /*!< no log */ \ + X(HG_LOG_LEVEL_ERROR, "error", &stderr) /*!< error log type */ \ + X(HG_LOG_LEVEL_WARNING, "warning", &stdout) /*!< warning log type */ \ + X(HG_LOG_LEVEL_MIN_DEBUG, "min_debug", &stdout) /*!< debug log type */ \ + X(HG_LOG_LEVEL_DEBUG, "debug", &stdout) /*!< debug log type */ \ + X(HG_LOG_LEVEL_MAX, "", NULL) + +/* HG_LOG_OUTLET: global variable name of log outlet. */ +#define HG_LOG_OUTLET(name) HG_UTIL_CAT(name, _log_outlet_g) + +/* HG_LOG_OUTLET_DECL: declare an outlet. */ +#define HG_LOG_OUTLET_DECL(name) struct hg_log_outlet HG_LOG_OUTLET(name) + +/* + * HG_LOG_OUTLET_INITIALIZER: initializer for a log in a global variable. + * (parent and debug_log are optional and can be set to NULL) + */ +#define HG_LOG_OUTLET_INITIALIZER(name, state, parent, debug_log) \ + { \ + HG_UTIL_STRINGIFY(name), state, HG_LOG_LEVEL_NONE, parent, debug_log, { NULL } \ + } + +/* HG_LOG_OUTLET_SUBSYS_INITIALIZER: initializer for a sub-system log. */ +#define HG_LOG_OUTLET_SUBSYS_INITIALIZER(name, parent_name) \ + HG_LOG_OUTLET_INITIALIZER(name, HG_LOG_PASS, &HG_LOG_OUTLET(parent_name), NULL) + +/* HG_LOG_OUTLET_SUBSYS_STATE_INITIALIZER: initializer for a sub-system log with + * a defined state. */ +#define HG_LOG_OUTLET_SUBSYS_STATE_INITIALIZER(name, parent_name, state) \ + HG_LOG_OUTLET_INITIALIZER(name, state, &HG_LOG_OUTLET(parent_name), NULL) + +/* HG_LOG_SUBSYS_REGISTER: register a name */ +#define HG_LOG_SUBSYS_REGISTER(name) \ + static void HG_UTIL_CAT(hg_log_outlet_, name)(void) HG_UTIL_CONSTRUCTOR; \ + static void HG_UTIL_CAT(hg_log_outlet_, name)(void) { hg_log_outlet_register(&HG_LOG_OUTLET(name)); } \ + /* Keep unused prototype to use semicolon at end of macro */ \ + void hg_log_outlet_##name##_unused(void) + +/* HG_LOG_SUBSYS_DECL_REGISTER: declare and register a log outlet. */ +#define HG_LOG_SUBSYS_DECL_REGISTER(name, parent_name) \ + struct hg_log_outlet HG_LOG_OUTLET(name) = HG_LOG_OUTLET_SUBSYS_INITIALIZER(name, parent_name); \ + HG_LOG_SUBSYS_REGISTER(name) + +/* HG_LOG_SUBSYS_DECL_STATE_REGISTER: declare and register a log outlet and + * enforce an init state. */ +#define HG_LOG_SUBSYS_DECL_STATE_REGISTER(name, parent_name, state) \ + struct hg_log_outlet HG_LOG_OUTLET(name) = \ + HG_LOG_OUTLET_SUBSYS_STATE_INITIALIZER(name, parent_name, state); \ + HG_LOG_SUBSYS_REGISTER(name) + +/* Log macro */ +#define HG_LOG_WRITE(name, log_level, ...) \ + do { \ + if (HG_LOG_OUTLET(name).level < log_level) \ + break; \ + hg_log_write(&HG_LOG_OUTLET(name), log_level, __FILE__, __LINE__, __func__, __VA_ARGS__); \ + } while (0) + +/* Log macro */ +#define HG_LOG_WRITE_DEBUG(name, debug_func, ...) \ + do { \ + if (HG_LOG_OUTLET(name).level < HG_LOG_LEVEL_MIN_DEBUG) \ + break; \ + if (HG_LOG_OUTLET(name).level >= HG_LOG_LEVEL_MIN_DEBUG && HG_LOG_OUTLET(name).debug_log) \ + hg_dlog_addlog(HG_LOG_OUTLET(name).debug_log, __FILE__, __LINE__, __func__, NULL, NULL); \ + if (HG_LOG_OUTLET(name).level == HG_LOG_LEVEL_DEBUG) { \ + hg_log_write(&HG_LOG_OUTLET(name), HG_LOG_LEVEL_DEBUG, __FILE__, __LINE__, __func__, \ + __VA_ARGS__); \ + debug_func; \ + } \ + } while (0) + +/** + * Additional macros for debug log support. + */ + +/* HG_LOG_DEBUG_DLOG: global variable name of debug log. */ +#define HG_LOG_DEBUG_DLOG(name) HG_UTIL_CAT(name, _dlog_g) + +/* HG_LOG_DEBUG_LE: global variable name of debug log entries. */ +#define HG_LOG_DEBUG_LE(name) HG_UTIL_CAT(name, _dlog_entries_g) + +/* HG_LOG_DEBUG_DECL_DLOG: declare new debug log. */ +#define HG_LOG_DEBUG_DECL_DLOG(name) struct hg_dlog HG_LOG_DEBUG_DLOG(name) + +/* HG_LOG_DEBUG_DECL_LE: declare array of debug log entries. */ +#define HG_LOG_DEBUG_DECL_LE(name, size) struct hg_dlog_entry HG_LOG_DEBUG_LE(name)[size] + +/* HG_LOG_DLOG_INITIALIZER: initializer for a debug log */ +#define HG_LOG_DLOG_INITIALIZER(name, size) \ + HG_DLOG_INITIALIZER(HG_UTIL_STRINGIFY(name), HG_LOG_DEBUG_LE(name), size, 1) + +/* HG_LOG_OUTLET_SUBSYS_DLOG_INITIALIZER: initializer for a sub-system with + * debug log. */ +#define HG_LOG_OUTLET_SUBSYS_DLOG_INITIALIZER(name, parent_name) \ + HG_LOG_OUTLET_INITIALIZER(name, HG_LOG_PASS, &HG_LOG_OUTLET(parent_name), &HG_LOG_DEBUG_DLOG(name)) + +/* HG_LOG_SUBSYS_DLOG_DECL_REGISTER: declare and register a log outlet with + * debug log. */ +#define HG_LOG_SUBSYS_DLOG_DECL_REGISTER(name, parent_name) \ + struct hg_log_outlet HG_LOG_OUTLET(name) = HG_LOG_OUTLET_SUBSYS_DLOG_INITIALIZER(name, parent_name); \ + HG_LOG_SUBSYS_REGISTER(name) + +/* HG_LOG_ADD_COUNTER32: add 32-bit debug log counter */ +#define HG_LOG_ADD_COUNTER32(name, counter_ptr, counter_name, counter_desc) \ + hg_dlog_mkcount32(HG_LOG_OUTLET(name).debug_log, counter_ptr, counter_name, counter_desc) + +/* HG_LOG_ADD_COUNTER64: add 64-bit debug log counter */ +#define HG_LOG_ADD_COUNTER64(name, counter_ptr, counter_name, counter_desc) \ + hg_dlog_mkcount64(HG_LOG_OUTLET(name)->debug_log, counter_ptr, counter_name, counter_desc) + +/*************************************/ +/* Public Type and Struct Definition */ +/*************************************/ + +#define X(a, b, c) a, +/* Log levels */ +enum hg_log_level { HG_LOG_LEVELS }; +#undef X + +/* Log states */ +enum hg_log_state { HG_LOG_PASS, HG_LOG_OFF, HG_LOG_ON }; + +/* Log outlet */ +struct hg_log_outlet { + const char * name; /* Name of outlet */ + enum hg_log_state state; /* Init state of outlet */ + enum hg_log_level level; /* Level of outlet */ + struct hg_log_outlet *parent; /* Parent of outlet */ + struct hg_dlog * debug_log; /* Debug log to use */ + HG_QUEUE_ENTRY(hg_log_outlet) entry; /* List entry */ +}; + +/*********************/ +/* Public Prototypes */ +/*********************/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Set the global log level. + * + * \param log_level [IN] enum log level type + */ +HG_UTIL_PUBLIC void hg_log_set_level(enum hg_log_level log_level); + +/** + * Get the global log level. + * + * \return global log_level + */ +HG_UTIL_PUBLIC enum hg_log_level hg_log_get_level(void); + +/** + * Set the log subsystems from a string. Format is: subsys1,subsys2,... + * Subsys can also be forced to be disabled with "~", e.g., ~subsys1 + * + * \param log_level [IN] null terminated string + */ +HG_UTIL_PUBLIC void hg_log_set_subsys(const char *log_subsys); + +/** + * Get the log subsystems as a string. Format is similar to hg_log_set_subsys(). + * Buffer returned is static. + * + * \return string of enabled log subsystems + */ +HG_UTIL_PUBLIC const char *hg_log_get_subsys(void); + +/** + * Set a specific subsystem's log level. + */ +HG_UTIL_PUBLIC void hg_log_set_subsys_level(const char *subsys, enum hg_log_level log_level); + +/** + * Get the log level from a string. + * + * \param log_level [IN] null terminated string + * + * \return log type enum value + */ +HG_UTIL_PUBLIC enum hg_log_level hg_log_name_to_level(const char *log_level); + +/** + * Set the logging function. + * + * \param log_func [IN] pointer to function + */ +HG_UTIL_PUBLIC void hg_log_set_func(int (*log_func)(FILE *stream, const char *format, ...)); + +/** + * Set the stream for error output. + * + * \param stream [IN/OUT] pointer to stream + */ +HG_UTIL_PUBLIC void hg_log_set_stream_error(FILE *stream); + +/** + * Get the stream for error output. + * + * \return pointer to stream + */ +HG_UTIL_PUBLIC FILE *hg_log_get_stream_error(void); + +/** + * Set the stream for warning output. + * + * \param stream [IN/OUT] pointer to stream + */ +HG_UTIL_PUBLIC void hg_log_set_stream_warning(FILE *stream); + +/** + * Get the stream for warning output. + * + * \return pointer to stream + */ +HG_UTIL_PUBLIC FILE *hg_log_get_stream_warning(void); + +/** + * Set the stream for debug output. + * + * \param stream [IN/OUT] pointer to stream + */ +HG_UTIL_PUBLIC void hg_log_set_stream_debug(FILE *stream); + +/** + * Get the stream for debug output. + * + * \return pointer to stream + */ +HG_UTIL_PUBLIC FILE *hg_log_get_stream_debug(void); + +/** + * Register log outlet. + * + * \param outlet [IN] log outlet + */ +HG_UTIL_PUBLIC void hg_log_outlet_register(struct hg_log_outlet *outlet); + +/** + * Write log. + * + * \param outlet [IN] log outlet + * \param log_level [IN] log level + * \param file [IN] file name + * \param line [IN] line number + * \param func [IN] function name + * \param format [IN] string format + */ +HG_UTIL_PUBLIC void hg_log_write(struct hg_log_outlet *outlet, enum hg_log_level log_level, const char *file, + unsigned int line, const char *func, const char *format, ...) + HG_UTIL_PRINTF_LIKE(6, 7); + +/*********************/ +/* Public Variables */ +/*********************/ + +/* Top error outlet */ +extern HG_UTIL_PUBLIC HG_LOG_OUTLET_DECL(hg); + +#ifdef __cplusplus +} +#endif + +#endif /* MERCURY_LOG_H */ diff --git a/src/mercury/include/mercury_macros.h b/src/mercury/include/mercury_macros.h new file mode 100644 index 0000000..5950679 --- /dev/null +++ b/src/mercury/include/mercury_macros.h @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2013-2020 Argonne National Laboratory, Department of Energy, + * UChicago Argonne, LLC and The HDF Group. + * All rights reserved. + * + * The full copyright notice, including terms governing use, modification, + * and redistribution, is contained in the COPYING file that can be + * found at the root of the source code distribution tree. + */ + +#ifndef MERCURY_MACROS_H +#define MERCURY_MACROS_H + +#include "mercury.h" +#include "mercury_bulk.h" +#include "mercury_proc.h" +#include "mercury_proc_bulk.h" + +#ifdef HG_HAS_BOOST +#include <boost/preprocessor.hpp> + +/** + * The purpose of these macros is to facilitate generation of encoding/decoding + * procs as well as the registration of new routines to an existing HG class. + * HG_XXX macros are private macros / MERCURY_XXX are public macros. + * Macros defined in this file are: + * - MERCURY_REGISTER + * - MERCURY_GEN_PROC + * - MERCURY_GEN_STRUCT_PROC + */ + +/****************/ +/* Local Macros */ +/****************/ + +/* Get type / name */ +#define HG_GEN_GET_TYPE(field) BOOST_PP_SEQ_HEAD(field) +#define HG_GEN_GET_NAME(field) BOOST_PP_SEQ_CAT(BOOST_PP_SEQ_TAIL(field)) + +/* Get struct field */ +#define HG_GEN_STRUCT_FIELD(r, data, param) HG_GEN_GET_TYPE(param) HG_GEN_GET_NAME(param); + +/* Generate structure */ +#define HG_GEN_STRUCT(struct_type_name, fields) \ + typedef struct { \ + BOOST_PP_SEQ_FOR_EACH(HG_GEN_STRUCT_FIELD, , fields) \ + \ + } struct_type_name; + +/* Generate proc for struct field */ +#define HG_GEN_PROC(r, struct_name, field) \ + ret = BOOST_PP_CAT(hg_proc_, HG_GEN_GET_TYPE(field)(proc, &struct_name->HG_GEN_GET_NAME(field))); \ + if (unlikely(ret != HG_SUCCESS)) { \ + return ret; \ + } + +/* Generate proc for struct */ +#define HG_GEN_STRUCT_PROC(struct_type_name, fields) \ + static HG_INLINE hg_return_t BOOST_PP_CAT(hg_proc_, struct_type_name)(hg_proc_t proc, void *data) \ + { \ + hg_return_t ret = HG_SUCCESS; \ + struct_type_name *struct_data = (struct_type_name *)data; \ + \ + BOOST_PP_SEQ_FOR_EACH(HG_GEN_PROC, struct_data, fields) \ + \ + return ret; \ + } + +/*****************/ +/* Public Macros */ +/*****************/ + +/* Register func_name */ +#define MERCURY_REGISTER(hg_class, func_name, in_struct_type_name, out_struct_type_name, rpc_cb) \ + HG_Register_name(hg_class, func_name, BOOST_PP_CAT(hg_proc_, in_struct_type_name), \ + BOOST_PP_CAT(hg_proc_, out_struct_type_name), rpc_cb) + +/* Generate struct and corresponding struct proc */ +#define MERCURY_GEN_PROC(struct_type_name, fields) \ + HG_GEN_STRUCT(struct_type_name, fields) \ + HG_GEN_STRUCT_PROC(struct_type_name, fields) + +/* In the case of user defined structures / MERCURY_GEN_STRUCT_PROC can be + * used to generate the corresponding proc routine. + * E.g., if user defined struct: + * typedef struct { + * uint64_t cookie; + * } bla_handle_t; + * MERCURY_GEN_STRUCT_PROC( struct_type_name, field sequence ): + * MERCURY_GEN_STRUCT_PROC( bla_handle_t, ((uint64_t)(cookie)) ) + */ +#define MERCURY_GEN_STRUCT_PROC(struct_type_name, fields) HG_GEN_STRUCT_PROC(struct_type_name, fields) + +#else /* HG_HAS_BOOST */ + +/* Register func_name */ +#define MERCURY_REGISTER(hg_class, func_name, in_struct_type_name, out_struct_type_name, rpc_cb) \ + HG_Register_name(hg_class, func_name, hg_proc_##in_struct_type_name, hg_proc_##out_struct_type_name, \ + rpc_cb) + +#endif /* HG_HAS_BOOST */ + +/* If no input args or output args, a void type can be + * passed to MERCURY_REGISTER + */ +#define hg_proc_void NULL + +#endif /* MERCURY_MACROS_H */ diff --git a/src/mercury/include/mercury_mem.h b/src/mercury/include/mercury_mem.h new file mode 100644 index 0000000..3c15c01 --- /dev/null +++ b/src/mercury/include/mercury_mem.h @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2013-2020 Argonne National Laboratory, Department of Energy, + * UChicago Argonne, LLC and The HDF Group. + * All rights reserved. + * + * The full copyright notice, including terms governing use, modification, + * and redistribution, is contained in the COPYING file that can be + * found at the root of the source code distribution tree. + */ + +#ifndef MERCURY_MEM_H +#define MERCURY_MEM_H + +#include "mercury_util_config.h" + +/*************************************/ +/* Public Type and Struct Definition */ +/*************************************/ + +/*****************/ +/* Public Macros */ +/*****************/ + +#define HG_MEM_CACHE_LINE_SIZE 64 +#define HG_MEM_PAGE_SIZE 4096 + +/*********************/ +/* Public Prototypes */ +/*********************/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Get system default page size. + * + * \return page size on success or negative on failure + */ +HG_UTIL_PUBLIC long hg_mem_get_page_size(void); + +/** + * Allocate size bytes and return a pointer to the allocated memory. + * The memory address will be a multiple of alignment, which must be a power of + * two, and size should be a multiple of alignment. + * + * \param alignment [IN] alignment size + * \param size [IN] total requested size + * + * \return a pointer to the allocated memory, or NULL in case of failure + */ +HG_UTIL_PUBLIC void *hg_mem_aligned_alloc(size_t alignment, size_t size); + +/** + * Free memory allocated from hg_aligned_alloc(). + * + * \param mem_ptr [IN] pointer to allocated memory + */ +HG_UTIL_PUBLIC void hg_mem_aligned_free(void *mem_ptr); + +/** + * Allocate a buffer with a `size`-bytes, `alignment`-aligned payload + * preceded by a `header_size` header, padding the allocation with up + * to `alignment - 1` bytes to ensure that the payload is properly aligned. + * + * If `alignment` is 0, do not try to align the payload. It's ok if + * `size` is 0, however, behavior is undefined if both `header_size` + * and `size` are 0. + * + * \param header_size [IN] size of header + * \param alignment [IN] alignment size + * \param size [IN] requested payload size + * + * \return a pointer to the payload or NULL on failure + */ +HG_UTIL_PUBLIC void *hg_mem_header_alloc(size_t header_size, size_t alignment, size_t size); + +/** + * Free the memory that was returned previously by a call to + * `hg_mem_header_alloc()`. + * + * \param header_size [IN] size of header + * \param alignment [IN] alignment size + * \param mem_ptr [IN] memory pointer + */ +HG_UTIL_PUBLIC void hg_mem_header_free(size_t header_size, size_t alignment, void *mem_ptr); + +/** + * Create/open a shared-memory mapped file of size \size with name \name. + * + * \param name [IN] name of mapped file + * \param size [IN] total requested size + * \param create [IN] create file if not existing + * + * \return a pointer to the mapped memory region, or NULL in case of failure + */ +HG_UTIL_PUBLIC void *hg_mem_shm_map(const char *name, size_t size, hg_util_bool_t create); + +/** + * Unmap a previously mapped region and close the file. + * + * \param name [IN] name of mapped file + * \param mem_ptr [IN] pointer to mapped memory region + * \param size [IN] size range of the mapped region + * + * \return non-negative on success, or negative in case of failure + */ +HG_UTIL_PUBLIC int hg_mem_shm_unmap(const char *name, void *mem_ptr, size_t size); + +#ifdef __cplusplus +} +#endif + +#endif /* MERCURY_MEM_H */ diff --git a/src/mercury/include/mercury_mem_pool.h b/src/mercury/include/mercury_mem_pool.h new file mode 100644 index 0000000..d2acfdd --- /dev/null +++ b/src/mercury/include/mercury_mem_pool.h @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2013-2020 Argonne National Laboratory, Department of Energy, + * UChicago Argonne, LLC and The HDF Group. + * All rights reserved. + * + * The full copyright notice, including terms governing use, modification, + * and redistribution, is contained in the COPYING file that can be + * found at the root of the source code distribution tree. + */ + +#ifndef MERCURY_MEM_POOL_H +#define MERCURY_MEM_POOL_H + +#include "mercury_util_config.h" + +/*************************************/ +/* Public Type and Struct Definition */ +/*************************************/ + +/** + * Register memory block. + * + * \param buf [IN] pointer to buffer + * \param size [IN] buffer size + * \param handle [OUT] handle + * \param arg [IN/OUT] optional arguments + * + * \return HG_UTIL_SUCCESS if successful / error code otherwise + */ +typedef int (*hg_mem_pool_register_func_t)(const void *buf, size_t size, void **handle, void *arg); + +/** + * Deregister memory block. + * + * \param handle [IN/OUT] handle + * \param arg [IN/OUT] optional arguments + * + * \return HG_UTIL_SUCCESS if successful / error code otherwise + */ +typedef int (*hg_mem_pool_deregister_func_t)(void *handle, void *arg); + +/*****************/ +/* Public Macros */ +/*****************/ + +/*********************/ +/* Public Prototypes */ +/*********************/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Create a memory pool with \block_count of size \chunk_count x \chunk_size + * bytes. Optionally register and deregister memory for each block using + * \register_func and \deregister_func respectively. + * + * \param chunk_size [IN] size of chunks + * \param chunk_count [IN] number of chunks + * \param block_count [IN] number of blocks + * \param register_func [IN] pointer to register function + * \param deregister_func [IN] pointer to deregister function + * \param arg [IN/OUT] optional arguments passed to register functions + * + * \return HG_UTIL_SUCCESS if successful / error code otherwise + */ +HG_UTIL_PUBLIC struct hg_mem_pool *hg_mem_pool_create(size_t chunk_size, size_t chunk_count, + size_t block_count, + hg_mem_pool_register_func_t register_func, + hg_mem_pool_deregister_func_t deregister_func, + void * arg); + +/** + * Destroy a memory pool. + * + * \param hg_mem_pool [IN/OUT] pointer to memory pool + * + */ +HG_UTIL_PUBLIC void hg_mem_pool_destroy(struct hg_mem_pool *hg_mem_pool); + +/** + * Allocate \size bytes and optionally return a memory handle + * \mr_handle if registration functions were provided. + * + * \param hg_mem_pool [IN/OUT] pointer to memory pool + * \param size [IN] requested size + * \param mr_handle [OUT] pointer to memory handle + * + * \return pointer to memory block + */ +HG_UTIL_PUBLIC void *hg_mem_pool_alloc(struct hg_mem_pool *hg_mem_pool, size_t size, void **mr_handle); + +/** + * Release memory at address \mem_ptr. + * + * \param hg_mem_pool [IN/OUT] pointer to memory pool + * \param mem_ptr [IN] pointer to memory + * \param mr_handle [INT] pointer to memory handle + * + */ +HG_UTIL_PUBLIC void hg_mem_pool_free(struct hg_mem_pool *hg_mem_pool, void *mem_ptr, void *mr_handle); + +/** + * Retrieve chunk offset relative to the address used for registering + * the memory block it belongs to. + * + * \param hg_mem_pool [IN/OUT] pointer to memory pool + * \param mem_ptr [IN] pointer to memory + * \param mr_handle [INT] pointer to memory handle + * + * \return offset within registered block. + */ +HG_UTIL_PUBLIC size_t hg_mem_pool_chunk_offset(struct hg_mem_pool *hg_mem_pool, void *mem_ptr, + void *mr_handle); + +#ifdef __cplusplus +} +#endif + +#endif /* MERCURY_MEM_POOL_H */ diff --git a/src/mercury/include/mercury_poll.h b/src/mercury/include/mercury_poll.h new file mode 100644 index 0000000..f4072a5 --- /dev/null +++ b/src/mercury/include/mercury_poll.h @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2013-2020 Argonne National Laboratory, Department of Energy, + * UChicago Argonne, LLC and The HDF Group. + * All rights reserved. + * + * The full copyright notice, including terms governing use, modification, + * and redistribution, is contained in the COPYING file that can be + * found at the root of the source code distribution tree. + */ + +#ifndef MERCURY_POLL_H +#define MERCURY_POLL_H + +#include "mercury_util_config.h" + +/*************************************/ +/* Public Type and Struct Definition */ +/*************************************/ + +typedef struct hg_poll_set hg_poll_set_t; + +typedef union hg_poll_data { + void * ptr; + int fd; + hg_util_uint32_t u32; + hg_util_uint64_t u64; +} hg_poll_data_t; + +struct hg_poll_event { + hg_util_uint32_t events; /* Poll events */ + hg_poll_data_t data; /* User data variable */ +}; + +/*****************/ +/* Public Macros */ +/*****************/ + +/** + * Polling events. + */ +#define HG_POLLIN (1 << 0) /* There is data to read. */ +#define HG_POLLOUT (1 << 1) /* Writing now will not block. */ +#define HG_POLLERR (1 << 2) /* Error condition. */ +#define HG_POLLHUP (1 << 3) /* Hung up. */ +#define HG_POLLINTR (1 << 4) /* Interrupted. */ + +/*********************/ +/* Public Prototypes */ +/*********************/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Create a new poll set. + * + * \return Pointer to poll set or NULL in case of failure + */ +HG_UTIL_PUBLIC hg_poll_set_t *hg_poll_create(void); + +/** + * Destroy a poll set. + * + * \param poll_set [IN/OUT] pointer to poll set + * + * \return Non-negative on success or negative on failure + */ +HG_UTIL_PUBLIC int hg_poll_destroy(hg_poll_set_t *poll_set); + +/** + * Get a file descriptor from an existing poll set. + * + * \param poll_set [IN] pointer to poll set + * + * \return Non-negative on success or negative on failure + */ +HG_UTIL_PUBLIC int hg_poll_get_fd(hg_poll_set_t *poll_set); + +/** + * Add file descriptor to poll set. + * + * \param poll_set [IN] pointer to poll set + * \param fd [IN] file descriptor + * \param event [IN] pointer to event struct + * + * \return Non-negative on success or negative on failure + */ +HG_UTIL_PUBLIC int hg_poll_add(hg_poll_set_t *poll_set, int fd, struct hg_poll_event *event); + +/** + * Remove file descriptor from poll set. + * + * \param poll_set [IN] pointer to poll set + * \param fd [IN] file descriptor + * + * \return Non-negative on success or negative on failure + */ +HG_UTIL_PUBLIC int hg_poll_remove(hg_poll_set_t *poll_set, int fd); + +/** + * Wait on a poll set for timeout ms, and return at most max_events. + * + * \param poll_set [IN] pointer to poll set + * \param timeout [IN] timeout (in milliseconds) + * \param max_events [IN] max number of events + * \param events [IN/OUT] array of events to be returned + * \param actual_events [OUT] actual number of events returned + * + * \return Non-negative on success or negative on failure + */ +HG_UTIL_PUBLIC int hg_poll_wait(hg_poll_set_t *poll_set, unsigned int timeout, unsigned int max_events, + struct hg_poll_event events[], unsigned int *actual_events); + +#ifdef __cplusplus +} +#endif + +#endif /* MERCURY_POLL_H */ diff --git a/src/mercury/include/mercury_proc.h b/src/mercury/include/mercury_proc.h new file mode 100644 index 0000000..f142634 --- /dev/null +++ b/src/mercury/include/mercury_proc.h @@ -0,0 +1,769 @@ +/* + * Copyright (C) 2013-2020 Argonne National Laboratory, Department of Energy, + * UChicago Argonne, LLC and The HDF Group. + * All rights reserved. + * + * The full copyright notice, including terms governing use, modification, + * and redistribution, is contained in the COPYING file that can be + * found at the root of the source code distribution tree. + */ + +#ifndef MERCURY_PROC_H +#define MERCURY_PROC_H + +#include "mercury_types.h" + +#include <string.h> +#ifdef HG_HAS_XDR +#include <limits.h> +#include <rpc/types.h> +#include <rpc/xdr.h> +#ifdef __APPLE__ +#define xdr_int8_t xdr_char +#define xdr_uint8_t xdr_u_char +#define xdr_uint16_t xdr_u_int16_t +#define xdr_uint32_t xdr_u_int32_t +#define xdr_uint64_t xdr_u_int64_t +#endif +#define xdr_hg_int8_t xdr_int8_t +#define xdr_hg_uint8_t xdr_uint8_t +#define xdr_hg_int16_t xdr_int16_t +#define xdr_hg_uint16_t xdr_uint16_t +#define xdr_hg_int32_t xdr_int32_t +#define xdr_hg_uint32_t xdr_uint32_t +#define xdr_hg_int64_t xdr_int64_t +#define xdr_hg_uint64_t xdr_uint64_t +#endif + +/*************************************/ +/* Public Type and Struct Definition */ +/*************************************/ + +/** + * Hash methods available for proc. + */ +typedef enum { HG_CRC16, HG_CRC32, HG_CRC64, HG_NOHASH } hg_proc_hash_t; + +/*****************/ +/* Public Macros */ +/*****************/ + +/* Encode/decode version number into uint32 */ +#define HG_GET_MAJOR(value) ((value >> 24) & 0xFF) +#define HG_GET_MINOR(value) ((value >> 16) & 0xFF) +#define HG_GET_PATCH(value) (value & 0xFFFF) +#define HG_VERSION ((HG_VERSION_MAJOR << 24) | (HG_VERSION_MINOR << 16) | HG_VERSION_PATCH) + +/** + * Operation flags. + */ +#define HG_PROC_SM (1 << 0) +#define HG_PROC_BULK_EAGER (1 << 1) + +/* Branch predictor hints */ +#ifndef _WIN32 +#ifndef likely +#define likely(x) __builtin_expect(!!(x), 1) +#endif +#ifndef unlikely +#define unlikely(x) __builtin_expect(!!(x), 0) +#endif +#else +#ifndef likely +#define likely(x) (x) +#endif +#ifndef unlikely +#define unlikely(x) (x) +#endif +#endif + +/* Check whether size exceeds current proc size left */ +#ifdef HG_HAS_XDR +#define HG_PROC_CHECK_SIZE(proc, size, label, ret) \ + do { \ + if (unlikely(((struct hg_proc *)proc)->current_buf->size_left < size)) { \ + ret = HG_OVERFLOW; \ + goto label; \ + } \ + } while (0) +#else +#define HG_PROC_CHECK_SIZE(proc, size, label, ret) \ + do { \ + if (unlikely(((struct hg_proc *)proc)->current_buf->size_left < size)) { \ + ret = hg_proc_set_size(proc, hg_proc_get_size(proc) + size); \ + if (ret != HG_SUCCESS) \ + goto label; \ + } \ + } while (0) +#endif + +/* Encode type */ +#define HG_PROC_TYPE_ENCODE(proc, data, size) \ + memcpy(((struct hg_proc *)proc)->current_buf->buf_ptr, data, size) + +/* Decode type */ +#define HG_PROC_TYPE_DECODE(proc, data, size) \ + memcpy(data, ((struct hg_proc *)proc)->current_buf->buf_ptr, size) + +/* Update proc pointers */ +#define HG_PROC_UPDATE(proc, size) \ + do { \ + ((struct hg_proc *)proc)->current_buf->buf_ptr = \ + (char *)((struct hg_proc *)proc)->current_buf->buf_ptr + size; \ + ((struct hg_proc *)proc)->current_buf->size_left -= size; \ + } while (0) + +/* Update checksum */ +#ifdef HG_HAS_CHECKSUMS +#define HG_PROC_CHECKSUM_UPDATE(proc, data, size) hg_proc_checksum_update(proc, data, size) +#else +#define HG_PROC_CHECKSUM_UPDATE(proc, data, size) +#endif + +/* Base proc function */ +#ifdef HG_HAS_XDR +#define HG_PROC_TYPE(proc, type, data, label, ret) \ + do { \ + HG_PROC_CHECK_SIZE(proc, sizeof(type), label, ret); \ + \ + if (xdr_##type(hg_proc_get_xdr_ptr(proc), data) == 0) { \ + ret = HG_PROTOCOL_ERROR; \ + goto label; \ + } \ + \ + HG_PROC_UPDATE(proc, sizeof(type)); \ + HG_PROC_CHECKSUM_UPDATE(proc, data, sizeof(type)); \ + } while (0) +#else +#define HG_PROC_TYPE(proc, type, data, label, ret) \ + do { \ + /* Do nothing in HG_FREE for basic types */ \ + if (hg_proc_get_op(proc) == HG_FREE) \ + goto label; \ + \ + /* If not enough space allocate extra space if encoding or just */ \ + /* get extra buffer if decoding */ \ + HG_PROC_CHECK_SIZE(proc, sizeof(type), label, ret); \ + \ + /* Encode, decode type */ \ + if (hg_proc_get_op(proc) == HG_ENCODE) \ + HG_PROC_TYPE_ENCODE(proc, data, sizeof(type)); \ + else \ + HG_PROC_TYPE_DECODE(proc, data, sizeof(type)); \ + \ + /* Update proc pointers etc */ \ + HG_PROC_UPDATE(proc, sizeof(type)); \ + HG_PROC_CHECKSUM_UPDATE(proc, data, sizeof(type)); \ + } while (0) +#endif + +/* Base proc function */ +#ifdef HG_HAS_XDR +#define HG_PROC_BYTES(proc, data, size, label, ret) \ + do { \ + HG_PROC_CHECK_SIZE(proc, size, label, ret); \ + \ + if (xdr_bytes(hg_proc_get_xdr_ptr(proc), (char **)&data, (u_int *)&size, UINT_MAX) == 0) { \ + ret = HG_PROTOCOL_ERROR; \ + goto label; \ + } \ + \ + HG_PROC_UPDATE(proc, size); \ + HG_PROC_CHECKSUM_UPDATE(proc, data, size); \ + } while (0) +#else +#define HG_PROC_BYTES(proc, data, size, label, ret) \ + do { \ + /* Do nothing in HG_FREE for basic types */ \ + if (hg_proc_get_op(proc) == HG_FREE) \ + goto label; \ + \ + /* If not enough space allocate extra space if encoding or just */ \ + /* get extra buffer if decoding */ \ + HG_PROC_CHECK_SIZE(proc, size, label, ret); \ + \ + /* Encode, decode type */ \ + if (hg_proc_get_op(proc) == HG_ENCODE) \ + HG_PROC_TYPE_ENCODE(proc, data, size); \ + else \ + HG_PROC_TYPE_DECODE(proc, data, size); \ + \ + /* Update proc pointers etc */ \ + HG_PROC_UPDATE(proc, size); \ + HG_PROC_CHECKSUM_UPDATE(proc, data, size); \ + } while (0) +#endif + +/*********************/ +/* Public Prototypes */ +/*********************/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Create a new encoding/decoding processor. + * + * \param hg_class [IN] HG class + * \param hash [IN] hash method used for computing checksum + * (if NULL, checksum is not computed) + * hash method: HG_CRC16, HG_CRC64, HG_NOHASH + * \param proc [OUT] pointer to abstract processor object + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t hg_proc_create(hg_class_t *hg_class, hg_proc_hash_t hash, hg_proc_t *proc); + +/** + * Create a new encoding/decoding processor. + * + * \param hg_class [IN] HG class + * \param buf [IN] pointer to buffer that will be used for + * serialization/deserialization + * \param buf_size [IN] buffer size + * \param op [IN] operation type: HG_ENCODE / HG_DECODE / + * HG_FREE \param hash [IN] hash method used for computing + * checksum (if NULL, checksum is not computed) hash method: HG_CRC16, + * HG_CRC64, HG_NOHASH \param proc [OUT] pointer to abstract + * processor object + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t hg_proc_create_set(hg_class_t *hg_class, void *buf, hg_size_t buf_size, hg_proc_op_t op, + hg_proc_hash_t hash, hg_proc_t *proc); + +/** + * Free the processor. + * + * \param proc [IN/OUT] abstract processor object + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t hg_proc_free(hg_proc_t proc); + +/** + * Reset the processor. + * + * \param proc [IN/OUT] abstract processor object + * \param buf [IN] pointer to buffer that will be used for + * serialization/deserialization + * \param buf_size [IN] buffer size + * \param op [IN] operation type: HG_ENCODE / HG_DECODE / + * HG_FREE + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t hg_proc_reset(hg_proc_t proc, void *buf, hg_size_t buf_size, hg_proc_op_t op); + +/** + * Get the HG class associated to the processor. + * + * \param proc [IN] abstract processor object + * + * \return HG class + */ +static HG_INLINE hg_class_t *hg_proc_get_class(hg_proc_t proc); + +/** + * Get the operation type associated to the processor. + * + * \param proc [IN] abstract processor object + * + * \return Operation type + */ +static HG_INLINE hg_proc_op_t hg_proc_get_op(hg_proc_t proc); + +/** + * Set flags to be associated with the processor. + * Flags are reset after a call to hg_proc_reset(). + * + * \param proc [IN] abstract processor object + * + * \return Non-negative flag value + */ +static HG_INLINE void hg_proc_set_flags(hg_proc_t proc, hg_uint8_t flags); + +/** + * Get the flags associated to the processor. + * + * \param proc [IN] abstract processor object + * + * \return Non-negative flag value + */ +static HG_INLINE hg_uint8_t hg_proc_get_flags(hg_proc_t proc); + +/** + * Get buffer size available for processing. + * + * \param proc [IN] abstract processor object + * + * \return Non-negative size value + */ +static HG_INLINE hg_size_t hg_proc_get_size(hg_proc_t proc); + +/** + * Get amount of buffer space that has actually been consumed + * + * \param proc [IN] abstract processor object + * + * \return Non-negative size value + */ +static HG_INLINE hg_size_t hg_proc_get_size_used(hg_proc_t proc); + +/** + * Request a new buffer size. This will modify the size of the buffer + * attached to the processor or create an extra processing buffer. + * + * \param proc [IN/OUT] abstract processor object + * \param buf_size [IN] buffer size + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t hg_proc_set_size(hg_proc_t proc, hg_size_t buf_size); + +/** + * Get size left for processing. + * + * \param proc [IN] abstract processor object + * + * \return Non-negative size value + */ +static HG_INLINE hg_size_t hg_proc_get_size_left(hg_proc_t proc); + +/** + * Get pointer to current buffer. Will reserve data_size for manual + * encoding. + * + * \param proc [IN] abstract processor object + * \param data_size [IN] data size + * + * \return Buffer pointer + */ +HG_PUBLIC void *hg_proc_save_ptr(hg_proc_t proc, hg_size_t data_size); + +/** + * Restore pointer from current buffer. + * + * \param proc [IN] abstract processor object + * \param data [IN] pointer to data + * \param data_size [IN] data size + * + * \return Buffer pointer + */ +HG_PUBLIC hg_return_t hg_proc_restore_ptr(hg_proc_t proc, void *data, hg_size_t data_size); + +#ifdef HG_HAS_XDR +/** + * Get pointer to current XDR stream (for manual encoding). + * + * \param proc [IN] abstract processor object + * + * \return XDR stream pointer + */ +static HG_INLINE XDR *hg_proc_get_xdr_ptr(hg_proc_t proc); +#endif + +/** + * Get eventual extra buffer used by processor. + * + * \param proc [IN] abstract processor object + * + * \return Pointer to buffer or NULL if no extra buffer has been used + */ +static HG_INLINE void *hg_proc_get_extra_buf(hg_proc_t proc); + +/** + * Get eventual size of the extra buffer used by processor. + * + * \param proc [IN] abstract processor object + * + * \return Size of buffer or 0 if no extra buffer has been used + */ +static HG_INLINE hg_size_t hg_proc_get_extra_size(hg_proc_t proc); + +/** + * Set extra buffer to mine (if other calls mine, buffer is no longer freed + * after hg_proc_free()) + * + * \param proc [IN] abstract processor object + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t hg_proc_set_extra_buf_is_mine(hg_proc_t proc, hg_bool_t mine); + +/** + * Flush the proc after data has been encoded or decoded and finalize + * internal checksum if checksum of data processed was initially requested. + * + * \param proc [IN] abstract processor object + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t hg_proc_flush(hg_proc_t proc); + +#ifdef HG_HAS_CHECKSUMS +/** + * Retrieve internal proc checksum hash. + * \remark Must be used after hg_proc_flush() has been called so that the + * internally computed checksum is in a finalized state. + * + * \param proc [IN/OUT] abstract processor object + * \param hash [IN/OUT] pointer to hash + * \param hash_size [IN] hash size + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t hg_proc_checksum_get(hg_proc_t proc, void *hash, hg_size_t hash_size); + +/** + * Verify that the hash passed matches the internal proc checksum. + * \remark Must be used after hg_proc_flush() has been called so that the + * internally computed checksum is in a finalized state. + * + * \param proc [IN/OUT] abstract processor object + * \param hash [IN] pointer to hash + * \param hash_size [IN] hash size + * + * \return HG_SUCCESS if matches or corresponding HG error code + */ +HG_PUBLIC hg_return_t hg_proc_checksum_verify(hg_proc_t proc, const void *hash, hg_size_t hash_size); +#endif + +/** + * Generic processing routine. + * + * \param proc [IN/OUT] abstract processor object + * \param data [IN/OUT] pointer to data + * + * \return HG_SUCCESS or corresponding HG error code + */ +static HG_INLINE hg_return_t hg_proc_hg_int8_t(hg_proc_t proc, void *data); + +/** + * Generic processing routine. + * + * \param proc [IN/OUT] abstract processor object + * \param data [IN/OUT] pointer to data + * + * \return HG_SUCCESS or corresponding HG error code + */ +static HG_INLINE hg_return_t hg_proc_hg_uint8_t(hg_proc_t proc, void *data); + +/** + * Generic processing routine. + * + * \param proc [IN/OUT] abstract processor object + * \param data [IN/OUT] pointer to data + * + * \return HG_SUCCESS or corresponding HG error code + */ +static HG_INLINE hg_return_t hg_proc_hg_int16_t(hg_proc_t proc, void *data); + +/** + * Generic processing routine. + * + * \param proc [IN/OUT] abstract processor object + * \param data [IN/OUT] pointer to data + * + * \return HG_SUCCESS or corresponding HG error code + */ +static HG_INLINE hg_return_t hg_proc_hg_uint16_t(hg_proc_t proc, void *data); + +/** + * Generic processing routine. + * + * \param proc [IN/OUT] abstract processor object + * \param data [IN/OUT] pointer to data + * + * \return HG_SUCCESS or corresponding HG error code + */ +static HG_INLINE hg_return_t hg_proc_hg_int32_t(hg_proc_t proc, void *data); + +/** + * Generic processing routine. + * + * \param proc [IN/OUT] abstract processor object + * \param data [IN/OUT] pointer to data + * + * \return HG_SUCCESS or corresponding HG error code + */ +static HG_INLINE hg_return_t hg_proc_hg_uint32_t(hg_proc_t proc, void *data); + +/** + * Generic processing routine. + * + * \param proc [IN/OUT] abstract processor object + * \param data [IN/OUT] pointer to data + * + * \return HG_SUCCESS or corresponding HG error code + */ +static HG_INLINE hg_return_t hg_proc_hg_int64_t(hg_proc_t proc, void *data); + +/** + * Generic processing routine. + * + * \param proc [IN/OUT] abstract processor object + * \param data [IN/OUT] pointer to data + * + * \return HG_SUCCESS or corresponding HG error code + */ +static HG_INLINE hg_return_t hg_proc_hg_uint64_t(hg_proc_t proc, void *data); + +/* Note: float types are not supported but can be built on top of the existing + * proc routines; encoding floats using XDR could modify checksum */ + +/** + * Generic processing routine for encoding stream of bytes. + * + * \param proc [IN/OUT] abstract processor object + * \param data [IN/OUT] pointer to data + * \param data_size [IN] data size + * + * \return HG_SUCCESS or corresponding HG error code + */ +static HG_INLINE hg_return_t hg_proc_bytes(hg_proc_t proc, void *data, hg_size_t data_size); + +/** + * For convenience map stdint types to hg types + */ +#define hg_proc_int8_t hg_proc_hg_int8_t +#define hg_proc_uint8_t hg_proc_hg_uint8_t +#define hg_proc_int16_t hg_proc_hg_int16_t +#define hg_proc_uint16_t hg_proc_hg_uint16_t +#define hg_proc_int32_t hg_proc_hg_int32_t +#define hg_proc_uint32_t hg_proc_hg_uint32_t +#define hg_proc_int64_t hg_proc_hg_int64_t +#define hg_proc_uint64_t hg_proc_hg_uint64_t + +/* Map mercury common types */ +#define hg_proc_hg_bool_t hg_proc_hg_uint8_t +#define hg_proc_hg_ptr_t hg_proc_hg_uint64_t +#define hg_proc_hg_size_t hg_proc_hg_uint64_t +#define hg_proc_hg_id_t hg_proc_hg_uint32_t + +/* Map hg_proc_raw/hg_proc_memcpy to hg_proc_bytes */ +#define hg_proc_memcpy hg_proc_raw +#define hg_proc_raw hg_proc_bytes + +/* Update checksum */ +#ifdef HG_HAS_CHECKSUMS +HG_PUBLIC void hg_proc_checksum_update(hg_proc_t proc, void *data, hg_size_t data_size); +#endif + +/************************************/ +/* Local Type and Struct Definition */ +/************************************/ + +/* HG proc buf */ +struct hg_proc_buf { + void * buf; /* Pointer to allocated buffer */ + void * buf_ptr; /* Pointer to current position */ + hg_size_t size; /* Total buffer size */ + hg_size_t size_left; /* Available size for user */ + hg_bool_t is_mine; +#ifdef HG_HAS_XDR + XDR xdr; +#endif +}; + +/* HG proc */ +struct hg_proc { + struct hg_proc_buf proc_buf; + struct hg_proc_buf extra_buf; + hg_class_t * hg_class; /* HG class */ + struct hg_proc_buf *current_buf; +#ifdef HG_HAS_CHECKSUMS + void * checksum; /* Checksum */ + void * checksum_hash; /* Base checksum buf */ + size_t checksum_size; /* Checksum size */ +#endif + hg_proc_op_t op; + hg_uint8_t flags; +}; + +/*---------------------------------------------------------------------------*/ +static HG_INLINE hg_class_t * + hg_proc_get_class(hg_proc_t proc) +{ + return ((struct hg_proc *)proc)->hg_class; +} + +/*---------------------------------------------------------------------------*/ +static HG_INLINE hg_proc_op_t +hg_proc_get_op(hg_proc_t proc) +{ + return ((struct hg_proc *)proc)->op; +} + +/*---------------------------------------------------------------------------*/ +static HG_INLINE void +hg_proc_set_flags(hg_proc_t proc, hg_uint8_t flags) +{ + ((struct hg_proc *)proc)->flags = flags; +} + +/*---------------------------------------------------------------------------*/ +static HG_INLINE hg_uint8_t +hg_proc_get_flags(hg_proc_t proc) +{ + return ((struct hg_proc *)proc)->flags; +} + +/*---------------------------------------------------------------------------*/ +static HG_INLINE hg_size_t +hg_proc_get_size(hg_proc_t proc) +{ + return ((struct hg_proc *)proc)->proc_buf.size + ((struct hg_proc *)proc)->extra_buf.size; +} + +/*---------------------------------------------------------------------------*/ +static HG_INLINE hg_size_t +hg_proc_get_size_used(hg_proc_t proc) +{ + return ((struct hg_proc *)proc)->current_buf->size - ((struct hg_proc *)proc)->current_buf->size_left; +} + +/*---------------------------------------------------------------------------*/ +static HG_INLINE hg_size_t +hg_proc_get_size_left(hg_proc_t proc) +{ + return ((struct hg_proc *)proc)->current_buf->size_left; +} + +/*---------------------------------------------------------------------------*/ +#ifdef HG_HAS_XDR +static HG_INLINE XDR * + hg_proc_get_xdr_ptr(hg_proc_t proc) +{ + return &((struct hg_proc *)proc)->current_buf->xdr; +} +#endif + +/*---------------------------------------------------------------------------*/ +static HG_INLINE void * +hg_proc_get_extra_buf(hg_proc_t proc) +{ + return ((struct hg_proc *)proc)->extra_buf.buf; +} + +/*---------------------------------------------------------------------------*/ +static HG_INLINE hg_size_t +hg_proc_get_extra_size(hg_proc_t proc) +{ + return ((struct hg_proc *)proc)->extra_buf.size; +} + +/*---------------------------------------------------------------------------*/ +static HG_INLINE hg_return_t +hg_proc_hg_int8_t(hg_proc_t proc, void *data) +{ + hg_return_t ret = HG_SUCCESS; + + HG_PROC_TYPE(proc, hg_int8_t, data, done, ret); + +done: + return ret; +} + +/*---------------------------------------------------------------------------*/ +static HG_INLINE hg_return_t +hg_proc_hg_uint8_t(hg_proc_t proc, void *data) +{ + hg_return_t ret = HG_SUCCESS; + + HG_PROC_TYPE(proc, hg_uint8_t, data, done, ret); + +done: + return ret; +} + +/*---------------------------------------------------------------------------*/ +static HG_INLINE hg_return_t +hg_proc_hg_int16_t(hg_proc_t proc, void *data) +{ + hg_return_t ret = HG_SUCCESS; + + HG_PROC_TYPE(proc, hg_int16_t, data, done, ret); + +done: + return ret; +} + +/*---------------------------------------------------------------------------*/ +static HG_INLINE hg_return_t +hg_proc_hg_uint16_t(hg_proc_t proc, void *data) +{ + hg_return_t ret = HG_SUCCESS; + + HG_PROC_TYPE(proc, hg_uint16_t, data, done, ret); + +done: + return ret; +} + +/*---------------------------------------------------------------------------*/ +static HG_INLINE hg_return_t +hg_proc_hg_int32_t(hg_proc_t proc, void *data) +{ + hg_return_t ret = HG_SUCCESS; + + HG_PROC_TYPE(proc, hg_int32_t, data, done, ret); + +done: + return ret; +} + +/*---------------------------------------------------------------------------*/ +static HG_INLINE hg_return_t +hg_proc_hg_uint32_t(hg_proc_t proc, void *data) +{ + hg_return_t ret = HG_SUCCESS; + + HG_PROC_TYPE(proc, hg_uint32_t, data, done, ret); + +done: + return ret; +} + +/*---------------------------------------------------------------------------*/ +static HG_INLINE hg_return_t +hg_proc_hg_int64_t(hg_proc_t proc, void *data) +{ + hg_return_t ret = HG_SUCCESS; + + HG_PROC_TYPE(proc, hg_int64_t, data, done, ret); + +done: + return ret; +} + +/*---------------------------------------------------------------------------*/ +static HG_INLINE hg_return_t +hg_proc_hg_uint64_t(hg_proc_t proc, void *data) +{ + hg_return_t ret = HG_SUCCESS; + + HG_PROC_TYPE(proc, hg_uint64_t, data, done, ret); + +done: + return ret; +} + +/*---------------------------------------------------------------------------*/ +static HG_INLINE hg_return_t +hg_proc_bytes(hg_proc_t proc, void *data, hg_size_t data_size) +{ + hg_return_t ret = HG_SUCCESS; + + HG_PROC_BYTES(proc, data, data_size, done, ret); + +done: + return ret; +} + +#ifdef __cplusplus +} +#endif + +#endif /* MERCURY_PROC_H */ diff --git a/src/mercury/include/mercury_proc_bulk.h b/src/mercury/include/mercury_proc_bulk.h new file mode 100644 index 0000000..f89face --- /dev/null +++ b/src/mercury/include/mercury_proc_bulk.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2013-2020 Argonne National Laboratory, Department of Energy, + * UChicago Argonne, LLC and The HDF Group. + * All rights reserved. + * + * The full copyright notice, including terms governing use, modification, + * and redistribution, is contained in the COPYING file that can be + * found at the root of the source code distribution tree. + */ + +#ifndef MERCURY_PROC_BULK_H +#define MERCURY_PROC_BULK_H + +#include "mercury_proc.h" + +/*************************************/ +/* Public Type and Struct Definition */ +/*************************************/ + +/*****************/ +/* Public Macros */ +/*****************/ + +/*********************/ +/* Public Prototypes */ +/*********************/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Generic processing routine. + * + * \param proc [IN/OUT] abstract processor object + * \param handle [IN/OUT] pointer to bulk handle + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t hg_proc_hg_bulk_t(hg_proc_t proc, void *data); + +#ifdef __cplusplus +} +#endif + +#endif /* MERCURY_PROC_BULK_H */ diff --git a/src/mercury/include/mercury_proc_string.h b/src/mercury/include/mercury_proc_string.h new file mode 100644 index 0000000..764eb20 --- /dev/null +++ b/src/mercury/include/mercury_proc_string.h @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2013-2020 Argonne National Laboratory, Department of Energy, + * UChicago Argonne, LLC and The HDF Group. + * All rights reserved. + * + * The full copyright notice, including terms governing use, modification, + * and redistribution, is contained in the COPYING file that can be + * found at the root of the source code distribution tree. + */ + +#ifndef MERCURY_PROC_STRING_H +#define MERCURY_PROC_STRING_H + +#include "mercury_proc.h" +#include "mercury_string_object.h" + +/*************************************/ +/* Public Type and Struct Definition */ +/*************************************/ + +typedef const char *hg_const_string_t; +typedef char * hg_string_t; + +/*****************/ +/* Public Macros */ +/*****************/ + +/*********************/ +/* Public Prototypes */ +/*********************/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Generic processing routine. + * + * \param proc [IN/OUT] abstract processor object + * \param data [IN/OUT] pointer to data + * + * \return HG_SUCCESS or corresponding HG error code + */ +static HG_INLINE hg_return_t hg_proc_hg_const_string_t(hg_proc_t proc, void *data); + +/** + * Generic processing routine. + * + * \param proc [IN/OUT] abstract processor object + * \param data [IN/OUT] pointer to data + * + * \return HG_SUCCESS or corresponding HG error code + */ +static HG_INLINE hg_return_t hg_proc_hg_string_t(hg_proc_t proc, void *data); + +/** + * Generic processing routine. + * + * \param proc [IN/OUT] abstract processor object + * \param string [IN/OUT] pointer to string + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t hg_proc_hg_string_object_t(hg_proc_t proc, void *string); + +/************************************/ +/* Local Type and Struct Definition */ +/************************************/ + +/*---------------------------------------------------------------------------*/ +static HG_INLINE hg_return_t +hg_proc_hg_const_string_t(hg_proc_t proc, void *data) +{ + hg_string_object_t string; + hg_const_string_t *strdata = (hg_const_string_t *)data; + hg_return_t ret = HG_SUCCESS; + + switch (hg_proc_get_op(proc)) { + case HG_ENCODE: + hg_string_object_init_const_char(&string, *strdata, 0); + ret = hg_proc_hg_string_object_t(proc, &string); + if (ret != HG_SUCCESS) + goto done; + hg_string_object_free(&string); + break; + case HG_DECODE: + ret = hg_proc_hg_string_object_t(proc, &string); + if (ret != HG_SUCCESS) + goto done; + *strdata = hg_string_object_swap(&string, 0); + hg_string_object_free(&string); + break; + case HG_FREE: + hg_string_object_init_const_char(&string, *strdata, 1); + ret = hg_proc_hg_string_object_t(proc, &string); + if (ret != HG_SUCCESS) + goto done; + break; + default: + break; + } + +done: + return ret; +} + +/*---------------------------------------------------------------------------*/ +static HG_INLINE hg_return_t +hg_proc_hg_string_t(hg_proc_t proc, void *data) +{ + hg_string_object_t string; + hg_string_t * strdata = (hg_string_t *)data; + hg_return_t ret = HG_SUCCESS; + + switch (hg_proc_get_op(proc)) { + case HG_ENCODE: + hg_string_object_init_char(&string, *strdata, 0); + ret = hg_proc_hg_string_object_t(proc, &string); + if (ret != HG_SUCCESS) + goto done; + hg_string_object_free(&string); + break; + case HG_DECODE: + ret = hg_proc_hg_string_object_t(proc, &string); + if (ret != HG_SUCCESS) + goto done; + *strdata = hg_string_object_swap(&string, 0); + hg_string_object_free(&string); + break; + case HG_FREE: + hg_string_object_init_char(&string, *strdata, 1); + ret = hg_proc_hg_string_object_t(proc, &string); + if (ret != HG_SUCCESS) + goto done; + break; + default: + break; + } + +done: + return ret; +} + +#ifdef __cplusplus +} +#endif + +#endif /* MERCURY_PROC_STRING_H */ diff --git a/src/mercury/include/mercury_queue.h b/src/mercury/include/mercury_queue.h new file mode 100644 index 0000000..116a209 --- /dev/null +++ b/src/mercury/include/mercury_queue.h @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2013-2020 Argonne National Laboratory, Department of Energy, + * UChicago Argonne, LLC and The HDF Group. + * All rights reserved. + * + * The full copyright notice, including terms governing use, modification, + * and redistribution, is contained in the COPYING file that can be + * found at the root of the source code distribution tree. + */ + +/* Code below is derived from sys/queue.h which follows the below notice: + * + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)queue.h 8.5 (Berkeley) 8/20/94 + */ + +#ifndef MERCURY_QUEUE_H +#define MERCURY_QUEUE_H + +#define HG_QUEUE_HEAD_INITIALIZER(name) \ + { \ + NULL, &(name).head \ + } + +#define HG_QUEUE_HEAD_INIT(struct_head_name, var_name) \ + struct struct_head_name var_name = HG_QUEUE_HEAD_INITIALIZER(var_name) + +#define HG_QUEUE_HEAD_DECL(struct_head_name, struct_entry_name) \ + struct struct_head_name { \ + struct struct_entry_name * head; \ + struct struct_entry_name **tail; \ + } + +#define HG_QUEUE_HEAD(struct_entry_name) \ + struct { \ + struct struct_entry_name * head; \ + struct struct_entry_name **tail; \ + } + +#define HG_QUEUE_ENTRY(struct_entry_name) \ + struct { \ + struct struct_entry_name *next; \ + } + +#define HG_QUEUE_INIT(head_ptr) \ + do { \ + (head_ptr)->head = NULL; \ + (head_ptr)->tail = &(head_ptr)->head; \ + } while (/*CONSTCOND*/ 0) + +#define HG_QUEUE_IS_EMPTY(head_ptr) ((head_ptr)->head == NULL) + +#define HG_QUEUE_FIRST(head_ptr) ((head_ptr)->head) + +#define HG_QUEUE_NEXT(entry_ptr, entry_field_name) ((entry_ptr)->entry_field_name.next) + +#define HG_QUEUE_PUSH_TAIL(head_ptr, entry_ptr, entry_field_name) \ + do { \ + (entry_ptr)->entry_field_name.next = NULL; \ + *(head_ptr)->tail = (entry_ptr); \ + (head_ptr)->tail = &(entry_ptr)->entry_field_name.next; \ + } while (/*CONSTCOND*/ 0) + +/* TODO would be nice to not have any condition */ +#define HG_QUEUE_POP_HEAD(head_ptr, entry_field_name) \ + do { \ + if ((head_ptr)->head && ((head_ptr)->head = (head_ptr)->head->entry_field_name.next) == NULL) \ + (head_ptr)->tail = &(head_ptr)->head; \ + } while (/*CONSTCOND*/ 0) + +#define HG_QUEUE_FOREACH(var, head_ptr, entry_field_name) \ + for ((var) = ((head_ptr)->head); (var); (var) = ((var)->entry_field_name.next)) + +/** + * Avoid using those for performance reasons or use mercury_list.h instead + */ + +#define HG_QUEUE_REMOVE(head_ptr, entry_ptr, type, entry_field_name) \ + do { \ + if ((head_ptr)->head == (entry_ptr)) { \ + HG_QUEUE_POP_HEAD((head_ptr), entry_field_name); \ + } \ + else { \ + struct type *curelm = (head_ptr)->head; \ + while (curelm->entry_field_name.next != (entry_ptr)) \ + curelm = curelm->entry_field_name.next; \ + if ((curelm->entry_field_name.next = curelm->entry_field_name.next->entry_field_name.next) == \ + NULL) \ + (head_ptr)->tail = &(curelm)->entry_field_name.next; \ + } \ + } while (/*CONSTCOND*/ 0) + +#endif /* MERCURY_QUEUE_H */ diff --git a/src/mercury/include/mercury_request.h b/src/mercury/include/mercury_request.h new file mode 100644 index 0000000..4d7fdf8 --- /dev/null +++ b/src/mercury/include/mercury_request.h @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2013-2020 Argonne National Laboratory, Department of Energy, + * UChicago Argonne, LLC and The HDF Group. + * All rights reserved. + * + * The full copyright notice, including terms governing use, modification, + * and redistribution, is contained in the COPYING file that can be + * found at the root of the source code distribution tree. + */ + +#ifndef MERCURY_REQUEST_H +#define MERCURY_REQUEST_H + +#include "mercury_util_config.h" + +#include "mercury_atomic.h" + +/** + * Purpose: define a request emulation library on top of the callback model + * that uses progress/trigger functions. Note that this library can not be + * safely used within RPCs in most cases - calling hg_request_wait causes + * deadlock when the caller function was triggered by HG_Trigger + * (or HG_Bulk_trigger). + */ + +typedef struct hg_request_class hg_request_class_t; /* Opaque request class */ +typedef struct hg_request hg_request_t; /* Opaque request object */ + +struct hg_request { + hg_request_class_t *request_class; + void * data; + hg_atomic_int32_t completed; +}; + +/** + * Progress callback, arg can be used to pass extra parameters required by + * underlying API. + * + * \param timeout [IN] timeout (in milliseconds) + * \param arg [IN] pointer to data passed to callback + * + * \return HG_UTIL_SUCCESS if any completion has occurred / error code otherwise + */ +typedef int (*hg_request_progress_func_t)(unsigned int timeout, void *arg); + +/** + * Trigger callback, arg can be used to pass extra parameters required by + * underlying API. + * + * \param timeout [IN] timeout (in milliseconds) + * \param flag [OUT] 1 if callback has been triggered, 0 otherwise + * \param arg [IN] pointer to data passed to callback + * + * \return HG_UTIL_SUCCESS or corresponding error code + */ +typedef int (*hg_request_trigger_func_t)(unsigned int timeout, unsigned int *flag, void *arg); + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Initialize the request class with the specific progress/trigger functions + * that will be called on hg_request_wait(). + * arg can be used to pass extra parameters required by underlying API. + * + * \param progress [IN] progress function + * \param trigger [IN] trigger function + * \param arg [IN] pointer to data passed to callback + * + * \return Pointer to request class or NULL in case of failure + */ +HG_UTIL_PUBLIC hg_request_class_t *hg_request_init(hg_request_progress_func_t progress, + hg_request_trigger_func_t trigger, void *arg); + +/** + * Finalize the request class. User args that were passed through + * hg_request_init() can be retrieved through the \a arg parameter. + * + * \param request_class [IN] pointer to request class + * \param arg [IN/OUT] pointer to init args + */ +HG_UTIL_PUBLIC void hg_request_finalize(hg_request_class_t *request_class, void **arg); + +/** + * Create a new request from a specified request class. The progress function + * explicitly makes progress and may insert the completed operation into a + * completion queue. The operation gets triggered after a call to the trigger + * function. + * + * \param request_class [IN] pointer to request class + * + * \return Pointer to request or NULL in case of failure + */ +HG_UTIL_PUBLIC hg_request_t *hg_request_create(hg_request_class_t *request_class); + +/** + * Destroy the request, freeing the resources. + * + * \param request [IN/OUT] pointer to request + */ +HG_UTIL_PUBLIC void hg_request_destroy(hg_request_t *request); + +/** + * Reset an existing request so that it can be safely re-used. + * + * \param request [IN/OUT] pointer to request + */ +static HG_UTIL_INLINE void hg_request_reset(hg_request_t *request); + +/** + * Mark the request as completed. (most likely called by a callback triggered + * after a call to trigger) + * + * \param request [IN/OUT] pointer to request + */ +static HG_UTIL_INLINE void hg_request_complete(hg_request_t *request); + +/** + * Wait timeout ms for the specified request to complete. + * + * \param request [IN/OUT] pointer to request + * \param timeout [IN] timeout (in milliseconds) + * \param flag [OUT] 1 if request has completed, 0 otherwise + * + * \return Non-negative on success or negative on failure + */ +HG_UTIL_PUBLIC int hg_request_wait(hg_request_t *request, unsigned int timeout, unsigned int *flag); + +/** + * Wait timeout ms for all the specified request to complete. + * + * \param count [IN] number of requests + * \param request [IN/OUT] arrays of requests + * \param timeout [IN] timeout (in milliseconds) + * \param flag [OUT] 1 if all requests have completed, 0 otherwise + * + * \return Non-negative on success or negative on failure + */ +static HG_UTIL_INLINE int hg_request_waitall(int count, hg_request_t *request[], unsigned int timeout, + unsigned int *flag); + +/** + * Attach user data to a specified request. + * + * \param request [IN/OUT] pointer to request + * \param data [IN] pointer to data + */ +static HG_UTIL_INLINE void hg_request_set_data(hg_request_t *request, void *data); + +/** + * Get user data from a specified request. + * + * \param request [IN/OUT] pointer to request + * + * \return Pointer to data or NULL if nothing was attached by user + */ +static HG_UTIL_INLINE void *hg_request_get_data(hg_request_t *request); + +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE void +hg_request_reset(hg_request_t *request) +{ + hg_atomic_set32(&request->completed, HG_UTIL_FALSE); +} + +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE void +hg_request_complete(hg_request_t *request) +{ + hg_atomic_set32(&request->completed, HG_UTIL_TRUE); +} + +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE int +hg_request_waitall(int count, hg_request_t *request[], unsigned int timeout, unsigned int *flag) +{ + int i; + + for (i = 0; i < count; i++) + hg_request_wait(request[i], timeout, flag); + + return HG_UTIL_SUCCESS; +} + +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE void +hg_request_set_data(hg_request_t *request, void *data) +{ + request->data = data; +} + +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE void * +hg_request_get_data(hg_request_t *request) +{ + return request->data; +} + +#ifdef __cplusplus +} +#endif + +#endif /* MERCURY_REQUEST_H */ diff --git a/src/mercury/include/mercury_string_object.h b/src/mercury/include/mercury_string_object.h new file mode 100644 index 0000000..5a7492a --- /dev/null +++ b/src/mercury/include/mercury_string_object.h @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2013-2020 Argonne National Laboratory, Department of Energy, + * UChicago Argonne, LLC and The HDF Group. + * All rights reserved. + * + * The full copyright notice, including terms governing use, modification, + * and redistribution, is contained in the COPYING file that can be + * found at the root of the source code distribution tree. + */ + +#ifndef MERCURY_STRING_OBJECT_H +#define MERCURY_STRING_OBJECT_H + +#include "mercury_types.h" + +/*************************************/ +/* Public Type and Struct Definition */ +/*************************************/ + +typedef struct hg_string_object { + char * data; + hg_bool_t is_const; + hg_bool_t is_owned; +} hg_string_object_t; + +/*****************/ +/* Public Macros */ +/*****************/ + +/*********************/ +/* Public Prototypes */ +/*********************/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Initialize a string object. + * + * \param string [OUT] pointer to string structure + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t hg_string_object_init(hg_string_object_t *string); + +/** + * Initialize a string object from the string pointed to by s. + * + * \param string [OUT] pointer to string structure + * \param s [IN] pointer to string + * \param is_owned [IN] boolean + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t hg_string_object_init_char(hg_string_object_t *string, char *s, hg_bool_t is_owned); + +/** + * Initialize a string object from the const string pointed to by s. + * + * \param string [OUT] pointer to string structure + * \param s [IN] pointer to string + * \param is_owned [IN] boolean + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t hg_string_object_init_const_char(hg_string_object_t *string, const char *s, + hg_bool_t is_owned); + +/** + * Free a string object. + * + * \param string [IN/OUT] pointer to string structure + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t hg_string_object_free(hg_string_object_t *string); + +/** + * Duplicate a string object. + * + * \param string [IN] pointer to string structure + * \param new_string [OUT] pointer to string structure + * + * \return HG_SUCCESS or corresponding HG error code + */ +HG_PUBLIC hg_return_t hg_string_object_dup(hg_string_object_t string, hg_string_object_t *new_string); + +/** + * Exchange the content of the string structure by the content of s. + * + * \param string [IN/OUT] pointer to string structure + * + * \return Pointer to string contained by string before the swap + */ +HG_PUBLIC char *hg_string_object_swap(hg_string_object_t *string, char *s); + +#ifdef __cplusplus +} +#endif + +#endif /* MERCURY_STRING_OBJECT_H */ diff --git a/src/mercury/include/mercury_thread.h b/src/mercury/include/mercury_thread.h new file mode 100644 index 0000000..3317c41 --- /dev/null +++ b/src/mercury/include/mercury_thread.h @@ -0,0 +1,228 @@ +/* + * Copyright (C) 2013-2020 Argonne National Laboratory, Department of Energy, + * UChicago Argonne, LLC and The HDF Group. + * All rights reserved. + * + * The full copyright notice, including terms governing use, modification, + * and redistribution, is contained in the COPYING file that can be + * found at the root of the source code distribution tree. + */ + +#ifndef MERCURY_THREAD_H +#define MERCURY_THREAD_H + +#if !defined(_WIN32) && !defined(_GNU_SOURCE) +#define _GNU_SOURCE +#endif +#include "mercury_util_config.h" + +#ifdef _WIN32 +#include <windows.h> +typedef HANDLE hg_thread_t; +typedef LPTHREAD_START_ROUTINE hg_thread_func_t; +typedef DWORD hg_thread_ret_t; +#define HG_THREAD_RETURN_TYPE hg_thread_ret_t WINAPI +typedef DWORD hg_thread_key_t; +typedef DWORD_PTR hg_cpu_set_t; +#else +#include <pthread.h> +typedef pthread_t hg_thread_t; +typedef void *(*hg_thread_func_t)(void *); +typedef void * hg_thread_ret_t; +#define HG_THREAD_RETURN_TYPE hg_thread_ret_t +typedef pthread_key_t hg_thread_key_t; +#ifdef __APPLE__ +/* Size definition for CPU sets. */ +#define HG_CPU_SETSIZE 1024 +#define HG_NCPUBITS (8 * sizeof(hg_cpu_mask_t)) +/* Type for array elements in 'cpu_set_t'. */ +typedef hg_util_uint64_t hg_cpu_mask_t; +typedef struct { + hg_cpu_mask_t bits[HG_CPU_SETSIZE / HG_NCPUBITS]; +} hg_cpu_set_t; +#else +typedef cpu_set_t hg_cpu_set_t; +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Initialize the thread. + * + * \param thread [IN/OUT] pointer to thread object + */ +HG_UTIL_PUBLIC void hg_thread_init(hg_thread_t *thread); + +/** + * Create a new thread for the given function. + * + * \param thread [IN/OUT] pointer to thread object + * \param f [IN] pointer to function + * \param data [IN] pointer to data than be passed to function f + * + * \return Non-negative on success or negative on failure + */ +HG_UTIL_PUBLIC int hg_thread_create(hg_thread_t *thread, hg_thread_func_t f, void *data); + +/** + * Ends the calling thread. + * + * \param ret [IN] exit code for the thread + * + * \return Non-negative on success or negative on failure + */ +HG_UTIL_PUBLIC void hg_thread_exit(hg_thread_ret_t ret); + +/** + * Wait for thread completion. + * + * \param thread [IN] thread object + * + * \return Non-negative on success or negative on failure + */ +HG_UTIL_PUBLIC int hg_thread_join(hg_thread_t thread); + +/** + * Terminate the thread. + * + * \param thread [IN] thread object + * + * \return Non-negative on success or negative on failure + */ +HG_UTIL_PUBLIC int hg_thread_cancel(hg_thread_t thread); + +/** + * Yield the processor. + * + * \return Non-negative on success or negative on failure + */ +HG_UTIL_PUBLIC int hg_thread_yield(void); + +/** + * Obtain handle of the calling thread. + * + * \return + */ +static HG_UTIL_INLINE hg_thread_t hg_thread_self(void); + +/** + * Compare thread IDs. + * + * \return Non-zero if equal, zero if not equal + */ +static HG_UTIL_INLINE int hg_thread_equal(hg_thread_t t1, hg_thread_t t2); + +/** + * Create a thread-specific data key visible to all threads in the process. + * + * \param key [OUT] pointer to thread key object + * + * \return Non-negative on success or negative on failure + */ +HG_UTIL_PUBLIC int hg_thread_key_create(hg_thread_key_t *key); + +/** + * Delete a thread-specific data key previously returned by + * hg_thread_key_create(). + * + * \param key [IN] thread key object + * + * \return Non-negative on success or negative on failure + */ +HG_UTIL_PUBLIC int hg_thread_key_delete(hg_thread_key_t key); + +/** + * Get value from specified key. + * + * \param key [IN] thread key object + * + * \return Pointer to data associated to the key + */ +static HG_UTIL_INLINE void *hg_thread_getspecific(hg_thread_key_t key); + +/** + * Set value to specified key. + * + * \param key [IN] thread key object + * \param value [IN] pointer to data that will be associated + * + * \return Non-negative on success or negative on failure + */ +static HG_UTIL_INLINE int hg_thread_setspecific(hg_thread_key_t key, const void *value); + +/** + * Get affinity mask. + * + * \param thread [IN] thread object + * \param cpu_mask [IN/OUT] cpu mask + * + * \return Non-negative on success or negative on failure + */ +HG_UTIL_PUBLIC int hg_thread_getaffinity(hg_thread_t thread, hg_cpu_set_t *cpu_mask); + +/** + * Set affinity mask. + * + * \param thread [IN] thread object + * \param cpu_mask [IN] cpu mask + * + * \return Non-negative on success or negative on failure + */ +HG_UTIL_PUBLIC int hg_thread_setaffinity(hg_thread_t thread, const hg_cpu_set_t *cpu_mask); + +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE hg_thread_t +hg_thread_self(void) +{ +#ifdef _WIN32 + return GetCurrentThread(); +#else + return pthread_self(); +#endif +} + +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE int +hg_thread_equal(hg_thread_t t1, hg_thread_t t2) +{ +#ifdef _WIN32 + return GetThreadId(t1) == GetThreadId(t2); +#else + return pthread_equal(t1, t2); +#endif +} + +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE void * +hg_thread_getspecific(hg_thread_key_t key) +{ +#ifdef _WIN32 + return TlsGetValue(key); +#else + return pthread_getspecific(key); +#endif +} + +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE int +hg_thread_setspecific(hg_thread_key_t key, const void *value) +{ +#ifdef _WIN32 + if (!TlsSetValue(key, (LPVOID)value)) + return HG_UTIL_FAIL; +#else + if (pthread_setspecific(key, value)) + return HG_UTIL_FAIL; +#endif + + return HG_UTIL_SUCCESS; +} + +#ifdef __cplusplus +} +#endif + +#endif /* MERCURY_THREAD_H */ diff --git a/src/mercury/include/mercury_thread_annotation.h b/src/mercury/include/mercury_thread_annotation.h new file mode 100644 index 0000000..f8613a4 --- /dev/null +++ b/src/mercury/include/mercury_thread_annotation.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2013-2020 Argonne National Laboratory, Department of Energy, + * UChicago Argonne, LLC and The HDF Group. + * All rights reserved. + * + * The full copyright notice, including terms governing use, modification, + * and redistribution, is contained in the COPYING file that can be + * found at the root of the source code distribution tree. + */ + +#ifndef MERCURY_THREAD_ANNOTATION_H +#define MERCURY_THREAD_ANNOTATION_H + +/* Enable thread safety attributes only with clang. + * The attributes can be safely erased when compiling with other compilers. */ +#if defined(__clang__) && (__clang_major__ > 3) +#define HG_THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x)) +#else +#define HG_THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op +#endif + +#define HG_LOCK_CAPABILITY(x) HG_THREAD_ANNOTATION_ATTRIBUTE__(capability(x)) + +#define HG_LOCK_ACQUIRE(...) HG_THREAD_ANNOTATION_ATTRIBUTE__(acquire_capability(__VA_ARGS__)) + +#define HG_LOCK_ACQUIRE_SHARED(...) HG_THREAD_ANNOTATION_ATTRIBUTE__(acquire_shared_capability(__VA_ARGS__)) + +#define HG_LOCK_RELEASE(...) HG_THREAD_ANNOTATION_ATTRIBUTE__(release_capability(__VA_ARGS__)) + +#define HG_LOCK_RELEASE_SHARED(...) HG_THREAD_ANNOTATION_ATTRIBUTE__(release_shared_capability(__VA_ARGS__)) + +#define HG_LOCK_TRY_ACQUIRE(...) HG_THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_capability(__VA_ARGS__)) + +#define HG_LOCK_TRY_ACQUIRE_SHARED(...) \ + HG_THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_shared_capability(__VA_ARGS__)) + +#define HG_LOCK_NO_THREAD_SAFETY_ANALYSIS HG_THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis) + +#endif /* MERCURY_THREAD_ANNOTATION_H */ diff --git a/src/mercury/include/mercury_thread_condition.h b/src/mercury/include/mercury_thread_condition.h new file mode 100644 index 0000000..c1a3d61 --- /dev/null +++ b/src/mercury/include/mercury_thread_condition.h @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2013-2020 Argonne National Laboratory, Department of Energy, + * UChicago Argonne, LLC and The HDF Group. + * All rights reserved. + * + * The full copyright notice, including terms governing use, modification, + * and redistribution, is contained in the COPYING file that can be + * found at the root of the source code distribution tree. + */ + +#ifndef MERCURY_THREAD_CONDITION_H +#define MERCURY_THREAD_CONDITION_H + +#include "mercury_thread_mutex.h" + +#ifdef _WIN32 +typedef CONDITION_VARIABLE hg_thread_cond_t; +#else +#if defined(HG_UTIL_HAS_PTHREAD_CONDATTR_SETCLOCK) && defined(HG_UTIL_HAS_CLOCK_MONOTONIC_COARSE) +#include <time.h> +#elif defined(HG_UTIL_HAS_SYSTIME_H) +#include <sys/time.h> +#endif +#include <stdlib.h> +typedef pthread_cond_t hg_thread_cond_t; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Initialize the condition. + * + * \param cond [IN/OUT] pointer to condition object + * + * \return Non-negative on success or negative on failure + */ +HG_UTIL_PUBLIC int hg_thread_cond_init(hg_thread_cond_t *cond); + +/** + * Destroy the condition. + * + * \param cond [IN/OUT] pointer to condition object + * + * \return Non-negative on success or negative on failure + */ +HG_UTIL_PUBLIC int hg_thread_cond_destroy(hg_thread_cond_t *cond); + +/** + * Wake one thread waiting for the condition to change. + * + * \param cond [IN/OUT] pointer to condition object + * + * \return Non-negative on success or negative on failure + */ +static HG_UTIL_INLINE int hg_thread_cond_signal(hg_thread_cond_t *cond); + +/** + * Wake all the threads waiting for the condition to change. + * + * \param cond [IN/OUT] pointer to condition object + * + * \return Non-negative on success or negative on failure + */ +static HG_UTIL_INLINE int hg_thread_cond_broadcast(hg_thread_cond_t *cond); + +/** + * Wait for the condition to change. + * + * \param cond [IN/OUT] pointer to condition object + * \param mutex [IN/OUT] pointer to mutex object + * + * \return Non-negative on success or negative on failure + */ +static HG_UTIL_INLINE int hg_thread_cond_wait(hg_thread_cond_t *cond, hg_thread_mutex_t *mutex); + +/** + * Wait timeout ms for the condition to change. + * + * \param cond [IN/OUT] pointer to condition object + * \param mutex [IN/OUT] pointer to mutex object + * \param timeout [IN] timeout (in milliseconds) + * + * \return Non-negative on success or negative on failure + */ +static HG_UTIL_INLINE int hg_thread_cond_timedwait(hg_thread_cond_t *cond, hg_thread_mutex_t *mutex, + unsigned int timeout); + +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE int +hg_thread_cond_signal(hg_thread_cond_t *cond) +{ +#ifdef _WIN32 + WakeConditionVariable(cond); +#else + if (pthread_cond_signal(cond)) + return HG_UTIL_FAIL; +#endif + + return HG_UTIL_SUCCESS; +} + +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE int +hg_thread_cond_broadcast(hg_thread_cond_t *cond) +{ +#ifdef _WIN32 + WakeAllConditionVariable(cond); +#else + if (pthread_cond_broadcast(cond)) + return HG_UTIL_FAIL; +#endif + + return HG_UTIL_SUCCESS; +} + +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE int +hg_thread_cond_wait(hg_thread_cond_t *cond, hg_thread_mutex_t *mutex) +{ +#ifdef _WIN32 + if (!SleepConditionVariableCS(cond, mutex, INFINITE)) + return HG_UTIL_FAIL; +#else + if (pthread_cond_wait(cond, mutex)) + return HG_UTIL_FAIL; +#endif + + return HG_UTIL_SUCCESS; +} + +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE int +hg_thread_cond_timedwait(hg_thread_cond_t *cond, hg_thread_mutex_t *mutex, unsigned int timeout) +{ +#ifdef _WIN32 + if (!SleepConditionVariableCS(cond, mutex, timeout)) + return HG_UTIL_FAIL; +#else +#if defined(HG_UTIL_HAS_PTHREAD_CONDATTR_SETCLOCK) && defined(HG_UTIL_HAS_CLOCK_MONOTONIC_COARSE) + struct timespec now; +#else + struct timeval now; +#endif + struct timespec abs_timeout; + ldiv_t ld; + + /* Need to convert timeout (ms) to absolute time */ +#if defined(HG_UTIL_HAS_PTHREAD_CONDATTR_SETCLOCK) && defined(HG_UTIL_HAS_CLOCK_MONOTONIC_COARSE) + clock_gettime(CLOCK_MONOTONIC_COARSE, &now); + + /* Get sec / nsec */ + ld = ldiv(now.tv_nsec + timeout * 1000000L, 1000000000L); + abs_timeout.tv_nsec = ld.rem; +#elif defined(HG_UTIL_HAS_SYSTIME_H) + gettimeofday(&now, NULL); + + /* Get sec / usec */ + ld = ldiv(now.tv_usec + timeout * 1000L, 1000000L); + abs_timeout.tv_nsec = ld.rem * 1000L; +#endif + abs_timeout.tv_sec = now.tv_sec + ld.quot; + + if (pthread_cond_timedwait(cond, mutex, &abs_timeout)) + return HG_UTIL_FAIL; +#endif + + return HG_UTIL_SUCCESS; +} + +#ifdef __cplusplus +} +#endif + +#endif /* MERCURY_THREAD_CONDITION_H */ diff --git a/src/mercury/include/mercury_thread_mutex.h b/src/mercury/include/mercury_thread_mutex.h new file mode 100644 index 0000000..b400952 --- /dev/null +++ b/src/mercury/include/mercury_thread_mutex.h @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2013-2020 Argonne National Laboratory, Department of Energy, + * UChicago Argonne, LLC and The HDF Group. + * All rights reserved. + * + * The full copyright notice, including terms governing use, modification, + * and redistribution, is contained in the COPYING file that can be + * found at the root of the source code distribution tree. + */ + +#ifndef MERCURY_THREAD_MUTEX_H +#define MERCURY_THREAD_MUTEX_H + +#include "mercury_util_config.h" + +#include "mercury_thread_annotation.h" + +#ifdef _WIN32 +#include <windows.h> +#define HG_THREAD_MUTEX_INITIALIZER NULL +typedef CRITICAL_SECTION hg_thread_mutex_t; +#else +#include <pthread.h> +#define HG_THREAD_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER +typedef pthread_mutex_t HG_LOCK_CAPABILITY("mutex") hg_thread_mutex_t; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Initialize the mutex. + * + * \param mutex [IN/OUT] pointer to mutex object + * + * \return Non-negative on success or negative on failure + */ +HG_UTIL_PUBLIC int hg_thread_mutex_init(hg_thread_mutex_t *mutex); + +/** + * Initialize the mutex, asking for "fast" mutex. + * + * \param mutex [IN/OUT] pointer to mutex object + * + * \return Non-negative on success or negative on failure + */ +HG_UTIL_PUBLIC int hg_thread_mutex_init_fast(hg_thread_mutex_t *mutex); + +/** + * Destroy the mutex. + * + * \param mutex [IN/OUT] pointer to mutex object + * + * \return Non-negative on success or negative on failure + */ +HG_UTIL_PUBLIC int hg_thread_mutex_destroy(hg_thread_mutex_t *mutex); + +/** + * Lock the mutex. + * + * \param mutex [IN/OUT] pointer to mutex object + */ +static HG_UTIL_INLINE void hg_thread_mutex_lock(hg_thread_mutex_t *mutex) HG_LOCK_ACQUIRE(*mutex); + +/** + * Try locking the mutex. + * + * \param mutex [IN/OUT] pointer to mutex object + * + * \return Non-negative on success or negative on failure + */ +static HG_UTIL_INLINE int hg_thread_mutex_try_lock(hg_thread_mutex_t *mutex) + HG_LOCK_TRY_ACQUIRE(HG_UTIL_SUCCESS, *mutex); + +/** + * Unlock the mutex. + * + * \param mutex [IN/OUT] pointer to mutex object + */ +static HG_UTIL_INLINE void hg_thread_mutex_unlock(hg_thread_mutex_t *mutex) HG_LOCK_RELEASE(*mutex); + +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE void +hg_thread_mutex_lock(hg_thread_mutex_t *mutex) HG_LOCK_NO_THREAD_SAFETY_ANALYSIS +{ +#ifdef _WIN32 + EnterCriticalSection(mutex); +#else + (void)pthread_mutex_lock(mutex); +#endif +} + +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE int +hg_thread_mutex_try_lock(hg_thread_mutex_t *mutex) HG_LOCK_NO_THREAD_SAFETY_ANALYSIS +{ +#ifdef _WIN32 + if (!TryEnterCriticalSection(mutex)) + return HG_UTIL_FAIL; +#else + if (pthread_mutex_trylock(mutex)) + return HG_UTIL_FAIL; +#endif + + return HG_UTIL_SUCCESS; +} + +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE void +hg_thread_mutex_unlock(hg_thread_mutex_t *mutex) HG_LOCK_NO_THREAD_SAFETY_ANALYSIS +{ +#ifdef _WIN32 + LeaveCriticalSection(mutex); +#else + (void)pthread_mutex_unlock(mutex); +#endif +} + +#ifdef __cplusplus +} +#endif + +#endif /* MERCURY_THREAD_MUTEX_H */ diff --git a/src/mercury/include/mercury_thread_pool.h b/src/mercury/include/mercury_thread_pool.h new file mode 100644 index 0000000..db973d1 --- /dev/null +++ b/src/mercury/include/mercury_thread_pool.h @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2013-2020 Argonne National Laboratory, Department of Energy, + * UChicago Argonne, LLC and The HDF Group. + * All rights reserved. + * + * The full copyright notice, including terms governing use, modification, + * and redistribution, is contained in the COPYING file that can be + * found at the root of the source code distribution tree. + */ + +#ifndef MERCURY_THREAD_POOL_H +#define MERCURY_THREAD_POOL_H + +#include "mercury_queue.h" +#include "mercury_thread.h" +#include "mercury_thread_condition.h" + +/*************************************/ +/* Public Type and Struct Definition */ +/*************************************/ + +typedef struct hg_thread_pool hg_thread_pool_t; + +struct hg_thread_pool { + unsigned int sleeping_worker_count; + HG_QUEUE_HEAD(hg_thread_work) queue; + int shutdown; + hg_thread_mutex_t mutex; + hg_thread_cond_t cond; +}; + +struct hg_thread_work { + hg_thread_func_t func; + void * args; + HG_QUEUE_ENTRY(hg_thread_work) entry; /* Internal */ +}; + +/*****************/ +/* Public Macros */ +/*****************/ + +/*********************/ +/* Public Prototypes */ +/*********************/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Initialize the thread pool. + * + * \param thread_count [IN] number of threads that will be created at + * initialization + * \param pool [OUT] pointer to pool object + * + * \return Non-negative on success or negative on failure + */ +HG_UTIL_PUBLIC int hg_thread_pool_init(unsigned int thread_count, hg_thread_pool_t **pool); + +/** + * Destroy the thread pool. + * + * \param pool [IN/OUT] pointer to pool object + * + * \return Non-negative on success or negative on failure + */ +HG_UTIL_PUBLIC int hg_thread_pool_destroy(hg_thread_pool_t *pool); + +/** + * Post work to the pool. Note that the operation may be queued depending on + * the number of threads and number of tasks already running. + * + * \param pool [IN/OUT] pointer to pool object + * \param work [IN] pointer to work struct + * + * \return Non-negative on success or negative on failure + */ +static HG_UTIL_INLINE int hg_thread_pool_post(hg_thread_pool_t *pool, struct hg_thread_work *work); + +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE int +hg_thread_pool_post(hg_thread_pool_t *pool, struct hg_thread_work *work) +{ + int ret = HG_UTIL_SUCCESS; + + if (!pool || !work) + return HG_UTIL_FAIL; + + if (!work->func) + return HG_UTIL_FAIL; + + hg_thread_mutex_lock(&pool->mutex); + + /* Are we shutting down ? */ + if (pool->shutdown) { + ret = HG_UTIL_FAIL; + goto unlock; + } + + /* Add task to task queue */ + HG_QUEUE_PUSH_TAIL(&pool->queue, work, entry); + + /* Wake up sleeping worker */ + if (pool->sleeping_worker_count && (hg_thread_cond_signal(&pool->cond) != HG_UTIL_SUCCESS)) + ret = HG_UTIL_FAIL; + +unlock: + hg_thread_mutex_unlock(&pool->mutex); + + return ret; +} + +#ifdef __cplusplus +} +#endif + +#endif /* MERCURY_THREAD_POOL_H */ diff --git a/src/mercury/include/mercury_thread_rwlock.h b/src/mercury/include/mercury_thread_rwlock.h new file mode 100644 index 0000000..f03d2aa --- /dev/null +++ b/src/mercury/include/mercury_thread_rwlock.h @@ -0,0 +1,215 @@ +/* + * Copyright (C) 2013-2020 Argonne National Laboratory, Department of Energy, + * UChicago Argonne, LLC and The HDF Group. + * All rights reserved. + * + * The full copyright notice, including terms governing use, modification, + * and redistribution, is contained in the COPYING file that can be + * found at the root of the source code distribution tree. + */ + +/* Copyright (C) 2017 Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted for any purpose (including commercial purposes) + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions, and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions, and the following disclaimer in the + * documentation and/or materials provided with the distribution. + * + * 3. In addition, redistributions of modified forms of the source or binary + * code must carry prominent notices stating that the original code was + * changed and the date of the change. + * + * 4. All publications or advertising materials mentioning features or use of + * this software are asked, but not required, to acknowledge that it was + * developed by Intel Corporation and credit the contributors. + * + * 5. Neither the name of Intel Corporation, nor the name of any Contributor + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MERCURY_THREAD_RWLOCK_H +#define MERCURY_THREAD_RWLOCK_H + +#include "mercury_util_config.h" + +#include "mercury_thread_annotation.h" + +#ifdef _WIN32 +#include <windows.h> +typedef PSRWLOCK hg_thread_rwlock_t; +#else +#include <pthread.h> +typedef pthread_rwlock_t HG_LOCK_CAPABILITY("rwlock") hg_thread_rwlock_t; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Initialize the rwlock. + * + * \param rwlock [IN/OUT] pointer to rwlock object + * + * \return Non-negative on success or negative on failure + */ +HG_UTIL_PUBLIC int hg_thread_rwlock_init(hg_thread_rwlock_t *rwlock); + +/** + * Destroy the rwlock. + * + * \param rwlock [IN/OUT] pointer to rwlock object + * + * \return Non-negative on success or negative on failure + */ +HG_UTIL_PUBLIC int hg_thread_rwlock_destroy(hg_thread_rwlock_t *rwlock); + +/** + * Take a read lock for the rwlock. + * + * \param rwlock [IN/OUT] pointer to rwlock object + */ +static HG_UTIL_INLINE void hg_thread_rwlock_rdlock(hg_thread_rwlock_t *rwlock) + HG_LOCK_ACQUIRE_SHARED(*rwlock); + +/** + * Try to take a read lock for the rwlock. + * + * \param rwlock [IN/OUT] pointer to rwlock object + * + * \return Non-negative on success or negative on failure + */ +static HG_UTIL_INLINE int hg_thread_rwlock_try_rdlock(hg_thread_rwlock_t *rwlock) + HG_LOCK_TRY_ACQUIRE_SHARED(HG_UTIL_SUCCESS, *rwlock); + +/** + * Release the read lock of the rwlock. + * + * \param rwlock [IN/OUT] pointer to rwlock object + */ +static HG_UTIL_INLINE void hg_thread_rwlock_release_rdlock(hg_thread_rwlock_t *rwlock) + HG_LOCK_RELEASE_SHARED(*rwlock); + +/** + * Take a write lock for the rwlock. + * + * \param rwlock [IN/OUT] pointer to rwlock object + */ +static HG_UTIL_INLINE void hg_thread_rwlock_wrlock(hg_thread_rwlock_t *rwlock) HG_LOCK_ACQUIRE(*rwlock); + +/** + * Try to take a write lock for the rwlock. + * + * \param rwlock [IN/OUT] pointer to rwlock object + * + * \return Non-negative on success or negative on failure + */ +static HG_UTIL_INLINE int hg_thread_rwlock_try_wrlock(hg_thread_rwlock_t *rwlock) + HG_LOCK_TRY_ACQUIRE(HG_UTIL_SUCCESS, *rwlock); + +/** + * Release the write lock of the rwlock. + * + * \param rwlock [IN/OUT] pointer to rwlock object + */ +static HG_UTIL_INLINE void hg_thread_rwlock_release_wrlock(hg_thread_rwlock_t *rwlock) + HG_LOCK_RELEASE(*rwlock); + +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE void +hg_thread_rwlock_rdlock(hg_thread_rwlock_t *rwlock) HG_LOCK_NO_THREAD_SAFETY_ANALYSIS +{ +#ifdef _WIN32 + AcquireSRWLockShared(rwlock); +#else + (void)pthread_rwlock_rdlock(rwlock); +#endif +} + +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE int +hg_thread_rwlock_try_rdlock(hg_thread_rwlock_t *rwlock) HG_LOCK_NO_THREAD_SAFETY_ANALYSIS +{ +#ifdef _WIN32 + if (TryAcquireSRWLockShared(rwlock) == 0) + return HG_UTIL_FAIL; +#else + if (pthread_rwlock_tryrdlock(rwlock)) + return HG_UTIL_FAIL; +#endif + + return HG_UTIL_SUCCESS; +} + +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE void +hg_thread_rwlock_release_rdlock(hg_thread_rwlock_t *rwlock) HG_LOCK_NO_THREAD_SAFETY_ANALYSIS +{ +#ifdef _WIN32 + ReleaseSRWLockShared(rwlock); +#else + (void)pthread_rwlock_unlock(rwlock); +#endif +} + +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE void +hg_thread_rwlock_wrlock(hg_thread_rwlock_t *rwlock) HG_LOCK_NO_THREAD_SAFETY_ANALYSIS +{ +#ifdef _WIN32 + ReleaseSRWLockExclusive(rwlock); +#else + (void)pthread_rwlock_wrlock(rwlock); +#endif +} + +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE int +hg_thread_rwlock_try_wrlock(hg_thread_rwlock_t *rwlock) HG_LOCK_NO_THREAD_SAFETY_ANALYSIS +{ +#ifdef _WIN32 + if (TryAcquireSRWLockExclusive(rwlock) == 0) + return HG_UTIL_FAIL; +#else + if (pthread_rwlock_trywrlock(rwlock)) + return HG_UTIL_FAIL; +#endif + + return HG_UTIL_SUCCESS; +} + +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE void +hg_thread_rwlock_release_wrlock(hg_thread_rwlock_t *rwlock) HG_LOCK_NO_THREAD_SAFETY_ANALYSIS +{ +#ifdef _WIN32 + ReleaseSRWLockExclusive(rwlock); +#else + (void)pthread_rwlock_unlock(rwlock); +#endif +} + +#ifdef __cplusplus +} +#endif + +#endif /* MERCURY_THREAD_RWLOCK_H */ diff --git a/src/mercury/include/mercury_thread_spin.h b/src/mercury/include/mercury_thread_spin.h new file mode 100644 index 0000000..36ce5f8 --- /dev/null +++ b/src/mercury/include/mercury_thread_spin.h @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2013-2020 Argonne National Laboratory, Department of Energy, + * UChicago Argonne, LLC and The HDF Group. + * All rights reserved. + * + * The full copyright notice, including terms governing use, modification, + * and redistribution, is contained in the COPYING file that can be + * found at the root of the source code distribution tree. + */ + +#ifndef MERCURY_THREAD_SPIN_H +#define MERCURY_THREAD_SPIN_H + +#include "mercury_util_config.h" + +#include "mercury_thread_annotation.h" + +#if defined(_WIN32) +#include <windows.h> +typedef volatile LONG hg_thread_spin_t; +#elif defined(HG_UTIL_HAS_PTHREAD_SPINLOCK_T) +#include <pthread.h> +typedef pthread_spinlock_t HG_LOCK_CAPABILITY("spin") hg_thread_spin_t; +#else +/* Default to hg_thread_mutex_t if pthread_spinlock_t is not supported */ +#include "mercury_thread_mutex.h" +typedef hg_thread_mutex_t HG_LOCK_CAPABILITY("mutex") hg_thread_spin_t; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Initialize the spin lock. + * + * \param lock [IN/OUT] pointer to lock object + * + * \return Non-negative on success or negative on failure + */ +HG_UTIL_PUBLIC int hg_thread_spin_init(hg_thread_spin_t *lock); + +/** + * Destroy the spin lock. + * + * \param lock [IN/OUT] pointer to lock object + * + * \return Non-negative on success or negative on failure + */ +HG_UTIL_PUBLIC int hg_thread_spin_destroy(hg_thread_spin_t *lock); + +/** + * Lock the spin lock. + * + * \param lock [IN/OUT] pointer to lock object + */ +static HG_UTIL_INLINE void hg_thread_spin_lock(hg_thread_spin_t *lock) HG_LOCK_ACQUIRE(*lock); + +/** + * Try locking the spin lock. + * + * \param mutex [IN/OUT] pointer to lock object + * + * \return Non-negative on success or negative on failure + */ +static HG_UTIL_INLINE int hg_thread_spin_try_lock(hg_thread_spin_t *lock) + HG_LOCK_TRY_ACQUIRE(HG_UTIL_SUCCESS, *lock); + +/** + * Unlock the spin lock. + * + * \param mutex [IN/OUT] pointer to lock object + */ +static HG_UTIL_INLINE void hg_thread_spin_unlock(hg_thread_spin_t *lock) HG_LOCK_RELEASE(*lock); + +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE void +hg_thread_spin_lock(hg_thread_spin_t *lock) HG_LOCK_NO_THREAD_SAFETY_ANALYSIS +{ +#if defined(_WIN32) + while (InterlockedExchange(lock, EBUSY)) { + /* Don't lock while waiting */ + while (*lock) { + YieldProcessor(); + + /* Compiler barrier. Prevent caching of *lock */ + MemoryBarrier(); + } + } +#elif defined(HG_UTIL_HAS_PTHREAD_SPINLOCK_T) + (void)pthread_spin_lock(lock); +#else + hg_thread_mutex_lock(lock); +#endif +} + +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE int +hg_thread_spin_try_lock(hg_thread_spin_t *lock) HG_LOCK_NO_THREAD_SAFETY_ANALYSIS +{ +#if defined(_WIN32) + return InterlockedExchange(lock, EBUSY); +#elif defined(HG_UTIL_HAS_PTHREAD_SPINLOCK_T) + if (pthread_spin_trylock(lock)) + return HG_UTIL_FAIL; + + return HG_UTIL_SUCCESS; +#else + return hg_thread_mutex_try_lock(lock); +#endif +} + +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE void +hg_thread_spin_unlock(hg_thread_spin_t *lock) HG_LOCK_NO_THREAD_SAFETY_ANALYSIS +{ +#if defined(_WIN32) + /* Compiler barrier. The store below acts with release semantics */ + MemoryBarrier(); + *lock = 0; +#elif defined(HG_UTIL_HAS_PTHREAD_SPINLOCK_T) + (void)pthread_spin_unlock(lock); +#else + hg_thread_mutex_unlock(lock); +#endif +} + +#ifdef __cplusplus +} +#endif + +#endif /* MERCURY_THREAD_SPIN_H */ diff --git a/src/mercury/include/mercury_time.h b/src/mercury/include/mercury_time.h new file mode 100644 index 0000000..f158638 --- /dev/null +++ b/src/mercury/include/mercury_time.h @@ -0,0 +1,503 @@ +/* + * Copyright (C) 2013-2020 Argonne National Laboratory, Department of Energy, + * UChicago Argonne, LLC and The HDF Group. + * All rights reserved. + * + * The full copyright notice, including terms governing use, modification, + * and redistribution, is contained in the COPYING file that can be + * found at the root of the source code distribution tree. + */ + +#ifndef MERCURY_TIME_H +#define MERCURY_TIME_H + +#include "mercury_util_config.h" + +#if defined(_WIN32) +#include <windows.h> +#elif defined(HG_UTIL_HAS_TIME_H) && defined(HG_UTIL_HAS_CLOCK_GETTIME) +#include <time.h> +#elif defined(__APPLE__) && defined(HG_UTIL_HAS_SYSTIME_H) +#include <mach/mach_time.h> +#include <sys/time.h> +#else +#include <stdio.h> +#include <unistd.h> +#if defined(HG_UTIL_HAS_SYSTIME_H) +#include <sys/time.h> +#else +#error "Not supported on this platform." +#endif +#endif + +/*************************************/ +/* Public Type and Struct Definition */ +/*************************************/ + +#if defined(HG_UTIL_HAS_TIME_H) && defined(HG_UTIL_HAS_CLOCK_GETTIME) +typedef struct timespec hg_time_t; +#else +typedef struct hg_time hg_time_t; + +struct hg_time { + long tv_sec; + long tv_usec; +}; +#endif + +/*****************/ +/* Public Macros */ +/*****************/ + +/*********************/ +/* Public Prototypes */ +/*********************/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Get an elapsed time on the calling processor. + * + * \param tv [OUT] pointer to returned time structure + * + * \return Non-negative on success or negative on failure + */ +static HG_UTIL_INLINE int hg_time_get_current(hg_time_t *tv); + +/** + * Get an elapsed time on the calling processor (resolution is ms). + * + * \param tv [OUT] pointer to returned time structure + * + * \return Non-negative on success or negative on failure + */ +static HG_UTIL_INLINE int hg_time_get_current_ms(hg_time_t *tv); + +/** + * Convert hg_time_t to double. + * + * \param tv [IN] time structure + * + * \return Converted time in seconds + */ +static HG_UTIL_INLINE double hg_time_to_double(hg_time_t tv); + +/** + * Convert double to hg_time_t. + * + * \param d [IN] time in seconds + * + * \return Converted time structure + */ +static HG_UTIL_INLINE hg_time_t hg_time_from_double(double d); + +/** + * Convert (integer) milliseconds to hg_time_t. + * + * \param ms [IN] time in milliseconds + * + * \return Converted time structure + */ +static HG_UTIL_INLINE hg_time_t hg_time_from_ms(unsigned int ms); + +/** + * Convert hg_time_t to (integer) milliseconds. + * + * \param tv [IN] time structure + * + * \return Time in milliseconds + */ +static HG_UTIL_INLINE unsigned int hg_time_to_ms(hg_time_t tv); + +/** + * Compare time values. + * + * \param in1 [IN] time structure + * \param in2 [IN] time structure + * + * \return 1 if in1 < in2, 0 otherwise + */ +static HG_UTIL_INLINE int hg_time_less(hg_time_t in1, hg_time_t in2); + +/** + * Diff time values and return the number of seconds elapsed between + * time \in2 and time \in1. + * + * \param in2 [IN] time structure + * \param in1 [IN] time structure + * + * \return Subtracted time + */ +static HG_UTIL_INLINE double hg_time_diff(hg_time_t in2, hg_time_t in1); + +/** + * Add time values. + * + * \param in1 [IN] time structure + * \param in2 [IN] time structure + * + * \return Summed time structure + */ +static HG_UTIL_INLINE hg_time_t hg_time_add(hg_time_t in1, hg_time_t in2); + +/** + * Subtract time values. + * + * \param in1 [IN] time structure + * \param in2 [IN] time structure + * + * \return Subtracted time structure + */ +static HG_UTIL_INLINE hg_time_t hg_time_subtract(hg_time_t in1, hg_time_t in2); + +/** + * Sleep until the time specified in rqt has elapsed. + * + * \param reqt [IN] time structure + * + * \return Non-negative on success or negative on failure + */ +static HG_UTIL_INLINE int hg_time_sleep(const hg_time_t rqt); + +/** + * Get a string containing current time/date stamp. + * + * \return Valid string or NULL on failure + */ +static HG_UTIL_INLINE char *hg_time_stamp(void); + +/*---------------------------------------------------------------------------*/ +#ifdef _WIN32 +static HG_UTIL_INLINE LARGE_INTEGER +get_FILETIME_offset(void) +{ + SYSTEMTIME s; + FILETIME f; + LARGE_INTEGER t; + + s.wYear = 1970; + s.wMonth = 1; + s.wDay = 1; + s.wHour = 0; + s.wMinute = 0; + s.wSecond = 0; + s.wMilliseconds = 0; + SystemTimeToFileTime(&s, &f); + t.QuadPart = f.dwHighDateTime; + t.QuadPart <<= 32; + t.QuadPart |= f.dwLowDateTime; + + return t; +} + +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE int +hg_time_get_current(hg_time_t *tv) +{ + LARGE_INTEGER t; + FILETIME f; + double t_usec; + static LARGE_INTEGER offset; + static double freq_to_usec; + static int initialized = 0; + static BOOL use_perf_counter = 0; + + if (!initialized) { + LARGE_INTEGER perf_freq; + initialized = 1; + use_perf_counter = QueryPerformanceFrequency(&perf_freq); + if (use_perf_counter) { + QueryPerformanceCounter(&offset); + freq_to_usec = (double)perf_freq.QuadPart / 1000000.; + } + else { + offset = get_FILETIME_offset(); + freq_to_usec = 10.; + } + } + if (use_perf_counter) { + QueryPerformanceCounter(&t); + } + else { + GetSystemTimeAsFileTime(&f); + t.QuadPart = f.dwHighDateTime; + t.QuadPart <<= 32; + t.QuadPart |= f.dwLowDateTime; + } + + t.QuadPart -= offset.QuadPart; + t_usec = (double)t.QuadPart / freq_to_usec; + t.QuadPart = t_usec; + tv->tv_sec = t.QuadPart / 1000000; + tv->tv_usec = t.QuadPart % 1000000; + + return HG_UTIL_SUCCESS; +} + +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE int +hg_time_get_current_ms(hg_time_t *tv) +{ + return hg_time_get_current(tv); +} + +/*---------------------------------------------------------------------------*/ +#elif defined(HG_UTIL_HAS_TIME_H) && defined(HG_UTIL_HAS_CLOCK_GETTIME) +static HG_UTIL_INLINE int +hg_time_get_current(hg_time_t *tv) +{ + clock_gettime(CLOCK_MONOTONIC, tv); + + return HG_UTIL_SUCCESS; +} + +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE int +hg_time_get_current_ms(hg_time_t *tv) +{ +/* ppc/32 and ppc/64 do not support CLOCK_MONOTONIC_COARSE in vdso */ +#if defined(__ppc64__) || defined(__ppc__) || defined(__PPC64__) || defined(__PPC__) || \ + !defined(HG_UTIL_HAS_CLOCK_MONOTONIC_COARSE) + clock_gettime(CLOCK_MONOTONIC, tv); +#else + /* We don't need fine grain time stamps, _COARSE resolution is 1ms */ + clock_gettime(CLOCK_MONOTONIC_COARSE, tv); +#endif + return HG_UTIL_SUCCESS; +} + +/*---------------------------------------------------------------------------*/ +#elif defined(__APPLE__) && defined(HG_UTIL_HAS_SYSTIME_H) +static HG_UTIL_INLINE int +hg_time_get_current(hg_time_t *tv) +{ + static uint64_t monotonic_timebase_factor = 0; + uint64_t monotonic_nsec; + + if (monotonic_timebase_factor == 0) { + mach_timebase_info_data_t timebase_info; + + (void)mach_timebase_info(&timebase_info); + monotonic_timebase_factor = timebase_info.numer / timebase_info.denom; + } + monotonic_nsec = (mach_absolute_time() * monotonic_timebase_factor); + tv->tv_sec = (long)(monotonic_nsec / 1000000000); + tv->tv_usec = (long)((monotonic_nsec - (uint64_t)tv->tv_sec) / 1000); + + return HG_UTIL_SUCCESS; +} + +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE int +hg_time_get_current_ms(hg_time_t *tv) +{ + return hg_time_get_current(tv); +} + +#else +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE int +hg_time_get_current(hg_time_t *tv) +{ + gettimeofday((struct timeval *)tv, NULL); + + return HG_UTIL_SUCCESS; +} + +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE int +hg_time_get_current_ms(hg_time_t *tv) +{ + return hg_time_get_current(tv); +} + +#endif +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE double +hg_time_to_double(hg_time_t tv) +{ +#if defined(HG_UTIL_HAS_TIME_H) && defined(HG_UTIL_HAS_CLOCK_GETTIME) + return (double)tv.tv_sec + (double)(tv.tv_nsec) * 0.000000001; +#else + return (double)tv.tv_sec + (double)(tv.tv_usec) * 0.000001; +#endif +} + +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE hg_time_t +hg_time_from_double(double d) +{ + hg_time_t tv; + + tv.tv_sec = (long)d; +#if defined(HG_UTIL_HAS_TIME_H) && defined(HG_UTIL_HAS_CLOCK_GETTIME) + tv.tv_nsec = (long)((d - (double)(tv.tv_sec)) * 1000000000); +#else + tv.tv_usec = (long)((d - (double)(tv.tv_sec)) * 1000000); +#endif + + return tv; +} + +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE unsigned int +hg_time_to_ms(hg_time_t tv) +{ +#if defined(HG_UTIL_HAS_TIME_H) && defined(HG_UTIL_HAS_CLOCK_GETTIME) + return (unsigned int)(tv.tv_sec * 1000 + tv.tv_nsec / 1000000); +#else + return (unsigned int)(tv.tv_sec * 1000 + tv.tv_usec / 1000); +#endif +} + +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE hg_time_t +hg_time_from_ms(unsigned int ms) +{ +#if defined(HG_UTIL_HAS_TIME_H) && defined(HG_UTIL_HAS_CLOCK_GETTIME) + return (hg_time_t){.tv_sec = ms / 1000, .tv_nsec = (ms - (ms / 1000) * 1000) * 1000000}; +#else + return (hg_time_t){.tv_sec = ms / 1000, .tv_usec = (ms - (ms / 1000) * 1000) * 1000}; +#endif +} + +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE int +hg_time_less(hg_time_t in1, hg_time_t in2) +{ + return ((in1.tv_sec < in2.tv_sec) || ((in1.tv_sec == in2.tv_sec) && +#if defined(HG_UTIL_HAS_TIME_H) && defined(HG_UTIL_HAS_CLOCK_GETTIME) + (in1.tv_nsec < in2.tv_nsec))); +#else + (in1.tv_usec < in2.tv_usec))); +#endif +} + +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE double +hg_time_diff(hg_time_t in2, hg_time_t in1) +{ +#if defined(HG_UTIL_HAS_TIME_H) && defined(HG_UTIL_HAS_CLOCK_GETTIME) + return ((double)in2.tv_sec + (double)(in2.tv_nsec) * 0.000000001) - + ((double)in1.tv_sec + (double)(in1.tv_nsec) * 0.000000001); +#else + return ((double)in2.tv_sec + (double)(in2.tv_usec) * 0.000001) - + ((double)in1.tv_sec + (double)(in1.tv_usec) * 0.000001); +#endif +} + +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE hg_time_t +hg_time_add(hg_time_t in1, hg_time_t in2) +{ + hg_time_t out; + + out.tv_sec = in1.tv_sec + in2.tv_sec; +#if defined(HG_UTIL_HAS_TIME_H) && defined(HG_UTIL_HAS_CLOCK_GETTIME) + out.tv_nsec = in1.tv_nsec + in2.tv_nsec; + if (out.tv_nsec > 1000000000) { + out.tv_nsec -= 1000000000; + out.tv_sec += 1; + } +#else + out.tv_usec = in1.tv_usec + in2.tv_usec; + if (out.tv_usec > 1000000) { + out.tv_usec -= 1000000; + out.tv_sec += 1; + } +#endif + + return out; +} + +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE hg_time_t +hg_time_subtract(hg_time_t in1, hg_time_t in2) +{ + hg_time_t out; + + out.tv_sec = in1.tv_sec - in2.tv_sec; +#if defined(HG_UTIL_HAS_TIME_H) && defined(HG_UTIL_HAS_CLOCK_GETTIME) + out.tv_nsec = in1.tv_nsec - in2.tv_nsec; + if (out.tv_nsec < 0) { + out.tv_nsec += 1000000000; + out.tv_sec -= 1; + } +#else + out.tv_usec = in1.tv_usec - in2.tv_usec; + if (out.tv_usec < 0) { + out.tv_usec += 1000000; + out.tv_sec -= 1; + } +#endif + + return out; +} + +/*---------------------------------------------------------------------------*/ +static HG_UTIL_INLINE int +hg_time_sleep(const hg_time_t rqt) +{ +#ifdef _WIN32 + DWORD dwMilliseconds = (DWORD)(hg_time_to_double(rqt) / 1000); + + Sleep(dwMilliseconds); +#elif defined(HG_UTIL_HAS_TIME_H) && defined(HG_UTIL_HAS_CLOCK_GETTIME) + if (nanosleep(&rqt, NULL)) + return HG_UTIL_FAIL; +#else + useconds_t usec = (useconds_t)rqt.tv_sec * 1000000 + (useconds_t)rqt.tv_usec; + + if (usleep(usec)) + return HG_UTIL_FAIL; +#endif + + return HG_UTIL_SUCCESS; +} + +/*---------------------------------------------------------------------------*/ +#define HG_UTIL_STAMP_MAX 128 +static HG_UTIL_INLINE char * +hg_time_stamp(void) +{ + static char buf[HG_UTIL_STAMP_MAX] = {'\0'}; + +#if defined(_WIN32) + /* TODO not implemented */ +#elif defined(HG_UTIL_HAS_TIME_H) && defined(HG_UTIL_HAS_CLOCK_GETTIME) + struct tm *local_time; + time_t t; + + t = time(NULL); + local_time = localtime(&t); + if (local_time == NULL) + return NULL; + + if (strftime(buf, HG_UTIL_STAMP_MAX, "%a, %d %b %Y %T %Z", local_time) == 0) + return NULL; +#else + struct timeval tv; + struct timezone tz; + unsigned long days, hours, minutes, seconds; + + gettimeofday(&tv, &tz); + days = (unsigned long)tv.tv_sec / (3600 * 24); + hours = ((unsigned long)tv.tv_sec - days * 24 * 3600) / 3600; + minutes = ((unsigned long)tv.tv_sec - days * 24 * 3600 - hours * 3600) / 60; + seconds = (unsigned long)tv.tv_sec - days * 24 * 3600 - hours * 3600 - minutes * 60; + hours -= (unsigned long)tz.tz_minuteswest / 60; + + snprintf(buf, HG_UTIL_STAMP_MAX, "%02lu:%02lu:%02lu (GMT-%d)", hours, minutes, seconds, + tz.tz_minuteswest / 60); +#endif + + return buf; +} + +#ifdef __cplusplus +} +#endif + +#endif /* MERCURY_TIME_H */ diff --git a/src/mercury/include/mercury_types.h b/src/mercury/include/mercury_types.h new file mode 100644 index 0000000..7ea6b17 --- /dev/null +++ b/src/mercury/include/mercury_types.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2013-2020 Argonne National Laboratory, Department of Energy, + * UChicago Argonne, LLC and The HDF Group. + * All rights reserved. + * + * The full copyright notice, including terms governing use, modification, + * and redistribution, is contained in the COPYING file that can be + * found at the root of the source code distribution tree. + */ + +#ifndef MERCURY_TYPES_H +#define MERCURY_TYPES_H + +#include "mercury_core_types.h" + +/*************************************/ +/* Public Type and Struct Definition */ +/*************************************/ + +typedef struct hg_class hg_class_t; /* Opaque HG class */ +typedef struct hg_context hg_context_t; /* Opaque HG context */ +typedef struct hg_addr * hg_addr_t; /* Abstract HG address */ +typedef struct hg_handle *hg_handle_t; /* Abstract RPC handle */ +typedef struct hg_bulk * hg_bulk_t; /* Abstract bulk data handle */ +typedef struct hg_proc * hg_proc_t; /* Abstract serialization processor */ +typedef struct hg_op_id * hg_op_id_t; /* Abstract operation id */ + +/* HG info struct */ +struct hg_info { + hg_class_t * hg_class; /* HG class */ + hg_context_t *context; /* HG context */ + hg_addr_t addr; /* HG address at target/origin */ + hg_id_t id; /* RPC ID */ + hg_uint8_t context_id; /* Context ID at target/origin */ +}; + +/** + * Bulk transfer operators. + */ +typedef enum { + HG_BULK_PUSH, /*!< push data to origin */ + HG_BULK_PULL /*!< pull data from origin */ +} hg_bulk_op_t; + +/* Callback info structs */ +struct hg_cb_info_lookup { + hg_addr_t addr; /* HG address */ +}; + +struct hg_cb_info_forward { + hg_handle_t handle; /* HG handle */ +}; + +struct hg_cb_info_respond { + hg_handle_t handle; /* HG handle */ +}; + +struct hg_cb_info_bulk { + hg_bulk_t origin_handle; /* HG Bulk origin handle */ + hg_bulk_t local_handle; /* HG Bulk local handle */ + hg_bulk_op_t op; /* Operation type */ + hg_size_t size; /* Total size transferred */ +}; + +struct hg_cb_info { + union { /* Union of callback info structures */ + struct hg_cb_info_lookup lookup; + struct hg_cb_info_forward forward; + struct hg_cb_info_respond respond; + struct hg_cb_info_bulk bulk; + } info; + void * arg; /* User data */ + hg_cb_type_t type; /* Callback type */ + hg_return_t ret; /* Return value */ +}; + +/* RPC / HG callbacks */ +typedef hg_return_t (*hg_rpc_cb_t)(hg_handle_t handle); +typedef hg_return_t (*hg_cb_t)(const struct hg_cb_info *callback_info); + +/* Proc callback for serializing/deserializing parameters */ +typedef hg_return_t (*hg_proc_cb_t)(hg_proc_t proc, void *data); + +/*****************/ +/* Public Macros */ +/*****************/ + +/* Constant values */ +#define HG_ADDR_NULL ((hg_addr_t)0) +#define HG_HANDLE_NULL ((hg_handle_t)0) +#define HG_BULK_NULL ((hg_bulk_t)0) +#define HG_PROC_NULL ((hg_proc_t)0) +#define HG_OP_ID_NULL ((hg_op_id_t)0) +#define HG_OP_ID_IGNORE ((hg_op_id_t *)1) + +#endif /* MERCURY_TYPES_H */ diff --git a/src/mercury/include/mercury_util.h b/src/mercury/include/mercury_util.h new file mode 100644 index 0000000..1e36e26 --- /dev/null +++ b/src/mercury/include/mercury_util.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2013-2020 Argonne National Laboratory, Department of Energy, + * UChicago Argonne, LLC and The HDF Group. + * All rights reserved. + * + * The full copyright notice, including terms governing use, modification, + * and redistribution, is contained in the COPYING file that can be + * found at the root of the source code distribution tree. + */ + +#ifndef MERCURY_UTIL_LOG_H +#define MERCURY_UTIL_LOG_H + +#include "mercury_util_config.h" + +/*************************************/ +/* Public Type and Struct Definition */ +/*************************************/ + +/*****************/ +/* Public Macros */ +/*****************/ + +/*********************/ +/* Public Prototypes */ +/*********************/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Set the log level for HG util. That setting is valid for all HG classes. + * + * \param level [IN] level string, valid values are: + * "none", "error", "warning", "debug" + */ +HG_UTIL_PUBLIC void HG_Util_set_log_level(const char *level); + +#ifdef __cplusplus +} +#endif + +#endif /* MERCURY_UTIL_LOG_H */ diff --git a/src/mercury/include/mercury_util_config.h b/src/mercury/include/mercury_util_config.h new file mode 100644 index 0000000..8237b4d --- /dev/null +++ b/src/mercury/include/mercury_util_config.h @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2013-2020 Argonne National Laboratory, Department of Energy, + * UChicago Argonne, LLC and The HDF Group. + * All rights reserved. + * + * The full copyright notice, including terms governing use, modification, + * and redistribution, is contained in the COPYING file that can be + * found at the root of the source code distribution tree. + */ + +/* Generated file. Only edit mercury_util_config.h.in. */ + +#ifndef MERCURY_UTIL_CONFIG_H +#define MERCURY_UTIL_CONFIG_H + +/*************************************/ +/* Public Type and Struct Definition */ +/*************************************/ + +/* Type definitions */ +#ifdef _WIN32 +typedef signed __int64 hg_util_int64_t; +typedef signed __int32 hg_util_int32_t; +typedef signed __int16 hg_util_int16_t; +typedef signed __int8 hg_util_int8_t; +typedef unsigned __int64 hg_util_uint64_t; +typedef unsigned __int32 hg_util_uint32_t; +typedef unsigned __int16 hg_util_uint16_t; +typedef unsigned __int8 hg_util_uint8_t; +#else +#include <stddef.h> +#include <stdint.h> +typedef int64_t hg_util_int64_t; +typedef int32_t hg_util_int32_t; +typedef int16_t hg_util_int16_t; +typedef int8_t hg_util_int8_t; +typedef uint64_t hg_util_uint64_t; +typedef uint32_t hg_util_uint32_t; +typedef uint16_t hg_util_uint16_t; +typedef uint8_t hg_util_uint8_t; +#endif +typedef hg_util_uint8_t hg_util_bool_t; +typedef hg_util_uint64_t hg_util_ptr_t; + +/* True / false */ +#define HG_UTIL_TRUE 1 +#define HG_UTIL_FALSE 0 + +/* Return codes */ +#define HG_UTIL_SUCCESS 0 +#define HG_UTIL_FAIL -1 + +/*****************/ +/* Public Macros */ +/*****************/ + +/* Visibility of symbols */ +#if defined(_WIN32) +#define HG_UTIL_ABI_IMPORT __declspec(dllimport) +#define HG_UTIL_ABI_EXPORT __declspec(dllexport) +#define HG_UTIL_ABI_HIDDEN +#elif defined(__GNUC__) && (__GNUC__ >= 4) +#define HG_UTIL_ABI_IMPORT __attribute__((visibility("default"))) +#define HG_UTIL_ABI_EXPORT __attribute__((visibility("default"))) +#define HG_UTIL_ABI_HIDDEN __attribute__((visibility("hidden"))) +#else +#define HG_UTIL_ABI_IMPORT +#define HG_UTIL_ABI_EXPORT +#define HG_UTIL_ABI_HIDDEN +#endif + +/* Inline macro */ +#ifdef _WIN32 +#define HG_UTIL_INLINE __inline +#else +#define HG_UTIL_INLINE __inline__ +#endif + +/* Check format arguments */ +#if defined(__GNUC__) +#define HG_UTIL_PRINTF_LIKE(_fmt, _firstarg) __attribute__((format(printf, _fmt, _firstarg))) +#else +#define HG_UTIL_PRINTF_LIKE(_fmt, _firstarg) +#endif + +/* Shared libraries */ +/* #undef HG_UTIL_BUILD_SHARED_LIBS */ +#ifdef HG_UTIL_BUILD_SHARED_LIBS +#ifdef mercury_util_EXPORTS +#define HG_UTIL_PUBLIC HG_UTIL_ABI_EXPORT +#else +#define HG_UTIL_PUBLIC HG_UTIL_ABI_IMPORT +#endif +#define HG_UTIL_PRIVATE HG_UTIL_ABI_HIDDEN +#else +#define HG_UTIL_PUBLIC +#define HG_UTIL_PRIVATE +#endif + +/* Define if has __attribute__((constructor)) */ +#define HG_UTIL_HAS_ATTR_CONSTRUCTOR + +/* Define if has __attribute__((constructor(priority))) */ +#define HG_UTIL_HAS_ATTR_CONSTRUCTOR_PRIORITY + +/* Define if has 'clock_gettime()' */ +#define HG_UTIL_HAS_CLOCK_GETTIME + +/* Define if has CLOCK_MONOTONIC_COARSE */ +#define HG_UTIL_HAS_CLOCK_MONOTONIC_COARSE + +/* Define is has debug */ +/* #undef HG_UTIL_HAS_DEBUG */ + +/* Define if has eventfd_t type */ +#define HG_UTIL_HAS_EVENTFD_T + +/* Define if has colored output */ +/* #undef HG_UTIL_HAS_LOG_COLOR */ + +/* Define if has <opa_primitives.h> */ +/* #undef HG_UTIL_HAS_OPA_PRIMITIVES_H */ + +/* Define if has 'pthread_condattr_setclock()' */ +#define HG_UTIL_HAS_PTHREAD_CONDATTR_SETCLOCK + +/* Define if has PTHREAD_MUTEX_ADAPTIVE_NP */ +#define HG_UTIL_HAS_PTHREAD_MUTEX_ADAPTIVE_NP + +/* Define if has pthread_spinlock_t type */ +#define HG_UTIL_HAS_PTHREAD_SPINLOCK_T + +/* Define if has <stdatomic.h> */ +#define HG_UTIL_HAS_STDATOMIC_H + +/* Define type size of atomic_long */ +#define HG_UTIL_ATOMIC_LONG_WIDTH 8 + +/* Define if has <sys/epoll.h> */ +#define HG_UTIL_HAS_SYSEPOLL_H + +/* Define if has <sys/event.h> */ +/* #undef HG_UTIL_HAS_SYSEVENT_H */ + +/* Define if has <sys/eventfd.h> */ +#define HG_UTIL_HAS_SYSEVENTFD_H + +/* Define if has <sys/time.h> */ +#define HG_UTIL_HAS_SYSTIME_H + +/* Define if has <time.h> */ +#define HG_UTIL_HAS_TIME_H + +#endif /* MERCURY_UTIL_CONFIG_H */ diff --git a/src/mercury/include/na.h b/src/mercury/include/na.h new file mode 100644 index 0000000..88b97f8 --- /dev/null +++ b/src/mercury/include/na.h @@ -0,0 +1,1064 @@ +/* + * Copyright (C) 2013-2020 Argonne National Laboratory, Department of Energy, + * UChicago Argonne, LLC and The HDF Group. + * All rights reserved. + * + * The full copyright notice, including terms governing use, modification, + * and redistribution, is contained in the COPYING file that can be + * found at the root of the source code distribution tree. + */ + +#ifndef NA_H +#define NA_H + +#include "na_types.h" + +/*************************************/ +/* Public Type and Struct Definition */ +/*************************************/ + +/* See na_types.h */ + +/*****************/ +/* Public Macros */ +/*****************/ + +/* See na_types.h */ + +/*********************/ +/* Public Prototypes */ +/*********************/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Initialize the network abstraction layer. + * Must be finalized with NA_Finalize(). + * + * \param info_string [IN] host address with port number (e.g., + * "tcp://localhost:3344" or + * "bmi+tcp://localhost:3344") + * \param listen [IN] listen for incoming connections + * + * \return Pointer to NA class or NULL in case of failure + */ +NA_PUBLIC na_class_t *NA_Initialize(const char *info_string, na_bool_t listen) NA_WARN_UNUSED_RESULT; + +/** + * Initialize the network abstraction layer with options provided by init_info. + * Must be finalized with NA_Finalize(). + * + * \param info_string [IN] host address with port number (e.g., + * "tcp://localhost:3344" or + * "bmi+tcp://localhost:3344") + * \param listen [IN] listen for incoming connections + * \param na_init_info [IN] (Optional) NA init info, NULL if no info + * + * \return Pointer to NA class or NULL in case of failure + */ +NA_PUBLIC na_class_t *NA_Initialize_opt(const char *info_string, na_bool_t listen, + const struct na_init_info *na_init_info) NA_WARN_UNUSED_RESULT; + +/** + * Finalize the network abstraction layer. + * + * \param na_class [IN/OUT] pointer to NA class + * + * \return NA_SUCCESS or corresponding NA error code + */ +NA_PUBLIC na_return_t NA_Finalize(na_class_t *na_class); + +/** + * Clean up all temporary files that were created in previous NA instances. + * While temporary resources (e.g., tmp files) are cleaned up on a call + * to NA_Finalize(), this routine gives a chance to programs that terminate + * abnormally to easily clean up those resources. This includes instances + * from all plugins. + */ +NA_PUBLIC void NA_Cleanup(void); + +/** + * Set the log level for NA. That setting is valid for all NA classes. + * + * \param level [IN] level string, valid values are: + * "none", "error", "warning", "debug" + */ +NA_PUBLIC void NA_Set_log_level(const char *level); + +/** + * Return the name of the NA class. + * + * \param na_class [IN] pointer to NA class + * + * \return Pointer to NA class name or NULL in case of failure + */ +static NA_INLINE const char *NA_Get_class_name(const na_class_t *na_class) NA_WARN_UNUSED_RESULT; + +/** + * Return the protocol of the NA class. + * + * \param na_class [IN] pointer to NA class + * + * \return Pointer to NA class protocol or NULL in case of failure + */ +static NA_INLINE const char *NA_Get_class_protocol(const na_class_t *na_class) NA_WARN_UNUSED_RESULT; + +/** + * Test whether class is listening or not. + * + * \param na_class [IN] pointer to NA class + * + * \return NA_TRUE if listening or NA_FALSE if not + */ +static NA_INLINE na_bool_t NA_Is_listening(const na_class_t *na_class) NA_WARN_UNUSED_RESULT; + +/** + * Create a new context. + * + * \param na_class [IN/OUT] pointer to NA class + * + * \return Pointer to NA context or NULL in case of failure + */ +NA_PUBLIC na_context_t *NA_Context_create(na_class_t *na_class) NA_WARN_UNUSED_RESULT; + +/** + * Create a new context with a specific ID. + * + * \param na_class [IN/OUT] pointer to NA class + * \param id [IN] context ID + * + * \return Pointer to NA context or NULL in case of failure + */ +NA_PUBLIC na_context_t *NA_Context_create_id(na_class_t *na_class, na_uint8_t id) NA_WARN_UNUSED_RESULT; + +/** + * Destroy a context created by using NA_Context_create(). + * + * \param na_class [IN/OUT] pointer to NA class + * \param context [IN/OUT] pointer to context of execution + * + * \return NA_SUCCESS or corresponding NA error code + */ +NA_PUBLIC na_return_t NA_Context_destroy(na_class_t *na_class, na_context_t *context); + +/** + * Allocate an operation ID for the higher level layer to save and + * pass back to the NA layer rather than have the NA layer allocate operation + * IDs all the time. + * Allocating an operation ID gives ownership of that ID to the higher level + * layer, hence it must be explicitly released with NA_Op_destroy() when it + * is no longer needed. + * + * \param na_class [IN/OUT] pointer to NA class + * + * \return valid pointer to operation ID or NULL + */ +NA_PUBLIC na_op_id_t *NA_Op_create(na_class_t *na_class); + +/** + * Destroy operation ID created with NA_Op_create(). + * Reference counting prevents involuntary free. + * + * \param na_class [IN/OUT] pointer to NA class + * \param op_id [IN] pointer to operation ID + * + * \return NA_SUCCESS or corresponding NA error code + */ +NA_PUBLIC na_return_t NA_Op_destroy(na_class_t *na_class, na_op_id_t *op_id); + +/** + * Lookup an addr from a peer address/name. Addresses need to be + * freed by calling NA_Addr_free(). + * + * \param na_class [IN/OUT] pointer to NA class + * \param name [IN] lookup name + * \param addr [OUT] pointer to abstract address + * + * \return NA_SUCCESS or corresponding NA error code + */ +NA_PUBLIC na_return_t NA_Addr_lookup(na_class_t *na_class, const char *name, na_addr_t *addr); + +/** + * Free the addr from the list of peers. + * + * \param na_class [IN/OUT] pointer to NA class + * \param addr [IN] abstract address + * + * \return NA_SUCCESS or corresponding NA error code + */ +NA_PUBLIC na_return_t NA_Addr_free(na_class_t *na_class, na_addr_t addr); + +/** + * Hint that the address is no longer valid. This may happen if the peer is + * no longer responding. This can be used to force removal of the + * peer address from the list of the peers, before freeing it and reclaim + * resources. + * + * \param na_class [IN/OUT] pointer to NA class + * \param addr [IN] abstract address + * + * \return NA_SUCCESS or corresponding NA error code + */ +NA_PUBLIC na_return_t NA_Addr_set_remove(na_class_t *na_class, na_addr_t addr); + +/** + * Access self address. + * + * \param na_class [IN/OUT] pointer to NA class + * \param addr [OUT] pointer to abstract address + * + * \return NA_SUCCESS or corresponding NA error code + */ +NA_PUBLIC na_return_t NA_Addr_self(na_class_t *na_class, na_addr_t *addr); + +/** + * Duplicate an existing NA abstract address. The duplicated address can be + * stored for later use and the origin address be freed safely. The duplicated + * address must be freed with NA_Addr_free(). + * + * \param na_class [IN/OUT] pointer to NA class + * \param addr [IN] abstract address + * \param new_addr [OUT] pointer to abstract address + * + * \return NA_SUCCESS or corresponding NA error code + */ +NA_PUBLIC na_return_t NA_Addr_dup(na_class_t *na_class, na_addr_t addr, na_addr_t *new_addr); + +/** + * Compare two addresses. + * + * \param na_class [IN/OUT] pointer to NA class + * \param addr1 [IN] abstract address + * \param addr2 [IN] abstract address + * + * \return NA_TRUE if addresses are determined to be equal, NA_FALSE otherwise + */ +NA_PUBLIC na_bool_t NA_Addr_cmp(na_class_t *na_class, na_addr_t addr1, na_addr_t addr2); + +/** + * Test whether address is self or not. + * + * \param na_class [IN/OUT] pointer to NA class + * \param addr [IN] abstract address + * + * \return NA_TRUE if self or NA_FALSE if not + */ +static NA_INLINE na_bool_t NA_Addr_is_self(na_class_t *na_class, na_addr_t addr); + +/** + * Convert an addr to a string (returned string includes the terminating + * null byte '\0'). If buf is NULL, the address is not converted and only + * the required size of the buffer is returned. If the input value passed + * through buf_size is too small, NA_OVERFLOW is returned and the buf_size + * output is set to the minimum size required. + * + * \param na_class [IN/OUT] pointer to NA class + * \param buf [IN/OUT] pointer to destination buffer + * \param buf_size [IN/OUT] pointer to buffer size + * \param addr [IN] abstract address + * + * \return NA_SUCCESS or corresponding NA error code + */ +NA_PUBLIC na_return_t NA_Addr_to_string(na_class_t *na_class, char *buf, na_size_t *buf_size, na_addr_t addr); + +/** + * Get size required to serialize address. + * + * \param na_class [IN/OUT] pointer to NA class + * \param addr [IN] abstract address + * + * \return Non-negative value + */ +static NA_INLINE na_size_t NA_Addr_get_serialize_size(na_class_t *na_class, + na_addr_t addr) NA_WARN_UNUSED_RESULT; + +/** + * Serialize address into a buffer. + * + * \param na_class [IN/OUT] pointer to NA class + * \param buf [IN/OUT] pointer to buffer used for serialization + * \param buf_size [IN] buffer size + * \param addr [IN] abstract address + * + * \return NA_SUCCESS or corresponding NA error code + */ +NA_PUBLIC na_return_t NA_Addr_serialize(na_class_t *na_class, void *buf, na_size_t buf_size, na_addr_t addr); + +/** + * Deserialize address from a buffer. The returned address must be freed with + * NA_Addr_free(). + * + * \param na_class [IN/OUT] pointer to NA class + * \param addr [OUT] pointer to abstract address + * \param buf [IN] pointer to buffer used for deserialization + * \param buf_size [IN] buffer size + * + * \return NA_SUCCESS or corresponding NA error code + */ +NA_PUBLIC na_return_t NA_Addr_deserialize(na_class_t *na_class, na_addr_t *addr, const void *buf, + na_size_t buf_size); + +/** + * Get the maximum size of messages supported by unexpected send/recv. + * Small message size. + * + * \param na_class [IN] pointer to NA class + * + * \return Non-negative value + */ +static NA_INLINE na_size_t NA_Msg_get_max_unexpected_size(const na_class_t *na_class) NA_WARN_UNUSED_RESULT; + +/** + * Get the maximum size of messages supported by expected send/recv. + * Small message size that may differ from the unexpected message size. + * + * \param na_class [IN] pointer to NA class + * + * \return Non-negative value + */ +static NA_INLINE na_size_t NA_Msg_get_max_expected_size(const na_class_t *na_class) NA_WARN_UNUSED_RESULT; + +/** + * Get the header size for unexpected messages. Plugins may use that header + * to encode specific information (such as source addr, etc). + * + * \param na_class [IN] pointer to NA class + * + * \return Non-negative value + */ +static NA_INLINE na_size_t NA_Msg_get_unexpected_header_size(const na_class_t *na_class) + NA_WARN_UNUSED_RESULT; + +/** + * Get the header size for expected messages. Plugins may use that header + * to encode specific information. + * + * \param na_class [IN] pointer to NA class + * + * \return Non-negative value + */ +static NA_INLINE na_size_t NA_Msg_get_expected_header_size(const na_class_t *na_class) NA_WARN_UNUSED_RESULT; + +/** + * Get the maximum tag value that can be used by send/recv (both expected and + * unexpected). + * + * \param na_class [IN] pointer to NA class + * + * \return Non-negative value + */ +static NA_INLINE na_tag_t NA_Msg_get_max_tag(const na_class_t *na_class) NA_WARN_UNUSED_RESULT; + +/** + * Allocate buf_size bytes and return a pointer to the allocated memory. + * If size is 0, NA_Msg_buf_alloc() returns NULL. The plugin_data output + * parameter can be used by the underlying plugin implementation to store + * internal memory information. + * + * \param na_class [IN/OUT] pointer to NA class + * \param buf_size [IN] buffer size + * \param plugin_data [OUT] pointer to internal plugin data + * + * \return Pointer to allocated memory or NULL in case of failure + */ +NA_PUBLIC void *NA_Msg_buf_alloc(na_class_t *na_class, na_size_t buf_size, + void **plugin_data) NA_WARN_UNUSED_RESULT; + +/** + * The NA_Msg_buf_free() function releases the memory space pointed to by buf, + * which must have been returned by a previous call to NA_Msg_buf_alloc(). + * If buf is NULL, no operation is performed. + * + * \param na_class [IN/OUT] pointer to NA class + * \param buf [IN] pointer to buffer + * \param plugin_data [IN] pointer to internal plugin data + * + * \return NA_SUCCESS or corresponding NA error code + */ +NA_PUBLIC na_return_t NA_Msg_buf_free(na_class_t *na_class, void *buf, void *plugin_data); + +/** + * Initialize a buffer so that it can be safely passed to the + * NA_Msg_send_unexpected() call. In the case the underlying plugin adds its + * own header to that buffer, the header will be written at this time and the + * usable buffer payload will be buf + NA_Msg_get_unexpected_header_size(). + * + * \param na_class [IN/OUT] pointer to NA class + * \param buf [IN] pointer to buffer + * \param buf_size [IN] buffer size + * + * \return NA_SUCCESS or corresponding NA error code + */ +NA_PUBLIC na_return_t NA_Msg_init_unexpected(na_class_t *na_class, void *buf, na_size_t buf_size); + +/** + * Send an unexpected message to dest_addr. Unexpected sends do not require a + * matching receive to complete. After completion, the user callback is + * placed into the context completion queue and can be triggered using + * NA_Trigger(). + * The plugin_data parameter returned from the NA_Msg_buf_alloc() call must + * be passed along with the buffer, it allows plugins to store and retrieve + * additional buffer information such as memory descriptors. + * \remark Note also that unexpected messages do not require an unexpected + * receive to be posted at the destination before sending the message and the + * destination is allowed to drop the message without notification. However, + * in general, NA plugins are encouraged to remain reliable to avoid unnecessary + * timeouts and cancelations. + * + * Users must manually create an operation ID through NA_Op_create() and pass + * it through op_id for future use and prevent multiple ID creation. + * + * \param na_class [IN/OUT] pointer to NA class + * \param context [IN/OUT] pointer to context of execution + * \param callback [IN] pointer to function callback + * \param arg [IN] pointer to data passed to callback + * \param buf [IN] pointer to send buffer + * \param buf_size [IN] buffer size + * \param plugin_data [IN] pointer to internal plugin data + * \param dest_addr [IN] abstract address of destination + * \param dest_id [IN] destination context ID + * \param tag [IN] tag attached to message + * \param op_id [IN/OUT] pointer to operation ID + * + * \return NA_SUCCESS or corresponding NA error code + */ +static NA_INLINE na_return_t NA_Msg_send_unexpected(na_class_t *na_class, na_context_t *context, + na_cb_t callback, void *arg, const void *buf, + na_size_t buf_size, void *plugin_data, + na_addr_t dest_addr, na_uint8_t dest_id, na_tag_t tag, + na_op_id_t *op_id); + +/** + * Receive an unexpected message. Unexpected receives may wait on any tag and + * any source depending on the implementation. After completion, the user + * callback parameter is placed into the context completion queue and can be + * triggered using NA_Trigger(). + * The plugin_data parameter returned from the NA_Msg_buf_alloc() call must + * be passed along with the buffer, it allows plugins to store and retrieve + * additional buffer information such as memory descriptors. + * + * Users must manually create an operation ID through NA_Op_create() and pass + * it through op_id for future use and prevent multiple ID creation. + * + * \param na_class [IN/OUT] pointer to NA class + * \param context [IN/OUT] pointer to context of execution + * \param callback [IN] pointer to function callback + * \param arg [IN] pointer to data passed to callback + * \param buf [IN] pointer to send buffer + * \param buf_size [IN] buffer size + * \param plugin_data [IN] pointer to internal plugin data + * \param op_id [IN/OUT] pointer to operation ID + * + * \return NA_SUCCESS or corresponding NA error code + */ +static NA_INLINE na_return_t NA_Msg_recv_unexpected(na_class_t *na_class, na_context_t *context, + na_cb_t callback, void *arg, void *buf, + na_size_t buf_size, void *plugin_data, na_op_id_t *op_id); + +/** + * Initialize a buffer so that it can be safely passed to the + * NA_Msg_send_expected() call. In the case the underlying plugin adds its + * own header to that buffer, the header will be written at this time and the + * usable buffer payload will be buf + NA_Msg_get_expected_header_size(). + * + * \param na_class [IN/OUT] pointer to NA class + * \param buf [IN] pointer to buffer + * \param buf_size [IN] buffer size + * + * \return NA_SUCCESS or corresponding NA error code + */ +NA_PUBLIC na_return_t NA_Msg_init_expected(na_class_t *na_class, void *buf, na_size_t buf_size); + +/** + * Send an expected message to dest_addr. After completion, the user callback is + * placed into the context completion queue and can be triggered using + * NA_Trigger(). + * The plugin_data parameter returned from the NA_Msg_buf_alloc() call must + * be passed along with the buffer, it allows plugins to store and retrieve + * additional buffer information such as memory descriptors. + * \remark Note that expected messages require an expected receive to be posted + * at the destination before sending the message, otherwise the destination is + * allowed to drop the message without notification. + * + * Users must manually create an operation ID through NA_Op_create() and pass + * it through op_id for future use and prevent multiple ID creation. + * + * \param na_class [IN/OUT] pointer to NA class + * \param context [IN/OUT] pointer to context of execution + * \param callback [IN] pointer to function callback + * \param arg [IN] pointer to data passed to callback + * \param buf [IN] pointer to send buffer + * \param buf_size [IN] buffer size + * \param plugin_data [IN] pointer to internal plugin data + * \param dest_addr [IN] abstract address of destination + * \param dest_id [IN] destination context ID + * \param tag [IN] tag attached to message + * \param op_id [IN/OUT] pointer to operation ID + * + * \return NA_SUCCESS or corresponding NA error code + */ +static NA_INLINE na_return_t NA_Msg_send_expected(na_class_t *na_class, na_context_t *context, + na_cb_t callback, void *arg, const void *buf, + na_size_t buf_size, void *plugin_data, na_addr_t dest_addr, + na_uint8_t dest_id, na_tag_t tag, na_op_id_t *op_id); + +/** + * Receive an expected message from source_addr. After completion, the user + * callback is placed into the context completion queue and can be triggered + * using NA_Trigger(). + * The plugin_data parameter returned from the NA_Msg_buf_alloc() call must + * be passed along with the buffer, it allows plugins to store and retrieve + * additional buffer information such as memory descriptors. + * + * Users must manually create an operation ID through NA_Op_create() and pass + * it through op_id for future use and prevent multiple ID creation. + * + * \param na_class [IN/OUT] pointer to NA class + * \param context [IN/OUT] pointer to context of execution + * \param callback [IN] pointer to function callback + * \param arg [IN] pointer to data passed to callback + * \param buf [IN] pointer to receive buffer + * \param buf_size [IN] buffer size + * \param plugin_data [IN] pointer to internal plugin data + * \param source_addr [IN] abstract address of source + * \param source_id [IN] source context ID + * \param tag [IN] matching tag used to receive message + * \param op_id [IN/OUT] pointer to operation ID + * + * \return NA_SUCCESS or corresponding NA error code + */ +static NA_INLINE na_return_t NA_Msg_recv_expected(na_class_t *na_class, na_context_t *context, + na_cb_t callback, void *arg, void *buf, na_size_t buf_size, + void *plugin_data, na_addr_t source_addr, + na_uint8_t source_id, na_tag_t tag, na_op_id_t *op_id); + +/** + * Create memory handle for RMA operations. + * For non-contiguous memory, use NA_Mem_handle_create_segments() instead. + * + * \remark Note to plugin developers: NA_Mem_handle_create() may be called + * multiple times on the same memory region. + * + * \param na_class [IN/OUT] pointer to NA class + * \param buf [IN] pointer to buffer that needs to be registered + * \param buf_size [IN] buffer size + * \param flags [IN] permission flag: + * - NA_MEM_READWRITE + * - NA_MEM_READ_ONLY + * \param mem_handle [OUT] pointer to returned abstract memory handle + * + * \return NA_SUCCESS or corresponding NA error code + */ +NA_PUBLIC na_return_t NA_Mem_handle_create(na_class_t *na_class, void *buf, na_size_t buf_size, + unsigned long flags, na_mem_handle_t *mem_handle); + +/** + * Create memory handle for RMA operations. + * Create_segments can be used to register scatter-gather lists and get a single + * memory handle. + * \remark Implemented only if the network transport or hardware supports it. + * + * \param na_class [IN/OUT] pointer to NA class + * \param segments [IN] pointer to array of segments composed of: + * - address of the segment that needs to be + * registered + * - size of the segment in bytes + * \param segment_count [IN] segment count + * \param flags [IN] permission flag: + * - NA_MEM_READWRITE + * - NA_MEM_READ_ONLY + * \param mem_handle [OUT] pointer to returned abstract memory handle + * + * \return NA_SUCCESS or corresponding NA error code + */ +NA_PUBLIC na_return_t NA_Mem_handle_create_segments(na_class_t *na_class, struct na_segment *segments, + na_size_t segment_count, unsigned long flags, + na_mem_handle_t *mem_handle); + +/** + * Free memory handle. + * + * \param na_class [IN/OUT] pointer to NA class + * \param mem_handle [IN] abstract memory handle + * + * \return NA_SUCCESS or corresponding NA error code + */ +NA_PUBLIC na_return_t NA_Mem_handle_free(na_class_t *na_class, na_mem_handle_t mem_handle); + +/** + * Get the maximum segment count that can be passed to + * NA_Mem_handle_create_segments(). + * + * \param na_class [IN] pointer to NA class + * + * \return Non-negative value + */ +static NA_INLINE na_size_t NA_Mem_handle_get_max_segments(const na_class_t *na_class) NA_WARN_UNUSED_RESULT; + +/** + * Register memory for RMA operations. + * Memory pieces must be registered before one-sided transfers can be + * initiated. + * + * \param na_class [IN/OUT] pointer to NA class + * \param mem_handle [IN] pointer to abstract memory handle + * + * \return NA_SUCCESS or corresponding NA error code + */ +NA_PUBLIC na_return_t NA_Mem_register(na_class_t *na_class, na_mem_handle_t mem_handle); + +/** + * Unregister memory. + * + * \param na_class [IN/OUT] pointer to NA class + * \param mem_handle [IN] abstract memory handle + * + * \return NA_SUCCESS or corresponding NA error code + */ +NA_PUBLIC na_return_t NA_Mem_deregister(na_class_t *na_class, na_mem_handle_t mem_handle); + +/** + * Get size required to serialize handle. + * + * \param na_class [IN/OUT] pointer to NA class + * \param mem_handle [IN] abstract memory handle + * + * \return Non-negative value + */ +static NA_INLINE na_size_t NA_Mem_handle_get_serialize_size(na_class_t * na_class, + na_mem_handle_t mem_handle) NA_WARN_UNUSED_RESULT; + +/** + * Serialize memory handle into a buffer. + * One-sided transfers require prior exchange of memory handles between + * peers, serialization callbacks can be used to "pack" a memory handle and + * send it across the network. + * \remark Memory handles can be variable size, therefore the space required + * to serialize a handle into a buffer can be obtained using + * NA_Mem_handle_get_serialize_size(). + * + * \param na_class [IN/OUT] pointer to NA class + * \param buf [IN/OUT] pointer to buffer used for serialization + * \param buf_size [IN] buffer size + * \param mem_handle [IN] abstract memory handle + * + * \return NA_SUCCESS or corresponding NA error code + */ +NA_PUBLIC na_return_t NA_Mem_handle_serialize(na_class_t *na_class, void *buf, na_size_t buf_size, + na_mem_handle_t mem_handle); + +/** + * Deserialize memory handle from buffer. + * + * \param na_class [IN/OUT] pointer to NA class + * \param mem_handle [OUT] pointer to abstract memory handle + * \param buf [IN] pointer to buffer used for deserialization + * \param buf_size [IN] buffer size + * + * \return NA_SUCCESS or corresponding NA error code + */ +NA_PUBLIC na_return_t NA_Mem_handle_deserialize(na_class_t *na_class, na_mem_handle_t *mem_handle, + const void *buf, na_size_t buf_size); + +/** + * Put data to remote address. + * Initiate a put to the registered memory regions with the given offset/size. + * After completion, the user callback is placed into a completion queue and + * can be triggered using NA_Trigger(). + * \remark Memory must be registered and handles exchanged between peers. + * + * Users must manually create an operation ID through NA_Op_create() and pass + * it through op_id for future use and prevent multiple ID creation. + * + * \param na_class [IN/OUT] pointer to NA class + * \param context [IN/OUT] pointer to context of execution + * \param callback [IN] pointer to function callback + * \param arg [IN] pointer to data passed to callback + * \param local_mem_handle [IN] abstract local memory handle + * \param local_offset [IN] local offset + * \param remote_mem_handle [IN] abstract remote memory handle + * \param remote_offset [IN] remote offset + * \param data_size [IN] size of data that needs to be transferred + * \param remote_addr [IN] abstract address of remote destination + * \param remote_id [IN] target ID of remote destination + * \param op_id [IN/OUT] pointer to operation ID + * + * \return NA_SUCCESS or corresponding NA error code + */ +static NA_INLINE na_return_t NA_Put(na_class_t *na_class, na_context_t *context, na_cb_t callback, void *arg, + na_mem_handle_t local_mem_handle, na_offset_t local_offset, + na_mem_handle_t remote_mem_handle, na_offset_t remote_offset, + na_size_t data_size, na_addr_t remote_addr, na_uint8_t remote_id, + na_op_id_t *op_id); + +/** + * Get data from remote address. + * Initiate a get to the registered memory regions with the given offset/size. + * After completion, the user callback is placed into a completion queue and + * can be triggered using NA_Trigger(). + * + * Users must manually create an operation ID through NA_Op_create() and pass + * it through op_id for future use and prevent multiple ID creation. + * + * \param na_class [IN/OUT] pointer to NA class + * \param context [IN/OUT] pointer to context of execution + * \param callback [IN] pointer to function callback + * \param arg [IN] pointer to data passed to callback + * \param local_mem_handle [IN] abstract local memory handle + * \param local_offset [IN] local offset + * \param remote_mem_handle [IN] abstract remote memory handle + * \param remote_offset [IN] remote offset + * \param data_size [IN] size of data that needs to be transferred + * \param remote_addr [IN] abstract address of remote source + * \param remote_id [IN] target ID of remote source + * \param op_id [IN/OUT] pointer to operation ID + * + * \return NA_SUCCESS or corresponding NA error code + */ +static NA_INLINE na_return_t NA_Get(na_class_t *na_class, na_context_t *context, na_cb_t callback, void *arg, + na_mem_handle_t local_mem_handle, na_offset_t local_offset, + na_mem_handle_t remote_mem_handle, na_offset_t remote_offset, + na_size_t data_size, na_addr_t remote_addr, na_uint8_t remote_id, + na_op_id_t *op_id); + +/** + * Retrieve file descriptor from NA plugin when supported. The descriptor + * can be used by upper layers for manual polling through the usual + * OS select/poll/epoll calls. + * + * \param na_class [IN/OUT] pointer to NA class + * \param context [IN/OUT] pointer to context of execution + * + * \return Non-negative integer if supported, 0 if not implemented and negative + * in case of error. + */ +static NA_INLINE int NA_Poll_get_fd(na_class_t *na_class, na_context_t *context) NA_WARN_UNUSED_RESULT; + +/** + * Used to signal when it is safe to block on the class/context poll descriptor + * or if there is already work that can be progressed. + * + * \param na_class [IN/OUT] pointer to NA class + * \param context [IN/OUT] pointer to context of execution + * + * \return NA_TRUE if it is safe to block or NA_FALSE otherwise + */ +NA_PUBLIC na_bool_t NA_Poll_try_wait(na_class_t *na_class, na_context_t *context); + +/** + * Try to progress communication for at most timeout until timeout is reached or + * any completion has occurred. + * Progress should not be considered as wait, in the sense that it cannot be + * assumed that completion of a specific operation will occur only when + * progress is called. + * + * \param na_class [IN/OUT] pointer to NA class + * \param context [IN/OUT] pointer to context of execution + * \param timeout [IN] timeout (in milliseconds) + * + * \return NA_SUCCESS if any completion has occurred / NA error code otherwise + */ +NA_PUBLIC na_return_t NA_Progress(na_class_t *na_class, na_context_t *context, unsigned int timeout); + +/** + * Execute at most max_count callbacks. If timeout is non-zero, wait up to + * timeout before returning. Function can return when at least one or more + * callbacks are triggered (at most max_count). + * + * \param context [IN/OUT] pointer to context of execution + * \param timeout [IN] timeout (in milliseconds) + * \param max_count [IN] maximum number of callbacks triggered + * \param callback_ret [IN/OUT] array of callback return values + * \param actual_count [OUT] actual number of callbacks triggered + * + * \return NA_SUCCESS or corresponding NA error code + */ +NA_PUBLIC na_return_t NA_Trigger(na_context_t *context, unsigned int timeout, unsigned int max_count, + int callback_ret[], unsigned int *actual_count); + +/** + * Cancel an ongoing operation. + * + * \param na_class [IN/OUT] pointer to NA class + * \param context [IN/OUT] pointer to context of execution + * \param op_id [IN] pointer to operation ID + * + * \return NA_SUCCESS or corresponding NA error code + */ +NA_PUBLIC na_return_t NA_Cancel(na_class_t *na_class, na_context_t *context, na_op_id_t *op_id); + +/** + * Convert error return code to string (null terminated). + * + * \param errnum [IN] error return code + * + * \return String + */ +NA_PUBLIC const char *NA_Error_to_string(na_return_t errnum) NA_WARN_UNUSED_RESULT; + +/************************************/ +/* Local Type and Struct Definition */ +/************************************/ + +/* NA info definition */ +struct na_info { + char *class_name; /* Class name (e.g., bmi) */ + char *protocol_name; /* Protocol (e.g., tcp, ib) */ + char *host_name; /* Host (may be NULL in anonymous mode) */ + /* Additional init info (NULL if no info) */ + const struct na_init_info *na_init_info; +}; + +/* NA class definition */ +struct na_class { + const struct na_class_ops *ops; /* Class operations */ + void * plugin_class; /* Plugin private class */ + char * protocol_name; /* Name of protocol */ + na_uint32_t progress_mode; /* NA progress mode */ + na_bool_t listen; /* Listen for connections */ +}; + +/* NA context definition */ +struct na_context { + void *plugin_context; /* Plugin private context */ +}; + +/* NA plugin callbacks */ +struct na_class_ops { + const char *class_name; + na_bool_t (*check_protocol)(const char *protocol_name); + na_return_t (*initialize)(na_class_t *na_class, const struct na_info *na_info, na_bool_t listen); + na_return_t (*finalize)(na_class_t *na_class); + void (*cleanup)(void); + na_return_t (*context_create)(na_class_t *na_class, void **plugin_context, na_uint8_t id); + na_return_t (*context_destroy)(na_class_t *na_class, void *plugin_context); + na_op_id_t *(*op_create)(na_class_t *na_class); + na_return_t (*op_destroy)(na_class_t *na_class, na_op_id_t *op_id); + na_return_t (*addr_lookup)(na_class_t *na_class, const char *name, na_addr_t *addr); + na_return_t (*addr_free)(na_class_t *na_class, na_addr_t addr); + na_return_t (*addr_set_remove)(na_class_t *na_class, na_addr_t addr); + na_return_t (*addr_self)(na_class_t *na_class, na_addr_t *addr); + na_return_t (*addr_dup)(na_class_t *na_class, na_addr_t addr, na_addr_t *new_addr); + na_bool_t (*addr_cmp)(na_class_t *na_class, na_addr_t addr1, na_addr_t addr2); + na_bool_t (*addr_is_self)(na_class_t *na_class, na_addr_t addr); + na_return_t (*addr_to_string)(na_class_t *na_class, char *buf, na_size_t *buf_size, na_addr_t addr); + na_size_t (*addr_get_serialize_size)(na_class_t *na_class, na_addr_t addr); + na_return_t (*addr_serialize)(na_class_t *na_class, void *buf, na_size_t buf_size, na_addr_t addr); + na_return_t (*addr_deserialize)(na_class_t *na_class, na_addr_t *addr, const void *buf, + na_size_t buf_size); + na_size_t (*msg_get_max_unexpected_size)(const na_class_t *na_class); + na_size_t (*msg_get_max_expected_size)(const na_class_t *na_class); + na_size_t (*msg_get_unexpected_header_size)(const na_class_t *na_class); + na_size_t (*msg_get_expected_header_size)(const na_class_t *na_class); + na_tag_t (*msg_get_max_tag)(const na_class_t *na_class); + void *(*msg_buf_alloc)(na_class_t *na_class, na_size_t buf_size, void **plugin_data); + na_return_t (*msg_buf_free)(na_class_t *na_class, void *buf, void *plugin_data); + na_return_t (*msg_init_unexpected)(na_class_t *na_class, void *buf, na_size_t buf_size); + na_return_t (*msg_send_unexpected)(na_class_t *na_class, na_context_t *context, na_cb_t callback, + void *arg, const void *buf, na_size_t buf_size, void *plugin_data, + na_addr_t dest_addr, na_uint8_t dest_id, na_tag_t tag, + na_op_id_t *op_id); + na_return_t (*msg_recv_unexpected)(na_class_t *na_class, na_context_t *context, na_cb_t callback, + void *arg, void *buf, na_size_t buf_size, void *plugin_data, + na_op_id_t *op_id); + na_return_t (*msg_init_expected)(na_class_t *na_class, void *buf, na_size_t buf_size); + na_return_t (*msg_send_expected)(na_class_t *na_class, na_context_t *context, na_cb_t callback, void *arg, + const void *buf, na_size_t buf_size, void *plugin_data, + na_addr_t dest_addr, na_uint8_t dest_id, na_tag_t tag, + na_op_id_t *op_id); + na_return_t (*msg_recv_expected)(na_class_t *na_class, na_context_t *context, na_cb_t callback, void *arg, + void *buf, na_size_t buf_size, void *plugin_data, na_addr_t source_addr, + na_uint8_t source_id, na_tag_t tag, na_op_id_t *op_id); + na_return_t (*mem_handle_create)(na_class_t *na_class, void *buf, na_size_t buf_size, unsigned long flags, + na_mem_handle_t *mem_handle); + na_return_t (*mem_handle_create_segments)(na_class_t *na_class, struct na_segment *segments, + na_size_t segment_count, unsigned long flags, + na_mem_handle_t *mem_handle); + na_return_t (*mem_handle_free)(na_class_t *na_class, na_mem_handle_t mem_handle); + na_size_t (*mem_handle_get_max_segments)(const na_class_t *na_class); + na_return_t (*mem_register)(na_class_t *na_class, na_mem_handle_t mem_handle); + na_return_t (*mem_deregister)(na_class_t *na_class, na_mem_handle_t mem_handle); + na_size_t (*mem_handle_get_serialize_size)(na_class_t *na_class, na_mem_handle_t mem_handle); + na_return_t (*mem_handle_serialize)(na_class_t *na_class, void *buf, na_size_t buf_size, + na_mem_handle_t mem_handle); + na_return_t (*mem_handle_deserialize)(na_class_t *na_class, na_mem_handle_t *mem_handle, const void *buf, + na_size_t buf_size); + na_return_t (*put)(na_class_t *na_class, na_context_t *context, na_cb_t callback, void *arg, + na_mem_handle_t local_mem_handle, na_offset_t local_offset, + na_mem_handle_t remote_mem_handle, na_offset_t remote_offset, na_size_t length, + na_addr_t remote_addr, na_uint8_t remote_id, na_op_id_t *op_id); + na_return_t (*get)(na_class_t *na_class, na_context_t *context, na_cb_t callback, void *arg, + na_mem_handle_t local_mem_handle, na_offset_t local_offset, + na_mem_handle_t remote_mem_handle, na_offset_t remote_offset, na_size_t length, + na_addr_t remote_addr, na_uint8_t remote_id, na_op_id_t *op_id); + int (*na_poll_get_fd)(na_class_t *na_class, na_context_t *context); + na_bool_t (*na_poll_try_wait)(na_class_t *na_class, na_context_t *context); + na_return_t (*progress)(na_class_t *na_class, na_context_t *context, unsigned int timeout); + na_return_t (*cancel)(na_class_t *na_class, na_context_t *context, na_op_id_t *op_id); +}; + +/*---------------------------------------------------------------------------*/ +static NA_INLINE const char * +NA_Get_class_name(const na_class_t *na_class) +{ + return na_class->ops->class_name; +} + +/*---------------------------------------------------------------------------*/ +static NA_INLINE const char * +NA_Get_class_protocol(const na_class_t *na_class) +{ + return na_class->protocol_name; +} + +/*---------------------------------------------------------------------------*/ +static NA_INLINE na_bool_t +NA_Is_listening(const na_class_t *na_class) +{ + return na_class->listen; +} + +/*---------------------------------------------------------------------------*/ +static NA_INLINE na_bool_t +NA_Addr_is_self(na_class_t *na_class, na_addr_t addr) +{ + return na_class->ops->addr_is_self(na_class, addr); +} + +/*---------------------------------------------------------------------------*/ +static NA_INLINE na_size_t +NA_Addr_get_serialize_size(na_class_t *na_class, na_addr_t addr) +{ + return (na_class->ops->addr_get_serialize_size) ? na_class->ops->addr_get_serialize_size(na_class, addr) + : 0; +} + +/*---------------------------------------------------------------------------*/ +static NA_INLINE na_size_t +NA_Msg_get_max_unexpected_size(const na_class_t *na_class) +{ + return na_class->ops->msg_get_max_unexpected_size(na_class); +} + +/*---------------------------------------------------------------------------*/ +static NA_INLINE na_size_t +NA_Msg_get_max_expected_size(const na_class_t *na_class) +{ + return na_class->ops->msg_get_max_expected_size(na_class); +} + +/*---------------------------------------------------------------------------*/ +static NA_INLINE na_size_t +NA_Msg_get_unexpected_header_size(const na_class_t *na_class) +{ + return (na_class->ops->msg_get_unexpected_header_size) + ? na_class->ops->msg_get_unexpected_header_size(na_class) + : 0; +} + +/*---------------------------------------------------------------------------*/ +static NA_INLINE na_size_t +NA_Msg_get_expected_header_size(const na_class_t *na_class) +{ + return (na_class->ops->msg_get_expected_header_size) + ? na_class->ops->msg_get_expected_header_size(na_class) + : 0; +} + +/*---------------------------------------------------------------------------*/ +static NA_INLINE na_tag_t +NA_Msg_get_max_tag(const na_class_t *na_class) +{ + return na_class->ops->msg_get_max_tag(na_class); +} + +/*---------------------------------------------------------------------------*/ +static NA_INLINE na_return_t +NA_Msg_send_unexpected(na_class_t *na_class, na_context_t *context, na_cb_t callback, void *arg, + const void *buf, na_size_t buf_size, void *plugin_data, na_addr_t dest_addr, + na_uint8_t dest_id, na_tag_t tag, na_op_id_t *op_id) +{ + return na_class->ops->msg_send_unexpected(na_class, context, callback, arg, buf, buf_size, plugin_data, + dest_addr, dest_id, tag, op_id); +} + +/*---------------------------------------------------------------------------*/ +static NA_INLINE na_return_t +NA_Msg_recv_unexpected(na_class_t *na_class, na_context_t *context, na_cb_t callback, void *arg, void *buf, + na_size_t buf_size, void *plugin_data, na_op_id_t *op_id) +{ + return na_class->ops->msg_recv_unexpected(na_class, context, callback, arg, buf, buf_size, plugin_data, + op_id); +} + +/*---------------------------------------------------------------------------*/ +static NA_INLINE na_return_t +NA_Msg_send_expected(na_class_t *na_class, na_context_t *context, na_cb_t callback, void *arg, + const void *buf, na_size_t buf_size, void *plugin_data, na_addr_t dest_addr, + na_uint8_t dest_id, na_tag_t tag, na_op_id_t *op_id) +{ + return na_class->ops->msg_send_expected(na_class, context, callback, arg, buf, buf_size, plugin_data, + dest_addr, dest_id, tag, op_id); +} + +/*---------------------------------------------------------------------------*/ +static NA_INLINE na_return_t +NA_Msg_recv_expected(na_class_t *na_class, na_context_t *context, na_cb_t callback, void *arg, void *buf, + na_size_t buf_size, void *plugin_data, na_addr_t source_addr, na_uint8_t source_id, + na_tag_t tag, na_op_id_t *op_id) +{ + return na_class->ops->msg_recv_expected(na_class, context, callback, arg, buf, buf_size, plugin_data, + source_addr, source_id, tag, op_id); +} + +/*---------------------------------------------------------------------------*/ +static NA_INLINE na_size_t +NA_Mem_handle_get_max_segments(const na_class_t *na_class) +{ + return (na_class->ops->mem_handle_get_max_segments) ? na_class->ops->mem_handle_get_max_segments(na_class) + : 1; +} + +/*---------------------------------------------------------------------------*/ +static NA_INLINE na_size_t +NA_Mem_handle_get_serialize_size(na_class_t *na_class, na_mem_handle_t mem_handle) +{ + return na_class->ops->mem_handle_get_serialize_size(na_class, mem_handle); +} + +/*---------------------------------------------------------------------------*/ +static NA_INLINE na_return_t +NA_Put(na_class_t *na_class, na_context_t *context, na_cb_t callback, void *arg, + na_mem_handle_t local_mem_handle, na_offset_t local_offset, na_mem_handle_t remote_mem_handle, + na_offset_t remote_offset, na_size_t data_size, na_addr_t remote_addr, na_uint8_t remote_id, + na_op_id_t *op_id) +{ + return na_class->ops->put(na_class, context, callback, arg, local_mem_handle, local_offset, + remote_mem_handle, remote_offset, data_size, remote_addr, remote_id, op_id); +} + +/*---------------------------------------------------------------------------*/ +static NA_INLINE na_return_t +NA_Get(na_class_t *na_class, na_context_t *context, na_cb_t callback, void *arg, + na_mem_handle_t local_mem_handle, na_offset_t local_offset, na_mem_handle_t remote_mem_handle, + na_offset_t remote_offset, na_size_t data_size, na_addr_t remote_addr, na_uint8_t remote_id, + na_op_id_t *op_id) +{ + return na_class->ops->get(na_class, context, callback, arg, local_mem_handle, local_offset, + remote_mem_handle, remote_offset, data_size, remote_addr, remote_id, op_id); +} + +/*---------------------------------------------------------------------------*/ +static NA_INLINE int +NA_Poll_get_fd(na_class_t *na_class, na_context_t *context) +{ + return (na_class->ops->na_poll_get_fd) ? na_class->ops->na_poll_get_fd(na_class, context) : -1; +} + +#ifdef __cplusplus +} +#endif + +#endif /* NA_H */ diff --git a/src/mercury/include/na_config.h b/src/mercury/include/na_config.h new file mode 100644 index 0000000..579ba63 --- /dev/null +++ b/src/mercury/include/na_config.h @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2013-2020 Argonne National Laboratory, Department of Energy, + * UChicago Argonne, LLC and The HDF Group. + * All rights reserved. + * + * The full copyright notice, including terms governing use, modification, + * and redistribution, is contained in the COPYING file that can be + * found at the root of the source code distribution tree. + */ + +/* Generated file. Only edit na_config.h.in. */ + +#ifndef NA_CONFIG_H +#define NA_CONFIG_H + +/*************************************/ +/* Public Type and Struct Definition */ +/*************************************/ + +/* Type definitions */ +#ifdef _WIN32 +typedef signed __int64 na_int64_t; +typedef signed __int32 na_int32_t; +typedef signed __int16 na_int16_t; +typedef signed __int8 na_int8_t; +typedef unsigned __int64 na_uint64_t; +typedef unsigned __int32 na_uint32_t; +typedef unsigned __int16 na_uint16_t; +typedef unsigned __int8 na_uint8_t; +#else +#include <stddef.h> +#include <stdint.h> +typedef int64_t na_int64_t; +typedef int32_t na_int32_t; +typedef int16_t na_int16_t; +typedef int8_t na_int8_t; +typedef uint64_t na_uint64_t; +typedef uint32_t na_uint32_t; +typedef uint16_t na_uint16_t; +typedef uint8_t na_uint8_t; +#endif +typedef na_uint8_t na_bool_t; +typedef na_uint64_t na_ptr_t; + +/* True / false */ +#define NA_TRUE 1 +#define NA_FALSE 0 + +/*****************/ +/* Public Macros */ +/*****************/ + +/* Visibility of symbols */ +#if defined(_WIN32) +#define NA_ABI_IMPORT __declspec(dllimport) +#define NA_ABI_EXPORT __declspec(dllexport) +#define NA_ABI_HIDDEN +#elif defined(__GNUC__) && (__GNUC__ >= 4) +#define NA_ABI_IMPORT __attribute__((visibility("default"))) +#define NA_ABI_EXPORT __attribute__((visibility("default"))) +#define NA_ABI_HIDDEN __attribute__((visibility("hidden"))) +#else +#define NA_ABI_IMPORT +#define NA_ABI_EXPORT +#define NA_ABI_HIDDEN +#endif + +/* Inline macro */ +#ifdef _WIN32 +#define NA_INLINE __inline +#else +#define NA_INLINE __inline__ +#endif + +/* Unused return values */ +#if defined(__GNUC__) +#define NA_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) +#else +#define NA_WARN_UNUSED_RESULT +#endif + +/* Fallthrough macro */ +#if defined(__GNUC__) && (__GNUC__ >= 7) +#define NA_FALLTHROUGH() __attribute__((fallthrough)) +#else +#define NA_FALLTHROUGH() +#endif + +/* Shared libraries */ +/* #undef NA_BUILD_SHARED_LIBS */ +#ifdef NA_BUILD_SHARED_LIBS +#ifdef na_EXPORTS +#define NA_PUBLIC NA_ABI_EXPORT +#else +#define NA_PUBLIC NA_ABI_IMPORT +#endif +#define NA_PRIVATE NA_ABI_HIDDEN +#else +#define NA_PUBLIC +#define NA_PRIVATE +#endif + +/* Build Options */ +#define NA_HAS_MULTI_PROGRESS +/* #undef NA_HAS_DEBUG */ + +/* BMI */ +/* #undef NA_HAS_BMI */ + +/* MPI */ +/* #undef NA_HAS_MPI */ +/* #undef NA_MPI_HAS_GNI_SETUP */ + +/* CCI */ +/* #undef NA_HAS_CCI */ + +/* OFI */ +/* #undef NA_HAS_OFI */ +/* #undef NA_OFI_HAS_EXT_GNI_H */ +/* #undef NA_OFI_GNI_HAS_UDREG */ + +/* NA SM */ +#define NA_HAS_SM +/* #undef NA_SM_HAS_UUID */ +#define NA_SM_HAS_CMA +#define NA_SM_SHM_PREFIX "na_sm" +#define NA_SM_TMP_DIRECTORY "/tmp" + +/* UCX */ +/* #undef NA_HAS_UCX */ +/* #undef NA_UCX_HAS_LIB_QUERY */ +/* #undef NA_UCX_HAS_THREAD_MODE_NAMES */ + +#endif /* NA_CONFIG_H */ diff --git a/src/mercury/include/na_sm.h b/src/mercury/include/na_sm.h new file mode 100644 index 0000000..709c639 --- /dev/null +++ b/src/mercury/include/na_sm.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2013-2020 Argonne National Laboratory, Department of Energy, + * UChicago Argonne, LLC and The HDF Group. + * All rights reserved. + * + * The full copyright notice, including terms governing use, modification, + * and redistribution, is contained in the COPYING file that can be + * found at the root of the source code distribution tree. + */ + +#ifndef NA_SM_H +#define NA_SM_H + +#include "na_types.h" + +/*************************************/ +/* Public Type and Struct Definition */ +/*************************************/ + +#ifdef NA_SM_HAS_UUID +typedef unsigned char na_sm_id_t[16]; +#else +typedef long na_sm_id_t; +#endif + +/*****************/ +/* Public Macros */ +/*****************/ + +/* String length of Host ID */ +#ifdef NA_SM_HAS_UUID +#define NA_SM_HOST_ID_LEN 36 +#else +#define NA_SM_HOST_ID_LEN 11 +#endif + +/*********************/ +/* Public Prototypes */ +/*********************/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Get the curent host ID (generate a new one if none exists). + * + * \param id [IN/OUT] pointer to SM host ID + * + * \return NA_SUCCESS or corresponding NA error code + */ +NA_PUBLIC na_return_t NA_SM_Host_id_get(na_sm_id_t *id); + +/** + * Convert host ID to string. String size must be NA_SM_HOST_ID_LEN + 1. + * + * \param id [IN] SM host ID + * \param string [IN/OUT] pointer to string + * + * \return NA_SUCCESS or corresponding NA error code + */ +NA_PUBLIC na_return_t NA_SM_Host_id_to_string(na_sm_id_t id, char *string); + +/** + * Convert string to host ID. String size must be NA_SM_HOST_ID_LEN + 1. + * + * \param string [IN] pointer to string + * \param id [IN/OUT] pointer to SM host ID + * + * \return NA_SUCCESS or corresponding NA error code + */ +NA_PUBLIC na_return_t NA_SM_String_to_host_id(const char *string, na_sm_id_t *id); + +/** + * Copy src host ID to dst. + * + * \param dst [IN/OUT] pointer to destination SM host ID + * \param src [IN] source SM host ID + */ +NA_PUBLIC void NA_SM_Host_id_copy(na_sm_id_t *dst, na_sm_id_t src); + +/** + * Compare two host IDs. + * + * \param id1 [IN] SM host ID + * \param id2 [IN] SM host ID + * + * \return NA_TRUE if equal or NA_FALSE otherwise + */ +NA_PUBLIC na_bool_t NA_SM_Host_id_cmp(na_sm_id_t id1, na_sm_id_t id2); + +#ifdef __cplusplus +} +#endif + +#endif /* NA_SM_H */ diff --git a/src/mercury/include/na_types.h b/src/mercury/include/na_types.h new file mode 100644 index 0000000..0062ebe --- /dev/null +++ b/src/mercury/include/na_types.h @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2013-2020 Argonne National Laboratory, Department of Energy, + * UChicago Argonne, LLC and The HDF Group. + * All rights reserved. + * + * The full copyright notice, including terms governing use, modification, + * and redistribution, is contained in the COPYING file that can be + * found at the root of the source code distribution tree. + */ + +#ifndef NA_TYPES_H +#define NA_TYPES_H + +#include "na_config.h" + +#include <limits.h> + +/*************************************/ +/* Public Type and Struct Definition */ +/*************************************/ + +typedef struct na_class na_class_t; /* Opaque NA class */ +typedef struct na_context na_context_t; /* Opaque NA execution context */ +typedef struct na_addr * na_addr_t; /* Abstract NA address */ +typedef na_uint64_t na_size_t; /* Size */ +typedef na_uint32_t na_tag_t; /* Tag */ +typedef struct na_op_id na_op_id_t; /* Opaque operation id */ + +typedef struct na_mem_handle *na_mem_handle_t; /* Abstract memory handle */ +typedef na_uint64_t na_offset_t; /* Offset */ + +/* Init info */ +struct na_init_info { + /* Preferred IP subnet to use. */ + const char *ip_subnet; + + /* Authorization key that can be used for communication. All processes + * should use the same key in order to communicate. + * NB. generation of keys is done through third-party libraries. */ + const char *auth_key; + + /* Max unexpected size hint that can be passed to control the size of + * unexpected messages. Note that the underlying plugin library may switch + * to different transfer protocols depending on the message size that is + * used. */ + na_size_t max_unexpected_size; + + /* Max expected size hint that can be passed to control the size of + * expected messages. Note that the underlying plugin library may switch + * to different transfer protocols depending on the message size that is + * used. */ + na_size_t max_expected_size; + + /* Progress mode flag. Setting NA_NO_BLOCK will force busy-spin on progress + * and remove any wait/notification calls. */ + na_uint32_t progress_mode; + + /* Maximum number of contexts that are expected to be created. */ + na_uint8_t max_contexts; + + /* Thread mode flags can be used to relax thread-safety when it is not + * needed. When setting NA_THREAD_MODE_SINGLE, only a single thread should + * access both NA classes and contexts at a time. */ + na_uint8_t thread_mode; +}; + +/* Segment */ +struct na_segment { + na_ptr_t base; /* Address of the segment */ + na_size_t len; /* Size of the segment in bytes */ +}; + +/* Return codes: + * Functions return 0 for success or corresponding return code */ +#define NA_RETURN_VALUES \ + X(NA_SUCCESS) /*!< operation succeeded */ \ + X(NA_PERMISSION) /*!< operation not permitted */ \ + X(NA_NOENTRY) /*!< no such file or directory */ \ + X(NA_INTERRUPT) /*!< operation interrupted */ \ + X(NA_AGAIN) /*!< operation must be retried */ \ + X(NA_NOMEM) /*!< out of memory */ \ + X(NA_ACCESS) /*!< permission denied */ \ + X(NA_FAULT) /*!< bad address */ \ + X(NA_BUSY) /*!< device or resource busy */ \ + X(NA_EXIST) /*!< entry already exists */ \ + X(NA_NODEV) /*!< no such device */ \ + X(NA_INVALID_ARG) /*!< invalid argument */ \ + X(NA_PROTOCOL_ERROR) /*!< protocol error */ \ + X(NA_OVERFLOW) /*!< value too large */ \ + X(NA_MSGSIZE) /*!< message size too long */ \ + X(NA_PROTONOSUPPORT) /*!< protocol not supported */ \ + X(NA_OPNOTSUPPORTED) /*!< operation not supported on endpoint */ \ + X(NA_ADDRINUSE) /*!< address already in use */ \ + X(NA_ADDRNOTAVAIL) /*!< cannot assign requested address */ \ + X(NA_HOSTUNREACH) /*!< cannot reach host during operation */ \ + X(NA_TIMEOUT) /*!< operation reached timeout */ \ + X(NA_CANCELED) /*!< operation canceled */ \ + X(NA_RETURN_MAX) + +#define X(a) a, +typedef enum na_return { NA_RETURN_VALUES } na_return_t; +#undef X + +/* Callback operation type */ +#define NA_CB_TYPES \ + X(NA_CB_SEND_UNEXPECTED) /*!< unexpected send callback */ \ + X(NA_CB_RECV_UNEXPECTED) /*!< unexpected recv callback */ \ + X(NA_CB_SEND_EXPECTED) /*!< expected send callback */ \ + X(NA_CB_RECV_EXPECTED) /*!< expected recv callback */ \ + X(NA_CB_PUT) /*!< put callback */ \ + X(NA_CB_GET) /*!< get callback */ \ + X(NA_CB_MAX) + +#define X(a) a, +typedef enum na_cb_type { NA_CB_TYPES } na_cb_type_t; +#undef X + +/* Callback info structs */ +struct na_cb_info_recv_unexpected { + na_size_t actual_buf_size; + na_addr_t source; + na_tag_t tag; +}; + +struct na_cb_info_recv_expected { + na_size_t actual_buf_size; +}; + +/* Callback info struct */ +struct na_cb_info { + union { /* Union of callback info structures */ + struct na_cb_info_recv_unexpected recv_unexpected; + struct na_cb_info_recv_expected recv_expected; + } info; + void * arg; /* User data */ + na_cb_type_t type; /* Callback type */ + na_return_t ret; /* Return value */ +}; + +/* Callback type */ +typedef int (*na_cb_t)(const struct na_cb_info *callback_info); + +/*****************/ +/* Public Macros */ +/*****************/ + +/* Constant values */ +#define NA_ADDR_NULL ((na_addr_t)0) +#define NA_MEM_HANDLE_NULL ((na_mem_handle_t)0) + +/* Max timeout */ +#define NA_MAX_IDLE_TIME (3600 * 1000) + +/* Context ID max value + * \remark This is not the user limit but only the limit imposed by the type */ +#define NA_CONTEXT_ID_MAX UINT8_MAX + +/* Tag max value + * \remark This is not the user limit but only the limit imposed by the type */ +#define NA_TAG_MAX UINT_MAX + +/* The memory attributes associated with the memory handle + * can be defined as read only, write only or read/write */ +#define NA_MEM_READ_ONLY 0x01 +#define NA_MEM_WRITE_ONLY 0x02 +#define NA_MEM_READWRITE 0x03 + +/* Progress modes */ +#define NA_NO_BLOCK 0x01 /*!< no blocking progress */ +#define NA_NO_RETRY 0x02 /*!< no retry of operations in progress */ + +/* Thread modes (default is thread-safe) */ +#define NA_THREAD_MODE_SINGLE_CLS (0x01) /*!< only one thread will access class */ +#define NA_THREAD_MODE_SINGLE_CTX (0x02) /*!< only one thread will access context */ +#define NA_THREAD_MODE_SINGLE (NA_THREAD_MODE_SINGLE_CLS | NA_THREAD_MODE_SINGLE_CTX) + +/* NA init info initializer */ +#define NA_INIT_INFO_INITIALIZER \ + (struct na_init_info) \ + { \ + .ip_subnet = NULL, .auth_key = NULL, .max_unexpected_size = 0, .max_expected_size = 0, \ + .progress_mode = 0, .max_contexts = 1, .thread_mode = 0 \ + } + +#endif /* NA_TYPES_H */ |