Skip to content

Commit e9b8495

Browse files
committed
Implement umfMemoryProviderAllocation[Split/Merge]
That is needed to support jemalloc (avoid leaking memory).
1 parent 74fe7d0 commit e9b8495

10 files changed

+284
-30
lines changed

include/umf/memory_provider.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,13 @@ umfMemoryProviderGetName(umf_memory_provider_handle_t hProvider);
156156
///
157157
UMF_EXPORT umf_memory_provider_handle_t umfGetLastFailedMemoryProvider(void);
158158

159+
UMF_EXPORT umf_result_t
160+
umfMemoryProviderAllocationSplit(umf_memory_provider_handle_t hProvider,
161+
void *ptr, size_t totalSize, size_t leftSize);
162+
UMF_EXPORT umf_result_t
163+
umfMemoryProviderAllocationMerge(umf_memory_provider_handle_t hProvider,
164+
void *lowPtr, void *highPtr, size_t totalSize);
165+
159166
#ifdef __cplusplus
160167
}
161168
#endif

include/umf/memory_provider_ops.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,11 @@ typedef struct umf_memory_provider_ops_t {
139139
/// @return pointer to a string containing the name of the \p provider
140140
///
141141
const char *(*get_name)(void *provider);
142+
143+
umf_result_t (*allocation_split)(void *provider, void *ptr,
144+
size_t totalSize, size_t leftSize);
145+
umf_result_t (*allocation_merge)(void *provider, void *lowPtr,
146+
void *highPtr, size_t totalSize);
142147
} umf_memory_provider_ops_t;
143148

144149
#ifdef __cplusplus

src/memory_provider.c

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <umf/memory_provider.h>
1212

1313
#include <assert.h>
14+
#include <stdio.h>
1415
#include <stdlib.h>
1516

