Skip to content

Commit 7090d5e

Browse files
committed
Add proxy library
Signed-off-by: Lukasz Dorau <[email protected]>
1 parent c02132a commit 7090d5e

File tree

10 files changed

+507
-0
lines changed

10 files changed

+507
-0
lines changed

.github/workflows/codeql.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,11 @@ jobs:
2222
- os: ubuntu-latest
2323
nproc: $(nproc)
2424
pool_scalable: 'ON'
25+
pool_jemalloc: 'ON'
2526
- os: windows-latest
2627
nproc: $Env:NUMBER_OF_PROCESSORS
2728
pool_scalable: 'OFF'
29+
pool_jemalloc: 'OFF'
2830
runs-on: ${{matrix.os}}
2931

3032
steps:
@@ -53,6 +55,7 @@ jobs:
5355
-DUMF_FORMAT_CODE_STYLE=ON
5456
-DUMF_DEVELOPER_MODE=ON
5557
-DUMF_ENABLE_POOL_TRACKING=ON
58+
-DUMF_BUILD_LIBUMF_POOL_JEMALLOC=${{matrix.pool_jemalloc}}
5659
-DUMF_BUILD_LIBUMF_POOL_SCALABLE=${{matrix.pool_scalable}}
5760
5861
- name: Build

src/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,3 +103,6 @@ install(TARGETS umf
103103
)
104104

105105
add_subdirectory(pool)
106+
if(LINUX) # TODO: enable it on Windows
107+
add_subdirectory(proxy_lib)
108+
endif()

src/proxy_lib/CMakeLists.txt

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# Copyright (C) 2023-2024 Intel Corporation
2+
# Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT.
3+
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4+
5+
include(${UMF_CMAKE_SOURCE_DIR}/cmake/helpers.cmake)
6+
7+
set(PROXY_LIBS umf jemalloc_pool)
8+
9+
set(PROXY_SOURCES
10+
proxy_lib.c
11+
)
12+
13+
set(PROXY_SOURCES_LINUX
14+
proxy_lib_linux.c
15+
)
16+
17+
set(PROXY_SOURCES_WINDOWS
18+
proxy_lib_windows.c
19+
)
20+
21+
set(PROXY_SOURCES_MACOSX
22+
proxy_lib_linux.c
23+
)
24+
25+
if(LINUX)
26+
set(PROXY_SOURCES ${PROXY_SOURCES} ${PROXY_SOURCES_LINUX})
27+
elseif(WINDOWS)
28+
set(PROXY_SOURCES ${PROXY_SOURCES} ${PROXY_SOURCES_WINDOWS})
29+
elseif(MACOSX)
30+
set(PROXY_SOURCES ${PROXY_SOURCES} ${PROXY_SOURCES_MACOSX})
31+
endif()
32+
33+
add_umf_library(NAME umf_proxy
34+
TYPE SHARED
35+
SRCS ${PROXY_SOURCES}
36+
LIBS ${PROXY_LIBS}
37+
LINUX_MAP_FILE ${CMAKE_CURRENT_SOURCE_DIR}/proxy_lib.map
38+
WINDOWS_DEF_FILE ${CMAKE_CURRENT_SOURCE_DIR}/proxy_lib.def)
39+
40+
add_library(${PROJECT_NAME}::proxy ALIAS umf_proxy)
41+
42+
target_include_directories(umf_proxy PUBLIC
43+
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
44+
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/src/utils>
45+
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
46+
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
47+
)
48+
49+
install(TARGETS umf_proxy
50+
EXPORT ${PROJECT_NAME}-targets)

src/proxy_lib/proxy_lib.c

