Skip to content

Commit 9bf1316

Browse files
Merge pull request #962 from igchor/poolTags
Implement umfPool[Set/Get]Tag
2 parents 109f4b2 + 86d2341 commit 9bf1316

File tree

6 files changed

+171
-0
lines changed

6 files changed

+171
-0
lines changed

include/umf/memory_pool.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,22 @@ umf_memory_pool_handle_t umfPoolByPtr(const void *ptr);
170170
umf_result_t umfPoolGetMemoryProvider(umf_memory_pool_handle_t hPool,
171171
umf_memory_provider_handle_t *hProvider);
172172

173+
///
174+
/// @brief Set a custom tag on the memory pool that can be later retrieved using umfPoolGetTag.
175+
/// @param hPool specified memory pool
176+
/// @param tag tag to be set
177+
/// @param oldTag [out][optional] previous tag set on the memory pool
178+
/// @return UMF_RESULT_SUCCESS on success or appropriate error code on failure.
179+
umf_result_t umfPoolSetTag(umf_memory_pool_handle_t hPool, void *tag,
180+
void **oldTag);
181+
182+
///
183+
/// @brief Retrieve the tag associated with the memory pool or NULL if no tag is set.
184+
/// @param hPool specified memory pool
185+
/// @param tag [out] tag associated with the memory pool
186+
/// @return UMF_RESULT_SUCCESS on success.
187+
umf_result_t umfPoolGetTag(umf_memory_pool_handle_t hPool, void **tag);
188+
173189
#ifdef __cplusplus
174190
}
175191
#endif

src/libumf.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,10 +103,12 @@ EXPORTS
103103
umfPoolFree
104104
umfPoolGetIPCHandleSize
105105
umfPoolGetLastAllocationError
106+
umfPoolGetTag
106107
umfPoolGetMemoryProvider
107108
umfPoolMalloc
108109
umfPoolMallocUsableSize
109110
umfPoolRealloc
111+
umfPoolSetTag
110112
umfProxyPoolOps
111113
umfPutIPCHandle
112114
umfScalablePoolOps

