From 8b6d3a03f12631cb18fcde4c178c4438c412167b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Plewa?= Date: Wed, 30 Jul 2025 17:15:57 +0200 Subject: [PATCH] add pool_by_name --- .github/workflows/reusable_compatibility.yml | 25 +- docs/config/spelling_exceptions.txt | 3 +- include/umf/memory_pool_ops.h | 7 +- include/umf/memory_provider_ops.h | 12 +- include/umf/pools/pool_disjoint.h | 2 + include/umf/pools/pool_jemalloc.h | 2 + include/umf/pools/pool_scalable.h | 2 + include/umf/providers/provider_cuda.h | 2 + .../umf/providers/provider_devdax_memory.h | 2 + include/umf/providers/provider_file_memory.h | 2 + include/umf/providers/provider_fixed_memory.h | 2 + include/umf/providers/provider_level_zero.h | 2 + include/umf/providers/provider_os_memory.h | 2 + src/base_alloc/base_alloc_global.c | 16 ++ src/base_alloc/base_alloc_global.h | 1 + src/ctl/ctl.c | 163 ++++++++--- src/ctl/ctl_internal.h | 64 +++-- src/memory_pool.c | 254 ++++++++++++++++++ src/memory_provider.c | 8 + src/utils/utils_name.h | 46 ++++ test/ctl/ctl_api.cpp | 188 +++++++++++++ test/memoryProviderAPI.cpp | 6 +- 22 files changed, 740 insertions(+), 71 deletions(-) create mode 100644 src/utils/utils_name.h diff --git a/.github/workflows/reusable_compatibility.yml b/.github/workflows/reusable_compatibility.yml index 5503a8110..573812ba4 100644 --- a/.github/workflows/reusable_compatibility.yml +++ b/.github/workflows/reusable_compatibility.yml @@ -95,10 +95,12 @@ jobs: - name: Run "tag" UMF tests with latest UMF libs (warnings enabled) working-directory: ${{github.workspace}}/tag_version/build - run: > - UMF_LOG="level:warning;flush:debug;output:stderr;pid:no" - LD_LIBRARY_PATH=${{github.workspace}}/latest_version/build/lib/ - ctest --verbose + env: + UMF_LOG: level:warning;flush:debug;output:stderr;pid:no + LD_LIBRARY_PATH: ${{github.workspace}}/latest_version/build/lib/ + run: | + ctest --verbose -E test_memoryProvider + test/test_memoryProvider --gtest_filter="-*Trace" # Browse all folders in the examples directory, build them using the # latest UMF version, and run them, excluding those in the exclude list. @@ -220,10 +222,11 @@ jobs: - name: Run "tag" UMF tests with latest UMF libs (warnings enabled) working-directory: ${{github.workspace}}/tag_version/build + env: + UMF_LOG: level:warning;flush:debug;output:stderr;pid:no run: | - $env:UMF_LOG="level:warning;flush:debug;output:stderr;pid:no" cp ${{github.workspace}}/latest_version/build/bin/Debug/umf.dll ${{github.workspace}}/tag_version/build/bin/Debug/umf.dll - ctest -C Debug --verbose + ctest -C Debug --verbose -E test_memoryProvider # Browse all folders in the examples directory, build them using the # latest UMF version, and run them, excluding those in the exclude list. @@ -361,10 +364,12 @@ jobs: - name: Run "tag" UMF tests with latest UMF libs (warnings enabled) working-directory: ${{github.workspace}}/tag_version/build - run: > - UMF_LOG="level:warning;flush:debug;output:stderr;pid:no" - LD_LIBRARY_PATH=${{github.workspace}}/latest_version/build/lib/ - ctest --verbose + env: + UMF_LOG: level:warning;flush:debug;output:stderr;pid:no + LD_LIBRARY_PATH: ${{github.workspace}}/latest_version/build/lib/ + run: | + ctest --verbose -E test_memoryProvider + test/test_memoryProvider --gtest_filter="-*Trace" # Browse all folders in the examples directory, build them using the # latest UMF version, and run them, excluding those in the exclude list. diff --git a/docs/config/spelling_exceptions.txt b/docs/config/spelling_exceptions.txt index d4e40a3ec..9515ed2dd 100644 --- a/docs/config/spelling_exceptions.txt +++ b/docs/config/spelling_exceptions.txt @@ -71,4 +71,5 @@ umfPoolMallocUsableSize umfPoolRealloc umfMemspaceUserFilter umfMemspaceMemtargetAdd -unfreed \ No newline at end of file +unfreed +zA diff --git a/include/umf/memory_pool_ops.h b/include/umf/memory_pool_ops.h index 4cba05319..a4d9bc391 100644 --- a/include/umf/memory_pool_ops.h +++ b/include/umf/memory_pool_ops.h @@ -138,8 +138,13 @@ typedef struct umf_memory_pool_ops_t { /// /// * Implementations *must* return default pool name when NULL is provided, /// otherwise the pool's name is returned. - /// @return UMF_RESULT_SUCCESS on success or appropriate error code on failure. /// + /// * The returned name should not exceed 64 characters including null character and may contain + /// only [a-zA-Z0-9_-] characters. Names violating these rules are deprecated + /// and will not be supported in the next major API release. + /// CTL functionality may be limited if other characters are returned. + /// + /// @return UMF_RESULT_SUCCESS on success or appropriate error code on failure. umf_result_t (*get_name)(void *pool, const char **name); /// diff --git a/include/umf/memory_provider_ops.h b/include/umf/memory_provider_ops.h index 16e1536fd..5ea5e0a87 100644 --- a/include/umf/memory_provider_ops.h +++ b/include/umf/memory_provider_ops.h @@ -123,8 +123,18 @@ typedef struct umf_memory_provider_ops_t { /// @brief Retrieve name of a given memory \p provider. /// @param provider pointer to the memory provider /// @param name [out] pointer to a string containing the name of the \p provider - /// @return UMF_RESULT_SUCCESS on success or appropriate error code on failure. + /// \details + /// * Implementations *must* return a literal null-terminated string. + /// + /// * Implementations *must* return default pool name when NULL is provided, + /// otherwise the pool's name is returned. /// + /// * The returned name should not exceed 64 characters and may contain + /// only [a-zA-Z0-9_-] characters. Names violating these rules are deprecated + /// and will not be supported in the next major API release. + /// CTL functionality may be limited if other characters are returned. + /// + /// @return UMF_RESULT_SUCCESS on success or appropriate error code on failure. umf_result_t (*get_name)(void *provider, const char **name); /// diff --git a/include/umf/pools/pool_disjoint.h b/include/umf/pools/pool_disjoint.h index 85b54d183..1758fee89 100644 --- a/include/umf/pools/pool_disjoint.h +++ b/include/umf/pools/pool_disjoint.h @@ -111,6 +111,8 @@ umf_result_t umfDisjointPoolParamsSetSharedLimits( /// @param hParams handle to the parameters of the disjoint pool. /// @param name custom name of the pool. Must not be NULL. Name longer than 63 /// characters will be truncated. +/// \details Name should contain only [a-zA-Z0-9_-] characters. +/// Other names are deprecated and may limit CTL functionality. /// @return UMF_RESULT_SUCCESS on success or appropriate error code on failure. umf_result_t umfDisjointPoolParamsSetName(umf_disjoint_pool_params_handle_t hParams, diff --git a/include/umf/pools/pool_jemalloc.h b/include/umf/pools/pool_jemalloc.h index 87901a6ee..f7c881b6c 100644 --- a/include/umf/pools/pool_jemalloc.h +++ b/include/umf/pools/pool_jemalloc.h @@ -47,6 +47,8 @@ umfJemallocPoolParamsSetNumArenas(umf_jemalloc_pool_params_handle_t hParams, /// @param hParams handle to the parameters of the jemalloc pool. /// @param name custom name. Must not be NULL. Name longer than 63 characters /// will be truncated. +/// \details Name should contain only [a-zA-Z0-9_-] characters. +/// Other names are deprecated and may limit CTL functionality. /// @return UMF_RESULT_SUCCESS on success or appropriate error code on failure. umf_result_t umfJemallocPoolParamsSetName(umf_jemalloc_pool_params_handle_t hParams, diff --git a/include/umf/pools/pool_scalable.h b/include/umf/pools/pool_scalable.h index c556909ab..749cd8a39 100644 --- a/include/umf/pools/pool_scalable.h +++ b/include/umf/pools/pool_scalable.h @@ -57,6 +57,8 @@ umfScalablePoolParamsSetKeepAllMemory(umf_scalable_pool_params_handle_t hParams, /// @param hParams handle to the parameters of the scalable pool. /// @param name custom name. Must not be NULL. Name longer than 63 characters /// will be truncated. +/// \details Name should contain only [a-zA-Z0-9_-] characters. +/// Other names are deprecated and may limit CTL functionality. /// @return UMF_RESULT_SUCCESS on success or appropriate error code on failure. umf_result_t umfScalablePoolParamsSetName(umf_scalable_pool_params_handle_t hParams, diff --git a/include/umf/providers/provider_cuda.h b/include/umf/providers/provider_cuda.h index 9e99f9aaf..dbed42219 100644 --- a/include/umf/providers/provider_cuda.h +++ b/include/umf/providers/provider_cuda.h @@ -65,6 +65,8 @@ umf_result_t umfCUDAMemoryProviderParamsSetAllocFlags( /// @param hParams handle to the parameters of the CUDA Memory Provider. /// @param name custom name. Must not be NULL. Name longer than 63 characters /// will be truncated. +/// \details Name should contain only [a-zA-Z0-9_-] characters. +/// Other names are deprecated and may limit CTL functionality. /// @return UMF_RESULT_SUCCESS on success or appropriate error code on failure. umf_result_t umfCUDAMemoryProviderParamsSetName( umf_cuda_memory_provider_params_handle_t hParams, const char *name); diff --git a/include/umf/providers/provider_devdax_memory.h b/include/umf/providers/provider_devdax_memory.h index 75f96398e..d57b1c5de 100644 --- a/include/umf/providers/provider_devdax_memory.h +++ b/include/umf/providers/provider_devdax_memory.h @@ -60,6 +60,8 @@ umf_result_t umfDevDaxMemoryProviderParamsSetProtection( /// @param hParams [in] handle to the parameters of the Devdax Memory Provider. /// @param name [in] custom name. Must not be NULL. Name longer than 63 characters /// will be truncated. +/// \details Name should contain only [a-zA-Z0-9_-] characters. +/// Other names are deprecated and may limit CTL functionality. /// @return UMF_RESULT_SUCCESS on success or appropriate error code on failure. umf_result_t umfDevDaxMemoryProviderParamsSetName( umf_devdax_memory_provider_params_handle_t hParams, const char *name); diff --git a/include/umf/providers/provider_file_memory.h b/include/umf/providers/provider_file_memory.h index 3e6ec908e..5586cfe7c 100644 --- a/include/umf/providers/provider_file_memory.h +++ b/include/umf/providers/provider_file_memory.h @@ -72,6 +72,8 @@ const umf_memory_provider_ops_t *umfFileMemoryProviderOps(void); /// @param hParams handle to the parameters of the File Memory Provider. /// @param name custom name. Must not be NULL. Name longer than 63 characters /// will be truncated. +/// \details Name should contain only [a-zA-Z0-9_-] characters. +/// Other names are deprecated and may limit CTL functionality. /// @return UMF_RESULT_SUCCESS on success or appropriate error code on failure. umf_result_t umfFileMemoryProviderParamsSetName( umf_file_memory_provider_params_handle_t hParams, const char *name); diff --git a/include/umf/providers/provider_fixed_memory.h b/include/umf/providers/provider_fixed_memory.h index 1f381e88a..fcedd5c00 100644 --- a/include/umf/providers/provider_fixed_memory.h +++ b/include/umf/providers/provider_fixed_memory.h @@ -55,6 +55,8 @@ const umf_memory_provider_ops_t *umfFixedMemoryProviderOps(void); /// @param hParams [in] handle to the parameters of the Fixed Memory Provider. /// @param name [in] custom name. Must not be NULL. Name longer than 63 characters /// will be truncated. +/// \details Name should contain only [a-zA-Z0-9_-] characters. +/// Other names are deprecated and may limit CTL functionality. /// @return UMF_RESULT_SUCCESS on success or appropriate error code on failure. umf_result_t umfFixedMemoryProviderParamsSetName( umf_fixed_memory_provider_params_handle_t hParams, const char *name); diff --git a/include/umf/providers/provider_level_zero.h b/include/umf/providers/provider_level_zero.h index 256ce0adf..65d7e3e78 100644 --- a/include/umf/providers/provider_level_zero.h +++ b/include/umf/providers/provider_level_zero.h @@ -95,6 +95,8 @@ umf_result_t umfLevelZeroMemoryProviderParamsSetDeviceOrdinal( /// @param hParams handle to the parameters of the Level Zero Memory Provider. /// @param name custom name. Must not be NULL. Name longer than 63 characters /// will be truncated. +/// \details Name should contain only [a-zA-Z0-9_-] characters. +/// Other names are deprecated and may limit CTL functionality. /// @return UMF_RESULT_SUCCESS on success or appropriate error code on failure. umf_result_t umfLevelZeroMemoryProviderParamsSetName( umf_level_zero_memory_provider_params_handle_t hParams, const char *name); diff --git a/include/umf/providers/provider_os_memory.h b/include/umf/providers/provider_os_memory.h index 978965621..93a20173c 100644 --- a/include/umf/providers/provider_os_memory.h +++ b/include/umf/providers/provider_os_memory.h @@ -138,6 +138,8 @@ umf_result_t umfOsMemoryProviderParamsSetPartitions( /// @param hParams handle to the parameters of the OS memory provider. /// @param name custom name. Must not be NULL. Name longer than 63 characters /// will be truncated. +/// \details Name should contain only [a-zA-Z0-9_-] characters. +/// Other names are deprecated and may limit CTL functionality. /// @return UMF_RESULT_SUCCESS on success or appropriate error code on failure. umf_result_t umfOsMemoryProviderParamsSetName(umf_os_memory_provider_params_handle_t hParams, diff --git a/src/base_alloc/base_alloc_global.c b/src/base_alloc/base_alloc_global.c index abed0879f..c7c4e2275 100644 --- a/src/base_alloc/base_alloc_global.c +++ b/src/base_alloc/base_alloc_global.c @@ -265,3 +265,19 @@ size_t umf_ba_global_malloc_usable_size(const void *ptr) { return usable_size; } + +char *umf_ba_global_strdup(const char *s) { + if (!s) { + return NULL; + } + + size_t len = strlen(s); + + char *ptr = umf_ba_global_alloc(len + 1); + if (!ptr) { + return NULL; + } + + memcpy(ptr, s, len + 1); + return ptr; +} diff --git a/src/base_alloc/base_alloc_global.h b/src/base_alloc/base_alloc_global.h index 4cec99725..71216ec6c 100644 --- a/src/base_alloc/base_alloc_global.h +++ b/src/base_alloc/base_alloc_global.h @@ -22,6 +22,7 @@ void umf_ba_destroy_global(void); bool umf_ba_global_is_destroyed(void); size_t umf_ba_global_malloc_usable_size(const void *ptr); void *umf_ba_global_aligned_alloc(size_t size, size_t alignment); +char *umf_ba_global_strdup(const char *s); #ifdef __cplusplus } diff --git a/src/ctl/ctl.c b/src/ctl/ctl.c index ff0425b1a..f33df885c 100644 --- a/src/ctl/ctl.c +++ b/src/ctl/ctl.c @@ -95,7 +95,7 @@ char *Strdup(const char *s) { } \ case CTL_ARG_TYPE_STRING: { \ char *str = va_arg(va, char *); \ - memcpy(output, str, ctl_argument->dest_size); \ + snprintf((char *)output, ctl_argument->dest_size, "%s", str); \ break; \ } \ case CTL_ARG_TYPE_INT: { \ @@ -108,9 +108,14 @@ char *Strdup(const char *s) { *(long long *)output = ll; \ break; \ } \ + case CTL_ARG_TYPE_UNSIGNED_LONG_LONG: { \ + unsigned long long ll = va_arg(va, unsigned long long); \ + *(unsigned long long *)output = ll; \ + break; \ + } \ case CTL_ARG_TYPE_PTR: { \ - void *p = va_arg(va, void *); \ - *(uintptr_t *)output = (uintptr_t)p; \ + void *ptr = va_arg(va, void *); \ + *(uintptr_t *)output = (uintptr_t)ptr; \ break; \ } \ default: \ @@ -173,9 +178,6 @@ static void *ctl_parse_args(const struct ctl_argument *arg_proto, char *arg) { char *arg_sep = strtok_r(arg, CTL_VALUE_ARG_SEPARATOR, &sptr); for (const struct ctl_argument_parser *p = arg_proto->parsers; p->parser != NULL; ++p) { - if (arg_sep == NULL) { - goto error_parsing; - } if (p->parser(arg_sep, dest_arg + p->dest_offset, p->dest_size) != 0) { goto error_parsing; @@ -375,31 +377,73 @@ ctl_find_and_execute_node(const umf_ctl_node_t *nodes, void *ctx, // if the node has an argument, but no next node, then it is an error goto error; } - void *node_arg; - if (strcmp(next_node, CTL_WILDCARD) == 0) { - if (source == CTL_QUERY_CONFIG_INPUT) { - LOG_ERR( - "ctl {} wildcard is not supported for config input"); - goto error; - } - // argument is a wildcard so we need to allocate it from va_list - node_arg = ctl_malloc_fn(n->arg->dest_size); - if (node_arg == NULL) { - goto error; - } - pop_va_list(args, n->arg, node_arg); - } else { - node_arg = ctl_parse_args(n->arg, next_node); - if (node_arg == NULL) { - goto error; + char *node_arg = ctl_malloc_fn(n->arg->dest_size); + if (node_arg == NULL) { + goto error; + } + + // Parse this argument. It might contain "struct" which is series of fields separated by comma. + // each field contains separate parser in the parsers array. + for (const struct ctl_argument_parser *p = n->arg->parsers; + p->dest_size != 0; ++p) { + + if (next_node && strcmp(next_node, CTL_WILDCARD) == 0) { + if (source == CTL_QUERY_CONFIG_INPUT) { + ctl_free_fn(node_arg); + LOG_ERR("ctl {} wildcard is not supported for config " + "input"); + goto error; + } + + if (p->type == CTL_ARG_TYPE_UNKNOWN) { + ctl_free_fn(node_arg); + LOG_ERR("ctl {} wildcard is not supported for node: %s", + node_name); + goto error; + } + char *output = node_arg + p->dest_offset; + pop_va_list(args, p, output); + } else { + if (!p->parser) { + LOG_ERR( + "this node can be passed only as {} wildcard: %s", + next_node); + ctl_free_fn(node_arg); + goto error; + } + int r = p->parser(next_node, node_arg + p->dest_offset, + p->dest_size); + if (r < 0) { + // Parsing failed — cleanup and propagate error + ctl_free_fn(node_arg); + goto error; + } else if (r > 0) { + // Parser did not consume next_node, which means this argument is optional + // and not present. Optional arguments are always at the end of the expected + // sequence, so we can safely stop parsing here. + // + // Example: + // Given two paths: + // "umf.pool.by_name.name.stats.allocs" + // "umf.pool.by_name.name.1.stats.allocs" + // The parser for 'by_name' expects the next node is string followed by optional + // integer index, if its sees "stats" instead of integer, like in second example + // it will return >0 to signal that the optional + // integer argument is not present. + // This allows the remaining nodes ("stats.allocs") to be parsed normally + // without treating "stats" as part of 'by_name'. + break; + } } + // we parsed next_node as an argument so we next one + next_node = strtok_r(NULL, CTL_QUERY_NODE_SEPARATOR, &sptr); } umf_ctl_index_utlist_t *entry = NULL; entry = ctl_malloc_fn(sizeof(*entry)); if (entry == NULL) { - ctl_free_fn(arg); + ctl_free_fn(node_arg); goto error; } @@ -408,8 +452,7 @@ ctl_find_and_execute_node(const umf_ctl_node_t *nodes, void *ctx, entry->arg_size = n->arg->dest_size; LL_APPEND(indexes, entry); - // we parsed next_node as an argument so we next one - next_node = strtok_r(NULL, CTL_QUERY_NODE_SEPARATOR, &sptr); + if (next_node == NULL) { // last node was a node with arg, but there is no next mode. // check if there is nameless leaf on next level @@ -678,6 +721,22 @@ umf_result_t ctl_load_config_from_file(struct ctl *ctl, void *ctx, return ret; } +/* + * ctl_parse_ull -- (internal) parses and returns an unsigned long long + */ +static unsigned long long ctl_parse_ull(const char *str) { + char *endptr; + int olderrno = errno; + errno = 0; + unsigned long long val = strtoull(str, &endptr, 0); + if (endptr == str || errno != 0) { + return ULLONG_MAX; + } + errno = olderrno; + + return val; +} + /* * ctl_parse_ll -- (internal) parses and returns a long long signed integer */ @@ -701,6 +760,9 @@ static long long ctl_parse_ll(const char *str) { int ctl_arg_boolean(const void *arg, void *dest, size_t dest_size) { /* suppress unused-parameter errors */ (void)dest_size; + if (!arg) { + return -1; + } int *intp = dest; char in = ((const char *)arg)[0]; @@ -716,10 +778,49 @@ int ctl_arg_boolean(const void *arg, void *dest, size_t dest_size) { return -1; } +/* + * ctl_arg_unsigned -- parses unsigned integer argument + */ +int ctl_arg_unsigned(const void *arg, void *dest, size_t dest_size) { + if (!arg) { + return -1; + } + + unsigned long long val = ctl_parse_ull(arg); + if (val == ULLONG_MAX) { + return -1; + } + + switch (dest_size) { + case sizeof(unsigned int): + if (val > UINT_MAX) { + return -1; + } + *(unsigned int *)dest = (unsigned int)val; + break; + case sizeof(unsigned long long): + *(unsigned long long *)dest = val; + break; + case sizeof(uint8_t): + if (val > UINT8_MAX) { + return -1; + } + *(uint8_t *)dest = (uint8_t)val; + break; + default: + return -1; + } + + return 0; +} + /* * ctl_arg_integer -- parses signed integer argument */ int ctl_arg_integer(const void *arg, void *dest, size_t dest_size) { + if (!arg) { + return -1; + } long long val = ctl_parse_ll(arg); if (val == LLONG_MIN) { return -1; @@ -735,12 +836,6 @@ int ctl_arg_integer(const void *arg, void *dest, size_t dest_size) { case sizeof(long long): *(long long *)dest = val; break; - case sizeof(uint8_t): - if (val > UINT8_MAX || val < 0) { - return -1; - } - *(uint8_t *)dest = (uint8_t)val; - break; default: return -1; } @@ -753,6 +848,10 @@ int ctl_arg_integer(const void *arg, void *dest, size_t dest_size) { * buffer */ int ctl_arg_string(const void *arg, void *dest, size_t dest_size) { + if (!arg) { + return -1; + } + /* check if the incoming string is longer or equal to dest_size */ if (strnlen(arg, dest_size) == dest_size) { return -1; diff --git a/src/ctl/ctl_internal.h b/src/ctl/ctl_internal.h index 8f51f534e..a043b01c4 100644 --- a/src/ctl/ctl_internal.h +++ b/src/ctl/ctl_internal.h @@ -56,37 +56,39 @@ enum ctl_node_type { typedef int (*ctl_arg_parser)(const void *arg, void *dest, size_t dest_size); -struct ctl_argument_parser { - size_t dest_offset; /* offset of the field inside of the argument */ - size_t dest_size; /* size of the field inside of the argument */ - ctl_arg_parser parser; -}; typedef enum ctl_arg_type { CTL_ARG_TYPE_UNKNOWN = 0, CTL_ARG_TYPE_BOOLEAN, CTL_ARG_TYPE_STRING, CTL_ARG_TYPE_INT, CTL_ARG_TYPE_LONG_LONG, + CTL_ARG_TYPE_UNSIGNED_LONG_LONG, CTL_ARG_TYPE_PTR, MAX_CTL_ARG_TYPE } ctl_arg_type_t; +struct ctl_argument_parser { + size_t dest_offset; /* offset of the field inside of the argument */ + size_t dest_size; /* size of the field inside of the argument */ + ctl_arg_type_t type; /* type of the argument */ + ctl_arg_parser parser; +}; + struct ctl_argument { size_t dest_size; /* size of the entire argument */ - ctl_arg_type_t type; /* type of the argument */ struct ctl_argument_parser parsers[]; /* array of 'fields' in arg */ }; -#define sizeof_member(t, m) sizeof(((t *)0)->m) +#define sizeof_member(type, member) sizeof(((type *)0)->member) -#define CTL_ARG_PARSER(t, p) \ - { 0, sizeof(t), p } +#define CTL_ARG_PARSER(type, vaarg_type, parser) \ + { 0, sizeof(type), vaarg_type, parser } -#define CTL_ARG_PARSER_STRUCT(t, m, p) \ - { offsetof(t, m), sizeof_member(t, m), p } +#define CTL_ARG_PARSER_STRUCT(type, member, vaarg_type, parser) \ + { offsetof(type, member), sizeof_member(type, member), vaarg_type, parser } #define CTL_ARG_PARSER_END \ - { 0, 0, NULL } + { 0, 0, 0, NULL } /* * CTL Tree node structure, do not use directly. All the necessary functionality @@ -133,39 +135,55 @@ void ctl_register_module_node(struct ctl *c, const char *name, struct ctl_node *n); int ctl_arg_boolean(const void *arg, void *dest, size_t dest_size); +int ctl_arg_integer(const void *arg, void *dest, size_t dest_size); +int ctl_arg_unsigned(const void *arg, void *dest, size_t dest_size); +int ctl_arg_string(const void *arg, void *dest, size_t dest_size); + #define CTL_ARG_BOOLEAN \ { \ - sizeof(int), CTL_ARG_TYPE_BOOLEAN, { \ - {0, sizeof(int), ctl_arg_boolean}, CTL_ARG_PARSER_END \ + sizeof(int), { \ + {0, sizeof(int), CTL_ARG_TYPE_BOOLEAN, ctl_arg_boolean}, \ + CTL_ARG_PARSER_END \ } \ } -int ctl_arg_integer(const void *arg, void *dest, size_t dest_size); #define CTL_ARG_INT \ { \ - sizeof(int), CTL_ARG_TYPE_INT, { \ - {0, sizeof(int), ctl_arg_integer}, CTL_ARG_PARSER_END \ + sizeof(int), { \ + {0, sizeof(int), CTL_ARG_TYPE_INT, ctl_arg_integer}, \ + CTL_ARG_PARSER_END \ } \ } #define CTL_ARG_LONG_LONG \ { \ - sizeof(long long), CTL_ARG_TYPE_LONG_LONG, { \ - {0, sizeof(long long), ctl_arg_integer}, CTL_ARG_PARSER_END \ + sizeof(long long), { \ + {0, sizeof(long long), CTL_ARG_TYPE_LONG_LONG, ctl_arg_integer}, \ + CTL_ARG_PARSER_END \ + } \ + } + +#define CTL_ARG_UNSIGNED_LONG_LONG \ + { \ + sizeof(unsigned long long), { \ + {0, sizeof(unsigned long long), CTL_ARG_TYPE_UNSIGNED_LONG_LONG, \ + ctl_arg_unsigned}, \ + CTL_ARG_PARSER_END \ } \ } -int ctl_arg_string(const void *arg, void *dest, size_t dest_size); #define CTL_ARG_STRING(len) \ { \ - len, CTL_ARG_TYPE_PTR, { \ - {0, len, ctl_arg_string}, CTL_ARG_PARSER_END \ + len, { \ + {0, len, CTL_ARG_TYPE_STRING, ctl_arg_string}, CTL_ARG_PARSER_END \ } \ } #define CTL_ARG_PTR \ { \ - sizeof(void *), CTL_ARG_TYPE_PTR, { {0, 0, NULL}, CTL_ARG_PARSER_END } \ + sizeof(void *), { \ + {0, sizeof(void *), CTL_ARG_TYPE_PTR, NULL}, CTL_ARG_PARSER_END \ + } \ } #define _CTL_STR(name) #name diff --git a/src/memory_pool.c b/src/memory_pool.c index 333ae56e6..93640a0ed 100644 --- a/src/memory_pool.c +++ b/src/memory_pool.c @@ -24,8 +24,20 @@ #include "utils_assert.h" #include "utils_concurrency.h" #include "utils_log.h" +#include "utils_name.h" #include "utlist.h" +// Handle UTHash memory allocation failures without aborting the process. +#define HASH_NONFATAL_OOM 1 +static bool uthash_oom = false; +#define uthash_nonfatal_oom(obj) \ + do { \ + (void)(obj); \ + uthash_oom = true; \ + } while (0) + +#include "uthash.h" + typedef struct ctl_default_entry_t { char *name; void *value; @@ -43,6 +55,111 @@ static struct ctl umf_pool_ctl_root; static void pool_ctl_init(void); +typedef struct pool_name_list_entry_t { + umf_memory_pool_handle_t pool; + struct pool_name_list_entry_t *next; +} pool_name_list_entry_t; + +typedef struct pool_name_dict_entry_t { + char *name; /* key */ + pool_name_list_entry_t *pools; + UT_hash_handle hh; +} pool_name_dict_entry_t; + +static pool_name_dict_entry_t *pools_by_name = NULL; +static utils_rwlock_t pools_by_name_lock; +static UTIL_ONCE_FLAG pools_by_name_init_once = UTIL_ONCE_FLAG_INIT; + +static void pools_by_name_init(void) { utils_rwlock_init(&pools_by_name_lock); } + +static umf_result_t pools_by_name_add(umf_memory_pool_handle_t pool) { + const char *name = NULL; + umf_result_t ret = pool->ops.get_name(pool->pool_priv, &name); + if (ret != UMF_RESULT_SUCCESS || !name) { + return ret; + } + + if (!utils_name_is_valid(name)) { + LOG_ERR("Pool name: %s contains invalid character, ctl by_name is not " + "supported for this pool", + name); + return UMF_RESULT_SUCCESS; + } + + utils_init_once(&pools_by_name_init_once, pools_by_name_init); + utils_write_lock(&pools_by_name_lock); + + pool_name_dict_entry_t *entry = NULL; + HASH_FIND_STR(pools_by_name, name, entry); + if (!entry) { + entry = umf_ba_global_alloc(sizeof(*entry)); + if (!entry) { + utils_write_unlock(&pools_by_name_lock); + return UMF_RESULT_ERROR_OUT_OF_HOST_MEMORY; + } + entry->name = umf_ba_global_strdup(name); + if (!entry->name) { + umf_ba_global_free(entry); + utils_write_unlock(&pools_by_name_lock); + return UMF_RESULT_ERROR_OUT_OF_HOST_MEMORY; + } + + entry->pools = NULL; + uthash_oom = false; + HASH_ADD_KEYPTR(hh, pools_by_name, entry->name, strlen(entry->name), + entry); + if (uthash_oom) { + umf_ba_global_free(entry->name); + umf_ba_global_free(entry); + utils_write_unlock(&pools_by_name_lock); + return UMF_RESULT_ERROR_OUT_OF_HOST_MEMORY; + } + } + + pool_name_list_entry_t *node = umf_ba_global_alloc(sizeof(*node)); + if (!node) { + utils_write_unlock(&pools_by_name_lock); + return UMF_RESULT_ERROR_OUT_OF_HOST_MEMORY; + } + node->pool = pool; + node->next = NULL; + LL_APPEND(entry->pools, node); + + utils_write_unlock(&pools_by_name_lock); + return UMF_RESULT_SUCCESS; +} + +static void pools_by_name_remove(umf_memory_pool_handle_t pool) { + const char *name = NULL; + if (pool->ops.get_name(pool->pool_priv, &name) != UMF_RESULT_SUCCESS || + !name) { + return; + } + + utils_init_once(&pools_by_name_init_once, pools_by_name_init); + utils_write_lock(&pools_by_name_lock); + + pool_name_dict_entry_t *entry = NULL; + HASH_FIND_STR(pools_by_name, name, entry); + if (entry) { + pool_name_list_entry_t *it = NULL, *tmp = NULL; + LL_FOREACH_SAFE(entry->pools, it, tmp) { + if (it->pool == pool) { + LL_DELETE(entry->pools, it); + umf_ba_global_free(it); + break; + } + } + if (entry->pools == NULL) { + HASH_DEL(pools_by_name, entry); + umf_ba_global_free(entry->name); + umf_ba_global_free(entry); + } + } + + utils_write_unlock(&pools_by_name_lock); +} + static umf_result_t CTL_SUBTREE_HANDLER(CTL_NONAME, by_handle)( void *ctx, umf_ctl_query_source_t source, void *arg, size_t size, umf_ctl_index_utlist_t *indexes, const char *extra_name, @@ -190,7 +307,134 @@ static umf_ctl_node_t CTL_NODE(by_handle)[] = { static const struct ctl_argument CTL_ARG(by_handle) = CTL_ARG_PTR; +typedef struct by_name_arg_t { + char name[255]; + size_t index; +} by_name_arg_t; + +// parses optional size_t argument. if arg is not integer then sets out to size_max +int by_name_index_parser(const void *arg, void *dest, size_t dest_size) { + size_t *out = (size_t *)dest; + + if (arg == NULL) { + *out = SIZE_MAX; + return 1; // node n + } + + int ret = ctl_arg_unsigned(arg, dest, dest_size); + if (ret) { + *out = SIZE_MAX; + return 1; + } + + return 0; +} + +static const struct ctl_argument CTL_ARG(by_name) = { + sizeof(by_name_arg_t), + {CTL_ARG_PARSER_STRUCT(by_name_arg_t, name, CTL_ARG_TYPE_STRING, + ctl_arg_string), + CTL_ARG_PARSER_STRUCT(by_name_arg_t, index, + CTL_ARG_TYPE_UNSIGNED_LONG_LONG, + by_name_index_parser), + CTL_ARG_PARSER_END}}; + +static umf_result_t CTL_SUBTREE_HANDLER(CTL_NONAME, by_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, va_list args) { + (void)ctx; + + utils_init_once(&pools_by_name_init_once, pools_by_name_init); + + by_name_arg_t *name_arg = (by_name_arg_t *)indexes->arg; + + utils_read_lock(&pools_by_name_lock); + pool_name_dict_entry_t *entry = NULL; + // find pool name in the hashmap + HASH_FIND_STR(pools_by_name, name_arg->name, entry); + if (!entry) { + utils_read_unlock(&pools_by_name_lock); + LOG_ERR("Pool %s not found", name_arg->name); + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + + size_t count = 0; + pool_name_list_entry_t *it = NULL; + LL_COUNT(entry->pools, it, count); + // Special case: if user asked for umf.pool.by_name.name.count, we just return + // number of pools sharing the same name. + if (strcmp(extra_name, "count") == 0) { + if (name_arg->index != SIZE_MAX) { + LOG_ERR("count field requires no index argument"); + utils_read_unlock(&pools_by_name_lock); + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + if (queryType != CTL_QUERY_READ) { + LOG_ERR("count field is read only"); + utils_read_unlock(&pools_by_name_lock); + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + size_t *output = (size_t *)arg; + *output = count; + utils_read_unlock(&pools_by_name_lock); + return UMF_RESULT_SUCCESS; + } + + if (queryType == CTL_QUERY_READ && count > 1 && + name_arg->index == SIZE_MAX) { + LOG_ERR("CTL 'by_name' read operation requires exactly one pool with " + "the specified name. " + "Actual number of pools with name '%s' is %zu. " + "You can add extra index parameter after the name to specify " + "exact pool e.g. umf.pool.by_name.pool_name,1.node_name", + name_arg->name, count); + utils_read_unlock(&pools_by_name_lock); + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + + umf_result_t ret = UMF_RESULT_SUCCESS; + size_t nr = 0; + + if (name_arg->index != SIZE_MAX && name_arg->index >= count) { + LOG_ERR( + "Invalid index %zu. Actual number of pools with name '%s' is %zu. ", + name_arg->index, name_arg->name, count); + utils_read_unlock(&pools_by_name_lock); + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + LL_FOREACH(entry->pools, it) { + if (name_arg->index != SIZE_MAX && nr++ != name_arg->index) { + continue; + } + va_list args2; + va_copy(args2, args); + umf_result_t r = ctl_query(&umf_pool_ctl_root, it->pool, source, + extra_name, queryType, arg, size, args2); + va_end(args2); + + if (r == UMF_RESULT_ERROR_INVALID_ARGUMENT) { + va_copy(args2, args); + r = it->pool->ops.ext_ctl(it->pool->pool_priv, source, extra_name, + arg, size, queryType, args2); + va_end(args2); + } + if (r != UMF_RESULT_SUCCESS && ret == UMF_RESULT_SUCCESS) { + ret = r; + } + } + utils_read_unlock(&pools_by_name_lock); + + return ret; +} + +static umf_ctl_node_t CTL_NODE(by_name)[] = { + CTL_LEAF_SUBTREE(CTL_NONAME, by_name), + CTL_NODE_END, +}; + umf_ctl_node_t CTL_NODE(pool)[] = {CTL_CHILD_WITH_ARG(by_handle), + CTL_CHILD_WITH_ARG(by_name), CTL_LEAF_SUBTREE(default), CTL_NODE_END}; static void pool_ctl_init(void) { @@ -311,6 +555,14 @@ static umf_result_t umfPoolCreateInternal(const umf_memory_pool_ops_t *ops, } *hPool = pool; + pools_by_name_add(pool); + + const char *pool_name = NULL; + if (ops->get_name(pool->pool_priv, &pool_name) == UMF_RESULT_SUCCESS && + pool_name) { + utils_warn_invalid_name("Memory pool", pool_name); + } + LOG_INFO("Memory pool created: %p", (void *)pool); return UMF_RESULT_SUCCESS; @@ -335,6 +587,8 @@ umf_result_t umfPoolDestroy(umf_memory_pool_handle_t hPool) { return UMF_RESULT_ERROR_UNKNOWN; } + pools_by_name_remove(hPool); + umf_result_t ret = hPool->ops.finalize(hPool->pool_priv); umf_memory_provider_handle_t hUpstreamProvider = NULL; diff --git a/src/memory_provider.c b/src/memory_provider.c index 8ed2a79db..eb7e88ead 100644 --- a/src/memory_provider.c +++ b/src/memory_provider.c @@ -21,6 +21,7 @@ #include "libumf.h" #include "memory_provider_internal.h" #include "utils_assert.h" +#include "utils_name.h" static umf_result_t CTL_SUBTREE_HANDLER(CTL_NONAME, by_handle)( void *ctx, umf_ctl_query_source_t source, void *arg, size_t size, @@ -252,6 +253,13 @@ umf_result_t umfMemoryProviderCreate(const umf_memory_provider_ops_t *ops, *hProvider = provider; + const char *provider_name = NULL; + if (provider->ops.get_name(provider->provider_priv, &provider_name) == + UMF_RESULT_SUCCESS && + provider_name) { + utils_warn_invalid_name("Memory provider", provider_name); + } + return UMF_RESULT_SUCCESS; } diff --git a/src/utils/utils_name.h b/src/utils/utils_name.h new file mode 100644 index 000000000..6914d06ba --- /dev/null +++ b/src/utils/utils_name.h @@ -0,0 +1,46 @@ +/* + * + * 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-exception + * + */ + +#ifndef UMF_UTILS_NAME_H +#define UMF_UTILS_NAME_H + +#include +#include + +#include "utils_log.h" + +#define MAX_NAME 64 + +static inline int utils_name_is_valid(const char *name) { + if (!name) { + return 0; + } + size_t len = strlen(name); + if (len > MAX_NAME) { + return 0; + } + for (size_t i = 0; i < len; ++i) { + char c = name[i]; + if (!isalnum((unsigned char)c) && c != '-' && c != '_') { + return 0; + } + } + return 1; +} + +static inline void utils_warn_invalid_name(const char *kind, const char *name) { + if (!utils_name_is_valid(name)) { + LOG_WARN("%s name \"%s\" is deprecated. It should be no more than 64 " + "characters including null character, containing only " + "alphanumerics, '_' or '-'. CTL functionality may be limited.", + kind, name); + } +} + +#endif /* UMF_UTILS_NAME_H */ diff --git a/test/ctl/ctl_api.cpp b/test/ctl/ctl_api.cpp index 35f8b9e44..0037d3dbb 100644 --- a/test/ctl/ctl_api.cpp +++ b/test/ctl/ctl_api.cpp @@ -432,3 +432,191 @@ TEST_F(test, ctl_logger_output_file) { UMF_RESULT_SUCCESS); EXPECT_STREQ(out_get, file_name); } + +TEST_F(test, ctl_by_name) { + umf_memory_provider_handle_t hProvider = NULL; + umf_os_memory_provider_params_handle_t os_memory_provider_params = NULL; + const umf_memory_provider_ops_t *os_provider_ops = umfOsMemoryProviderOps(); + if (os_provider_ops == NULL) { + GTEST_SKIP() << "OS memory provider is not supported!"; + } + + int ret = umfOsMemoryProviderParamsCreate(&os_memory_provider_params); + ret = umfMemoryProviderCreate(os_provider_ops, os_memory_provider_params, + &hProvider); + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + umfOsMemoryProviderParamsDestroy(os_memory_provider_params); + + umf_disjoint_pool_params_handle_t disjoint_pool_params = NULL; + ret = umfDisjointPoolParamsCreate(&disjoint_pool_params); + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + + const char *pool_name = "test_disjoint_pool"; + ret = umfDisjointPoolParamsSetName(disjoint_pool_params, pool_name); + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + + umf_memory_pool_handle_t hPool = NULL; + ret = umfPoolCreate(umfDisjointPoolOps(), hProvider, disjoint_pool_params, + 0, &hPool); + + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + + const char *pool_name2 = "test_disjoint_pool2"; + ret = umfDisjointPoolParamsSetName(disjoint_pool_params, pool_name2); + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + + umf_memory_pool_handle_t hPool2 = NULL; + ret = umfPoolCreate(umfDisjointPoolOps(), hProvider, disjoint_pool_params, + 0, &hPool2); + umfDisjointPoolParamsDestroy(disjoint_pool_params); + + size_t pool_count; + + ret = umfCtlGet("umf.pool.by_name.test_disjoint_pool.count", &pool_count, + sizeof(pool_count)); + + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + ASSERT_EQ(pool_count, 1ull); + + ret = umfCtlGet("umf.pool.by_name.test_disjoint_pool2.count", &pool_count, + sizeof(pool_count)); + + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + ASSERT_EQ(pool_count, 1ull); + + size_t alloc_count; + ret = umfCtlGet("umf.pool.by_name.{}.stats.alloc_count", &alloc_count, + sizeof(alloc_count), pool_name); + + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + ASSERT_EQ(alloc_count, 0ull); + + ret = umfCtlGet("umf.pool.by_name.{}.stats.alloc_count", &alloc_count, + sizeof(alloc_count), pool_name2); + + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + ASSERT_EQ(alloc_count, 0ull); + + // allocate from pool1 + void *ptr1 = umfPoolMalloc(hPool, 1024); + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + ASSERT_NE(ptr1, nullptr); + + // we can use pool name in the string without {} too + ret = umfCtlGet("umf.pool.by_name.test_disjoint_pool.stats.alloc_count", + &alloc_count, sizeof(alloc_count)); + + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + ASSERT_EQ(alloc_count, 1ull); + + ret = umfCtlGet("umf.pool.by_name.test_disjoint_pool2.stats.alloc_count", + &alloc_count, sizeof(alloc_count)); + + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + ASSERT_EQ(alloc_count, 0ull); + + ret = umfPoolFree(hPool, ptr1); + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + + // we can use index parameter too + ret = umfCtlGet("umf.pool.by_name.test_disjoint_pool.0.stats.alloc_count", + &alloc_count, sizeof(alloc_count)); + + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + ASSERT_EQ(alloc_count, 0ull); + + ret = umfCtlGet("umf.pool.by_name.test_disjoint_pool2.{}.stats.alloc_count", + &alloc_count, sizeof(alloc_count), 0); + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + ASSERT_EQ(alloc_count, 0ull); + + // test too big pool index + ret = umfCtlGet("umf.pool.by_name.test_disjoint_pool2.10.stats.alloc_count", + &alloc_count, sizeof(alloc_count)); + ASSERT_EQ(ret, UMF_RESULT_ERROR_INVALID_ARGUMENT); + + ret = umfPoolDestroy(hPool); + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + ret = umfPoolDestroy(hPool2); + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + ret = umfMemoryProviderDestroy(hProvider); + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); +} + +TEST_F(test, ctl_by_name_collision) { + umf_memory_provider_handle_t hProvider = NULL; + umf_os_memory_provider_params_handle_t os_memory_provider_params = NULL; + const umf_memory_provider_ops_t *os_provider_ops = umfOsMemoryProviderOps(); + if (os_provider_ops == NULL) { + GTEST_SKIP() << "OS memory provider is not supported!"; + } + + int ret = umfOsMemoryProviderParamsCreate(&os_memory_provider_params); + ret = umfMemoryProviderCreate(os_provider_ops, os_memory_provider_params, + &hProvider); + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + umfOsMemoryProviderParamsDestroy(os_memory_provider_params); + + umf_disjoint_pool_params_handle_t disjoint_pool_params = NULL; + ret = umfDisjointPoolParamsCreate(&disjoint_pool_params); + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + + const char *pool_name = "test_disjoint_pool"; + ret = umfDisjointPoolParamsSetName(disjoint_pool_params, pool_name); + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + + umf_memory_pool_handle_t hPool = NULL; + ret = umfPoolCreate(umfDisjointPoolOps(), hProvider, disjoint_pool_params, + 0, &hPool); + + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + + umf_memory_pool_handle_t hPool2 = NULL; + ret = umfPoolCreate(umfDisjointPoolOps(), hProvider, disjoint_pool_params, + 0, &hPool2); + umfDisjointPoolParamsDestroy(disjoint_pool_params); + + // allocate from pool1 + void *ptr1 = umfPoolMalloc(hPool, 1024); + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + ASSERT_NE(ptr1, nullptr); + + size_t pool_count; + ret = umfCtlGet("umf.pool.by_name.test_disjoint_pool.count", &pool_count, + sizeof(pool_count)); + + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + ASSERT_EQ(pool_count, 2ull); + + // If there is more than one pool with the same name, + // CtlGet by_name will return an error + size_t alloc_count; + ret = umfCtlGet("umf.pool.by_name.{}.stats.alloc_count", &alloc_count, + sizeof(alloc_count), pool_name); + + ASSERT_EQ(ret, UMF_RESULT_ERROR_INVALID_ARGUMENT); + + // ctl set and exec will still work. But there is no CTL entry for now to test it + + // todo: add test when ctl entries will be extended + + // we can read from specific pool with index argument + ret = umfCtlGet("umf.pool.by_name.test_disjoint_pool.0.stats.alloc_count", + &alloc_count, sizeof(alloc_count), pool_name, 0); + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + ASSERT_EQ(alloc_count, 1ull); + + ret = umfCtlGet("umf.pool.by_name.{}.1.stats.alloc_count", &alloc_count, + sizeof(alloc_count), pool_name); + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + ASSERT_EQ(alloc_count, 0ull); + + ret = umfPoolFree(hPool, ptr1); + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + ret = umfPoolDestroy(hPool); + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + ret = umfPoolDestroy(hPool2); + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + ret = umfMemoryProviderDestroy(hProvider); + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); +} diff --git a/test/memoryProviderAPI.cpp b/test/memoryProviderAPI.cpp index a1768b1f6..52a8955d2 100644 --- a/test/memoryProviderAPI.cpp +++ b/test/memoryProviderAPI.cpp @@ -26,7 +26,7 @@ TEST_F(test, memoryProviderTrace) { auto tracingProvider = umf_test::wrapProviderUnique( traceProviderCreate(nullProvider, true, &calls, trace)); - size_t call_count = 0; + size_t call_count = 1; // get_name is called during initialization void *ptr; auto ret = umfMemoryProviderAlloc(tracingProvider.get(), 0, 0, &ptr); @@ -64,8 +64,8 @@ TEST_F(test, memoryProviderTrace) { ret = umfMemoryProviderGetName(tracingProvider.get(), &pName); ASSERT_EQ(ret, UMF_RESULT_SUCCESS); ASSERT_NE(pName, nullptr); - ASSERT_EQ(calls["name"], 1UL); - ASSERT_EQ(calls.size(), ++call_count); + ASSERT_EQ(calls["name"], 2UL); + ASSERT_EQ(calls.size(), call_count); ASSERT_EQ(std::string(pName), std::string("null")); ret = umfMemoryProviderPurgeLazy(tracingProvider.get(), &page_size,