2#include "gaia/config/config.h"
3#include "gaia/config/profiler.h"
11#if GAIA_ALLOC_ARENA_LOCK || GAIA_ASSERT_ENABLED
14#if !GAIA_ALLOC_ARENA_LOCK && GAIA_ASSERT_ENABLED
18#if GAIA_PLATFORM_WINDOWS
19 #define GAIA_MEM_ALLC(size) ::malloc(size)
20 #define GAIA_MEM_FREE(ptr) ::free(ptr)
23 #if !defined(aligned_alloc)
24 #define GAIA_MEM_ALLC_A(size, alig) ::_aligned_malloc(size, alig)
25 #define GAIA_MEM_FREE_A(ptr) ::_aligned_free(ptr)
27 #define GAIA_MEM_ALLC_A(size, alig) ::aligned_alloc(alig, size)
28 #define GAIA_MEM_FREE_A(ptr) ::aligned_free(ptr)
31 #define GAIA_MEM_ALLC(size) ::malloc(size)
32 #define GAIA_MEM_ALLC_A(size, alig) ::aligned_alloc(alig, size)
33 #define GAIA_MEM_FREE(ptr) ::free(ptr)
34 #define GAIA_MEM_FREE_A(ptr) ::free(ptr)
53 class ArenaLock final {
54#if GAIA_ALLOC_ARENA_LOCK
55 inline static std::atomic_int32_t s_lock{0};
56#elif GAIA_ASSERT_ENABLED
57 inline static std::atomic_flag s_entered = ATOMIC_FLAG_INIT;
58 inline static std::atomic<std::thread::id> s_owner{};
59 #if GAIA_ECS_TEST_HOOKS
60 inline static std::atomic_uint32_t s_testViolations = 0;
65 ArenaLock() noexcept {
66#if GAIA_ALLOC_ARENA_LOCK
67 while (s_lock.exchange(1, std::memory_order_acquire) != 0) {
68 while (s_lock.load(std::memory_order_relaxed) != 0)
69 #if defined(__aarch64__)
70 __asm__
volatile(
"yield");
75#elif GAIA_ASSERT_ENABLED
76 const auto me = std::this_thread::get_id();
77 if (s_entered.test_and_set(std::memory_order_acquire)) {
78 const auto other = s_owner.load(std::memory_order_relaxed);
79 #if GAIA_ECS_TEST_HOOKS
86 other == me &&
"Allocation arena entered concurrently from two "
87 "threads. Define GAIA_ALLOC_ARENA_LOCK=1 to serialize the arenas, or "
88 "keep one mutating World per process.");
91 s_owner.store(me, std::memory_order_relaxed);
96 ~ArenaLock() noexcept {
97#if GAIA_ALLOC_ARENA_LOCK
98 s_lock.store(0, std::memory_order_release);
99#elif GAIA_ASSERT_ENABLED
100 s_entered.clear(std::memory_order_release);
104 ArenaLock(
const ArenaLock&) =
delete;
105 ArenaLock& operator=(
const ArenaLock&) =
delete;
110 GAIA_NODISCARD
static uint32_t test_violations() noexcept {
111#if !GAIA_ALLOC_ARENA_LOCK && GAIA_ASSERT_ENABLED && GAIA_ECS_TEST_HOOKS
112 return s_testViolations.load(std::memory_order_relaxed);
126 GAIA_NODISCARD
static void*
alloc(
size_t size) {
127 GAIA_ASSERT(size > 0);
129 void* ptr = GAIA_MEM_ALLC(size);
130 GAIA_ASSERT(ptr !=
nullptr);
131 GAIA_PROF_ALLOC(ptr, size);
139 GAIA_NODISCARD
static void*
alloc([[maybe_unused]]
const char* name,
size_t size) {
140 GAIA_ASSERT(size > 0);
142 void* ptr = GAIA_MEM_ALLC(size);
143 GAIA_ASSERT(ptr !=
nullptr);
144 GAIA_PROF_ALLOC2(ptr, size, name);
152 GAIA_NODISCARD
static void*
alloc_alig(
size_t size,
size_t alig) {
153 GAIA_ASSERT(size > 0);
154 GAIA_ASSERT(alig > 0);
157 size = (size + alig - 1) & ~(alig - 1);
158 void* ptr = GAIA_MEM_ALLC_A(size, alig);
159 GAIA_ASSERT(ptr !=
nullptr);
160 GAIA_PROF_ALLOC(ptr, size);
169 GAIA_NODISCARD
static void*
alloc_alig([[maybe_unused]]
const char* name,
size_t size,
size_t alig) {
170 GAIA_ASSERT(size > 0);
171 GAIA_ASSERT(alig > 0);
174 size = (size + alig - 1) & ~(alig - 1);
175 void* ptr = GAIA_MEM_ALLC_A(size, alig);
176 GAIA_ASSERT(ptr !=
nullptr);
177 GAIA_PROF_ALLOC2(ptr, size, name);
184 GAIA_ASSERT(ptr !=
nullptr);
193 static void free([[maybe_unused]]
const char* name,
void* ptr) {
194 GAIA_ASSERT(ptr !=
nullptr);
197 GAIA_PROF_FREE2(ptr, name);
203 GAIA_ASSERT(ptr !=
nullptr);
205 GAIA_MEM_FREE_A(ptr);
212 static void free_alig([[maybe_unused]]
const char* name,
void* ptr) {
213 GAIA_ASSERT(ptr !=
nullptr);
215 GAIA_MEM_FREE_A(ptr);
216 GAIA_PROF_FREE2(ptr, name);
237 template <
typename T,
typename Adaptor = DefaultAllocatorAdaptor>
238 GAIA_NODISCARD
static T*
alloc(uint32_t cnt = 1) {
239 return (T*)Adaptor::get().alloc(
sizeof(T) * cnt);
247 template <
typename T,
typename Adaptor = DefaultAllocatorAdaptor>
248 GAIA_NODISCARD
static T*
alloc(
const char* name, uint32_t cnt = 1) {
249 return (T*)Adaptor::get().alloc(name,
sizeof(T) * cnt);
257 template <
typename T,
typename Adaptor = DefaultAllocatorAdaptor>
258 GAIA_NODISCARD
static T*
alloc_alig(
size_t alig, uint32_t cnt = 1) {
259 return (T*)Adaptor::get().alloc_alig(
sizeof(T) * cnt, alig);
268 template <
typename T,
typename Adaptor = DefaultAllocatorAdaptor>
269 GAIA_NODISCARD
static T*
alloc_alig(
const char* name,
size_t alig, uint32_t cnt = 1) {
270 return (T*)Adaptor::get().alloc_alig(name,
sizeof(T) * cnt, alig);
275 template <
typename Adaptor = DefaultAllocatorAdaptor>
277 Adaptor::get().free(ptr);
283 template <
typename Adaptor = DefaultAllocatorAdaptor>
284 static void free(
const char* name,
void* ptr) {
285 Adaptor::get().free(name, ptr);
290 template <
typename Adaptor = DefaultAllocatorAdaptor>
292 Adaptor::get().free_alig(ptr);
298 template <
typename Adaptor = DefaultAllocatorAdaptor>
300 Adaptor::get().free_alig(name, ptr);
307 GAIA_NODISCARD
inline void* mem_alloc(
size_t size) {
315 GAIA_NODISCARD
inline void* mem_alloc(
const char* name,
size_t size) {
324 GAIA_NODISCARD
inline void* mem_alloc_alig(
size_t size,
size_t alig) {
334 GAIA_NODISCARD
inline void* mem_alloc_alig(
const char* name,
size_t size,
size_t alig) {
340 inline void mem_free(
void* ptr) {
347 inline void mem_free(
const char* name,
void* ptr) {
353 inline void mem_free_alig(
void* ptr) {
360 inline void mem_free_alig(
const char* name,
void* ptr) {
368 template <
typename T,
typename V>
369 GAIA_NODISCARD
constexpr T align(T num, V alignment) {
370 return alignment == 0 ? num : ((num + (alignment - 1)) / alignment) * alignment;
378 template <
size_t alignment,
typename T>
379 GAIA_NODISCARD
constexpr T align(T num) {
380 return ((num + (alignment - 1)) & ~(alignment - 1));
387 template <
typename T,
typename V>
388 GAIA_NODISCARD
constexpr uint32_t padding(T num, V alignment) {
389 return (uint32_t)(align(num, alignment) - num);
396 template <
size_t alignment,
typename T>
397 GAIA_NODISCARD
constexpr uint32_t padding(T num) {
398 return (uint32_t)(align<alignment>(num) - num);
406 template <
typename Dst,
typename Src>
407 GAIA_NODISCARD Dst bit_cast(
const Src& src) {
408 static_assert(
sizeof(Dst) ==
sizeof(Src));
409 static_assert(std::is_trivially_copyable_v<Src>);
410 static_assert(std::is_trivially_copyable_v<Dst>);
416 memmove((
void*)&dst, (
const void*)&src,
sizeof(Dst));
422 template <
typename T>
435 memmove(m_p, (
const void*)&value,
sizeof(T));
441 GAIA_NODISCARD
operator T()
const {
443 memmove((
void*)&tmp, (
const void*)m_p,
sizeof(T));
Pointer wrapper for writing memory in defined way (not causing undefined behavior)
Definition mem_alloc.h:423
unaligned_ref & operator=(const T &value)
Stores a value using byte-wise copying.
Definition mem_alloc.h:434
unaligned_ref(void *p)
Creates a reference to potentially unaligned storage.
Definition mem_alloc.h:429
Typed allocation facade for allocator adaptors.
Definition mem_alloc.h:231
static GAIA_NODISCARD T * alloc_alig(const char *name, size_t alig, uint32_t cnt=1)
Allocates named aligned storage for objects without constructing them.
Definition mem_alloc.h:269
static GAIA_NODISCARD T * alloc_alig(size_t alig, uint32_t cnt=1)
Allocates aligned storage for objects without constructing them.
Definition mem_alloc.h:258
static void free_alig(void *ptr)
Releases aligned storage allocated through an adaptor.
Definition mem_alloc.h:291
static GAIA_NODISCARD T * alloc(const char *name, uint32_t cnt=1)
Allocates named storage for objects without constructing them.
Definition mem_alloc.h:248
static GAIA_NODISCARD T * alloc(uint32_t cnt=1)
Allocates storage for objects without constructing them.
Definition mem_alloc.h:238
static void free_alig(const char *name, void *ptr)
Releases named aligned storage allocated through an adaptor.
Definition mem_alloc.h:299
static void free(void *ptr)
Releases storage allocated through an adaptor.
Definition mem_alloc.h:276
static void free(const char *name, void *ptr)
Releases named storage allocated through an adaptor.
Definition mem_alloc.h:284
Provides the shared default allocator instance expected by AllocHelper.
Definition mem_alloc.h:221
static DefaultAllocator & get()
Returns the process-local default allocator.
Definition mem_alloc.h:224
Stateless allocator backed by the platform heap.
Definition mem_alloc.h:122
static GAIA_NODISCARD void * alloc_alig(size_t size, size_t alig)
Allocates an aligned memory region.
Definition mem_alloc.h:152
static void free_alig(void *ptr)
Releases an aligned memory region.
Definition mem_alloc.h:202
static void free(void *ptr)
Releases an unaligned memory region.
Definition mem_alloc.h:183
static GAIA_NODISCARD void * alloc(const char *name, size_t size)
Allocates a named unaligned memory region.
Definition mem_alloc.h:139
static GAIA_NODISCARD void * alloc(size_t size)
Allocates an unaligned memory region.
Definition mem_alloc.h:126
static void free_alig(const char *name, void *ptr)
Releases a named aligned memory region.
Definition mem_alloc.h:212
static void free(const char *name, void *ptr)
Releases a named unaligned memory region.
Definition mem_alloc.h:193
static GAIA_NODISCARD void * alloc_alig(const char *name, size_t size, size_t alig)
Allocates a named aligned memory region.
Definition mem_alloc.h:169