Skip to content

Commit 5e6f4c0

Browse files
committed
implement umfPoolTrimMemory
1 parent 39ef156 commit 5e6f4c0

17 files changed

+310
-19
lines changed

include/umf/memory_pool.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,16 @@ umf_result_t umfPoolSetTag(umf_memory_pool_handle_t hPool, void *tag,
196196
/// @return UMF_RESULT_SUCCESS on success.
197197
umf_result_t umfPoolGetTag(umf_memory_pool_handle_t hPool, void **tag);
198198

199+
///
200+
/// @brief Trims memory of the pool, removing resources that are not needed
201+
/// to keep the pool operational.
202+
/// @param hPool specified memory pool
203+
/// @param minBytesToKeep minimum number of bytes to keep in the pool (if
204+
/// possible)
205+
/// @return UMF_RESULT_SUCCESS on success or appropriate error code on failure.
206+
umf_result_t umfPoolTrimMemory(umf_memory_pool_handle_t hPool,
207+
size_t minBytesToKeep);
208+
199209
#ifdef __cplusplus
200210
}
201211
#endif

include/umf/memory_pool_ops.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ extern "C" {
2222
/// @brief Version of the Memory Pool ops structure.
2323
/// NOTE: This is equal to the latest UMF version, in which the ops structure
2424
/// has been modified.
25-
#define UMF_POOL_OPS_VERSION_CURRENT UMF_MAKE_VERSION(1, 0)
25+
#define UMF_POOL_OPS_VERSION_CURRENT UMF_MAKE_VERSION(1, 1)
2626

2727
///
2828
/// @brief This structure comprises function pointers used by corresponding umfPool*
@@ -168,6 +168,16 @@ typedef struct umf_memory_pool_ops_t {
168168
const char *name, void *arg, size_t size,
169169
umf_ctl_query_type_t queryType, va_list args);
170170

171+
///
172+
/// @brief Trims memory of the pool, removing resources that are not needed
173+
/// to keep the pool operational.
174+
/// @param pool pointer to the memory pool
175+
/// @param minBytesToKeep minimum number of bytes to keep in the pool (if
176+
/// possible)
177+
/// @return UMF_RESULT_SUCCESS on success or appropriate error code on
178+
/// failure.
179+
///
180+
umf_result_t (*ext_trim_memory)(void *pool, size_t minBytesToKeep);
171181
} umf_memory_pool_ops_t;
172182

173183
#ifdef __cplusplus

src/libumf.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,3 +144,5 @@ EXPORTS
144144
umfJemallocPoolParamsDestroy
145145
umfJemallocPoolParamsSetNumArenas
146146
umfPoolGetName
147+
; Added in UMF_1.1
148+
umfPoolTrimMemory

src/libumf.map

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,3 +141,7 @@ UMF_1.0 {
141141
local:
142142
*;
143143
};
144+
145+
UMF_1.1 {
146+
umfPoolTrimMemory;
147+
} UMF_1.0;

src/memory_pool.c

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,13 @@ umfDefaultCtlPoolHandle(void *hPool, umf_ctl_query_source_t operationType,
167167
return UMF_RESULT_ERROR_NOT_SUPPORTED;
168168
}
169169

