Skip to content

Commit 6de8410

Browse files
committed
add umfDisjointPoolTrimMemory
1 parent 1faa557 commit 6de8410

File tree

5 files changed

+126
-0
lines changed

5 files changed

+126
-0
lines changed

include/umf/pools/pool_disjoint.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,12 @@ umf_result_t
106106
umfDisjointPoolParamsSetName(umf_disjoint_pool_params_handle_t hParams,
107107
const char *name);
108108

109+
/// @brief Tries to release unused pooled memory back to the provider.
110+
/// @param pool handle to the memory pool.
111+
/// @param minSlabsToKeep minimum number of slabs to keep.
112+
/// @return UMF_RESULT_SUCCESS on success or appropriate error code on failure.
113+
umf_result_t umfDisjointPoolTrimMemory(void *pool, size_t minSlabsToKeep);
114+
109115
const umf_memory_pool_ops_t *umfDisjointPoolOps(void);
110116

111117
#ifdef __cplusplus

src/libumf.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ EXPORTS
139139
umfCtlExec
140140
umfCtlGet
141141
umfCtlSet
142+
umfDisjointPoolTrimMemory
142143
umfJemallocPoolParamsCreate
143144
umfJemallocPoolParamsDestroy
144145
umfJemallocPoolParamsSetNumArenas

