Skip to content

Commit 61bebd5

Browse files
committed
Add base allocator
Add a MT-safe fixed-size-pool base allocator. Signed-off-by: Lukasz Dorau <[email protected]>
1 parent 8512ab0 commit 61bebd5

File tree

10 files changed

+405
-2
lines changed

10 files changed

+405
-2
lines changed
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
[default]
2-
# Don't correct the "ASSERTne" word
3-
extend-ignore-words-re = ["ASSER", "Tne"]
2+
# Don't correct the following words:
3+
extend-ignore-words-re = ["ASSER", "Tne", "ba", "BA"]

src/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ add_subdirectory(utils)
99
set(UMF_LIBS umf_utils)
1010

1111
set(UMF_SOURCES
12+
base_alloc/base_alloc.c
1213
memory_pool.c
1314
memory_provider.c
1415
memory_provider_get_last_failed.c
@@ -19,14 +20,17 @@ set(UMF_SOURCES
1920
)
2021

2122
set(UMF_SOURCES_LINUX
23+
base_alloc/base_alloc_linux.c
2224
provider/provider_tracking_linux.c
2325
)
2426

2527
set(UMF_SOURCES_WINDOWS
28+
base_alloc/base_alloc_windows.c
2629
provider/provider_tracking_windows.c
2730
)
2831

2932
set(UMF_SOURCES_MACOSX
33+
base_alloc/base_alloc_linux.c
3034
provider/provider_tracking_linux.c
3135
)
3236

src/base_alloc/base_alloc.c

Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
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.h"
11+
#include "base_alloc_internal.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_POOL_SIZE (ba_os_get_page_size())
17+
18+
// minimum number of chunks used to calculate the size of pools
19+
#define MINIMUM_CHUNK_COUNT (128)
20+
21+
// alignment of the base allocator
22+
#define MEMORY_ALIGNMENT (8)
23+
24+
typedef struct umf_ba_chunk_t umf_ba_chunk_t;
25+
typedef struct umf_ba_next_pool_t umf_ba_next_pool_t;
26+
27+
// memory chunk of size 'chunk_size'
28+
struct umf_ba_chunk_t {
29+
umf_ba_chunk_t *next;
30+
char user_data[];
31+
};
32+
33+
// metadata is set and used only in the main (the first) pool
34+
struct umf_ba_main_pool_meta_t {
35+
size_t pool_size; // size of each pool (argument of each ba_os_alloc() call)
36+
size_t chunk_size; // size of all memory chunks in this pool
37+
os_mutex_t *free_lock; // lock of free_list
38+
umf_ba_chunk_t *free_list; // list of free chunks
39+
#ifndef NDEBUG
40+
size_t n_pools;
41+
size_t n_allocs;
42+
size_t n_chunks;
43+
#endif /* NDEBUG */
44+
};
45+
46+
// the main pool of the base allocator (there is only one such pool)
47+
struct umf_ba_pool_t {
48+
// address of the beginning of the next pool (a list of allocated pools to be freed in umf_ba_destroy())
49+
umf_ba_next_pool_t *next_pool;
50+
51+
// metadata is set and used only in the main (the first) pool
52+
struct umf_ba_main_pool_meta_t metadata;
53+
54+
// data area of the main pool (the first one) starts here
55+
char data[];
56+
};
57+
58+
// the "next" pools of the base allocator (pools allocated later, when we run out of the memory of the main pool)
59+
struct umf_ba_next_pool_t {
60+
// address of the beginning of the next pool (a list of allocated pools to be freed in umf_ba_destroy())
61+
umf_ba_next_pool_t *next_pool;
62+
63+
// data area of all pools except of the main (the first one) starts here
64+
char data[];
65+
};
66+
67+
#ifndef NDEBUG
68+
static void ba_debug_checks(umf_ba_pool_t *pool) {
69+
// count pools
70+
size_t n_pools = 1;
71+
umf_ba_next_pool_t *next_pool = pool->next_pool;
72+
while (next_pool) {
73+
n_pools++;
74+
next_pool = next_pool->next_pool;
75+
}
76+
assert(n_pools == pool->metadata.n_pools);
77+
78+
// count chunks
79+
size_t n_free_chunks = 0;
80+
umf_ba_chunk_t *next_chunk = pool->metadata.free_list;
81+
while (next_chunk) {
82+
n_free_chunks++;
83+
next_chunk = next_chunk->next;
84+
}
85+
assert(n_free_chunks == pool->metadata.n_chunks - pool->metadata.n_allocs);
86+
}
87+
#endif /* NDEBUG */
88+
89+
// ba_divide_memory_into_chunks - divide given memory into chunks of chunk_size and add them to the free_list
90+
static void ba_divide_memory_into_chunks(umf_ba_pool_t *pool, void *ptr,
91+
size_t size) {
92+
assert(pool->metadata.free_list == NULL);
93+
assert(size > pool->metadata.chunk_size);
94+
95+
char *data_ptr = ptr;
96+
size_t size_left = size;
97+
98+
umf_ba_chunk_t *current_chunk = (umf_ba_chunk_t *)data_ptr;
99+
umf_ba_chunk_t *prev_chunk = current_chunk;
100+
101+
while (size_left >= pool->metadata.chunk_size) {
102+
current_chunk = (umf_ba_chunk_t *)data_ptr;
103+
prev_chunk->next = current_chunk;
104+
105+
data_ptr += pool->metadata.chunk_size;
106+
size_left -= pool->metadata.chunk_size;
107+
prev_chunk = current_chunk;
108+
#ifndef NDEBUG
109+
pool->metadata.n_chunks++;
110+
#endif /* NDEBUG */
111+
}
112+
113+
current_chunk->next = NULL;
114+
pool->metadata.free_list = ptr; // address of the first chunk
115+
}
116+
117+
umf_ba_pool_t *umf_ba_create(size_t size) {
118+
size_t chunk_size = align_size(size, MEMORY_ALIGNMENT);
119+
size_t mutex_size = align_size(util_mutex_get_size(), MEMORY_ALIGNMENT);
120+
121+
size_t metadata_size = sizeof(struct umf_ba_main_pool_meta_t);
122+
size_t pool_size = sizeof(void *) + metadata_size + mutex_size +
123+
(MINIMUM_CHUNK_COUNT * chunk_size);
124+
if (pool_size < MINIMUM_POOL_SIZE) {
125+
pool_size = MINIMUM_POOL_SIZE;
126+
}
127+
128+
pool_size = align_size(pool_size, ba_os_get_page_size());
129+
130+
umf_ba_pool_t *pool = (umf_ba_pool_t *)ba_os_alloc(pool_size);
131+
if (!pool) {
132+
return NULL;
133+
}
134+
135+
pool->metadata.pool_size = pool_size;
136+
pool->metadata.chunk_size = chunk_size;
137+
pool->next_pool = NULL; // this is the only pool now
138+
#ifndef NDEBUG
139+
pool->metadata.n_pools = 1;
140+
pool->metadata.n_allocs = 0;
141+
pool->metadata.n_chunks = 0;
142+
#endif /* NDEBUG */
143+
144+
char *data_ptr = (char *)&pool->data;
145+
size_t size_left = pool_size - offsetof(umf_ba_pool_t, data);
146+
147+
// allocate and init free_lock
148+
pool->metadata.free_lock = util_mutex_init(data_ptr);
149+
if (!pool->metadata.free_lock) {
150+
ba_os_free(pool, pool_size);
151+
return NULL;
152+
}
153+
154+
data_ptr += mutex_size; // free_lock is here
155+
size_left -= mutex_size; // for free_lock
156+
157+
pool->metadata.free_list = NULL;
158+
ba_divide_memory_into_chunks(pool, data_ptr, size_left);
159+
160+
return pool;
161+
}
162+
163+
void *umf_ba_alloc(umf_ba_pool_t *pool) {
164+
util_mutex_lock(pool->metadata.free_lock);
165+
if (pool->metadata.free_list == NULL) {
166+
umf_ba_next_pool_t *new_pool =
167+
(umf_ba_next_pool_t *)ba_os_alloc(pool->metadata.pool_size);
168+
if (!new_pool) {
169+
util_mutex_unlock(pool->metadata.free_lock);
170+
return NULL;
171+
}
172+
173+
// add the new pool to the list of pools
174+
new_pool->next_pool = pool->next_pool;
175+
pool->next_pool = new_pool;
176+
177+
#ifndef NDEBUG
178+
pool->metadata.n_pools++;
179+
#endif /* NDEBUG */
180+
181+
size_t size =
182+
pool->metadata.pool_size - offsetof(umf_ba_next_pool_t, data);
183+
ba_divide_memory_into_chunks(pool, &new_pool->data, size);
184+
}
185+
186+
umf_ba_chunk_t *chunk = pool->metadata.free_list;
187+
pool->metadata.free_list = pool->metadata.free_list->next;
188+
#ifndef NDEBUG
189+
pool->metadata.n_allocs++;
190+
ba_debug_checks(pool);
191+
#endif /* NDEBUG */
192+
util_mutex_unlock(pool->metadata.free_lock);
193+
194+
return chunk;
195+
}
196+
197+
void umf_ba_free(umf_ba_pool_t *pool, void *ptr) {
198+
if (ptr == NULL) {
199+
return;
200+
}
201+
202+
umf_ba_chunk_t *chunk = (umf_ba_chunk_t *)ptr;
203+
204+
util_mutex_lock(pool->metadata.free_lock);
205+
chunk->next = pool->metadata.free_list;
206+
pool->metadata.free_list = chunk;
207+
#ifndef NDEBUG
208+
pool->metadata.n_allocs--;
209+
ba_debug_checks(pool);
210+
#endif /* NDEBUG */
211+
util_mutex_unlock(pool->metadata.free_lock);
212+
}
213+
214+
void umf_ba_destroy(umf_ba_pool_t *pool) {
215+
#ifndef NDEBUG
216+
assert(pool->metadata.n_allocs == 0);
217+
ba_debug_checks(pool);
218+
#endif /* NDEBUG */
219+
size_t size = pool->metadata.pool_size;
220+
umf_ba_next_pool_t *current_pool;
221+
umf_ba_next_pool_t *next_pool = pool->next_pool;
222+
while (next_pool) {
223+
current_pool = next_pool;
224+
next_pool = next_pool->next_pool;
225+
ba_os_free(current_pool, size);
226+
}
227+
228+
util_mutex_destroy_not_free(pool->metadata.free_lock);
229+
ba_os_free(pool, size);
230+
}

src/base_alloc/base_alloc.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
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_H
11+
#define UMF_BASE_ALLOC_H 1
12+
13+
#include <stddef.h>
14+
15+
typedef struct umf_ba_pool_t umf_ba_pool_t;
16+
17+
umf_ba_pool_t *umf_ba_create(size_t size);
18+
void *umf_ba_alloc(umf_ba_pool_t *pool);
19+
void umf_ba_free(umf_ba_pool_t *pool, void *ptr);
20+
void umf_ba_destroy(umf_ba_pool_t *pool);
21+
22+
#endif /* UMF_BASE_ALLOC_H */

src/base_alloc/base_alloc_internal.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+
#ifndef UMF_BASE_ALLOC_INTERNAL_H
9+
#define UMF_BASE_ALLOC_INTERNAL_H 1
10+
11+
#include <stddef.h>
12+
13+
#ifndef _WIN32
14+
#include <unistd.h>
15+
#endif
16+
17+
void *ba_os_alloc(size_t size);
18+
void ba_os_free(void *ptr, size_t size);
19+
size_t ba_os_get_page_size(void);
20+
21+
#endif /* UMF_BASE_ALLOC_INTERNAL_H */

src/base_alloc/base_alloc_linux.c

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
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 <stdio.h>
9+
#include <sys/mman.h>
10+
#include <sys/syscall.h>
11+
#include <unistd.h>
12+
13+
#include "base_alloc.h"
14+
15+
void *ba_os_alloc(size_t size) {
16+
return mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS,
17+
-1, 0);
18+
}
19+
20+
void ba_os_free(void *ptr, size_t size) { munmap(ptr, size); }
21+
22+
size_t ba_os_get_page_size(void) { return sysconf(_SC_PAGE_SIZE); }

