Skip to content

Commit 75f78c9

Browse files
authored
Merge pull request #248 from ldorau/Implement_and_use_umf_ba_linear_free_in_the_proxy_library
Implement and use umf_ba_linear_free() in the proxy library
2 parents 61d11a9 + daebf9d commit 75f78c9

File tree

5 files changed

+127
-15
lines changed

5 files changed

+127
-15
lines changed

src/base_alloc/base_alloc_linear.c

Lines changed: 96 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,18 @@
1313
#include "utils_common.h"
1414
#include "utils_concurrency.h"
1515

16+
#ifndef NDEBUG
17+
#define DEBUG_RUN_CHECKS(pool) ba_debug_checks(pool)
18+
#define DEBUG_SET_VAR(var, value) DO_WHILE_EXPRS((var = value))
19+
#define DEBUG_INC_VAR(var) DO_WHILE_EXPRS((var++))
20+
#define DEBUG_DEC_VAR(var) DO_WHILE_EXPRS((var--))
21+
#else
22+
#define DEBUG_RUN_CHECKS(pool) DO_WHILE_EMPTY
23+
#define DEBUG_SET_VAR(var, value) DO_WHILE_EMPTY
24+
#define DEBUG_INC_VAR(var) DO_WHILE_EMPTY
25+
#define DEBUG_DEC_VAR(var) DO_WHILE_EMPTY
26+
#endif /* NDEBUG */
27+
1628
// minimum size of a single pool of the linear base allocator
1729
#define MINIMUM_LINEAR_POOL_SIZE (ba_os_get_page_size())
1830

@@ -27,9 +39,11 @@ typedef struct umf_ba_main_linear_pool_meta_t {
2739
os_mutex_t lock;
2840
char *data_ptr;
2941
size_t size_left;
42+
size_t pool_n_allocs; // number of allocations in this pool
3043
#ifndef NDEBUG
3144
size_t n_pools;
32-
#endif /* NDEBUG */
45+
size_t global_n_allocs; // global number of allocations in all pools
46+
#endif /* NDEBUG */
3347
} umf_ba_main_linear_pool_meta_t;
3448