src/libumf.map

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,11 @@ UMF_1.0 {
9898
umfPoolGetIPCHandleSize;
9999
umfPoolGetLastAllocationError;
100100
umfPoolGetMemoryProvider;
101+
umfPoolGetTag;
101102
umfPoolMalloc;
102103
umfPoolMallocUsableSize;
103104
umfPoolRealloc;
105+
umfPoolSetTag;
104106
umfProxyPoolOps;
105107
umfPutIPCHandle;
106108
umfScalablePoolOps;

src/memory_pool.c

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,13 @@ static umf_result_t umfPoolCreateInternal(const umf_memory_pool_ops_t *ops,
5252

5353
pool->flags = flags;
5454
pool->ops = *ops;
55+
pool->tag = NULL;
56+
57+
if (NULL == utils_mutex_init(&pool->lock)) {
58+
LOG_ERR("Failed to initialize mutex for pool");
59+
ret = UMF_RESULT_ERROR_UNKNOWN;
60+
goto err_lock_init;
61+
}
5562

5663
ret = ops->initialize(pool->provider, params, &pool->pool_priv);
5764
if (ret != UMF_RESULT_SUCCESS) {
@@ -63,6 +70,8 @@ static umf_result_t umfPoolCreateInternal(const umf_memory_pool_ops_t *ops,
6370
return UMF_RESULT_SUCCESS;
6471

6572
err_pool_init:
73+
utils_mutex_destroy_not_free(&pool->lock);
74+
err_lock_init:
6675
if (!(flags & UMF_POOL_CREATE_FLAG_DISABLE_TRACKING)) {
6776
umfMemoryProviderDestroy(pool->provider);
6877
}
@@ -87,6 +96,8 @@ void umfPoolDestroy(umf_memory_pool_handle_t hPool) {
8796
umfMemoryProviderDestroy(hUpstreamProvider);
8897
}
8998

99+
utils_mutex_destroy_not_free(&hPool->lock);
100+
90101
LOG_INFO("Memory pool destroyed: %p", (void *)hPool);
91102

92103
// TODO: this free keeps memory in base allocator, so it can lead to OOM in some scenarios (it should be optimized)
@@ -172,3 +183,24 @@ umf_result_t umfPoolGetLastAllocationError(umf_memory_pool_handle_t hPool) {
172183
UMF_CHECK((hPool != NULL), UMF_RESULT_ERROR_INVALID_ARGUMENT);
173184
return hPool->ops.get_last_allocation_error(hPool->pool_priv);
174185
}
186+
187+
umf_result_t umfPoolSetTag(umf_memory_pool_handle_t hPool, void *tag,
188+
void **oldTag) {
189+
UMF_CHECK((hPool != NULL), UMF_RESULT_ERROR_INVALID_ARGUMENT);
190+
utils_mutex_lock(&hPool->lock);
191+
if (oldTag) {
192+
*oldTag = hPool->tag;
193+
}
194+
hPool->tag = tag;
195+
utils_mutex_unlock(&hPool->lock);
196+
return UMF_RESULT_SUCCESS;
197+
}
198+
199+
umf_result_t umfPoolGetTag(umf_memory_pool_handle_t hPool, void **tag) {
200+
UMF_CHECK((hPool != NULL), UMF_RESULT_ERROR_INVALID_ARGUMENT);
201+
UMF_CHECK((tag != NULL), UMF_RESULT_ERROR_INVALID_ARGUMENT);
202+
utils_mutex_lock(&hPool->lock);
203+
*tag = hPool->tag;
204+
utils_mutex_unlock(&hPool->lock);
205+
return UMF_RESULT_SUCCESS;
206+
}

src/memory_pool_internal.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ extern "C" {
2222
#endif
2323

2424
#include "base_alloc.h"
25+
#include "utils_concurrency.h"
2526

2627
typedef struct umf_memory_pool_t {
2728
void *pool_priv;
@@ -30,6 +31,9 @@ typedef struct umf_memory_pool_t {
3031

3132
// Memory provider used by the pool.
3233
umf_memory_provider_handle_t provider;
34+
35+
utils_mutex_t lock;
36+
void *tag;
3337
} umf_memory_pool_t;
3438

3539
#ifdef __cplusplus

test/memoryPoolAPI.cpp

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,121 @@ TEST_F(test, BasicPoolByPtrTest) {
178178
ASSERT_EQ(ret, UMF_RESULT_SUCCESS);
179179
}
180180

181+
struct tagTest : umf_test::test {
182+
void SetUp() override {
183+
test::SetUp();
184+
provider = umf_test::wrapProviderUnique(nullProviderCreate());
185+
pool = umf_test::wrapPoolUnique(
186+
createPoolChecked(umfProxyPoolOps(), provider.get(), nullptr));
187+
}
188+
189+
umf::provider_unique_handle_t provider;
190+
umf::pool_unique_handle_t pool;
191+
};
192+
193+
TEST_F(tagTest, SetAndGet) {
194+
umf_result_t ret = umfPoolSetTag(pool.get(), (void *)0x99, nullptr);
195+
ASSERT_EQ(ret, UMF_RESULT_SUCCESS);
196+
197+
void *tag;
198+
ret = umfPoolGetTag(pool.get(), &tag);
199+
ASSERT_EQ(ret, UMF_RESULT_SUCCESS);
200+
ASSERT_EQ(tag, (void *)0x99);
201+
202+
void *oldTag;
203+
ret = umfPoolSetTag(pool.get(), (void *)0x100, &oldTag);
204+
ASSERT_EQ(ret, UMF_RESULT_SUCCESS);
205+
ASSERT_EQ(oldTag, (void *)0x99);
206+
207+
ret = umfPoolGetTag(pool.get(), &tag);
208+
ASSERT_EQ(ret, UMF_RESULT_SUCCESS);
209+
ASSERT_EQ(tag, (void *)0x100);
210+
}
211+
212+
TEST_F(tagTest, SetAndGetNull) {
213+
umf_result_t ret = umfPoolSetTag(pool.get(), nullptr, nullptr);
214+
ASSERT_EQ(ret, UMF_RESULT_SUCCESS);
215+
216+
void *tag;
217+
ret = umfPoolGetTag(pool.get(), &tag);
218+
ASSERT_EQ(ret, UMF_RESULT_SUCCESS);
219+
ASSERT_EQ(tag, nullptr);
220+
}
221+
222+
TEST_F(tagTest, NoSetAndGet) {
223+
void *tag;
224+
umf_result_t ret = umfPoolGetTag(pool.get(), &tag);
225+
ASSERT_EQ(ret, UMF_RESULT_SUCCESS);
226+
ASSERT_EQ(tag, nullptr);
227+
}
228+
229+
TEST_F(tagTest, SetAndGetMt) {
230+
static constexpr size_t NUM_THREADS = 8;
231+
static constexpr size_t NUM_OPS_PER_THREAD = 16;
232+
233+
std::vector<std::thread> threads;
234+
235+
auto encodeTag = [](size_t thread, size_t op) -> void * {
236+
return reinterpret_cast<void *>(thread * NUM_OPS_PER_THREAD + op);
237+
};
238+
239+
auto decodeTag = [](void *tag) -> std::pair<size_t, size_t> {
240+
auto op = reinterpret_cast<size_t>(tag) & (NUM_OPS_PER_THREAD - 1);
241+
auto thread = reinterpret_cast<size_t>(tag) / NUM_OPS_PER_THREAD;
242+
return {thread, op};
243+
};
244+
245+
for (size_t i = 0; i < NUM_THREADS; i++) {
246+
threads.emplace_back([this, i, encodeTag, decodeTag] {
247+
for (size_t j = 0; j < NUM_OPS_PER_THREAD; j++) {
248+
void *oldTag;
249+
umf_result_t ret =
250+
umfPoolSetTag(pool.get(), encodeTag(i, j), &oldTag);
251+
ASSERT_EQ(ret, UMF_RESULT_SUCCESS);
252+
253+
void *queriedTag;
254+
ret = umfPoolGetTag(pool.get(), &queriedTag);
255+
ASSERT_EQ(ret, UMF_RESULT_SUCCESS);
256+
257+
auto [t1, op1] = decodeTag(oldTag);
258+
auto [t2, op2] = decodeTag(queriedTag);
259+
// if the tag was set by the same thread, the op part should be the same or higher
260+
ASSERT_TRUE(t1 != t2 || op2 >= op1);
261+
}
262+
});
263+
}
264+
265+
for (auto &thread : threads) {
266+
thread.join();
267+
}
268+
269+
void *tag;
270+
auto ret = umfPoolGetTag(pool.get(), &tag);
271+
ASSERT_EQ(ret, UMF_RESULT_SUCCESS);
272+
273+
auto [t, op] = decodeTag(tag);
274+
ASSERT_TRUE(t < NUM_THREADS);
275+
ASSERT_TRUE(op == NUM_OPS_PER_THREAD - 1);
276+
}
277+
278+
TEST_F(tagTest, SetAndGetInvalidPtr) {
279+
umf_result_t ret = umfPoolSetTag(pool.get(), nullptr, nullptr);
280+
ASSERT_EQ(ret, UMF_RESULT_SUCCESS);
281+
282+
ret = umfPoolGetTag(pool.get(), nullptr);
283+
ASSERT_EQ(ret, UMF_RESULT_ERROR_INVALID_ARGUMENT);
284+
}
285+
286+
TEST_F(tagTest, SetAndGetInvalidPool) {
287+
umf_result_t ret =
288+
umfPoolSetTag(nullptr, reinterpret_cast<void *>(0x1), nullptr);
289+
ASSERT_EQ(ret, UMF_RESULT_ERROR_INVALID_ARGUMENT);
290+
291+
void *tag;
292+
ret = umfPoolGetTag(nullptr, &tag);
293+
ASSERT_EQ(ret, UMF_RESULT_ERROR_INVALID_ARGUMENT);
294+
}
295+
181296
INSTANTIATE_TEST_SUITE_P(
182297
mallocPoolTest, umfPoolTest,
183298
::testing::Values(poolCreateExtParams{&MALLOC_POOL_OPS, nullptr,

0 commit comments

Comments
 (0)