Skip to content

Commit f1c84f8

Browse files
committed
Do not fail linear allocator with OOM
The linear base allocator fails with OOM now, when it runs out of memory requested in umf_ba_linear_create(). The linear base allocator is used to allocate structures which size depends on number of nodes for example, so it is not known before calling umf_ba_linear_create(). This patch adds a feature that the pool of linear allocator can "grow" (a new memory region is allocated from OS in fact) when more space is needed. Signed-off-by: Lukasz Dorau <[email protected]>
1 parent 4c95ea8 commit f1c84f8

File tree

1 file changed

+83
-16
lines changed

1 file changed

+83
-16
lines changed

src/base_alloc/base_alloc_linear.c

Lines changed: 83 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,24 +19,59 @@
1919
// alignment of the linear base allocator
2020
#define MEMORY_ALIGNMENT (sizeof(uintptr_t))
2121

22-
// metadata of the linear base allocator
23-
typedef struct {
24-
size_t pool_size;
22+
typedef struct umf_ba_next_linear_pool_t umf_ba_next_linear_pool_t;
23+
24+
// metadata is set and used only in the main (the first) pool
25+
typedef struct umf_ba_main_linear_pool_meta_t {
26+
size_t pool_size; // size of each pool (argument of each ba_os_alloc() call)
2527
os_mutex_t lock;
2628
char *data_ptr;
2729
size_t size_left;
30+
#ifndef NDEBUG
31+
size_t n_pools;
32+
#endif /* NDEBUG */
2833
} umf_ba_main_linear_pool_meta_t;
2934

30-
// pool of the linear base allocator
35+
// the main pool of the linear base allocator (there is only one such pool)
3136
struct umf_ba_linear_pool {
37+
// address of the beginning of the next pool (a list of allocated pools
38+
// to be freed in umf_ba_linear_destroy())
39+
umf_ba_next_linear_pool_t *next_pool;
40+
41+
// metadata is set and used only in the main (the first) pool
3242
umf_ba_main_linear_pool_meta_t metadata;
33-
char data[]; // data area starts here
43+
44+
// data area of the main pool (the first one) starts here
45+
char data[];
46+
};
47+
48+
// the "next" pools of the linear base allocator (pools allocated later,
49+
// when we run out of the memory of the main pool)
50+
struct umf_ba_next_linear_pool_t {
51+
// address of the beginning of the next pool (a list of allocated pools
52+
// to be freed in umf_ba_linear_destroy())
53+
umf_ba_next_linear_pool_t *next_pool;
54+
55+
// data area of all pools except of the main (the first one) starts here
56+
char data[];
3457
};
3558

59+
#ifndef NDEBUG
60+
static void ba_debug_checks(umf_ba_linear_pool_t *pool) {
61+
// count pools
62+
size_t n_pools = 1;
63+
umf_ba_next_linear_pool_t *next_pool = pool->next_pool;
64+
while (next_pool) {
65+
n_pools++;
66+
next_pool = next_pool->next_pool;
67+
}
68+
assert(n_pools == pool->metadata.n_pools);
69+
}
70+
#endif /* NDEBUG */
71+
3672
umf_ba_linear_pool_t *umf_ba_linear_create(size_t pool_size) {
37-
size_t mutex_size = align_size(util_mutex_get_size(), MEMORY_ALIGNMENT);
3873
size_t metadata_size = sizeof(umf_ba_main_linear_pool_meta_t);
39-
pool_size = pool_size + metadata_size + mutex_size;
74+
pool_size = pool_size + metadata_size;
4075
if (pool_size < MINIMUM_LINEAR_POOL_SIZE) {
4176
pool_size = MINIMUM_LINEAR_POOL_SIZE;
4277
}
@@ -56,6 +91,10 @@ umf_ba_linear_pool_t *umf_ba_linear_create(size_t pool_size) {
5691
pool->metadata.pool_size = pool_size;
5792
pool->metadata.data_ptr = data_ptr;
5893
pool->metadata.size_left = size_left;
94+
pool->next_pool = NULL; // this is the only pool now
95+
#ifndef NDEBUG
96+
pool->metadata.n_pools = 1;
97+
#endif /* NDEBUG */
5998

6099
// init lock
61100
os_mutex_t *lock = util_mutex_init(&pool->metadata.lock);
@@ -69,27 +108,55 @@ umf_ba_linear_pool_t *umf_ba_linear_create(size_t pool_size) {
69108

70109
void *umf_ba_linear_alloc(umf_ba_linear_pool_t *pool, size_t size) {
71110
size_t aligned_size = align_size(size, MEMORY_ALIGNMENT);
72-
73111
util_mutex_lock(&pool->metadata.lock);
74112
if (pool->metadata.size_left < aligned_size) {
75-
fprintf(stderr,
76-
"error: umf_ba_linear_alloc() failed (requested size: %zu > "
77-
"space left: %zu)\n",
78-
aligned_size, pool->metadata.size_left);
79-
util_mutex_unlock(&pool->metadata.lock);
80-
assert(pool->metadata.size_left >= aligned_size);
81-
return NULL; // out of memory
113+
umf_ba_next_linear_pool_t *new_pool =
114+
(umf_ba_next_linear_pool_t *)ba_os_alloc(pool->metadata.pool_size);
115+
if (!new_pool) {
116+
util_mutex_unlock(&pool->metadata.lock);
117+
return NULL;
118+
}
119+
120+
void *data_ptr = &new_pool->data;
121+
size_t size_left = pool->metadata.pool_size -
122+
offsetof(umf_ba_next_linear_pool_t, data);
123+
align_ptr_size(&data_ptr, &size_left, MEMORY_ALIGNMENT);
124+
125+
pool->metadata.data_ptr = data_ptr;
126+
pool->metadata.size_left = size_left;
127+
128+
// add the new pool to the list of pools
129+
new_pool->next_pool = pool->next_pool;
130+
pool->next_pool = new_pool;
131+
#ifndef NDEBUG
132+
pool->metadata.n_pools++;
133+
#endif /* NDEBUG */
82134
}
83135

84136
void *ptr = pool->metadata.data_ptr;
85137
pool->metadata.data_ptr += aligned_size;
86138
pool->metadata.size_left -= aligned_size;
139+
#ifndef NDEBUG
140+
ba_debug_checks(pool);
141+
#endif /* NDEBUG */
87142
util_mutex_unlock(&pool->metadata.lock);
88143

89144
return ptr;
90145
}
91146

92147
void umf_ba_linear_destroy(umf_ba_linear_pool_t *pool) {
148+
#ifndef NDEBUG
149+
ba_debug_checks(pool);
150+
#endif /* NDEBUG */
151+
size_t size = pool->metadata.pool_size;
152+
umf_ba_next_linear_pool_t *current_pool;
153+
umf_ba_next_linear_pool_t *next_pool = pool->next_pool;
154+
while (next_pool) {
155+
current_pool = next_pool;
156+
next_pool = next_pool->next_pool;
157+
ba_os_free(current_pool, size);
158+
}
159+
93160
util_mutex_destroy_not_free(&pool->metadata.lock);
94-
ba_os_free(pool, pool->metadata.pool_size);
161+
ba_os_free(pool, size);
95162
}

0 commit comments

Comments
 (0)