MayaFlux 0.4.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
LiveArena.hpp
Go to the documentation of this file.
1#pragma once
2
3namespace MayaFlux {
4
5namespace internal {
6
7#if defined(MAYAFLUX_PLATFORM_WINDOWS)
8#define MF_PRETTY_FUNCTION __FUNCSIG__
9#else
10#define MF_PRETTY_FUNCTION __PRETTY_FUNCTION__
11#endif
12
13 /**
14 * @brief Maximum number of objects that can be registered in the live arena.
15 */
16 inline constexpr std::size_t LIVE_ARENA_MAX_ENTRIES = 256;
17
18 /**
19 * @brief Maximum length of a live arena key, including null terminator.
20 */
21 inline constexpr std::size_t LIVE_ARENA_KEY_MAX = 64;
22
23 /**
24 * @brief Default capacity of the live arena in bytes.
25 */
26 inline constexpr std::size_t LIVE_ARENA_CAPACITY = static_cast<const std::size_t>(16 * 1024 * 1024);
27
28 // =========================================================================
29
30 /**
31 * @brief Single directory entry mapping a string key to a location within the arena.
32 *
33 * offset == UINT32_MAX indicates an externally owned object whose pointer
34 * is stored in g_live_arena_shared_ptrs at the same index.
35 */
38 uint32_t offset;
39 uint32_t size;
41 };
42
43 /**
44 * @brief POD header block placed at the start of the arena storage.
45 *
46 * The directory array lives here. All offsets in LiveArenaEntry are
47 * relative to the first byte of object storage that follows this header.
48 */
53
54 // =========================================================================
55
56 /**
57 * @brief Raw backing storage for the live arena with external linkage.
58 *
59 * Exported so the JIT can locate it via DynamicLibrarySearchGenerator.
60 * The first sizeof(LiveArenaHeader) bytes are the directory.
61 * Object storage begins immediately after.
62 */
63 alignas(std::max_align_t) extern MAYAFLUX_API unsigned char g_live_arena_storage[LIVE_ARENA_CAPACITY];
64
65 /**
66 * @brief Byte offset of the bump pointer from the start of the object region.
67 */
68 extern MAYAFLUX_API uint32_t g_live_arena_bump;
69
70 /**
71 * @brief Shared ownership table for objects registered via expose_live.
72 *
73 * Stores shared_ptr<void> so live_cast can reconstruct a typed shared_ptr<T>
74 * via static_pointer_cast. Null for bump-allocated entries.
75 */
76 extern MAYAFLUX_API std::shared_ptr<void> g_live_arena_shared_ptrs[LIVE_ARENA_MAX_ENTRIES];
77
78 // =========================================================================
79
80 /**
81 * @brief Returns a pointer to the header block at the start of arena storage.
82 */
84 {
85 return reinterpret_cast<LiveArenaHeader*>(g_live_arena_storage);
86 }
87
88 /**
89 * @brief Returns a pointer to the object region (immediately after the header).
90 */
91 inline unsigned char* live_arena_object_region() noexcept
92 {
94 }
95
96 /**
97 * @brief Aligns @p value up to the next multiple of @p align.
98 */
99 inline uint32_t align_up(uint32_t value, uint32_t align) noexcept
100 {
101 return (value + align - 1U) & ~(align - 1U);
102 }
103
104 /**
105 * @brief Locates an existing directory entry by key.
106 * @return Pointer to the matching entry, or nullptr if not found.
107 */
108 inline LiveArenaEntry* live_arena_find(const char* key) noexcept
109 {
110 auto* hdr = live_arena_header();
111 for (uint32_t i = 0; i < hdr->entry_count; ++i) {
112 if (hdr->entries[i].occupied
113 && std::strncmp(hdr->entries[i].key, key, LIVE_ARENA_KEY_MAX) == 0) {
114 return &hdr->entries[i];
115 }
116 }
117 return nullptr;
118 }
119
120 /**
121 * @brief Allocates @p size bytes from the arena with @p alignment.
122 *
123 * Writes a directory entry under @p key. Returns nullptr if the arena
124 * is full, the key is already registered, or capacity is exceeded.
125 */
126 inline void* live_arena_alloc(const char* key, uint32_t size, uint32_t alignment) noexcept
127 {
128 auto* hdr = live_arena_header();
129
130 if (hdr->entry_count >= LIVE_ARENA_MAX_ENTRIES) {
131 return nullptr;
132 }
133
134 if (live_arena_find(key)) {
135 return nullptr;
136 }
137
138 const auto object_region_capacity = static_cast<uint32_t>(LIVE_ARENA_CAPACITY - sizeof(LiveArenaHeader));
139
140 const uint32_t aligned_bump = align_up(g_live_arena_bump, alignment);
141
142 if (aligned_bump + size > object_region_capacity) {
143 return nullptr;
144 }
145
146 const uint32_t idx = hdr->entry_count;
147 auto& entry = hdr->entries[idx];
148 std::strncpy(entry.key, key, LIVE_ARENA_KEY_MAX - 1);
149 entry.key[LIVE_ARENA_KEY_MAX - 1] = '\0';
150 entry.offset = aligned_bump;
151 entry.size = size;
152 entry.occupied = true;
153
154 g_live_arena_bump = aligned_bump + size;
155 ++hdr->entry_count;
156
157 return live_arena_object_region() + aligned_bump;
158 }
159
160 /**
161 * @brief Registers an externally owned shared_ptr under @p key.
162 *
163 * Stores shared_ptr<void> in g_live_arena_shared_ptrs at the entry index.
164 * offset is set to UINT32_MAX to distinguish from bump-allocated entries.
165 *
166 * @return true on success, false if the key is already registered or
167 * the directory is full.
168 */
169 inline bool live_arena_expose(const char* key, std::shared_ptr<void> shared) noexcept
170 {
171 auto* hdr = live_arena_header();
172
173 if (hdr->entry_count >= LIVE_ARENA_MAX_ENTRIES) {
174 return false;
175 }
176
177 if (live_arena_find(key)) {
178 return false;
179 }
180
181 const uint32_t idx = hdr->entry_count;
182 auto& entry = hdr->entries[idx];
183 std::strncpy(entry.key, key, LIVE_ARENA_KEY_MAX - 1);
184 entry.key[LIVE_ARENA_KEY_MAX - 1] = '\0';
185 entry.offset = UINT32_MAX;
186 entry.size = UINT32_MAX;
187 entry.occupied = true;
188
189 g_live_arena_shared_ptrs[idx] = std::move(shared);
190
191 ++hdr->entry_count;
192 return true;
193 }
194
195 /**
196 * @brief Extracts the unqualified type name from a compiler-generated function signature.
197 *
198 * Parses __PRETTY_FUNCTION__ to extract the final component of a fully qualified
199 * type name. Used by MF_LIVE_EXPOSE_AUTO to derive stable per-type key prefixes
200 * without requiring the caller to supply a string literal.
201 *
202 * @tparam T Type to name.
203 * @return string_view into static storage valid for the process lifetime.
204 */
205 template <typename T>
206 constexpr std::string_view live_type_name() noexcept
207 {
208 std::string_view sv = MF_PRETTY_FUNCTION;
209 auto eq = sv.find("T = ");
210 auto end = sv.find(';', eq);
211 if (end == std::string_view::npos) {
212 end = sv.rfind(']');
213 }
214 if (eq == std::string_view::npos || end == std::string_view::npos) {
215 return sv;
216 }
217 sv = sv.substr(eq + 4, end - eq - 4);
218 auto colon = sv.rfind(':');
219 if (colon == std::string_view::npos) {
220 return sv;
221 }
222 return sv.substr(colon + 1);
223 }
224
225 /**
226 * @brief Formats a live arena key as "TypeName_N" into @p buf.
227 *
228 * @param buf Destination buffer of at least LIVE_ARENA_KEY_MAX bytes.
229 * @param name Unqualified type name, typically from live_type_name<T>().
230 * @param count Per-type monotonic counter value.
231 */
232 inline void live_format_key(char* buf, std::string_view name, uint32_t count) noexcept
233 {
234 std::snprintf(buf, LIVE_ARENA_KEY_MAX, "%.*s_%u",
235 static_cast<int>(name.size()), name.data(), count);
236 }
237
238} // namespace MayaFlux::internal
239
240/**
241 * @brief Exposes @p ptr to the live arena under a user-supplied key when MAYAFLUX_LIVE is defined.
242 *
243 * Expands to a no-op when MAYAFLUX_LIVE is not defined.
244 */
245#ifdef MAYAFLUX_LIVE
246#define MF_LIVE_EXPOSE(key, ptr) ::MayaFlux::expose_live((key), (ptr))
247#else
248#define MF_LIVE_EXPOSE(key, ptr) ((void)0)
249#endif
250
251/**
252 * @brief Auto-expose variant that deduces the key prefix from the shared_ptr element type.
253 *
254 * Generates keys of the form "TypeName_N" where N is a per-type monotonic counter.
255 * The static counter is local to each template instantiation so each type maintains
256 * its own independent sequence: Sine_0, Sine_1, Phasor_0, Phasor_1, etc.
257 *
258 * Use when no explicit name is available at the call site (e.g. read_* methods).
259 *
260 * @param ptr shared_ptr whose element_type drives both the key prefix and the counter.
261 */
262#ifdef MAYAFLUX_LIVE
263#define MF_LIVE_EXPOSE_AUTO(ptr) \
264 do { \
265 using _MfT = typename decltype(ptr)::element_type; \
266 static std::atomic<uint32_t> s_live_counter { 0 }; \
267 char _mf_key[::MayaFlux::internal::LIVE_ARENA_KEY_MAX]; \
268 ::MayaFlux::internal::live_format_key( \
269 _mf_key, \
270 ::MayaFlux::internal::live_type_name<_MfT>(), \
271 s_live_counter.fetch_add(1)); \
272 ::MayaFlux::expose_live(_mf_key, (ptr)); \
273 } while (0)
274#else
275#define MF_LIVE_EXPOSE_AUTO(ptr) ((void)0)
276#endif
277
278/**
279 * @brief Auto-expose variant with an explicit name prefix and per-name counter.
280 *
281 * Generates keys of the form "name_N" where N is a monotonic counter local to
282 * the call site's template instantiation. Use inside Creator macro expansions
283 * where the method name is available via the stringified macro parameter.
284 *
285 * @param name String literal key prefix, typically #method_name.
286 * @param ptr shared_ptr to expose.
287 */
288#ifdef MAYAFLUX_LIVE
289#define MF_LIVE_EXPOSE_NAMED(name, ptr) \
290 do { \
291 static std::atomic<uint32_t> s_live_counter { 0 }; \
292 char _mf_key[::MayaFlux::internal::LIVE_ARENA_KEY_MAX]; \
293 ::MayaFlux::internal::live_format_key( \
294 _mf_key, \
295 std::string_view { (name) }, \
296 s_live_counter.fetch_add(1)); \
297 ::MayaFlux::expose_live(_mf_key, (ptr)); \
298 } while (0)
299#else
300#define MF_LIVE_EXPOSE_NAMED(name, ptr) ((void)0)
301#endif
302// =============================================================================
303
304/**
305 * @brief Constructs a T in-place inside the live arena and registers it under @p key.
306 *
307 * The returned shared_ptr is stable for the process lifetime and participates
308 * in normal shared_ptr ref-counting. The arena owns the underlying storage;
309 * the shared_ptr uses a no-op deleter so the arena remains the sole allocator.
310 *
311 * Reachable from JIT'd code immediately via live_cast<T>(key).
312 *
313 * @tparam T Type to construct.
314 * @param key Null-terminated string used to locate the object from the JIT.
315 * Must be unique within the arena.
316 * @param args Arguments forwarded to T's constructor.
317 * @return shared_ptr to the constructed object, or nullptr if the arena is
318 * full or @p key is already registered.
319 */
320template <typename T, typename... Args>
321std::shared_ptr<T> make_live(const char* key, Args&&... args)
322{
323 void* mem = internal::live_arena_alloc(
324 key,
325 static_cast<uint32_t>(sizeof(T)),
326 static_cast<uint32_t>(alignof(T)));
327
328 if (!mem) {
329 return nullptr;
330 }
331
332 T* obj = ::new (mem) T(std::forward<Args>(args)...);
333 return std::shared_ptr<T>(obj, [](T*) { });
334}
335
336/**
337 * @brief Exposes an existing shared_ptr-managed object to the live arena under @p key.
338 *
339 * The shared_ptr ref-count is incremented; the object remains alive at least
340 * as long as the arena entry exists. Reachable from JIT'd code via live_cast<T>(key).
341 *
342 * @tparam T Type of the object.
343 * @param key Null-terminated string used to locate the object from the JIT.
344 * @param obj shared_ptr to the existing object.
345 * @return true on success, false if @p key is already registered or the
346 * directory is full.
347 */
348template <typename T>
349bool expose_live(const char* key, const std::shared_ptr<T>& obj) noexcept
350{
351 return internal::live_arena_expose(key, std::static_pointer_cast<void>(obj));
352}
353
354/**
355 * @brief Exposes an existing raw pointer to the live arena under @p key.
356 *
357 * Lifetime management remains entirely the caller's responsibility.
358 * The arena stores a no-op shared_ptr so live_cast returns a valid
359 * shared_ptr without affecting the object's actual lifetime.
360 *
361 * @tparam T Type of the object.
362 * @param key Null-terminated string used to locate the object from the JIT.
363 * @param ptr Raw pointer to the existing object.
364 * @return true on success, false if @p key is already registered or the
365 * directory is full.
366 */
367template <typename T>
368bool expose_live(const char* key, T* ptr) noexcept
369{
370 return internal::live_arena_expose(key, std::shared_ptr<void>(ptr, [](void*) { }));
371}
372
373/**
374 * @brief Internal implementation for live_cast. Resolves through the export
375 * table so JIT'd code can reach arena globals across DLL boundaries.
376 *
377 * @param key Null-terminated arena key.
378 * @return Raw pointer to the registered object, or nullptr.
379 */
380MAYAFLUX_API void* live_cast_impl(const char* key) noexcept;
381
382/**
383 * @brief Retrieves a live-arena object by key as a shared_ptr<T>.
384 *
385 * Delegates the directory walk to live_cast_impl so the call resolves
386 * through the DLL export table on all platforms. The returned shared_ptr
387 * uses a no-op deleter; lifetime is managed by the arena or the original
388 * expose_live caller.
389 *
390 * @tparam T Expected type of the registered object.
391 * @param key Null-terminated string used at make_live or expose_live time.
392 * @return shared_ptr<T>, or nullptr if the key is not found.
393 */
394template <typename T>
395std::shared_ptr<T> live_cast(const char* key) noexcept
396{
397 void* ptr = live_cast_impl(key);
398 if (!ptr) {
399 return nullptr;
400 }
401 return std::shared_ptr<T>(static_cast<T*>(ptr), [](T*) { });
402}
403
404/**
405 * @brief Dumps all live arena entries to stderr.
406 *
407 * Prints each occupied entry's index, key, and offset, followed by
408 * the total entry count and bump pointer value. Intended for diagnostic
409 * use during development; output goes to stderr unconditionally.
410 */
411MAYAFLUX_API void live_arena_dump() noexcept;
412
413} // namespace MayaFlux
#define MF_PRETTY_FUNCTION
Definition LiveArena.hpp:10
Eigen::Index count
Range size
constexpr std::size_t LIVE_ARENA_KEY_MAX
Maximum length of a live arena key, including null terminator.
Definition LiveArena.hpp:21
uint32_t align_up(uint32_t value, uint32_t align) noexcept
Aligns value up to the next multiple of align.
Definition LiveArena.hpp:99
constexpr std::size_t LIVE_ARENA_MAX_ENTRIES
Maximum number of objects that can be registered in the live arena.
Definition LiveArena.hpp:16
constexpr std::string_view live_type_name() noexcept
Extracts the unqualified type name from a compiler-generated function signature.
uint32_t g_live_arena_bump
Byte offset of the bump pointer from the start of the object region.
Definition LiveArena.cpp:8
constexpr std::size_t LIVE_ARENA_CAPACITY
Default capacity of the live arena in bytes.
Definition LiveArena.hpp:26
void live_format_key(char *buf, std::string_view name, uint32_t count) noexcept
Formats a live arena key as "TypeName_N" into buf.
LiveArenaHeader * live_arena_header() noexcept
Returns a pointer to the header block at the start of arena storage.
Definition LiveArena.hpp:83
unsigned char * live_arena_object_region() noexcept
Returns a pointer to the object region (immediately after the header).
Definition LiveArena.hpp:91
bool live_arena_expose(const char *key, std::shared_ptr< void > shared) noexcept
Registers an externally owned shared_ptr under key.
LiveArenaEntry * live_arena_find(const char *key) noexcept
Locates an existing directory entry by key.
void * live_arena_alloc(const char *key, uint32_t size, uint32_t alignment) noexcept
Allocates size bytes from the arena with alignment.
unsigned char g_live_arena_storage[LIVE_ARENA_CAPACITY]
Raw backing storage for the live arena with external linkage.
Definition LiveArena.cpp:7
std::shared_ptr< void > g_live_arena_shared_ptrs[LIVE_ARENA_MAX_ENTRIES]
Shared ownership table for objects registered via expose_live.
Definition LiveArena.cpp:9
MAYAFLUX_API void * live_cast_impl(const char *key) noexcept
Internal implementation for live_cast.
Definition LiveArena.cpp:13
bool expose_live(const char *key, const std::shared_ptr< T > &obj) noexcept
Exposes an existing shared_ptr-managed object to the live arena under key.
MAYAFLUX_API void live_arena_dump() noexcept
Dumps all live arena entries to stderr.
Definition LiveArena.cpp:32
std::shared_ptr< T > live_cast(const char *key) noexcept
Retrieves a live-arena object by key as a shared_ptr<T>.
std::shared_ptr< T > make_live(const char *key, Args &&... args)
Constructs a T in-place inside the live arena and registers it under key.
Main namespace for the Maya Flux audio engine.
Definition Runtime.cpp:12
char key[LIVE_ARENA_KEY_MAX]
Definition LiveArena.hpp:37
Single directory entry mapping a string key to a location within the arena.
Definition LiveArena.hpp:36
LiveArenaEntry entries[LIVE_ARENA_MAX_ENTRIES]
Definition LiveArena.hpp:51
POD header block placed at the start of the arena storage.
Definition LiveArena.hpp:49