Skip to content

Commit c8a2d34

Browse files
committed
[umf] replace memory tracker with a critnib-based impl
This implementation is lock-free and does not require C++ runtime.
1 parent 4c7cb30 commit c8a2d34

File tree

6 files changed

+115
-88
lines changed

6 files changed

+115
-88
lines changed

source/common/unified_malloc_framework/CMakeLists.txt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,15 @@
66
set(UMF_SOURCES
77
src/memory_pool.c
88
src/memory_provider.c
9-
src/memory_tracker.cpp
9+
src/memory_tracker.c
1010
src/memory_provider_get_last_failed.cpp
11+
src/critnib/critnib.c
1112
)
1213

14+
if (MSVC)
15+
set(UMF_SOURCES ${UMF_SOURCES} src/memory_tracker_windows.cpp)
16+
endif()
17+
1318
if(UMF_BUILD_SHARED_LIBRARY)
1419
message(WARNING "Unified Malloc Framework is still an early work in progress."
1520
"There are no API/ABI backward compatibility guarantees. There will be breakages."

source/common/unified_malloc_framework/src/memory_tracker.cpp renamed to source/common/unified_malloc_framework/src/memory_tracker.c

Lines changed: 69 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -9,112 +9,97 @@
99
*/
1010

1111
#include "memory_tracker.h"
12+
#include "critnib/critnib.h"
13+
14+
#include <umf/memory_pool.h>
1215
#include <umf/memory_provider.h>
1316
#include <umf/memory_provider_ops.h>
1417

15-
#include <cassert>
16-
#include <map>
17-
#include <mutex>
18-
#include <shared_mutex>
18+
#include <assert.h>
19+
#include <errno.h>
1920
#include <stdlib.h>
2021

21-
#ifdef _WIN32
22-
#include <windows.h>
23-
#endif
24-
25-
// TODO: reimplement in C and optimize...
26-
struct umf_memory_tracker_t {
27-
enum umf_result_t add(void *pool, const void *ptr, size_t size) {
28-
std::unique_lock<std::shared_mutex> lock(mtx);
22+
#if !defined(_WIN32)
23+
critnib *TRACKER = NULL;
24+
void __attribute__((constructor)) createLibTracker() {
25+
TRACKER = critnib_new();
26+
}
27+
void __attribute__((destructor)) deleteLibTracker() { critnib_delete(TRACKER); }
2928

30-
if (size == 0) {
31-
return UMF_RESULT_SUCCESS;
32-
}
29+
umf_memory_tracker_handle_t umfMemoryTrackerGet(void) {
30+
return (umf_memory_tracker_handle_t)TRACKER;
31+
}
32+
#endif
3333

34-
auto ret =
35-
map.try_emplace(reinterpret_cast<uintptr_t>(ptr), size, pool);
36-
return ret.second ? UMF_RESULT_SUCCESS : UMF_RESULT_ERROR_UNKNOWN;
37-
}
34+
struct tracker_value_t {
35+
umf_memory_pool_handle_t pool;
36+
size_t size;
37+
};
3838

39-
enum umf_result_t remove(const void *ptr, size_t size) {
40-
std::unique_lock<std::shared_mutex> lock(mtx);
39+
static enum umf_result_t
40+
umfMemoryTrackerAdd(umf_memory_tracker_handle_t hTracker,
41+
umf_memory_pool_handle_t pool, const void *ptr,
42+
size_t size) {
43+
assert(ptr);
4144

42-
map.erase(reinterpret_cast<uintptr_t>(ptr));
45+
struct tracker_value_t *value =
46+
(struct tracker_value_t *)malloc(sizeof(struct tracker_value_t));
47+
value->pool = pool;
48+
value->size = size;
4349

44-
// TODO: handle removing part of the range
45-
(void)size;
50+
int ret = critnib_insert((critnib *)hTracker, (uintptr_t)ptr, value, 0);
4651

52+
if (ret == 0) {
4753
return UMF_RESULT_SUCCESS;
4854
}
4955

50-
void *find(const void *ptr) {
51-
std::shared_lock<std::shared_mutex> lock(mtx);
52-
53-
auto intptr = reinterpret_cast<uintptr_t>(ptr);
54-
auto it = map.upper_bound(intptr);
55-
if (it == map.begin()) {
56-
return nullptr;
57-
}
58-
59-
--it;
60-
61-
auto address = it->first;
62-
auto size = it->second.first;
63-
auto pool = it->second.second;
64-
65-
if (intptr >= address && intptr < address + size) {
66-
return pool;
67-
}
56+
free(value);
6857

69-
return nullptr;
58+
if (ret == ENOMEM) {
59+
return UMF_RESULT_ERROR_OUT_OF_HOST_MEMORY;
7060
}
7161

72-
private:
73-
std::shared_mutex mtx;
74-
std::map<uintptr_t, std::pair<size_t, void *>> map;
75-
};
76-
77-
static enum umf_result_t
78-
umfMemoryTrackerAdd(umf_memory_tracker_handle_t hTracker, void *pool,
79-
const void *ptr, size_t size) {
80-
return hTracker->add(pool, ptr, size);
62+
// This should not happen
63+
// TODO: add logging here
64+
return UMF_RESULT_ERROR_UNKNOWN;
8165
}
8266

8367
static enum umf_result_t
8468
umfMemoryTrackerRemove(umf_memory_tracker_handle_t hTracker, const void *ptr,
8569
size_t size) {
86-
return hTracker->remove(ptr, size);
87-
}
70+
assert(ptr);
71+
72+
// TODO: there is no support for removing partial ranges (or multipe entires
73+
// in a single remove call) yet.
74+
// Every umfMemoryTrackerAdd(..., ptr, ...) should have a corresponsding
75+
// umfMemoryTrackerRemove call with the same ptr value.
76+
(void)size;
77+
78+
void *value = critnib_remove((critnib *)hTracker, (uintptr_t)ptr);
79+
if (!value) {
80+
// This should not happen
81+
// TODO: add logging here
82+
return UMF_RESULT_ERROR_UNKNOWN;
83+
}
8884

89-
extern "C" {
85+
free(value);
9086

91-
#if defined(_WIN32) && defined(UMF_SHARED_LIBRARY)
92-
umf_memory_tracker_t *tracker = nullptr;
93-
BOOL APIENTRY DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
94-
if (fdwReason == DLL_PROCESS_DETACH) {
95-
delete tracker;
96-
} else if (fdwReason == DLL_PROCESS_ATTACH) {
97-
tracker = new umf_memory_tracker_t;
98-
}
99-
return TRUE;
100-
}
101-
#elif defined(_WIN32)
102-
umf_memory_tracker_t trackerInstance;
103-
umf_memory_tracker_t *tracker = &trackerInstance;
104-
#else
105-
umf_memory_tracker_t *tracker = nullptr;
106-
void __attribute__((constructor)) createLibTracker() {
107-
tracker = new umf_memory_tracker_t;
87+
return UMF_RESULT_SUCCESS;
10888
}
10989

110-
void __attribute__((destructor)) deleteLibTracker() { delete tracker; }
111-
#endif
90+
umf_memory_pool_handle_t
91+
umfMemoryTrackerGetPool(umf_memory_tracker_handle_t hTracker, const void *ptr) {
92+
assert(ptr);
11293

113-
umf_memory_tracker_handle_t umfMemoryTrackerGet(void) { return tracker; }
94+
uintptr_t rkey;
95+
struct tracker_value_t *rvalue;
96+
int found = critnib_find((critnib *)hTracker, (uintptr_t)ptr, FIND_LE,
97+
(void *)&rkey, (void **)&rvalue);
98+
if (!found) {
99+
return NULL;
100+
}
114101

115-
void *umfMemoryTrackerGetPool(umf_memory_tracker_handle_t hTracker,
116-
const void *ptr) {
117-
return hTracker->find(ptr);
102+
return (rkey + rvalue->size >= (uintptr_t)ptr) ? rvalue->pool : NULL;
118103
}
119104

120105
struct umf_tracking_memory_provider_t {
@@ -136,7 +121,7 @@ static enum umf_result_t trackingAlloc(void *hProvider, size_t size,
136121
}
137122

138123
ret = umfMemoryProviderAlloc(p->hUpstream, size, alignment, ptr);
139-
if (ret != UMF_RESULT_SUCCESS) {
124+
if (ret != UMF_RESULT_SUCCESS || !*ptr) {
140125
return ret;
141126
}
142127

@@ -159,9 +144,11 @@ static enum umf_result_t trackingFree(void *hProvider, void *ptr, size_t size) {
159144
// to avoid a race condition. If the order would be different, other thread
160145
// could allocate the memory at address `ptr` before a call to umfMemoryTrackerRemove
161146
// resulting in inconsistent state.
162-
ret = umfMemoryTrackerRemove(p->hTracker, ptr, size);
163-
if (ret != UMF_RESULT_SUCCESS) {
164-
return ret;
147+
if (ptr) {
148+
ret = umfMemoryTrackerRemove(p->hTracker, ptr, size);
149+
if (ret != UMF_RESULT_SUCCESS) {
150+
return ret;
151+
}
165152
}
166153

167154
ret = umfMemoryProviderFree(p->hUpstream, ptr, size);
@@ -267,4 +254,3 @@ void umfTrackingMemoryProviderGetUpstreamProvider(
267254
(umf_tracking_memory_provider_t *)hTrackingProvider;
268255
*hUpstream = p->hUpstream;
269256
}
270-
}

source/common/unified_malloc_framework/src/memory_tracker.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ extern "C" {
2222
typedef struct umf_memory_tracker_t *umf_memory_tracker_handle_t;
2323

2424
umf_memory_tracker_handle_t umfMemoryTrackerGet(void);
25-
void *umfMemoryTrackerGetPool(umf_memory_tracker_handle_t hTracker,
26-
const void *ptr);
25+
umf_memory_pool_handle_t
26+
umfMemoryTrackerGetPool(umf_memory_tracker_handle_t hTracker, const void *ptr);
2727

2828
// Creates a memory provider that tracks each allocation/deallocation through umf_memory_tracker_handle_t and
2929
// forwards all requests to hUpstream memory Provider. hUpstream lifetime should be managed by the user of this function.
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
*
3+
* Copyright (C) 2023 Intel Corporation
4+
*
5+
* Part of the Unified-Runtime Project, under the Apache License v2.0 with LLVM Exceptions.
6+
* See LICENSE.TXT
7+
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
8+
*
9+
*/
10+
11+
#include <windows.h>
12+
13+
#if defined(UMF_SHARED_LIBRARY)
14+
critnib *TRACKER = NULL;
15+
BOOL APIENTRY DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
16+
if (fdwReason == DLL_PROCESS_DETACH) {
17+
critnib_delete(TRACKER);
18+
} else if (fdwReason == DLL_PROCESS_ATTACH) {
19+
TRACKER = critnib_new();
20+
}
21+
return TRUE;
22+
}
23+
#else
24+
struct tracker_t {
25+
tracker_t() { map = critnib_new(); }
26+
~tracker_t() { critnib_remove(map); }
27+
critnib *map;
28+
};
29+
tracker_t TRACKER_INSTANCE;
30+
critnib *TRACKER = TRACKER_INSTANCE.map;
31+
#endif
32+
33+
umf_memory_tracker_handle_t umfMemoryTrackerGet(void) {
34+
return (umf_memory_tracker_handle_t)TRACKER;
35+
}

test/unified_malloc_framework/common/provider.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ static enum umf_result_t nullAlloc(void *provider, size_t size,
2323
(void)provider;
2424
(void)size;
2525
(void)alignment;
26-
(void)ptr;
26+
*ptr = NULL;
2727
return UMF_RESULT_SUCCESS;
2828
}
2929

test/unified_malloc_framework/memoryProviderAPI.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ TEST_F(test, memoryProviderTrace) {
2323

2424
size_t call_count = 0;
2525

26-
auto ret = umfMemoryProviderAlloc(tracingProvider.get(), 0, 0, nullptr);
26+
void *ptr;
27+
auto ret = umfMemoryProviderAlloc(tracingProvider.get(), 0, 0, &ptr);
2728
ASSERT_EQ(ret, UMF_RESULT_SUCCESS);
2829
ASSERT_EQ(calls["alloc"], 1);
2930
ASSERT_EQ(calls.size(), ++call_count);

0 commit comments

Comments
 (0)