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.
Files changed (101) hide show
  1. cbase/__infra__.pxd +6 -0
  2. cbase/__init__.pxd +6 -0
  3. cbase/__init__.py +28 -0
  4. cbase/allocator_protocol/__infra__.pxd +58 -0
  5. cbase/allocator_protocol/__init__.pxd +58 -0
  6. cbase/allocator_protocol/__init__.py +45 -0
  7. cbase/allocator_protocol/c_allocator_protocol.c +31818 -0
  8. cbase/allocator_protocol/c_allocator_protocol.cp313-win_amd64.pyd +0 -0
  9. cbase/allocator_protocol/c_allocator_protocol.h +352 -0
  10. cbase/allocator_protocol/c_allocator_protocol.pxd +64 -0
  11. cbase/allocator_protocol/c_allocator_protocol.pyi +144 -0
  12. cbase/allocator_protocol/c_allocator_protocol.pyx +209 -0
  13. cbase/allocator_protocol/c_heap_allocator.c +38144 -0
  14. cbase/allocator_protocol/c_heap_allocator.cp313-win_amd64.pyd +0 -0
  15. cbase/allocator_protocol/c_heap_allocator.h +444 -0
  16. cbase/allocator_protocol/c_heap_allocator.pxd +97 -0
  17. cbase/allocator_protocol/c_heap_allocator.pyi +161 -0
  18. cbase/allocator_protocol/c_heap_allocator.pyx +300 -0
  19. cbase/allocator_protocol/c_nt_shm_allocator.c +33399 -0
  20. cbase/allocator_protocol/c_nt_shm_allocator.cp313-win_amd64.pyd +0 -0
  21. cbase/allocator_protocol/c_nt_shm_allocator.h +996 -0
  22. cbase/allocator_protocol/c_nt_shm_allocator.pxd +102 -0
  23. cbase/allocator_protocol/c_nt_shm_allocator.pyx +223 -0
  24. cbase/allocator_protocol/c_shm_allocator.h +1168 -0
  25. cbase/allocator_protocol/c_shm_allocator.pxd +142 -0
  26. cbase/allocator_protocol/c_shm_allocator.pyi +299 -0
  27. cbase/allocator_protocol/c_shm_allocator.pyx +442 -0
  28. cbase/allocator_protocol/c_shm_comp.c +4643 -0
  29. cbase/allocator_protocol/c_shm_comp.cp313-win_amd64.pyd +0 -0
  30. cbase/allocator_protocol/c_shm_comp.h +49 -0
  31. cbase/allocator_protocol/c_shm_comp.pxd +38 -0
  32. cbase/allocator_protocol/c_shm_comp.pyx +1 -0
  33. cbase/backports/__infra__.pxd +17 -0
  34. cbase/backports/__init__.pxd +17 -0
  35. cbase/backports/__init__.py +9 -0
  36. cbase/backports/pydict.h +55 -0
  37. cbase/backports/pydict.pxd +5 -0
  38. cbase/backports/pylong.c +9946 -0
  39. cbase/backports/pylong.cp313-win_amd64.pyd +0 -0
  40. cbase/backports/pylong.h +79 -0
  41. cbase/backports/pylong.pxd +23 -0
  42. cbase/backports/pylong.pyx +61 -0
  43. cbase/bytemap/__infra__.pxd +42 -0
  44. cbase/bytemap/__init__.pxd +42 -0
  45. cbase/bytemap/__init__.py +21 -0
  46. cbase/bytemap/c_bytemap.c +52076 -0
  47. cbase/bytemap/c_bytemap.cp313-win_amd64.pyd +0 -0
  48. cbase/bytemap/c_bytemap.h +867 -0
  49. cbase/bytemap/c_bytemap.pxd +370 -0
  50. cbase/bytemap/c_bytemap.pyx +1758 -0
  51. cbase/bytemap/xxh3.h +55 -0
  52. cbase/bytemap/xxhash.c +42 -0
  53. cbase/bytemap/xxhash.h +7487 -0
  54. cbase/env.c +11109 -0
  55. cbase/env.cp313-win_amd64.pyd +0 -0
  56. cbase/env.pxd +8 -0
  57. cbase/env.pyi +48 -0
  58. cbase/env.pyx +46 -0
  59. cbase/includes/cbase/allocator_protocol/c_allocator_protocol.c +31818 -0
  60. cbase/includes/cbase/allocator_protocol/c_allocator_protocol.h +352 -0
  61. cbase/includes/cbase/allocator_protocol/c_heap_allocator.c +38144 -0
  62. cbase/includes/cbase/allocator_protocol/c_heap_allocator.h +444 -0
  63. cbase/includes/cbase/allocator_protocol/c_nt_shm_allocator.c +33399 -0
  64. cbase/includes/cbase/allocator_protocol/c_nt_shm_allocator.h +996 -0
  65. cbase/includes/cbase/allocator_protocol/c_shm_allocator.h +1168 -0
  66. cbase/includes/cbase/allocator_protocol/c_shm_comp.c +4643 -0
  67. cbase/includes/cbase/allocator_protocol/c_shm_comp.h +49 -0
  68. cbase/includes/cbase/backports/pydict.h +55 -0
  69. cbase/includes/cbase/backports/pylong.c +9946 -0
  70. cbase/includes/cbase/backports/pylong.h +79 -0
  71. cbase/includes/cbase/bytemap/c_bytemap.c +52076 -0
  72. cbase/includes/cbase/bytemap/c_bytemap.h +867 -0
  73. cbase/includes/cbase/bytemap/xxh3.h +55 -0
  74. cbase/includes/cbase/bytemap/xxhash.c +42 -0
  75. cbase/includes/cbase/bytemap/xxhash.h +7487 -0
  76. cbase/includes/cbase/env.c +11109 -0
  77. cbase/includes/cbase/int128.h +81 -0
  78. cbase/includes/cbase/intern_string/c_intern_string.c +28081 -0
  79. cbase/includes/cbase/intern_string/c_intern_string.h +614 -0
  80. cbase/includes/cbase/nt/pthread_nt_compat.h +102 -0
  81. cbase/includes/cbase/nt/regex_nt_compat.h +133 -0
  82. cbase/includes/cbase/nt/time_sched_nt_compat.h +32 -0
  83. cbase/int128.h +81 -0
  84. cbase/int128.pxd +15 -0
  85. cbase/intern_string/BENCHMARK.md +157 -0
  86. cbase/intern_string/__infra__.pxd +11 -0
  87. cbase/intern_string/__init__.pxd +11 -0
  88. cbase/intern_string/__init__.py +19 -0
  89. cbase/intern_string/c_intern_string.c +28081 -0
  90. cbase/intern_string/c_intern_string.cp313-win_amd64.pyd +0 -0
  91. cbase/intern_string/c_intern_string.h +614 -0
  92. cbase/intern_string/c_intern_string.pxd +118 -0
  93. cbase/intern_string/c_intern_string.pyi +376 -0
  94. cbase/intern_string/c_intern_string.pyx +765 -0
  95. cbase/nt/pthread_nt_compat.h +102 -0
  96. cbase/nt/regex_nt_compat.h +133 -0
  97. cbase/nt/time_sched_nt_compat.h +32 -0
  98. pycybase-0.1.8.post4.dist-info/METADATA +180 -0
  99. pycybase-0.1.8.post4.dist-info/RECORD +101 -0
  100. pycybase-0.1.8.post4.dist-info/WHEEL +5 -0
  101. pycybase-0.1.8.post4.dist-info/top_level.txt +1 -0
