Skip to content

Commit 6a3ae03

Browse files
committed
x
1 parent b1ca59d commit 6a3ae03

17 files changed

+268
-10
lines changed

include/umf/memory_pool.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,15 @@ umf_result_t umfPoolSetTag(umf_memory_pool_handle_t hPool, void *tag,
190190
/// @return UMF_RESULT_SUCCESS on success.
191191
umf_result_t umfPoolGetTag(umf_memory_pool_handle_t hPool, void **tag);
192192

193+
///
194+
/// @brief Trims memory pool to keep at least \p minBytesToKeep bytes of memory
195+
/// if possible.
196+
/// @param hPool specified memory pool
197+
/// @param minBytesToKeep minimum number of bytes to keep in the pool
198+
/// @return UMF_RESULT_SUCCESS on success or appropriate error code on failure.
199+
umf_result_t umfPoolTrimMemory(umf_memory_pool_handle_t hPool,
200+
size_t minBytesToKeep);
201+
193202
#ifdef __cplusplus
194203
}
195204
#endif

include/umf/memory_pool_ops.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,16 @@ typedef struct umf_memory_pool_ops_t {
161161
/// @return A constant character string representing the pool's name.
162162
///
163163
const char *(*ext_get_name)(void *pool);
164+
165+
///
166+
/// @brief Trims memory of the pool, removing resources that are not needed
167+
/// to keep the pool operational.
168+
/// @param pool pointer to the memory pool
169+
/// @param minBytesToKeep minimum number of bytes to keep in the pool if
170+
/// possible.
171+
/// @return UMF_RESULT_SUCCESS on success or appropriate error code on failure.
172+
///
173+
umf_result_t (*ext_trim_memory)(void *pool, size_t minBytesToKeep);
164174
} umf_memory_pool_ops_t;
165175

166176
#ifdef __cplusplus

src/libumf.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,3 +144,4 @@ EXPORTS
144144
umfJemallocPoolParamsDestroy
145145
umfJemallocPoolParamsSetNumArenas
146146
umfPoolGetName
147+
umfPoolTrimMemory

src/libumf.map

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,4 +144,5 @@ UMF_0.12 {
144144
umfJemallocPoolParamsDestroy;
145145
umfJemallocPoolParamsSetNumArenas;
146146
umfPoolGetName;
147+
umfPoolTrimMemory;
147148
} UMF_0.11;

src/memory_pool.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,10 @@ static umf_result_t umfPoolCreateInternal(const umf_memory_pool_ops_t *ops,
240240
}
241241