Lines changed: 291 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,291 @@
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+
// UMF proxy library
9+
10+
#include <assert.h>
11+
#include <stdio.h>
12+
#include <stdlib.h>
13+
#include <string.h>
14+
15+
#include <umf/memory_pool.h>
16+
#include <umf/memory_provider.h>
17+
#include <umf/pools/pool_jemalloc.h>
18+
#include <umf/providers/provider_os_memory.h>
19+
20+
#include "base_alloc_linear.h"
21+
#include "proxy_lib.h"
22+
#include "utils_common.h"
23+
#include "utils_concurrency.h"
24+
25+
/*
26+
* The UMF proxy library uses three memory allocators:
27+
* 1) the internal linear base allocator (LBA) based on the anonymous mapped memory
28+
* 2) the system allocator (libc malloc on Linux for example)
29+
* 3) the main one - UMF pool allocator.
30+
*
31+
* Ad 1)
32+
* The internal linear base allocator (LBA) is used from the very beginning
33+
* to the start of the constructor of the proxy library to allocate memory
34+
* for OS specific structures used during loading and starting applications
35+
* (for example _dl_init() and _dl_fini() on Linux storing arrays of all
36+
* constructors and destructors that have to be called). That memory will be leaked
37+
* on purpose (we believe OS will free it), because we cannot free the memory
38+
* containing array of destructors that have to be called at the end.
39+
* There are only 4 such allocations on Linux now:
40+
* - realloc(0x0, 480)
41+
* - malloc(20)
42+
* - malloc(24)
43+
* - malloc(72704)
44+
*
45+
* Ad 2)
46+
* The system allocator is used from the start of the constructor of the proxy library
47+
* to the creation of a UMF pool to allocate memory needed by umfMemoryProviderCreate()
48+
* and umfPoolCreate(). It will be freed by the system allocator.
49+
*
50+
* Ad 3)
51+
* The UMF pool allocator (the main one) is used from the creation to the destruction
52+
* of a UMF pool to allocate memory needed by an application. It should be freed
53+
* by an application.
54+
*/
55+
56+
static UTIL_ONCE_FLAG Base_alloc_initialized = UTIL_ONCE_FLAG_INIT;
57+
static UTIL_ONCE_FLAG alloc_hooks_are_initialized = UTIL_ONCE_FLAG_INIT;
58+
59+
static umf_ba_linear_pool_t *Base_alloc = NULL;
60+
static int Constructors_started = 0;
61+
static umf_memory_provider_handle_t OS_memory_provider = NULL;
62+
static umf_memory_pool_handle_t Proxy_pool = NULL;
63+
64+
// it protects us from recursion in umfPool*()
65+
static __TLS int was_called_from_umfPool = 0;
66+
67+
malloc_func_t System_malloc_func;
68+
calloc_func_t System_calloc_func;
69+
realloc_func_t System_realloc_func;
70+
free_func_t System_free_func;
71+
aligned_alloc_func_t System_aligned_alloc_func;
72+
73+
/*****************************************************************************/
74+
/*** The constructor and destructor of the proxy library *********************/
75+
/*****************************************************************************/
76+
77+
void proxy_lib_create_common(void) {
78+
// The constructors of libraries were started. Stop using the linear base
79+
// allocator that does not free the memory (we leak it on purpose in order
80+
// to not free the memory used by _dl_fini() containing a map (array)
81+
// of all destructors) and start using the system allocator.
82+
Constructors_started = 1;
83+
84+
umf_os_memory_provider_params_t os_params =
85+
umfOsMemoryProviderParamsDefault();
86+
enum umf_result_t umf_result;
87+
88+
umf_result = umfMemoryProviderCreate(umfOsMemoryProviderOps(), &os_params,
89+
&OS_memory_provider);
90+
if (umf_result != UMF_RESULT_SUCCESS) {
91+
fprintf(stderr, "error: creating OS memory provider failed\n");
92+
exit(-1);
93+
}
94+
95+
umf_result = umfPoolCreate(umfJemallocPoolOps(), OS_memory_provider, NULL,
96+
0, &Proxy_pool);
97+
if (umf_result != UMF_RESULT_SUCCESS) {
98+
fprintf(stderr, "error: creating jemalloc pool manager failed\n");
99+
exit(-1);
100+
}
101+
// The UMF pool created (Proxy_pool != NULL). Stop using the system allocator
102+
// and start using the UMF pool allocator.
103+
}
104+
105+
void proxy_lib_destroy_common(void) {
106+
umf_memory_pool_handle_t pool = Proxy_pool;
107+
Proxy_pool = NULL;
108+
umfPoolDestroy(pool);
109+
umfMemoryProviderDestroy(OS_memory_provider);
110+
}
111+
112+
/*****************************************************************************/
113+
/*** The linear base allocator functions *************************************/
114+
/*****************************************************************************/
115+
116+
static void proxy_lib_ba_init(void) { Base_alloc = umf_ba_linear_create(0); }
117+
118+
static inline void *ba_malloc(size_t size) {
119+
util_init_once(&Base_alloc_initialized, proxy_lib_ba_init);
120+
return umf_ba_linear_alloc(Base_alloc, size);
121+
}
122+
123+
static inline void *ba_calloc(size_t nmemb, size_t size) {
124+
util_init_once(&Base_alloc_initialized, proxy_lib_ba_init);
125+
// umf_ba_linear_alloc() returns zeroed memory
126+
return umf_ba_linear_alloc(Base_alloc, nmemb * size);
127+
}
128+
129+
static inline void *ba_realloc(void *ptr, size_t size) {
130+
util_init_once(&Base_alloc_initialized, proxy_lib_ba_init);
131+
132+
if (size == 0) {
133+
// it means free(ptr), but this BA does not free the memory
134+
return NULL;
135+
}
136+
137+
if (ptr == NULL) {
138+
return umf_ba_linear_alloc(Base_alloc, size);
139+
}
140+
141+
size_t max_size = umf_ba_linear_pool_contains_pointer(Base_alloc, ptr);
142+
assert(max_size > 0); // assert that pool contains the pointer
143+
144+
void *new_ptr = umf_ba_linear_alloc(Base_alloc, size);
145+
if (!new_ptr) {
146+
return NULL;
147+
}
148+
149+
if (size > max_size) {
150+
size = max_size;
151+
}
152+
153+
memcpy(new_ptr, ptr, size);
154+
155+
return new_ptr;
156+
}
157+
158+
static inline void *ba_aligned_alloc(size_t alignment, size_t size) {
159+
util_init_once(&Base_alloc_initialized, proxy_lib_ba_init);
160+
void *ptr = umf_ba_linear_alloc(Base_alloc, size + alignment);
161+
return align_ptr(ptr, alignment);
162+
}
163+
164+
/*****************************************************************************/
165+
/*** The system allocator functions ******************************************/
166+
/*****************************************************************************/
167+
168+
static inline void *system_malloc(size_t size) {
169+
util_init_once(&alloc_hooks_are_initialized, proxy_lib_system_alloc_init);
170+
return System_malloc_func(size);
171+
}
172+
173+
static inline void *system_calloc(size_t nmemb, size_t size) {
174+
util_init_once(&alloc_hooks_are_initialized, proxy_lib_system_alloc_init);
175+
return System_calloc_func(nmemb, size);
176+
}
177+
178+
static inline void *system_realloc(void *ptr, size_t size) {
179+
util_init_once(&alloc_hooks_are_initialized, proxy_lib_system_alloc_init);
180+
return System_realloc_func(ptr, size);
181+
}
182+
183+
static inline void system_free(void *ptr) {
184+
util_init_once(&alloc_hooks_are_initialized, proxy_lib_system_alloc_init);
185+
System_free_func(ptr);
186+
}
187+
188+
static inline void *system_aligned_alloc(size_t alignment, size_t size) {
189+
util_init_once(&alloc_hooks_are_initialized, proxy_lib_system_alloc_init);
190+
if (!System_aligned_alloc_func) {
191+
return NULL; // unsupported
192+
}
193+
return System_aligned_alloc_func(alignment, size);
194+
}
195+
196+
/*****************************************************************************/
197+
/*** The UMF pool allocator functions (the public API) ***********************/
198+
/*****************************************************************************/
199+
200+
void *malloc(size_t size) {
201+
if (!was_called_from_umfPool && Proxy_pool) {
202+
was_called_from_umfPool = 1;
203+
void *ptr = umfPoolMalloc(Proxy_pool, size);
204+
was_called_from_umfPool = 0;
205+
return ptr;
206+
}
207+
208+
if (Constructors_started) {
209+
return system_malloc(size);
210+
}
211+
212+
return ba_malloc(size);
213+
}
214+
215+
void *calloc(size_t nmemb, size_t size) {
216+
if (!was_called_from_umfPool && Proxy_pool) {
217+
was_called_from_umfPool = 1;
218+
void *ptr = umfPoolCalloc(Proxy_pool, nmemb, size);
219+
was_called_from_umfPool = 0;
220+
return ptr;
221+
}
222+
223+
if (Constructors_started) {
224+
return system_calloc(nmemb, size);
225+
}
226+
227+
return ba_calloc(nmemb, size);
228+
}
229+
230+
void *realloc(void *ptr, size_t size) {
231+
if (!was_called_from_umfPool && Proxy_pool) {
232+
was_called_from_umfPool = 1;
233+
void *new_ptr = umfPoolRealloc(Proxy_pool, ptr, size);
234+
was_called_from_umfPool = 0;
235+
return new_ptr;
236+
}
237+
238+
if (Constructors_started) {
239+
return system_realloc(ptr, size);
240+
}
241+
242+
return ba_realloc(ptr, size);
243+
}
244+
245+
void free(void *ptr) {
246+
if (ptr == NULL) {
247+
return;
248+
}
249+
250+
umf_memory_pool_handle_t pool = umfPoolByPtr(ptr);
251+
if (pool) {
252+
enum umf_result_t umf_result = umfPoolFree(pool, ptr);
253+
if (umf_result != UMF_RESULT_SUCCESS) {
254+
fprintf(stderr, "error: umfPoolFree() failed\n");
255+
assert(0);
256+
}
257+
return;
258+
}
259+
260+
if (Constructors_started) {
261+
system_free(ptr);
262+
}
263+
264+
return;
265+
}
266+
267+
void *aligned_alloc(size_t alignment, size_t size) {
268+
if (!was_called_from_umfPool && Proxy_pool) {
269+
was_called_from_umfPool = 1;
270+
void *ptr = umfPoolAlignedMalloc(Proxy_pool, size, alignment);
271+
was_called_from_umfPool = 0;
272+
return ptr;
273+
}
274+
275+
if (Constructors_started) {
276+
return system_aligned_alloc(alignment, size);
277+
}
278+
279+
return ba_aligned_alloc(alignment, size);
280+
}
281+
282+
size_t malloc_usable_size(void *ptr) {
283+
if (!was_called_from_umfPool && Proxy_pool) {
284+
was_called_from_umfPool = 1;
285+
size_t size = umfPoolMallocUsableSize(Proxy_pool, ptr);
286+
was_called_from_umfPool = 0;
287+
return size;
288+
}
289+
290+
return 0; // unsupported in this case
291+
}

src/proxy_lib/proxy_lib.def

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
;;;; Begin Copyright Notice
2+
; Copyright (C) 2024 Intel Corporation
3+
; Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT.
4+
; SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
5+
;;;; End Copyright Notice
6+
7+
LIBRARY UMF_PROXY
8+
EXPORTS
9+
aligned_alloc
10+
calloc
11+
free
12+
malloc
13+
malloc_usable_size
14+
realloc

0 commit comments

Comments
 (0)