@@ -0,0 +1,996 @@
1
+ #ifndef AP_SHM_C_NT_SHM_ALLOCATOR_H
2
+ #define AP_SHM_C_NT_SHM_ALLOCATOR_H
3
+
4
+ #ifndef _WIN32
5
+ #error "c_nt_shm_allocator.h is intended for Windows builds only"
6
+ #endif
7
+
8
+ #ifndef WIN32_LEAN_AND_MEAN
9
+ #define WIN32_LEAN_AND_MEAN
10
+ #endif
11
+
12
+ #include <errno.h>
13
+ #include <stddef.h>
14
+ #include <stdint.h>
15
+ #include <stdio.h>
16
+ #include <stdlib.h>
17
+ #include <string.h>
18
+ #include <windows.h>
19
+
20
+ #include "cbase/nt/pthread_nt_compat.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
+ // ========== Structs ==========
53
+
54
+ typedef struct nt_shm_page {
55
+ size_t capacity;
56
+ size_t occupied;
57
+ size_t offset;
58
+ struct nt_shm_allocator* allocator;
59
+ struct nt_shm_memory_block* allocated;
60
+ char shm_name[AP_SHM_NAME_LEN];
61
+ char prev_name[AP_SHM_NAME_LEN];
62
+ } nt_shm_page;
63
+
64
+ typedef struct nt_shm_page_ctx {
65
+ nt_shm_page* shm_page;
66
+ HANDLE handle;
67
+ char* buffer;
68
+ struct nt_shm_page_ctx* prev;
69
+ } nt_shm_page_ctx;
70
+
71
+ typedef struct nt_shm_memory_block {
72
+ size_t capacity;
73
+ size_t size;
74
+ struct nt_shm_memory_block* next_free;
75
+ struct nt_shm_memory_block* next_allocated;
76
+ nt_shm_page* parent_page;
77
+ char buffer[];
78
+ } nt_shm_memory_block;
79
+
80
+ typedef struct nt_shm_allocator {
81
+ char shm_name[AP_SHM_NAME_LEN];
82
+ size_t pid;
83
+ char lock_name[AP_SHM_NAME_LEN];
84
+ size_t mapped_size;
85
+ char active_page[AP_SHM_NAME_LEN];
86
+ size_t mapped_pages;
87
+ nt_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
+ pthread_mutex_t lock; // dummy; present for c_allocator_protocol.h compat
93
+ } nt_shm_allocator;
94
+
95
+ typedef struct nt_shm_allocator_ctx {
96
+ nt_shm_allocator* shm_allocator;
97
+ HANDLE shm_handle;
98
+ HANDLE lock_handle;
99
+ nt_shm_page_ctx* active_page;
100
+ } nt_shm_allocator_ctx;
101
+
102
+ // ========== Internal Helpers ==========
103
+
104
+ static const size_t c_nt_shm_page_overhead = (sizeof(nt_shm_page) + sizeof(void*) - 1) & ~(sizeof(void*) - 1);
105
+ static const size_t c_nt_shm_block_overhead = (sizeof(nt_shm_memory_block) + sizeof(void*) - 1) & ~(sizeof(void*) - 1);
106
+
107
+ /**
108
+ * @brief Convert a UTF-8 name to a wide-char name with "Global\\" prefix.
109
+ * @param utf8_name UTF-8 input name (e.g. "/c_cbase_shm_ac_1234_7f...").
110
+ * @param wide_out Output buffer for wide-char name (must be at least AP_SHM_NAME_LEN WCHARs).
111
+ * @return Number of WCHARs written (excluding null terminator), or 0 on error.
112
+ */
113
+ static inline int c_nt_shm_to_wide(const char* utf8_name, wchar_t* wide_out) {
114
+ if (!utf8_name || !wide_out) return 0;
115
+
116
+ // Skip leading '/' in POSIX-style names
117
+ const char* src = utf8_name;
118
+ if (src[0] == '/') src++;
119
+
120
+ // Prepend "Global\\"
121
+ wchar_t* dst = wide_out;
122
+ wcscpy(dst, L"Global\\");
123
+ dst += 7;
124
+
125
+ // Convert ASCII to wide chars
126
+ int count = 7;
127
+ while (*src && count < AP_SHM_NAME_LEN - 1) {
128
+ *dst++ = (wchar_t) (unsigned char) *src++;
129
+ count++;
130
+ }
131
+ *dst = L'\0';
132
+ return count;
133
+ }
134
+
135
+ /**
136
+ * @brief Lock the allocator via the cached named mutex handle.
137
+ */
138
+ static inline int c_nt_shm_lock(nt_shm_allocator_ctx* ctx) {
139
+ if (!ctx || !ctx->lock_handle) return -1;
140
+ DWORD result = WaitForSingleObject(ctx->lock_handle, INFINITE);
141
+ return (result == WAIT_OBJECT_0) ? 0 : -1;
142
+ }
143
+
144
+ /**
145
+ * @brief Unlock the allocator via the cached named mutex handle.
146
+ */
147
+ static inline void c_nt_shm_unlock(nt_shm_allocator_ctx* ctx) {
148
+ if (ctx && ctx->lock_handle) {
149
+ ReleaseMutex(ctx->lock_handle);
150
+ }
151
+ }
152
+
153
+ // ========== Utility Functions ==========
154
+
155
+ static inline size_t c_nt_shm_page_roundup(nt_shm_allocator* allocator, size_t size) {
156
+ return (size + allocator->autopage_alignment - 1) & ~(allocator->autopage_alignment - 1);
157
+ }
158
+
159
+ static inline size_t c_nt_shm_block_roundup(size_t size) {
160
+ return (size + sizeof(void*) - 1) & ~(sizeof(void*) - 1);
161
+ }
162
+
163
+ static inline void c_nt_shm_allocator_name(const char* shm_prefix, char* out) {
164
+ DWORD pid = GetCurrentProcessId();
165
+ snprintf(out, AP_SHM_NAME_LEN, "%s_ac_%lx", shm_prefix, (unsigned long) pid);
166
+ }
167
+
168
+ static inline void c_nt_shm_page_name(nt_shm_allocator* allocator, char* out) {
169
+ DWORD pid = GetCurrentProcessId();
170
+ size_t page_idx = allocator->mapped_pages;
171
+ snprintf(out, AP_SHM_NAME_LEN, "%s_pg_%lx_%lx_%zx", allocator->shm_prefix, (unsigned long) pid, (unsigned long) (uintptr_t) allocator, page_idx);
172
+ }
173
+
174
+ static inline void c_nt_shm_mutex_name(const char* shm_prefix, char* out) {
175
+ DWORD pid = GetCurrentProcessId();
176
+ snprintf(out, AP_SHM_NAME_LEN, "%s_mtx_%lx", shm_prefix, (unsigned long) pid);
177
+ }
178
+
179
+ // ========== Scan / Dangling ==========
180
+
181
+ /**
182
+ * @brief Try to open a file mapping with the given name pattern.
183
+ *
184
+ * Iterates through candidate PIDs to find a matching file mapping.
185
+ * Uses the naming convention: {prefix}_{suffix}_{pid}
186
+ *
187
+ * @param prefix UTF-8 prefix (e.g. "/c_cbase_shm_ac").
188
+ * @param out Output buffer to receive the found UTF-8 name.
189
+ * @param out_len Size of output buffer.
190
+ * @return 0 on success, -1 with errno set otherwise.
191
+ */
192
+ static inline int c_nt_shm_scan(const char* prefix, const char* suffix, char* out, size_t out_len) {
193
+ if (!prefix || !out || !out_len) {
194
+ errno = EINVAL;
195
+ return -1;
196
+ }
197
+
198
+ // Skip leading '/'
199
+ const char* pfx = prefix;
200
+ if (pfx[0] == '/') pfx++;
201
+
202
+ // The full prefix is {prefix}_{suffix}
203
+ char full_prefix[AP_SHM_NAME_LEN];
204
+ if (suffix) {
205
+ snprintf(full_prefix, sizeof(full_prefix), "%s_%s", pfx, suffix);
206
+ }
207
+ else {
208
+ strncpy(full_prefix, pfx, sizeof(full_prefix) - 1);
209
+ full_prefix[sizeof(full_prefix) - 1] = '\0';
210
+ }
211
+
212
+ // Build wide prefix: "Global\\{full_prefix}_"
213
+ wchar_t wide_prefix[AP_SHM_NAME_LEN];
214
+ int prefix_len = swprintf(wide_prefix, AP_SHM_NAME_LEN, L"Global\\%hs_", full_prefix);
215
+ if (prefix_len < 0) {
216
+ errno = EINVAL;
217
+ return -1;
218
+ }
219
+
220
+ // Try current process and a range of nearby PIDs
221
+ DWORD current_pid = GetCurrentProcessId();
222
+ for (DWORD offset = 0; offset < 65536; offset++) {
223
+ DWORD try_pid = (current_pid + offset) % 65536;
224
+ if (try_pid == 0) continue;
225
+
226
+ wchar_t wide_name[AP_SHM_NAME_LEN];
227
+ swprintf(wide_name, AP_SHM_NAME_LEN, L"Global\\%hs_%lx", full_prefix, (unsigned long) try_pid);
228
+
229
+ HANDLE h = OpenFileMappingW(FILE_MAP_READ, FALSE, wide_name);
230
+ if (h != NULL) {
231
+ CloseHandle(h);
232
+ // Convert back to UTF-8
233
+ int ret = WideCharToMultiByte(CP_UTF8, 0, wide_name, -1, out, (int) out_len, NULL, NULL);
234
+ if (ret > 0) {
235
+ return 0;
236
+ }
237
+ }
238
+ }
239
+
240
+ errno = ENOENT;
241
+ return -1;
242
+ }
243
+
244
+ /**
245
+ * @brief Scan for an allocator SHM name (appends "_ac" to prefix).
246
+ */
247
+ static inline int c_nt_shm_scan_allocator(const char* shm_prefix, char* out) {
248
+ if (!shm_prefix) shm_prefix = AP_SHM_ALLOCATOR_PREFIX;
249
+ return c_nt_shm_scan(shm_prefix, "ac", out, AP_SHM_NAME_LEN);
250
+ }
251
+
252
+ /**
253
+ * @brief Scan for a page SHM name (appends "_pg" to prefix).
254
+ */
255
+ static inline int c_nt_shm_scan_page(const char* shm_prefix, char* out) {
256
+ if (!shm_prefix) shm_prefix = AP_SHM_ALLOCATOR_PREFIX;
257
+ return c_nt_shm_scan(shm_prefix, "pg", out, AP_SHM_NAME_LEN);
258
+ }
259
+
260
+ /**
261
+ * @brief Extract pid embedded in allocator/page SHM name.
262
+ */
263
+ static inline DWORD c_nt_shm_pid(const char* shm_name) {
264
+ if (!shm_name) {
265
+ errno = EINVAL;
266
+ return 0;
267
+ }
268
+
269
+ const char* base = shm_name;
270
+ // Skip "Global\" prefix if present
271
+ if (strncmp(base, "Global\\", 7) == 0) base += 7;
272
+ if (base[0] == '/') base++;
273
+
274
+ // Name format: {prefix}_{pid_hex}_{suffix} or {prefix}_{pid_hex}
275
+ const char* suffix_us = strrchr(base, '_');
276
+ if (!suffix_us) {
277
+ errno = EINVAL;
278
+ return 0;
279
+ }
280
+
281
+ const char* pid_us = NULL;
282
+ for (const char* p = base; p < suffix_us; p++) {
283
+ if (*p == '_') pid_us = p;
284
+ }
285
+ if (!pid_us) {
286
+ errno = EINVAL;
287
+ return 0;
288
+ }
289
+
290
+ const char* pid_start = pid_us + 1;
291
+ size_t pid_len = (size_t) (suffix_us - pid_start);
292
+ if (pid_len == 0 || pid_len >= 32) {
293
+ errno = EINVAL;
294
+ return 0;
295
+ }
296
+
297
+ char pidbuf[32];
298
+ memcpy(pidbuf, pid_start, pid_len);
299
+ pidbuf[pid_len] = '\0';
300
+
301
+ errno = 0;
302
+ char* endptr = NULL;
303
+ unsigned long v = strtoul(pidbuf, &endptr, 16);
304
+ if (errno != 0 || endptr == pidbuf || *endptr != '\0') {
305
+ errno = EINVAL;
306
+ return 0;
307
+ }
308
+
309
+ return (DWORD) v;
310
+ }
311
+
312
+ /**
313
+ * @brief Check if a process is still running.
314
+ */
315
+ static inline int c_nt_shm_process_alive(DWORD pid) {
316
+ if (pid == 0) return 0;
317
+ HANDLE h = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid);
318
+ if (!h) return 0;
319
+ DWORD exit_code;
320
+ BOOL ok = GetExitCodeProcess(h, &exit_code);
321
+ CloseHandle(h);
322
+ return ok && exit_code == STILL_ACTIVE;
323
+ }
324
+
325
+ /**
326
+ * @brief Find and map a dangling allocator (whose creator pid is gone).
327
+ */
328
+ static inline nt_shm_allocator* c_nt_shm_allocator_dangling(const char* shm_prefix, char* shm_name) {
329
+ if (!shm_name) {
330
+ errno = EINVAL;
331
+ return NULL;
332
+ }
333
+
334
+ if (!shm_prefix) shm_prefix = AP_SHM_ALLOCATOR_PREFIX;
335
+
336
+ // Build wide prefix: "Global\\{shm_prefix}_ac_"
337
+ const char* pfx = shm_prefix;
338
+ if (pfx[0] == '/') pfx++;
339
+
340
+ wchar_t wide_prefix[AP_SHM_NAME_LEN];
341
+ swprintf(wide_prefix, AP_SHM_NAME_LEN, L"Global\\%hs_ac_", pfx);
342
+
343
+ // Scan PIDs
344
+ DWORD current_pid = GetCurrentProcessId();
345
+ for (DWORD offset = 0; offset < 65536; offset++) {
346
+ DWORD try_pid = (current_pid + offset) % 65536;
347
+ if (try_pid == 0) continue;
348
+
349
+ wchar_t wide_name[AP_SHM_NAME_LEN];
350
+ swprintf(wide_name, AP_SHM_NAME_LEN, L"Global\\%hs_ac_%lx", pfx, (unsigned long) try_pid);
351
+
352
+ if (c_nt_shm_process_alive(try_pid)) continue;
353
+
354
+ HANDLE h = OpenFileMappingW(FILE_MAP_READ | FILE_MAP_WRITE, FALSE, wide_name);
355
+ if (!h) continue;
356
+
357
+ void* map = MapViewOfFile(h, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, sizeof(nt_shm_allocator));
358
+ if (!map) {
359
+ CloseHandle(h);
360
+ continue;
361
+ }
362
+
363
+ // Convert wide name back to UTF-8 for output
364
+ WideCharToMultiByte(CP_UTF8, 0, wide_name, -1, shm_name, AP_SHM_NAME_LEN, NULL, NULL);
365
+ return (nt_shm_allocator*) map;
366
+ }
367
+
368
+ errno = ENOENT;
369
+ return NULL;
370
+ }
371
+
372
+ /**
373
+ * @brief Unlink all dangling allocator/page SHM objects.
374
+ *
375
+ * On Windows, file mappings are destroyed when all handles are closed.
376
+ * This function attempts to open and close any dangling objects, but
377
+ * the actual cleanup happens when the last handle is closed.
378
+ */
379
+ static inline void c_nt_shm_clear_dangling(const char* shm_prefix) {
380
+ if (!shm_prefix) shm_prefix = AP_SHM_ALLOCATOR_PREFIX;
381
+
382
+ const char* pfx = shm_prefix;
383
+ if (pfx[0] == '/') pfx++;
384
+
385
+ const char* suffixes[] = {"ac", "pg"};
386
+ size_t suffix_count = 2;
387
+
388
+ for (DWORD offset = 0; offset < 65536; offset++) {
389
+ DWORD try_pid = (GetCurrentProcessId() + offset) % 65536;
390
+ if (try_pid == 0) continue;
391
+
392
+ if (c_nt_shm_process_alive(try_pid)) continue;
393
+
394
+ for (size_t i = 0; i < suffix_count; i++) {
395
+ wchar_t wide_name[AP_SHM_NAME_LEN];
396
+ swprintf(wide_name, AP_SHM_NAME_LEN, L"Global\\%hs_%hs_%lx", pfx, suffixes[i], (unsigned long) try_pid);
397
+
398
+ HANDLE h = OpenFileMappingW(FILE_MAP_READ, FALSE, wide_name);
399
+ if (h) CloseHandle(h); // Closing our handle helps destroy the mapping
400
+ }
401
+ }
402
+ }
403
+
404
+ // ========== Page Management ==========
405
+
406
+ static inline nt_shm_page_ctx* c_nt_shm_page_new(nt_shm_allocator* allocator, size_t page_capacity) {
407
+ nt_shm_page_ctx* ctx = (nt_shm_page_ctx*) calloc(1, sizeof(nt_shm_page_ctx));
408
+ if (!ctx) return NULL;
409
+
410
+ nt_shm_page* meta = (nt_shm_page*) calloc(1, c_nt_shm_page_overhead);
411
+ if (!meta) {
412
+ free(ctx);
413
+ return NULL;
414
+ }
415
+ ctx->shm_page = meta;
416
+
417
+ meta->capacity = page_capacity;
418
+ meta->occupied = c_nt_shm_page_overhead;
419
+ meta->offset = 0;
420
+
421
+ c_nt_shm_page_name(allocator, meta->shm_name);
422
+
423
+ // Create named file mapping for the page
424
+ wchar_t wide_name[AP_SHM_NAME_LEN];
425
+ if (c_nt_shm_to_wide(meta->shm_name, wide_name) == 0) {
426
+ free(meta);
427
+ free(ctx);
428
+ return NULL;
429
+ }
430
+
431
+ DWORD size_high = (DWORD) (page_capacity >> 32);
432
+ DWORD size_low = (DWORD) (page_capacity & 0xFFFFFFFF);
433
+
434
+ HANDLE hMapping = CreateFileMappingW(
435
+ INVALID_HANDLE_VALUE,
436
+ NULL,
437
+ PAGE_READWRITE,
438
+ size_high,
439
+ size_low,
440
+ wide_name
441
+ );
442
+
443
+ if (!hMapping) {
444
+ free(meta);
445
+ free(ctx);
446
+ return NULL;
447
+ }
448
+
449
+ ctx->shm_page = meta;
450
+ ctx->handle = hMapping;
451
+ ctx->buffer = NULL;
452
+ ctx->prev = NULL;
453
+
454
+ return ctx;
455
+ }
456
+
457
+ static inline int c_nt_shm_page_map(nt_shm_allocator* allocator, nt_shm_page_ctx* page_ctx) {
458
+ if (!allocator || !page_ctx || !page_ctx->shm_page || !page_ctx->handle) {
459
+ errno = EINVAL;
460
+ return -1;
461
+ }
462
+
463
+ size_t page_capacity = page_ctx->shm_page->capacity;
464
+ size_t offset = allocator->mapped_size;
465
+
466
+ // Map view of the file mapping
467
+ void* mapped = MapViewOfFile(page_ctx->handle, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, page_capacity);
468
+ if (!mapped) {
469
+ return -1;
470
+ }
471
+
472
+ // Copy metadata to start of mapped page
473
+ nt_shm_page* page_meta = (nt_shm_page*) mapped;
474
+ memcpy(page_meta, page_ctx->shm_page, c_nt_shm_page_overhead);
475
+ free(page_ctx->shm_page);
476
+ page_ctx->shm_page = page_meta;
477
+ page_ctx->buffer = (char*) mapped;
478
+
479
+ // Set correct offset and linkage
480
+ page_meta->offset = offset;
481
+ page_meta->allocator = allocator;
482
+
483
+ if (allocator->mapped_pages == 0) {
484
+ page_meta->prev_name[0] = '\0';
485
+ }
486
+ else {
487
+ memcpy(page_meta->prev_name, allocator->active_page, AP_SHM_NAME_LEN);
488
+ page_meta->prev_name[AP_SHM_NAME_LEN - 1] = '\0';
489
+ }
490
+
491
+ allocator->mapped_size += page_capacity;
492
+ memcpy(allocator->active_page, page_meta->shm_name, AP_SHM_NAME_LEN);
493
+ allocator->active_page[AP_SHM_NAME_LEN - 1] = '\0';
494
+ allocator->mapped_pages++;
495
+
496
+ return 0;
497
+ }
498
+
499
+ static inline void c_nt_shm_page_reclaim(nt_shm_allocator* allocator, nt_shm_page_ctx* page_ctx) {
500
+ if (!allocator || !page_ctx || !page_ctx->shm_page) {
501
+ errno = EINVAL;
502
+ return;
503
+ }
504
+
505
+ nt_shm_page* page = page_ctx->shm_page;
506
+
507
+ nt_shm_memory_block** prevp = &page->allocated;
508
+ while (*prevp) {
509
+ nt_shm_memory_block* block = *prevp;
510
+ if (block->size != 0) break;
511
+
512
+ *prevp = block->next_allocated;
513
+ block->next_allocated = NULL;
514
+
515
+ nt_shm_memory_block** free_prev = &allocator->free_list;
516
+ while (*free_prev && *free_prev != block) {
517
+ free_prev = &(*free_prev)->next_free;
518
+ }
519
+ if (*free_prev == block) {
520
+ *free_prev = block->next_free;
521
+ }
522
+ block->next_free = NULL;
523
+
524
+ size_t cap_total = block->capacity + c_nt_shm_block_overhead;
525
+ if (page->occupied >= cap_total) {
526
+ page->occupied -= cap_total;
527
+ }
528
+ }
529
+ }
530
+
531
+ // ========== Public API ==========
532
+
533
+ /**
534
+ * @brief Extend allocator with a new page, locking via pthread_mutex if provided.
535
+ */
536
+ static inline nt_shm_page_ctx* c_nt_shm_allocator_extend(nt_shm_allocator_ctx* ctx, size_t capacity, pthread_mutex_t* lock) {
537
+ if (!ctx || !ctx->shm_allocator) {
538
+ errno = EINVAL;
539
+ return NULL;
540
+ }
541
+
542
+ nt_shm_allocator* allocator = ctx->shm_allocator;
543
+ uint8_t locked = 0;
544
+
545
+ if (lock) {
546
+ int ret = pthread_mutex_lock(lock);
547
+ if (ret != 0) {
548
+ errno = ret;
549
+ return NULL;
550
+ }
551
+ locked = 1;
552
+ }
553
+
554
+ // Determine new page capacity
555
+ if (capacity == 0) {
556
+ if (!ctx->active_page) {
557
+ capacity = allocator->autopage_capacity;
558
+ }
559
+ else {
560
+ size_t prev_cap = ctx->active_page->shm_page->capacity;
561
+ capacity = prev_cap * 2;
562
+ if (capacity < allocator->autopage_capacity) {
563
+ capacity = allocator->autopage_capacity;
564
+ }
565
+ else if (capacity > allocator->autopage_capacity_max) {
566
+ capacity = allocator->autopage_capacity_max;
567
+ }
568
+ }
569
+ }
570
+
571
+ size_t aligned_capacity = c_nt_shm_page_roundup(allocator, capacity);
572
+
573
+ // Create new page
574
+ nt_shm_page_ctx* page_ctx = c_nt_shm_page_new(allocator, aligned_capacity);
575
+ if (!page_ctx) {
576
+ if (locked) pthread_mutex_unlock(lock);
577
+ return NULL;
578
+ }
579
+
580
+ // Map new page
581
+ if (c_nt_shm_page_map(allocator, page_ctx) != 0) {
582
+ CloseHandle(page_ctx->handle);
583
+ free(page_ctx->shm_page);
584
+ free(page_ctx);
585
+ if (locked) pthread_mutex_unlock(lock);
586
+ return NULL;
587
+ }
588
+
589
+ // Update context
590
+ page_ctx->prev = ctx->active_page;
591
+ ctx->active_page = page_ctx;
592
+
593
+ if (locked) pthread_mutex_unlock(lock);
594
+ return page_ctx;
595
+ }
596
+
597
+ /**
598
+ * @brief Create a new NT SHM allocator.
599
+ * @param region_size Ignored on NT (pages are independently mapped).
600
+ * @param shm_prefix Custom SHM name prefix (NULL for compile-time default).
601
+ * @return Allocator context or NULL on failure.
602
+ */
603
+ static inline nt_shm_allocator_ctx* c_nt_shm_allocator_new(size_t region_size, const char* shm_prefix) {
604
+ (void) region_size; // Not used on NT -- pages are independently mapped
605
+
606
+ nt_shm_allocator_ctx* ctx = (nt_shm_allocator_ctx*) calloc(1, sizeof(nt_shm_allocator_ctx));
607
+ if (!ctx) return NULL;
608
+
609
+ if (!shm_prefix) shm_prefix = AP_SHM_ALLOCATOR_PREFIX;
610
+
611
+ size_t prefix_len = strnlen(shm_prefix, AP_SHM_PREFIX_MAX);
612
+ if (prefix_len >= AP_SHM_PREFIX_MAX) {
613
+ fprintf(stderr, "c_nt_shm_allocator_new: shm_prefix exceeds AP_SHM_PREFIX_MAX (%zu)\n", (size_t) AP_SHM_PREFIX_MAX);
614
+ abort();
615
+ }
616
+
617
+ // Step 1: Create named file mapping for allocator metadata
618
+ char meta_shm_name[AP_SHM_NAME_LEN];
619
+ c_nt_shm_allocator_name(shm_prefix, meta_shm_name);
620
+
621
+ wchar_t wide_meta_name[AP_SHM_NAME_LEN];
622
+ if (c_nt_shm_to_wide(meta_shm_name, wide_meta_name) == 0) {
623
+ free(ctx);
624
+ return NULL;
625
+ }
626
+
627
+ HANDLE meta_hMapping = CreateFileMappingW(
628
+ INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE,
629
+ 0, sizeof(nt_shm_allocator), wide_meta_name
630
+ );
631
+
632
+ if (!meta_hMapping) {
633
+ free(ctx);
634
+ return NULL;
635
+ }
636
+
637
+ // Step 2: Map allocator metadata into memory
638
+ nt_shm_allocator* meta = (nt_shm_allocator*) MapViewOfFile(
639
+ meta_hMapping, FILE_MAP_READ | FILE_MAP_WRITE,
640
+ 0, 0, sizeof(nt_shm_allocator)
641
+ );
642
+
643
+ if (!meta) {
644
+ CloseHandle(meta_hMapping);
645
+ free(ctx);
646
+ return NULL;
647
+ }
648
+
649
+ // Step 3: Initialize metadata in shared memory
650
+ memset(meta, 0, sizeof(nt_shm_allocator));
651
+
652
+ size_t name_len = strnlen(meta_shm_name, AP_SHM_NAME_LEN - 1);
653
+ memcpy(meta->shm_name, meta_shm_name, name_len);
654
+ meta->shm_name[name_len] = '\0';
655
+ meta->pid = (size_t) GetCurrentProcessId();
656
+ meta->mapped_size = 0;
657
+ meta->mapped_pages = 0;
658
+ meta->active_page[0] = '\0';
659
+ meta->autopage_capacity = AP_SHM_AUTOPAGE_CAPACITY;
660
+ meta->autopage_capacity_max = AP_SHM_AUTOPAGE_CAPACITY_MAX;
661
+ meta->autopage_alignment = AP_SHM_AUTOPAGE_ALIGNMENT;
662
+
663
+ memcpy(meta->shm_prefix, shm_prefix, prefix_len);
664
+ meta->shm_prefix[prefix_len] = '\0';
665
+
666
+ // Step 4: Create named mutex for inter-process synchronization
667
+ c_nt_shm_mutex_name(shm_prefix, meta->lock_name);
668
+ meta->lock_name[AP_SHM_NAME_LEN - 1] = '\0';
669
+
670
+ wchar_t wide_mutex_name[AP_SHM_NAME_LEN];
671
+ if (c_nt_shm_to_wide(meta->lock_name, wide_mutex_name) == 0) {
672
+ UnmapViewOfFile(meta);
673
+ CloseHandle(meta_hMapping);
674
+ free(ctx);
675
+ return NULL;
676
+ }
677
+
678
+ HANDLE lock_handle = CreateMutexW(NULL, FALSE, wide_mutex_name);
679
+ if (!lock_handle) {
680
+ UnmapViewOfFile(meta);
681
+ CloseHandle(meta_hMapping);
682
+ free(ctx);
683
+ return NULL;
684
+ }
685
+
686
+ // Step 5: Initialize intra-process mutex (CRITICAL_SECTION via pthread compat)
687
+ if (pthread_mutex_init(&meta->lock, NULL) != 0) {
688
+ CloseHandle(lock_handle);
689
+ UnmapViewOfFile(meta);
690
+ CloseHandle(meta_hMapping);
691
+ free(ctx);
692
+ return NULL;
693
+ }
694
+
695
+ // Step 6: Fill context
696
+ ctx->shm_allocator = meta;
697
+ ctx->shm_handle = meta_hMapping;
698
+ ctx->lock_handle = lock_handle;
699
+ ctx->active_page = NULL;
700
+
701
+ return ctx;
702
+ }
703
+
704
+ /**
705
+ * @brief Tear down allocator, unmapping pages and closing handles.
706
+ */
707
+ static inline void c_nt_shm_allocator_free(nt_shm_allocator_ctx* ctx) {
708
+ if (!ctx) return;
709
+
710
+ // Step 1: Unmap all pages
711
+ nt_shm_page_ctx* page_ctx = ctx->active_page;
712
+ while (page_ctx) {
713
+ nt_shm_page_ctx* prev = page_ctx->prev;
714
+ nt_shm_page* page_meta = page_ctx->shm_page;
715
+
716
+ if (page_meta) {
717
+ UnmapViewOfFile((void*) page_meta);
718
+ }
719
+
720
+ if (page_ctx->handle) {
721
+ CloseHandle(page_ctx->handle);
722
+ }
723
+
724
+ free(page_ctx);
725
+ page_ctx = prev;
726
+ }
727
+
728
+ // Step 2: Unmap allocator metadata
729
+ nt_shm_allocator* allocator = ctx->shm_allocator;
730
+ if (allocator) {
731
+ pthread_mutex_destroy(&allocator->lock);
732
+ UnmapViewOfFile((void*) allocator);
733
+ }
734
+
735
+ // Step 3: Close handles
736
+ if (ctx->shm_handle) CloseHandle(ctx->shm_handle);
737
+ if (ctx->lock_handle) CloseHandle(ctx->lock_handle);
738
+
739
+ // Step 4: Free context
740
+ free(ctx);
741
+ }
742
+
743
+ static inline void* c_nt_shm_calloc(nt_shm_allocator_ctx* ctx, size_t size, pthread_mutex_t* lock) {
744
+ if (!ctx || !ctx->shm_allocator || size == 0) {
745
+ errno = EINVAL;
746
+ return NULL;
747
+ }
748
+
749
+ size_t cap_net = c_nt_shm_block_roundup(size);
750
+ size_t cap_total = cap_net + c_nt_shm_block_overhead;
751
+ nt_shm_allocator* allocator = ctx->shm_allocator;
752
+
753
+ uint8_t locked = 0;
754
+ pthread_mutex_t* builtin_lock = &allocator->lock;
755
+ pthread_mutex_t* child_lock = &allocator->lock;
756
+ if (lock) {
757
+ if (lock == builtin_lock) child_lock = NULL;
758
+ int ret = pthread_mutex_lock(lock);
759
+ if (ret != 0) {
760
+ errno = ret;
761
+ return NULL;
762
+ }
763
+ locked = 1;
764
+ }
765
+ else {
766
+ child_lock = NULL;
767
+ }
768
+
769
+ // Ensure there's an active page with space
770
+ nt_shm_page_ctx* page_ctx = ctx->active_page;
771
+ if (!page_ctx) {
772
+ size_t target_cap = allocator->autopage_capacity;
773
+ while (target_cap < cap_total + c_nt_shm_page_overhead) {
774
+ target_cap *= 2;
775
+ }
776
+
777
+ page_ctx = c_nt_shm_allocator_extend(ctx, target_cap, child_lock);
778
+ if (!page_ctx) {
779
+ if (locked) pthread_mutex_unlock(lock);
780
+ return NULL;
781
+ }
782
+ }
783
+
784
+ nt_shm_page* page_meta = page_ctx->shm_page;
785
+
786
+ if (page_meta->occupied + cap_total > page_meta->capacity) {
787
+ size_t target_cap = page_meta->capacity;
788
+ if (target_cap < allocator->autopage_capacity) {
789
+ target_cap = allocator->autopage_capacity;
790
+ }
791
+ else if (target_cap < allocator->autopage_capacity_max) {
792
+ target_cap *= 2;
793
+ }
794
+
795
+ while (target_cap < cap_total + c_nt_shm_page_overhead) {
796
+ target_cap *= 2;
797
+ }
798
+ page_ctx = c_nt_shm_allocator_extend(ctx, target_cap, child_lock);
799
+ if (!page_ctx) {
800
+ if (locked) pthread_mutex_unlock(lock);
801
+ return NULL;
802
+ }
803
+ page_meta = page_ctx->shm_page;
804
+ }
805
+
806
+ // Allocate from active page
807
+ size_t offset = page_meta->occupied;
808
+ nt_shm_memory_block* block = (nt_shm_memory_block*) (page_ctx->buffer + offset);
809
+ block->capacity = cap_net;
810
+ block->size = size;
811
+ block->next_free = NULL;
812
+ block->parent_page = page_meta;
813
+ block->next_allocated = page_meta->allocated;
814
+ page_meta->allocated = block;
815
+ page_meta->occupied += cap_total;
816
+
817
+ if (locked) pthread_mutex_unlock(lock);
818
+
819
+ memset(block + 1, 0, cap_net);
820
+ return (void*) block->buffer;
821
+ }
822
+
823
+ static inline void* c_nt_shm_request(nt_shm_allocator_ctx* ctx, size_t size, int scan_all_pages, pthread_mutex_t* lock) {
824
+ if (!ctx || !ctx->shm_allocator || size == 0) {
825
+ errno = EINVAL;
826
+ return NULL;
827
+ }
828
+
829
+ size_t cap_net = c_nt_shm_block_roundup(size);
830
+ size_t cap_total = cap_net + c_nt_shm_block_overhead;
831
+ nt_shm_allocator* allocator = ctx->shm_allocator;
832
+
833
+ uint8_t locked = 0;
834
+ pthread_mutex_t* builtin_lock = &allocator->lock;
835
+ pthread_mutex_t* child_lock = &allocator->lock;
836
+ if (lock) {
837
+ if (lock == builtin_lock) child_lock = NULL;
838
+ int ret = pthread_mutex_lock(lock);
839
+ if (ret != 0) {
840
+ errno = ret;
841
+ return NULL;
842
+ }
843
+ locked = 1;
844
+ }
845
+ else {
846
+ child_lock = NULL;
847
+ }
848
+
849
+ // Step 1: Try free list reuse
850
+ nt_shm_memory_block** prevp = &allocator->free_list;
851
+ nt_shm_memory_block* free_blk = allocator->free_list;
852
+ while (free_blk) {
853
+ if (free_blk->capacity >= cap_net) {
854
+ *prevp = free_blk->next_free;
855
+ free_blk->next_free = NULL;
856
+ free_blk->size = size;
857
+ if (locked) pthread_mutex_unlock(lock);
858
+ memset(free_blk + 1, 0, cap_net);
859
+ return (void*) free_blk->buffer;
860
+ }
861
+ prevp = &free_blk->next_free;
862
+ free_blk = free_blk->next_free;
863
+ }
864
+
865
+ // Step 2: Locate a page with enough space
866
+ nt_shm_page_ctx* target_ctx = NULL;
867
+ if (scan_all_pages) {
868
+ nt_shm_page_ctx* iter = ctx->active_page;
869
+ while (iter) {
870
+ nt_shm_page* meta = iter->shm_page;
871
+ if (meta && meta->occupied + cap_total <= meta->capacity) {
872
+ target_ctx = iter;
873
+ break;
874
+ }
875
+ iter = iter->prev;
876
+ }
877
+ }
878
+ else if (ctx->active_page) {
879
+ nt_shm_page* meta = ctx->active_page->shm_page;
880
+ if (meta && meta->occupied + cap_total <= meta->capacity) {
881
+ target_ctx = ctx->active_page;
882
+ }
883
+ }
884
+
885
+ // Step 3: Extend if no page fits
886
+ if (!target_ctx) {
887
+ nt_shm_page_ctx* current = ctx->active_page;
888
+ size_t target_cap;
889
+
890
+ if (!current) {
891
+ target_cap = allocator->autopage_capacity;
892
+ while (target_cap < cap_total + c_nt_shm_page_overhead) {
893
+ target_cap *= 2;
894
+ }
895
+ }
896
+ else {
897
+ size_t prev_cap = current->shm_page->capacity;
898
+ size_t new_cap = prev_cap;
899
+
900
+ if (new_cap < allocator->autopage_capacity) {
901
+ new_cap = allocator->autopage_capacity;
902
+ }
903
+ else if (new_cap < allocator->autopage_capacity_max) {
904
+ new_cap *= 2;
905
+ }
906
+
907
+ while (new_cap < cap_total + c_nt_shm_page_overhead) {
908
+ new_cap *= 2;
909
+ }
910
+ target_cap = new_cap;
911
+ }
912
+
913
+ target_ctx = c_nt_shm_allocator_extend(ctx, target_cap, child_lock);
914
+ if (!target_ctx) {
915
+ if (locked) pthread_mutex_unlock(lock);
916
+ return NULL;
917
+ }
918
+ }
919
+
920
+ // Step 4: Allocate from target page
921
+ nt_shm_page* page_meta = target_ctx->shm_page;
922
+ size_t offset = page_meta->occupied;
923
+ nt_shm_memory_block* block = (nt_shm_memory_block*) (target_ctx->buffer + offset);
924
+ block->capacity = cap_net;
925
+ block->size = size;
926
+ block->next_free = NULL;
927
+ block->parent_page = page_meta;
928
+ block->next_allocated = page_meta->allocated;
929
+ page_meta->allocated = block;
930
+ page_meta->occupied += cap_total;
931
+
932
+ if (locked) pthread_mutex_unlock(lock);
933
+
934
+ memset(block + 1, 0, cap_net);
935
+ return (void*) block->buffer;
936
+ }
937
+
938
+ static inline void c_nt_shm_free(void* ptr, pthread_mutex_t* lock) {
939
+ if (!ptr) {
940
+ errno = EINVAL;
941
+ return;
942
+ }
943
+
944
+ nt_shm_memory_block* block = (nt_shm_memory_block*) ((char*) ptr - c_nt_shm_block_overhead);
945
+ nt_shm_page* page = block->parent_page;
946
+ if (!page || !page->allocator) {
947
+ errno = EINVAL;
948
+ return;
949
+ }
950
+
951
+ nt_shm_allocator* allocator = page->allocator;
952
+
953
+ uint8_t locked = 0;
954
+ if (lock) {
955
+ int ret = pthread_mutex_lock(lock);
956
+ if (ret != 0) {
957
+ errno = ret;
958
+ return;
959
+ }
960
+ locked = 1;
961
+ }
962
+
963
+ block->size = 0;
964
+ block->next_free = allocator->free_list;
965
+ allocator->free_list = block;
966
+
967
+ if (locked) pthread_mutex_unlock(lock);
968
+ }
969
+
970
+ static inline void c_nt_shm_reclaim(nt_shm_allocator_ctx* ctx, pthread_mutex_t* lock) {
971
+ if (!ctx || !ctx->shm_allocator) {
972
+ errno = EINVAL;
973
+ return;
974
+ }
975
+
976
+ uint8_t locked = 0;
977
+ if (lock) {
978
+ int ret = pthread_mutex_lock(lock);
979
+ if (ret != 0) {
980
+ errno = ret;
981
+ return;
982
+ }
983
+ locked = 1;
984
+ }
985
+
986
+ nt_shm_allocator* allocator = ctx->shm_allocator;
987
+ nt_shm_page_ctx* page_ctx = ctx->active_page;
988
+ while (page_ctx) {
989
+ c_nt_shm_page_reclaim(allocator, page_ctx);
990
+ page_ctx = page_ctx->prev;
991
+ }
992
+
993
+ if (locked) pthread_mutex_unlock(lock);
994
+ }
995
+
996
+ #endif // AP_SHM_C_NT_SHM_ALLOCATOR_H