Skip to content

Commit 0f4ddc5

Browse files
committed
Implement umf_ba_linear_free() for linear base allocator
Implement umf_ba_linear_free() for linear base allocator. umf_ba_linear_free() really frees memory only if all allocations from an inactive pool were freed. Signed-off-by: Lukasz Dorau <[email protected]>
1 parent ac4bfb2 commit 0f4ddc5

File tree

3 files changed

+98
-2
lines changed

3 files changed

+98
-2
lines changed

src/base_alloc/base_alloc_linear.c

Lines changed: 84 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@
1717
#define DEBUG_RUN_CHECKS(pool) ba_debug_checks(pool)
1818
#define DEBUG_SET_VAR(var, value) DO_WHILE_EXPRS((var = value))
1919
#define DEBUG_INC_VAR(var) DO_WHILE_EXPRS((var++))
20+
#define DEBUG_DEC_VAR(var) DO_WHILE_EXPRS((var--))
2021
#else
2122
#define DEBUG_RUN_CHECKS(pool) DO_WHILE_EMPTY
2223
#define DEBUG_SET_VAR(var, value) DO_WHILE_EMPTY
2324
#define DEBUG_INC_VAR(var) DO_WHILE_EMPTY
25+
#define DEBUG_DEC_VAR(var) DO_WHILE_EMPTY
2426
#endif /* NDEBUG */
2527

2628
// minimum size of a single pool of the linear base allocator
@@ -37,9 +39,11 @@ typedef struct umf_ba_main_linear_pool_meta_t {
3739
os_mutex_t lock;
3840
char *data_ptr;
3941
size_t size_left;
42+
size_t pool_n_allocs; // number of allocations in this pool
4043
#ifndef NDEBUG
4144
size_t n_pools;
42-
#endif /* NDEBUG */
45+
size_t global_n_allocs; // global number of allocations in all pools
46+
#endif /* NDEBUG */
4347
} umf_ba_main_linear_pool_meta_t;
4448

4549
// the main pool of the linear base allocator (there is only one such pool)
@@ -62,7 +66,8 @@ struct umf_ba_next_linear_pool_t {
6266
// to be freed in umf_ba_linear_destroy())
6367
umf_ba_next_linear_pool_t *next_pool;
6468

65-
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
6671

6772
// data area of all pools except of the main (the first one) starts here
6873
char data[];
@@ -104,7 +109,9 @@ umf_ba_linear_pool_t *umf_ba_linear_create(size_t pool_size) {
104109
pool->metadata.data_ptr = data_ptr;
105110
pool->metadata.size_left = size_left;
106111
pool->next_pool = NULL; // this is the only pool now
112+
pool->metadata.pool_n_allocs = 0;
107113
DEBUG_SET_VAR(pool->metadata.n_pools, 1);
114+
DEBUG_SET_VAR(pool->metadata.global_n_allocs, 0);
108115

109116
// init lock
110117
os_mutex_t *lock = util_mutex_init(&pool->metadata.lock);
@@ -139,6 +146,7 @@ void *umf_ba_linear_alloc(umf_ba_linear_pool_t *pool, size_t size) {
139146
}
140147

141148
new_pool->pool_size = pool_size;
149+
new_pool->pool_n_allocs = 0;
142150

143151
void *data_ptr = &new_pool->data;
144152
size_t size_left =
@@ -158,12 +166,79 @@ void *umf_ba_linear_alloc(umf_ba_linear_pool_t *pool, size_t size) {
158166
void *ptr = pool->metadata.data_ptr;
159167
pool->metadata.data_ptr += aligned_size;
160168
pool->metadata.size_left -= aligned_size;
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);
161175
DEBUG_RUN_CHECKS(pool);
162176
util_mutex_unlock(&pool->metadata.lock);
163177

164178
return ptr;
165179
}
166180

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+
167242
void umf_ba_linear_destroy(umf_ba_linear_pool_t *pool) {
168243
// Do not destroy if we are running in the proxy library,
169244
// because it may need those resources till
@@ -172,7 +247,14 @@ void umf_ba_linear_destroy(umf_ba_linear_pool_t *pool) {
172247
return;
173248
}
174249

250+
#ifndef NDEBUG
175251
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+
}
257+
#endif /* NDEBUG */
176258

177259
umf_ba_next_linear_pool_t *current_pool;
178260
umf_ba_next_linear_pool_t *next_pool = pool->next_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

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)