MMgr v1.0.0
Zero-heap memory manager in C11. One borrowed buffer, carved by asserted offset.
Loading...
Searching...
No Matches
locus_carcerum.h
Go to the documentation of this file.
1/* MMgr - Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2 * SPDX-License-Identifier: AGPL-3.0-or-later OR LicenseRef-Commercial OR LicenseRef-Educational
3 *
4 * Every use falls under AGPL-3.0-or-later unless you hold explicit permission, which is either a
5 * negotiated commercial licensing contract or an educator's license issued to you personally.
6 */
7/**
8 * @file locus_carcerum.h
9 * @brief A prison site: a caller's storage divided into cellblocks, with the warden that holds them.
10 * @author dstroy0 (Douglas Quigg) <dquigg123@gmail.com>
11 * @date 2026-08-29
12 *
13 * @note LocusCarcerum() emits each cellblock's state and the warden that holds them and that every
14 * call goes through, all as initialized data. Nothing runs at startup. A configuration the
15 * asserts reject fails the build.
16 * @note The storage is the caller's. A cellblock is declared over a pool - ParsMemoriaeInternae or
17 * ParsMemoriaeExternum in mmgr.h - and its SeriesCellularum record holds that pool's address.
18 * The linker resolves it. No call computes a cellblock's address from a site and an index,
19 * and which memory a cellblock sits in was settled where its pool was declared.
20 * @note A cellblock has two tiers. The persistent tier runs up from base and the temporary tier runs
21 * down from size. Both boundaries are the two halves of one atomic word, and a take moves its own
22 * half by one fetch-add whose old value carries the other, through the cellblock's turnstiles.
23 * @note A cellblock's declaration picks its security level. MMGR_MINIMUM_SECURITY leaves a cell's
24 * bytes as they are on release. MMGR_MAXIMUM_SECURITY zeroes them. The warden is const:
25 * nothing after the declaration can change the level a cellblock runs at.
26 * @note The public entries are English and the internals are Latin, this filename included.
27 * @note spatium provides the spans over those bytes.
28 */
29#ifndef MMGR_LOCUS_CARCERUM_H
30#define MMGR_LOCUS_CARCERUM_H
31
32#include "mmgr.h"
33
34EMBED_BEGIN_DECLS
35
36/**
37 * @brief Set to 1 to track each tier's high-water mark.
38 *
39 * @note Off by default because the two marks cost a size_t each in every SeriesCellularum, and a
40 * build that never reads them should not carry them.
41 * @note Adds SeriesCellularum::persistent_hw and SeriesCellularum::temporary_hw, and the update that
42 * raises them in carcer_grow, which both tiers reach. One mark per tier: neither counts the
43 * other's bytes.
44 * @note The #ifndef leaves a build's own definition standing, whether it arrives on the command line
45 * or from a header included ahead of this one.
46 * @note Takes 0 or 1 alone. Both readers are #if, which would take any non-zero as set. The check
47 * below keeps a mistyped value from quietly widening every SeriesCellularum.
48 */
49#ifndef MMGR_ENABLE_HW_MEM_CAPACITY_CB
50#define MMGR_ENABLE_HW_MEM_CAPACITY_CB 0
51#endif
52#if (MMGR_ENABLE_HW_MEM_CAPACITY_CB != 0) && (MMGR_ENABLE_HW_MEM_CAPACITY_CB != 1)
53#error "MMGR_ENABLE_HW_MEM_CAPACITY_CB must be 0 or 1"
54#endif
55
56/**
57 * @brief The most owners a program's owner table holds.
58 *
59 * @note The table is the program's one list of owners, laid by MMGR_OWNERS in the order they are
60 * declared, and an owner's place in it is its key. A declaration past this count fails the build.
61 */
62#ifndef MMGR_CARCER_OWNERS
63#define MMGR_CARCER_OWNERS 128u
64#endif
65
66/**
67 * @brief The fragmentation at which a cellblock's mover sorts its movable cells, as a percentage of the
68 * bytes its persistent tier occupies.
69 *
70 * @note mmgr_series_cellularum_fragmentation reads what the build compares against this.
71 */
72#ifndef MMGR_MOVABLE_SETTLE_PERCENT
73#define MMGR_MOVABLE_SETTLE_PERCENT 25u
74#endif
75#if (MMGR_MOVABLE_SETTLE_PERCENT < 1u) || (MMGR_MOVABLE_SETTLE_PERCENT > 100u)
76#error "MMGR_MOVABLE_SETTLE_PERCENT must be from 1 to 100"
77#endif
78
79/** @brief The milliseconds between the mover's readings of its cellblock. */
80#ifndef MMGR_MOVABLE_READ_MS
81#define MMGR_MOVABLE_READ_MS 100u
82#endif
83
84/** @brief The readings in a row that must find the cellblock quiet before the mover sorts. */
85#ifndef MMGR_MOVABLE_QUIET_READINGS
86#define MMGR_MOVABLE_QUIET_READINGS 3u
87#endif
88
89/** @brief The longest the mover waits between readings after a sort it backed off from. */
90#ifndef MMGR_MOVABLE_BACKOFF_MOST_MS
91#define MMGR_MOVABLE_BACKOFF_MOST_MS 25600u
92#endif
93
94#include "embed_atomic.h"
95#include "embed_turnstile.h"
96
97/**
98 * @brief Alignment every prisoner is handed out at: one machine word, or the atomic size word's alignment where
99 * that is wider.
100 *
101 * @note Derived from the two types in place of a number. Every cell carries an atomic size word at its header
102 * and at its foot, and a cell's offset and payload are whole multiples of this, and each of those words
103 * lands at its own alignment at every width.
104 * @note LocusCarcerum() puts this alignment on the storage it declares. This rounds only
105 * running offsets inside a cellblock.
106 */
107#define MMGR_CARCER_ALIGN \
108 ((sizeof(embed_word) > _Alignof(embed_atomic_usize)) ? (size_t)sizeof(embed_word) \
109 : (size_t)_Alignof(embed_atomic_usize))
110
111/**
112 * @brief Asserts MMGR_CARCER_ALIGN is a power of two.
113 *
114 * @note An offset is rounded by masking off its low bits, which lands on a multiple only for a power
115 * of two. carcer_round and CARCER_HDR in locus_carcerum.c both round that way.
116 */
117EMBED_STATIC_ASSERT((MMGR_CARCER_ALIGN & (MMGR_CARCER_ALIGN - 1u)) == 0u,
118 "the cellblock rounds offsets by masking, which needs a power of two alignment");
119
120/**
121 * @brief Asserts the storage a pool declaration aligns is aligned to the cell grain.
122 *
123 * @note A cell's words lie at whole grains from the storage's first byte, and they stand at their alignment only
124 * where that byte does.
125 */
126EMBED_STATIC_ASSERT((MMGR_ALIGN_BYTES % MMGR_CARCER_ALIGN) == 0u,
127 "MMGR_ALIGN_BYTES must be a multiple of MMGR_CARCER_ALIGN, the grain a cell's words stand on");
128
129/**
130 * @brief One owner of cells: its name, its key, the payload bytes of the cells it has now on both
131 * tiers, and the most it had at once.
132 *
133 * @note The library's storage, laid at compile time. The owner table is the list of these records
134 * MMGR_OWNERS declares in the program's one translation unit for it, and the same declaration
135 * defines each owner's identity: a const pointer into the table the linker resolves, which the
136 * owner's module names on every take, give and growth, as a movable cell's entry is named. Nothing
137 * registers at run time. The pointer's const says only that the holder does not write; the
138 * record is MMgr's to write. Every entry refuses NULL in place of an owner, and every take
139 * refuses a pointer that is not one of the table's.
140 * @note The key is the owner's place in the table, counted from 1.
141 * @note A take adds the cell's payload bytes to in_use and raises hw to it; a give takes the same
142 * bytes off. Every count is an atomic word, added to and raised in one step each, and an owner
143 * shared between contexts counts exactly.
144 * @note The bytes counted are the cell's own: the request rounded to a whole word, counted on the take
145 * and read off the cell's word on the give, and a matched pair returns in_use to where it stood.
146 * @note Every cell carries its owner in the word behind its payload, and comes off that owner as a give
147 * drops it. A give or a growth naming another owner is refused and told through
148 * mmgr_carcer_hw_misnamed.
149 */
150typedef struct
151{
152 const char *name; /**< The owner's name, as a reader prints it, unique in the table [BORROWS]. */
153 size_t place; /**< The owner's place in the table, from 1. */
154 embed_atomic_usize in_use; /**< Payload bytes of the cells the owner has now. */
155 embed_atomic_usize hw; /**< Running maximum of in_use. */
156} CellOwner;
157
158/**
159 * @brief The owner word a cell carries behind its payload, one machine word laid as fields, high to low: the
160 * owner's place in the table, the completion bit, signaling bits, and the nanosecond the cell was taken on
161 * the library's clock, rolling over its bits.
162 *
163 * @note The owner test is one AND: the word's place field against an owner's place. A word whose place field
164 * is 0 names no owner, as a free cell's does. The completion bit is set once, by the one context whose
165 * swap lands, when the cell's bytes come off its owner: two gives reaching the same cell count it off
166 * once between them. The signaling bits are the cellblock's to raise and read with one atomic
167 * step each. The timestamp is what the cell's take read on mmgr_carcer_hw_clock_ns, in the bits left,
168 * and a reader of lifetimes takes it as rolling.
169 * @note The place field is sized from MMGR_CARCER_OWNERS, the fewest bits every owner's place fits, from 4 to
170 * 16; then 1 completion bit, 3 signaling bits, and the timestamp in the bits left, the word's
171 * width less those: 1024 owners on a 64-bit word leave 49 timestamp bits, on a 32-bit word 17, rolling
172 * every 131 microseconds, and on a 16-bit word 1. The layout fails the build where no timestamp bit is left.
173 */
174#if MMGR_CARCER_OWNERS < 0x10u
175#define MMGR_OWNER_PLACE_BITS 4u
176#elif MMGR_CARCER_OWNERS < 0x20u
177#define MMGR_OWNER_PLACE_BITS 5u
178#elif MMGR_CARCER_OWNERS < 0x40u
179#define MMGR_OWNER_PLACE_BITS 6u
180#elif MMGR_CARCER_OWNERS < 0x80u
181#define MMGR_OWNER_PLACE_BITS 7u
182#elif MMGR_CARCER_OWNERS < 0x100u
183#define MMGR_OWNER_PLACE_BITS 8u
184#elif MMGR_CARCER_OWNERS < 0x200u
185#define MMGR_OWNER_PLACE_BITS 9u
186#elif MMGR_CARCER_OWNERS < 0x400u
187#define MMGR_OWNER_PLACE_BITS 10u
188#elif MMGR_CARCER_OWNERS < 0x800u
189#define MMGR_OWNER_PLACE_BITS 11u
190#elif MMGR_CARCER_OWNERS < 0x1000u
191#define MMGR_OWNER_PLACE_BITS 12u
192#elif MMGR_CARCER_OWNERS < 0x2000u
193#define MMGR_OWNER_PLACE_BITS 13u
194#elif MMGR_CARCER_OWNERS < 0x4000u
195#define MMGR_OWNER_PLACE_BITS 14u
196#elif MMGR_CARCER_OWNERS < 0x8000u
197#define MMGR_OWNER_PLACE_BITS 15u
198#elif MMGR_CARCER_OWNERS < 0x10000u
199#define MMGR_OWNER_PLACE_BITS 16u
200#else
201#error "MMGR_CARCER_OWNERS must be below 65536: the owner word's place field is 16 bits at the most"
202#endif
203#define MMGR_OWNER_SIGNAL_BITS 3u
204EMBED_STATIC_ASSERT(EMBED_WORD_BITS > (MMGR_OWNER_PLACE_BITS + 1u + MMGR_OWNER_SIGNAL_BITS),
205 "the owner word must have a timestamp bit left after its place, completion and signaling bits");
206#define MMGR_OWNER_TIME_BITS (EMBED_WORD_BITS - MMGR_OWNER_PLACE_BITS - 1u - MMGR_OWNER_SIGNAL_BITS)
207#define MMGR_OWNER_TIME_MASK (((embed_atomic_usize_value)1u << MMGR_OWNER_TIME_BITS) - 1u)
208#define MMGR_OWNER_SIGNAL_SHIFT MMGR_OWNER_TIME_BITS
209#define MMGR_OWNER_COMPLETE ((embed_atomic_usize_value)1u << (MMGR_OWNER_TIME_BITS + MMGR_OWNER_SIGNAL_BITS))
210#define MMGR_OWNER_PLACE_SHIFT (MMGR_OWNER_TIME_BITS + MMGR_OWNER_SIGNAL_BITS + 1u)
211#define MMGR_OWNER_PLACE_MASK ((((embed_atomic_usize_value)1u << MMGR_OWNER_PLACE_BITS) - 1u) << MMGR_OWNER_PLACE_SHIFT)
212EMBED_STATIC_ASSERT((size_t)MMGR_CARCER_OWNERS < ((size_t)1u << MMGR_OWNER_PLACE_BITS),
213 "MMGR_CARCER_OWNERS must fit the owner word's place field");
214
215/**
216 * @brief The library's clock in nanoseconds, which a cell's owner word carries the take on.
217 *
218 * @return The nanoseconds since a start the port chooses, wrapping at 2^64.
219 * @note Weak, and the default answers 0 every time: a build that defines none stamps every take 0. A port
220 * defines this name to replace it.
221 */
223
224/**
225 * @brief The program's owner table and the owners in it, which MMGR_OWNERS defines once.
226 *
227 * @note A program that reaches a cellblock declares its owners with MMGR_OWNERS in one translation unit;
228 * a build with none fails at the link, naming these.
229 */
231extern const size_t mmgr_owner_table_count;
232
233/**
234 * @brief One owner's place in the table, counted from 0, as the enumeration MMGR_OWNERS lays names it.
235 *
236 * @param[in] name_ The owner's identity, as declared.
237 * @param[in] text_ The owner's name as a reader prints it, unread here.
238 */
239#define MMGR_OWNER_PLACE(name_, text_) mmgr_owner_place_##name_,
240
241/**
242 * @brief One owner's record as the table's initializer lays it: its name, its place from 1, nothing counted.
243 *
244 * @param[in] name_ The owner's identity, as declared.
245 * @param[in] text_ The owner's name as a reader prints it [BORROWS].
246 */
247#define MMGR_OWNER_RECORD(name_, text_) \
248 {(text_), (size_t)mmgr_owner_place_##name_ + 1u, EMBED_ATOMIC_INIT(0u), EMBED_ATOMIC_INIT(0u)},
249
250/**
251 * @brief One owner's identity defined: a const pointer to its record in the table, resolved by the linker.
252 *
253 * @param[in] name_ The owner's identity, as declared.
254 * @param[in] text_ The owner's name as a reader prints it, unread here.
255 */
256#define MMGR_OWNER_POINTER(name_, text_) const CellOwner *const name_ = &mmgr_owner_table[mmgr_owner_place_##name_];
257
258/**
259 * @brief One owner's identity declared, for the header the owner's module reads it through.
260 *
261 * @param[in] name_ The owner's identity, as declared.
262 * @param[in] text_ The owner's name as a reader prints it, unread here.
263 */
264#define MMGR_OWNER_EXTERN(name_, text_) extern const CellOwner *const name_;
265
266/**
267 * @brief The program's owners declared as data: the table laid, its count, and each owner's identity defined.
268 *
269 * @param[in] list_ The program's list of owners, a macro taking one macro and applying it to each
270 * owner as list_(macro_) expanding to macro_(name_, text_) for every owner, name_ the
271 * identity the owner's module names and text_ the name a reader prints.
272 * @note Expanded once, in the translation unit that owns the program's memory. The enumeration gives each
273 * owner its place, the table is sized to the owners declared and holds their records in that order,
274 * and each identity is a pointer into it the linker resolves: there is no registration and nothing
275 * runs before the first take. A list longer than MMGR_CARCER_OWNERS fails the build.
276 */
277#define MMGR_OWNERS(list_) \
278 enum \
279 { \
280 list_(MMGR_OWNER_PLACE) mmgr_owner_table_laid \
281 }; \
282 EMBED_STATIC_ASSERT((size_t)mmgr_owner_table_laid <= (size_t)MMGR_CARCER_OWNERS, \
283 "more owners are declared than MMGR_CARCER_OWNERS allows"); \
284 CellOwner mmgr_owner_table[(size_t)mmgr_owner_table_laid] = {list_(MMGR_OWNER_RECORD)}; \
285 const size_t mmgr_owner_table_count = (size_t)mmgr_owner_table_laid; \
286 list_(MMGR_OWNER_POINTER)
287
288/**
289 * @brief Whether an owner is one of the table's.
290 *
291 * @param[in] owner The owner, which may be NULL [BORROWS].
292 * @return EMBED_TRUE where it is the record standing at its own place in the table.
293 */
294embed_bool mmgr_owner_listed(const CellOwner *owner);
295
296/**
297 * @brief The payload bytes of the cells an owner has now.
298 *
299 * @param[in] owner The owner [BORROWS].
300 * @return The bytes, off one word.
301 */
302EMBED_INLINE size_t mmgr_owner_in_use(const CellOwner *owner)
303{
304 // Explicit cast reads the word as the bytes it counts
305 return (size_t)embed_atomic_usize_load(&owner->in_use);
306}
307
308/**
309 * @brief The most payload bytes an owner has had at once.
310 *
311 * @param[in] owner The owner [BORROWS].
312 * @return The bytes, off one word.
313 */
314EMBED_INLINE size_t mmgr_owner_hw(const CellOwner *owner)
315{
316 // Explicit cast reads the word as the bytes it counts
317 return (size_t)embed_atomic_usize_load(&owner->hw);
318}
319
320/**
321 * @brief Told of each give or growth that names an owner other than the one the cell counts to.
322 *
323 * @param[in] named The owner the give or the growth named [BORROWS].
324 * @param[in] cell_owner The owner the cell counts to, NULL where its owner word names no registered
325 * owner [BORROWS].
326 * @param[in] bytes The cell's payload bytes.
327 * @note Such a give or growth is refused and changes nothing: the cell stays with its holder, counted
328 * to its owner. A program that sees this has a give in the wrong place, and a port logs it.
329 * @note Weak, and the default does nothing. A port defines this name to replace it.
330 */
331void mmgr_carcer_hw_misnamed(const CellOwner *named, const CellOwner *cell_owner, size_t bytes);
332
333/**
334 * @brief The bits each boundary has in the word that carries both, half the atomic size word, and the mask of a
335 * half.
336 */
337#define MMGR_CARCER_HALF_BITS ((unsigned int)((sizeof(embed_atomic_usize_value) * 8u) / 2u))
338#define MMGR_CARCER_HALF_MASK ((embed_atomic_usize_value)(((embed_atomic_usize_value)1u << MMGR_CARCER_HALF_BITS) - 1u))
339
340/**
341 * @brief The most bytes one cellblock may hold: half a boundary's reach. The other half is the headroom the
342 * bumps a round refuses move the boundary into before the next writer settles it.
343 *
344 * @note LocusCarcerum() asserts each pool against this. A cell's header carries a size this large in the bits
345 * above its two state bits.
346 */
347#define MMGR_CARCER_BYTES_MOST ((size_t)1u << (MMGR_CARCER_HALF_BITS - 1u))
348
349/**
350 * @brief The most payload one bump may ask: the headroom shared by the bumps in flight on the turnstiles' lanes,
351 * each of which may move its boundary once past MMGR_CARCER_BYTES_MOST before the refusal stops the rest.
352 * A take past it goes to the writer's path at once.
353 */
354#define MMGR_CARCER_WANT_MOST ((size_t)1u << (MMGR_CARCER_HALF_BITS - 8u))
355
356/**
357 * @brief One cellblock's state: its bytes, the two boundaries that grow toward each other, the closed word a
358 * writer holds, the sink a refused store lands on, and the turnstiles every pass goes through.
359 *
360 * @note Written by the declaration that emits the cellblock and by the entries that cellblock owns. The base is
361 * the cellblock's own storage, and nothing derives an address from a site and an index.
362 * @note bounds is the two boundaries in one word: its low half the offset just past the last persistent cell,
363 * its high half the offset of the lowest temporary cell, each in bytes. A take bumps its tier's half by one
364 * fetch-add on the word, whose old value carries the other boundary as it stood at that same instant: the
365 * room the take reads. A bump the gap cannot meet leaves the boundary where it moved it and adds its
366 * bytes to the tier's overrun, persistent_overrun or temporary_overrun; every later bump of either tier is
367 * refused, since the boundaries only move toward each other between writers and every bump reads them as
368 * one word, and the next writer takes the overrun back off the boundary before it reads the tier. Between a
369 * refusal and that writer the boundary stands past the tier's cells by the overrun. The algorithm is
370 * written out in docs/concepts/locus-carcerum-algorithm.md and checked in theory/locus_carcerum.
371 * @note closed is the writer's word: bit 0 while a compaction, a growth or a move works alone, bit 1
372 * while a context sleeps on the word. A pass that reads bit 0 set touches nothing and tries once more after
373 * a backoff step. sink is the word every store of a pass that touches nothing lands on.
374 * @note turnstiles is the bank of lanes a pass badges in at, EmbedTurnstiles' way, which a writer drains after
375 * setting the closed bit. Zero at rest, as the last member, which a declaration leaves to the zeroing of
376 * static storage.
377 * @note in_use is this cellblock's running total, counted where each owner's is; persistent_in_use is the part
378 * of it lying in the persistent tier, which a sort reads. movable is the option mmgr_movable_init assigns,
379 * and reads zero without it.
380 */
381typedef struct
382{
383 uint8_t *const base; /**< First byte of the cellblock [BORROWS]. */
384 const size_t size; /**< Bytes in the cellblock. */
385 embed_atomic_usize bounds; /**< The persistent end in the low half, the temporary top in the high. */
386 embed_atomic_usize
387 persistent_overrun; /**< Bytes refused takes moved the persistent end by since the last writer. */
388 embed_atomic_usize temporary_overrun; /**< Bytes refused takes moved the temporary top by since the last writer. */
389 embed_atomic_usize in_use; /**< Payload bytes of the cells taken and not given back. */
390 embed_atomic_usize persistent_in_use; /**< The part of in_use lying in the persistent tier. */
391 embed_atomic_uptr movable; /**< The movable option's state, as an integer, zero without it. */
392 embed_atomic_word closed; /**< The writer's bit, and the sleeper flag above it. */
393 embed_atomic_usize sink; /**< Where a store that must not land lands. */
394#if MMGR_ENABLE_HW_MEM_CAPACITY_CB
395 embed_atomic_usize persistent_hw; /**< Running maximum of the persistent end. */
396 embed_atomic_usize temporary_hw; /**< Running maximum of the bytes taken from the top. */
397#endif
398 EmbedTurnstiles turnstiles; /**< The lanes a pass badges in at. */
400
401/** @brief The high-water members' first values, or nothing where the build tracks none. */
402#if MMGR_ENABLE_HW_MEM_CAPACITY_CB
403#define MMGR_CARCER_STATE_HW , EMBED_ATOMIC_INIT(0u), EMBED_ATOMIC_INIT(0u)
404#else
405#define MMGR_CARCER_STATE_HW
406#endif
407
408/**
409 * @brief A cellblock's state as it stands over bytes_ bytes at base_ with no cell taken: both tiers
410 * empty, every mark at zero, and the turnstiles left to the zeroing of the storage they stand in.
411 *
412 * @param[in] base_ First byte of the cellblock [BORROWS].
413 * @param[in] bytes_ Bytes in the cellblock.
414 */
415#define MMGR_CARCER_STATE(base_, bytes_) \
416 {(base_), \
417 (bytes_), \
418 EMBED_ATOMIC_INIT((embed_atomic_usize_value)(bytes_) << MMGR_CARCER_HALF_BITS), \
419 EMBED_ATOMIC_INIT(0u), \
420 EMBED_ATOMIC_INIT(0u), \
421 EMBED_ATOMIC_INIT(0u), \
422 EMBED_ATOMIC_INIT(0u), \
423 EMBED_ATOMIC_INIT(0u), \
424 EMBED_ATOMIC_INIT(0u), \
425 EMBED_ATOMIC_INIT(0u) MMGR_CARCER_STATE_HW, \
426 {0}}
427
428/**
429 * @brief The identity a movable cell's taker holds: a const pointer to the cell's entry in the
430 * cellblock's translation table.
431 *
432 * @note The entry is the mutable word, and it is MMgr's alone: it names where the cell stands now, and a
433 * move changes it. The pointer the caller holds never changes while the cell lives, however often
434 * the cell moves, and the layout is declared nowhere a caller can reach.
435 */
437
438/**
439 * @brief What a program gives a cellblock for the movable option, once, through its guard's movable_init.
440 *
441 * @note The option is the program's to choose. A cellblock with none takes and gives as it always does,
442 * each entry testing one word, and its movable_buf_alloc answers NULL.
443 * @note storage holds the table, its claim bits and the rows. It lies outside the prison, and so nothing of the
444 * option pins a tier or counts as a cell. A pool declared at compile time gives it a link-time address.
445 * @note width bounds the lanes the cellblock's turnstiles may have open while the mover reads it quiet: twice the
446 * lanes open at or under width plus one, with no pass since the mover's last reading. The system's logical
447 * processors bound it: no more contexts than that run at once.
448 * @note rows are the rows of touches, one a lane and the last for the lanes past them, which
449 * mmgr_movable_touches reads.
450 * @note thread_started and thread_wait together are the mover's thread. With either unset, nothing moves a cell
451 * on its own, and mmgr_movable_buf_move remains for a caller to call. thread_id badges a pass that names
452 * no owner; thread_yield runs between a sort's moves, with the cellblock open.
453 * @note thread_wait answers EMBED_FALSE to end the mover. thread_moving, where set, is told as each sort's moves
454 * start and end, for a mover at low priority to raise itself across the copies a reader may wait on.
455 */
456typedef struct
457{
458 void *const storage; /**< Bytes for the option, zero as given [BORROWS]. */
459 const size_t storage_bytes; /**< Bytes at storage, MMGR_MOVABLE_STORAGE_BYTES at least. */
460 const size_t cells; /**< Movable cells the table can name at once. */
461 const size_t width; /**< The lanes open bound the mover reads quiet under, one at the fewest. */
462 const size_t rows; /**< Rows of touches, one a lane, the last shared by the lanes past it. */
463 embed_bool (*const thread_started)(void (*run)(void *context), void *context); /**< Starts the mover. */
464 size_t (*const thread_id)(void); /**< The calling thread's id, for a pass naming no owner. */
465 embed_bool (*const thread_wait)(uint32_t milliseconds); /**< Sleeps the mover, EMBED_FALSE to end it. */
466 void (*const thread_yield)(void); /**< Gives up the processor between a sort's moves, where set. */
467 void (*const thread_moving)(embed_bool moving); /**< Told as each move starts and ends, where set. */
469
470/**
471 * @brief Arguments for the calls that reach a movable cell's bytes, each reading the members it names.
472 *
473 * @note offset and bytes are checked against the cell, and a span past its end is refused.
474 */
475typedef struct
476{
477 const CarcerMovableEntry *const cell; /**< The cell [BORROWS]. */
478 const CarcerMovableEntry *const other_cell; /**< The cell compare orders it against, NULL for src. */
479 const size_t offset; /**< Where in the cell the call starts. */
480 const size_t other_offset; /**< Where in other_cell compare starts. */
481 void *const dst; /**< Where read copies to [BORROWS]. */
482 const void *const src; /**< What write copies from and compare orders against [BORROWS]. */
483 const size_t bytes; /**< Bytes the call covers. */
485
486/** @brief Bytes of the option's state ahead of its turnstiles, which locus_carcerum.c asserts it fits. */
487#define MMGR_MOVABLE_HEAD_BYTES 192u
488
489/** @brief The bits of one word of the option's claim bits: one an entry. */
490#define MMGR_MOVABLE_CLAIM_BITS ((size_t)(8u * sizeof(embed_atomic_usize)))
491
492/**
493 * @brief The storage the movable option takes for a table of cells_ entries and rows_ rows of touches.
494 *
495 * @param[in] cells_ Movable cells the table can name at once.
496 * @param[in] width_ The lanes open bound the mover reads quiet under, which takes no storage.
497 * @param[in] rows_ Rows of touches.
498 * @note A constant expression, so that a pool can be declared at this size: the state, one word an entry, one
499 * claim bit an entry in whole words, and two words a row.
500 */
501#define MMGR_MOVABLE_STORAGE_BYTES(cells_, width_, rows_) \
502 (MMGR_MOVABLE_HEAD_BYTES + \
503 (((cells_) + (((cells_) + MMGR_MOVABLE_CLAIM_BITS - 1u) / MMGR_MOVABLE_CLAIM_BITS) + (2u * (rows_))) * \
504 sizeof(embed_atomic_usize)))
505
506/**
507 * @brief Offset just past the last persistent cell.
508 *
509 * @param[in] series Cellblock to read [BORROWS].
510 * @return The persistent end, in bytes.
511 * @note The end as the word holds it. A trim a give left owed on the open word does not run here;
512 * mmgr_buf_available and the next take run it.
513 */
515{
516 // Explicit cast reads the word's low half as the offset it holds
517 return (size_t)(embed_atomic_usize_load(&series->bounds) & MMGR_CARCER_HALF_MASK);
518}
519
520/**
521 * @brief Offset of the lowest temporary cell.
522 *
523 * @param[in] series Cellblock to read [BORROWS].
524 * @return The temporary top, in bytes.
525 * @note The top as the word holds it. A trim a give left owed on the open word does not run here;
526 * mmgr_buf_available and the next take run it.
527 */
529{
530 // Explicit cast reads the word's high half as the offset it holds
531 return (size_t)(embed_atomic_usize_load(&series->bounds) >> MMGR_CARCER_HALF_BITS);
532}
533
534/**
535 * @brief Whether no cell is taken from a cellblock.
536 *
537 * @param[in] series Cellblock to read [BORROWS].
538 * @return EMBED_TRUE where both tiers stand empty.
539 */
540EMBED_INLINE embed_bool mmgr_series_cellularum_idle(const SeriesCellularum *series)
541{
542 // Explicit cast narrows the int result of the test to the embed_bool container
543 return (embed_bool)((mmgr_series_cellularum_persistent_end(series) == 0u) &&
544 (mmgr_series_cellularum_temporary_top(series) == series->size));
545}
546
547/**
548 * @brief The furthest a cellblock's persistent tier has reached.
549 *
550 * @param[in] series Cellblock to read [BORROWS].
551 * @return The bytes, 0 where the build tracks none (MMGR_ENABLE_HW_MEM_CAPACITY_CB).
552 */
554{
555#if MMGR_ENABLE_HW_MEM_CAPACITY_CB
556 // Explicit cast reads the word as the bytes it counts
557 return (size_t)embed_atomic_usize_load(&series->persistent_hw);
558#else
559 (void)series;
560 return 0u;
561#endif
562}
563
564/**
565 * @brief The most bytes a cellblock's temporary tier has taken from the top.
566 *
567 * @param[in] series Cellblock to read [BORROWS].
568 * @return The bytes, 0 where the build tracks none (MMGR_ENABLE_HW_MEM_CAPACITY_CB).
569 */
571{
572#if MMGR_ENABLE_HW_MEM_CAPACITY_CB
573 // Explicit cast reads the word as the bytes it counts
574 return (size_t)embed_atomic_usize_load(&series->temporary_hw);
575#else
576 (void)series;
577 return 0u;
578#endif
579}
580
581/**
582 * @brief The minimum security guard over one cellblock: a released cell keeps whatever is in it.
583 *
584 * @note EMBED_TABLE_LAYOUT asserts the thirteen members sit at consecutive EMBED_FUNCTION_POINTER_BYTES offsets,
585 * with nothing else.
586 * @note Every entry is bound to the cellblock its declaration named. No call carries a cellblock
587 * argument and there is nothing to pass wrongly. A cellblock at this level has no zeroing
588 * release. The difference between the two levels is what exists, not what a caller remembers
589 * to reach for.
590 * @note A cell of either tier is given back the same way, through persistent_buf_release, which reads the tier off
591 * the cell's offset.
592 */
593typedef struct
594{
595 void *(*persistent_buf_alloc)(
596 size_t size,
597 const CellOwner *owner); /**< Takes size bytes from the bottom for owner, unzeroed [RETURNS OWNERSHIP]. */
599 void *prisoner,
600 const CellOwner *owner); /**< Releases owner's prisoner of either tier, cell left as it is [TAKES OWNERSHIP]. */
601 void *(*temporary_buf_alloc)(
602 size_t size,
603 const CellOwner *owner); /**< Takes size bytes from the top for owner, unzeroed [RETURNS OWNERSHIP]. */
604 embed_bool (*who_owns_buf)(const void *at); /**< Whether at lies in this cellblock's bytes [BORROWS]. */
605 size_t (*buf_available)(void); /**< Bytes between the two tiers. */
606 void *(*buf_grow)(
607 void *prisoner, size_t size,
608 const CellOwner *owner); /**< Grows the newest cell of its tier to size bytes in place, unzeroed. */
609 embed_bool (*movable_init)(const CarcerMovableInitCfg *args); /**< Assigns the movable option, once. */
610 const CarcerMovableEntry *(*movable_buf_alloc)(
611 size_t size, const CellOwner *owner); /**< Takes a cell a move may move: its identity, NULL where refused. */
612 embed_bool (*movable_buf_release)(const CarcerMovableEntry *cell,
613 const CellOwner *owner); /**< Releases owner's movable cell, left as it is. */
614 embed_bool (*movable_buf_read)(const CarcerMovableCfg *args); /**< Copies out of a movable cell. */
615 embed_bool (*movable_buf_write)(const CarcerMovableCfg *args); /**< Copies into a movable cell. */
616 embed_iword (*movable_buf_compare)(const CarcerMovableCfg *args); /**< Orders a movable cell's bytes. */
617 size_t (*movable_buf_measure)(const CarcerMovableCfg *args); /**< Bytes before a movable cell's next zero. */
619EMBED_TABLE_LAYOUT(MinimumSecurityGuard, persistent_buf_alloc, persistent_buf_release, temporary_buf_alloc,
620 who_owns_buf, buf_available, buf_grow, movable_init, movable_buf_alloc, movable_buf_release,
621 movable_buf_read, movable_buf_write, movable_buf_compare, movable_buf_measure);
622
623/**
624 * @brief The maximum security guard over one cellblock: every release zeroes the cell first.
625 *
626 * @note EMBED_TABLE_LAYOUT asserts the thirteen members sit at consecutive EMBED_FUNCTION_POINTER_BYTES offsets,
627 * with nothing else.
628 * @note The same thirteen entries, and no unzeroed release among them. Each zeroing runs before the
629 * cell's header goes free: the bytes are zero at the instant they become available.
630 */
631typedef struct
632{
633 void *(*persistent_buf_alloc)(
634 size_t size, const CellOwner *owner); /**< Takes size bytes from the bottom for owner [RETURNS OWNERSHIP]. */
636 void *prisoner,
637 const CellOwner *owner); /**< Zeroes owner's cell of either tier, then releases it [TAKES OWNERSHIP]. */
638 void *(*temporary_buf_alloc)(
639 size_t size, const CellOwner *owner); /**< Takes size bytes from the top for owner [RETURNS OWNERSHIP]. */
640 embed_bool (*who_owns_buf)(const void *at); /**< Whether at lies in this cellblock's bytes [BORROWS]. */
641 size_t (*buf_available)(void); /**< Bytes between the two tiers. */
642 void *(*buf_grow)(void *prisoner, size_t size,
643 const CellOwner *owner); /**< Grows the newest cell of its tier to size bytes in place, the added
644 bytes zeroed. */
645 embed_bool (*movable_init)(const CarcerMovableInitCfg *args); /**< Assigns the movable option, once. */
646 const CarcerMovableEntry *(*movable_buf_alloc)(
647 size_t size, const CellOwner *owner); /**< Takes a cell a move may move: its identity, NULL where refused. */
648 embed_bool (*movable_buf_release)(const CarcerMovableEntry *cell,
649 const CellOwner *owner); /**< Zeroes owner's movable cell, then releases it. */
650 embed_bool (*movable_buf_read)(const CarcerMovableCfg *args); /**< Copies out of a movable cell. */
651 embed_bool (*movable_buf_write)(const CarcerMovableCfg *args); /**< Copies into a movable cell. */
652 embed_iword (*movable_buf_compare)(const CarcerMovableCfg *args); /**< Orders a movable cell's bytes. */
653 size_t (*movable_buf_measure)(const CarcerMovableCfg *args); /**< Bytes before a movable cell's next zero. */
655EMBED_TABLE_LAYOUT(MaximumSecurityGuard, persistent_buf_alloc, persistent_buf_release, temporary_buf_alloc,
656 who_owns_buf, buf_available, buf_grow, movable_init, movable_buf_alloc, movable_buf_release,
657 movable_buf_read, movable_buf_write, movable_buf_compare, movable_buf_measure);
658
659/**
660 * @brief The movable entries of one cellblock, bound to it through series_cellularum_.
661 *
662 * @param[in] prisonsite_ Site whose name every entry symbol carries.
663 * @param[in] name_ Pool whose name the symbols carry alongside the site's.
664 * @param[in] wipe_to_zero_on_release_ Security flag, which the option keeps for its releases and moves.
665 * @param[in] series_cellularum_ The cellblock's record, as a pointer expression.
666 */
667#define MMGR_CARCER_MOVABLE_ENTRIES(prisonsite_, name_, wipe_to_zero_on_release_, series_cellularum_) \
668 static embed_bool prisonsite_##_##name_##_movable_init(const CarcerMovableInitCfg *args) \
669 { \
670 return mmgr_movable_init((series_cellularum_), args, (wipe_to_zero_on_release_)); \
671 } \
672 static const CarcerMovableEntry *prisonsite_##_##name_##_movable_buf_alloc(size_t size, const CellOwner *owner) \
673 { \
674 return mmgr_movable_buf_alloc((series_cellularum_), size, owner); \
675 } \
676 static embed_bool prisonsite_##_##name_##_movable_buf_release(const CarcerMovableEntry *cell, \
677 const CellOwner *owner) \
678 { \
679 return mmgr_movable_buf_release((series_cellularum_), cell, owner); \
680 } \
681 static embed_bool prisonsite_##_##name_##_movable_buf_read(const CarcerMovableCfg *args) \
682 { \
683 return mmgr_movable_buf_read((series_cellularum_), args); \
684 } \
685 static embed_bool prisonsite_##_##name_##_movable_buf_write(const CarcerMovableCfg *args) \
686 { \
687 return mmgr_movable_buf_write((series_cellularum_), args); \
688 } \
689 static embed_iword prisonsite_##_##name_##_movable_buf_compare(const CarcerMovableCfg *args) \
690 { \
691 return mmgr_movable_buf_compare((series_cellularum_), args); \
692 } \
693 static size_t prisonsite_##_##name_##_movable_buf_measure(const CarcerMovableCfg *args) \
694 { \
695 return mmgr_movable_buf_measure((series_cellularum_), args); \
696 }
697
698/**
699 * @brief Everything one cellblock is: its bytes, its state, and the entries bound to it.
700 *
701 * @param[in] prisonsite_ Site the cellblock is built at, which every emitted symbol carries.
702 * @param[in] type_ MinimumSecurityGuard or MaximumSecurityGuard, read by MMGR_CARCER_MEM.
703 * @param[in] name_ Pool the cellblock is built over, which is also the name it is reached by
704 * as a member of its site [BORROWS].
705 * @param[in] wipe_to_zero_on_release_ 1 where a release zeroes the cell first, 0 where it does not.
706 * @note The site name is part of every symbol. Two sites may each hold a cellblock of the same name,
707 * at different security levels, without colliding.
708 * @note wipe_to_zero_on_release_ is a literal: the branch it guards folds away and the security
709 * level costs nothing to read.
710 * @note The extent comes from the pool two ways. name_##_bytes is the count its declaration was
711 * handed and is what the record carries, and sizeof is what the compiler laid down. The
712 * asserts test the second: they say something the first cannot say about itself.
713 * @warning A pool too small to hold one cell fails the build. Only the alignment is a power of two,
714 * and nothing reads the size as a mask. A bad size is a mistake the language would
715 * otherwise accept. A wrong name or guard type fails on its own, and two pools are separate
716 * objects: they cannot share an address and there is no overlap to check.
717 * @note No upper bound. The pool states how much storage the cellblock gets, and nothing in the
718 * library sizes anything from a ceiling over it.
719 */
720#define MMGR_CARCER_BODY(prisonsite_, type_, name_, wipe_to_zero_on_release_) \
721 MMGR_PARS_CLAIMED_ONCE(name_); \
722 EMBED_STATIC_ASSERT(sizeof(mmgr_pars_storage_##name_) >= (2u * MMGR_CARCER_ALIGN), \
723 #prisonsite_ "." #name_ " is too small for one cell"); \
724 EMBED_STATIC_ASSERT(sizeof(mmgr_pars_storage_##name_) <= MMGR_CARCER_BYTES_MOST, \
725 #prisonsite_ "." #name_ " is larger than a tier boundary's half word can name"); \
726 static SeriesCellularum prisonsite_##_##name_##_ctx = MMGR_CARCER_STATE(mmgr_pars_storage_##name_, name_##_bytes); \
727 MMGR_ALLOC_SIZE(1) static void *prisonsite_##_##name_##_persistent_buf_alloc(size_t size, const CellOwner *owner) \
728 { \
729 if (wipe_to_zero_on_release_) \
730 { \
731 return mmgr_persistent_max_security_buf_alloc(&prisonsite_##_##name_##_ctx, size, owner); \
732 } \
733 return mmgr_persistent_buf_alloc(&prisonsite_##_##name_##_ctx, size, owner); \
734 } \
735 static void prisonsite_##_##name_##_persistent_buf_release(void *prisoner, const CellOwner *owner) \
736 { \
737 if (wipe_to_zero_on_release_) \
738 { \
739 mmgr_persistent_max_security_buf_release(&prisonsite_##_##name_##_ctx, prisoner, owner); \
740 } \
741 else \
742 { \
743 mmgr_persistent_buf_release(&prisonsite_##_##name_##_ctx, prisoner, owner); \
744 } \
745 } \
746 MMGR_ALLOC_SIZE(1) \
747 static void *prisonsite_##_##name_##_temporary_buf_alloc(size_t size, const CellOwner *owner) \
748 { \
749 if (wipe_to_zero_on_release_) \
750 { \
751 return mmgr_temporary_max_security_buf_alloc(&prisonsite_##_##name_##_ctx, size, owner); \
752 } \
753 return mmgr_temporary_buf_alloc(&prisonsite_##_##name_##_ctx, size, owner); \
754 } \
755 static embed_bool prisonsite_##_##name_##_who_owns_buf(const void *at) \
756 { \
757 return mmgr_who_owns_buf(&prisonsite_##_##name_##_ctx, at); \
758 } \
759 static size_t prisonsite_##_##name_##_buf_available(void) \
760 { \
761 const size_t available = mmgr_buf_available(&prisonsite_##_##name_##_ctx); \
762 return available; \
763 } \
764 MMGR_ALLOC_SIZE(2) \
765 static void *prisonsite_##_##name_##_buf_grow(void *prisoner, size_t size, const CellOwner *owner) \
766 { \
767 if (wipe_to_zero_on_release_) \
768 { \
769 return mmgr_max_security_buf_grow(&prisonsite_##_##name_##_ctx, prisoner, size, owner); \
770 } \
771 return mmgr_buf_grow(&prisonsite_##_##name_##_ctx, prisoner, size, owner); \
772 } \
773 MMGR_CARCER_MOVABLE_ENTRIES(prisonsite_, name_, wipe_to_zero_on_release_, &prisonsite_##_##name_##_ctx)
774
775/**
776 * @brief One cellblock as a member of its site's type.
777 *
778 * @param[in] prisonsite_ Site the cellblock is built at, unused here.
779 * @param[in] type_ MinimumSecurityGuard or MaximumSecurityGuard, the member's type.
780 * @param[in] name_ Pool the cellblock is built over, and the name the member is given.
781 * @param[in] wipe_to_zero_on_release_ Security flag, unused here.
782 * @note Takes all four because the three readers share one tuple shape, as MMGR_MINIMUM_SECURITY
783 * describes.
784 */
785#define MMGR_CARCER_MEM(prisonsite_, type_, name_, wipe_to_zero_on_release_) type_ name_;
786
787/**
788 * @brief One cellblock's entries, in the order its guard declares them.
789 *
790 * @param[in] prisonsite_ Site whose name every entry symbol carries.
791 * @param[in] type_ Guard type, unused here.
792 * @param[in] name_ Pool whose name the symbols carry alongside the site's.
793 * @param[in] wipe_to_zero_on_release_ Security flag, unused here.
794 * @note MinimumSecurityGuard and MaximumSecurityGuard declare the same thirteen members in the same
795 * order. One initializer serves either level.
796 */
797#define MMGR_CARCER_SEAT(prisonsite_, type_, name_, wipe_to_zero_on_release_) \
798 {prisonsite_##_##name_##_persistent_buf_alloc, \
799 prisonsite_##_##name_##_persistent_buf_release, \
800 prisonsite_##_##name_##_temporary_buf_alloc, \
801 prisonsite_##_##name_##_who_owns_buf, \
802 prisonsite_##_##name_##_buf_available, \
803 prisonsite_##_##name_##_buf_grow, \
804 prisonsite_##_##name_##_movable_init, \
805 prisonsite_##_##name_##_movable_buf_alloc, \
806 prisonsite_##_##name_##_movable_buf_release, \
807 prisonsite_##_##name_##_movable_buf_read, \
808 prisonsite_##_##name_##_movable_buf_write, \
809 prisonsite_##_##name_##_movable_buf_compare, \
810 prisonsite_##_##name_##_movable_buf_measure},
811
812/**
813 * @brief Declares a minimum security cellblock over a pool.
814 *
815 * @param[in] name_ Pool the cellblock is built over, which is also the name it is reached by as a
816 * member of its site [BORROWS].
817 * @note The pool carries its own extent: no size is written here. A cellblock is a dressing over
818 * bytes that already exist, and where those bytes live was settled by which of
819 * ParsMemoriaeInternae or ParsMemoriaeExternum declared them.
820 * @note Expands to a tuple in place of code. The site reads the same list three times, once for
821 * the cellblocks' bodies, once for its own members, once for their entries. An element has to
822 * stay data until the site says which of the three it is being read as.
823 */
824#define MMGR_MINIMUM_SECURITY(name_) (MinimumSecurityGuard, name_, 0)
825
826/**
827 * @brief Declares a maximum security cellblock over a pool.
828 *
829 * @param[in] name_ Pool the cellblock is built over, which is also the name it is reached by as a
830 * member of its site [BORROWS].
831 * @note The trailing 1 is the wipe flag MMGR_CARCER_BODY reads to pick the zeroing release on both
832 * tiers. Choosing this over MMGR_MINIMUM_SECURITY is where a cellblock's level is settled, and
833 * nothing after the declaration can change it.
834 * @note Carries no size, for the reason MMGR_MINIMUM_SECURITY gives.
835 */
836#define MMGR_MAXIMUM_SECURITY(name_) (MaximumSecurityGuard, name_, 1)
837
838/**
839 * @brief Strips a tuple's parentheses.
840 *
841 * @param[in] ... The tuple's elements, which its own parentheses deliver as this macro's arguments.
842 * @return Those elements, comma separated, with nothing around them.
843 * @note Written MMGR_UNTUPLE tuple_, with no parentheses of its own: the tuple supplies them.
844 */
845#define MMGR_UNTUPLE(...) __VA_ARGS__
846
847/**
848 * @brief Applies one reader to one cellblock tuple, with the site spliced in ahead of it.
849 *
850 * @param[in] what_ Reader to apply: MMGR_CARCER_BODY, MMGR_CARCER_MEM or MMGR_CARCER_SEAT.
851 * @param[in] prisonsite_ Site, which reaches the reader ahead of the tuple's own elements.
852 * @param[in] tuple_ One MMGR_MINIMUM_SECURITY or MMGR_MAXIMUM_SECURITY tuple.
853 * @return What the reader expands to.
854 * @note Two steps, because a macro's arguments are counted before they are expanded. The inner call
855 * lets the flattened tuple reach the reader as separate arguments.
856 */
857#define MMGR_CARCER_APPLY(what_, prisonsite_, tuple_) MMGR_CARCER_APPLY_(what_, prisonsite_, MMGR_UNTUPLE tuple_)
858
859/**
860 * @brief Expands to what_(prisonsite_, __VA_ARGS__).
861 *
862 * @param[in] what_ Reader to apply.
863 * @param[in] prisonsite_ Site, which reaches the reader ahead of the elements.
864 * @param[in] ... The tuple's elements, already flattened by MMGR_UNTUPLE.
865 * @return What the reader expands to.
866 * @note The inner half of MMGR_CARCER_APPLY's two steps. Its arguments are expanded before they are
867 * substituted: MMGR_UNTUPLE runs here and the tuple's elements reach the reader as separate
868 * arguments in place of one.
869 */
870#define MMGR_CARCER_APPLY_(what_, prisonsite_, ...) what_(prisonsite_, __VA_ARGS__)
871
872/**
873 * @brief Applies the reader to a site's one cellblock tuple.
874 *
875 * @param[in] what_ Reader to apply: MMGR_CARCER_BODY, MMGR_CARCER_MEM or MMGR_CARCER_SEAT.
876 * @param[in] prisonsite_ Site, which reaches the reader ahead of the tuple's own elements.
877 * @param[in] SeriesCellularum1_ The cellblock's tuple.
878 * @note The base of the expansion. Every longer line ends in this one, and it alone names no other.
879 */
880#define MMGR_CARCER_W1(what_, prisonsite_, SeriesCellularum1_) MMGR_CARCER_APPLY(what_, prisonsite_, SeriesCellularum1_)
881
882/**
883 * @brief Applies the reader to a site's two cellblock tuples.
884 *
885 * @param[in] what_ Reader to apply.
886 * @param[in] prisonsite_ Site, which reaches the reader with every tuple.
887 * @param[in] SeriesCellularum1_ First cellblock's tuple, which MMGR_CARCER_W1 takes.
888 * @param[in] SeriesCellularum2_ Second cellblock's tuple, applied after it.
889 * @note The step every longer line repeats: expand the line one shorter, then apply the reader once
890 * more. The preprocessor cannot step through a list: each cellblock count needs a line of its
891 * own.
892 */
893#define MMGR_CARCER_W2(what_, prisonsite_, SeriesCellularum1_, SeriesCellularum2_) \
894 MMGR_CARCER_W1(what_, prisonsite_, SeriesCellularum1_) \
895 MMGR_CARCER_APPLY(what_, prisonsite_, SeriesCellularum2_)
896
897/**
898 * @brief Applies the reader to a site's three cellblock tuples.
899 *
900 * @param[in] what_ Reader to apply.
901 * @param[in] prisonsite_ Site, which reaches the reader with every tuple.
902 * @param[in] SeriesCellularum1_ First cellblock's tuple.
903 * @param[in] SeriesCellularum2_ Second cellblock's tuple.
904 * @param[in] SeriesCellularum3_ Third cellblock's tuple, applied after MMGR_CARCER_W2 expands the first two.
905 * @note MMGR_CARCER_WALK selects this line for a site declaring three cellblocks.
906 */
907#define MMGR_CARCER_W3(what_, prisonsite_, SeriesCellularum1_, SeriesCellularum2_, SeriesCellularum3_) \
908 MMGR_CARCER_W2(what_, prisonsite_, SeriesCellularum1_, SeriesCellularum2_) \
909 MMGR_CARCER_APPLY(what_, prisonsite_, SeriesCellularum3_)
910
911/**
912 * @brief Applies the reader to a site's four cellblock tuples.
913 *
914 * @param[in] what_ Reader to apply.
915 * @param[in] prisonsite_ Site, which reaches the reader with every tuple.
916 * @param[in] SeriesCellularum1_ First cellblock's tuple.
917 * @param[in] SeriesCellularum2_ Second cellblock's tuple.
918 * @param[in] SeriesCellularum3_ Third cellblock's tuple.
919 * @param[in] SeriesCellularum4_ Fourth cellblock's tuple, applied after MMGR_CARCER_W3 expands the first three.
920 * @note MMGR_CARCER_WALK selects this line for a site declaring four cellblocks.
921 */
922#define MMGR_CARCER_W4(what_, prisonsite_, SeriesCellularum1_, SeriesCellularum2_, SeriesCellularum3_, \
923 SeriesCellularum4_) \
924 MMGR_CARCER_W3(what_, prisonsite_, SeriesCellularum1_, SeriesCellularum2_, SeriesCellularum3_) \
925 MMGR_CARCER_APPLY(what_, prisonsite_, SeriesCellularum4_)
926
927/**
928 * @brief Applies the reader to a site's five cellblock tuples.
929 *
930 * @param[in] what_ Reader to apply.
931 * @param[in] prisonsite_ Site, which reaches the reader with every tuple.
932 * @param[in] SeriesCellularum1_ First cellblock's tuple.
933 * @param[in] SeriesCellularum2_ Second cellblock's tuple.
934 * @param[in] SeriesCellularum3_ Third cellblock's tuple.
935 * @param[in] SeriesCellularum4_ Fourth cellblock's tuple.
936 * @param[in] SeriesCellularum5_ Fifth cellblock's tuple, applied after MMGR_CARCER_W4 expands the first four.
937 * @note MMGR_CARCER_WALK selects this line for a site declaring five cellblocks.
938 */
939#define MMGR_CARCER_W5(what_, prisonsite_, SeriesCellularum1_, SeriesCellularum2_, SeriesCellularum3_, \
940 SeriesCellularum4_, SeriesCellularum5_) \
941 MMGR_CARCER_W4(what_, prisonsite_, SeriesCellularum1_, SeriesCellularum2_, SeriesCellularum3_, SeriesCellularum4_) \
942 MMGR_CARCER_APPLY(what_, prisonsite_, SeriesCellularum5_)
943
944/**
945 * @brief Applies the reader to a site's six cellblock tuples.
946 *
947 * @param[in] what_ Reader to apply.
948 * @param[in] prisonsite_ Site, which reaches the reader with every tuple.
949 * @param[in] SeriesCellularum1_ First cellblock's tuple.
950 * @param[in] SeriesCellularum2_ Second cellblock's tuple.
951 * @param[in] SeriesCellularum3_ Third cellblock's tuple.
952 * @param[in] SeriesCellularum4_ Fourth cellblock's tuple.
953 * @param[in] SeriesCellularum5_ Fifth cellblock's tuple.
954 * @param[in] SeriesCellularum6_ Sixth cellblock's tuple, applied after MMGR_CARCER_W5 expands the first five.
955 * @note MMGR_CARCER_WALK selects this line for a site declaring six cellblocks.
956 */
957#define MMGR_CARCER_W6(what_, prisonsite_, SeriesCellularum1_, SeriesCellularum2_, SeriesCellularum3_, \
958 SeriesCellularum4_, SeriesCellularum5_, SeriesCellularum6_) \
959 MMGR_CARCER_W5(what_, prisonsite_, SeriesCellularum1_, SeriesCellularum2_, SeriesCellularum3_, SeriesCellularum4_, \
960 SeriesCellularum5_) \
961 MMGR_CARCER_APPLY(what_, prisonsite_, SeriesCellularum6_)
962
963/**
964 * @brief Applies the reader to a site's seven cellblock tuples.
965 *
966 * @param[in] what_ Reader to apply.
967 * @param[in] prisonsite_ Site, which reaches the reader with every tuple.
968 * @param[in] SeriesCellularum1_ First cellblock's tuple.
969 * @param[in] SeriesCellularum2_ Second cellblock's tuple.
970 * @param[in] SeriesCellularum3_ Third cellblock's tuple.
971 * @param[in] SeriesCellularum4_ Fourth cellblock's tuple.
972 * @param[in] SeriesCellularum5_ Fifth cellblock's tuple.
973 * @param[in] SeriesCellularum6_ Sixth cellblock's tuple.
974 * @param[in] SeriesCellularum7_ Seventh cellblock's tuple, applied after MMGR_CARCER_W6 expands the first six.
975 * @note MMGR_CARCER_WALK selects this line for a site declaring seven cellblocks.
976 */
977#define MMGR_CARCER_W7(what_, prisonsite_, SeriesCellularum1_, SeriesCellularum2_, SeriesCellularum3_, \
978 SeriesCellularum4_, SeriesCellularum5_, SeriesCellularum6_, SeriesCellularum7_) \
979 MMGR_CARCER_W6(what_, prisonsite_, SeriesCellularum1_, SeriesCellularum2_, SeriesCellularum3_, SeriesCellularum4_, \
980 SeriesCellularum5_, SeriesCellularum6_) \
981 MMGR_CARCER_APPLY(what_, prisonsite_, SeriesCellularum7_)
982
983/**
984 * @brief Applies the reader to a site's eight cellblock tuples.
985 *
986 * @param[in] what_ Reader to apply.
987 * @param[in] prisonsite_ Site, which reaches the reader with every tuple.
988 * @param[in] SeriesCellularum1_ First cellblock's tuple.
989 * @param[in] SeriesCellularum2_ Second cellblock's tuple.
990 * @param[in] SeriesCellularum3_ Third cellblock's tuple.
991 * @param[in] SeriesCellularum4_ Fourth cellblock's tuple.
992 * @param[in] SeriesCellularum5_ Fifth cellblock's tuple.
993 * @param[in] SeriesCellularum6_ Sixth cellblock's tuple.
994 * @param[in] SeriesCellularum7_ Seventh cellblock's tuple.
995 * @param[in] SeriesCellularum8_ Eighth cellblock's tuple, applied after MMGR_CARCER_W7 expands the first seven.
996 * @note MMGR_CARCER_WALK selects this line for a site declaring eight cellblocks.
997 * @warning The last line written. A site declaring nine cellblocks pastes MMGR_CARCER_W9, which does
998 * not exist. Writing that line is the whole fix. Nothing else changes and no configured
999 * ceiling is involved.
1000 */
1001#define MMGR_CARCER_W8(what_, prisonsite_, SeriesCellularum1_, SeriesCellularum2_, SeriesCellularum3_, \
1002 SeriesCellularum4_, SeriesCellularum5_, SeriesCellularum6_, SeriesCellularum7_, \
1003 SeriesCellularum8_) \
1004 MMGR_CARCER_W7(what_, prisonsite_, SeriesCellularum1_, SeriesCellularum2_, SeriesCellularum3_, SeriesCellularum4_, \
1005 SeriesCellularum5_, SeriesCellularum6_, SeriesCellularum7_) \
1006 MMGR_CARCER_APPLY(what_, prisonsite_, SeriesCellularum8_)
1007
1008/**
1009 * @brief Expands the walk matching the cellblock count.
1010 *
1011 * @param[in] what_ Reader to apply: MMGR_CARCER_BODY, MMGR_CARCER_MEM or MMGR_CARCER_SEAT.
1012 * @param[in] prisonsite_ Site, forwarded to the walk ahead of the tuples.
1013 * @param[in] ... MMGR_MINIMUM_SECURITY and MMGR_MAXIMUM_SECURITY tuples, one per cellblock.
1014 * @note EMBED_CAT builds the line's name from EMBED_NARG's count of the tuples.
1015 * @warning EMBED_NARG gives 1 for an empty list. A site declaring no cellblocks reaches
1016 * MMGR_CARCER_W1 and fails there with too few arguments for the reader.
1017 */
1018#define MMGR_CARCER_WALK(what_, prisonsite_, ...) \
1019 EMBED_CAT(MMGR_CARCER_W, EMBED_NARG(__VA_ARGS__))(what_, prisonsite_, __VA_ARGS__)
1020
1021/**
1022 * @brief Declares a prison site and the cellblocks built at it.
1023 *
1024 * @param[in] prisonsite_ Name of the site. Its cellblocks are reached as members of it.
1025 * @param[in] ... MMGR_MINIMUM_SECURITY and MMGR_MAXIMUM_SECURITY declarations, one each.
1026 * @note Everything it emits is initialized data. Nothing runs at startup, and a cellblock's first
1027 * byte is the address of its own storage, which the linker resolves.
1028 * @note The warden is the const struct this emits under the site's name. Every call reaches a
1029 * cellblock through it.
1030 * @note Every emitted symbol carries the site's name. A program may declare as many sites as it
1031 * likes and two of them may each hold a cellblock called the same thing.
1032 * @note A cellblock's entries are bound to that cellblock. No call names one and none can reach
1033 * another's bytes. A minimum security cellblock has no zeroing release and a maximum security
1034 * one has no plain release.
1035 * @warning The bytes, the state and the warden are all static. A declaration in a header gives
1036 * every translation unit that includes it a site of its own in place of one they share.
1037 */
1038#define LocusCarcerum(prisonsite_, ...) \
1039 MMGR_CARCER_WALK(MMGR_CARCER_BODY, prisonsite_, __VA_ARGS__) \
1040 EMBED_TABLE_STORAGE struct \
1041 { \
1042 MMGR_CARCER_WALK(MMGR_CARCER_MEM, prisonsite_, __VA_ARGS__) \
1043 } prisonsite_ EMBED_UNUSED = {MMGR_CARCER_WALK(MMGR_CARCER_SEAT, prisonsite_, __VA_ARGS__)}
1044
1045/**
1046 * @brief Takes size bytes from a cellblock's persistent tier.
1047 *
1048 * @param[in,out] series Cellblock to take from [BORROWS].
1049 * @param[in] size Bytes wanted.
1050 * @param[in] owner The registered owner the cell is counted to [BORROWS].
1051 * @return Start of the cell, or NULL when owner is NULL or not registered or the
1052 * cellblock cannot meet the take [RETURNS OWNERSHIP].
1053 * @note Reached through the cellblock's own guard, never called by name.
1054 * @note A persistent cell stands until the same cellblock's persistent_buf_release takes it.
1055 * @note A cell handed out adds its payload bytes, the request rounded to a word, to owner's in_use
1056 * and raises owner's hw to it. A refused take counts nothing.
1057 * @note The take is one pass through the cellblock's turnstiles and one fetch-add on the persistent end: the
1058 * cell is the bytes the bump covered, and its header and owner word are laid once the room is read off the
1059 * top. A bump the gap cannot meet adds its bytes to the tier's overrun and the take closes the cellblock, drains
1060 * the turnstiles and walks the tier alone: every run of free cells merged into one, the first run that holds the take
1061 * taken and split where its rest holds a cell, the free run reaching the end returned to the gap, and the gap bumped
1062 * where no run fits. Only then is the take refused. No path branches on what another context did.
1063 * @warning The bytes are not zeroed, and a cell laid over bytes a prisoner occupied before still
1064 * carries what that prisoner left. A cellblock declared MMGR_MAXIMUM_SECURITY zeroes, and it
1065 * zeroes on release.
1066 */
1067MMGR_ALLOC_SIZE(2) void *mmgr_persistent_buf_alloc(SeriesCellularum *series, size_t size, const CellOwner *owner);
1068
1069/**
1070 * @brief Takes size bytes from a maximum security cellblock's persistent tier.
1071 *
1072 * @param[in,out] series Cellblock to take from [BORROWS].
1073 * @param[in] size Bytes wanted.
1074 * @param[in] owner The registered owner the cell is counted to [BORROWS].
1075 * @return As mmgr_persistent_buf_alloc answers [RETURNS OWNERSHIP].
1076 * @note The take mmgr_persistent_buf_alloc makes. A maximum security cellblock zeroes on release.
1077 */
1080
1081/**
1082 * @brief Releases a prisoner, leaving the cell's bytes as they are.
1083 *
1084 * @param[in,out] series Cellblock the prisoner came from [BORROWS].
1085 * @param[in] prisoner First byte of the cell [TAKES OWNERSHIP].
1086 * @param[in] owner The owner the cell counts to, as its owner word names it [BORROWS].
1087 * @note Which tier the cell lies in is read from its offset against the persistent end. A NULL prisoner or a
1088 * NULL owner changes nothing.
1089 * @note The give is one fetch-or on the cell's owner word, which sets its completion bit, and one store on the
1090 * cell's header, which goes free: the cell's payload bytes, read off its header, come off owner's in_use
1091 * in the context whose fetch-or found the bit clear. A release naming an owner other than the cell's
1092 * changes nothing and is told through mmgr_carcer_hw_misnamed; one of a cell given back already changes
1093 * nothing either. No pass and no wait: the cell's words are its holder's alone.
1094 * @note A free cell stays in its tier for the compaction a take the gap cannot meet runs, which merges it with
1095 * its free neighbors, takes it where it fits, or returns it to the gap where it reaches the end.
1096 * @warning A prisoner outside this cellblock's own bytes stops the program with MMGR_FATAL: there is no state
1097 * to carry on from.
1098 * @warning prisoner is dead once this returns and its bytes are not zeroed. The cellblock may hand
1099 * them out again.
1100 * @warning The bound is the cellblock's storage and never a cell boundary. An address inside these
1101 * bytes that is not the first byte of a cell is still read as a cell's word.
1102 */
1103void mmgr_persistent_buf_release(SeriesCellularum *series, void *prisoner, const CellOwner *owner);
1104
1105/**
1106 * @brief Zeroes a cell, then releases the prisoner.
1107 *
1108 * @param[in,out] series Cellblock the prisoner came from [BORROWS].
1109 * @param[in,out] prisoner First byte of the cell, zeroed before release [TAKES OWNERSHIP].
1110 * @param[in] owner The owner the cell counts to, as its owner word names it [BORROWS].
1111 * @note The extent comes from the cell's own header. A caller cannot under-zero a cell by naming
1112 * fewer bytes than it carries. A NULL prisoner or a NULL owner changes nothing.
1113 * @note The zeroing is the only step that separates this from mmgr_persistent_buf_release, and it runs
1114 * once the owner named is found to be the cell's and the cell still counted: a release naming another
1115 * owner, or of a cell given back already, zeroes nothing.
1116 * @warning A prisoner outside this cellblock's own bytes stops the program with MMGR_FATAL.
1117 * @warning prisoner is dead once this returns. The cellblock may hand those bytes out again.
1118 * @warning The bound is the cellblock's storage and never a cell boundary. An address inside these
1119 * bytes that is not the first byte of a cell still has its extent read from the bytes ahead
1120 * of it, and is zeroed for whatever length those hold.
1121 */
1122void mmgr_persistent_max_security_buf_release(SeriesCellularum *series, void *prisoner, const CellOwner *owner);
1123
1124/**
1125 * @brief Grows a cell to size payload bytes in place, where it is the newest cell of its tier.
1126 *
1127 * @param[in,out] series Cellblock the prisoner came from [BORROWS].
1128 * @param[in,out] prisoner First byte of the cell [BORROWS].
1129 * @param[in] size Payload bytes the cell is to carry, rounded as a take rounds them.
1130 * @param[in] owner The owner the cell counts to, as its owner word names it, which the growth
1131 * is counted to [BORROWS].
1132 * @return The cell's first byte after the growth: the prisoner itself for a persistent
1133 * cell, or for one that carries size already; a lower address for a temporary
1134 * cell, whose bytes moved down. NULL with nothing changed where the cell is not
1135 * the newest of its tier, a cell having been taken above it since, or where
1136 * the gap cannot meet the growth. A NULL prisoner, a NULL owner, or an owner
1137 * other than the cell's, which is told through mmgr_carcer_hw_misnamed, returns
1138 * NULL [RETURNS OWNERSHIP].
1139 * @note The growth closes the cellblock, drains the turnstiles and works alone. The newest cell of a tier is
1140 * the cell its boundary stands against: the persistent end at the cell's end, the temporary top at the
1141 * cell's header. The gap is read exactly, the boundary stored past the growth, and no other cell moves. A
1142 * caller whose growth is refused takes a larger cell, copies into it and releases the first, as it would
1143 * without this entry.
1144 * @note The added bytes are as they lie: a persistent growth's hold the word that stood behind the
1145 * payload, a temporary growth's the cell's own old header and payload. mmgr_max_security_buf_grow
1146 * zeroes them.
1147 * @warning The prisoner's address is dead after a temporary growth that answers a lower one: the cell
1148 * is reached at the address answered from then on. A prisoner outside this cellblock's own
1149 * bytes stops with MMGR_FATAL, as a release does.
1150 */
1151MMGR_ALLOC_SIZE(3) void *mmgr_buf_grow(SeriesCellularum *series, void *prisoner, size_t size, const CellOwner *owner);
1152
1153/**
1154 * @brief Grows a cell in place as mmgr_buf_grow does, and zeroes the bytes the growth adds.
1155 *
1156 * @param[in,out] series Cellblock the prisoner came from [BORROWS].
1157 * @param[in,out] prisoner First byte of the cell [BORROWS].
1158 * @param[in] size Payload bytes the cell is to carry.
1159 * @param[in] owner The owner a persistent cell's growth is counted to [BORROWS].
1160 * @return As mmgr_buf_grow answers [RETURNS OWNERSHIP].
1161 * @note The zeroing covers the growth's bytes alone: the payload the cell carried keeps its bytes, moved
1162 * for a temporary cell.
1163 */
1165void *mmgr_max_security_buf_grow(SeriesCellularum *series, void *prisoner, size_t size, const CellOwner *owner);
1166
1167/**
1168 * @brief Takes size bytes from a cellblock's temporary tier.
1169 *
1170 * @param[in,out] series Cellblock to take from [BORROWS].
1171 * @param[in] size Bytes wanted.
1172 * @param[in] owner The registered owner the cell is counted to [BORROWS].
1173 * @return Start of the cell, or NULL when owner is NULL or not registered or the
1174 * cellblock cannot meet the take [RETURNS OWNERSHIP].
1175 * @note The caller gives the cell back through mmgr_persistent_buf_release, as a persistent cell is given back,
1176 * which takes the bytes off owner. The cell carries owner in the word behind its payload.
1177 * @note The take is mmgr_persistent_buf_alloc's with the arithmetic mirrored: one pass through the cellblock's
1178 * turnstiles and one fetch-add on the temporary top, which moves it down by the cell; the room is read off the
1179 * persistent end, and a bump that crosses it adds its bytes to the tier's overrun. The take then closes the
1180 * cellblock, drains the turnstiles and walks the tier alone: every run of free cells merged into one, the
1181 * fitting run nearest the cellblock's size taken and split where its rest holds a cell, the free run at the top
1182 * returned to the gap, and the top bumped where no run fits. Only then is the take refused.
1183 * @warning The bytes are not zeroed, and an allocation returns whatever the last prisoner left. A
1184 * cellblock declared MMGR_MAXIMUM_SECURITY zeroes, and it zeroes on release.
1185 */
1186void *mmgr_temporary_buf_alloc(SeriesCellularum *series, size_t size, const CellOwner *owner);
1187
1188/**
1189 * @brief Takes size bytes from a maximum security cellblock's temporary tier.
1190 *
1191 * @param[in,out] series Cellblock to take from [BORROWS].
1192 * @param[in] size Bytes wanted.
1193 * @param[in] owner The registered owner the cell is counted to [BORROWS].
1194 * @return As mmgr_temporary_buf_alloc answers [RETURNS OWNERSHIP].
1195 * @note The take mmgr_temporary_buf_alloc makes. A maximum security cellblock zeroes on release.
1196 */
1198void *mmgr_temporary_max_security_buf_alloc(SeriesCellularum *series, size_t size, const CellOwner *owner);
1199
1200/**
1201 * @brief Whether at lies inside the cellblock's bytes.
1202 *
1203 * @param[in] series Cellblock to test against [BORROWS].
1204 * @param[in] at Address to test [BORROWS].
1205 * @return EMBED_TRUE when at lies in the cellblock's storage, which is [base, base + size).
1206 * @note One unsigned compare covers both ends, since an address below base wraps to a difference
1207 * larger than any size.
1208 * @warning Any address in the cellblock answers true, the first byte of a cell or any other. This says
1209 * where an address is and nothing of what is there. A true answer is not a warrant that at
1210 * may be released. The releases read a cell's word from whatever address they are handed.
1211 */
1212embed_bool mmgr_who_owns_buf(const SeriesCellularum *series, const void *at);
1213
1214/**
1215 * @brief The owner a cell counts to, read off the word behind its payload.
1216 *
1217 * @param[in] series Cellblock the prisoner came from [BORROWS].
1218 * @param[in] prisoner First byte of the cell [BORROWS].
1219 * @return The registered owner the cell's owner word names, or NULL where prisoner is NULL, lies
1220 * outside the cellblock, is a cell no context holds, or its word names no registered owner
1221 * [BORROWS].
1222 * @note A holder that grows or gives back a cell another part of the program took, a line that grows in the
1223 * hands of whoever is writing it, names the owner this answers.
1224 * @warning The bound is the cellblock's storage and never a cell boundary, as for a release: an address
1225 * inside these bytes that is not the first byte of a cell is read as a cell's word.
1226 */
1227const CellOwner *mmgr_owner_of_buf(const SeriesCellularum *series, const void *prisoner);
1228
1229/**
1230 * @brief The bytes lying between the two tiers.
1231 *
1232 * @param[in,out] series Cellblock to read [BORROWS].
1233 * @return The temporary top less the persistent end, read off one word.
1234 * @note An allocation out of that gap needs a cell header from the same bytes, and the request is
1235 * rounded up to a whole word first. A request of exactly this many bytes cannot be met.
1236 * @note The trims a give left owed on the open word run first, and the gap read has every owed trim run.
1237 * @warning The answer is a snapshot. Any allocation at all leaves it stale before the caller can act
1238 * on it.
1239 */
1241
1242/**
1243 * @brief Zeroes size bytes at prisoner, releasing nothing.
1244 *
1245 * @param[in,out] prisoner First byte to clear [BORROWS].
1246 * @param[in] size Bytes to clear.
1247 * @note The stores are volatile: clearing bytes nothing reads afterwards is not dropped as dead
1248 * work. The middle is stored a word at a time, the two edges a byte at a time.
1249 * @note The extent is the caller's to state. A zeroing release takes it from the cell's own word.
1250 * @warning prisoner must be writable for size bytes.
1251 */
1252void mmgr_zero_buf(void *prisoner, size_t size);
1253
1254/**
1255 * @brief Rounds size up to a whole machine word.
1256 *
1257 * @param[in] size Count to round.
1258 * @return size rounded up to a multiple of MMGR_CARCER_ALIGN, and size itself when it already
1259 * is one.
1260 * @note A size of 0 rounds to 0. The allocators do not round a request this way. They carry a
1261 * request of 0 up to one word first: no cell is handed out empty.
1262 * @warning MMGR_CARCER_ALIGN - 1 is added before the mask. A size within a word of SIZE_MAX wraps
1263 * to 0.
1264 */
1265size_t mmgr_align_up_buf(size_t size);
1266
1267/**
1268 * @brief Puts a cellblock back to the state it was declared in: both tiers empty, the closed word and the
1269 * turnstiles at rest, the option unassigned, and no byte of its storage touched.
1270 *
1271 * @param[in,out] series Cellblock to empty [BORROWS].
1272 * @note Every cell the cellblock has handed out is dead once this returns. A suite calls it between
1273 * cases over one declared cellblock; a program with a cellblock to empty while another context
1274 * reaches it has a cellblock whose cells that context still holds.
1275 * @note Nothing outside the tiers is read as a cell, and so no cell's words are touched.
1276 */
1278
1279/**
1280 * @brief Counts bytes that lie in no cellblock to an owner, as a ring's or a page's are.
1281 *
1282 * @param[in] owner The registered owner [BORROWS].
1283 * @param[in] bytes The bytes.
1284 * @note An owner's counts are the program's running total, and memory a program takes outside a
1285 * cellblock belongs in it. No cellblock's own running total moves.
1286 */
1287void mmgr_owner_bytes_taken(const CellOwner *owner, size_t bytes);
1288
1289/**
1290 * @brief Takes bytes mmgr_owner_bytes_taken counted off their owner.
1291 *
1292 * @param[in] owner The owner they were counted to [BORROWS].
1293 * @param[in] bytes The bytes.
1294 */
1295void mmgr_owner_bytes_given(const CellOwner *owner, size_t bytes);
1296
1297/**
1298 * @brief A cellblock's fragmentation: the bytes its persistent tier occupies and no cell's payload holds, as
1299 * a percentage of the bytes it occupies.
1300 *
1301 * @param[in] series Cellblock to read [BORROWS].
1302 * @return The percentage, 0 to 100, read against the hundredth of the bytes occupied rounded up, and 0
1303 * below a hundred bytes occupied.
1304 * @note Read off the persistent end and the tier's running total, with no walk. Each cell's words count among
1305 * the bytes no payload holds, and a tier of small cells reads some fragmentation with no free cell.
1306 * @note The temporary tier reads nothing: its cells last one call, and its top comes back as they end. A
1307 * free cell no movable cell stands above is no work for a mover either: a sort that finds nothing to
1308 * move is remembered, and not run again until the reading rises.
1309 */
1311
1312/**
1313 * @brief Assigns a cellblock's movable option: the translation table, its claim bits, the rows of touches and
1314 * the mover's thread. A cellblock's guard calls this, as movable_init.
1315 *
1316 * @param[in,out] series Cellblock the option serves [BORROWS].
1317 * @param[in] args The storage, the sizes and the thread's calls [BORROWS].
1318 * @param[in] wipes Whether a cell a move leaves or a release gives back is zeroed first.
1319 * @return EMBED_TRUE where the option is assigned and, where a thread was given, the
1320 * thread started. EMBED_FALSE where it is assigned already, the storage is short
1321 * or a size is zero, nothing changed, or where the thread did not start, which
1322 * leaves the option assigned and nothing moving a cell.
1323 * @note Assigned once, at a time no context reaches a movable cell. The cellblock's turnstiles are what the
1324 * mover reads as occupancy: the lanes open against the option's width.
1325 */
1326embed_bool mmgr_movable_init(SeriesCellularum *series, const CarcerMovableInitCfg *args, embed_bool wipes);
1327
1328/**
1329 * @brief Takes a persistent cell a move may move, and names it in the table.
1330 *
1331 * @param[in,out] series Cellblock to take from [BORROWS].
1332 * @param[in] size Bytes wanted.
1333 * @param[in] owner The registered owner the cell is counted to [BORROWS].
1334 * @return The cell's identity, or NULL where the option is not assigned, the cellblock
1335 * cannot meet the take or the table has no free entry, nothing counted.
1336 * @note The identity is a const pointer to the cell's entry. It stands for the cell's life whatever moves
1337 * the cell, and the movable_buf_ calls are the only way from it to the bytes.
1338 * @warning A pointer kept past mmgr_movable_buf_release names an entry a later take may reuse.
1339 */
1340const CarcerMovableEntry *mmgr_movable_buf_alloc(SeriesCellularum *series, size_t size, const CellOwner *owner);
1341
1342/**
1343 * @brief Gives a movable cell back and frees its entry.
1344 *
1345 * @param[in,out] series Cellblock the cell is in [BORROWS].
1346 * @param[in] cell The cell [BORROWS].
1347 * @param[in] owner The owner the cell is counted to [BORROWS].
1348 * @return EMBED_TRUE where it went back. EMBED_FALSE where cell is NULL, the entry is free or
1349 * owner is not the cell's, which mmgr_carcer_hw_misnamed is told of, nothing changed.
1350 * @note One pass through the cellblock's turnstiles: the entry read, the cell given back as a release gives it,
1351 * the entry's word and its claim bit cleared. A move is a writer, which drains the passes before it and
1352 * closes the cellblock to those after: the two never reach the entry at once.
1353 */
1354embed_bool mmgr_movable_buf_release(SeriesCellularum *series, const CarcerMovableEntry *cell, const CellOwner *owner);
1355
1356/**
1357 * @brief Copies args->bytes from args->offset of a movable cell to args->dst.
1358 *
1359 * @param[in,out] series Cellblock the cell is in [BORROWS].
1360 * @param[in] args The cell, offset, dst and bytes [BORROWS].
1361 * @return EMBED_TRUE where copied, EMBED_FALSE where the span passes the cell's end or
1362 * the entry is free, nothing copied.
1363 * @note Thin: one pass through the cellblock's turnstiles, badged with the calling thread's id, the entry's
1364 * word read, the bytes copied, and the lane let go. A pass that finds the cellblock closed to a move
1365 * backs off one step and tries once more.
1366 */
1367embed_bool mmgr_movable_buf_read(const SeriesCellularum *series, const CarcerMovableCfg *args);
1368
1369/**
1370 * @brief Copies args->bytes from args->src to args->offset of a movable cell.
1371 *
1372 * @param[in,out] series Cellblock the cell is in [BORROWS].
1373 * @param[in] args The cell, offset, src and bytes [BORROWS].
1374 * @return As mmgr_movable_buf_read answers.
1375 * @note Atomic against a move and not against another writer: two contexts writing the same bytes order
1376 * themselves.
1377 */
1379
1380/**
1381 * @brief Orders args->bytes of a movable cell against args->src, or against another cell where
1382 * args->other_cell is set.
1383 *
1384 * @param[in,out] series Cellblock the cell is in [BORROWS].
1385 * @param[in] args The cell, offset, and src or other_cell and other_offset, and bytes [BORROWS].
1386 * @return As memor.cmp orders them, and 1 where a span passes its cell's end or an entry is
1387 * free.
1388 * @note Two cells are the most one call reaches, both read under the one pass.
1389 */
1390embed_iword mmgr_movable_buf_compare(const SeriesCellularum *series, const CarcerMovableCfg *args);
1391
1392/**
1393 * @brief The bytes from args->offset of a movable cell to the next zero byte.
1394 *
1395 * @param[in,out] series Cellblock the cell is in [BORROWS].
1396 * @param[in] args The cell and offset [BORROWS].
1397 * @return The bytes, or the bytes to the cell's end where no zero stands before it.
1398 */
1400
1401/**
1402 * @brief Moves one movable cell into the first free cell below it, highest cell first.
1403 *
1404 * @param[in,out] series Cellblock whose cells to move [BORROWS].
1405 * @return EMBED_TRUE where a cell moved. EMBED_FALSE where none could at this reading: no
1406 * free run fits below any of them.
1407 * @note The mover's step, a writer's: the cellblock closed and the turnstiles drained, the highest named cell
1408 * read, the persistent tier walked below it for the first free run that holds it, the cell copied, its
1409 * owner word carried over, the entry flipped to the copy, the cell left behind set free, and the cellblock
1410 * opened. The same state moves the same cell to the same place.
1411 * @note Called until it answers EMBED_FALSE, the cells stand as low as the free runs let them, and the free run
1412 * reaching the tier's end returns to the gap with each walk.
1413 * @note The copy carries the cell's owner word, and so the owner's count stands as it was through the move.
1414 * @note A cell no free run below takes rules out every cell at least its size lying under it, since
1415 * those look through the same free runs for as much room.
1416 * @warning Each cell moved, and each smaller size refused, costs a read of the named part of the table
1417 * and a walk of the tier below the cell.
1418 */
1420
1421/**
1422 * @brief The mover: what the thread the program gave runs, until its thread_wait answers EMBED_FALSE.
1423 *
1424 * @param[in,out] series Cellblock with the option assigned and thread_wait given [BORROWS].
1425 * @note Each MMGR_MOVABLE_READ_MS it reads the cellblock. Fragmentation under MMGR_MOVABLE_SETTLE_PERCENT
1426 * ends the reading. Over it, MMGR_MOVABLE_QUIET_READINGS in a row must find the cellblock quiet: twice
1427 * the lanes its turnstiles have open at or under the option's width plus one, and no pass through them
1428 * since the last reading. It then moves cells one at a time, the cellblock open and the processor yielded
1429 * between moves, and reads the same after each: a pass, or lanes open past quiet, says the moves meet
1430 * pressure, and it backs off.
1431 * @note Backing off doubles the wait, up to MMGR_MOVABLE_BACKOFF_MOST_MS. A sort that ends with nothing left
1432 * to move resets the wait, and the fragmentation it left is remembered: nothing is run again until the
1433 * reading rises past that or falls under the threshold first.
1434 */
1436
1437/**
1438 * @brief The contexts queued at the cellblock's turnstiles now: those that found every open lane busy and wait for
1439 * a lane.
1440 *
1441 * @param[in] series Cellblock to read [BORROWS].
1442 * @return The queued contexts, 0 without the option.
1443 */
1445
1446/**
1447 * @brief One row of touches a lane, counted as passes go through it.
1448 *
1449 * @param[in,out] series Cellblock to read [BORROWS].
1450 * @param[in] at The row, from 0: the lane of the same place.
1451 * @param[out] id The id of the last badge through the lane: an owner's place, or a thread's id plus one
1452 * for a pass naming no owner [BORROWS].
1453 * @param[out] touches The passes through the lane so far [BORROWS].
1454 * @return EMBED_TRUE where the row holds a lane's passes, EMBED_FALSE past the rows in use or
1455 * for a lane no pass has gone through.
1456 * @note A telemetry row, counted by one add as each pass badges in, and nothing decides on it. The option's last
1457 * row counts every lane past the rest, and answers an id of SIZE_MAX.
1458 */
1459embed_bool mmgr_movable_touches(const SeriesCellularum *series, size_t at, size_t *id, size_t *touches);
1460
1461EMBED_END_DECLS
1462
1463#endif
void * mmgr_persistent_buf_alloc(SeriesCellularum *series, size_t size, const CellOwner *owner)
Takes size bytes from a cellblock's persistent tier.
static size_t mmgr_owner_hw(const CellOwner *owner)
The most payload bytes an owner has had at once.
static size_t mmgr_series_cellularum_temporary_hw(const SeriesCellularum *series)
The most bytes a cellblock's temporary tier has taken from the top.
size_t mmgr_movable_queue_depth(const SeriesCellularum *series)
The contexts queued at the cellblock's turnstiles now: those that found every open lane busy and wait...
const CarcerMovableEntry * mmgr_movable_buf_alloc(SeriesCellularum *series, size_t size, const CellOwner *owner)
Takes a persistent cell a move may move, and names it in the table.
size_t mmgr_buf_available(SeriesCellularum *series)
The bytes lying between the two tiers.
embed_bool mmgr_movable_buf_release(SeriesCellularum *series, const CarcerMovableEntry *cell, const CellOwner *owner)
Gives a movable cell back and frees its entry.
static embed_bool mmgr_series_cellularum_idle(const SeriesCellularum *series)
Whether no cell is taken from a cellblock.
void mmgr_series_cellularum_emptied(SeriesCellularum *series)
Puts a cellblock back to the state it was declared in: both tiers empty, the closed word and the turn...
void mmgr_persistent_buf_release(SeriesCellularum *series, void *prisoner, const CellOwner *owner)
Releases a prisoner, leaving the cell's bytes as they are.
embed_bool mmgr_movable_init(SeriesCellularum *series, const CarcerMovableInitCfg *args, embed_bool wipes)
Assigns a cellblock's movable option: the translation table, its claim bits, the rows of touches and ...
size_t mmgr_movable_buf_measure(const SeriesCellularum *series, const CarcerMovableCfg *args)
The bytes from args->offset of a movable cell to the next zero byte.
void * mmgr_max_security_buf_grow(SeriesCellularum *series, void *prisoner, size_t size, const CellOwner *owner)
Grows a cell in place as mmgr_buf_grow does, and zeroes the bytes the growth adds.
void mmgr_owner_bytes_given(const CellOwner *owner, size_t bytes)
Takes bytes mmgr_owner_bytes_taken counted off their owner.
void * mmgr_temporary_buf_alloc(SeriesCellularum *series, size_t size, const CellOwner *owner)
Takes size bytes from a cellblock's temporary tier.
void * mmgr_persistent_max_security_buf_alloc(SeriesCellularum *series, size_t size, const CellOwner *owner)
Takes size bytes from a maximum security cellblock's persistent tier.
void mmgr_movable_run(SeriesCellularum *series)
The mover: what the thread the program gave runs, until its thread_wait answers EMBED_FALSE.
embed_bool mmgr_owner_listed(const CellOwner *owner)
Whether an owner is one of the table's.
void mmgr_carcer_hw_misnamed(const CellOwner *named, const CellOwner *cell_owner, size_t bytes)
Told of each give or growth that names an owner other than the one the cell counts to.
embed_bool mmgr_movable_buf_write(SeriesCellularum *series, const CarcerMovableCfg *args)
Copies args->bytes from args->src to args->offset of a movable cell.
embed_iword mmgr_movable_buf_compare(const SeriesCellularum *series, const CarcerMovableCfg *args)
Orders args->bytes of a movable cell against args->src, or against another cell where args->other_cel...
#define MMGR_OWNER_PLACE_BITS
The owner word a cell carries behind its payload, one machine word laid as fields,...
embed_bool mmgr_movable_buf_read(const SeriesCellularum *series, const CarcerMovableCfg *args)
Copies args->bytes from args->offset of a movable cell to args->dst.
static size_t mmgr_series_cellularum_persistent_hw(const SeriesCellularum *series)
The furthest a cellblock's persistent tier has reached.
static size_t mmgr_owner_in_use(const CellOwner *owner)
The payload bytes of the cells an owner has now.
void mmgr_zero_buf(void *prisoner, size_t size)
Zeroes size bytes at prisoner, releasing nothing.
void * mmgr_temporary_max_security_buf_alloc(SeriesCellularum *series, size_t size, const CellOwner *owner)
Takes size bytes from a maximum security cellblock's temporary tier.
embed_bool mmgr_movable_touches(const SeriesCellularum *series, size_t at, size_t *id, size_t *touches)
One row of touches a lane, counted as passes go through it.
size_t mmgr_align_up_buf(size_t size)
Rounds size up to a whole machine word.
#define MMGR_CARCER_ALIGN
Alignment every prisoner is handed out at: one machine word, or the atomic size word's alignment wher...
static size_t mmgr_series_cellularum_temporary_top(const SeriesCellularum *series)
Offset of the lowest temporary cell.
struct CarcerMovableEntry CarcerMovableEntry
The identity a movable cell's taker holds: a const pointer to the cell's entry in the cellblock's tra...
size_t mmgr_series_cellularum_fragmentation(const SeriesCellularum *series)
A cellblock's fragmentation: the bytes its persistent tier occupies and no cell's payload holds,...
static size_t mmgr_series_cellularum_persistent_end(const SeriesCellularum *series)
Offset just past the last persistent cell.
void mmgr_owner_bytes_taken(const CellOwner *owner, size_t bytes)
Counts bytes that lie in no cellblock to an owner, as a ring's or a page's are.
void * mmgr_buf_grow(SeriesCellularum *series, void *prisoner, size_t size, const CellOwner *owner)
Grows a cell to size payload bytes in place, where it is the newest cell of its tier.
const CellOwner * mmgr_owner_of_buf(const SeriesCellularum *series, const void *prisoner)
The owner a cell counts to, read off the word behind its payload.
CellOwner mmgr_owner_table[]
The program's owner table and the owners in it, which MMGR_OWNERS defines once.
#define MMGR_CARCER_OWNERS
The most owners a program's owner table holds.
uint64_t mmgr_carcer_hw_clock_ns(void)
The library's clock in nanoseconds, which a cell's owner word carries the take on.
embed_bool mmgr_movable_buf_move(SeriesCellularum *series)
Moves one movable cell into the first free cell below it, highest cell first.
#define MMGR_CARCER_HALF_BITS
The bits each boundary has in the word that carries both, half the atomic size word,...
embed_bool mmgr_who_owns_buf(const SeriesCellularum *series, const void *at)
Whether at lies inside the cellblock's bytes.
void mmgr_persistent_max_security_buf_release(SeriesCellularum *series, void *prisoner, const CellOwner *owner)
Zeroes a cell, then releases the prisoner.
The embedded_types headers every module builds on.
#define MMGR_ALIGN_BYTES
The DMA module, reached only when MMGR_ENABLE_DMA is set.
Definition mmgr.h:59
#define MMGR_ALLOC_SIZE(arg_)
States which argument of an allocation entry gives the extent of what it returns.
Definition mmgr.h:115
Arguments for the calls that reach a movable cell's bytes, each reading the members it names.
const CarcerMovableEntry *const cell
const void *const src
const CarcerMovableEntry *const other_cell
const size_t offset
const size_t bytes
const size_t other_offset
What a program gives a cellblock for the movable option, once, through its guard's movable_init.
void(*const) thread_yield(void)
embed_bool(*const) thread_wait(uint32_t milliseconds)
void(*const) thread_moving(embed_bool moving)
embed_bool(*const) thread_started(void(*run)(void *context), void *context)
size_t(*const) thread_id(void)
const size_t storage_bytes
Asserts MMGR_CARCER_ALIGN is a power of two.
embed_atomic_usize hw
embed_atomic_usize in_use
const char * name
The maximum security guard over one cellblock: every release zeroes the cell first.
embed_bool(*) movable_init(const CarcerMovableInitCfg *args)
embed_iword(*) movable_buf_compare(const CarcerMovableCfg *args)
embed_bool(*) movable_buf_write(const CarcerMovableCfg *args)
embed_bool(*) who_owns_buf(const void *at)
size_t(*) buf_available(void)
embed_bool(*) movable_buf_release(const CarcerMovableEntry *cell, const CellOwner *owner)
embed_bool(*) movable_buf_read(const CarcerMovableCfg *args)
void(*) persistent_buf_release(void *prisoner, const CellOwner *owner)
size_t(*) movable_buf_measure(const CarcerMovableCfg *args)
The minimum security guard over one cellblock: a released cell keeps whatever is in it.
embed_bool(*) movable_init(const CarcerMovableInitCfg *args)
embed_bool(*) movable_buf_write(const CarcerMovableCfg *args)
void(*) persistent_buf_release(void *prisoner, const CellOwner *owner)
embed_bool(*) movable_buf_read(const CarcerMovableCfg *args)
embed_iword(*) movable_buf_compare(const CarcerMovableCfg *args)
size_t(*) movable_buf_measure(const CarcerMovableCfg *args)
embed_bool(*) movable_buf_release(const CarcerMovableEntry *cell, const CellOwner *owner)
size_t(*) buf_available(void)
embed_bool(*) who_owns_buf(const void *at)
One cellblock's state: its bytes, the two boundaries that grow toward each other, the closed word a w...
embed_atomic_uptr movable
embed_atomic_usize in_use
embed_atomic_usize sink
EmbedTurnstiles turnstiles
embed_atomic_usize persistent_overrun
uint8_t *const base
embed_atomic_word closed
embed_atomic_usize persistent_in_use
const size_t size
embed_atomic_usize bounds
embed_atomic_usize temporary_overrun