diff --git a/benchmark/benchmark_umf.hpp b/benchmark/benchmark_umf.hpp index cd4b31ae4..1e93811f7 100644 --- a/benchmark/benchmark_umf.hpp +++ b/benchmark/benchmark_umf.hpp @@ -45,7 +45,7 @@ struct provider_interface { return; } umfCtlExec("umf.provider.by_handle.stats.peak_memory.reset", provider, - NULL); + NULL, 0); } void postBench([[maybe_unused]] ::benchmark::State &state) { @@ -54,7 +54,7 @@ struct provider_interface { } size_t arg; umf_result_t ret = umfCtlGet( - "umf.provider.by_handle.stats.allocated_memory", provider, &arg); + "umf.provider.by_handle.stats.allocated_memory", provider, &arg, 0); if (ret == UMF_RESULT_SUCCESS) { state.counters["provider_memory_allocated"] = static_cast(arg); diff --git a/include/umf/base.h b/include/umf/base.h index 0a12a319e..ab306870c 100644 --- a/include/umf/base.h +++ b/include/umf/base.h @@ -66,27 +66,30 @@ typedef enum umf_ctl_query_type { /// @param name name of an attribute to be retrieved /// @param ctx pointer to the pool or the provider /// @param arg [out] pointer to the variable where the value will be stored +/// @param size size of the value, depends on the context /// @return UMF_RESULT_SUCCESS on success or UMF_RESULT_ERROR_UNKNOWN on failure. /// -umf_result_t umfCtlGet(const char *name, void *ctx, void *arg); +umf_result_t umfCtlGet(const char *name, void *ctx, void *arg, size_t size); /// /// @brief Set value of a specified attribute at the given name. /// @param name name of an attribute to be set -/// @param ctx pointer to the pool or the provider +/// @param ctx pointer to the pool or the provider, NULL for the 'default' path /// @param arg [in] pointer to the value that will be set +/// @param size [in] size of the value, depends on the context /// @return UMF_RESULT_SUCCESS on success or UMF_RESULT_ERROR_UNKNOWN on failure. /// -umf_result_t umfCtlSet(const char *name, void *ctx, void *arg); +umf_result_t umfCtlSet(const char *name, void *ctx, void *arg, size_t size); /// /// @brief Execute callback related with the specified attribute. /// @param name name of an attribute to be executed /// @param ctx pointer to the pool or the provider /// @param arg [in/out] pointer to the value, can be used as an input or output +/// @param size [in] size of the value, depends on the context /// @return UMF_RESULT_SUCCESS on success or UMF_RESULT_ERROR_UNKNOWN on failure. /// -umf_result_t umfCtlExec(const char *name, void *ctx, void *arg); +umf_result_t umfCtlExec(const char *name, void *ctx, void *arg, size_t size); #ifdef __cplusplus } diff --git a/include/umf/memory_pool.h b/include/umf/memory_pool.h index c46cac7d6..836d91ab2 100644 --- a/include/umf/memory_pool.h +++ b/include/umf/memory_pool.h @@ -165,6 +165,13 @@ umf_memory_pool_handle_t umfPoolByPtr(const void *ptr); umf_result_t umfPoolGetMemoryProvider(umf_memory_pool_handle_t hPool, umf_memory_provider_handle_t *hProvider); +/// +/// @brief Retrieve name of a given memory \p pool. +/// @param pool handle to the memory pool +/// @return pointer to a string containing the name of the \p pool +/// +const char *umfPoolGetName(umf_memory_pool_handle_t pool); + /// /// @brief Set a custom tag on the memory pool that can be later retrieved using umfPoolGetTag. /// @param hPool specified memory pool diff --git a/include/umf/memory_pool_ops.h b/include/umf/memory_pool_ops.h index 1c9772268..d8ad485a7 100644 --- a/include/umf/memory_pool_ops.h +++ b/include/umf/memory_pool_ops.h @@ -131,16 +131,24 @@ typedef struct umf_memory_pool_ops_t { /// The function is used to perform various control operations /// on the memory pool. /// - /// @param hPool handle to the memory pool. + /// @param pool handle to the memory pool. /// @param operationType type of the operation to be performed. /// @param name name associated with the operation. /// @param arg argument for the operation. + /// @param size size of the argument [optional - check path requirements] /// @param queryType type of the query to be performed. /// /// @return umf_result_t result of the control operation. /// - umf_result_t (*ctl)(void *hPool, int operationType, const char *name, - void *arg, umf_ctl_query_type_t queryType); + umf_result_t (*ctl)(void *pool, int operationType, const char *name, + void *arg, size_t size, umf_ctl_query_type_t queryType); + + /// + /// @brief Get the name of the memory pool. + /// @param pool pointer to the memory pool + /// @return name of the memory pool + /// + const char *(*get_name)(void *pool); } umf_memory_pool_ops_t; #ifdef __cplusplus diff --git a/include/umf/memory_provider_ops.h b/include/umf/memory_provider_ops.h index a531c10ba..c677ae611 100644 --- a/include/umf/memory_provider_ops.h +++ b/include/umf/memory_provider_ops.h @@ -259,12 +259,13 @@ typedef struct umf_memory_provider_ops_t { /// @param operationType type of the operation to be performed. /// @param name name associated with the operation. /// @param arg argument for the operation. + /// @param size TODO(kfilipek) /// @param queryType type of the query to be performed. /// /// @return umf_result_t result of the control operation. /// umf_result_t (*ctl)(void *hProvider, int operationType, const char *name, - void *arg, umf_ctl_query_type_t queryType); + void *arg, size_t size, umf_ctl_query_type_t queryType); } umf_memory_provider_ops_t; diff --git a/src/ctl/ctl.c b/src/ctl/ctl.c index da6661d26..230286e88 100644 --- a/src/ctl/ctl.c +++ b/src/ctl/ctl.c @@ -36,8 +36,6 @@ #include #endif -#define CTL_MAX_ENTRIES 100 - #define MAX_CONFIG_FILE_LEN (1 << 20) /* 1 megabyte */ #define CTL_STRING_QUERY_SEPARATOR ";" @@ -49,21 +47,6 @@ static int ctl_global_first_free = 0; static umf_ctl_node_t CTL_NODE(global)[CTL_MAX_ENTRIES]; -/* - * This is the top level node of the ctl tree structure. Each node can contain - * children and leaf nodes. - * - * Internal nodes simply create a new path in the tree whereas child nodes are - * the ones providing the read/write functionality by the means of callbacks. - * - * Each tree node must be NULL-terminated, CTL_NODE_END macro is provided for - * convenience. - */ -struct ctl { - umf_ctl_node_t root[CTL_MAX_ENTRIES]; - int first_free; -}; - void *Zalloc(size_t sz) { void *ptr = umf_ba_global_alloc(sz); if (ptr) { @@ -81,36 +64,6 @@ char *Strdup(const char *s) { return p; } -umf_result_t umfCtlGet(const char *name, void *ctx, void *arg) { - if (name == NULL || arg == NULL || ctx == NULL) { - return UMF_RESULT_ERROR_INVALID_ARGUMENT; - } - return ctl_query(NULL, ctx, CTL_QUERY_PROGRAMMATIC, name, CTL_QUERY_READ, - arg) - ? UMF_RESULT_ERROR_UNKNOWN - : UMF_RESULT_SUCCESS; -} - -umf_result_t umfCtlSet(const char *name, void *ctx, void *arg) { - if (name == NULL || arg == NULL || ctx == NULL) { - return UMF_RESULT_ERROR_INVALID_ARGUMENT; - } - return ctl_query(NULL, ctx, CTL_QUERY_PROGRAMMATIC, name, CTL_QUERY_WRITE, - arg) - ? UMF_RESULT_ERROR_UNKNOWN - : UMF_RESULT_SUCCESS; -} - -umf_result_t umfCtlExec(const char *name, void *ctx, void *arg) { - if (name == NULL || ctx == NULL) { - return UMF_RESULT_ERROR_INVALID_ARGUMENT; - } - return ctl_query(NULL, ctx, CTL_QUERY_PROGRAMMATIC, name, - CTL_QUERY_RUNNABLE, arg) - ? UMF_RESULT_ERROR_UNKNOWN - : UMF_RESULT_SUCCESS; -} - /* * ctl_find_node -- (internal) searches for a matching entry point in the * provided nodes @@ -296,10 +249,10 @@ static void ctl_query_cleanup_real_args(const umf_ctl_node_t *n, void *real_arg, */ static int ctl_exec_query_read(void *ctx, const umf_ctl_node_t *n, umf_ctl_query_source_t source, void *arg, - umf_ctl_index_utlist_t *indexes, + size_t size, umf_ctl_index_utlist_t *indexes, const char *extra_name, umf_ctl_query_type_t query_type) { - (void)extra_name, (void)query_type; + (void)query_type; assert(n != NULL); assert(n->cb[CTL_QUERY_READ] != NULL); assert(MAX_CTL_QUERY_TYPE != query_type); @@ -309,7 +262,7 @@ static int ctl_exec_query_read(void *ctx, const umf_ctl_node_t *n, return -1; } - return n->cb[CTL_QUERY_READ](ctx, source, arg, indexes, NULL, + return n->cb[CTL_QUERY_READ](ctx, source, arg, size, indexes, extra_name, MAX_CTL_QUERY_TYPE); } @@ -318,10 +271,10 @@ static int ctl_exec_query_read(void *ctx, const umf_ctl_node_t *n, */ static int ctl_exec_query_write(void *ctx, const umf_ctl_node_t *n, umf_ctl_query_source_t source, void *arg, - umf_ctl_index_utlist_t *indexes, + size_t size, umf_ctl_index_utlist_t *indexes, const char *extra_name, umf_ctl_query_type_t query_type) { - (void)extra_name, (void)query_type; + (void)query_type; assert(n != NULL); assert(n->cb[CTL_QUERY_WRITE] != NULL); assert(MAX_CTL_QUERY_TYPE != query_type); @@ -336,8 +289,8 @@ static int ctl_exec_query_write(void *ctx, const umf_ctl_node_t *n, return -1; } - int ret = n->cb[CTL_QUERY_WRITE](ctx, source, real_arg, indexes, NULL, - MAX_CTL_QUERY_TYPE); + int ret = n->cb[CTL_QUERY_WRITE](ctx, source, real_arg, size, indexes, + extra_name, MAX_CTL_QUERY_TYPE); ctl_query_cleanup_real_args(n, real_arg, source); return ret; @@ -348,31 +301,32 @@ static int ctl_exec_query_write(void *ctx, const umf_ctl_node_t *n, */ static int ctl_exec_query_runnable(void *ctx, const umf_ctl_node_t *n, umf_ctl_query_source_t source, void *arg, - umf_ctl_index_utlist_t *indexes, + size_t size, umf_ctl_index_utlist_t *indexes, const char *extra_name, umf_ctl_query_type_t query_type) { (void)extra_name, (void)query_type; assert(n != NULL); assert(n->cb[CTL_QUERY_RUNNABLE] != NULL); assert(MAX_CTL_QUERY_TYPE != query_type); - return n->cb[CTL_QUERY_RUNNABLE](ctx, source, arg, indexes, NULL, - MAX_CTL_QUERY_TYPE); + return n->cb[CTL_QUERY_RUNNABLE](ctx, source, arg, size, indexes, + extra_name, MAX_CTL_QUERY_TYPE); } static int ctl_exec_query_subtree(void *ctx, const umf_ctl_node_t *n, umf_ctl_query_source_t source, void *arg, - umf_ctl_index_utlist_t *indexes, + size_t size, umf_ctl_index_utlist_t *indexes, const char *extra_name, umf_ctl_query_type_t query_type) { assert(n != NULL); assert(n->cb[CTL_QUERY_SUBTREE] != NULL); assert(MAX_CTL_QUERY_TYPE != query_type); - return n->cb[CTL_QUERY_SUBTREE](ctx, source, arg, indexes, extra_name, + return n->cb[CTL_QUERY_SUBTREE](ctx, source, arg, size, indexes, extra_name, query_type); } typedef int (*umf_ctl_exec_query_t)(void *ctx, const umf_ctl_node_t *n, umf_ctl_query_source_t source, void *arg, + size_t size, umf_ctl_index_utlist_t *indexes, const char *extra_name, umf_ctl_query_type_t query_type); @@ -389,7 +343,8 @@ static umf_ctl_exec_query_t ctl_exec_query[MAX_CTL_QUERY_TYPE] = { * from the ctl tree */ int ctl_query(struct ctl *ctl, void *ctx, umf_ctl_query_source_t source, - const char *name, umf_ctl_query_type_t type, void *arg) { + const char *name, umf_ctl_query_type_t type, void *arg, + size_t size) { if (name == NULL) { errno = EINVAL; return -1; @@ -426,10 +381,9 @@ int ctl_query(struct ctl *ctl, void *ctx, umf_ctl_query_source_t source, goto out; } - const char *extra_name = &name[0] + name_offset; ret = ctl_exec_query[n->type == CTL_NODE_SUBTREE ? CTL_QUERY_SUBTREE : type]( - ctx, n, source, arg, indexes, extra_name, type); + ctx, n, source, arg, size, indexes, name + name_offset, type); out: ctl_delete_indexes(indexes); @@ -496,7 +450,7 @@ static int ctl_load_config(struct ctl *ctl, void *ctx, char *buf) { } r = ctl_query(ctl, ctx, CTL_QUERY_CONFIG_INPUT, name, CTL_QUERY_WRITE, - value); + value, 0); if (r < 0 && ctx != NULL) { return -1; diff --git a/src/ctl/ctl.h b/src/ctl/ctl.h index 968998fc2..84686b4c8 100644 --- a/src/ctl/ctl.h +++ b/src/ctl/ctl.h @@ -27,7 +27,7 @@ extern "C" { #endif -struct ctl; +#define CTL_MAX_ENTRIES 100 typedef struct ctl_index_utlist { const char *name; @@ -46,7 +46,7 @@ typedef enum ctl_query_source { } umf_ctl_query_source_t; typedef int (*node_callback)(void *ctx, umf_ctl_query_source_t type, void *arg, - umf_ctl_index_utlist_t *indexes, + size_t size, umf_ctl_index_utlist_t *indexes, const char *extra_name, umf_ctl_query_type_t query_type); @@ -98,8 +98,23 @@ typedef struct ctl_node { const struct ctl_node *children; } umf_ctl_node_t; +/* + * This is the top level node of the ctl tree structure. Each node can contain + * children and leaf nodes. + * + * Internal nodes simply create a new path in the tree whereas child nodes are + * the ones providing the read/write functionality by the means of callbacks. + * + * Each tree node must be NULL-terminated, CTL_NODE_END macro is provided for + * convenience. + */ +struct ctl { + umf_ctl_node_t root[CTL_MAX_ENTRIES]; + int first_free; +}; + struct ctl *ctl_new(void); -void ctl_delete(struct ctl *stats); +void ctl_delete(struct ctl *c); void initialize_global_ctl(void); @@ -138,7 +153,8 @@ int ctl_arg_string(const void *arg, void *dest, size_t dest_size); #define CTL_NODE(name, ...) ctl_node_##__VA_ARGS__##_##name int ctl_query(struct ctl *ctl, void *ctx, umf_ctl_query_source_t source, - const char *name, umf_ctl_query_type_t type, void *arg); + const char *name, umf_ctl_query_type_t type, void *arg, + size_t size); /* Declaration of a new child node */ #define CTL_CHILD(name, ...) \ @@ -175,6 +191,18 @@ int ctl_query(struct ctl *ctl, void *ctx, umf_ctl_query_source_t source, NULL \ } +/* + * Declaration of a new RW leaf. This type of RW leaf doesn't require parsing + * of the argument. The argument is passed directly to the read/write callback. + */ +#define CTL_LEAF_RW_no_arg(name, ...) \ + { \ + CTL_STR(name), CTL_NODE_LEAF, \ + {CTL_READ_HANDLER(name, __VA_ARGS__), \ + CTL_WRITE_HANDLER(name, __VA_ARGS__), NULL, NULL}, \ + NULL, NULL \ + } + /* * Declaration of a new write-only leaf. If used the corresponding write * function must be declared by CTL_WRITE_HANDLER macro. diff --git a/src/libumf.c b/src/libumf.c index 7c10402fe..b807303b1 100644 --- a/src/libumf.c +++ b/src/libumf.c @@ -8,6 +8,7 @@ */ #include +#include #include "base_alloc_global.h" #include "ipc_cache.h" @@ -97,3 +98,42 @@ void umfTearDown(void) { } int umfGetCurrentVersion(void) { return UMF_VERSION_CURRENT; } + +umf_result_t umfCtlGet(const char *name, void *ctx, void *arg, size_t size) { + if (name == NULL || arg == NULL) { + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + // using ctx is disallowed for default settings + if (ctx && strstr(name, ".default.")) { + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + return ctl_query(NULL, ctx, CTL_QUERY_PROGRAMMATIC, name, CTL_QUERY_READ, + arg, size) + ? UMF_RESULT_ERROR_UNKNOWN + : UMF_RESULT_SUCCESS; +} + +umf_result_t umfCtlSet(const char *name, void *ctx, void *arg, size_t size) { + // Context can be NULL when setting defaults + if (name == NULL || arg == NULL || size == 0) { + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + // using ctx is disallowed for default settings + if (ctx && strstr(name, ".default.")) { + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + return ctl_query(NULL, ctx, CTL_QUERY_PROGRAMMATIC, name, CTL_QUERY_WRITE, + arg, size) + ? UMF_RESULT_ERROR_UNKNOWN + : UMF_RESULT_SUCCESS; +} + +umf_result_t umfCtlExec(const char *name, void *ctx, void *arg, size_t size) { + if (name == NULL || arg == NULL || ctx == NULL) { + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + return ctl_query(NULL, ctx, CTL_QUERY_PROGRAMMATIC, name, + CTL_QUERY_RUNNABLE, arg, size) + ? UMF_RESULT_ERROR_UNKNOWN + : UMF_RESULT_SUCCESS; +} diff --git a/src/libumf.def b/src/libumf.def index 925beab3b..aaf430277 100644 --- a/src/libumf.def +++ b/src/libumf.def @@ -142,3 +142,4 @@ EXPORTS umfJemallocPoolParamsCreate umfJemallocPoolParamsDestroy umfJemallocPoolParamsSetNumArenas + umfPoolGetName diff --git a/src/libumf.map b/src/libumf.map index a9a94b3a3..41aa48e9c 100644 --- a/src/libumf.map +++ b/src/libumf.map @@ -142,4 +142,5 @@ UMF_0.12 { umfJemallocPoolParamsCreate; umfJemallocPoolParamsDestroy; umfJemallocPoolParamsSetNumArenas; + umfPoolGetName; } UMF_0.11; diff --git a/src/memory_pool.c b/src/memory_pool.c index 985600d2a..be387bca9 100644 --- a/src/memory_pool.c +++ b/src/memory_pool.c @@ -7,43 +7,99 @@ * */ -#include "libumf.h" -#include "memory_pool_internal.h" -#include "utils_assert.h" - +#include #include #include #include -#include +#include #include "base_alloc_global.h" +#include "ctl/ctl.h" +#include "libumf.h" #include "memory_pool_internal.h" #include "memory_provider_internal.h" #include "provider_tracking.h" +#include "utils_assert.h" +#include "utils_concurrency.h" +#include "utils_log.h" + +#define UMF_DEFAULT_SIZE 100 +#define UMF_DEFAULT_LEN 100 + +utils_mutex_t ctl_mtx; +static UTIL_ONCE_FLAG mem_pool_ctl_initialized = UTIL_ONCE_FLAG_INIT; + +char CTL_DEFAULT_ENTRIES[UMF_DEFAULT_SIZE][UMF_DEFAULT_LEN] = {0}; +char CTL_DEFAULT_VALUES[UMF_DEFAULT_SIZE][UMF_DEFAULT_LEN] = {0}; + +void ctl_init(void) { utils_mutex_init(&ctl_mtx); } static int CTL_SUBTREE_HANDLER(by_handle_pool)(void *ctx, umf_ctl_query_source_t source, - void *arg, + void *arg, size_t size, umf_ctl_index_utlist_t *indexes, const char *extra_name, umf_ctl_query_type_t queryType) { (void)indexes, (void)source; umf_memory_pool_handle_t hPool = (umf_memory_pool_handle_t)ctx; - hPool->ops.ctl(hPool, /*unused*/ 0, extra_name, arg, queryType); + hPool->ops.ctl(hPool->pool_priv, /*unused*/ 0, extra_name, arg, size, + queryType); + return 0; +} + +static int CTL_SUBTREE_HANDLER(default)(void *ctx, + umf_ctl_query_source_t source, + void *arg, size_t size, + umf_ctl_index_utlist_t *indexes, + const char *extra_name, + umf_ctl_query_type_t queryType) { + (void)indexes, (void)source, (void)ctx; + utils_init_once(&mem_pool_ctl_initialized, ctl_init); + utils_mutex_lock(&ctl_mtx); + if (queryType == CTL_QUERY_WRITE) { + int i = 0; + for (; i < UMF_DEFAULT_SIZE; i++) { + if (CTL_DEFAULT_ENTRIES[i][0] == '\0' || + strcmp(CTL_DEFAULT_ENTRIES[i], extra_name) == 0) { + strncpy(CTL_DEFAULT_ENTRIES[i], extra_name, UMF_DEFAULT_LEN); + strncpy(CTL_DEFAULT_VALUES[i], arg, UMF_DEFAULT_LEN); + break; + } + } + if (UMF_DEFAULT_SIZE == i) { + LOG_ERR("Default entries array is full"); + return -1; + } + } else if (queryType == CTL_QUERY_READ) { + int i = 0; + for (; i < UMF_DEFAULT_SIZE; i++) { + if (strcmp(CTL_DEFAULT_ENTRIES[i], extra_name) == 0) { + strncpy(arg, CTL_DEFAULT_VALUES[i], size); + break; + } + } + if (UMF_DEFAULT_SIZE == i) { + LOG_WARN("Wrong path name: %s", extra_name); + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + } + utils_mutex_unlock(&ctl_mtx); return 0; } umf_ctl_node_t CTL_NODE(pool)[] = {CTL_LEAF_SUBTREE2(by_handle, by_handle_pool), - CTL_NODE_END}; + CTL_LEAF_SUBTREE(default), CTL_NODE_END}; static umf_result_t umfDefaultCtlPoolHandle(void *hPool, int operationType, const char *name, void *arg, + size_t size, umf_ctl_query_type_t queryType) { (void)hPool; (void)operationType; (void)name; (void)arg; + (void)size; (void)queryType; return UMF_RESULT_ERROR_NOT_SUPPORTED; } @@ -80,6 +136,8 @@ static umf_result_t umfPoolCreateInternal(const umf_memory_pool_ops_t *ops, pool->provider = provider; } + utils_init_once(&mem_pool_ctl_initialized, ctl_init); + pool->flags = flags; pool->ops = *ops; pool->tag = NULL; @@ -99,6 +157,16 @@ static umf_result_t umfPoolCreateInternal(const umf_memory_pool_ops_t *ops, goto err_pool_init; } + // Set default property "name" to pool if exists + for (int i = 0; i < UMF_DEFAULT_SIZE; i++) { + if (CTL_DEFAULT_ENTRIES[i][0] != '\0' && + strstr(CTL_DEFAULT_ENTRIES[i], ops->get_name(NULL))) { + ops->ctl(pool->pool_priv, CTL_QUERY_PROGRAMMATIC, + CTL_DEFAULT_ENTRIES[i], CTL_DEFAULT_VALUES[i], + UMF_DEFAULT_LEN, CTL_QUERY_WRITE); + } + } + *hPool = pool; LOG_INFO("Memory pool created: %p", (void *)pool); return UMF_RESULT_SUCCESS; @@ -168,6 +236,11 @@ umf_result_t umfPoolGetMemoryProvider(umf_memory_pool_handle_t hPool, return UMF_RESULT_SUCCESS; } +const char *umfPoolGetName(umf_memory_pool_handle_t pool) { + UMF_CHECK(pool != NULL, NULL); + return pool->ops.get_name(pool->pool_priv); +} + umf_result_t umfPoolCreate(const umf_memory_pool_ops_t *ops, umf_memory_provider_handle_t provider, const void *params, umf_pool_create_flags_t flags, diff --git a/src/memory_provider.c b/src/memory_provider.c index 627ad23ac..eceb57d69 100644 --- a/src/memory_provider.c +++ b/src/memory_provider.c @@ -22,13 +22,13 @@ #include "utils_assert.h" static int CTL_SUBTREE_HANDLER(by_handle_provider)( - void *ctx, umf_ctl_query_source_t source, void *arg, + void *ctx, umf_ctl_query_source_t source, void *arg, size_t size, umf_ctl_index_utlist_t *indexes, const char *extra_name, umf_ctl_query_type_t queryType) { - (void)indexes, (void)source; + (void)indexes, (void)source, (void)size; umf_memory_provider_handle_t hProvider = (umf_memory_provider_handle_t)ctx; hProvider->ops.ctl(hProvider->provider_priv, /*unused*/ 0, extra_name, arg, - queryType); + size, queryType); return 0; } @@ -110,11 +110,13 @@ static umf_result_t umfDefaultCloseIPCHandle(void *provider, void *ptr, static umf_result_t umfDefaultCtlHandle(void *provider, int operationType, const char *name, void *arg, + size_t size, umf_ctl_query_type_t queryType) { (void)provider; (void)operationType; (void)name; (void)arg; + (void)size; (void)queryType; return UMF_RESULT_ERROR_NOT_SUPPORTED; } diff --git a/src/pool/pool_disjoint.c b/src/pool/pool_disjoint.c index 8ce9f70dd..2b8a97930 100644 --- a/src/pool/pool_disjoint.c +++ b/src/pool/pool_disjoint.c @@ -19,11 +19,72 @@ #include "base_alloc_global.h" #include "pool_disjoint_internal.h" #include "provider/provider_tracking.h" +#include "umf/base.h" #include "uthash/utlist.h" #include "utils_common.h" #include "utils_concurrency.h" #include "utils_log.h" #include "utils_math.h" +#include + +/* Disjoint pool CTL implementation */ +struct ctl disjoint_ctl_root; +static UTIL_ONCE_FLAG ctl_initialized = UTIL_ONCE_FLAG_INIT; + +static int CTL_READ_HANDLER(name)(void *ctx, umf_ctl_query_source_t source, + void *arg, size_t size, + umf_ctl_index_utlist_t *indexes, + const char *extra_name, + umf_ctl_query_type_t queryType) { + (void)source, (void)indexes, (void)queryType, (void)size, (void)extra_name; + disjoint_pool_t *pool = (disjoint_pool_t *)ctx; + + if (arg == NULL) { + return -1; + } + + strncpy((char *)arg, pool->params.name, size); + return 0; +} + +static int CTL_WRITE_HANDLER(name)(void *ctx, umf_ctl_query_source_t source, + void *arg, size_t size, + umf_ctl_index_utlist_t *indexes, + const char *extra_name, + umf_ctl_query_type_t queryType) { + (void)source, (void)indexes, (void)queryType, (void)size, (void)extra_name; + disjoint_pool_t *pool = (disjoint_pool_t *)ctx; + if (arg == NULL) { + return -1; + } + + strncpy(pool->params.name, (char *)arg, sizeof(pool->params.name) - 1); + return 0; +} + +static const umf_ctl_node_t CTL_NODE(disjoint)[] = {CTL_LEAF_RW_no_arg(name), + CTL_NODE_END}; + +static void initialize_disjoint_ctl(void) { + CTL_REGISTER_MODULE(&disjoint_ctl_root, disjoint); +} + +umf_result_t disjoint_pool_ctl(void *hPool, int operationType, const char *name, + void *arg, size_t size, + umf_ctl_query_type_t queryType) { + (void)operationType, (void)queryType; + utils_init_once(&ctl_initialized, initialize_disjoint_ctl); + + const char *prefix = "disjoint."; + const char *name_wo_prefix = strstr(name, "disjoint."); + + // Check if the name has the prefix + if ((name_wo_prefix = strstr(name, prefix)) == NULL) { + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + return ctl_query(&disjoint_ctl_root, hPool, CTL_QUERY_PROGRAMMATIC, + name_wo_prefix, queryType, arg, size); +} // Temporary solution for disabling memory poisoning. This is needed because // AddressSanitizer does not support memory poisoning for GPU allocations. @@ -928,6 +989,14 @@ void disjoint_pool_finalize(void *pool) { umf_ba_global_free(hPool); } +const char *disjoint_pool_get_name(void *pool) { + disjoint_pool_t *hPool = (disjoint_pool_t *)pool; + if (pool == NULL) { + return "disjoint"; + } + return hPool->params.name; +} + static umf_memory_pool_ops_t UMF_DISJOINT_POOL_OPS = { .version = UMF_VERSION_CURRENT, .initialize = disjoint_pool_initialize, @@ -939,6 +1008,8 @@ static umf_memory_pool_ops_t UMF_DISJOINT_POOL_OPS = { .malloc_usable_size = disjoint_pool_malloc_usable_size, .free = disjoint_pool_free, .get_last_allocation_error = disjoint_pool_get_last_allocation_error, + .get_name = disjoint_pool_get_name, + .ctl = disjoint_pool_ctl, }; const umf_memory_pool_ops_t *umfDisjointPoolOps(void) { @@ -964,7 +1035,7 @@ void umfDisjointPoolSharedLimitsDestroy( umf_result_t umfDisjointPoolParamsCreate(umf_disjoint_pool_params_handle_t *hParams) { - static const char *DEFAULT_NAME = "disjoint_pool"; + static char *DEFAULT_NAME = "disjoint"; if (!hParams) { LOG_ERR("disjoint pool params handle is NULL"); @@ -986,11 +1057,14 @@ umfDisjointPoolParamsCreate(umf_disjoint_pool_params_handle_t *hParams) { .cur_pool_size = 0, .pool_trace = 0, .shared_limits = NULL, - .name = {*DEFAULT_NAME}, }; - *hParams = params; + // Find default name and update params name + if (params->name[0] == '\0') { + strncpy(params->name, DEFAULT_NAME, sizeof(params->name) - 1); + } + *hParams = params; return UMF_RESULT_SUCCESS; } diff --git a/src/pool/pool_scalable.c b/src/pool/pool_scalable.c index ea0260659..5c57a51d4 100644 --- a/src/pool/pool_scalable.c +++ b/src/pool/pool_scalable.c @@ -411,36 +411,21 @@ static umf_result_t tbb_get_last_allocation_error(void *pool) { return TLS_last_allocation_error; } -static int CTL_READ_HANDLER(tracking_enabled)(void *ctx, - umf_ctl_query_source_t source, - void *arg, - umf_ctl_index_utlist_t *indexes, - const char *extra_name, - umf_ctl_query_type_t query_type) { - /* suppress unused-parameter errors */ - (void)source, (void)indexes, (void)ctx, (void)extra_name, (void)query_type; - - int *arg_out = arg; - umf_memory_pool_handle_t pool = (umf_memory_pool_handle_t)ctx; - *arg_out = pool->flags & UMF_POOL_CREATE_FLAG_DISABLE_TRACKING ? 0 : 1; - return 0; -} - -static const umf_ctl_node_t CTL_NODE(params)[] = {CTL_LEAF_RO(tracking_enabled), - CTL_NODE_END}; - -static void initialize_pool_ctl(void) { - pool_scallable_ctl_root = ctl_new(); - CTL_REGISTER_MODULE(pool_scallable_ctl_root, params); -} +static void initialize_pool_ctl(void) { pool_scallable_ctl_root = ctl_new(); } static umf_result_t pool_ctl(void *hPool, int operationType, const char *name, - void *arg, umf_ctl_query_type_t query_type) { + void *arg, size_t size, + umf_ctl_query_type_t query_type) { (void)operationType; // unused umf_memory_pool_handle_t pool_provider = (umf_memory_pool_handle_t)hPool; utils_init_once(&ctl_initialized, initialize_pool_ctl); - return ctl_query(pool_scallable_ctl_root, pool_provider, - CTL_QUERY_PROGRAMMATIC, name, query_type, arg); + return ctl_query(pool_scallable_ctl_root, pool_provider->pool_priv, + CTL_QUERY_PROGRAMMATIC, name, query_type, arg, size); +} + +static const char *scalable_get_name(void *pool) { + (void)pool; // unused + return "scalable"; } static umf_memory_pool_ops_t UMF_SCALABLE_POOL_OPS = { @@ -454,7 +439,9 @@ static umf_memory_pool_ops_t UMF_SCALABLE_POOL_OPS = { .malloc_usable_size = tbb_malloc_usable_size, .free = tbb_free, .get_last_allocation_error = tbb_get_last_allocation_error, - .ctl = pool_ctl}; + .ctl = pool_ctl, + .get_name = scalable_get_name, +}; const umf_memory_pool_ops_t *umfScalablePoolOps(void) { return &UMF_SCALABLE_POOL_OPS; diff --git a/src/provider/provider_ctl_stats_impl.h b/src/provider/provider_ctl_stats_impl.h index a13d31276..565ed78b0 100644 --- a/src/provider/provider_ctl_stats_impl.h +++ b/src/provider/provider_ctl_stats_impl.h @@ -23,12 +23,12 @@ extern "C" { static int CTL_READ_HANDLER(peak_memory)(void *ctx, umf_ctl_query_source_t source, - void *arg, + void *arg, size_t size, umf_ctl_index_utlist_t *indexes, const char *extra_name, umf_ctl_query_type_t query_type) { /* suppress unused-parameter errors */ - (void)source, (void)indexes, (void)extra_name, (void)query_type; + (void)source, (void)size, (void)indexes, (void)extra_name, (void)query_type; size_t *arg_out = arg; CTL_PROVIDER_TYPE *provider = (CTL_PROVIDER_TYPE *)ctx; @@ -38,12 +38,12 @@ static int CTL_READ_HANDLER(peak_memory)(void *ctx, static int CTL_READ_HANDLER(allocated_memory)(void *ctx, umf_ctl_query_source_t source, - void *arg, + void *arg, size_t size, umf_ctl_index_utlist_t *indexes, const char *extra_name, umf_ctl_query_type_t query_type) { /* suppress unused-parameter errors */ - (void)source, (void)indexes, (void)extra_name, (void)query_type; + (void)source, (void)size, (void)indexes, (void)extra_name, (void)query_type; size_t *arg_out = arg; CTL_PROVIDER_TYPE *provider = (CTL_PROVIDER_TYPE *)ctx; @@ -53,12 +53,13 @@ static int CTL_READ_HANDLER(allocated_memory)(void *ctx, } static int CTL_RUNNABLE_HANDLER(reset)(void *ctx, umf_ctl_query_source_t source, - void *arg, + void *arg, size_t size, umf_ctl_index_utlist_t *indexes, const char *extra_name, umf_ctl_query_type_t query_type) { /* suppress unused-parameter errors */ - (void)source, (void)indexes, (void)arg, (void)extra_name, (void)query_type; + (void)source, (void)indexes, (void)arg, (void)size, (void)extra_name, + (void)query_type; CTL_PROVIDER_TYPE *provider = (CTL_PROVIDER_TYPE *)ctx; size_t allocated; diff --git a/src/provider/provider_fixed_memory.c b/src/provider/provider_fixed_memory.c index 1ce937313..90c863994 100644 --- a/src/provider/provider_fixed_memory.c +++ b/src/provider/provider_fixed_memory.c @@ -280,11 +280,11 @@ static umf_result_t fixed_free(void *provider, void *ptr, size_t size) { } static umf_result_t fixed_ctl(void *provider, int operationType, - const char *name, void *arg, + const char *name, void *arg, size_t size, umf_ctl_query_type_t query_type) { utils_init_once(&ctl_initialized, initialize_fixed_ctl); return ctl_query(fixed_memory_ctl_root, provider, operationType, name, - query_type, arg); + query_type, arg, size); } static umf_memory_provider_ops_t UMF_FIXED_MEMORY_PROVIDER_OPS = { diff --git a/src/provider/provider_os_memory.c b/src/provider/provider_os_memory.c index 744293691..1b4d14f39 100644 --- a/src/provider/provider_os_memory.c +++ b/src/provider/provider_os_memory.c @@ -179,12 +179,13 @@ static UTIL_ONCE_FLAG ctl_initialized = UTIL_ONCE_FLAG_INIT; static int CTL_READ_HANDLER(ipc_enabled)(void *ctx, umf_ctl_query_source_t source, - void *arg, + void *arg, size_t size, umf_ctl_index_utlist_t *indexes, const char *extra_name, umf_ctl_query_type_t query_type) { /* suppress unused-parameter errors */ - (void)source, (void)indexes, (void)ctx, (void)extra_name, (void)query_type; + (void)source, (void)indexes, (void)ctx, (void)extra_name, (void)query_type, + (void)size; int *arg_out = arg; os_memory_provider_t *os_provider = (os_memory_provider_t *)ctx; @@ -1442,10 +1443,11 @@ static umf_result_t os_close_ipc_handle(void *provider, void *ptr, } static umf_result_t os_ctl(void *hProvider, int operationType, const char *name, - void *arg, umf_ctl_query_type_t query_type) { + void *arg, size_t size, + umf_ctl_query_type_t query_type) { utils_init_once(&ctl_initialized, initialize_os_ctl); return ctl_query(os_memory_ctl_root, hProvider, operationType, name, - query_type, arg); + query_type, arg, size); } static umf_memory_provider_ops_t UMF_OS_MEMORY_PROVIDER_OPS = { diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index adfbd69e5..3197e7232 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -245,6 +245,12 @@ add_umf_test( ${BA_SOURCES_FOR_TEST} LIBS ${UMF_UTILS_FOR_TEST}) +add_umf_test( + NAME disjoint_pool_ctl + SRCS pools/disjoint_pool_ctl.cpp malloc_compliance_tests.cpp + ${BA_SOURCES_FOR_TEST} + LIBS ${UMF_UTILS_FOR_TEST}) + add_umf_test( NAME c_api_disjoint_pool SRCS c_api/disjoint_pool.c ${BA_SOURCES_FOR_TEST} diff --git a/test/ctl/ctl_api.cpp b/test/ctl/ctl_api.cpp index 09ecfa877..7fea30820 100644 --- a/test/ctl/ctl_api.cpp +++ b/test/ctl/ctl_api.cpp @@ -7,7 +7,15 @@ * */ +#include +#include #include +#include // For std::ref +#include +#include +#include +#include +#include #include #include @@ -16,6 +24,9 @@ #include #include "../common/base.hpp" +#include "umf/base.h" +#include "umf/pools/pool_disjoint.h" +#include "umf/providers/provider_fixed_memory.h" #include "gtest/gtest.h" using namespace umf_test; @@ -35,7 +46,7 @@ TEST_F(test, ctl_by_handle_os_provider) { int ipc_enabled = 0xBAD; ret = umfCtlGet("umf.provider.by_handle.params.ipc_enabled", hProvider, - &ipc_enabled); + &ipc_enabled, 0); ASSERT_EQ(ret, UMF_RESULT_SUCCESS); ASSERT_EQ(ipc_enabled, 0); @@ -43,27 +54,7 @@ TEST_F(test, ctl_by_handle_os_provider) { umfMemoryProviderDestroy(hProvider); } -// Create a memory provider and a memory pool -umf_memory_provider_handle_t create_memory_provider() { - const umf_memory_provider_ops_t *provider_ops = umfOsMemoryProviderOps(); - umf_os_memory_provider_params_handle_t params = NULL; - umf_memory_provider_handle_t provider; - - int ret = umfOsMemoryProviderParamsCreate(¶ms); - if (ret != UMF_RESULT_SUCCESS) { - return 0; - } - - ret = umfMemoryProviderCreate(provider_ops, params, &provider); - umfOsMemoryProviderParamsDestroy(params); - if (ret != UMF_RESULT_SUCCESS) { - return 0; - } - - return provider; -} - -class CtlTest : public ::testing::Test { +class Pool { public: class CtlException : public std::exception { public: @@ -74,12 +65,7 @@ class CtlTest : public ::testing::Test { const char *msg; }; - CtlTest() : provider(NULL), pool(NULL) {} - - void SetUp() override { - provider = NULL; - pool = NULL; - } + Pool() : provider(NULL), pool(NULL) {} void instantiatePool(const umf_memory_pool_ops_t *pool_ops, const void *pool_params, @@ -87,59 +73,289 @@ class CtlTest : public ::testing::Test { freeResources(); provider = create_memory_provider(); if (provider == NULL) { - throw CtlException("Failed to create a memory provider!"); + throw CtlException("Provider is not supported!"); } int ret = umfPoolCreate(pool_ops, provider, pool_params, flags, &pool); if (ret != UMF_RESULT_SUCCESS) { - throw CtlException("Failed to create a memory provider!"); + umfMemoryProviderDestroy(provider); + throw CtlException("Failed to create memory pool"); + } + } + + // Template specialization for different types of reference value + template T getReferenceValue() { + if constexpr (std::is_arithmetic_v) { + return 0xBAD; + } else if constexpr (std::is_same_v) { + return "0xBAD"; } } template - void validateQuery( - std::function - ctlApiFunction, - const char *name, T expectedValue, umf_result_t expected) { - T value = 0xBAD; - umf_result_t ret = ctlApiFunction(name, pool, &value); + void validateQuery(std::function + ctlApiFunction, + const char *name, T expectedValue, umf_result_t expected, + bool disableContext = 0) { + T value = getReferenceValue(); + umf_result_t ret; + char ret_buf[256] = {0}; + if constexpr (std::is_same_v) { + strncpy(ret_buf, value.c_str(), sizeof(ret_buf)); + ret = ctlApiFunction(name, disableContext ? nullptr : pool, + (void *)ret_buf, sizeof(ret_buf)); + } else { + strncpy(ret_buf, value, sizeof(value)); + ret = ctlApiFunction(name, disableContext ? nullptr : pool, &value, + sizeof(value)); + } + ASSERT_EQ(ret, expected); if (ret == UMF_RESULT_SUCCESS) { - ASSERT_EQ(value, expectedValue); + ASSERT_EQ(ret_buf, expectedValue); } - ASSERT_EQ(ret, UMF_RESULT_SUCCESS); } - void TearDown() override { freeResources(); } + template + void executeQuery(std::function + ctlApiFunction, + const char *name, T value, bool disableContext = 0) { + size_t value_len; + if constexpr (std::is_arithmetic_v) { + value_len = sizeof(value); + } else if constexpr (std::is_same_v) { + value_len = strlen(value.c_str()); + } else if constexpr (std::is_same_v) { + value_len = strlen(value); + } else { + throw CtlException("Unsupported type for value"); + } + umf_result_t ret = ctlApiFunction(name, disableContext ? nullptr : pool, + (void *)value, value_len); + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + } - private: void freeResources() { if (pool) { umfPoolDestroy(pool); + pool = NULL; } if (provider) { umfMemoryProviderDestroy(provider); + provider = NULL; + } + if (data) { + free(data); + data = nullptr; } } umf_memory_provider_handle_t provider; umf_memory_pool_handle_t pool; + void *data = nullptr; + + private: + // Create a memory provider + umf_memory_provider_handle_t create_memory_provider() { + const umf_memory_provider_ops_t *provider_ops = + umfFixedMemoryProviderOps(); + umf_fixed_memory_provider_params_handle_t params = NULL; + + data = malloc(1024 * 1024); + int ret = + umfFixedMemoryProviderParamsCreate(¶ms, data, 1024 * 1024); + if (ret != UMF_RESULT_SUCCESS) { + return 0; + } + + ret = umfMemoryProviderCreate(provider_ops, params, &provider); + umfFixedMemoryProviderParamsDestroy(params); + if (ret != UMF_RESULT_SUCCESS) { + return 0; + } + + return provider; + } }; -TEST_F(CtlTest, ctl_by_handle_scalablePool) { +class CtlTest : public ::testing::Test { + public: + CtlTest() {} + + void SetUp() override {} + + void TearDown() override {} + + private: +}; + +TEST_F(CtlTest, ctlDefaultNegative) { + void *ctx = (void *)0xBABE; + void *arg = (void *)0xBABE; + + auto res = umfCtlGet("umf.pool.default.disjoint.some_path", ctx, arg, 0); + ASSERT_EQ(res, UMF_RESULT_ERROR_INVALID_ARGUMENT); + + res = umfCtlSet("umf.pool.default.disjoint.some_path", ctx, arg, 0); + ASSERT_EQ(res, UMF_RESULT_ERROR_INVALID_ARGUMENT); +} + +/* Case: default settings + * This test sets a default value and then retrieves it */ +TEST_F(CtlTest, ctlDefault) { + void *ctx = NULL; + const char *arg = "default_name"; + + auto res = umfCtlSet("umf.pool.default.some_pool.some_path", ctx, + (void *)arg, strlen(arg)); + ASSERT_EQ(res, UMF_RESULT_SUCCESS); + + char output[64] = {0}; + res = umfCtlGet("umf.pool.default.some_pool.some_path", ctx, (void *)output, + sizeof(output)); + ASSERT_EQ(res, UMF_RESULT_SUCCESS); + ASSERT_STREQ(output, arg); +} + +/* Case: multi-threaded test for pool defaults + * This test sets a default value in multiple threads and then retrieves it */ +TEST_F(CtlTest, ctlDefaultPoolMultithreaded) { + std::vector threads; + std::atomic totalRecords = 0; + const char *predefined_value = "xyzzyx"; + std::string name_prefix = "umf.pool.default.some_pool."; + for (int i = 0; i < 8; i++) { + threads.emplace_back( + [i, &totalRecords, &predefined_value, &name_prefix]() { + for (int j = 0; j < 10; j++) { + std::string name = name_prefix + std::to_string(i * 10 + j); + umfCtlSet(name.c_str(), NULL, (void *)predefined_value, + strlen(predefined_value)); + std::atomic_fetch_add(&totalRecords, 1UL); + } + }); + } + for (auto &thread : threads) { + thread.join(); + } + + char output[100] = {0}; + for (size_t i = 0; i < totalRecords.load(); i++) { + std::string name = name_prefix + std::to_string(i); + auto status = + umfCtlGet(name.c_str(), nullptr, (void *)output, sizeof(output)); + ASSERT_EQ(status, UMF_RESULT_SUCCESS); + ASSERT_EQ(std::string(output), std::string(predefined_value)); + } +} + +/* Case: overwriting an existing value for pool defaults + * This test sets a default value and then overwrites it with a new value */ +TEST_F(CtlTest, ctlDefaultPoolOverwrite) { + constexpr int max_size = 10; + std::vector values; + const std::string name = "umf.pool.default.some_pool"; + + for (int i = 0; i < max_size; i++) { + values.push_back("value_" + std::to_string(i)); + umfCtlSet(name.c_str(), NULL, (void *)values.back().c_str(), + values.back().size()); + } + + char output[100] = {0}; + umf_result_t status = + umfCtlGet(name.c_str(), NULL, (void *)output, sizeof(output)); + ASSERT_EQ(status, UMF_RESULT_SUCCESS); + ASSERT_EQ(std::string(output), values.back()); +} + +TEST_F(CtlTest, ctlNameValidation) { + std::string name = "umf.pool.default.disjoint.name"; + std::string value = "new_disjoint_pool_name"; + umf_disjoint_pool_params_handle_t params = NULL; + + Pool p; + try { + p.executeQuery(umfCtlSet, name.c_str(), value.c_str(), true); + umf_result_t res = umfDisjointPoolParamsCreate(¶ms); + ASSERT_EQ(res, UMF_RESULT_SUCCESS); + + p.instantiatePool(umfDisjointPoolOps(), params); + p.validateQuery(umfCtlGet, "umf.pool.by_handle.disjoint.name", value, + UMF_RESULT_SUCCESS); + + umfDisjointPoolParamsDestroy(params); + } catch (Pool::CtlException &e) { + umfDisjointPoolParamsDestroy(params); + GTEST_SKIP() << e.what(); + } catch (...) { + GTEST_FAIL() << "Unknown exception!"; + } + p.freeResources(); +} + +TEST_F(CtlTest, ctlSizeValidation) { + std::string name = "umf.pool.default.disjoint.name"; + std::string value = "1234567890"; + umf_disjoint_pool_params_handle_t params = NULL; + + Pool p; try { - instantiatePool(umfScalablePoolOps(), NULL); - validateQuery(umfCtlGet, - "umf.pool.by_handle.params.tracking_enabled", 1, - UMF_RESULT_SUCCESS); - - instantiatePool(umfScalablePoolOps(), NULL, - UMF_POOL_CREATE_FLAG_DISABLE_TRACKING); - validateQuery(umfCtlGet, - "umf.pool.by_handle.params.tracking_enabled", 0, - UMF_RESULT_SUCCESS); - } catch (CtlTest::CtlException &e) { + p.executeQuery(umfCtlSet, name.c_str(), value.c_str(), true); + umf_result_t res = umfDisjointPoolParamsCreate(¶ms); + ASSERT_EQ(res, UMF_RESULT_SUCCESS); + + p.instantiatePool(umfDisjointPoolOps(), params); + char output[100] = {0}; + umfCtlGet("umf.pool.default.disjoint.name", NULL, output, + sizeof(output)); + ASSERT_EQ(std::string(output), value); + + memset(output, 0, sizeof(output)); + umfCtlGet("umf.pool.default.disjoint.name", NULL, output, + value.size() / 2); + auto half_value = value.substr(0, value.size() / 2); + ASSERT_EQ(half_value, std::string(output)); + + umfDisjointPoolParamsDestroy(params); + } catch (Pool::CtlException &e) { + umfDisjointPoolParamsDestroy(params); GTEST_SKIP() << e.what(); } catch (...) { GTEST_FAIL() << "Unknown exception!"; } + p.freeResources(); +} + +#ifdef NOT_IMPLEMENTED_YET +TEST_F(CtlTest, ctlDefaultMultithreadedProvider) { + std::vector threads; + std::atomic totalRecords = 0; + const char *predefined_value = "xyzzyx"; + std::string name_prefix = "umf.provider.default.some_pool."; + for (int i = 0; i < 8; i++) { + threads.emplace_back( + [i, &totalRecords, &predefined_value, &name_prefix]() { + for (int j = 0; j < 10; j++) { + std::string name = name_prefix + std::to_string(i * 10 + j); + umfCtlSet(name.c_str(), NULL, (void *)predefined_value, + strlen(predefined_value)); + std::atomic_fetch_add(&totalRecords, 1); + } + }); + } + for (auto &thread : threads) { + thread.join(); + } + + char output[100] = {0}; + for (size_t i = 0; i < totalRecords.load(); i++) { + std::string name = name_prefix + std::to_string(i); + auto status = + umfCtlGet(name.c_str(), nullptr, (void *)output, sizeof(output)); + ASSERT_EQ(status, UMF_RESULT_SUCCESS); + ASSERT_EQ(std::string(output), std::string(predefined_value)); + } } +#endif \ No newline at end of file diff --git a/test/ctl/ctl_debug.c b/test/ctl/ctl_debug.c index 5bc2920ea..444d4d21b 100644 --- a/test/ctl/ctl_debug.c +++ b/test/ctl/ctl_debug.c @@ -26,12 +26,13 @@ struct ctl *get_debug_ctl(void) { return ctl_debug; } */ static int CTL_WRITE_HANDLER(alloc_pattern)(void *ctx, umf_ctl_query_source_t source, - void *arg, + void *arg, size_t size, umf_ctl_index_utlist_t *indexes, const char *extra_name, umf_ctl_query_type_t query_type) { /* suppress unused-parameter errors */ - (void)source, (void)indexes, (void)ctx, (void)extra_name, (void)query_type; + (void)source, (void)indexes, (void)ctx, (void)extra_name, (void)query_type, + (void)size; int arg_in = *(int *)arg; alloc_pattern = arg_in; @@ -43,12 +44,13 @@ static int CTL_WRITE_HANDLER(alloc_pattern)(void *ctx, */ static int CTL_READ_HANDLER(alloc_pattern)(void *ctx, umf_ctl_query_source_t source, - void *arg, + void *arg, size_t size, umf_ctl_index_utlist_t *indexes, const char *extra_name, umf_ctl_query_type_t query_type) { /* suppress unused-parameter errors */ - (void)source, (void)indexes, (void)ctx, (void)extra_name, (void)query_type; + (void)source, (void)indexes, (void)ctx, (void)extra_name, (void)query_type, + (void)size; int *arg_out = arg; *arg_out = alloc_pattern; @@ -57,12 +59,13 @@ static int CTL_READ_HANDLER(alloc_pattern)(void *ctx, static int CTL_WRITE_HANDLER(enable_logging)(void *ctx, umf_ctl_query_source_t source, - void *arg, + void *arg, size_t size, umf_ctl_index_utlist_t *indexes, const char *extra_name, umf_ctl_query_type_t query_type) { /* suppress unused-parameter errors */ - (void)source, (void)indexes, (void)ctx, (void)extra_name, (void)query_type; + (void)source, (void)indexes, (void)ctx, (void)extra_name, (void)query_type, + (void)size; int arg_in = *(int *)arg; enable_logging = arg_in; @@ -71,12 +74,13 @@ static int CTL_WRITE_HANDLER(enable_logging)(void *ctx, static int CTL_READ_HANDLER(enable_logging)(void *ctx, umf_ctl_query_source_t source, - void *arg, + void *arg, size_t size, umf_ctl_index_utlist_t *indexes, const char *extra_name, umf_ctl_query_type_t query_type) { /* suppress unused-parameter errors */ - (void)source, (void)indexes, (void)ctx, (void)extra_name, (void)query_type; + (void)source, (void)indexes, (void)ctx, (void)extra_name, (void)query_type, + (void)size; int *arg_out = arg; *arg_out = enable_logging; @@ -85,12 +89,13 @@ static int CTL_READ_HANDLER(enable_logging)(void *ctx, static int CTL_WRITE_HANDLER(log_level)(void *ctx, umf_ctl_query_source_t source, - void *arg, + void *arg, size_t size, umf_ctl_index_utlist_t *indexes, const char *extra_name, umf_ctl_query_type_t query_type) { /* suppress unused-parameter errors */ - (void)source, (void)indexes, (void)ctx, (void)extra_name, (void)query_type; + (void)source, (void)indexes, (void)ctx, (void)extra_name, (void)query_type, + (void)size; int arg_in = *(int *)arg; log_level = arg_in; @@ -98,12 +103,13 @@ static int CTL_WRITE_HANDLER(log_level)(void *ctx, } static int CTL_READ_HANDLER(log_level)(void *ctx, umf_ctl_query_source_t source, - void *arg, + void *arg, size_t size, umf_ctl_index_utlist_t *indexes, const char *extra_name, umf_ctl_query_type_t query_type) { /* suppress unused-parameter errors */ - (void)source, (void)indexes, (void)ctx, (void)extra_name, (void)query_type; + (void)source, (void)indexes, (void)ctx, (void)extra_name, (void)query_type, + (void)size; int *arg_out = arg; *arg_out = log_level; diff --git a/test/ctl/ctl_unittest.cpp b/test/ctl/ctl_unittest.cpp index c35759c67..b64b98866 100644 --- a/test/ctl/ctl_unittest.cpp +++ b/test/ctl/ctl_unittest.cpp @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2024 Intel Corporation + * Copyright (C) 2024-2025 Intel Corporation * * Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -21,31 +21,32 @@ TEST_F(test, ctl_debug_read_from_string) { int value = 0; ctl_query(ctl_handler, NULL, CTL_QUERY_PROGRAMMATIC, - "debug.heap.alloc_pattern", CTL_QUERY_READ, &value); + "debug.heap.alloc_pattern", CTL_QUERY_READ, &value, 0); ASSERT_EQ(value, 1); // Test setting alloc_pattern to 2 ctl_load_config_from_string(ctl_handler, NULL, "debug.heap.alloc_pattern=2"); ctl_query(ctl_handler, NULL, CTL_QUERY_PROGRAMMATIC, - "debug.heap.alloc_pattern", CTL_QUERY_READ, &value); + "debug.heap.alloc_pattern", CTL_QUERY_READ, &value, 0); ASSERT_EQ(value, 2); // Test setting alloc_pattern to 0 ctl_load_config_from_string(ctl_handler, NULL, "debug.heap.alloc_pattern=0"); ctl_query(ctl_handler, NULL, CTL_QUERY_PROGRAMMATIC, - "debug.heap.alloc_pattern", CTL_QUERY_READ, &value); + "debug.heap.alloc_pattern", CTL_QUERY_READ, &value, 0); ASSERT_EQ(value, 0); // Negative test: non-existent configuration ASSERT_NE(ctl_query(ctl_handler, NULL, CTL_QUERY_PROGRAMMATIC, - "debug.heap.non_existent", CTL_QUERY_READ, &value), + "debug.heap.non_existent", CTL_QUERY_READ, &value, 0), 0); // Negative test: invalid path ASSERT_NE(ctl_query(ctl_handler, NULL, CTL_QUERY_PROGRAMMATIC, - "invalid.path.alloc_pattern", CTL_QUERY_READ, &value), + "invalid.path.alloc_pattern", CTL_QUERY_READ, &value, + 0), 0); debug_ctl_register(ctl_handler); @@ -74,17 +75,17 @@ TEST_F(test, ctl_debug_read_from_file) { int value = 0; ctl_query(ctl_handler, NULL, CTL_QUERY_PROGRAMMATIC, - "debug.heap.alloc_pattern", CTL_QUERY_READ, &value); + "debug.heap.alloc_pattern", CTL_QUERY_READ, &value, 0); ASSERT_EQ(value, 321); value = 0; ctl_query(ctl_handler, NULL, CTL_QUERY_PROGRAMMATIC, "debug.heap.log_level", - CTL_QUERY_READ, &value); + CTL_QUERY_READ, &value, 0); ASSERT_EQ(value, 5); value = 0; ctl_query(ctl_handler, NULL, CTL_QUERY_PROGRAMMATIC, - "debug.heap.enable_logging", CTL_QUERY_READ, &value); + "debug.heap.enable_logging", CTL_QUERY_READ, &value, 0); ASSERT_EQ(value, 1); debug_ctl_register(ctl_handler); diff --git a/test/pools/disjoint_pool.cpp b/test/pools/disjoint_pool.cpp index f64e61931..e080aa080 100644 --- a/test/pools/disjoint_pool.cpp +++ b/test/pools/disjoint_pool.cpp @@ -12,6 +12,8 @@ #include "provider.hpp" #include "provider_null.h" #include "provider_trace.h" +#include "umf/base.h" +#include "umf/memory_pool.h" using umf_test::test; using namespace umf_test; @@ -327,6 +329,54 @@ TEST_F(test, disjointPoolInvalidBucketSize) { umfDisjointPoolParamsDestroy(params); } +TEST_F(test, disjointPoolName) { + umf_disjoint_pool_params_handle_t params = nullptr; + umf_result_t res = umfDisjointPoolParamsCreate(¶ms); + EXPECT_EQ(res, UMF_RESULT_SUCCESS); + umf_memory_provider_handle_t provider_handle = nullptr; + umf_memory_pool_handle_t pool = NULL; + + struct memory_provider : public umf_test::provider_base_t { + umf_result_t expectedResult; + umf_result_t alloc(size_t size, size_t alignment, void **ptr) noexcept { + *ptr = umf_ba_global_aligned_alloc(size, alignment); + return UMF_RESULT_SUCCESS; + } + + umf_result_t free(void *ptr, [[maybe_unused]] size_t size) noexcept { + // do the actual free only when we expect the success + if (expectedResult == UMF_RESULT_SUCCESS) { + umf_ba_global_free(ptr); + } + return expectedResult; + } + + umf_result_t + get_min_page_size([[maybe_unused]] const void *ptr, + [[maybe_unused]] size_t *pageSize) noexcept { + *pageSize = 1024; + return UMF_RESULT_SUCCESS; + } + }; + umf_memory_provider_ops_t provider_ops = + umf_test::providerMakeCOps(); + + auto providerUnique = + wrapProviderUnique(createProviderChecked(&provider_ops, nullptr)); + + provider_handle = providerUnique.get(); + + res = + umfPoolCreate(umfDisjointPoolOps(), provider_handle, params, 0, &pool); + EXPECT_EQ(res, UMF_RESULT_SUCCESS); + const char *name = umfPoolGetName(pool); + EXPECT_STREQ(name, "disjoint"); + + EXPECT_EQ(umfPoolGetName(nullptr), nullptr); + umfPoolDestroy(pool); + umfDisjointPoolParamsDestroy(params); +} + INSTANTIATE_TEST_SUITE_P(disjointPoolTests, umfPoolTest, ::testing::Values(poolCreateExtParams{ umfDisjointPoolOps(), defaultDisjointPoolConfig, diff --git a/test/pools/disjoint_pool_ctl.cpp b/test/pools/disjoint_pool_ctl.cpp new file mode 100644 index 000000000..421e582ca --- /dev/null +++ b/test/pools/disjoint_pool_ctl.cpp @@ -0,0 +1,153 @@ +// Copyright (C) 2025 Intel Corporation +// Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exceptiongi + +#include +#include +#include +#include +#include +#include + +#include "base.hpp" +#include "utils_log.h" + +using umf_test::test; +using namespace umf_test; + +#define RETURN_SUCCESS(ret) ASSERT_EQ(ret, UMF_RESULT_SUCCESS) + +// Encapsulating class for pool creation and destruction +class PoolWrapper { + public: + PoolWrapper(umf_memory_provider_handle_t provider, + const umf_memory_pool_ops_t *poolOps, + umf_disjoint_pool_params_handle_t params = nullptr) + : m_pool(nullptr), m_provider(provider), m_poolOps(poolOps), + m_params(params) { + auto res = umfPoolCreate(m_poolOps, m_provider, m_params, 0, &m_pool); + if (res != UMF_RESULT_SUCCESS) { + m_pool = nullptr; + LOG_ERR("Failed to create memory pool"); + } + } + + ~PoolWrapper() { + if (m_pool) { + umfPoolDestroy(m_pool); + } + } + + umf_memory_pool_handle_t get() const { return m_pool; } + + // Disallow copy and move + PoolWrapper(const PoolWrapper &) = delete; + PoolWrapper &operator=(const PoolWrapper &) = delete; + PoolWrapper(PoolWrapper &&) = delete; + PoolWrapper &operator=(PoolWrapper &&) = delete; + + private: + umf_memory_pool_handle_t m_pool; + umf_memory_provider_handle_t m_provider; + const umf_memory_pool_ops_t *m_poolOps; + umf_disjoint_pool_params_handle_t m_params; +}; + +// Encapsulating class for provider creation and destruction +class ProviderWrapper { + public: + ProviderWrapper(const umf_memory_provider_ops_t *providerOps, + void *params = nullptr) + : m_provider(nullptr), m_providerOps(providerOps), m_params(params) { + auto res = + umfMemoryProviderCreate(m_providerOps, m_params, &m_provider); + if (res != UMF_RESULT_SUCCESS) { + m_provider = nullptr; + LOG_ERR("Failed to create memory provider"); + } + } + + ~ProviderWrapper() { + if (m_provider) { + umfMemoryProviderDestroy(m_provider); + } + } + + umf_memory_provider_handle_t get() const { return m_provider; } + + // Disallow copy and move + ProviderWrapper(const ProviderWrapper &) = delete; + ProviderWrapper &operator=(const ProviderWrapper &) = delete; + ProviderWrapper(ProviderWrapper &&) = delete; + ProviderWrapper &operator=(ProviderWrapper &&) = delete; + + private: + umf_memory_provider_handle_t m_provider; + const umf_memory_provider_ops_t *m_providerOps; + void *m_params; +}; + +TEST_F(test, disjointCtlName) { + umf_os_memory_provider_params_handle_t os_memory_provider_params = nullptr; + if (UMF_RESULT_ERROR_NOT_SUPPORTED == + umfOsMemoryProviderParamsCreate(&os_memory_provider_params)) { + GTEST_SKIP() << "OS memory provider is not supported!"; + } + + ProviderWrapper providerWrapper(umfOsMemoryProviderOps(), + os_memory_provider_params); + if (providerWrapper.get() == NULL) { + GTEST_SKIP() << "OS memory provider is not supported!"; + } + + // Set default name + const char *val = "disjoint_new_name"; + RETURN_SUCCESS(umfCtlSet("umf.pool.default.disjoint.name", NULL, + (void *)val, strlen(val))); + + umf_disjoint_pool_params_handle_t params = nullptr; + RETURN_SUCCESS(umfDisjointPoolParamsCreate(¶ms)); + PoolWrapper poolWrapper(providerWrapper.get(), umfDisjointPoolOps(), + params); + + // Check that the default name is correctly set + const char *name = umfPoolGetName(poolWrapper.get()); + ASSERT_STREQ(name, val); + + // Clean up + RETURN_SUCCESS(umfDisjointPoolParamsDestroy(params)); + RETURN_SUCCESS(umfOsMemoryProviderParamsDestroy(os_memory_provider_params)); +} + +TEST_F(test, disjointCtlChangeNameTwice) { + umf_os_memory_provider_params_handle_t os_memory_provider_params = nullptr; + if (UMF_RESULT_ERROR_NOT_SUPPORTED == + umfOsMemoryProviderParamsCreate(&os_memory_provider_params)) { + GTEST_SKIP() << "OS memory provider is not supported!"; + } + ProviderWrapper providerWrapper(umfOsMemoryProviderOps(), + os_memory_provider_params); + if (providerWrapper.get() == NULL) { + GTEST_SKIP() << "OS memory provider is not supported!"; + } + // Set default name + const char *val = "disjoint_new_name"; + const char *val2 = "another_name"; + RETURN_SUCCESS(umfCtlSet("umf.pool.default.disjoint.name", NULL, + (void *)val, strlen(val))); + RETURN_SUCCESS(umfCtlSet("umf.pool.default.disjoint.name", NULL, + (void *)val2, strlen(val2))); + + umf_disjoint_pool_params_handle_t params = nullptr; + RETURN_SUCCESS(umfDisjointPoolParamsCreate(¶ms)); + PoolWrapper poolWrapper(providerWrapper.get(), umfDisjointPoolOps(), + params); + + // Check that the default name is correctly set + const char *name = umfPoolGetName(poolWrapper.get()); + ASSERT_STREQ(name, val2); + + // Clean up + RETURN_SUCCESS(umfDisjointPoolParamsDestroy(params)); + RETURN_SUCCESS(umfOsMemoryProviderParamsDestroy(os_memory_provider_params)); +}