src/base_alloc/base_alloc_windows.c

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+
#include <windows.h>
9+
10+
void *ba_os_alloc(size_t size) {
11+
return VirtualAlloc(NULL, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
12+
}
13+
14+
void ba_os_free(void *ptr, size_t size) { VirtualFree(ptr, 0, MEM_RELEASE); }
15+
16+
size_t ba_os_get_page_size(void) { return 4096; /* TODO */ }
17+
18+
//
19+
// TODO:
20+
// 1) implement ba_os_get_page_size()
21+
//

test/CMakeLists.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ function(add_umf_test)
3939
target_include_directories(${TEST_TARGET_NAME} PRIVATE
4040
${UMF_CMAKE_SOURCE_DIR}/include
4141
${UMF_CMAKE_SOURCE_DIR}/src
42+
${UMF_CMAKE_SOURCE_DIR}/src/base_alloc
4243
${UMF_TEST_DIR}/common
4344
${UMF_TEST_DIR})
4445

@@ -109,3 +110,11 @@ if(UMF_BUILD_OS_MEMORY_PROVIDER AND LINUX) # OS-specific functions are implement
109110
SRCS provider_os_memory_config.cpp
110111
LIBS umf_utils numa)
111112
endif()
113+
114+
# TODO: add Windows tests
115+
if(LINUX)
116+
# the base_alloc test uses linux pthreads
117+
add_umf_test(NAME base_alloc
118+
SRCS test_base_alloc.c
119+
LIBS umf_utils)
120+
endif()

test/common/test_helpers.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include <stdarg.h>
1010
#include <stdio.h>
11+
#include <stdlib.h>
1112
#include <umf/base.h>
1213
#include <umf/memory_pool.h>
1314
#include <umf/memory_provider_ops.h>

0 commit comments

Comments
 (0)