From 4dee8a2226c68eefdf76c41c04eefb4f52be45f6 Mon Sep 17 00:00:00 2001 From: Rafal Rudnicki Date: Wed, 18 Jun 2025 08:25:25 +0000 Subject: [PATCH] x --- include/umf/memory_pool.h | 9 +++ include/umf/memory_pool_ops.h | 10 +++ src/libumf.def | 1 + src/libumf.map | 1 + src/memory_pool.c | 14 ++++ src/memory_provider.c | 4 ++ src/pool/pool_disjoint.c | 42 ++++++++++++ src/pool/pool_jemalloc.c | 24 +++++++ src/pool/pool_proxy.c | 17 ++++- src/pool/pool_scalable.c | 14 +++- test/common/pool.hpp | 4 ++ test/common/pool_null.c | 6 ++ test/common/pool_trace.c | 6 ++ test/common/provider.hpp | 12 +++- test/ipcAPI.cpp | 4 +- test/memoryPoolAPI.cpp | 36 ++++++++--- test/poolFixtures.hpp | 39 +++++++++++ test/pools/disjoint_pool.cpp | 82 +++++++++++++++++++++++- test/pools/jemalloc_coarse_devdax.cpp | 3 +- test/pools/jemalloc_pool.cpp | 3 +- test/provider_tracking_fixture_tests.cpp | 10 ++- test/utils/cpp_helpers.hpp | 11 ++-- 22 files changed, 324 insertions(+), 28 deletions(-) diff --git a/include/umf/memory_pool.h b/include/umf/memory_pool.h index c9b02214e..6d943f67f 100644 --- a/include/umf/memory_pool.h +++ b/include/umf/memory_pool.h @@ -190,6 +190,15 @@ umf_result_t umfPoolSetTag(umf_memory_pool_handle_t hPool, void *tag, /// @return UMF_RESULT_SUCCESS on success. umf_result_t umfPoolGetTag(umf_memory_pool_handle_t hPool, void **tag); +/// +/// @brief Trims memory pool to keep at least \p minBytesToKeep bytes of memory +/// if possible. +/// @param hPool specified memory pool +/// @param minBytesToKeep minimum number of bytes to keep in the pool +/// @return UMF_RESULT_SUCCESS on success or appropriate error code on failure. +umf_result_t umfPoolTrimMemory(umf_memory_pool_handle_t hPool, + size_t minBytesToKeep); + #ifdef __cplusplus } #endif diff --git a/include/umf/memory_pool_ops.h b/include/umf/memory_pool_ops.h index 7b03ec8d2..2b28ca4ed 100644 --- a/include/umf/memory_pool_ops.h +++ b/include/umf/memory_pool_ops.h @@ -161,6 +161,16 @@ typedef struct umf_memory_pool_ops_t { /// @return A constant character string representing the pool's name. /// const char *(*ext_get_name)(void *pool); + + /// + /// @brief Trims memory of the pool, removing resources that are not needed + /// to keep the pool operational. + /// @param pool pointer to the memory pool + /// @param minBytesToKeep minimum number of bytes to keep in the pool if + /// possible. + /// @return UMF_RESULT_SUCCESS on success or appropriate error code on failure. + /// + umf_result_t (*trim_memory)(void *pool, size_t minBytesToKeep); } umf_memory_pool_ops_t; #ifdef __cplusplus diff --git a/src/libumf.def b/src/libumf.def index 10b0326b8..3007ae60f 100644 --- a/src/libumf.def +++ b/src/libumf.def @@ -144,3 +144,4 @@ EXPORTS umfJemallocPoolParamsDestroy umfJemallocPoolParamsSetNumArenas umfPoolGetName + umfPoolTrimMemory diff --git a/src/libumf.map b/src/libumf.map index e283c3853..da3ad8c02 100644 --- a/src/libumf.map +++ b/src/libumf.map @@ -144,4 +144,5 @@ UMF_0.12 { umfJemallocPoolParamsDestroy; umfJemallocPoolParamsSetNumArenas; umfPoolGetName; + umfPoolTrimMemory; } UMF_0.11; diff --git a/src/memory_pool.c b/src/memory_pool.c index ad5182236..3f3d78cf9 100644 --- a/src/memory_pool.c +++ b/src/memory_pool.c @@ -240,6 +240,10 @@ static umf_result_t umfPoolCreateInternal(const umf_memory_pool_ops_t *ops, } umf_result_t umfPoolDestroy(umf_memory_pool_handle_t hPool) { + if (hPool == NULL) { + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + if (umf_ba_global_is_destroyed()) { return UMF_RESULT_ERROR_UNKNOWN; } @@ -417,3 +421,13 @@ umf_result_t umfPoolGetTag(umf_memory_pool_handle_t hPool, void **tag) { utils_mutex_unlock(&hPool->lock); return UMF_RESULT_SUCCESS; } + +umf_result_t umfPoolTrimMemory(umf_memory_pool_handle_t hPool, + size_t minBytesToKeep) { + UMF_CHECK((hPool != NULL), UMF_RESULT_ERROR_INVALID_ARGUMENT); + if (hPool->ops.trim_memory == NULL) { + return UMF_RESULT_ERROR_NOT_SUPPORTED; + } + + return hPool->ops.trim_memory(hPool->pool_priv, minBytesToKeep); +} diff --git a/src/memory_provider.c b/src/memory_provider.c index c262ad80d..7082d7653 100644 --- a/src/memory_provider.c +++ b/src/memory_provider.c @@ -165,6 +165,10 @@ void assignOpsIpcDefaults(umf_memory_provider_ops_t *ops) { static bool validateOps(const umf_memory_provider_ops_t *ops) { // Validate mandatory operations one by one + if (ops->get_name == NULL) { + LOG_ERR("missing get_name function pointer\n"); + return false; + } CHECK_OP(ops, alloc); CHECK_OP(ops, free); CHECK_OP(ops, get_recommended_page_size); diff --git a/src/pool/pool_disjoint.c b/src/pool/pool_disjoint.c index ca5e483f5..a64cd7373 100644 --- a/src/pool/pool_disjoint.c +++ b/src/pool/pool_disjoint.c @@ -1031,6 +1031,47 @@ const char *disjoint_pool_get_name(void *pool) { return hPool->params.name; } +umf_result_t disjoint_pool_trim_memory(void *pool, size_t minBytesToKeep) { + disjoint_pool_t *hPool = (disjoint_pool_t *)pool; + if (hPool == NULL) { + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + + for (size_t i = 0; i < hPool->buckets_num; i++) { + bucket_t *bucket = hPool->buckets[i]; + utils_mutex_lock(&bucket->bucket_lock); + + int skip = (int)minBytesToKeep; + + // remove empty slabs from the pool + slab_list_item_t *it = NULL, *tmp = NULL; + LL_FOREACH_SAFE(bucket->available_slabs, it, tmp) { + slab_t *slab = it->val; + if (slab->num_chunks_allocated == 0) { + // skip first minBytesToKeep bytes from each bucket + if (skip > 0) { + skip -= (int)slab->slab_size; + continue; + } + + // remove slab + pool_unregister_slab(hPool, slab); + DL_DELETE(bucket->available_slabs, it); + assert(bucket->available_slabs_num > 0); + bucket->available_slabs_num--; + destroy_slab(slab); + + // update stats + bucket_update_stats(bucket, 0, -1); + } + } + + utils_mutex_unlock(&bucket->bucket_lock); + } + + return UMF_RESULT_SUCCESS; +} + static umf_memory_pool_ops_t UMF_DISJOINT_POOL_OPS = { .version = UMF_VERSION_CURRENT, .initialize = disjoint_pool_initialize, @@ -1044,6 +1085,7 @@ static umf_memory_pool_ops_t UMF_DISJOINT_POOL_OPS = { .get_last_allocation_error = disjoint_pool_get_last_allocation_error, .ext_get_name = disjoint_pool_get_name, .ext_ctl = disjoint_pool_ctl, + .trim_memory = disjoint_pool_trim_memory, }; const umf_memory_pool_ops_t *umfDisjointPoolOps(void) { diff --git a/src/pool/pool_jemalloc.c b/src/pool/pool_jemalloc.c index 8d6a2daf2..3224ab5a5 100644 --- a/src/pool/pool_jemalloc.c +++ b/src/pool/pool_jemalloc.c @@ -552,6 +552,28 @@ static umf_result_t op_get_last_allocation_error(void *pool) { return TLS_last_allocation_error; } +static const char *op_get_name(void *pool) { + (void)pool; // not used + return "jemalloc"; +} + +static umf_result_t op_trim_memory(void *pool, size_t minBytesToKeep) { + (void)minBytesToKeep; // unused - TODO? + + jemalloc_memory_pool_t *je_pool = (jemalloc_memory_pool_t *)pool; + for (size_t i = 0; i < je_pool->n_arenas; i++) { + char cmd[64]; + unsigned arena = je_pool->arena_index[i]; + snprintf(cmd, sizeof(cmd), "arena.%u.purge", arena); + if (je_mallctl(cmd, NULL, NULL, NULL, 0)) { + LOG_ERR("Could not purge jemalloc arena %u", arena); + return UMF_RESULT_ERROR_UNKNOWN; + } + } + + return UMF_RESULT_SUCCESS; +} + static umf_memory_pool_ops_t UMF_JEMALLOC_POOL_OPS = { .version = UMF_POOL_OPS_VERSION_CURRENT, .initialize = op_initialize, @@ -563,6 +585,8 @@ static umf_memory_pool_ops_t UMF_JEMALLOC_POOL_OPS = { .malloc_usable_size = op_malloc_usable_size, .free = op_free, .get_last_allocation_error = op_get_last_allocation_error, + .ext_get_name = op_get_name, + .trim_memory = op_trim_memory, }; const umf_memory_pool_ops_t *umfJemallocPoolOps(void) { diff --git a/src/pool/pool_proxy.c b/src/pool/pool_proxy.c index 208b46d4c..03a275d30 100644 --- a/src/pool/pool_proxy.c +++ b/src/pool/pool_proxy.c @@ -125,6 +125,18 @@ static umf_result_t proxy_get_last_allocation_error(void *pool) { return TLS_last_allocation_error; } +static const char *proxy_get_name(void *pool) { + (void)pool; // not used + return "proxy"; +} + +static umf_result_t proxy_trim_memory(void *pool, size_t minBytesToKeep) { + (void)pool; + (void)minBytesToKeep; + + return UMF_RESULT_SUCCESS; +} + static umf_memory_pool_ops_t UMF_PROXY_POOL_OPS = { .version = UMF_POOL_OPS_VERSION_CURRENT, .initialize = proxy_pool_initialize, @@ -135,7 +147,10 @@ static umf_memory_pool_ops_t UMF_PROXY_POOL_OPS = { .aligned_malloc = proxy_aligned_malloc, .malloc_usable_size = proxy_malloc_usable_size, .free = proxy_free, - .get_last_allocation_error = proxy_get_last_allocation_error}; + .get_last_allocation_error = proxy_get_last_allocation_error, + .ext_get_name = proxy_get_name, + .trim_memory = proxy_trim_memory, +}; const umf_memory_pool_ops_t *umfProxyPoolOps(void) { return &UMF_PROXY_POOL_OPS; diff --git a/src/pool/pool_scalable.c b/src/pool/pool_scalable.c index fa63351c2..c3a24ca35 100644 --- a/src/pool/pool_scalable.c +++ b/src/pool/pool_scalable.c @@ -13,15 +13,15 @@ #include #include -#include -#include #include #include #include #include #include "base_alloc_global.h" +#include "ctl/ctl.h" #include "libumf.h" +#include "memory_pool_internal.h" #include "pool_scalable_internal.h" #include "utils_common.h" #include "utils_concurrency.h" @@ -60,6 +60,7 @@ typedef struct tbb_callbacks_t { bool (*pool_destroy)(void *); void *(*pool_identify)(void *object); size_t (*pool_msize)(void *, void *); + int (*pool_allocation_command)(int, void *); #ifdef _WIN32 HMODULE lib_handle; #else @@ -431,6 +432,14 @@ static const char *scalable_get_name(void *pool) { return "scalable"; } +static umf_result_t scalable_trim_memory(void *pool, size_t minBytesToKeep) { + (void)pool; // unused + (void)minBytesToKeep; // unused + + //scalable_allocation_command? + return UMF_RESULT_SUCCESS; +} + static umf_memory_pool_ops_t UMF_SCALABLE_POOL_OPS = { .version = UMF_POOL_OPS_VERSION_CURRENT, .initialize = tbb_pool_initialize, @@ -444,6 +453,7 @@ static umf_memory_pool_ops_t UMF_SCALABLE_POOL_OPS = { .get_last_allocation_error = tbb_get_last_allocation_error, .ext_ctl = pool_ctl, .ext_get_name = scalable_get_name, + .trim_memory = scalable_trim_memory, }; const umf_memory_pool_ops_t *umfScalablePoolOps(void) { diff --git a/test/common/pool.hpp b/test/common/pool.hpp index d9873810a..80481c5d1 100644 --- a/test/common/pool.hpp +++ b/test/common/pool.hpp @@ -117,6 +117,10 @@ typedef struct pool_base_t { umf_result_t get_last_allocation_error() noexcept { return UMF_RESULT_SUCCESS; } + umf_result_t + trim_memory([[maybe_unused]] size_t minBytesToKeep) noexcept { + return UMF_RESULT_SUCCESS; + } } pool_base_t; struct malloc_pool : public pool_base_t { diff --git a/test/common/pool_null.c b/test/common/pool_null.c index a44f3c4ab..ea64e4397 100644 --- a/test/common/pool_null.c +++ b/test/common/pool_null.c @@ -66,6 +66,11 @@ static umf_result_t nullGetLastStatus(void *pool) { return UMF_RESULT_SUCCESS; } +static const char *nullGetName(void *pool) { + (void)pool; // not used + return "null"; +} + umf_memory_pool_ops_t UMF_NULL_POOL_OPS = { .version = UMF_POOL_OPS_VERSION_CURRENT, .initialize = nullInitialize, @@ -76,5 +81,6 @@ umf_memory_pool_ops_t UMF_NULL_POOL_OPS = { .aligned_malloc = nullAlignedMalloc, .malloc_usable_size = nullMallocUsableSize, .free = nullFree, + .ext_get_name = nullGetName, .get_last_allocation_error = nullGetLastStatus, }; diff --git a/test/common/pool_trace.c b/test/common/pool_trace.c index e4479548f..ca0d38877 100644 --- a/test/common/pool_trace.c +++ b/test/common/pool_trace.c @@ -92,6 +92,11 @@ static umf_result_t traceGetLastStatus(void *pool) { return umfPoolGetLastAllocationError(trace_pool->params.hUpstreamPool); } +static const char *traceGetName(void *pool) { + (void)pool; // not used + return "trace"; +} + umf_memory_pool_ops_t UMF_TRACE_POOL_OPS = { .version = UMF_POOL_OPS_VERSION_CURRENT, .initialize = traceInitialize, @@ -102,5 +107,6 @@ umf_memory_pool_ops_t UMF_TRACE_POOL_OPS = { .aligned_malloc = traceAlignedMalloc, .malloc_usable_size = traceMallocUsableSize, .free = traceFree, + .ext_get_name = traceGetName, .get_last_allocation_error = traceGetLastStatus, }; diff --git a/test/common/provider.hpp b/test/common/provider.hpp index a393a77c6..b976fdcd4 100644 --- a/test/common/provider.hpp +++ b/test/common/provider.hpp @@ -52,7 +52,9 @@ typedef struct provider_base_t { [[maybe_unused]] size_t *pageSize) noexcept { return UMF_RESULT_ERROR_UNKNOWN; } - const char *get_name() noexcept { return "base"; } + const char *get_name(/*[[maybe_unused]] void *provider*/) noexcept { + return "base"; + } umf_result_t ext_purge_lazy([[maybe_unused]] void *ptr, [[maybe_unused]] size_t size) noexcept { return UMF_RESULT_ERROR_UNKNOWN; @@ -125,7 +127,9 @@ struct provider_ba_global : public provider_base_t { umf_ba_global_free(ptr); return UMF_RESULT_SUCCESS; } - const char *get_name() noexcept { return "umf_ba_global"; } + const char *get_name(/*[[maybe_unused]] void *provider*/) noexcept { + return "umf_ba_global"; + } }; umf_memory_provider_ops_t BA_GLOBAL_PROVIDER_OPS = @@ -150,7 +154,9 @@ struct provider_mock_out_of_mem : public provider_base_t { umf_result_t free(void *ptr, size_t size) noexcept { return helper_prov.free(ptr, size); } - const char *get_name() noexcept { return "mock_out_of_mem"; } + const char *get_name(/*[[maybe_unused]] void *provider*/) noexcept { + return "mock_out_of_mem"; + } }; const umf_memory_provider_ops_t MOCK_OUT_OF_MEM_PROVIDER_OPS = diff --git a/test/ipcAPI.cpp b/test/ipcAPI.cpp index 15e995acd..4e2837e72 100644 --- a/test/ipcAPI.cpp +++ b/test/ipcAPI.cpp @@ -47,7 +47,9 @@ struct provider_mock_ipc : public umf_test::provider_base_t { return ret; } - const char *get_name() noexcept { return "mock_ipc"; } + const char *get_name(/*[[maybe_unused]] void *provider*/) noexcept { + return "mock_ipc"; + } umf_result_t ext_get_ipc_handle_size(size_t *size) noexcept { *size = sizeof(provider_ipc_data_t); diff --git a/test/memoryPoolAPI.cpp b/test/memoryPoolAPI.cpp index 8df9735a2..831649b14 100644 --- a/test/memoryPoolAPI.cpp +++ b/test/memoryPoolAPI.cpp @@ -13,7 +13,9 @@ #include #include +#include #include +#include #ifdef UMF_PROXY_LIB_ENABLED #include @@ -303,7 +305,12 @@ INSTANTIATE_TEST_SUITE_P( &BA_GLOBAL_PROVIDER_OPS, nullptr, nullptr}, poolCreateExtParams{umfDisjointPoolOps(), defaultDisjointPoolConfig, defaultDisjointPoolConfigDestroy, - &BA_GLOBAL_PROVIDER_OPS, nullptr, nullptr})); + &BA_GLOBAL_PROVIDER_OPS, nullptr, nullptr}), + // poolCreateExtParams{umfScalablePoolOps(), nullptr, nullptr, + // &BA_GLOBAL_PROVIDER_OPS, nullptr, nullptr}, + // poolCreateExtParams{umfJemallocPoolOps(), nullptr, nullptr, + // &BA_GLOBAL_PROVIDER_OPS, nullptr, nullptr}), + poolCreateExtParamsNameGen); INSTANTIATE_TEST_SUITE_P(mallocMultiPoolTest, umfMultiPoolTest, ::testing::Values(poolCreateExtParams{ @@ -435,7 +442,9 @@ TEST_F(test, getLastFailedMemoryProvider) { return UMF_RESULT_SUCCESS; } - const char *get_name() noexcept { return this->name; } + const char *get_name(/*[[maybe_unused]] void *provider*/) noexcept { + return this->name; + } const char *name; }; @@ -525,11 +534,18 @@ TEST_P(poolHandleCheck, poolHandleCheckAll) { // will be called with zero-initialized arguments. INSTANTIATE_TEST_SUITE_P( poolHandleCheck, poolHandleCheck, - ::testing::Values( - umf_test::withGeneratedArgs(umfPoolMalloc), - umf_test::withGeneratedArgs(umfPoolAlignedMalloc), - umf_test::withGeneratedArgs(umfPoolFree), - umf_test::withGeneratedArgs(umfPoolCalloc), - umf_test::withGeneratedArgs(umfPoolRealloc), - umf_test::withGeneratedArgs(umfPoolMallocUsableSize), - umf_test::withGeneratedArgs(umfPoolGetLastAllocationError))); + ::testing::Values(umf_test::withGeneratedArgs(umfPoolMalloc), + umf_test::withGeneratedArgs(umfPoolAlignedMalloc), + umf_test::withGeneratedArgs(umfPoolFree), + umf_test::withGeneratedArgs(umfPoolCalloc), + umf_test::withGeneratedArgs(umfPoolRealloc), + umf_test::withGeneratedArgs(umfPoolMallocUsableSize), + umf_test::withGeneratedArgs(umfPoolGetLastAllocationError) + /* umf_test::withGeneratedArgs(umfPoolByPtr), + umf_test::withGeneratedArgs(umfPoolGetMemoryProvider), + umf_test::withGeneratedArgs(umfPoolGetTag), + umf_test::withGeneratedArgs(umfPoolSetTag), + umf_test::withGeneratedArgs(umfPoolCreate), + umf_test::withGeneratedArgs(umfPoolDestroy), + umf_test::withGeneratedArgs(umfPoolTrimMemory))*/ + )); diff --git a/test/poolFixtures.hpp b/test/poolFixtures.hpp index 23f519ecc..3ae6b5717 100644 --- a/test/poolFixtures.hpp +++ b/test/poolFixtures.hpp @@ -33,6 +33,28 @@ using poolCreateExtParams = pfnPoolParamsDestroy, const umf_memory_provider_ops_t *, pfnProviderParamsCreate, pfnProviderParamsDestroy>; +std::string poolCreateExtParamsNameGen( + const testing::TestParamInfo param) { + + const umf_memory_pool_ops_t *pool_ops = std::get<0>(param.param); + const umf_memory_provider_ops_t *provider_ops = std::get<3>(param.param); + + std::string poolName = + pool_ops->ext_get_name ? pool_ops->ext_get_name(NULL) : "unknown_pool"; + std::string providerName = + //* + provider_ops->get_name ? provider_ops->get_name(NULL) + : "unknown_provider"; + // */ + + std::string poolParams = + std::get<1>(param.param) + ? std::string("_w_params") + std::to_string(param.index) + : std::string(""); + + return poolName + poolParams + "_" + providerName; +} + umf_test::pool_unique_handle_t poolCreateExtUnique(poolCreateExtParams params) { auto [pool_ops, poolParamsCreate, poolParamsDestroy, provider_ops, providerParamsCreate, providerParamsDestroy] = params; @@ -402,6 +424,23 @@ TEST_P(umfPoolTest, multiThreadedMallocFreeRandomSizes) { } } +TEST_P(umfPoolTest, trimMemory) { + constexpr size_t size = 1024; + + umf_memory_pool_handle_t hPool = pool.get(); + char *ptr = (char *)umfPoolMalloc(hPool, size); + ASSERT_NE(ptr, nullptr); + + umf_result_t ret = umfFree(ptr); + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + + // Call to umfPoolTrimMemory should purge the whole memory pool + ret = umfPoolTrimMemory(hPool, 0); + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + + // TODO add CTL to check that the memory was actually purged +} + TEST_P(umfMemTest, outOfMem) { static constexpr size_t allocSize = 4096; auto hPool = pool.get(); diff --git a/test/pools/disjoint_pool.cpp b/test/pools/disjoint_pool.cpp index 7780846ce..b2d0e1702 100644 --- a/test/pools/disjoint_pool.cpp +++ b/test/pools/disjoint_pool.cpp @@ -271,6 +271,85 @@ TEST_F(test, sharedLimits) { EXPECT_EQ(MaxSize / SlabMinSize * 2, numFrees); } +TEST_F(test, disjointPoolTrim) { + struct memory_provider : public umf_test::provider_base_t { + 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 { + umf_ba_global_free(ptr); + return UMF_RESULT_SUCCESS; + } + }; + + umf_memory_provider_ops_t provider_ops = + umf_test::providerMakeCOps(); + + auto providerUnique = + wrapProviderUnique(createProviderChecked(&provider_ops, nullptr)); + + umf_memory_provider_handle_t provider_handle; + provider_handle = providerUnique.get(); + + umf_disjoint_pool_params_handle_t params = + (umf_disjoint_pool_params_handle_t)defaultDisjointPoolConfig(); + params->pool_trace = 3; + // Set the slab min size to 64 so allocating 64 bytes will use the whole + // slab. + params->slab_min_size = 64; + params->capacity = 4; + + // in "internals" test we use ops interface to directly manipulate the pool + // structure + const umf_memory_pool_ops_t *ops = umfDisjointPoolOps(); + EXPECT_NE(ops, nullptr); + + disjoint_pool_t *pool; + umf_result_t res = ops->initialize(provider_handle, params, (void **)&pool); + EXPECT_EQ(res, UMF_RESULT_SUCCESS); + EXPECT_NE(pool, nullptr); + + // do 4 allocs, then free all of them + size_t size = 64; + void *ptrs[4] = {0}; + ptrs[0] = ops->malloc(pool, size); + EXPECT_NE(ptrs[0], nullptr); + ptrs[1] = ops->malloc(pool, size); + EXPECT_NE(ptrs[1], nullptr); + ptrs[2] = ops->malloc(pool, size); + EXPECT_NE(ptrs[2], nullptr); + ptrs[3] = ops->malloc(pool, size); + EXPECT_NE(ptrs[3], nullptr); + + ops->free(pool, ptrs[0]); + ops->free(pool, ptrs[1]); + ops->free(pool, ptrs[2]); + ops->free(pool, ptrs[3]); + + // Because we set the slab min size to 64, each allocation should go to the + // separate slab. Additionally, because we set the capacity to 4, all slabs + // should still be in the pool available for new allocations. + EXPECT_EQ(pool->buckets[0]->available_slabs_num, 4); + EXPECT_EQ(pool->buckets[0]->curr_slabs_in_use, 0); + EXPECT_EQ(pool->buckets[0]->curr_slabs_in_pool, 4); + + // Trim memory - leave only one slab + ops->trim_memory(pool, pool->buckets[0]->size); + EXPECT_EQ(pool->buckets[0]->available_slabs_num, 1); + EXPECT_EQ(pool->buckets[0]->curr_slabs_in_pool, 1); + + // Trim the rest of memory + ops->trim_memory(pool, 0); + EXPECT_EQ(pool->buckets[0]->available_slabs_num, 0); + EXPECT_EQ(pool->buckets[0]->curr_slabs_in_pool, 0); + + ops->finalize(pool); + res = umfDisjointPoolParamsDestroy(params); + EXPECT_EQ(res, UMF_RESULT_SUCCESS); +} + TEST_F(test, disjointPoolNullParams) { umf_result_t res = umfDisjointPoolParamsCreate(nullptr); EXPECT_EQ(res, UMF_RESULT_ERROR_INVALID_ARGUMENT); @@ -360,7 +439,8 @@ INSTANTIATE_TEST_SUITE_P(disjointPoolTests, umfPoolTest, ::testing::Values(poolCreateExtParams{ umfDisjointPoolOps(), defaultDisjointPoolConfig, defaultDisjointPoolConfigDestroy, - &BA_GLOBAL_PROVIDER_OPS, nullptr, nullptr})); + &BA_GLOBAL_PROVIDER_OPS, nullptr, nullptr}), + poolCreateExtParamsNameGen); void *memProviderParams() { return (void *)&DEFAULT_DISJOINT_CAPACITY; } diff --git a/test/pools/jemalloc_coarse_devdax.cpp b/test/pools/jemalloc_coarse_devdax.cpp index 53d2a41b3..9192d6bd5 100644 --- a/test/pools/jemalloc_coarse_devdax.cpp +++ b/test/pools/jemalloc_coarse_devdax.cpp @@ -41,4 +41,5 @@ static std::vector poolParamsList = : std::vector{}; INSTANTIATE_TEST_SUITE_P(jemallocCoarseDevDaxTest, umfPoolTest, - ::testing::ValuesIn(poolParamsList)); + ::testing::ValuesIn(poolParamsList), + poolCreateExtParamsNameGen); diff --git a/test/pools/jemalloc_pool.cpp b/test/pools/jemalloc_pool.cpp index 69c4cf1a8..239ddc53c 100644 --- a/test/pools/jemalloc_pool.cpp +++ b/test/pools/jemalloc_pool.cpp @@ -87,7 +87,8 @@ INSTANTIATE_TEST_SUITE_P( poolCreateExtParams{umfJemallocPoolOps(), createJemallocParams<1>, destroyJemallocParams, umfOsMemoryProviderOps(), createOsMemoryProviderParams, - destroyOsMemoryProviderParams})); + destroyOsMemoryProviderParams}), + poolCreateExtParamsNameGen); // this test makes sure that jemalloc does not use // memory provider to allocate metadata (and hence diff --git a/test/provider_tracking_fixture_tests.cpp b/test/provider_tracking_fixture_tests.cpp index 8ab708275..6e2bd3e48 100644 --- a/test/provider_tracking_fixture_tests.cpp +++ b/test/provider_tracking_fixture_tests.cpp @@ -37,7 +37,9 @@ struct provider_from_pool : public umf_test::provider_base_t { umf_result_t free(void *ptr, size_t) noexcept { return umfPoolFree(pool, ptr); } - const char *get_name() noexcept { return "provider_from_pool"; } + const char *get_name(/*[[maybe_unused]] void *provider*/) noexcept { + return "provider_from_pool"; + } virtual ~provider_from_pool() { if (pool) { @@ -83,10 +85,12 @@ INSTANTIATE_TEST_SUITE_P(TrackingProviderPoolTest, umfPoolTest, ::testing::Values(poolCreateExtParams{ umfProxyPoolOps(), nullptr, nullptr, &PROVIDER_FROM_POOL_OPS, - providerFromPoolParamsCreate, nullptr})); + providerFromPoolParamsCreate, nullptr}), + poolCreateExtParamsNameGen); INSTANTIATE_TEST_SUITE_P(TrackingProviderMultiPoolTest, umfMultiPoolTest, ::testing::Values(poolCreateExtParams{ umfProxyPoolOps(), nullptr, nullptr, &PROVIDER_FROM_POOL_OPS, - providerFromPoolParamsCreate, nullptr})); + providerFromPoolParamsCreate, nullptr}), + poolCreateExtParamsNameGen); diff --git a/test/utils/cpp_helpers.hpp b/test/utils/cpp_helpers.hpp index ae18d7b1b..948cda852 100644 --- a/test/utils/cpp_helpers.hpp +++ b/test/utils/cpp_helpers.hpp @@ -10,11 +10,6 @@ #ifndef UMF_TEST_HELPERS_HPP #define UMF_TEST_HELPERS_HPP 1 -#include -#include -#include -#include - #include #include #include @@ -22,6 +17,11 @@ #include #include +#include +#include +#include +#include + namespace umf_test { using pool_unique_handle_t = @@ -79,6 +79,7 @@ template umf_memory_pool_ops_t poolOpsBase() { UMF_ASSIGN_OP(ops, T, malloc_usable_size, ((size_t)0)); UMF_ASSIGN_OP(ops, T, free, UMF_RESULT_SUCCESS); UMF_ASSIGN_OP(ops, T, get_last_allocation_error, UMF_RESULT_ERROR_UNKNOWN); + UMF_ASSIGN_OP(ops, T, trim_memory, UMF_RESULT_SUCCESS); return ops; }