242242
umf_result_t umfPoolDestroy(umf_memory_pool_handle_t hPool) {
243+
if (hPool == NULL) {
244+
return UMF_RESULT_ERROR_INVALID_ARGUMENT;
245+
}
246+
243247
if (umf_ba_global_is_destroyed()) {
244248
return UMF_RESULT_ERROR_UNKNOWN;
245249
}
@@ -417,3 +421,13 @@ umf_result_t umfPoolGetTag(umf_memory_pool_handle_t hPool, void **tag) {
417421
utils_mutex_unlock(&hPool->lock);
418422
return UMF_RESULT_SUCCESS;
419423
}
424+
425+
umf_result_t umfPoolTrimMemory(umf_memory_pool_handle_t hPool,
426+
size_t minBytesToKeep) {
427+
UMF_CHECK((hPool != NULL), UMF_RESULT_ERROR_INVALID_ARGUMENT);
428+
if (hPool->ops.ext_trim_memory == NULL) {
429+
return UMF_RESULT_ERROR_NOT_SUPPORTED;
430+
}
431+
432+
return hPool->ops.ext_trim_memory(hPool->pool_priv, minBytesToKeep);
433+
}

src/pool/pool_disjoint.c

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1031,6 +1031,47 @@ const char *disjoint_pool_get_name(void *pool) {
10311031
return hPool->params.name;
10321032
}
10331033

1034+
umf_result_t disjoint_pool_trim_memory(void *pool, size_t minBytesToKeep) {
1035+
disjoint_pool_t *hPool = (disjoint_pool_t *)pool;
1036+
if (hPool == NULL) {
1037+
return UMF_RESULT_ERROR_INVALID_ARGUMENT;
1038+
}
1039+
1040+
for (size_t i = 0; i < hPool->buckets_num; i++) {
1041+
bucket_t *bucket = hPool->buckets[i];
1042+
utils_mutex_lock(&bucket->bucket_lock);
1043+
1044+
int skip = (int)minBytesToKeep;
1045+
1046+
// remove empty slabs from the pool
1047+
slab_list_item_t *it = NULL, *tmp = NULL;
1048+
LL_FOREACH_SAFE(bucket->available_slabs, it, tmp) {
1049+
slab_t *slab = it->val;
1050+
if (slab->num_chunks_allocated == 0) {
1051+
// skip first minBytesToKeep bytes from each bucket
1052+
if (skip > 0) {
1053+
skip -= slab->slab_size;
1054+
continue;
1055+
}
1056+
1057+
// remove slab
1058+
pool_unregister_slab(hPool, slab);
1059+
DL_DELETE(bucket->available_slabs, it);
1060+
assert(bucket->available_slabs_num > 0);
1061+
bucket->available_slabs_num--;
1062+
destroy_slab(slab);
1063+
1064+
// update stats
1065+
bucket_update_stats(bucket, 0, -1);
1066+
}
1067+
}
1068+
1069+
utils_mutex_unlock(&bucket->bucket_lock);
1070+
}
1071+
1072+
return UMF_RESULT_SUCCESS;
1073+
}
1074+
10341075
static umf_memory_pool_ops_t UMF_DISJOINT_POOL_OPS = {
10351076
.version = UMF_VERSION_CURRENT,
10361077
.initialize = disjoint_pool_initialize,
@@ -1044,6 +1085,7 @@ static umf_memory_pool_ops_t UMF_DISJOINT_POOL_OPS = {
10441085
.get_last_allocation_error = disjoint_pool_get_last_allocation_error,
10451086
.ext_get_name = disjoint_pool_get_name,
10461087
.ext_ctl = disjoint_pool_ctl,
1088+
.ext_trim_memory = disjoint_pool_trim_memory,
10471089
};
10481090

10491091
const umf_memory_pool_ops_t *umfDisjointPoolOps(void) {

src/pool/pool_jemalloc.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -552,6 +552,23 @@ static umf_result_t op_get_last_allocation_error(void *pool) {
552552
return TLS_last_allocation_error;
553553
}
554554

555+
static umf_result_t op_trim_memory(void *pool, size_t minBytesToKeep) {
556+
(void)minBytesToKeep; // unused - TODO?
557+
558+
jemalloc_memory_pool_t *je_pool = (jemalloc_memory_pool_t *)pool;
559+
for (size_t i = 0; i < je_pool->n_arenas; i++) {
560+
char cmd[64];
561+
unsigned arena = je_pool->arena_index[i];
562+
snprintf(cmd, sizeof(cmd), "arena.%u.purge", arena);
563+
if (je_mallctl(cmd, NULL, NULL, NULL, 0)) {
564+
LOG_ERR("Could not purge jemalloc arena %u", arena);
565+
return UMF_RESULT_ERROR_UNKNOWN;
566+
}
567+
}
568+
569+
return UMF_RESULT_SUCCESS;
570+
}
571+
555572
static umf_memory_pool_ops_t UMF_JEMALLOC_POOL_OPS = {
556573
.version = UMF_POOL_OPS_VERSION_CURRENT,
557574
.initialize = op_initialize,
@@ -563,6 +580,7 @@ static umf_memory_pool_ops_t UMF_JEMALLOC_POOL_OPS = {
563580
.malloc_usable_size = op_malloc_usable_size,
564581
.free = op_free,
565582
.get_last_allocation_error = op_get_last_allocation_error,
583+
.ext_trim_memory = op_trim_memory,
566584
};
567585

568586
const umf_memory_pool_ops_t *umfJemallocPoolOps(void) {

src/pool/pool_proxy.c

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,18 @@ static umf_result_t proxy_get_last_allocation_error(void *pool) {
125125
return TLS_last_allocation_error;
126126
}
127127

128+
static const char *proxy_get_name(void *pool) {
129+
(void)pool; // not used
130+
return "proxy";
131+
}
132+
133+
static umf_result_t proxy_trim_memory(void *pool, size_t minBytesToKeep) {
134+
(void)pool;
135+
(void)minBytesToKeep;
136+
137+
return UMF_RESULT_SUCCESS;
138+
}
139+
128140
static umf_memory_pool_ops_t UMF_PROXY_POOL_OPS = {
129141
.version = UMF_POOL_OPS_VERSION_CURRENT,
130142
.initialize = proxy_pool_initialize,
@@ -135,7 +147,10 @@ static umf_memory_pool_ops_t UMF_PROXY_POOL_OPS = {
135147
.aligned_malloc = proxy_aligned_malloc,
136148
.malloc_usable_size = proxy_malloc_usable_size,
137149
.free = proxy_free,
138-
.get_last_allocation_error = proxy_get_last_allocation_error};
150+
.get_last_allocation_error = proxy_get_last_allocation_error,
151+
.ext_get_name = proxy_get_name,
152+
.ext_trim_memory = proxy_trim_memory,
153+
};
139154

140155
const umf_memory_pool_ops_t *umfProxyPoolOps(void) {
141156
return &UMF_PROXY_POOL_OPS;

src/pool/pool_scalable.c

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,15 @@
1313
#include <stdio.h>
1414
#include <string.h>
1515

16-
#include <ctl/ctl.h>
17-
#include <memory_pool_internal.h>
1816
#include <umf/memory_pool.h>
1917
#include <umf/memory_pool_ops.h>
2018
#include <umf/memory_provider.h>
2119
#include <umf/pools/pool_scalable.h>
2220

2321
#include "base_alloc_global.h"
22+
#include "ctl/ctl.h"
2423
#include "libumf.h"
24+
#include "memory_pool_internal.h"
2525
#include "pool_scalable_internal.h"
2626
#include "utils_common.h"
2727
#include "utils_concurrency.h"
@@ -60,6 +60,7 @@ typedef struct tbb_callbacks_t {
6060
bool (*pool_destroy)(void *);
6161
void *(*pool_identify)(void *object);
6262
size_t (*pool_msize)(void *, void *);
63+
int (*pool_allocation_command)(int, void *);
6364
#ifdef _WIN32
6465
HMODULE lib_handle;
6566
#else
@@ -431,6 +432,14 @@ static const char *scalable_get_name(void *pool) {
431432
return "scalable";
432433
}
433434

435+
static umf_result_t scalable_trim_memory(void *pool, size_t minBytesToKeep) {
436+
(void)pool; // unused
437+
(void)minBytesToKeep; // unused
438+
439+
//scalable_allocation_command?
440+
return UMF_RESULT_SUCCESS;
441+
}
442+
434443
static umf_memory_pool_ops_t UMF_SCALABLE_POOL_OPS = {
435444
.version = UMF_POOL_OPS_VERSION_CURRENT,
436445
.initialize = tbb_pool_initialize,
@@ -444,6 +453,7 @@ static umf_memory_pool_ops_t UMF_SCALABLE_POOL_OPS = {
444453
.get_last_allocation_error = tbb_get_last_allocation_error,
445454
.ext_ctl = pool_ctl,
446455
.ext_get_name = scalable_get_name,
456+
.ext_trim_memory = scalable_trim_memory,
447457
};
448458

449459
const umf_memory_pool_ops_t *umfScalablePoolOps(void) {

test/common/pool.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,10 @@ typedef struct pool_base_t {
117117
umf_result_t get_last_allocation_error() noexcept {
118118
return UMF_RESULT_SUCCESS;
119119
}
120+
umf_result_t
121+
ext_trim_memory([[maybe_unused]] size_t minBytesToKeep) noexcept {
122+
return UMF_RESULT_SUCCESS;
123+
}
120124
} pool_base_t;
121125

122126
struct malloc_pool : public pool_base_t {

test/memoryPoolAPI.cpp

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@
1313

1414
#include <umf/memory_provider.h>
1515
#include <umf/pools/pool_disjoint.h>
16+
#include <umf/pools/pool_jemalloc.h>
1617
#include <umf/pools/pool_proxy.h>
18+
#include <umf/pools/pool_scalable.h>
1719

1820
#ifdef UMF_PROXY_LIB_ENABLED
1921
#include <umf/proxy_lib_new_delete.h>
@@ -303,7 +305,12 @@ INSTANTIATE_TEST_SUITE_P(
303305
&BA_GLOBAL_PROVIDER_OPS, nullptr, nullptr},
304306
poolCreateExtParams{umfDisjointPoolOps(), defaultDisjointPoolConfig,
305307
defaultDisjointPoolConfigDestroy,
306-
&BA_GLOBAL_PROVIDER_OPS, nullptr, nullptr}));
308+
&BA_GLOBAL_PROVIDER_OPS, nullptr, nullptr}),
309+
// poolCreateExtParams{umfScalablePoolOps(), nullptr, nullptr,
310+
// &BA_GLOBAL_PROVIDER_OPS, nullptr, nullptr},
311+
// poolCreateExtParams{umfJemallocPoolOps(), nullptr, nullptr,
312+
// &BA_GLOBAL_PROVIDER_OPS, nullptr, nullptr}),
313+
poolCreateExtParamsNameGen);
307314

308315
INSTANTIATE_TEST_SUITE_P(mallocMultiPoolTest, umfMultiPoolTest,
309316
::testing::Values(poolCreateExtParams{
@@ -532,4 +539,11 @@ INSTANTIATE_TEST_SUITE_P(
532539
umf_test::withGeneratedArgs(umfPoolCalloc),
533540
umf_test::withGeneratedArgs(umfPoolRealloc),
534541
umf_test::withGeneratedArgs(umfPoolMallocUsableSize),
535-
umf_test::withGeneratedArgs(umfPoolGetLastAllocationError)));
542+
umf_test::withGeneratedArgs(umfPoolGetLastAllocationError),
543+
umf_test::withGeneratedArgs(umfPoolByPtr),
544+
umf_test::withGeneratedArgs(umfPoolGetMemoryProvider),
545+
umf_test::withGeneratedArgs(umfPoolGetTag),
546+
umf_test::withGeneratedArgs(umfPoolSetTag),
547+
umf_test::withGeneratedArgs(umfPoolCreate),
548+
umf_test::withGeneratedArgs(umfPoolDestroy),
549+
umf_test::withGeneratedArgs(umfPoolTrimMemory)));

test/poolFixtures.hpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,24 @@ using poolCreateExtParams =
3333
pfnPoolParamsDestroy, const umf_memory_provider_ops_t *,
3434
pfnProviderParamsCreate, pfnProviderParamsDestroy>;
3535

36+
std::string poolCreateExtParamsNameGen(
37+
const testing::TestParamInfo<poolCreateExtParams> param) {
38+
39+
const umf_memory_pool_ops_t *pool_ops = std::get<0>(param.param);
40+
const umf_memory_provider_ops_t *provider_ops = std::get<3>(param.param);
41+
42+
std::string poolName =
43+
pool_ops->ext_get_name ? pool_ops->ext_get_name(NULL) : "unknown_pool";
44+
std::string providerName = provider_ops->get_name
45+
? provider_ops->get_name(NULL)
46+
: "unknown_provider";
47+
48+
std::string poolParams =
49+
std::get<1>(param.param) ? std::string("_w_params") + std::to_string(param.index) : std::string("");
50+
51+
return poolName + poolParams + "_" + providerName;
52+
}
53+
3654
umf_test::pool_unique_handle_t poolCreateExtUnique(poolCreateExtParams params) {
3755
auto [pool_ops, poolParamsCreate, poolParamsDestroy, provider_ops,
3856
providerParamsCreate, providerParamsDestroy] = params;
@@ -402,6 +420,23 @@ TEST_P(umfPoolTest, multiThreadedMallocFreeRandomSizes) {
402420
}
403421
}
404422

423+
TEST_P(umfPoolTest, trimMemory) {
424+
constexpr size_t size = 1024;
425+
426+
umf_memory_pool_handle_t hPool = pool.get();
427+
char *ptr = (char *)umfPoolMalloc(hPool, size);
428+
ASSERT_NE(ptr, nullptr);
429+
430+
umf_result_t ret = umfFree(ptr);
431+
ASSERT_EQ(ret, UMF_RESULT_SUCCESS);
432+
433+
// Call to umfPoolTrimMemory should purge the whole memory pool
434+
ret = umfPoolTrimMemory(hPool, 0);
435+
ASSERT_EQ(ret, UMF_RESULT_SUCCESS);
436+
437+
// TODO add CTL to check that the memory was actually purged
438+
}
439+
405440
TEST_P(umfMemTest, outOfMem) {
406441
static constexpr size_t allocSize = 4096;
407442
auto hPool = pool.get();

0 commit comments

Comments
 (0)