1617
typedef struct umf_memory_provider_t {
@@ -129,3 +130,46 @@ const char *umfMemoryProviderGetName(umf_memory_provider_handle_t hProvider) {
129130
umf_memory_provider_handle_t umfGetLastFailedMemoryProvider(void) {
130131
return *umfGetLastFailedMemoryProviderPtr();
131132
}
133+
134+
umf_result_t
135+
umfMemoryProviderAllocationSplit(umf_memory_provider_handle_t hProvider,
136+
void *ptr, size_t totalSize, size_t leftSize) {
137+
if (leftSize >= totalSize) {
138+
fprintf(stderr,
139+
"umfMemoryProviderAllocationSplit failed. check failed: %zu >= "
140+
"%zu\n",
141+
leftSize, totalSize);
142+
return UMF_RESULT_ERROR_INVALID_ARGUMENT;
143+
}
144+
145+
umf_result_t res = hProvider->ops.allocation_split(
146+
hProvider->provider_priv, ptr, totalSize, leftSize);
147+
checkErrorAndSetLastProvider(res, hProvider);
148+
return res;
149+
}
150+
151+
umf_result_t
152+
umfMemoryProviderAllocationMerge(umf_memory_provider_handle_t hProvider,
153+
void *lowPtr, void *highPtr,
154+
size_t totalSize) {
155+
if ((uintptr_t)lowPtr >= (uintptr_t)highPtr) {
156+
fprintf(
157+
stderr,
158+
"umfMemoryProviderAllocationMerge failed. check failed: %p >= %p\n",
159+
lowPtr, highPtr);
160+
return UMF_RESULT_ERROR_INVALID_ARGUMENT;
161+
}
162+
163+
if ((uintptr_t)highPtr - (uintptr_t)lowPtr > totalSize) {
164+
fprintf(stderr,
165+
"umfMemoryProviderAllocationMerge failed. check failed: %p - "
166+
"%p > %zu\n",
167+
highPtr, lowPtr, totalSize);
168+
return UMF_RESULT_ERROR_INVALID_ARGUMENT;
169+
}
170+
171+
umf_result_t res = hProvider->ops.allocation_merge(
172+
hProvider->provider_priv, lowPtr, highPtr, totalSize);
173+
checkErrorAndSetLastProvider(res, hProvider);
174+
return res;
175+
}

src/pool/pool_jemalloc.c

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -210,15 +210,14 @@ static bool arena_extent_split(extent_hooks_t *extent_hooks, void *addr,
210210
size_t size, size_t size_a, size_t size_b,
211211
bool committed, unsigned arena_ind) {
212212
(void)extent_hooks; // unused
213-
(void)addr; // unused
214-
(void)size; // unused
215-
(void)size_a; // unused
216-
(void)size_b; // unused
217213
(void)committed; // unused
218-
(void)arena_ind; // unused
214+
(void)size_b;
219215

220-
// TODO: add this function to the provider API to support Windows and USM
221-
return true; // true means failure (unsupported)
216+
assert(size_a + size_b == size);
217+
218+
jemalloc_memory_pool_t *pool = get_pool_by_arena_index(arena_ind);
219+
return umfMemoryProviderAllocationSplit(pool->provider, addr, size,
220+
size_a) != UMF_RESULT_SUCCESS;
222221
}
223222

224223
// arena_extent_merge - an extent merge function conforms to the extent_merge_t type and optionally
@@ -231,15 +230,12 @@ static bool arena_extent_merge(extent_hooks_t *extent_hooks, void *addr_a,
231230
size_t size_a, void *addr_b, size_t size_b,
232231
bool committed, unsigned arena_ind) {
233232
(void)extent_hooks; // unused
234-
(void)addr_a; // unused
235-
(void)addr_b; // unused
236-
(void)size_a; // unused
237-
(void)size_b; // unused
238233
(void)committed; // unused
239-
(void)arena_ind; // unused
240234

241-
// TODO: add this function to the provider API to support Windows and USM
242-
return true; // true means failure (unsupported)
235+
jemalloc_memory_pool_t *pool = get_pool_by_arena_index(arena_ind);
236+
return umfMemoryProviderAllocationMerge(pool->provider, addr_a, addr_b,
237+
size_a + size_b) !=
238+
UMF_RESULT_SUCCESS;
243239
}
244240

245241
// The extent_hooks_t structure comprises function pointers which are described individually below.

src/pool/pool_scalable.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ static void tbb_raw_free_wrapper(intptr_t pool_id, void *ptr, size_t bytes) {
121121
TLS_last_free_error = ret;
122122
fprintf(
123123
stderr,
124-
"Memory provider failed to free memory, addr = %p, size = %lu\n",
124+
"Memory provider failed to free memory, addr = %p, size = %zu\n",
125125
ptr, bytes);
126126
}
127127
}

src/provider/provider_os_memory.c

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,26 @@ static const char *os_get_name(void *provider) {
449449
return "OS";
450450
}
451451

452+
static umf_result_t os_allocation_split(void *provider, void *ptr,
453+
size_t totalSize, size_t leftSize) {
454+
(void)provider;
455+
(void)ptr;
456+
(void)totalSize;
457+
(void)leftSize;
458+
// nop
459+
return UMF_RESULT_SUCCESS;
460+
}
461+
462+
static umf_result_t os_allocation_merge(void *provider, void *lowPtr,
463+
void *highPtr, size_t totalSize) {
464+
(void)provider;
465+
(void)lowPtr;
466+
(void)highPtr;
467+
(void)totalSize;
468+
// nop
469+
return UMF_RESULT_SUCCESS;
470+
}
471+
452472
umf_memory_provider_ops_t UMF_OS_MEMORY_PROVIDER_OPS = {
453473
.version = UMF_VERSION_CURRENT,
454474
.initialize = os_initialize,
@@ -461,4 +481,5 @@ umf_memory_provider_ops_t UMF_OS_MEMORY_PROVIDER_OPS = {
461481
.purge_lazy = os_purge_lazy,
462482
.purge_force = os_purge_force,
463483
.get_name = os_get_name,
464-
};
484+
.allocation_split = os_allocation_split,
485+
.allocation_merge = os_allocation_merge};

src/provider/provider_tracking.c

Lines changed: 159 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,15 @@
99

1010
#include "provider_tracking.h"
1111
#include "critnib.h"
12+
#include "utils_concurrency.h"
1213

1314
#include <umf/memory_pool.h>
1415
#include <umf/memory_provider.h>
1516
#include <umf/memory_provider_ops.h>
1617

1718
#include <assert.h>
1819
#include <errno.h>
20+
#include <stdio.h>
1921
#include <stdlib.h>
2022

2123
typedef struct tracker_value_t {
@@ -32,7 +34,7 @@ static umf_result_t umfMemoryTrackerAdd(umf_memory_tracker_handle_t hTracker,
3234
value->pool = pool;
3335
value->size = size;
3436

35-
int ret = critnib_insert((critnib *)hTracker, (uintptr_t)ptr, value, 0);
37+
int ret = critnib_insert(hTracker->map, (uintptr_t)ptr, value, 0);
3638

3739
if (ret == 0) {
3840
return UMF_RESULT_SUCCESS;
@@ -59,7 +61,7 @@ static umf_result_t umfMemoryTrackerRemove(umf_memory_tracker_handle_t hTracker,
5961
// umfMemoryTrackerRemove call with the same ptr value.
6062
(void)size;
6163

62-
void *value = critnib_remove((critnib *)hTracker, (uintptr_t)ptr);
64+
void *value = critnib_remove(hTracker->map, (uintptr_t)ptr);
6365
if (!value) {
6466
// This should not happen
6567
// TODO: add logging here
@@ -77,7 +79,7 @@ umfMemoryTrackerGetPool(umf_memory_tracker_handle_t hTracker, const void *ptr) {
7779

7880
uintptr_t rkey;
7981
tracker_value_t *rvalue;
80-
int found = critnib_find((critnib *)hTracker, (uintptr_t)ptr, FIND_LE,
82+
int found = critnib_find(hTracker->map, (uintptr_t)ptr, FIND_LE,
8183
(void *)&rkey, (void **)&rvalue);
8284
if (!found) {
8385
return NULL;
@@ -119,6 +121,158 @@ static umf_result_t trackingAlloc(void *hProvider, size_t size,
119121
return ret;
120122
}
121123

124+
static umf_result_t trackingAllocationSplit(void *provider, void *ptr,
125+
size_t totalSize, size_t leftSize) {
126+
umf_result_t ret = UMF_RESULT_ERROR_UNKNOWN;
127+
umf_tracking_memory_provider_t *p =
128+
(umf_tracking_memory_provider_t *)provider;
129+
130+
tracker_value_t *splitValue =
131+
(tracker_value_t *)malloc(sizeof(tracker_value_t));
132+
splitValue->pool = p->pool;
133+
splitValue->size = leftSize;
134+
135+
int r = util_mutex_lock(p->hTracker->splitMergeMutex);
136+
if (r) {
137+
goto err_lock;
138+
}
139+
140+
tracker_value_t *value =
141+
(tracker_value_t *)critnib_get(p->hTracker->map, (uintptr_t)ptr);
142+
if (!value) {
143+
fprintf(stderr, "tracking split: no such value\n");
144+
ret = UMF_RESULT_ERROR_INVALID_ARGUMENT;
145+
goto err;
146+
}
147+
if (value->size != totalSize) {
148+
fprintf(stderr, "tracking split: %zu != %zu\n", value->size, totalSize);
149+
ret = UMF_RESULT_ERROR_INVALID_ARGUMENT;
150+
goto err;
151+
}
152+
153+
ret = umfMemoryProviderAllocationSplit(p->hUpstream, ptr, totalSize,
154+
leftSize);
155+
if (ret != UMF_RESULT_SUCCESS) {
156+
fprintf(stderr,
157+
"tracking split: umfMemoryProviderAllocationSplit failed\n");
158+
goto err;
159+
}
160+
161+
void *highPtr = (void *)(((uintptr_t)ptr) + leftSize);
162+
size_t rightSize = totalSize - leftSize;
163+
164+
// We'll have duplicate entry for the range [highPtr, rightValue->size] but this is fine,
165+
// the value is the same anyway and we forbid splitting/removing that range concurrently
166+
ret = umfMemoryTrackerAdd(p->hTracker, p->pool, highPtr, rightSize);
167+
if (ret != UMF_RESULT_SUCCESS) {
168+
fprintf(stderr, "tracking split: umfMemoryTrackerAdd failed\n");
169+
// TODO: what now? should we rollback the split? This is can only happen due to ENOMEM
170+
// so it's unlikely but probably the best solution would be to try to preallocate everything
171+
// (value and critnib nodes) before calling umfMemoryProviderAllocationSplit.
172+
goto err;
173+
}
174+
175+
int cret = critnib_insert(p->hTracker->map, (uintptr_t)ptr,
176+
(void *)splitValue, 1 /* update */);
177+
// this cannot fail since we know the element exists (nothing to allocate)
178+
assert(cret == 0);
179+
(void)cret;
180+
181+
// free the original value
182+
free(value);
183+
util_mutex_unlock(p->hTracker->splitMergeMutex);
184+
185+
return UMF_RESULT_SUCCESS;
186+
187+
err:
188+
util_mutex_unlock(p->hTracker->splitMergeMutex);
189+
err_lock:
190+
free(splitValue);
191+
return ret;
192+
}
193+
194+
static umf_result_t trackingAllocationMerge(void *provider, void *lowPtr,
195+
void *highPtr, size_t totalSize) {
196+
umf_result_t ret = UMF_RESULT_ERROR_UNKNOWN;
197+
umf_tracking_memory_provider_t *p =
198+
(umf_tracking_memory_provider_t *)provider;
199+
200+
tracker_value_t *mergedValue =
201+
(tracker_value_t *)malloc(sizeof(tracker_value_t));
202+
mergedValue->pool = p->pool;
203+
mergedValue->size = totalSize;
204+
205+
if (!mergedValue) {
206+
return UMF_RESULT_ERROR_OUT_OF_HOST_MEMORY;
207+
}
208+
209+
int r = util_mutex_lock(p->hTracker->splitMergeMutex);
210+
if (r) {
211+
goto err_lock;
212+
}
213+
214+
tracker_value_t *leftValue =
215+
(tracker_value_t *)critnib_get(p->hTracker->map, (uintptr_t)lowPtr);
216+
if (!leftValue) {
217+
fprintf(stderr, "tracking merge: no left value\n");
218+
ret = UMF_RESULT_ERROR_INVALID_ARGUMENT;
219+
goto err;
220+
}
221+
tracker_value_t *rightValue =
222+
(tracker_value_t *)critnib_get(p->hTracker->map, (uintptr_t)highPtr);
223+
if (!rightValue) {
224+
fprintf(stderr, "tracking merge: no right value\n");
225+
ret = UMF_RESULT_ERROR_INVALID_ARGUMENT;
226+
goto err;
227+
}
228+
if (leftValue->pool != rightValue->pool) {
229+
fprintf(stderr, "tracking merge: pool mismatch\n");
230+
ret = UMF_RESULT_ERROR_INVALID_ARGUMENT;
231+
goto err;
232+
}
233+
if (leftValue->size + rightValue->size != totalSize) {
234+
fprintf(stderr, "tracking merge: leftValue->size + rightValue->size != "
235+
"totalSize\n");
236+
ret = UMF_RESULT_ERROR_INVALID_ARGUMENT;
237+
goto err;
238+
}
239+
240+
ret = umfMemoryProviderAllocationMerge(p->hUpstream, lowPtr, highPtr,
241+
totalSize);
242+
if (ret != UMF_RESULT_SUCCESS) {
243+
fprintf(stderr,
244+
"tracking merge: umfMemoryProviderAllocationMerge failed\n");
245+
goto err;
246+
}
247+
248+
// We'll have duplicate entry for the range [highPtr, rightValue->size] but this is fine,
249+
// the value is the same anyway and we forbid splitting/removing that range concurrently
250+
int cret = critnib_insert(p->hTracker->map, (uintptr_t)lowPtr,
251+
(void *)mergedValue, 1 /* update */);
252+
// this cannot fail since we know the element exists (nothing to allocate)
253+
assert(cret == 0);
254+
(void)cret;
255+
256+
// free old value that we just replaced with mergedValue
257+
free(leftValue);
258+
259+
void *erasedRightValue =
260+
critnib_remove(p->hTracker->map, (uintptr_t)highPtr);
261+
assert(erasedRightValue == rightValue);
262+
263+
free(erasedRightValue);
264+
265+
util_mutex_unlock(p->hTracker->splitMergeMutex);
266+
267+
return UMF_RESULT_SUCCESS;
268+
269+
err:
270+
util_mutex_unlock(p->hTracker->splitMergeMutex);
271+
err_lock:
272+
free(mergedValue);
273+
return ret;
274+
}
275+
122276
static umf_result_t trackingFree(void *hProvider, void *ptr, size_t size) {
123277
umf_result_t ret;
124278
umf_tracking_memory_provider_t *p =
@@ -214,7 +368,8 @@ umf_memory_provider_ops_t UMF_TRACKING_MEMORY_PROVIDER_OPS = {
214368
.purge_force = trackingPurgeForce,
215369
.purge_lazy = trackingPurgeLazy,
216370
.get_name = trackingName,
217-
};
371+
.allocation_split = trackingAllocationSplit,
372+
.allocation_merge = trackingAllocationMerge};
218373

219374
umf_result_t umfTrackingMemoryProviderCreate(
220375
umf_memory_provider_handle_t hUpstream, umf_memory_pool_handle_t hPool,

0 commit comments

Comments
 (0)