src/libumf.map

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ UMF_0.12 {
139139
umfCtlExec;
140140
umfCtlGet;
141141
umfCtlSet;
142+
umfDisjointPoolTrimMemory;
142143
umfJemallocPoolParamsCreate;
143144
umfJemallocPoolParamsDestroy;
144145
umfJemallocPoolParamsSetNumArenas;

src/pool/pool_disjoint.c

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -941,6 +941,45 @@ static umf_memory_pool_ops_t UMF_DISJOINT_POOL_OPS = {
941941
.get_last_allocation_error = disjoint_pool_get_last_allocation_error,
942942
};
943943

944+
umf_result_t umfDisjointPoolTrimMemory(void *pool, size_t minSlabsToKeep) {
945+
if (pool == NULL) {
946+
return UMF_RESULT_ERROR_INVALID_ARGUMENT;
947+
}
948+
disjoint_pool_t *hPool = (disjoint_pool_t *)pool;
949+
950+
for (size_t i = 0; i < hPool->buckets_num; i++) {
951+
bucket_t *bucket = hPool->buckets[i];
952+
utils_mutex_lock(&bucket->bucket_lock);
953+
954+
int skip = (int)minSlabsToKeep;
955+
// remove empty slabs from the pool
956+
slab_list_item_t *it = NULL, *tmp = NULL;
957+
LL_FOREACH_SAFE(bucket->available_slabs, it, tmp) {
958+
slab_t *slab = it->val;
959+
if (slab->num_chunks_allocated == 0) {
960+
// skip first minSlabsToKeep slabs from each bucket
961+
if (--skip >= 0) {
962+
continue;
963+
}
964+
965+
// remove slab
966+
pool_unregister_slab(hPool, slab);
967+
DL_DELETE(bucket->available_slabs, it);
968+
assert(bucket->available_slabs_num > 0);
969+
bucket->available_slabs_num--;
970+
destroy_slab(slab);
971+
972+
// update stats
973+
bucket_update_stats(bucket, 0, -1);
974+
}
975+
}
976+
977+
utils_mutex_unlock(&bucket->bucket_lock);
978+
}
979+
980+
return UMF_RESULT_SUCCESS;
981+
}
982+
944983
const umf_memory_pool_ops_t *umfDisjointPoolOps(void) {
945984
return &UMF_DISJOINT_POOL_OPS;
946985
}

test/pools/disjoint_pool.cpp

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,85 @@ TEST_F(test, sharedLimits) {
269269
EXPECT_EQ(MaxSize / SlabMinSize * 2, numFrees);
270270
}
271271

272+
TEST_F(test, disjointPoolTrim) {
273+
struct memory_provider : public umf_test::provider_base_t {
274+
umf_result_t alloc(size_t size, size_t alignment, void **ptr) noexcept {
275+
*ptr = umf_ba_global_aligned_alloc(size, alignment);
276+
return UMF_RESULT_SUCCESS;
277+
}
278+
279+
umf_result_t free(void *ptr, [[maybe_unused]] size_t size) noexcept {
280+
umf_ba_global_free(ptr);
281+
return UMF_RESULT_SUCCESS;
282+
}
283+
};
284+
285+
umf_memory_provider_ops_t provider_ops =
286+
umf_test::providerMakeCOps<memory_provider, void>();
287+
288+
auto providerUnique =
289+
wrapProviderUnique(createProviderChecked(&provider_ops, nullptr));
290+
291+
umf_memory_provider_handle_t provider_handle;
292+
provider_handle = providerUnique.get();
293+
294+
umf_disjoint_pool_params_handle_t params =
295+
(umf_disjoint_pool_params_handle_t)defaultDisjointPoolConfig();
296+
params->pool_trace = 3;
297+
// Set the slab min size to 64 so allocating 64 bytes will use the whole
298+
// slab.
299+
params->slab_min_size = 64;
300+
params->capacity = 4;
301+
302+
// in "internals" test we use ops interface to directly manipulate the pool
303+
// structure
304+
const umf_memory_pool_ops_t *ops = umfDisjointPoolOps();
305+
EXPECT_NE(ops, nullptr);
306+
307+
disjoint_pool_t *pool;
308+
umf_result_t res = ops->initialize(provider_handle, params, (void **)&pool);
309+
EXPECT_EQ(res, UMF_RESULT_SUCCESS);
310+
EXPECT_NE(pool, nullptr);
311+
312+
// do 4 allocs, then free all of them
313+
size_t size = 64;
314+
void *ptrs[4] = {0};
315+
ptrs[0] = ops->malloc(pool, size);
316+
EXPECT_NE(ptrs[0], nullptr);
317+
ptrs[1] = ops->malloc(pool, size);
318+
EXPECT_NE(ptrs[1], nullptr);
319+
ptrs[2] = ops->malloc(pool, size);
320+
EXPECT_NE(ptrs[2], nullptr);
321+
ptrs[3] = ops->malloc(pool, size);
322+
EXPECT_NE(ptrs[3], nullptr);
323+
324+
ops->free(pool, ptrs[0]);
325+
ops->free(pool, ptrs[1]);
326+
ops->free(pool, ptrs[2]);
327+
ops->free(pool, ptrs[3]);
328+
329+
// Because we set the slab min size to 64, each allocation should go to the
330+
// separate slab. Additionally, because we set the capacity to 4, all slabs
331+
// should still be in the pool available for new allocations.
332+
EXPECT_EQ(pool->buckets[0]->available_slabs_num, 4);
333+
EXPECT_EQ(pool->buckets[0]->curr_slabs_in_use, 0);
334+
EXPECT_EQ(pool->buckets[0]->curr_slabs_in_pool, 4);
335+
336+
// Trim memory - leave only one slab
337+
umfDisjointPoolTrimMemory(pool, 1);
338+
EXPECT_EQ(pool->buckets[0]->available_slabs_num, 1);
339+
EXPECT_EQ(pool->buckets[0]->curr_slabs_in_pool, 1);
340+
341+
// Trim the rest of memory
342+
umfDisjointPoolTrimMemory(pool, 0);
343+
EXPECT_EQ(pool->buckets[0]->available_slabs_num, 0);
344+
EXPECT_EQ(pool->buckets[0]->curr_slabs_in_pool, 0);
345+
346+
ops->finalize(pool);
347+
res = umfDisjointPoolParamsDestroy(params);
348+
EXPECT_EQ(res, UMF_RESULT_SUCCESS);
349+
}
350+
272351
TEST_F(test, disjointPoolNullParams) {
273352
umf_result_t res = umfDisjointPoolParamsCreate(nullptr);
274353
EXPECT_EQ(res, UMF_RESULT_ERROR_INVALID_ARGUMENT);

0 commit comments

Comments
 (0)