170+
static umf_result_t umfDefaultTrimMemory(void *provider,
171+
size_t minBytesToKeep) {
172+
(void)provider;
173+
(void)minBytesToKeep;
174+
return UMF_RESULT_ERROR_NOT_SUPPORTED;
175+
}
176+
170177
// logical sum (OR) of all umf_pool_create_flags_t flags
171178
static const umf_pool_create_flags_t UMF_POOL_CREATE_FLAG_ALL =
172179
UMF_POOL_CREATE_FLAG_OWN_PROVIDER | UMF_POOL_CREATE_FLAG_DISABLE_TRACKING;
@@ -189,9 +196,9 @@ static umf_result_t umfPoolCreateInternal(const umf_memory_pool_ops_t *ops,
189196
const void *params,
190197
umf_pool_create_flags_t flags,
191198
umf_memory_pool_handle_t *hPool) {
192-
if (!ops || !provider || !hPool) {
193-
return UMF_RESULT_ERROR_INVALID_ARGUMENT;
194-
}
199+
UMF_CHECK((ops != NULL), UMF_RESULT_ERROR_INVALID_ARGUMENT);
200+
UMF_CHECK((provider != NULL), UMF_RESULT_ERROR_INVALID_ARGUMENT);
201+
UMF_CHECK((hPool != NULL), UMF_RESULT_ERROR_INVALID_ARGUMENT);
195202

196203
// validate flags
197204
if (flags & ~UMF_POOL_CREATE_FLAG_ALL) {
@@ -234,6 +241,10 @@ static umf_result_t umfPoolCreateInternal(const umf_memory_pool_ops_t *ops,
234241
pool->ops.ext_ctl = umfDefaultCtlPoolHandle;
235242
}
236243

244+
if (NULL == pool->ops.ext_trim_memory) {
245+
pool->ops.ext_trim_memory = umfDefaultTrimMemory;
246+
}
247+
237248
if (NULL == utils_mutex_init(&pool->lock)) {
238249
LOG_ERR("Failed to initialize mutex for pool");
239250
ret = UMF_RESULT_ERROR_UNKNOWN;
@@ -277,6 +288,8 @@ static umf_result_t umfPoolCreateInternal(const umf_memory_pool_ops_t *ops,
277288
}
278289

279290
umf_result_t umfPoolDestroy(umf_memory_pool_handle_t hPool) {
291+
UMF_CHECK((hPool != NULL), UMF_RESULT_ERROR_INVALID_ARGUMENT);
292+
280293
if (umf_ba_global_is_destroyed()) {
281294
return UMF_RESULT_ERROR_UNKNOWN;
282295
}
@@ -454,3 +467,10 @@ umf_result_t umfPoolGetTag(umf_memory_pool_handle_t hPool, void **tag) {
454467
utils_mutex_unlock(&hPool->lock);
455468
return UMF_RESULT_SUCCESS;
456469
}
470+
471+
umf_result_t umfPoolTrimMemory(umf_memory_pool_handle_t hPool,
472+
size_t minBytesToKeep) {
473+
UMF_CHECK((hPool != NULL), UMF_RESULT_ERROR_INVALID_ARGUMENT);
474+
475+
return hPool->ops.ext_trim_memory(hPool->pool_priv, minBytesToKeep);
476+
}

src/pool/pool_disjoint.c

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1133,6 +1133,46 @@ static umf_result_t disjoint_pool_get_name(void *pool, const char **name) {
11331133
return UMF_RESULT_SUCCESS;
11341134
}
11351135

1136+
umf_result_t disjoint_pool_trim_memory(void *pool, size_t minBytesToKeep) {
1137+
assert(pool != NULL);
1138+
disjoint_pool_t *hPool = (disjoint_pool_t *)pool;
1139+
1140+
// tracking the number of bytes left to keep
1141+
int bytesLeftToKeep = (int)minBytesToKeep;
1142+
1143+
for (size_t i = 0; i < hPool->buckets_num; i++) {
1144+
bucket_t *bucket = hPool->buckets[i];
1145+
utils_mutex_lock(&bucket->bucket_lock);
1146+
1147+
// remove empty slabs from the pool
1148+
slab_list_item_t *it = NULL, *tmp = NULL;
1149+
LL_FOREACH_SAFE(bucket->available_slabs, it, tmp) {
1150+
slab_t *slab = it->val;
1151+
if (slab->num_chunks_allocated == 0) {
1152+
if (bytesLeftToKeep > 0) {
1153+
// if we still have bytes to keep, do not remove slab
1154+
bytesLeftToKeep -= (int)slab->slab_size;
1155+
continue;
1156+
}
1157+
1158+
// remove slab
1159+
pool_unregister_slab(hPool, slab);
1160+
DL_DELETE(bucket->available_slabs, it);
1161+
assert(bucket->available_slabs_num > 0);
1162+
bucket->available_slabs_num--;
1163+
destroy_slab(slab);
1164+
1165+
// update stats
1166+
bucket_update_stats(bucket, 0, -1);
1167+
}
1168+
}
1169+
1170+
utils_mutex_unlock(&bucket->bucket_lock);
1171+
}
1172+
1173+
return UMF_RESULT_SUCCESS;
1174+
}
1175+
11361176
static umf_memory_pool_ops_t UMF_DISJOINT_POOL_OPS = {
11371177
.version = UMF_POOL_OPS_VERSION_CURRENT,
11381178
.initialize = disjoint_pool_initialize,
@@ -1146,6 +1186,7 @@ static umf_memory_pool_ops_t UMF_DISJOINT_POOL_OPS = {
11461186
.get_last_allocation_error = disjoint_pool_get_last_allocation_error,
11471187
.get_name = disjoint_pool_get_name,
11481188
.ext_ctl = disjoint_pool_ctl,
1189+
.ext_trim_memory = disjoint_pool_trim_memory,
11491190
};
11501191

11511192
const umf_memory_pool_ops_t *umfDisjointPoolOps(void) {

src/pool/pool_jemalloc.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -563,6 +563,28 @@ static umf_result_t op_get_name(void *pool, const char **name) {
563563
return UMF_RESULT_SUCCESS;
564564
}
565565

566+
static umf_result_t op_trim_memory(void *pool, size_t minBytesToKeep) {
567+
// there is no way to tell jemalloc to keep a minimum number of bytes
568+
// so we just purge all arenas
569+
if (minBytesToKeep > 0) {
570+
LOG_WARN("Ignoring minBytesToKeep (%zu) in jemalloc pool",
571+
minBytesToKeep);
572+
}
573+
574+
jemalloc_memory_pool_t *je_pool = (jemalloc_memory_pool_t *)pool;
575+
for (size_t i = 0; i < je_pool->n_arenas; i++) {
576+
char cmd[64];
577+
unsigned arena = je_pool->arena_index[i];
578+
snprintf(cmd, sizeof(cmd), "arena.%u.purge", arena);
579+
if (je_mallctl(cmd, NULL, NULL, NULL, 0)) {
580+
LOG_ERR("Could not purge jemalloc arena %u", arena);
581+
return UMF_RESULT_ERROR_UNKNOWN;
582+
}
583+
}
584+
585+
return UMF_RESULT_SUCCESS;
586+
}
587+
566588
static umf_memory_pool_ops_t UMF_JEMALLOC_POOL_OPS = {
567589
.version = UMF_POOL_OPS_VERSION_CURRENT,
568590
.initialize = op_initialize,
@@ -575,6 +597,7 @@ static umf_memory_pool_ops_t UMF_JEMALLOC_POOL_OPS = {
575597
.free = op_free,
576598
.get_last_allocation_error = op_get_last_allocation_error,
577599
.get_name = op_get_name,
600+
.ext_trim_memory = op_trim_memory,
578601
};
579602

580603
const umf_memory_pool_ops_t *umfJemallocPoolOps(void) {

src/pool/pool_proxy.c

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,13 @@ static umf_result_t proxy_get_name(void *pool, const char **name) {
136136
return UMF_RESULT_SUCCESS;
137137
}
138138

139+
static umf_result_t proxy_trim_memory(void *pool, size_t minBytesToKeep) {
140+
(void)pool;
141+
(void)minBytesToKeep;
142+
143+
return UMF_RESULT_SUCCESS;
144+
}
145+
139146
static umf_memory_pool_ops_t UMF_PROXY_POOL_OPS = {
140147
.version = UMF_POOL_OPS_VERSION_CURRENT,
141148
.initialize = proxy_pool_initialize,
@@ -147,7 +154,9 @@ static umf_memory_pool_ops_t UMF_PROXY_POOL_OPS = {
147154
.malloc_usable_size = proxy_malloc_usable_size,
148155
.free = proxy_free,
149156
.get_last_allocation_error = proxy_get_last_allocation_error,
150-
.get_name = proxy_get_name};
157+
.get_name = proxy_get_name,
158+
.ext_trim_memory = proxy_trim_memory,
159+
};
151160

152161
const umf_memory_pool_ops_t *umfProxyPoolOps(void) {
153162
return &UMF_PROXY_POOL_OPS;

src/pool/pool_scalable.c

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -422,12 +422,14 @@ static umf_result_t tbb_get_last_allocation_error(void *pool) {
422422
return TLS_last_allocation_error;
423423
}
424424

425+
static void initialize_pool_ctl(void) {}
426+
425427
static umf_result_t pool_ctl(void *hPool, umf_ctl_query_source_t operationType,
426428
const char *name, void *arg, size_t size,
427429
umf_ctl_query_type_t query_type, va_list args) {
428430
(void)operationType; // unused
429431
umf_memory_pool_handle_t pool_provider = (umf_memory_pool_handle_t)hPool;
430-
utils_init_once(&ctl_initialized, NULL);
432+
utils_init_once(&ctl_initialized, initialize_pool_ctl);
431433
return ctl_query(&pool_scallable_ctl_root, pool_provider->pool_priv,
432434
CTL_QUERY_PROGRAMMATIC, name, query_type, arg, size, args);
433435
}
@@ -438,6 +440,13 @@ static umf_result_t scalable_get_name(void *pool, const char **name) {
438440
return UMF_RESULT_SUCCESS;
439441
}
440442

443+
static umf_result_t scalable_trim_memory(void *pool, size_t minBytesToKeep) {
444+
(void)pool; // unused
445+
(void)minBytesToKeep; // unused
446+
447+
return UMF_RESULT_SUCCESS;
448+
}
449+
441450
static umf_memory_pool_ops_t UMF_SCALABLE_POOL_OPS = {
442451
.version = UMF_POOL_OPS_VERSION_CURRENT,
443452
.initialize = tbb_pool_initialize,
@@ -451,6 +460,7 @@ static umf_memory_pool_ops_t UMF_SCALABLE_POOL_OPS = {
451460
.get_last_allocation_error = tbb_get_last_allocation_error,
452461
.ext_ctl = pool_ctl,
453462
.get_name = scalable_get_name,
463+
.ext_trim_memory = scalable_trim_memory,
454464
};
455465

456466
const umf_memory_pool_ops_t *umfScalablePoolOps(void) {

src/utils/utils_posix_concurrency.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ int utils_mutex_unlock(utils_mutex_t *m) {
3838
}
3939

4040
void utils_init_once(UTIL_ONCE_FLAG *flag, void (*oneCb)(void)) {
41+
if (oneCb == NULL) {
42+
LOG_FATAL("utils_init_once: callback is NULL");
43+
return;
44+
}
45+
4146
pthread_once(flag, oneCb);
4247
}
4348

0 commit comments

Comments
 (0)