Skip to content

Commit e26ee7d

Browse files
committed
add pool_by_name
1 parent 0367963 commit e26ee7d

File tree

6 files changed

+323
-2
lines changed

6 files changed

+323
-2
lines changed

src/base_alloc/base_alloc_global.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,3 +265,19 @@ size_t umf_ba_global_malloc_usable_size(const void *ptr) {
265265

266266
return usable_size;
267267
}
268+
269+
char *umf_ba_global_strdup(const char *s) {
270+
if (!s) {
271+
return NULL;
272+
}
273+
274+
size_t len = strlen(s);
275+
276+
char *ptr = umf_ba_global_alloc(len + 1);
277+
if (!ptr) {
278+
return NULL;
279+
}
280+
281+
memcpy(ptr, s, len + 1);
282+
return ptr;
283+
}

src/base_alloc/base_alloc_global.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ void umf_ba_destroy_global(void);
2222
bool umf_ba_global_is_destroyed(void);
2323
size_t umf_ba_global_malloc_usable_size(const void *ptr);
2424
void *umf_ba_global_aligned_alloc(size_t size, size_t alignment);
25+
char *umf_ba_global_strdup(const char *s);
2526

2627
#ifdef __cplusplus
2728
}

src/ctl/ctl.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ char *Strdup(const char *s) {
9595
} \
9696
case CTL_ARG_TYPE_STRING: { \
9797
char *str = va_arg(va, char *); \
98-
memcpy(output, str, ctl_argument->dest_size); \
98+
snprintf((char *)output, ctl_argument->dest_size, "%s", str); \
9999
break; \
100100
} \
101101
case CTL_ARG_TYPE_INT: { \

src/ctl/ctl_internal.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ int ctl_arg_integer(const void *arg, void *dest, size_t dest_size);
158158
int ctl_arg_string(const void *arg, void *dest, size_t dest_size);
159159
#define CTL_ARG_STRING(len) \
160160
{ \
161-
len, CTL_ARG_TYPE_PTR, { \
161+
len, CTL_ARG_TYPE_STRING, { \
162162
{0, len, ctl_arg_string}, CTL_ARG_PARSER_END \
163163
} \
164164
}

src/memory_pool.c

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,17 @@
2626
#include "utils_log.h"
2727
#include "utlist.h"
2828

29+
// Handle UTHash memory allocation failures without aborting the process.
30+
#define HASH_NONFATAL_OOM 1
31+
static bool uthash_oom = false;
32+
#define uthash_nonfatal_oom(obj) \
33+
do { \
34+
(void)(obj); \
35+
uthash_oom = true; \
36+
} while (0)
37+
38+
#include "uthash.h"
39+
2940
typedef struct ctl_default_entry_t {
3041
char *name;
3142
void *value;
@@ -43,6 +54,104 @@ static struct ctl umf_pool_ctl_root;
4354

4455
static void pool_ctl_init(void);
4556

57+
typedef struct pool_name_list_entry_t {
58+
umf_memory_pool_handle_t pool;
59+
struct pool_name_list_entry_t *next;
60+
} pool_name_list_entry_t;
61+
62+
typedef struct pool_name_dict_entry_t {
63+
char *name; /* key */
64+
pool_name_list_entry_t *pools;
65+
UT_hash_handle hh;
66+
} pool_name_dict_entry_t;
67+
68+
static pool_name_dict_entry_t *pools_by_name = NULL;
69+
static utils_rwlock_t pools_by_name_lock;
70+
static UTIL_ONCE_FLAG pools_by_name_init_once = UTIL_ONCE_FLAG_INIT;
71+
72+
static void pools_by_name_init(void) { utils_rwlock_init(&pools_by_name_lock); }
73+
74+
static umf_result_t pools_by_name_add(umf_memory_pool_handle_t pool) {
75+
const char *name = NULL;
76+
umf_result_t ret = pool->ops.get_name(pool->pool_priv, &name);
77+
if (ret != UMF_RESULT_SUCCESS || !name) {
78+
return ret;
79+
}
80+
81+
utils_init_once(&pools_by_name_init_once, pools_by_name_init);
82+
utils_write_lock(&pools_by_name_lock);
83+
84+
pool_name_dict_entry_t *entry = NULL;
85+
HASH_FIND_STR(pools_by_name, name, entry);
86+
if (!entry) {
87+
entry = umf_ba_global_alloc(sizeof(*entry));
88+
if (!entry) {
89+
utils_write_unlock(&pools_by_name_lock);
90+
return UMF_RESULT_ERROR_OUT_OF_HOST_MEMORY;
91+
}
92+
entry->name = umf_ba_global_strdup(name);
93+
if (!entry->name) {
94+
umf_ba_global_free(entry);
95+
utils_write_unlock(&pools_by_name_lock);
96+
return UMF_RESULT_ERROR_OUT_OF_HOST_MEMORY;
97+
}
98+
99+
entry->pools = NULL;
100+
uthash_oom = false;
101+
HASH_ADD_KEYPTR(hh, pools_by_name, entry->name, strlen(entry->name),
102+
entry);
103+
if (uthash_oom) {
104+
umf_ba_global_free(entry->name);
105+
umf_ba_global_free(entry);
106+
utils_write_unlock(&pools_by_name_lock);
107+
return UMF_RESULT_ERROR_OUT_OF_HOST_MEMORY;
108+
}
109+
}
110+
111+
pool_name_list_entry_t *node = umf_ba_global_alloc(sizeof(*node));
112+
if (!node) {
113+
utils_write_unlock(&pools_by_name_lock);
114+
return UMF_RESULT_ERROR_OUT_OF_HOST_MEMORY;
115+
}
116+
node->pool = pool;
117+
node->next = NULL;
118+
LL_APPEND(entry->pools, node);
119+
120+
utils_write_unlock(&pools_by_name_lock);
121+
return UMF_RESULT_SUCCESS;
122+
}
123+
124+
static void pools_by_name_remove(umf_memory_pool_handle_t pool) {
125+
const char *name = NULL;
126+
if (pool->ops.get_name(pool->pool_priv, &name) != UMF_RESULT_SUCCESS ||
127+
!name) {
128+
return;
129+
}
130+
131+
utils_init_once(&pools_by_name_init_once, pools_by_name_init);
132+
utils_write_lock(&pools_by_name_lock);
133+
134+
pool_name_dict_entry_t *entry = NULL;
135+
HASH_FIND_STR(pools_by_name, name, entry);
136+
if (entry) {
137+
pool_name_list_entry_t *it = NULL, *tmp = NULL;
138+
LL_FOREACH_SAFE(entry->pools, it, tmp) {
139+
if (it->pool == pool) {
140+
LL_DELETE(entry->pools, it);
141+
umf_ba_global_free(it);
142+
break;
143+
}
144+
}
145+
if (entry->pools == NULL) {
146+
HASH_DEL(pools_by_name, entry);
147+
umf_ba_global_free(entry->name);
148+
umf_ba_global_free(entry);
149+
}
150+
}
151+
152+
utils_write_unlock(&pools_by_name_lock);
153+
}
154+
46155
static umf_result_t CTL_SUBTREE_HANDLER(CTL_NONAME, by_handle)(
47156
void *ctx, umf_ctl_query_source_t source, void *arg, size_t size,
48157
umf_ctl_index_utlist_t *indexes, const char *extra_name,
@@ -190,7 +299,69 @@ static umf_ctl_node_t CTL_NODE(by_handle)[] = {
190299

191300
static const struct ctl_argument CTL_ARG(by_handle) = CTL_ARG_PTR;
192301

302+
static umf_result_t CTL_SUBTREE_HANDLER(CTL_NONAME, by_name)(
303+
void *ctx, umf_ctl_query_source_t source, void *arg, size_t size,
304+
umf_ctl_index_utlist_t *indexes, const char *extra_name,
305+
umf_ctl_query_type_t queryType, va_list args) {
306+
(void)ctx;
307+
308+
utils_init_once(&pools_by_name_init_once, pools_by_name_init);
309+
310+
const char *name = (const char *)indexes->arg;
311+
312+
utils_read_lock(&pools_by_name_lock);
313+
pool_name_dict_entry_t *entry = NULL;
314+
HASH_FIND_STR(pools_by_name, name, entry);
315+
if (!entry) {
316+
utils_read_unlock(&pools_by_name_lock);
317+
LOG_ERR("Pool %s not found", name);
318+
return UMF_RESULT_ERROR_INVALID_ARGUMENT;
319+
}
320+
321+
size_t count = 0;
322+
pool_name_list_entry_t *it = NULL;
323+
LL_COUNT(entry->pools, it, count);
324+
if (queryType == CTL_QUERY_READ && count > 1) {
325+
LOG_ERR("CTL 'by_name' read operation requires exactly one pool with "
326+
"the specified name. "
327+
"Actual number of pools with name '%s' is %zu.",
328+
name, count);
329+
utils_read_unlock(&pools_by_name_lock);
330+
return UMF_RESULT_ERROR_INVALID_ARGUMENT;
331+
}
332+
333+
umf_result_t ret = UMF_RESULT_SUCCESS;
334+
LL_FOREACH(entry->pools, it) {
335+
va_list args2;
336+
va_copy(args2, args);
337+
umf_result_t r = ctl_query(&umf_pool_ctl_root, it->pool, source,
338+
extra_name, queryType, arg, size, args2);
339+
va_end(args2);
340+
341+
if (r == UMF_RESULT_ERROR_INVALID_ARGUMENT) {
342+
va_copy(args2, args);
343+
r = it->pool->ops.ext_ctl(it->pool->pool_priv, source, extra_name,
344+
arg, size, queryType, args2);
345+
va_end(args2);
346+
}
347+
if (r != UMF_RESULT_SUCCESS && ret == UMF_RESULT_SUCCESS) {
348+
ret = r;
349+
}
350+
}
351+
utils_read_unlock(&pools_by_name_lock);
352+
353+
return ret;
354+
}
355+
356+
static umf_ctl_node_t CTL_NODE(by_name)[] = {
357+
CTL_LEAF_SUBTREE(CTL_NONAME, by_name),
358+
CTL_NODE_END,
359+
};
360+
361+
static const struct ctl_argument CTL_ARG(by_name) = CTL_ARG_STRING(255);
362+
193363
umf_ctl_node_t CTL_NODE(pool)[] = {CTL_CHILD_WITH_ARG(by_handle),
364+
CTL_CHILD_WITH_ARG(by_name),
194365
CTL_LEAF_SUBTREE(default), CTL_NODE_END};
195366

196367
static void pool_ctl_init(void) {
@@ -311,6 +482,7 @@ static umf_result_t umfPoolCreateInternal(const umf_memory_pool_ops_t *ops,
311482
}
312483

313484
*hPool = pool;
485+
pools_by_name_add(pool);
314486
LOG_INFO("Memory pool created: %p", (void *)pool);
315487
return UMF_RESULT_SUCCESS;
316488

@@ -330,6 +502,8 @@ umf_result_t umfPoolDestroy(umf_memory_pool_handle_t hPool) {
330502
return UMF_RESULT_ERROR_UNKNOWN;
331503
}
332504

505+
pools_by_name_remove(hPool);
506+
333507
umf_result_t ret = hPool->ops.finalize(hPool->pool_priv);
334508

335509
umf_memory_provider_handle_t hUpstreamProvider = NULL;

test/ctl/ctl_api.cpp

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,3 +432,133 @@ TEST_F(test, ctl_logger_output_file) {
432432
UMF_RESULT_SUCCESS);
433433
EXPECT_STREQ(out_get, file_name);
434434
}
435+
436+
TEST_F(test, ctl_by_name) {
437+
umf_memory_provider_handle_t hProvider = NULL;
438+
umf_os_memory_provider_params_handle_t os_memory_provider_params = NULL;
439+
const umf_memory_provider_ops_t *os_provider_ops = umfOsMemoryProviderOps();
440+
if (os_provider_ops == NULL) {
441+
GTEST_SKIP() << "OS memory provider is not supported!";
442+
}
443+
444+
int ret = umfOsMemoryProviderParamsCreate(&os_memory_provider_params);
445+
ret = umfMemoryProviderCreate(os_provider_ops, os_memory_provider_params,
446+
&hProvider);
447+
ASSERT_EQ(ret, UMF_RESULT_SUCCESS);
448+
umfOsMemoryProviderParamsDestroy(os_memory_provider_params);
449+
450+
umf_disjoint_pool_params_handle_t disjoint_pool_params = NULL;
451+
ret = umfDisjointPoolParamsCreate(&disjoint_pool_params);
452+
ASSERT_EQ(ret, UMF_RESULT_SUCCESS);
453+
454+
const char *pool_name = "test_disjoint_pool";
455+
ret = umfDisjointPoolParamsSetName(disjoint_pool_params, pool_name);
456+
ASSERT_EQ(ret, UMF_RESULT_SUCCESS);
457+
458+
umf_memory_pool_handle_t hPool = NULL;
459+
ret = umfPoolCreate(umfDisjointPoolOps(), hProvider, disjoint_pool_params,
460+
0, &hPool);
461+
462+
ASSERT_EQ(ret, UMF_RESULT_SUCCESS);
463+
464+
const char *pool_name2 = "test_disjoint_pool2";
465+
ret = umfDisjointPoolParamsSetName(disjoint_pool_params, pool_name2);
466+
ASSERT_EQ(ret, UMF_RESULT_SUCCESS);
467+
468+
umf_memory_pool_handle_t hPool2 = NULL;
469+
ret = umfPoolCreate(umfDisjointPoolOps(), hProvider, disjoint_pool_params,
470+
0, &hPool2);
471+
umfDisjointPoolParamsDestroy(disjoint_pool_params);
472+
473+
size_t alloc_count;
474+
ret = umfCtlGet("umf.pool.by_name.{}.stats.alloc_count", &alloc_count,
475+
sizeof(alloc_count), pool_name);
476+
477+
ASSERT_EQ(ret, UMF_RESULT_SUCCESS);
478+
ASSERT_EQ(alloc_count, 0ull);
479+
480+
ret = umfCtlGet("umf.pool.by_name.{}.stats.alloc_count", &alloc_count,
481+
sizeof(alloc_count), pool_name2);
482+
483+
ASSERT_EQ(ret, UMF_RESULT_SUCCESS);
484+
ASSERT_EQ(alloc_count, 0ull);
485+
486+
// allocate from pool1
487+
void *ptr1 = umfPoolMalloc(hPool, 1024);
488+
ASSERT_EQ(ret, UMF_RESULT_SUCCESS);
489+
ASSERT_NE(ptr1, nullptr);
490+
491+
// we can use pool name in the string without {} too
492+
ret = umfCtlGet("umf.pool.by_name.test_disjoint_pool.stats.alloc_count",
493+
&alloc_count, sizeof(alloc_count));
494+
495+
ASSERT_EQ(ret, UMF_RESULT_SUCCESS);
496+
ASSERT_EQ(alloc_count, 1ull);
497+
498+
ret = umfCtlGet("umf.pool.by_name.test_disjoint_pool2.stats.alloc_count",
499+
&alloc_count, sizeof(alloc_count));
500+
501+
ASSERT_EQ(ret, UMF_RESULT_SUCCESS);
502+
ASSERT_EQ(alloc_count, 0ull);
503+
504+
ret = umfPoolFree(hPool, ptr1);
505+
ASSERT_EQ(ret, UMF_RESULT_SUCCESS);
506+
ret = umfPoolDestroy(hPool);
507+
ASSERT_EQ(ret, UMF_RESULT_SUCCESS);
508+
ret = umfPoolDestroy(hPool2);
509+
ASSERT_EQ(ret, UMF_RESULT_SUCCESS);
510+
ret = umfMemoryProviderDestroy(hProvider);
511+
ASSERT_EQ(ret, UMF_RESULT_SUCCESS);
512+
}
513+
514+
TEST_F(test, ctl_by_name_collision) {
515+
umf_memory_provider_handle_t hProvider = NULL;
516+
umf_os_memory_provider_params_handle_t os_memory_provider_params = NULL;
517+
const umf_memory_provider_ops_t *os_provider_ops = umfOsMemoryProviderOps();
518+
if (os_provider_ops == NULL) {
519+
GTEST_SKIP() << "OS memory provider is not supported!";
520+
}
521+
522+
int ret = umfOsMemoryProviderParamsCreate(&os_memory_provider_params);
523+
ret = umfMemoryProviderCreate(os_provider_ops, os_memory_provider_params,
524+
&hProvider);
525+
ASSERT_EQ(ret, UMF_RESULT_SUCCESS);
526+
umfOsMemoryProviderParamsDestroy(os_memory_provider_params);
527+
528+
umf_disjoint_pool_params_handle_t disjoint_pool_params = NULL;
529+
ret = umfDisjointPoolParamsCreate(&disjoint_pool_params);
530+
ASSERT_EQ(ret, UMF_RESULT_SUCCESS);
531+
532+
const char *pool_name = "test_disjoint_pool";
533+
ret = umfDisjointPoolParamsSetName(disjoint_pool_params, pool_name);
534+
ASSERT_EQ(ret, UMF_RESULT_SUCCESS);
535+
536+
umf_memory_pool_handle_t hPool = NULL;
537+
ret = umfPoolCreate(umfDisjointPoolOps(), hProvider, disjoint_pool_params,
538+
0, &hPool);
539+
540+
ASSERT_EQ(ret, UMF_RESULT_SUCCESS);
541+
542+
umf_memory_pool_handle_t hPool2 = NULL;
543+
ret = umfPoolCreate(umfDisjointPoolOps(), hProvider, disjoint_pool_params,
544+
0, &hPool2);
545+
umfDisjointPoolParamsDestroy(disjoint_pool_params);
546+
547+
// If there is more than one pool with the same name,
548+
// CtlGet by_nume will return an error
549+
size_t alloc_count;
550+
ret = umfCtlGet("umf.pool.by_name.{}.stats.alloc_count", &alloc_count,
551+
sizeof(alloc_count), pool_name);
552+
553+
ASSERT_EQ(ret, UMF_RESULT_ERROR_INVALID_ARGUMENT);
554+
555+
// ctl set and exec will still work. But there is no CTL entry for now to test it
556+
// todo: add test when ctl entries will be extended
557+
558+
ret = umfPoolDestroy(hPool);
559+
ASSERT_EQ(ret, UMF_RESULT_SUCCESS);
560+
ret = umfPoolDestroy(hPool2);
561+
ASSERT_EQ(ret, UMF_RESULT_SUCCESS);
562+
ret = umfMemoryProviderDestroy(hProvider);
563+
ASSERT_EQ(ret, UMF_RESULT_SUCCESS);
564+
}

0 commit comments

Comments
 (0)