Skip to content

Commit 162f699

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 162f699

File tree

10 files changed

+340
-2
lines changed

10 files changed

+340
-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: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
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 ba_mem_chunk_t ba_mem_chunk_t;
25+
26+
// memory chunk of size 'chunk_size'
27+
struct ba_mem_chunk_t {
28+
ba_mem_chunk_t *next;
29+
};
30+
31+
struct umf_ba_pool_meta_t {
32+
// address of the beginning of this pool (allocated region)
33+
umf_ba_pool_t *pool_addr;
34+
// address of the beginning of the next pool (a list of allocated pools to be freed in umf_ba_destroy())
35+
umf_ba_pool_t *next_pool;
36+
37+
// the following fields are set and used only in the main (the first) pool
38+
size_t pool_size; // size of each pool (argument of each ba_os_alloc() call)
39+
size_t pool_usable_size; // usable size of each pool
40+
size_t chunk_size; // size of all memory chunks in this pool
41+
os_mutex_t *free_lock; // lock of free_list
42+
ba_mem_chunk_t *free_list; // list of free chunks
43+
};
44+
45+
struct umf_ba_pool_t {
46+
struct umf_ba_pool_meta_t metadata;
47+
char data[];
48+
};
49+
50+
// ba_divide_memory_into_chunks - divide given memory into chunks of chunk_size and add them to the free_list
51+
static void ba_divide_memory_into_chunks(umf_ba_pool_t *pool, void *ptr,
52+
size_t size) {
53+
assert(pool->metadata.free_list == NULL);
54+
assert(size > pool->metadata.chunk_size);
55+
56+
char *pool_memory = ptr;
57+
size_t size_left = size;
58+
59+
ba_mem_chunk_t *current_chunk = (ba_mem_chunk_t *)pool_memory;
60+
ba_mem_chunk_t *prev_chunk = current_chunk;
61+
62+
while (size_left >= pool->metadata.chunk_size) {
63+
current_chunk = (ba_mem_chunk_t *)pool_memory;
64+
prev_chunk->next = current_chunk;
65+
66+
pool_memory += pool->metadata.chunk_size;
67+
size_left -= pool->metadata.chunk_size;
68+
prev_chunk = current_chunk;
69+
}
70+
71+
current_chunk->next = NULL;
72+
pool->metadata.free_list = ptr; // address of the first chunk
73+
}
74+
75+
umf_ba_pool_t *umf_ba_create(size_t size) {
76+
size_t chunk_size = align_size(size, MEMORY_ALIGNMENT);
77+
size_t mutex_size = align_size(util_mutex_get_size(), MEMORY_ALIGNMENT);
78+
size_t metadata_size = offsetof(struct umf_ba_pool_t, data);
79+
size_t pool_size =
80+
metadata_size + mutex_size + (MINIMUM_CHUNK_COUNT * chunk_size);
81+
if (pool_size < MINIMUM_POOL_SIZE) {
82+
pool_size = MINIMUM_POOL_SIZE;
83+
}
84+
85+
pool_size = align_size(pool_size, ba_os_get_page_size());
86+
87+
umf_ba_pool_t *pool = (umf_ba_pool_t *)ba_os_alloc(pool_size);
88+
if (!pool) {
89+
return NULL;
90+
}
91+
92+
pool->metadata.pool_addr = pool;
93+
pool->metadata.pool_size = pool_size;
94+
pool->metadata.pool_usable_size = pool_size - metadata_size;
95+
pool->metadata.next_pool = NULL; // this is the only pool now
96+
pool->metadata.chunk_size = chunk_size;
97+
98+
char *pool_memory = (char *)&pool->data;
99+
size_t size_left = pool->metadata.pool_usable_size;
100+
101+
// allocate and init free_lock
102+
pool->metadata.free_lock = util_mutex_init(pool_memory);
103+
if (!pool->metadata.free_lock) {
104+
return NULL;
105+
}
106+
107+
pool_memory += mutex_size; // free_lock is here
108+
size_left -= mutex_size; // for free_lock
109+
110+
pool->metadata.free_list = NULL;
111+
ba_divide_memory_into_chunks(pool, pool_memory, size_left);
112+
113+
return pool;
114+
}
115+
116+
void *umf_ba_alloc(umf_ba_pool_t *pool) {
117+
util_mutex_lock(pool->metadata.free_lock);
118+
if (pool->metadata.free_list == NULL) {
119+
umf_ba_pool_t *new_pool =
120+
(umf_ba_pool_t *)ba_os_alloc(pool->metadata.pool_size);
121+
if (!new_pool) {
122+
util_mutex_unlock(pool->metadata.free_lock);
123+
return NULL;
124+
}
125+
126+
// add the new pool to the list of pools
127+
new_pool->metadata.pool_addr = new_pool;
128+
new_pool->metadata.next_pool = pool->metadata.next_pool;
129+
pool->metadata.next_pool = new_pool;
130+
131+
ba_divide_memory_into_chunks(pool, &new_pool->data,
132+
pool->metadata.pool_usable_size);
133+
}
134+
135+
ba_mem_chunk_t *chunk = pool->metadata.free_list;
136+
pool->metadata.free_list = pool->metadata.free_list->next;
137+
util_mutex_unlock(pool->metadata.free_lock);
138+
139+
return chunk;
140+
}
141+
142+
void umf_ba_free(umf_ba_pool_t *pool, void *ptr) {
143+
if (ptr == NULL) {
144+
return;
145+
}
146+
147+
ba_mem_chunk_t *chunk = (ba_mem_chunk_t *)ptr;
148+
149+
util_mutex_lock(pool->metadata.free_lock);
150+
chunk->next = pool->metadata.free_list;
151+
pool->metadata.free_list = chunk;
152+
util_mutex_unlock(pool->metadata.free_lock);
153+
}
154+
155+
void umf_ba_destroy(umf_ba_pool_t *pool) {
156+
size_t size = pool->metadata.pool_size;
157+
umf_ba_pool_t *current_pool;
158+
umf_ba_pool_t *next_pool = pool->metadata.next_pool;
159+
while (next_pool) {
160+
current_pool = next_pool;
161+
next_pool = next_pool->metadata.next_pool;
162+
ba_os_free(current_pool->metadata.pool_addr, size);
163+
}
164+
165+
util_mutex_destroy_not_free(pool->metadata.free_lock);
166+
ba_os_free(pool, size);
167+
}

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>