3549
// the main pool of the linear base allocator (there is only one such pool)
@@ -52,7 +66,8 @@ struct umf_ba_next_linear_pool_t {
5266
// to be freed in umf_ba_linear_destroy())
5367
umf_ba_next_linear_pool_t *next_pool;
5468

55-
size_t pool_size; // size of this pool (argument of ba_os_alloc() call)
69+
size_t pool_size; // size of this pool (argument of ba_os_alloc() call)
70+
size_t pool_n_allocs; // number of allocations in this pool
5671

5772
// data area of all pools except of the main (the first one) starts here
5873
char data[];
@@ -94,9 +109,9 @@ umf_ba_linear_pool_t *umf_ba_linear_create(size_t pool_size) {
94109
pool->metadata.data_ptr = data_ptr;
95110
pool->metadata.size_left = size_left;
96111
pool->next_pool = NULL; // this is the only pool now
97-
#ifndef NDEBUG
98-
pool->metadata.n_pools = 1;
99-
#endif /* NDEBUG */
112+
pool->metadata.pool_n_allocs = 0;
113+
DEBUG_SET_VAR(pool->metadata.n_pools, 1);
114+
DEBUG_SET_VAR(pool->metadata.global_n_allocs, 0);
100115

101116
// init lock
102117
os_mutex_t *lock = util_mutex_init(&pool->metadata.lock);
@@ -131,6 +146,7 @@ void *umf_ba_linear_alloc(umf_ba_linear_pool_t *pool, size_t size) {
131146
}
132147

133148
new_pool->pool_size = pool_size;
149+
new_pool->pool_n_allocs = 0;
134150

135151
void *data_ptr = &new_pool->data;
136152
size_t size_left =
@@ -143,23 +159,86 @@ void *umf_ba_linear_alloc(umf_ba_linear_pool_t *pool, size_t size) {
143159
// add the new pool to the list of pools
144160
new_pool->next_pool = pool->next_pool;
145161
pool->next_pool = new_pool;
146-
#ifndef NDEBUG
147-
pool->metadata.n_pools++;
148-
#endif /* NDEBUG */
162+
DEBUG_INC_VAR(pool->metadata.n_pools);
149163
}
150164

151165
assert(pool->metadata.size_left >= aligned_size);
152166
void *ptr = pool->metadata.data_ptr;
153167
pool->metadata.data_ptr += aligned_size;
154168
pool->metadata.size_left -= aligned_size;
155-
#ifndef NDEBUG
156-
ba_debug_checks(pool);
157-
#endif /* NDEBUG */
169+
if (pool->next_pool) {
170+
pool->next_pool->pool_n_allocs++;
171+
} else {
172+
pool->metadata.pool_n_allocs++;
173+
}
174+
DEBUG_INC_VAR(pool->metadata.global_n_allocs);
175+
DEBUG_RUN_CHECKS(pool);
158176
util_mutex_unlock(&pool->metadata.lock);
159177

160178
return ptr;
161179
}
162180

181+
// check if ptr belongs to pool
182+
static inline int pool_contains_ptr(void *pool, size_t pool_size,
183+
void *data_begin, void *ptr) {
184+
return ((char *)ptr >= (char *)data_begin &&
185+
(char *)ptr < ((char *)(pool)) + pool_size);
186+
}
187+
188+
// umf_ba_linear_free() really frees memory only if all allocations from an inactive pool were freed
189+
// It returns:
190+
// 0 - ptr belonged to the pool and was freed
191+
// -1 - ptr doesn't belong to the pool and wasn't freed
192+
int umf_ba_linear_free(umf_ba_linear_pool_t *pool, void *ptr) {
193+
util_mutex_lock(&pool->metadata.lock);
194+
DEBUG_RUN_CHECKS(pool);
195+
if (pool_contains_ptr(pool, pool->metadata.pool_size, pool->data, ptr)) {
196+
pool->metadata.pool_n_allocs--;
197+
DEBUG_DEC_VAR(pool->metadata.global_n_allocs);
198+
size_t page_size = ba_os_get_page_size();
199+
if ((pool->metadata.pool_n_allocs == 0) && pool->next_pool &&
200+
(pool->metadata.pool_size > page_size)) {
201+
// we can free the first (main) pool except of the first page containing the metadata
202+
void *ptr = pool + page_size;
203+
size_t size = pool->metadata.pool_size - page_size;
204+
ba_os_free(ptr, size);
205+
}
206+
DEBUG_RUN_CHECKS(pool);
207+
util_mutex_unlock(&pool->metadata.lock);
208+
return 0;
209+
}
210+
211+
umf_ba_next_linear_pool_t *next_pool = pool->next_pool;
212+
umf_ba_next_linear_pool_t *prev_pool = NULL;
213+
while (next_pool) {
214+
if (pool_contains_ptr(next_pool, next_pool->pool_size, next_pool->data,
215+
ptr)) {
216+
DEBUG_DEC_VAR(pool->metadata.global_n_allocs);
217+
next_pool->pool_n_allocs--;
218+
// pool->next_pool is the active pool - we cannot free it
219+
if ((next_pool->pool_n_allocs == 0) &&
220+
next_pool != pool->next_pool) {
221+
assert(prev_pool); // it cannot be the active pool
222+
assert(prev_pool->next_pool == next_pool);
223+
prev_pool->next_pool = next_pool->next_pool;
224+
DEBUG_DEC_VAR(pool->metadata.n_pools);
225+
void *ptr = next_pool;
226+
size_t size = next_pool->pool_size;
227+
ba_os_free(ptr, size);
228+
}
229+
DEBUG_RUN_CHECKS(pool);
230+
util_mutex_unlock(&pool->metadata.lock);
231+
return 0;
232+
}
233+
prev_pool = next_pool;
234+
next_pool = next_pool->next_pool;
235+
}
236+
237+
util_mutex_unlock(&pool->metadata.lock);
238+
// ptr doesn't belong to the pool and wasn't freed
239+
return -1;
240+
}
241+
163242
void umf_ba_linear_destroy(umf_ba_linear_pool_t *pool) {
164243
// Do not destroy if we are running in the proxy library,
165244
// because it may need those resources till
@@ -169,7 +248,12 @@ void umf_ba_linear_destroy(umf_ba_linear_pool_t *pool) {
169248
}
170249

171250
#ifndef NDEBUG
172-
ba_debug_checks(pool);
251+
DEBUG_RUN_CHECKS(pool);
252+
if (pool->metadata.global_n_allocs) {
253+
fprintf(stderr, "umf_ba_linear_destroy(): global_n_allocs = %zu\n",
254+
pool->metadata.global_n_allocs);
255+
assert(pool->metadata.global_n_allocs == 0);
256+
}
173257
#endif /* NDEBUG */
174258

175259
umf_ba_next_linear_pool_t *current_pool;

src/base_alloc/base_alloc_linear.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@ void umf_ba_linear_destroy(umf_ba_linear_pool_t *pool);
2929
size_t umf_ba_linear_pool_contains_pointer(umf_ba_linear_pool_t *pool,
3030
void *ptr);
3131

32+
// umf_ba_linear_free() really frees memory only if all allocations from an inactive pool were freed
33+
// It returns:
34+
// 0 - ptr belonged to the pool and was freed
35+
// -1 - ptr doesn't belong to the pool and wasn't freed
36+
int umf_ba_linear_free(umf_ba_linear_pool_t *pool, void *ptr);
37+
3238
#ifdef __cplusplus
3339
}
3440
#endif

src/proxy_lib/proxy_lib.c

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,9 @@ static inline void *ba_generic_realloc(umf_ba_linear_pool_t *pool, void *ptr,
127127

128128
memcpy(new_ptr, ptr, new_size);
129129

130+
// we can free the old ptr now
131+
umf_ba_linear_free(pool, ptr);
132+
130133
return new_ptr;
131134
}
132135

@@ -136,7 +139,7 @@ static inline void *ba_generic_realloc(umf_ba_linear_pool_t *pool, void *ptr,
136139

137140
static void ba_leak_create(void) { Base_alloc_leak = umf_ba_linear_create(0); }
138141

139-
// it does not implement destroy(), because it will not free memory at all
142+
// it does not implement destroy(), because we cannot destroy non-freed memory
140143

141144
static inline void *ba_leak_malloc(size_t size) {
142145
util_init_once(&Base_alloc_leak_initialized, ba_leak_create);
@@ -160,6 +163,10 @@ static inline void *ba_leak_aligned_alloc(size_t alignment, size_t size) {
160163
return (void *)ALIGN_UP((uintptr_t)ptr, alignment);
161164
}
162165

166+
static inline int ba_leak_free(void *ptr) {
167+
return umf_ba_linear_free(Base_alloc_leak, ptr);
168+
}
169+
163170
static inline size_t ba_leak_pool_contains_pointer(void *ptr) {
164171
return umf_ba_linear_pool_contains_pointer(Base_alloc_leak, ptr);
165172
}
@@ -221,8 +228,7 @@ void free(void *ptr) {
221228
return;
222229
}
223230

224-
if (ba_leak_pool_contains_pointer(ptr)) {
225-
// allocations from the leak linear base allocator will not be freed at all
231+
if (ba_leak_free(ptr) == 0) {
226232
return;
227233
}
228234

src/utils/utils_common.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,14 @@
1919
extern "C" {
2020
#endif
2121

22+
#define DO_WHILE_EMPTY \
23+
do { \
24+
} while (0)
25+
#define DO_WHILE_EXPRS(expression) \
26+
do { \
27+
expression; \
28+
} while (0)
29+
2230
#ifdef _WIN32 /* Windows */
2331

2432
#define __TLS __declspec(thread)

test/test_base_alloc_linear.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ TEST_F(test, baseAllocLinearAllocMoreThanPoolSize) {
2525
void *ptr = umf_ba_linear_alloc(pool.get(), new_size);
2626
UT_ASSERTne(ptr, NULL);
2727
memset(ptr, 0, new_size);
28+
29+
umf_ba_linear_free(pool.get(), ptr);
2830
}
2931

3032
TEST_F(test, baseAllocLinearPoolContainsPointer) {
@@ -43,6 +45,8 @@ TEST_F(test, baseAllocLinearPoolContainsPointer) {
4345
// assert pool does NOT contain pointer 0x0123
4446
UT_ASSERTeq(umf_ba_linear_pool_contains_pointer(pool.get(), (void *)0x0123),
4547
0);
48+
49+
umf_ba_linear_free(pool.get(), ptr);
4650
}
4751

4852
TEST_F(test, baseAllocLinearMultiThreadedAllocMemset) {
@@ -77,6 +81,10 @@ TEST_F(test, baseAllocLinearMultiThreadedAllocMemset) {
7781
UT_ASSERTeq(*(buffer[i].ptr + k), (i + TID) & 0xFF);
7882
}
7983
}
84+
85+
for (int i = 0; i < ITERATIONS; i++) {
86+
umf_ba_linear_free(pool, buffer[i].ptr);
87+
}
8088
};
8189

8290
std::vector<std::thread> threads;

0 commit comments

Comments
 (0)