PyCyBase 0.1.8.post4__cp313-cp313-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cbase/__infra__.pxd +6 -0
- cbase/__init__.pxd +6 -0
- cbase/__init__.py +28 -0
- cbase/allocator_protocol/__infra__.pxd +58 -0
- cbase/allocator_protocol/__init__.pxd +58 -0
- cbase/allocator_protocol/__init__.py +45 -0
- cbase/allocator_protocol/c_allocator_protocol.c +31818 -0
- cbase/allocator_protocol/c_allocator_protocol.cp313-win_amd64.pyd +0 -0
- cbase/allocator_protocol/c_allocator_protocol.h +352 -0
- cbase/allocator_protocol/c_allocator_protocol.pxd +64 -0
- cbase/allocator_protocol/c_allocator_protocol.pyi +144 -0
- cbase/allocator_protocol/c_allocator_protocol.pyx +209 -0
- cbase/allocator_protocol/c_heap_allocator.c +38144 -0
- cbase/allocator_protocol/c_heap_allocator.cp313-win_amd64.pyd +0 -0
- cbase/allocator_protocol/c_heap_allocator.h +444 -0
- cbase/allocator_protocol/c_heap_allocator.pxd +97 -0
- cbase/allocator_protocol/c_heap_allocator.pyi +161 -0
- cbase/allocator_protocol/c_heap_allocator.pyx +300 -0
- cbase/allocator_protocol/c_nt_shm_allocator.c +33399 -0
- cbase/allocator_protocol/c_nt_shm_allocator.cp313-win_amd64.pyd +0 -0
- cbase/allocator_protocol/c_nt_shm_allocator.h +996 -0
- cbase/allocator_protocol/c_nt_shm_allocator.pxd +102 -0
- cbase/allocator_protocol/c_nt_shm_allocator.pyx +223 -0
- cbase/allocator_protocol/c_shm_allocator.h +1168 -0
- cbase/allocator_protocol/c_shm_allocator.pxd +142 -0
- cbase/allocator_protocol/c_shm_allocator.pyi +299 -0
- cbase/allocator_protocol/c_shm_allocator.pyx +442 -0
- cbase/allocator_protocol/c_shm_comp.c +4643 -0
- cbase/allocator_protocol/c_shm_comp.cp313-win_amd64.pyd +0 -0
- cbase/allocator_protocol/c_shm_comp.h +49 -0
- cbase/allocator_protocol/c_shm_comp.pxd +38 -0
- cbase/allocator_protocol/c_shm_comp.pyx +1 -0
- cbase/backports/__infra__.pxd +17 -0
- cbase/backports/__init__.pxd +17 -0
- cbase/backports/__init__.py +9 -0
- cbase/backports/pydict.h +55 -0
- cbase/backports/pydict.pxd +5 -0
- cbase/backports/pylong.c +9946 -0
- cbase/backports/pylong.cp313-win_amd64.pyd +0 -0
- cbase/backports/pylong.h +79 -0
- cbase/backports/pylong.pxd +23 -0
- cbase/backports/pylong.pyx +61 -0
- cbase/bytemap/__infra__.pxd +42 -0
- cbase/bytemap/__init__.pxd +42 -0
- cbase/bytemap/__init__.py +21 -0
- cbase/bytemap/c_bytemap.c +52076 -0
- cbase/bytemap/c_bytemap.cp313-win_amd64.pyd +0 -0
- cbase/bytemap/c_bytemap.h +867 -0
- cbase/bytemap/c_bytemap.pxd +370 -0
- cbase/bytemap/c_bytemap.pyx +1758 -0
- cbase/bytemap/xxh3.h +55 -0
- cbase/bytemap/xxhash.c +42 -0
- cbase/bytemap/xxhash.h +7487 -0
- cbase/env.c +11109 -0
- cbase/env.cp313-win_amd64.pyd +0 -0
- cbase/env.pxd +8 -0
- cbase/env.pyi +48 -0
- cbase/env.pyx +46 -0
- cbase/includes/cbase/allocator_protocol/c_allocator_protocol.c +31818 -0
- cbase/includes/cbase/allocator_protocol/c_allocator_protocol.h +352 -0
- cbase/includes/cbase/allocator_protocol/c_heap_allocator.c +38144 -0
- cbase/includes/cbase/allocator_protocol/c_heap_allocator.h +444 -0
- cbase/includes/cbase/allocator_protocol/c_nt_shm_allocator.c +33399 -0
- cbase/includes/cbase/allocator_protocol/c_nt_shm_allocator.h +996 -0
- cbase/includes/cbase/allocator_protocol/c_shm_allocator.h +1168 -0
- cbase/includes/cbase/allocator_protocol/c_shm_comp.c +4643 -0
- cbase/includes/cbase/allocator_protocol/c_shm_comp.h +49 -0
- cbase/includes/cbase/backports/pydict.h +55 -0
- cbase/includes/cbase/backports/pylong.c +9946 -0
- cbase/includes/cbase/backports/pylong.h +79 -0
- cbase/includes/cbase/bytemap/c_bytemap.c +52076 -0
- cbase/includes/cbase/bytemap/c_bytemap.h +867 -0
- cbase/includes/cbase/bytemap/xxh3.h +55 -0
- cbase/includes/cbase/bytemap/xxhash.c +42 -0
- cbase/includes/cbase/bytemap/xxhash.h +7487 -0
- cbase/includes/cbase/env.c +11109 -0
- cbase/includes/cbase/int128.h +81 -0
- cbase/includes/cbase/intern_string/c_intern_string.c +28081 -0
- cbase/includes/cbase/intern_string/c_intern_string.h +614 -0
- cbase/includes/cbase/nt/pthread_nt_compat.h +102 -0
- cbase/includes/cbase/nt/regex_nt_compat.h +133 -0
- cbase/includes/cbase/nt/time_sched_nt_compat.h +32 -0
- cbase/int128.h +81 -0
- cbase/int128.pxd +15 -0
- cbase/intern_string/BENCHMARK.md +157 -0
- cbase/intern_string/__infra__.pxd +11 -0
- cbase/intern_string/__init__.pxd +11 -0
- cbase/intern_string/__init__.py +19 -0
- cbase/intern_string/c_intern_string.c +28081 -0
- cbase/intern_string/c_intern_string.cp313-win_amd64.pyd +0 -0
- cbase/intern_string/c_intern_string.h +614 -0
- cbase/intern_string/c_intern_string.pxd +118 -0
- cbase/intern_string/c_intern_string.pyi +376 -0
- cbase/intern_string/c_intern_string.pyx +765 -0
- cbase/nt/pthread_nt_compat.h +102 -0
- cbase/nt/regex_nt_compat.h +133 -0
- cbase/nt/time_sched_nt_compat.h +32 -0
- pycybase-0.1.8.post4.dist-info/METADATA +180 -0
- pycybase-0.1.8.post4.dist-info/RECORD +101 -0
- pycybase-0.1.8.post4.dist-info/WHEEL +5 -0
- pycybase-0.1.8.post4.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,1168 @@
|
|
|
1
|
+
#ifndef _GNU_SOURCE
|
|
2
|
+
#define _GNU_SOURCE
|
|
3
|
+
#endif
|
|
4
|
+
#ifndef AP_SHM_C_SHM_ALLOCATOR_H
|
|
5
|
+
#define AP_SHM_C_SHM_ALLOCATOR_H
|
|
6
|
+
|
|
7
|
+
#include <dirent.h>
|
|
8
|
+
#include <errno.h>
|
|
9
|
+
#include <fcntl.h>
|
|
10
|
+
#include <limits.h>
|
|
11
|
+
#include <pthread.h>
|
|
12
|
+
#include <signal.h>
|
|
13
|
+
#include <stddef.h>
|
|
14
|
+
#include <stdint.h>
|
|
15
|
+
#include <stdio.h>
|
|
16
|
+
#include <stdlib.h>
|
|
17
|
+
#include <string.h>
|
|
18
|
+
#include <sys/mman.h>
|
|
19
|
+
#include <sys/stat.h>
|
|
20
|
+
#include <unistd.h>
|
|
21
|
+
|
|
22
|
+
// Configuration
|
|
23
|
+
|
|
24
|
+
#ifndef AP_SHM_AUTOPAGE_CAPACITY
|
|
25
|
+
#define AP_SHM_AUTOPAGE_CAPACITY (64 * 1024) /* 64 KiB */
|
|
26
|
+
#endif
|
|
27
|
+
|
|
28
|
+
#ifndef AP_SHM_AUTOPAGE_CAPACITY_MAX
|
|
29
|
+
#define AP_SHM_AUTOPAGE_CAPACITY_MAX (16 * 1024 * 1024) /* 16 MiB */
|
|
30
|
+
#endif
|
|
31
|
+
|
|
32
|
+
#ifndef AP_SHM_AUTOPAGE_ALIGNMENT
|
|
33
|
+
#define AP_SHM_AUTOPAGE_ALIGNMENT (4 * 1024) /* 4 KiB */
|
|
34
|
+
#endif
|
|
35
|
+
|
|
36
|
+
#ifndef AP_SHM_ALLOCATOR_PREFIX
|
|
37
|
+
#define AP_SHM_ALLOCATOR_PREFIX "/c_cbase_shm"
|
|
38
|
+
#endif
|
|
39
|
+
|
|
40
|
+
#ifndef AP_SHM_NAME_LEN
|
|
41
|
+
#define AP_SHM_NAME_LEN 256
|
|
42
|
+
#endif
|
|
43
|
+
|
|
44
|
+
#ifndef AP_SHM_PREFIX_MAX
|
|
45
|
+
#define AP_SHM_PREFIX_MAX 64
|
|
46
|
+
#endif
|
|
47
|
+
|
|
48
|
+
#ifndef AP_SHM_ALLOCATOR_DEFAULT_REGION_SIZE
|
|
49
|
+
#define AP_SHM_ALLOCATOR_DEFAULT_REGION_SIZE (128ULL << 30) /* 128 GiB */
|
|
50
|
+
#endif
|
|
51
|
+
|
|
52
|
+
typedef struct shm_page {
|
|
53
|
+
size_t capacity; // total capacity, excluding metadata
|
|
54
|
+
size_t occupied; // bytes occupied, excluding metadata
|
|
55
|
+
size_t offset;
|
|
56
|
+
struct shm_allocator* allocator;
|
|
57
|
+
struct shm_memory_block* allocated;
|
|
58
|
+
char shm_name[AP_SHM_NAME_LEN];
|
|
59
|
+
char prev_name[AP_SHM_NAME_LEN];
|
|
60
|
+
} shm_page;
|
|
61
|
+
|
|
62
|
+
typedef struct shm_page_ctx {
|
|
63
|
+
shm_page* shm_page;
|
|
64
|
+
int shm_fd;
|
|
65
|
+
char* buffer;
|
|
66
|
+
struct shm_page_ctx* prev;
|
|
67
|
+
} shm_page_ctx;
|
|
68
|
+
|
|
69
|
+
typedef struct shm_memory_block {
|
|
70
|
+
size_t capacity;
|
|
71
|
+
size_t size;
|
|
72
|
+
struct shm_memory_block* next_free;
|
|
73
|
+
struct shm_memory_block* next_allocated;
|
|
74
|
+
shm_page* parent_page;
|
|
75
|
+
char buffer[];
|
|
76
|
+
} shm_memory_block;
|
|
77
|
+
|
|
78
|
+
typedef struct shm_allocator {
|
|
79
|
+
char shm_name[AP_SHM_NAME_LEN];
|
|
80
|
+
size_t pid;
|
|
81
|
+
pthread_mutex_t lock;
|
|
82
|
+
uintptr_t region;
|
|
83
|
+
size_t region_size;
|
|
84
|
+
size_t mapped_size;
|
|
85
|
+
char active_page[AP_SHM_NAME_LEN];
|
|
86
|
+
size_t mapped_pages;
|
|
87
|
+
shm_memory_block* free_list;
|
|
88
|
+
size_t autopage_capacity;
|
|
89
|
+
size_t autopage_capacity_max;
|
|
90
|
+
size_t autopage_alignment;
|
|
91
|
+
char shm_prefix[AP_SHM_PREFIX_MAX];
|
|
92
|
+
} shm_allocator;
|
|
93
|
+
|
|
94
|
+
typedef struct shm_allocator_ctx {
|
|
95
|
+
shm_allocator* shm_allocator;
|
|
96
|
+
int shm_fd;
|
|
97
|
+
shm_page_ctx* active_page;
|
|
98
|
+
} shm_allocator_ctx;
|
|
99
|
+
|
|
100
|
+
// ========== Forward Declarations (public API) ==========
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* @brief Round up size to allocator->autopage_alignment.
|
|
104
|
+
* @param size Requested size in bytes.
|
|
105
|
+
* @return Rounded-up size.
|
|
106
|
+
*/
|
|
107
|
+
static size_t c_shm_page_roundup(shm_allocator* allocator, size_t size);
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* @brief Round up size to pointer alignment.
|
|
111
|
+
* @param size Requested size in bytes.
|
|
112
|
+
* @return Rounded-up size.
|
|
113
|
+
*/
|
|
114
|
+
static size_t c_shm_block_roundup(size_t size);
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* @brief Scan /dev/shm for an entry matching prefix.
|
|
118
|
+
* @param prefix Prefix to match (may include leading '/').
|
|
119
|
+
* @param out Output buffer to receive the found name (with leading '/').
|
|
120
|
+
* @return 0 on success, -1 with errno set otherwise.
|
|
121
|
+
*/
|
|
122
|
+
static inline int c_shm_scan(const char* prefix, char* out);
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* @brief Allocate a new shared-memory page metadata + fd (not yet mapped).
|
|
126
|
+
* @param allocator Allocator metadata (shared) or NULL.
|
|
127
|
+
* @param page_capacity Total bytes including metadata.
|
|
128
|
+
* @return Page context or NULL on failure.
|
|
129
|
+
*/
|
|
130
|
+
static inline shm_page_ctx* c_shm_page_new(shm_allocator* allocator, size_t page_capacity);
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* @brief Map a page into the allocator's reserved region at the next offset.
|
|
134
|
+
* @param allocator Allocator metadata (shared).
|
|
135
|
+
* @param page_ctx Page context to map.
|
|
136
|
+
* @return 0 on success, -1 on error.
|
|
137
|
+
*/
|
|
138
|
+
static inline int c_shm_page_map(shm_allocator* allocator, shm_page_ctx* page_ctx);
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* @brief Reclaim freed blocks of a given page back to unallocated state, with best effort.
|
|
142
|
+
* @param allocator Allocator metadata (shared).
|
|
143
|
+
* @param page_ctx Page context to reclaim from.
|
|
144
|
+
*/
|
|
145
|
+
static inline void c_shm_page_reclaim(shm_allocator* allocator, shm_page_ctx* page_ctx);
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* @brief Extend allocator with a new page, optionally locking.
|
|
149
|
+
* @param ctx Allocator context.
|
|
150
|
+
* @param capacity Payload bytes requested (0 for auto sizing).
|
|
151
|
+
* @param lock Optional mutex to lock during extend.
|
|
152
|
+
* @return New active page context or NULL on error.
|
|
153
|
+
*/
|
|
154
|
+
static inline shm_page_ctx* c_shm_allocator_extend(shm_allocator_ctx* ctx, size_t capacity, pthread_mutex_t* lock);
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* @brief Create a new allocator and reserve address space.
|
|
158
|
+
* @param region_size Virtual region size to reserve (0 for default).
|
|
159
|
+
* @param shm_prefix Custom SHM name prefix (NULL for compile-time default).
|
|
160
|
+
* @return Allocator context or NULL on failure.
|
|
161
|
+
*/
|
|
162
|
+
static inline shm_allocator_ctx* c_shm_allocator_new(size_t region_size, const char* shm_prefix);
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* @brief Tear down allocator, unmapping pages and unlinking SHM objects.
|
|
166
|
+
* @param ctx Allocator context; NULL-safe.
|
|
167
|
+
*/
|
|
168
|
+
static inline void c_shm_allocator_free(shm_allocator_ctx* ctx);
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* @brief Allocate zeroed memory from shared pages.
|
|
172
|
+
* @param ctx Allocator context.
|
|
173
|
+
* @param size Requested size.
|
|
174
|
+
* @param lock Optional mutex controlling allocation.
|
|
175
|
+
* @return Pointer to zeroed memory or NULL on error.
|
|
176
|
+
*/
|
|
177
|
+
static inline void* c_shm_calloc(shm_allocator_ctx* ctx, size_t size, pthread_mutex_t* lock);
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* @brief Allocate memory with optional free-list reuse and page scanning.
|
|
181
|
+
* @param ctx Allocator context.
|
|
182
|
+
* @param size Requested size.
|
|
183
|
+
* @param scan_all_pages Non-zero scans older pages before extending.
|
|
184
|
+
* @param lock Optional mutex controlling allocation.
|
|
185
|
+
* @return Pointer to allocated memory or NULL on error.
|
|
186
|
+
*/
|
|
187
|
+
static inline void* c_shm_request(shm_allocator_ctx* ctx, size_t size, int scan_all_pages, pthread_mutex_t* lock);
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* @brief Return a previously allocated block to the allocator free list.
|
|
191
|
+
* @param ptr User pointer obtained from request/calloc.
|
|
192
|
+
* @param lock Optional mutex controlling free.
|
|
193
|
+
*/
|
|
194
|
+
static inline void c_shm_free(void* ptr, pthread_mutex_t* lock);
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* @brief Best-effort reclaim of freed blocks across all pages owned by ctx.
|
|
198
|
+
* @param ctx Allocator context.
|
|
199
|
+
* @param lock Optional mutex controlling reclaim.
|
|
200
|
+
*/
|
|
201
|
+
static inline void c_shm_reclaim(shm_allocator_ctx* ctx, pthread_mutex_t* lock);
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* @brief Scan for an allocator SHM name (appends "_ac" to prefix).
|
|
205
|
+
* @param shm_prefix Prefix to scan for (NULL for compile-time default).
|
|
206
|
+
* @param out Output buffer for found name.
|
|
207
|
+
* @return 0 on success, -1 on failure.
|
|
208
|
+
*/
|
|
209
|
+
static inline int c_shm_scan_allocator(const char* shm_prefix, char* out);
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* @brief Scan for a page SHM name (appends "_pg" to prefix).
|
|
213
|
+
* @param shm_prefix Prefix to scan for (NULL for compile-time default).
|
|
214
|
+
* @param out Output buffer for found name.
|
|
215
|
+
* @return 0 on success, -1 on failure.
|
|
216
|
+
*/
|
|
217
|
+
static inline int c_shm_scan_page(const char* shm_prefix, char* out);
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* @brief Extract pid embedded in allocator/page SHM name.
|
|
221
|
+
*
|
|
222
|
+
* The name format is {prefix}_{pid_hex}_{suffix}. This function locates
|
|
223
|
+
* the pid by scanning from the right — it is prefix-agnostic.
|
|
224
|
+
*
|
|
225
|
+
* @param shm_name SHM object name (with or without leading '/').
|
|
226
|
+
* @return pid on success, -1 with errno=EINVAL on parse failure.
|
|
227
|
+
*/
|
|
228
|
+
static inline pid_t c_shm_pid(const char* shm_name);
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* @brief Find and map a dangling allocator (whose creator pid is gone).
|
|
232
|
+
* @param shm_prefix Prefix to scan for (NULL for compile-time default).
|
|
233
|
+
* @param shm_name Output buffer to receive the dangling allocator name.
|
|
234
|
+
* @return Mapped allocator pointer or NULL with errno set.
|
|
235
|
+
*/
|
|
236
|
+
static inline shm_allocator* c_shm_allocator_dangling(const char* shm_prefix, char* shm_name);
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* @brief Unlink all dangling allocator/page SHM objects.
|
|
240
|
+
* @param shm_prefix Prefix to scan for (NULL for compile-time defaults).
|
|
241
|
+
*/
|
|
242
|
+
static inline void c_shm_clear_dangling(const char* shm_prefix);
|
|
243
|
+
|
|
244
|
+
// ========== Utility Functions ==========
|
|
245
|
+
|
|
246
|
+
static const size_t c_shm_page_overhead = (sizeof(shm_page) + sizeof(void*) - 1) & ~(sizeof(void*) - 1);
|
|
247
|
+
static const size_t c_shm_block_overhead = (sizeof(shm_memory_block) + sizeof(void*) - 1) & ~(sizeof(void*) - 1);
|
|
248
|
+
|
|
249
|
+
static inline size_t c_shm_page_roundup(shm_allocator* allocator, size_t size) {
|
|
250
|
+
return (size + allocator->autopage_alignment - 1) & ~(allocator->autopage_alignment - 1);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
static inline size_t c_shm_block_roundup(size_t size) {
|
|
254
|
+
return (size + sizeof(void*) - 1) & ~(sizeof(void*) - 1);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
static inline void c_shm_allocator_name(const void* region, const char* shm_prefix, char* out) {
|
|
258
|
+
pid_t pid = getpid();
|
|
259
|
+
// The shm name should be in format of {prefix}_ac_{pid_hex}_{region_hex}
|
|
260
|
+
snprintf(out, AP_SHM_NAME_LEN, "%s_ac_%lx_%lx", shm_prefix, (long) pid, (unsigned long) (uintptr_t) region);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
static inline void c_shm_page_name(shm_allocator* allocator, char* out) {
|
|
264
|
+
pid_t pid = getpid();
|
|
265
|
+
size_t page_idx = allocator->mapped_pages;
|
|
266
|
+
// The shm name should be in format of {prefix}_pg_{pid_hex}_{region_hex}_{page_idx_hex}
|
|
267
|
+
snprintf(out, AP_SHM_NAME_LEN, "%s_pg_%lx_%lx_%zx", allocator->shm_prefix, (long) pid, (unsigned long) allocator->region, page_idx);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
static inline int c_shm_scan(const char* prefix, char* out) {
|
|
271
|
+
if (!prefix || !out) {
|
|
272
|
+
errno = EINVAL;
|
|
273
|
+
return -1;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
if (prefix[0] == '/') {
|
|
277
|
+
prefix += 1;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
DIR* dir = opendir("/dev/shm");
|
|
281
|
+
if (!dir) {
|
|
282
|
+
return -1;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
size_t prefix_len = strlen(prefix);
|
|
286
|
+
struct dirent* ent = NULL;
|
|
287
|
+
int found = 0;
|
|
288
|
+
|
|
289
|
+
while ((ent = readdir(dir)) != NULL) {
|
|
290
|
+
if (ent->d_name[0] == '.') {
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
293
|
+
if (strncmp(ent->d_name, prefix, prefix_len) != 0) {
|
|
294
|
+
continue;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
size_t name_len = strnlen(ent->d_name, AP_SHM_NAME_LEN - 1);
|
|
298
|
+
if (name_len + 2 <= AP_SHM_NAME_LEN) {
|
|
299
|
+
out[0] = '/';
|
|
300
|
+
memcpy(out + 1, ent->d_name, name_len);
|
|
301
|
+
out[1 + name_len] = '\0';
|
|
302
|
+
found = 1;
|
|
303
|
+
break;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
closedir(dir);
|
|
308
|
+
|
|
309
|
+
if (!found) {
|
|
310
|
+
errno = ENOENT;
|
|
311
|
+
return -1;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
return 0;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
static inline shm_page_ctx* c_shm_page_new(shm_allocator* allocator, size_t page_capacity) {
|
|
318
|
+
shm_page_ctx* ctx = (shm_page_ctx*) calloc(1, sizeof(shm_page_ctx));
|
|
319
|
+
if (!ctx) {
|
|
320
|
+
return NULL;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
shm_page* meta = (shm_page*) calloc(1, c_shm_page_overhead);
|
|
324
|
+
if (!meta) {
|
|
325
|
+
free(ctx);
|
|
326
|
+
return NULL;
|
|
327
|
+
}
|
|
328
|
+
ctx->shm_page = meta;
|
|
329
|
+
|
|
330
|
+
meta->capacity = page_capacity;
|
|
331
|
+
meta->occupied = c_shm_page_overhead;
|
|
332
|
+
meta->offset = 0;
|
|
333
|
+
|
|
334
|
+
c_shm_page_name(allocator, meta->shm_name);
|
|
335
|
+
|
|
336
|
+
int fd = shm_open(meta->shm_name, O_CREAT | O_RDWR | O_EXCL, 0600);
|
|
337
|
+
if (fd == -1) {
|
|
338
|
+
free(meta);
|
|
339
|
+
free(ctx);
|
|
340
|
+
return NULL;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
if (ftruncate(fd, page_capacity) != 0) {
|
|
344
|
+
close(fd);
|
|
345
|
+
shm_unlink(meta->shm_name);
|
|
346
|
+
free(meta);
|
|
347
|
+
free(ctx);
|
|
348
|
+
return NULL;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
ctx->shm_page = meta;
|
|
352
|
+
ctx->shm_fd = fd;
|
|
353
|
+
ctx->buffer = NULL;
|
|
354
|
+
ctx->prev = NULL;
|
|
355
|
+
|
|
356
|
+
return ctx;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
static inline int c_shm_page_map(shm_allocator* allocator, shm_page_ctx* page_ctx) {
|
|
360
|
+
if (!allocator || !page_ctx || !page_ctx->shm_page || page_ctx->shm_fd < 0) {
|
|
361
|
+
errno = EINVAL;
|
|
362
|
+
return -1;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
size_t page_capacity = page_ctx->shm_page->capacity;
|
|
366
|
+
size_t offset = allocator->mapped_size;
|
|
367
|
+
|
|
368
|
+
if (offset + page_capacity > allocator->region_size) {
|
|
369
|
+
errno = ENOMEM;
|
|
370
|
+
return -1;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/* compute target address from integer region base */
|
|
374
|
+
void* target_addr = (void*) ((char*) (uintptr_t) allocator->region + offset);
|
|
375
|
+
void* mapped = mmap(target_addr, page_capacity, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, page_ctx->shm_fd, 0);
|
|
376
|
+
if (mapped == MAP_FAILED) {
|
|
377
|
+
return -1;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// Step 0: Point to metadata at start of page
|
|
381
|
+
shm_page* page_meta = (shm_page*) mapped;
|
|
382
|
+
memcpy(page_meta, page_ctx->shm_page, c_shm_page_overhead);
|
|
383
|
+
free(page_ctx->shm_page);
|
|
384
|
+
page_ctx->shm_page = page_meta;
|
|
385
|
+
page_ctx->buffer = (char*) mapped; // occupied already included the overhead, so the buffer should start at mapped
|
|
386
|
+
|
|
387
|
+
// Step 1: Set correct offset
|
|
388
|
+
page_meta->offset = offset;
|
|
389
|
+
page_meta->allocator = allocator;
|
|
390
|
+
|
|
391
|
+
// Step 2: Set prev_name = current active_page (enables backward traversal)
|
|
392
|
+
if (allocator->mapped_pages == 0) {
|
|
393
|
+
page_meta->prev_name[0] = '\0';
|
|
394
|
+
}
|
|
395
|
+
else {
|
|
396
|
+
memcpy(page_meta->prev_name, allocator->active_page, AP_SHM_NAME_LEN);
|
|
397
|
+
page_meta->prev_name[AP_SHM_NAME_LEN - 1] = '\0';
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// Step 4: Update allocator state
|
|
401
|
+
allocator->mapped_size += page_capacity;
|
|
402
|
+
memcpy(allocator->active_page, page_meta->shm_name, AP_SHM_NAME_LEN);
|
|
403
|
+
allocator->active_page[AP_SHM_NAME_LEN - 1] = '\0';
|
|
404
|
+
allocator->mapped_pages++;
|
|
405
|
+
|
|
406
|
+
return 0;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
static inline void c_shm_page_reclaim(shm_allocator* allocator, shm_page_ctx* page_ctx) {
|
|
410
|
+
if (!allocator || !page_ctx || !page_ctx->shm_page) {
|
|
411
|
+
errno = EINVAL;
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
shm_page* page = page_ctx->shm_page;
|
|
416
|
+
|
|
417
|
+
// Best-effort: walk from newest; stop at first live block.
|
|
418
|
+
shm_memory_block** prevp = &page->allocated;
|
|
419
|
+
while (*prevp) {
|
|
420
|
+
shm_memory_block* block = *prevp;
|
|
421
|
+
if (block->size != 0) {
|
|
422
|
+
break; // first live block reached; older blocks retained
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// Remove from page allocation list
|
|
426
|
+
*prevp = block->next_allocated;
|
|
427
|
+
block->next_allocated = NULL;
|
|
428
|
+
|
|
429
|
+
// Remove from allocator free list (single occurrence)
|
|
430
|
+
shm_memory_block** free_prev = &allocator->free_list;
|
|
431
|
+
while (*free_prev && *free_prev != block) {
|
|
432
|
+
free_prev = &(*free_prev)->next_free;
|
|
433
|
+
}
|
|
434
|
+
if (*free_prev == block) {
|
|
435
|
+
*free_prev = block->next_free;
|
|
436
|
+
}
|
|
437
|
+
block->next_free = NULL;
|
|
438
|
+
|
|
439
|
+
// Return occupied size back to page
|
|
440
|
+
size_t cap_total = block->capacity + c_shm_block_overhead;
|
|
441
|
+
if (page->occupied >= cap_total) {
|
|
442
|
+
page->occupied -= cap_total;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// ========== Public API Functions ==========
|
|
448
|
+
|
|
449
|
+
static inline shm_page_ctx* c_shm_allocator_extend(shm_allocator_ctx* ctx, size_t capacity, pthread_mutex_t* lock) {
|
|
450
|
+
if (!ctx || !ctx->shm_allocator) {
|
|
451
|
+
errno = EINVAL;
|
|
452
|
+
return NULL;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
shm_allocator* allocator = ctx->shm_allocator;
|
|
456
|
+
uint8_t locked = 0;
|
|
457
|
+
|
|
458
|
+
if (lock) {
|
|
459
|
+
int ret = pthread_mutex_lock(lock);
|
|
460
|
+
if (ret != 0) {
|
|
461
|
+
errno = ret;
|
|
462
|
+
return NULL;
|
|
463
|
+
}
|
|
464
|
+
locked = 1;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// Step 1: Determine new page capacity
|
|
468
|
+
if (capacity == 0) {
|
|
469
|
+
if (!ctx->active_page) {
|
|
470
|
+
capacity = allocator->autopage_capacity;
|
|
471
|
+
}
|
|
472
|
+
else {
|
|
473
|
+
size_t prev_cap = ctx->active_page->shm_page->capacity;
|
|
474
|
+
capacity = prev_cap * 2;
|
|
475
|
+
if (capacity < allocator->autopage_capacity) {
|
|
476
|
+
capacity = allocator->autopage_capacity;
|
|
477
|
+
}
|
|
478
|
+
else if (capacity > allocator->autopage_capacity_max) {
|
|
479
|
+
capacity = allocator->autopage_capacity_max;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
size_t aligned_capacity = c_shm_page_roundup(allocator, capacity);
|
|
485
|
+
|
|
486
|
+
if (aligned_capacity + allocator->mapped_size > allocator->region_size) {
|
|
487
|
+
if (locked) pthread_mutex_unlock(lock);
|
|
488
|
+
errno = ENOMEM;
|
|
489
|
+
return NULL;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
// Step 2: Create new page
|
|
493
|
+
shm_page_ctx* page_ctx = c_shm_page_new(allocator, aligned_capacity);
|
|
494
|
+
if (!page_ctx) {
|
|
495
|
+
if (locked) pthread_mutex_unlock(lock);
|
|
496
|
+
return NULL;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// Step 3: Map new page into allocator region
|
|
500
|
+
if (c_shm_page_map(allocator, page_ctx) != 0) {
|
|
501
|
+
shm_unlink(page_ctx->shm_page->shm_name);
|
|
502
|
+
close(page_ctx->shm_fd);
|
|
503
|
+
free(page_ctx->shm_page); // temp heap metadata
|
|
504
|
+
free(page_ctx);
|
|
505
|
+
if (locked) pthread_mutex_unlock(lock);
|
|
506
|
+
return NULL;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
// Step 4: Update context active_page
|
|
510
|
+
page_ctx->prev = ctx->active_page;
|
|
511
|
+
ctx->active_page = page_ctx;
|
|
512
|
+
|
|
513
|
+
// Unlock if we locked here
|
|
514
|
+
if (locked) pthread_mutex_unlock(lock);
|
|
515
|
+
return page_ctx;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
static inline shm_allocator_ctx* c_shm_allocator_new(size_t region_size, const char* shm_prefix) {
|
|
519
|
+
shm_allocator_ctx* ctx = (shm_allocator_ctx*) calloc(1, sizeof(shm_allocator_ctx));
|
|
520
|
+
if (!ctx) {
|
|
521
|
+
return NULL;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
if (!shm_prefix) {
|
|
525
|
+
shm_prefix = AP_SHM_ALLOCATOR_PREFIX;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
size_t prefix_len = strnlen(shm_prefix, AP_SHM_PREFIX_MAX);
|
|
529
|
+
if (prefix_len >= AP_SHM_PREFIX_MAX) {
|
|
530
|
+
fprintf(stderr, "c_shm_allocator_new: shm_prefix exceeds AP_SHM_PREFIX_MAX (%zu)\n", (size_t) AP_SHM_PREFIX_MAX);
|
|
531
|
+
abort();
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
if (region_size == 0) {
|
|
535
|
+
region_size = AP_SHM_ALLOCATOR_DEFAULT_REGION_SIZE; // 128 GiB
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
// Step 1: Reserve virtual address space for the 128 GiB page region
|
|
539
|
+
void* virtual_region = mmap(
|
|
540
|
+
NULL, region_size,
|
|
541
|
+
PROT_NONE,
|
|
542
|
+
MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE,
|
|
543
|
+
-1, 0
|
|
544
|
+
);
|
|
545
|
+
if (virtual_region == MAP_FAILED) {
|
|
546
|
+
free(ctx);
|
|
547
|
+
return NULL;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
// Step 2: Create SHM object for allocator metadata
|
|
551
|
+
char meta_shm_name[AP_SHM_NAME_LEN];
|
|
552
|
+
c_shm_allocator_name(virtual_region, shm_prefix, meta_shm_name);
|
|
553
|
+
|
|
554
|
+
int meta_fd = shm_open(meta_shm_name, O_CREAT | O_RDWR | O_EXCL, 0600);
|
|
555
|
+
if (meta_fd == -1) {
|
|
556
|
+
munmap(virtual_region, region_size);
|
|
557
|
+
free(ctx);
|
|
558
|
+
return NULL;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
if (ftruncate(meta_fd, sizeof(shm_allocator)) != 0) {
|
|
562
|
+
close(meta_fd);
|
|
563
|
+
shm_unlink(meta_shm_name);
|
|
564
|
+
munmap(virtual_region, region_size);
|
|
565
|
+
free(ctx);
|
|
566
|
+
return NULL;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
// Step 3: Map allocator metadata into memory
|
|
570
|
+
shm_allocator* meta = (shm_allocator*) mmap(NULL, sizeof(shm_allocator), PROT_READ | PROT_WRITE, MAP_SHARED, meta_fd, 0);
|
|
571
|
+
if (meta == MAP_FAILED) {
|
|
572
|
+
close(meta_fd);
|
|
573
|
+
shm_unlink(meta_shm_name);
|
|
574
|
+
munmap(virtual_region, region_size);
|
|
575
|
+
free(ctx);
|
|
576
|
+
return NULL;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
// Step 4: Initialize metadata IN SHARED MEMORY
|
|
580
|
+
// Must zero first (mmap doesn't guarantee zeroed memory for ftruncate'd SHM)
|
|
581
|
+
memset(meta, 0, sizeof(shm_allocator));
|
|
582
|
+
|
|
583
|
+
size_t name_len = strnlen(meta_shm_name, AP_SHM_NAME_LEN - 1);
|
|
584
|
+
memcpy(meta->shm_name, meta_shm_name, name_len);
|
|
585
|
+
meta->shm_name[name_len] = '\0';
|
|
586
|
+
meta->pid = (size_t) getpid();
|
|
587
|
+
meta->region = (uintptr_t) virtual_region;
|
|
588
|
+
meta->region_size = region_size;
|
|
589
|
+
meta->mapped_size = 0;
|
|
590
|
+
meta->mapped_pages = 0;
|
|
591
|
+
meta->active_page[0] = '\0';
|
|
592
|
+
meta->autopage_capacity = AP_SHM_AUTOPAGE_CAPACITY;
|
|
593
|
+
meta->autopage_capacity_max = AP_SHM_AUTOPAGE_CAPACITY_MAX;
|
|
594
|
+
meta->autopage_alignment = AP_SHM_AUTOPAGE_ALIGNMENT;
|
|
595
|
+
|
|
596
|
+
memcpy(meta->shm_prefix, shm_prefix, prefix_len);
|
|
597
|
+
meta->shm_prefix[prefix_len] = '\0';
|
|
598
|
+
|
|
599
|
+
// Step 5: Initialize mutex in shared memory with PTHREAD_PROCESS_SHARED
|
|
600
|
+
pthread_mutexattr_t mattr;
|
|
601
|
+
if (pthread_mutexattr_init(&mattr) != 0) {
|
|
602
|
+
goto cleanup;
|
|
603
|
+
}
|
|
604
|
+
if (pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED) != 0) {
|
|
605
|
+
pthread_mutexattr_destroy(&mattr);
|
|
606
|
+
goto cleanup;
|
|
607
|
+
}
|
|
608
|
+
if (pthread_mutex_init(&meta->lock, &mattr) != 0) {
|
|
609
|
+
pthread_mutexattr_destroy(&mattr);
|
|
610
|
+
goto cleanup;
|
|
611
|
+
}
|
|
612
|
+
pthread_mutexattr_destroy(&mattr);
|
|
613
|
+
|
|
614
|
+
// Step 6: Fill context
|
|
615
|
+
ctx->shm_allocator = meta;
|
|
616
|
+
ctx->shm_fd = meta_fd;
|
|
617
|
+
ctx->active_page = NULL;
|
|
618
|
+
|
|
619
|
+
return ctx;
|
|
620
|
+
|
|
621
|
+
cleanup:
|
|
622
|
+
munmap(virtual_region, region_size);
|
|
623
|
+
munmap(meta, sizeof(shm_allocator));
|
|
624
|
+
close(meta_fd);
|
|
625
|
+
shm_unlink(meta_shm_name);
|
|
626
|
+
free(ctx);
|
|
627
|
+
return NULL;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
static inline void c_shm_allocator_free(shm_allocator_ctx* ctx) {
|
|
631
|
+
if (!ctx) {
|
|
632
|
+
return;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
char shm_name[AP_SHM_NAME_LEN];
|
|
636
|
+
|
|
637
|
+
// Step 1: Unmap all pages
|
|
638
|
+
shm_page_ctx* page_ctx = ctx->active_page;
|
|
639
|
+
while (page_ctx) {
|
|
640
|
+
shm_page_ctx* prev = page_ctx->prev;
|
|
641
|
+
shm_page* page_meta = page_ctx->shm_page;
|
|
642
|
+
|
|
643
|
+
strncpy(shm_name, page_meta->shm_name, sizeof(shm_name) - 1);
|
|
644
|
+
shm_name[sizeof(shm_name) - 1] = '\0';
|
|
645
|
+
|
|
646
|
+
if (page_meta) {
|
|
647
|
+
munmap((void*) page_meta, page_meta->capacity);
|
|
648
|
+
shm_unlink(shm_name);
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
if (page_ctx->shm_fd >= 0) {
|
|
652
|
+
close(page_ctx->shm_fd);
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
free(page_ctx);
|
|
656
|
+
page_ctx = prev;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
// Step 2: Unmap allocator region
|
|
660
|
+
shm_allocator* allocator = ctx->shm_allocator;
|
|
661
|
+
if (allocator) {
|
|
662
|
+
// Step 2.1: Save shm_name before unmapping
|
|
663
|
+
strncpy(shm_name, allocator->shm_name, sizeof(shm_name) - 1);
|
|
664
|
+
shm_name[sizeof(shm_name) - 1] = '\0';
|
|
665
|
+
|
|
666
|
+
// Step 2.2: Destroy mutex
|
|
667
|
+
pthread_mutex_destroy(&allocator->lock);
|
|
668
|
+
|
|
669
|
+
// Step 2.3: Unmap region
|
|
670
|
+
if (allocator->region) {
|
|
671
|
+
munmap((void*) allocator->region, allocator->region_size);
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
// Step 2.4: Unmap allocator shm
|
|
675
|
+
munmap((void*) allocator, sizeof(shm_allocator));
|
|
676
|
+
|
|
677
|
+
// Step 2.5: Unlink allocator SHM
|
|
678
|
+
shm_unlink(shm_name);
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
// Step 3: Close allocator SHM fd
|
|
682
|
+
if (ctx->shm_fd >= 0) {
|
|
683
|
+
close(ctx->shm_fd);
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
// Step 4: Free context
|
|
687
|
+
free(ctx);
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
static inline void* c_shm_calloc(shm_allocator_ctx* ctx, size_t size, pthread_mutex_t* lock) {
|
|
691
|
+
if (!ctx || !ctx->shm_allocator || size == 0) {
|
|
692
|
+
errno = EINVAL;
|
|
693
|
+
return NULL;
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
size_t cap_net = c_shm_block_roundup(size);
|
|
697
|
+
// the overhead is already aligned due to struct padding
|
|
698
|
+
size_t cap_total = cap_net + c_shm_block_overhead;
|
|
699
|
+
shm_allocator* allocator = ctx->shm_allocator;
|
|
700
|
+
|
|
701
|
+
// Step 1: Lock allocator
|
|
702
|
+
// Locking strategy:
|
|
703
|
+
// - If lock is NULL, caller is responsible for locking, calloc and extend will not be locked
|
|
704
|
+
// - If lock is allocator's builtin lock, calloc will be locked, extend will not be locked to void double lock
|
|
705
|
+
// - If lock is a different lock, calloc will use the provided lock, extend will also use the builtin lock
|
|
706
|
+
|
|
707
|
+
uint8_t locked = 0;
|
|
708
|
+
pthread_mutex_t* builtin_lock = &allocator->lock;
|
|
709
|
+
pthread_mutex_t* child_lock = &allocator->lock;
|
|
710
|
+
if (lock) {
|
|
711
|
+
if (lock == builtin_lock) {
|
|
712
|
+
child_lock = NULL; // avoid double locking
|
|
713
|
+
}
|
|
714
|
+
int ret = pthread_mutex_lock(lock);
|
|
715
|
+
if (ret != 0) {
|
|
716
|
+
errno = ret;
|
|
717
|
+
return NULL;
|
|
718
|
+
}
|
|
719
|
+
locked = 1;
|
|
720
|
+
}
|
|
721
|
+
else {
|
|
722
|
+
child_lock = NULL; // caller is responsible for locking
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
// Step 2: Extend the allocator if there is no active_page or insufficient space
|
|
726
|
+
shm_page_ctx* page_ctx = ctx->active_page;
|
|
727
|
+
if (!page_ctx) {
|
|
728
|
+
// Step 2.1: Roundup to nearest allocator->autopage_capacity * 2^n, for the first page
|
|
729
|
+
size_t target_cap = allocator->autopage_capacity;
|
|
730
|
+
while (target_cap < cap_total + c_shm_page_overhead) {
|
|
731
|
+
target_cap *= 2;
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
page_ctx = c_shm_allocator_extend(ctx, target_cap, child_lock);
|
|
735
|
+
if (!page_ctx) {
|
|
736
|
+
if (locked) pthread_mutex_unlock(lock);
|
|
737
|
+
return NULL;
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
shm_page* page_meta = page_ctx->shm_page;
|
|
742
|
+
|
|
743
|
+
// Step 2: Extend the allocator if there is no active_page or insufficient space
|
|
744
|
+
if (page_meta->occupied + cap_total > page_meta->capacity) {
|
|
745
|
+
// Step 2.1: Determine new page capacity
|
|
746
|
+
size_t target_cap = page_meta->capacity;
|
|
747
|
+
|
|
748
|
+
if (target_cap < allocator->autopage_capacity) {
|
|
749
|
+
target_cap = allocator->autopage_capacity;
|
|
750
|
+
}
|
|
751
|
+
else if (target_cap < allocator->autopage_capacity_max) {
|
|
752
|
+
target_cap *= 2;
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
// Step 2.1: Roundup to nearest allocator->autopage_capacity * 2^n, for the first page
|
|
756
|
+
while (target_cap < cap_total + c_shm_page_overhead) {
|
|
757
|
+
target_cap *= 2;
|
|
758
|
+
}
|
|
759
|
+
page_ctx = c_shm_allocator_extend(ctx, target_cap, child_lock);
|
|
760
|
+
if (!page_ctx) {
|
|
761
|
+
if (locked) pthread_mutex_unlock(lock);
|
|
762
|
+
return NULL;
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
page_meta = page_ctx->shm_page;
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
// Step 3: Allocate memory from active page
|
|
769
|
+
size_t offset = page_meta->occupied;
|
|
770
|
+
shm_memory_block* block = (shm_memory_block*) (page_ctx->buffer + offset);
|
|
771
|
+
block->capacity = cap_net;
|
|
772
|
+
block->size = size;
|
|
773
|
+
block->next_free = NULL;
|
|
774
|
+
|
|
775
|
+
// Step 4: Link fresh block to its page (calloc path is always fresh)
|
|
776
|
+
block->parent_page = page_meta;
|
|
777
|
+
block->next_allocated = page_meta->allocated;
|
|
778
|
+
page_meta->allocated = block;
|
|
779
|
+
|
|
780
|
+
page_meta->occupied += cap_total;
|
|
781
|
+
|
|
782
|
+
// Step 5: Unlock allocator
|
|
783
|
+
if (locked) pthread_mutex_unlock(lock);
|
|
784
|
+
|
|
785
|
+
memset(block + 1, 0, cap_net);
|
|
786
|
+
return (void*) block->buffer;
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
static inline void* c_shm_request(shm_allocator_ctx* ctx, size_t size, int scan_all_pages, pthread_mutex_t* lock) {
|
|
790
|
+
if (!ctx || !ctx->shm_allocator || size == 0) {
|
|
791
|
+
errno = EINVAL;
|
|
792
|
+
return NULL;
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
size_t cap_net = c_shm_block_roundup(size);
|
|
796
|
+
size_t cap_total = cap_net + c_shm_block_overhead;
|
|
797
|
+
shm_allocator* allocator = ctx->shm_allocator;
|
|
798
|
+
|
|
799
|
+
uint8_t locked = 0;
|
|
800
|
+
pthread_mutex_t* builtin_lock = &allocator->lock;
|
|
801
|
+
pthread_mutex_t* child_lock = &allocator->lock;
|
|
802
|
+
if (lock) {
|
|
803
|
+
if (lock == builtin_lock) {
|
|
804
|
+
child_lock = NULL;
|
|
805
|
+
}
|
|
806
|
+
int ret = pthread_mutex_lock(lock);
|
|
807
|
+
if (ret != 0) {
|
|
808
|
+
errno = ret;
|
|
809
|
+
return NULL;
|
|
810
|
+
}
|
|
811
|
+
locked = 1;
|
|
812
|
+
}
|
|
813
|
+
else {
|
|
814
|
+
child_lock = NULL;
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
// Step 1: Try allocator-level free list reuse
|
|
818
|
+
shm_memory_block** prevp = &allocator->free_list;
|
|
819
|
+
shm_memory_block* free_blk = allocator->free_list;
|
|
820
|
+
while (free_blk) {
|
|
821
|
+
if (free_blk->capacity >= cap_net) {
|
|
822
|
+
*prevp = free_blk->next_free;
|
|
823
|
+
free_blk->next_free = NULL;
|
|
824
|
+
free_blk->size = size;
|
|
825
|
+
if (locked) pthread_mutex_unlock(lock);
|
|
826
|
+
memset(free_blk + 1, 0, cap_net);
|
|
827
|
+
return (void*) free_blk->buffer;
|
|
828
|
+
}
|
|
829
|
+
prevp = &free_blk->next_free;
|
|
830
|
+
free_blk = free_blk->next_free;
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
// Step 2: Locate a page with enough space (optionally scan all pages)
|
|
834
|
+
shm_page_ctx* target_ctx = NULL;
|
|
835
|
+
if (scan_all_pages) {
|
|
836
|
+
shm_page_ctx* iter = ctx->active_page;
|
|
837
|
+
while (iter) {
|
|
838
|
+
shm_page* meta = iter->shm_page;
|
|
839
|
+
if (meta && meta->occupied + cap_total <= meta->capacity) {
|
|
840
|
+
target_ctx = iter;
|
|
841
|
+
break;
|
|
842
|
+
}
|
|
843
|
+
iter = iter->prev;
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
else if (ctx->active_page) {
|
|
847
|
+
shm_page* meta = ctx->active_page->shm_page;
|
|
848
|
+
if (meta && meta->occupied + cap_total <= meta->capacity) {
|
|
849
|
+
target_ctx = ctx->active_page;
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
// Step 3: Extend allocator if no existing page fits
|
|
854
|
+
if (!target_ctx) {
|
|
855
|
+
shm_page_ctx* current = ctx->active_page;
|
|
856
|
+
size_t target_cap;
|
|
857
|
+
|
|
858
|
+
if (!current) {
|
|
859
|
+
target_cap = allocator->autopage_capacity;
|
|
860
|
+
while (target_cap < cap_total + c_shm_page_overhead) {
|
|
861
|
+
target_cap *= 2;
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
else {
|
|
865
|
+
size_t prev_cap = current->shm_page->capacity;
|
|
866
|
+
size_t new_cap = prev_cap;
|
|
867
|
+
|
|
868
|
+
if (new_cap < allocator->autopage_capacity) {
|
|
869
|
+
new_cap = allocator->autopage_capacity;
|
|
870
|
+
}
|
|
871
|
+
else if (new_cap < allocator->autopage_capacity_max) {
|
|
872
|
+
new_cap *= 2;
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
while (new_cap < cap_total + c_shm_page_overhead) {
|
|
876
|
+
new_cap *= 2;
|
|
877
|
+
}
|
|
878
|
+
target_cap = new_cap;
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
target_ctx = c_shm_allocator_extend(ctx, target_cap, child_lock);
|
|
882
|
+
if (!target_ctx) {
|
|
883
|
+
if (locked) pthread_mutex_unlock(lock);
|
|
884
|
+
return NULL;
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
// Step 4: Allocate from the selected page
|
|
889
|
+
shm_page* page_meta = target_ctx->shm_page;
|
|
890
|
+
size_t offset = page_meta->occupied;
|
|
891
|
+
shm_memory_block* block = (shm_memory_block*) (target_ctx->buffer + offset);
|
|
892
|
+
block->capacity = cap_net;
|
|
893
|
+
block->size = size;
|
|
894
|
+
block->next_free = NULL;
|
|
895
|
+
|
|
896
|
+
// Step 5: Link block to its page for potential recycling scans
|
|
897
|
+
block->parent_page = page_meta;
|
|
898
|
+
block->next_allocated = page_meta->allocated;
|
|
899
|
+
page_meta->allocated = block;
|
|
900
|
+
|
|
901
|
+
page_meta->occupied += cap_total;
|
|
902
|
+
|
|
903
|
+
if (locked) pthread_mutex_unlock(lock);
|
|
904
|
+
|
|
905
|
+
memset(block + 1, 0, cap_net);
|
|
906
|
+
return (void*) block->buffer;
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
static inline void c_shm_free(void* ptr, pthread_mutex_t* lock) {
|
|
910
|
+
if (!ptr) {
|
|
911
|
+
errno = EINVAL;
|
|
912
|
+
return;
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
shm_memory_block* block = (shm_memory_block*) ((char*) ptr - c_shm_block_overhead);
|
|
916
|
+
shm_page* page = block->parent_page;
|
|
917
|
+
if (!page || !page->allocator) {
|
|
918
|
+
errno = EINVAL;
|
|
919
|
+
return;
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
shm_allocator* allocator = page->allocator;
|
|
923
|
+
|
|
924
|
+
uint8_t locked = 0;
|
|
925
|
+
if (lock) {
|
|
926
|
+
int ret = pthread_mutex_lock(lock);
|
|
927
|
+
if (ret != 0) {
|
|
928
|
+
errno = ret;
|
|
929
|
+
return;
|
|
930
|
+
}
|
|
931
|
+
locked = 1;
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
block->size = 0;
|
|
935
|
+
block->next_free = allocator->free_list;
|
|
936
|
+
allocator->free_list = block;
|
|
937
|
+
|
|
938
|
+
if (locked) pthread_mutex_unlock(lock);
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
static inline void c_shm_reclaim(shm_allocator_ctx* ctx, pthread_mutex_t* lock) {
|
|
942
|
+
if (!ctx || !ctx->shm_allocator) {
|
|
943
|
+
errno = EINVAL;
|
|
944
|
+
return;
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
uint8_t locked = 0;
|
|
948
|
+
if (lock) {
|
|
949
|
+
int ret = pthread_mutex_lock(lock);
|
|
950
|
+
if (ret != 0) {
|
|
951
|
+
errno = ret;
|
|
952
|
+
return;
|
|
953
|
+
}
|
|
954
|
+
locked = 1;
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
shm_allocator* allocator = ctx->shm_allocator;
|
|
958
|
+
shm_page_ctx* page_ctx = ctx->active_page;
|
|
959
|
+
while (page_ctx) {
|
|
960
|
+
c_shm_page_reclaim(allocator, page_ctx);
|
|
961
|
+
page_ctx = page_ctx->prev;
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
if (locked) pthread_mutex_unlock(lock);
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
// ========== Internal SHM Management API ==========
|
|
968
|
+
|
|
969
|
+
static inline int c_shm_scan_allocator(const char* shm_prefix, char* out) {
|
|
970
|
+
if (!shm_prefix) shm_prefix = AP_SHM_ALLOCATOR_PREFIX;
|
|
971
|
+
char scan_prefix[AP_SHM_PREFIX_MAX + 4]; // +4 for "_ac\0"
|
|
972
|
+
snprintf(scan_prefix, sizeof(scan_prefix), "%s_ac", shm_prefix);
|
|
973
|
+
return c_shm_scan(scan_prefix, out);
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
static inline int c_shm_scan_page(const char* shm_prefix, char* out) {
|
|
977
|
+
if (!shm_prefix) shm_prefix = AP_SHM_ALLOCATOR_PREFIX;
|
|
978
|
+
char scan_prefix[AP_SHM_PREFIX_MAX + 4]; // +4 for "_pg\0"
|
|
979
|
+
snprintf(scan_prefix, sizeof(scan_prefix), "%s_pg", shm_prefix);
|
|
980
|
+
return c_shm_scan(scan_prefix, out);
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
static inline pid_t c_shm_pid(const char* shm_name) {
|
|
984
|
+
if (!shm_name) {
|
|
985
|
+
errno = EINVAL;
|
|
986
|
+
return -1;
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
const char* base = shm_name;
|
|
990
|
+
if (base[0] == '/') {
|
|
991
|
+
base++;
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
// Name format: {prefix}_{pid_hex}_{suffix}
|
|
995
|
+
// Walk from the right: find last '_' (suffix separator),
|
|
996
|
+
// then the '_' before it (pid separator).
|
|
997
|
+
const char* suffix_us = strrchr(base, '_');
|
|
998
|
+
if (!suffix_us) {
|
|
999
|
+
errno = EINVAL;
|
|
1000
|
+
return -1;
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
// Find the underscore that starts the pid segment (rightmost before suffix)
|
|
1004
|
+
const char* pid_us = NULL;
|
|
1005
|
+
for (const char* p = base; p < suffix_us; p++) {
|
|
1006
|
+
if (*p == '_') pid_us = p;
|
|
1007
|
+
}
|
|
1008
|
+
if (!pid_us) {
|
|
1009
|
+
errno = EINVAL;
|
|
1010
|
+
return -1;
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
const char* pid_start = pid_us + 1;
|
|
1014
|
+
size_t pid_len = (size_t) (suffix_us - pid_start);
|
|
1015
|
+
if (pid_len == 0 || pid_len >= 32) {
|
|
1016
|
+
errno = EINVAL;
|
|
1017
|
+
return -1;
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
char pidbuf[32];
|
|
1021
|
+
memcpy(pidbuf, pid_start, pid_len);
|
|
1022
|
+
pidbuf[pid_len] = '\0';
|
|
1023
|
+
|
|
1024
|
+
errno = 0;
|
|
1025
|
+
char* endptr = NULL;
|
|
1026
|
+
unsigned long v = strtoul(pidbuf, &endptr, 16);
|
|
1027
|
+
if (errno != 0 || endptr == pidbuf || *endptr != '\0') {
|
|
1028
|
+
errno = EINVAL;
|
|
1029
|
+
return -1;
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
if (v > (unsigned long) INT_MAX) {
|
|
1033
|
+
errno = EINVAL;
|
|
1034
|
+
return -1;
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
return (pid_t) v;
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
static inline shm_allocator* c_shm_allocator_dangling(const char* shm_prefix, char* shm_name) {
|
|
1041
|
+
if (!shm_name) {
|
|
1042
|
+
errno = EINVAL;
|
|
1043
|
+
return NULL;
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
if (!shm_prefix) shm_prefix = AP_SHM_ALLOCATOR_PREFIX;
|
|
1047
|
+
|
|
1048
|
+
char scan_prefix[AP_SHM_PREFIX_MAX + 4];
|
|
1049
|
+
snprintf(scan_prefix, sizeof(scan_prefix), "%s_ac", shm_prefix);
|
|
1050
|
+
|
|
1051
|
+
const char* fs_prefix = scan_prefix;
|
|
1052
|
+
if (fs_prefix[0] == '/') fs_prefix += 1;
|
|
1053
|
+
size_t prefix_len = strlen(fs_prefix);
|
|
1054
|
+
|
|
1055
|
+
DIR* dir = opendir("/dev/shm");
|
|
1056
|
+
if (!dir) {
|
|
1057
|
+
return NULL;
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
struct dirent* ent = NULL;
|
|
1061
|
+
shm_allocator* mapped = NULL;
|
|
1062
|
+
char candidate[AP_SHM_NAME_LEN];
|
|
1063
|
+
|
|
1064
|
+
while ((ent = readdir(dir)) != NULL) {
|
|
1065
|
+
if (ent->d_name[0] == '.') continue;
|
|
1066
|
+
if (strncmp(ent->d_name, fs_prefix, prefix_len) != 0) continue;
|
|
1067
|
+
|
|
1068
|
+
size_t name_len = strnlen(ent->d_name, AP_SHM_NAME_LEN - 1);
|
|
1069
|
+
if (name_len + 2 > AP_SHM_NAME_LEN) {
|
|
1070
|
+
continue; // would truncate; skip this entry
|
|
1071
|
+
}
|
|
1072
|
+
candidate[0] = '/';
|
|
1073
|
+
memcpy(candidate + 1, ent->d_name, name_len);
|
|
1074
|
+
candidate[1 + name_len] = '\0';
|
|
1075
|
+
|
|
1076
|
+
pid_t pid = c_shm_pid(candidate);
|
|
1077
|
+
if (pid <= 0) continue;
|
|
1078
|
+
|
|
1079
|
+
if (kill(pid, 0) == 0 || errno != ESRCH) {
|
|
1080
|
+
errno = 0; // either alive or another error; treat as not dangling
|
|
1081
|
+
continue;
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
int fd = shm_open(candidate, O_RDWR, 0);
|
|
1085
|
+
if (fd == -1) {
|
|
1086
|
+
continue;
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
struct stat st;
|
|
1090
|
+
if (fstat(fd, &st) != 0) {
|
|
1091
|
+
close(fd);
|
|
1092
|
+
continue;
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
void* map = mmap(NULL, (size_t) st.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
|
1096
|
+
close(fd);
|
|
1097
|
+
if (map == MAP_FAILED) {
|
|
1098
|
+
continue;
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
strncpy(shm_name, candidate, AP_SHM_NAME_LEN - 1);
|
|
1102
|
+
shm_name[AP_SHM_NAME_LEN - 1] = '\0';
|
|
1103
|
+
mapped = (shm_allocator*) map;
|
|
1104
|
+
break;
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
closedir(dir);
|
|
1108
|
+
|
|
1109
|
+
if (!mapped) {
|
|
1110
|
+
errno = ENOENT;
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
return mapped;
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
static inline void c_shm_clear_dangling(const char* shm_prefix) {
|
|
1117
|
+
DIR* dir = opendir("/dev/shm");
|
|
1118
|
+
if (!dir) {
|
|
1119
|
+
return;
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
if (!shm_prefix) shm_prefix = AP_SHM_ALLOCATOR_PREFIX;
|
|
1123
|
+
|
|
1124
|
+
const char* suffixes[] = {"_ac", "_pg"};
|
|
1125
|
+
size_t suffix_count = 2;
|
|
1126
|
+
|
|
1127
|
+
struct dirent* ent = NULL;
|
|
1128
|
+
while ((ent = readdir(dir)) != NULL) {
|
|
1129
|
+
if (ent->d_name[0] == '.') continue;
|
|
1130
|
+
|
|
1131
|
+
int matched = 0;
|
|
1132
|
+
for (size_t i = 0; i < suffix_count; ++i) {
|
|
1133
|
+
char full_prefix[AP_SHM_PREFIX_MAX + 4];
|
|
1134
|
+
snprintf(full_prefix, sizeof(full_prefix), "%s%s", shm_prefix, suffixes[i]);
|
|
1135
|
+
const char* p = full_prefix;
|
|
1136
|
+
if (p[0] == '/') p += 1;
|
|
1137
|
+
size_t len = strlen(p);
|
|
1138
|
+
if (strncmp(ent->d_name, p, len) == 0) {
|
|
1139
|
+
matched = 1;
|
|
1140
|
+
break;
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
if (!matched) continue;
|
|
1144
|
+
|
|
1145
|
+
char candidate[AP_SHM_NAME_LEN];
|
|
1146
|
+
size_t name_len = strnlen(ent->d_name, AP_SHM_NAME_LEN - 1);
|
|
1147
|
+
if (name_len + 2 > AP_SHM_NAME_LEN) {
|
|
1148
|
+
continue; // would truncate; skip this entry
|
|
1149
|
+
}
|
|
1150
|
+
candidate[0] = '/';
|
|
1151
|
+
memcpy(candidate + 1, ent->d_name, name_len);
|
|
1152
|
+
candidate[1 + name_len] = '\0';
|
|
1153
|
+
|
|
1154
|
+
pid_t pid = c_shm_pid(candidate);
|
|
1155
|
+
if (pid <= 0) {
|
|
1156
|
+
continue;
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
errno = 0;
|
|
1160
|
+
if (kill(pid, 0) == -1 && errno == ESRCH) {
|
|
1161
|
+
shm_unlink(candidate); // best effort
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
closedir(dir);
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
#endif // AP_SHM_C_SHM_ALLOCATOR_H
|