@@ -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+
181296INSTANTIATE_TEST_SUITE_P (
182297 mallocPoolTest, umfPoolTest,
183298 ::testing::Values (poolCreateExtParams{&MALLOC_POOL_OPS, nullptr ,
0 commit comments