Skip to content

Commit 3f11894

Browse files
igchorldorau
authored andcommitted
Implement umfMemoryProviderAllocation[Split/Merge]
That is needed to support jemalloc (avoid leaking memory).
1 parent 887bb1f commit 3f11894

File tree

7 files changed

+219
-17
lines changed

7 files changed

+219
-17
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 umfMemoryProviderAllocationMerge(
163+
umf_memory_provider_handle_t hProvider, void *leftPtr, void *rightPtr,
164+
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 *leftPtr,
146+
void *rightPtr, 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 *leftPtr, void *rightPtr,
154+
size_t totalSize) {
155+
if ((uintptr_t)leftPtr >= (uintptr_t)rightPtr) {
156+
fprintf(
157+
stderr,
158+
"umfMemoryProviderAllocationMerge failed. check failed: %p >= %p\n",
159+
leftPtr, rightPtr);
160+
return UMF_RESULT_ERROR_INVALID_ARGUMENT;
161+
}
162+
163+
if ((uintptr_t)rightPtr - (uintptr_t)leftPtr > totalSize) {
164+
fprintf(stderr,
165+
"umfMemoryProviderAllocationMerge failed. check failed: %p - "
166+
"%p > %zu\n",
167+
rightPtr, leftPtr, totalSize);
168+
return UMF_RESULT_ERROR_INVALID_ARGUMENT;
169+
}
170+
171+
umf_result_t res = hProvider->ops.allocation_merge(
172+
hProvider->provider_priv, leftPtr, rightPtr, 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
@@ -122,7 +122,7 @@ static void tbb_raw_free_wrapper(intptr_t pool_id, void *ptr, size_t bytes) {
122122
TLS_last_free_error = ret;
123123
fprintf(
124124
stderr,
125-
"Memory provider failed to free memory, addr = %p, size = %lu\n",
125+
"Memory provider failed to free memory, addr = %p, size = %zu\n",
126126
ptr, bytes);
127127
}
128128
}

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 *leftPtr,
463+
void *rightPtr, size_t totalSize) {
464+
(void)provider;
465+
(void)leftPtr;
466+
(void)rightPtr;
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: 130 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,134 @@ static umf_result_t trackingAlloc(void *hProvider, size_t size,
120120
return ret;
121121
}
122122

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

252381
umf_result_t umfTrackingMemoryProviderCreate(
253382
umf_memory_provider_handle_t hUpstream, umf_memory_pool_handle_t hPool,

0 commit comments

Comments
 (0)