test/test_base_alloc.c

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
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.h"
16+
#include "test_helpers.h"
17+
18+
#define NTHREADS 10
19+
#define ITERATIONS 1000
20+
#define ALLOCATION_SIZE 16
21+
22+
static void *start_routine(void *arg) {
23+
unsigned char *ptrs[ITERATIONS];
24+
umf_ba_pool_t *pool = (umf_ba_pool_t *)arg;
25+
26+
long TID = syscall(SYS_gettid);
27+
28+
for (int i = 0; i < ITERATIONS; i++) {
29+
ptrs[i] = umf_ba_alloc(pool);
30+
memset(ptrs[i], (i + TID) & 0xFF, ALLOCATION_SIZE);
31+
}
32+
33+
for (int i = 0; i < ITERATIONS; i++) {
34+
for (int k = 0; k < ALLOCATION_SIZE; k++) {
35+
if (*(ptrs[i] + k) != ((i + TID) & 0xFF)) {
36+
fprintf(stderr,
37+
"i = %i k = %i, *(ptrs[i] + k) = %i != ((i + TID) & "
38+
"0xFF) = %li\n",
39+
i, k, *(ptrs[i] + k), ((i + TID) & 0xFF));
40+
}
41+
assert(*(ptrs[i] + k) == ((i + TID) & 0xFF));
42+
}
43+
umf_ba_free(pool, ptrs[i]);
44+
}
45+
46+
return NULL;
47+
}
48+
49+
int main() {
50+
pthread_t thread[NTHREADS];
51+
umf_ba_pool_t *pool = umf_ba_create(ALLOCATION_SIZE);
52+
53+
for (int i = 0; i < NTHREADS; i++) {
54+
int ret = pthread_create(&thread[i], NULL, start_routine, pool);
55+
if (ret) {
56+
fprintf(stderr, "pthread_create() failed!\n");
57+
UT_ASSERTeq(ret, 0);
58+
}
59+
}
60+
61+
for (int i = 0; i < NTHREADS; i++) {
62+
void *retval;
63+
int ret = pthread_join(thread[i], &retval);
64+
if (ret) {
65+
fprintf(stderr, "pthread_join() failed!\n");
66+
UT_ASSERTeq(ret, 0);
67+
}
68+
}
69+
70+
return 0;
71+
}

0 commit comments

Comments
 (0)