Skip to content

Commit 10d56ce

Browse files
igchorldorau
authored andcommitted
Implement umfMemoryProviderAllocation[Split/Merge]
That is needed to support jemalloc (avoid leaking memory).
1 parent e23fbd4 commit 10d56ce

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
@@ -119,6 +119,134 @@ static umf_result_t trackingAlloc(void *hProvider, size_t size,
119119
return ret;
120120
}
121121

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

221350
umf_result_t umfTrackingMemoryProviderCreate(
222351
umf_memory_provider_handle_t hUpstream, umf_memory_pool_handle_t hPool,

0 commit comments

Comments
 (0)