Skip to content

Commit 9f7b62a

Browse files
committed
Add linear base allocator
Signed-off-by: Lukasz Dorau <[email protected]>
1 parent 61bebd5 commit 9f7b62a

File tree

5 files changed

+257
-0
lines changed

5 files changed

+257
-0
lines changed

src/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ set(UMF_LIBS umf_utils)
1010

1111
set(UMF_SOURCES
1212
base_alloc/base_alloc.c
13+
base_alloc/base_alloc_linear.c
1314
memory_pool.c
1415
memory_provider.c
1516
memory_provider_get_last_failed.c

src/base_alloc/base_alloc_linear.c

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
/*
2+
* Copyright (C) 2024 Intel Corporation
3+
*
4+
* Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT.
5+
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
*/
7+
8+
#include <assert.h>
9+
10+
#include "base_alloc_internal.h"
11+
#include "base_alloc_linear.h"
12+
#include "utils_common.h"
13+
#include "utils_concurrency.h"
14+
15+
// minimum size of a single pool of the base allocator
16+
#define MINIMUM_LINEAR_POOL_SIZE (ba_os_get_page_size())
17+
18+
// alignment of the base allocator
19+
#define MEMORY_ALIGNMENT (8)
20+
21+
typedef struct umf_ba_next_linear_pool_t umf_ba_next_linear_pool_t;
22+
23+
// metadata is set and used only in the main (the first) pool
24+
struct umf_ba_main_linear_pool_meta_t {
25+
size_t pool_size; // size of each pool (argument of each ba_os_alloc() call)
26+
os_mutex_t *lock;
27+
char *data_ptr;
28+
size_t size_left;
29+
#ifndef NDEBUG
30+
size_t n_pools;
31+
#endif /* NDEBUG */
32+
};
33+
34+
// the main pool of the base allocator (there is only one such pool)
35+
struct umf_ba_linear_pool_t {
36+
// address of the beginning of the next pool (a list of allocated pools to be freed in umf_ba_linear_destroy())
37+
umf_ba_next_linear_pool_t *next_pool;
38+
39+
// metadata is set and used only in the main (the first) pool
40+
struct umf_ba_main_linear_pool_meta_t metadata;
41+
42+
// data area of the main pool (the first one) starts here
43+
char data[];
44+
};
45+
46+
// the "next" pools of the base allocator (pools allocated later, when we run out of the memory of the main pool)
47+
struct umf_ba_next_linear_pool_t {
48+
// address of the beginning of the next pool (a list of allocated pools to be freed in umf_ba_linear_destroy())
49+
umf_ba_next_linear_pool_t *next_pool;
50+
51+
// data area of all pools except of the main (the first one) starts here
52+
char data[];
53+
};
54+
55+
#ifndef NDEBUG
56+
static void ba_debug_checks(umf_ba_linear_pool_t *pool) {
57+
// count pools
58+
size_t n_pools = 1;
59+
umf_ba_next_linear_pool_t *next_pool = pool->next_pool;
60+
while (next_pool) {
61+
n_pools++;
62+
next_pool = next_pool->next_pool;
63+
}
64+
assert(n_pools == pool->metadata.n_pools);
65+
}
66+
#endif /* NDEBUG */
67+
68+
umf_ba_linear_pool_t *umf_ba_linear_create(size_t pool_size) {
69+
size_t mutex_size = align_size(util_mutex_get_size(), MEMORY_ALIGNMENT);
70+
size_t metadata_size = sizeof(struct umf_ba_main_linear_pool_meta_t);
71+
pool_size = pool_size + metadata_size + mutex_size;
72+
if (pool_size < MINIMUM_LINEAR_POOL_SIZE) {
73+
pool_size = MINIMUM_LINEAR_POOL_SIZE;
74+
}
75+
76+
pool_size = align_size(pool_size, ba_os_get_page_size());
77+
78+
umf_ba_linear_pool_t *pool = (umf_ba_linear_pool_t *)ba_os_alloc(pool_size);
79+
if (!pool) {
80+
return NULL;
81+
}
82+
83+
pool->metadata.pool_size = pool_size;
84+
pool->next_pool = NULL; // this is the only pool now
85+
#ifndef NDEBUG
86+
pool->metadata.n_pools = 1;
87+
#endif /* NDEBUG */
88+
89+
char *data_ptr = (char *)&pool->data;
90+
size_t size_left = pool_size - offsetof(umf_ba_linear_pool_t, data);
91+
92+
// allocate and init lock
93+
pool->metadata.lock = util_mutex_init(data_ptr);
94+
if (!pool->metadata.lock) {
95+
ba_os_free(pool, pool_size);
96+
return NULL;
97+
}
98+
99+
pool->metadata.data_ptr = data_ptr + mutex_size; // lock is here
100+
pool->metadata.size_left = size_left - mutex_size; // for lock
101+
102+
return pool;
103+
}
104+
105+
void *umf_ba_linear_alloc(umf_ba_linear_pool_t *pool, size_t size) {
106+
size_t aligned_size = align_size(size, MEMORY_ALIGNMENT);
107+
util_mutex_lock(pool->metadata.lock);
108+
if (pool->metadata.size_left < aligned_size) {
109+
umf_ba_next_linear_pool_t *new_pool =
110+
(umf_ba_next_linear_pool_t *)ba_os_alloc(pool->metadata.pool_size);
111+
if (!new_pool) {
112+
util_mutex_unlock(pool->metadata.lock);
113+
return NULL;
114+
}
115+
116+
// add the new pool to the list of pools
117+
new_pool->next_pool = pool->next_pool;
118+
pool->next_pool = new_pool;
119+
120+
#ifndef NDEBUG
121+
pool->metadata.n_pools++;
122+
#endif /* NDEBUG */
123+
124+
pool->metadata.data_ptr = (char *)&new_pool->data;
125+
pool->metadata.size_left = pool->metadata.pool_size -
126+
offsetof(umf_ba_next_linear_pool_t, data);
127+
}
128+
129+
void *ptr = pool->metadata.data_ptr;
130+
pool->metadata.data_ptr += aligned_size;
131+
pool->metadata.size_left -= aligned_size;
132+
#ifndef NDEBUG
133+
ba_debug_checks(pool);
134+
#endif /* NDEBUG */
135+
util_mutex_unlock(pool->metadata.lock);
136+
137+
return ptr;
138+
}
139+
140+
void umf_ba_linear_destroy(umf_ba_linear_pool_t *pool) {
141+
#ifndef NDEBUG
142+
ba_debug_checks(pool);
143+
#endif /* NDEBUG */
144+
size_t size = pool->metadata.pool_size;
145+
umf_ba_next_linear_pool_t *current_pool;
146+
umf_ba_next_linear_pool_t *next_pool = pool->next_pool;
147+
while (next_pool) {
148+
current_pool = next_pool;
149+
next_pool = next_pool->next_pool;
150+
ba_os_free(current_pool, size);
151+
}
152+
153+
util_mutex_destroy_not_free(pool->metadata.lock);
154+
ba_os_free(pool, size);
155+
}

src/base_alloc/base_alloc_linear.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/*
2+
* Copyright (C) 2024 Intel Corporation
3+
*
4+
* Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT.
5+
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
*/
7+
8+
/* A MT-safe fixed-size-pool base allocator */
9+
10+
#ifndef UMF_BASE_ALLOC_LINEAR_H
11+
#define UMF_BASE_ALLOC_LINEAR_H 1
12+
13+
#include <stddef.h>
14+
15+
typedef struct umf_ba_linear_pool_t umf_ba_linear_pool_t;
16+
17+
umf_ba_linear_pool_t *umf_ba_linear_create(size_t pool_size);
18+
void *umf_ba_linear_alloc(umf_ba_linear_pool_t *pool, size_t size);
19+
void umf_ba_linear_destroy(umf_ba_linear_pool_t *pool);
20+
21+
#endif /* UMF_BASE_ALLOC_LINEAR_H */

test/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,4 +117,7 @@ if(LINUX)
117117
add_umf_test(NAME base_alloc
118118
SRCS test_base_alloc.c
119119
LIBS umf_utils)
120+
add_umf_test(NAME base_alloc_linear
121+
SRCS test_base_alloc_linear.c
122+
LIBS umf_utils)
120123
endif()

test/test_base_alloc_linear.c

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/*
2+
* Copyright (C) 2024 Intel Corporation
3+
*
4+
* Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT.
5+
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
*/
7+
8+
#include <assert.h>
9+
#include <pthread.h>
10+
#include <stdio.h>
11+
#include <string.h>
12+
#include <sys/syscall.h>
13+
#include <unistd.h>
14+
15+
#include "base_alloc_linear.h"
16+
#include "test_helpers.h"
17+
18+
#define NTHREADS 10
19+
#define ITERATIONS 1000
20+
#define MAX_ALLOCATION_SIZE 1024
21+
22+
static void *start_routine(void *arg) {
23+
struct buffer_t {
24+
unsigned char *ptr;
25+
size_t size;
26+
} buffer[ITERATIONS];
27+
umf_ba_linear_pool_t *pool = (umf_ba_linear_pool_t *)arg;
28+
29+
long TID = syscall(SYS_gettid);
30+
31+
for (int i = 0; i < ITERATIONS; i++) {
32+
buffer[i].size = (rand() * MAX_ALLOCATION_SIZE) / RAND_MAX;
33+
buffer[i].ptr = umf_ba_linear_alloc(pool, buffer[i].size);
34+
memset(buffer[i].ptr, (i + TID) & 0xFF, buffer[i].size);
35+
}
36+
37+
for (int i = 0; i < ITERATIONS; i++) {
38+
for (int k = 0; k < buffer[i].size; k++) {
39+
if (*(buffer[i].ptr + k) != ((i + TID) & 0xFF)) {
40+
fprintf(
41+
stderr,
42+
"i = %i k = %i, *(buffer[i].ptr + k) = %i != ((i + TID) & "
43+
"0xFF) = %li\n",
44+
i, k, *(buffer[i].ptr + k), ((i + TID) & 0xFF));
45+
}
46+
UT_ASSERTeq(*(buffer[i].ptr + k), ((i + TID) & 0xFF));
47+
}
48+
}
49+
50+
return NULL;
51+
}
52+
53+
int main() {
54+
pthread_t thread[NTHREADS];
55+
umf_ba_linear_pool_t *pool = umf_ba_linear_create(MAX_ALLOCATION_SIZE);
56+
57+
for (int i = 0; i < NTHREADS; i++) {
58+
int ret = pthread_create(&thread[i], NULL, start_routine, pool);
59+
if (ret) {
60+
fprintf(stderr, "pthread_create() failed!\n");
61+
UT_ASSERTeq(ret, 0);
62+
}
63+
}
64+
65+
for (int i = 0; i < NTHREADS; i++) {
66+
void *retval;
67+
int ret = pthread_join(thread[i], &retval);
68+
if (ret) {
69+
fprintf(stderr, "pthread_join() failed!\n");
70+
UT_ASSERTeq(ret, 0);
71+
}
72+
}
73+
74+
umf_ba_linear_destroy(pool);
75+
76+
return 0;
77+
}

0 commit comments

Comments
 (0)