fixing bugs and adding some test scripts
Some checks failed
CodeQL / Analyze (${{ matrix.language }}) (autobuild, c-cpp) (push) Has been cancelled
Microsoft C++ Code Analysis / Analyze (push) Has been cancelled

This commit is contained in:
Dennis Eichhorn 2025-04-27 20:10:58 +00:00
parent 2883ca0841
commit eb9a135ca7
36 changed files with 577 additions and 160 deletions

View File

@ -17,8 +17,8 @@
// Only allowed for data >= 64 bits // Only allowed for data >= 64 bits
bool is_empty(const byte* region, uint64 size, int32 steps = 8) bool is_empty(const byte* region, uint64 size, int32 steps = 8)
{ {
// Quick check of first 8 bytes // Quick check of first byte
if (*((uint64 *) region) != 0) { if (*region != 0) {
return false; return false;
} }

58
check_cpu_features.sh Normal file
View File

@ -0,0 +1,58 @@
#!/bin/bash
check_cpu_features() {
local FLAGS=""
local MACROS=""
# Check for SSE4.2
if lscpu | grep -qi sse4_2; then
FLAGS+=" -msse4.2"
MACROS+=" -D__SSE4_2__"
fi
# Check for AVX
if lscpu | grep -qi avx; then
FLAGS+=" -mavx"
MACROS+=" -D__AVX__"
fi
# Check for AVX2
if lscpu | grep -qi avx2; then
FLAGS+=" -mavx2"
MACROS+=" -D__AVX2__"
fi
# Check for AVX512
if lscpu | grep -qi avx512; then
FLAGS+=" -mavx512f -mavx512cd -mavx512vl -mavx512bw -mavx512dq -mavx512ifma -mavx512vbmi"
MACROS+=" -D__AVX512F__"
fi
# Check for FMA
if lscpu | grep -qi fma; then
FLAGS+=" -mfma"
MACROS+=" -D__FMA__"
fi
# Check for POPCNT
if lscpu | grep -qi popcnt; then
FLAGS+=" -mpopcnt"
MACROS+=" -D__POPCNT__"
fi
# Check for endianness
if [ "$(lscpu | grep -i 'byte order' | awk '{print $3}')" = "Little" ]; then
MACROS+=" -D__LITTLE_ENDIAN__"
elif [ "$(lscpu | grep -i 'byte order' | awk '{print $3}')" = "Big" ]; then
MACROS+=" -D__BIG_ENDIAN__"
fi
# Return the results (two different methods shown)
# Method 1: Set global variables
CPU_FLAGS="$FLAGS"
CPU_MACROS="$MACROS"
# Method 2: Output to stdout (alternative approach)
echo "$FLAGS $MACROS"
}

View File

@ -34,7 +34,7 @@ struct DatabasePool {
void db_pool_alloc(DatabasePool* pool, uint8 count) { void db_pool_alloc(DatabasePool* pool, uint8 count) {
ASSERT_SIMPLE(count); ASSERT_SIMPLE(count);
PROFILE(PROFILE_DB_POOL_ALLOC, NULL, false, true); PROFILE(PROFILE_DB_POOL_ALLOC, NULL, false, true);
LOG_1("Allocating DatabasePool"); LOG_1("Allocating DatabasePool for %d connections", {{LOG_DATA_BYTE, &count}});
uint64 size = count * sizeof(DatabaseConnection) uint64 size = count * sizeof(DatabaseConnection)
+ sizeof(uint64) * CEIL_DIV(count, 64) // free + sizeof(uint64) * CEIL_DIV(count, 64) // free
@ -43,8 +43,6 @@ void db_pool_alloc(DatabasePool* pool, uint8 count) {
pool->connections = (DatabaseConnection *) platform_alloc_aligned(size, 64); pool->connections = (DatabaseConnection *) platform_alloc_aligned(size, 64);
pool->free = (uint64 *) ROUND_TO_NEAREST((uintptr_t) (pool->connections + count * sizeof(DatabaseConnection)), 64); pool->free = (uint64 *) ROUND_TO_NEAREST((uintptr_t) (pool->connections + count * sizeof(DatabaseConnection)), 64);
pool->count = count; pool->count = count;
LOG_1("Allocated DatabasePool: %n B", {{LOG_DATA_UINT64, &pool->count}});
} }
void db_pool_add(DatabasePool* __restrict pool, DatabaseConnection* __restrict db) noexcept { void db_pool_add(DatabasePool* __restrict pool, DatabaseConnection* __restrict db) noexcept {
@ -53,6 +51,8 @@ void db_pool_add(DatabasePool* __restrict pool, DatabaseConnection* __restrict d
} }
void db_pool_free(DatabasePool* pool) { void db_pool_free(DatabasePool* pool) {
LOG_1("Freeing DatabasePool");
for (int32 i = 0; i < pool->count; ++i) { for (int32 i = 0; i < pool->count; ++i) {
db_close(&pool->connections[i]); db_close(&pool->connections[i]);
} }
@ -60,8 +60,6 @@ void db_pool_free(DatabasePool* pool) {
platform_aligned_free((void **) &pool->connections); platform_aligned_free((void **) &pool->connections);
pool->free = NULL; pool->free = NULL;
pool->count = 0; pool->count = 0;
LOG_1("Freed DatabasePool");
} }
// Returns free database connection or null if none could be found // Returns free database connection or null if none could be found

View File

@ -67,7 +67,9 @@ enum LogDataType {
LOG_DATA_NONE, LOG_DATA_NONE,
LOG_DATA_VOID, LOG_DATA_VOID,
LOG_DATA_BYTE, LOG_DATA_BYTE,
LOG_DATA_INT16,
LOG_DATA_INT32, LOG_DATA_INT32,
LOG_DATA_UINT16,
LOG_DATA_UINT32, LOG_DATA_UINT32,
LOG_DATA_INT64, LOG_DATA_INT64,
LOG_DATA_UINT64, LOG_DATA_UINT64,
@ -100,7 +102,7 @@ struct LogDataArray{
LogData data[LOG_DATA_ARRAY]; LogData data[LOG_DATA_ARRAY];
}; };
// @bug This probably requires thread safety // @bug This needs to be thread safe
byte* log_get_memory() noexcept byte* log_get_memory() noexcept
{ {
if (_log_memory->pos + MAX_LOG_LENGTH > _log_memory->size) { if (_log_memory->pos + MAX_LOG_LENGTH > _log_memory->size) {
@ -116,6 +118,7 @@ byte* log_get_memory() noexcept
} }
// @performance This should only be called async to avoid blocking (e.g. render loop) // @performance This should only be called async to avoid blocking (e.g. render loop)
// @bug This needs to be thread safe
void log_to_file() void log_to_file()
{ {
// we don't log an empty log pool // we don't log an empty log pool
@ -157,6 +160,7 @@ void log_flush()
_log_memory->pos = 0; _log_memory->pos = 0;
} }
// @bug This needs to be thread safe
void log(const char* str, const char* file, const char* function, int32 line) void log(const char* str, const char* file, const char* function, int32 line)
{ {
if (!_log_memory) { if (!_log_memory) {
@ -199,6 +203,7 @@ void log(const char* str, const char* file, const char* function, int32 line)
} }
} }
// @bug This needs to be thread safe
void log(const char* format, LogDataArray data, const char* file, const char* function, int32 line) void log(const char* format, LogDataArray data, const char* file, const char* function, int32 line)
{ {
if (!_log_memory) { if (!_log_memory) {
@ -236,6 +241,12 @@ void log(const char* format, LogDataArray data, const char* file, const char* fu
case LOG_DATA_BYTE: { case LOG_DATA_BYTE: {
sprintf_fast_iter(msg->message, temp_format, (int32) *((byte *) data.data[i].value)); sprintf_fast_iter(msg->message, temp_format, (int32) *((byte *) data.data[i].value));
} break; } break;
case LOG_DATA_INT16: {
sprintf_fast_iter(msg->message, temp_format, (int32) *((int16 *) data.data[i].value));
} break;
case LOG_DATA_UINT16: {
sprintf_fast_iter(msg->message, temp_format, (uint32) *((uint16 *) data.data[i].value));
} break;
case LOG_DATA_INT32: { case LOG_DATA_INT32: {
sprintf_fast_iter(msg->message, temp_format, *((int32 *) data.data[i].value)); sprintf_fast_iter(msg->message, temp_format, *((int32 *) data.data[i].value));
} break; } break;

View File

@ -13,7 +13,6 @@
#include "../utils/TimeUtils.h" #include "../utils/TimeUtils.h"
#include "../thread/Spinlock.cpp" #include "../thread/Spinlock.cpp"
#include "../thread/Atomic.h" #include "../thread/Atomic.h"
#include "../system/Allocator.h"
#include "../hash/GeneralHash.h" #include "../hash/GeneralHash.h"
#include "../architecture/Intrinsics.h" #include "../architecture/Intrinsics.h"
#include "../compiler/CompilerUtils.h" #include "../compiler/CompilerUtils.h"
@ -24,6 +23,7 @@
enum TimingStats { enum TimingStats {
PROFILE_TEMP, PROFILE_TEMP,
PROFILE_MEMORY_ALLOC,
PROFILE_FILE_UTILS, PROFILE_FILE_UTILS,
PROFILE_BUFFER_ALLOC, PROFILE_BUFFER_ALLOC,
PROFILE_CHUNK_ALLOC, PROFILE_CHUNK_ALLOC,

View File

@ -16,6 +16,8 @@
DEBUG_COUNTER_DRIVE_READ, DEBUG_COUNTER_DRIVE_READ,
DEBUG_COUNTER_DRIVE_WRITE, DEBUG_COUNTER_DRIVE_WRITE,
DEBUG_COUNTER_THREAD,
DEBUG_COUNTER_GPU_VERTEX_UPLOAD, DEBUG_COUNTER_GPU_VERTEX_UPLOAD,
DEBUG_COUNTER_GPU_UNIFORM_UPLOAD, DEBUG_COUNTER_GPU_UNIFORM_UPLOAD,
DEBUG_COUNTER_GPU_DRAW_CALLS, DEBUG_COUNTER_GPU_DRAW_CALLS,
@ -50,6 +52,16 @@ void log_increment(int32 id, int64 by = 1) noexcept
atomic_add_acquire(&_stats_counter[id], by); atomic_add_acquire(&_stats_counter[id], by);
} }
inline
void log_decrement(int32 id, int64 by = 1) noexcept
{
if (!_stats_counter) {
return;
}
atomic_sub_acquire(&_stats_counter[id], by);
}
inline inline
void log_counter(int32 id, int64 value) noexcept void log_counter(int32 id, int64 value) noexcept
{ {
@ -63,11 +75,13 @@ void log_counter(int32 id, int64 value) noexcept
#if (!DEBUG && !INTERNAL) || RELEASE #if (!DEBUG && !INTERNAL) || RELEASE
#define LOG_INCREMENT(a) ((void) 0) #define LOG_INCREMENT(a) ((void) 0)
#define LOG_INCREMENT_BY(a, b) ((void) 0) #define LOG_INCREMENT_BY(a, b) ((void) 0)
#define LOG_DECREMENT(a) ((void) 0)
#define LOG_COUNTER(a, b) ((void) 0) #define LOG_COUNTER(a, b) ((void) 0)
#define RESET_COUNTER(a) ((void) 0) #define RESET_COUNTER(a) ((void) 0)
#else #else
#define LOG_INCREMENT(a) log_increment((a), 1) #define LOG_INCREMENT(a) log_increment((a), 1)
#define LOG_INCREMENT_BY(a, b) log_increment((a), (b)) #define LOG_INCREMENT_BY(a, b) log_increment((a), (b))
#define LOG_DECREMENT(a) log_decrement((a), 1)
#define LOG_COUNTER(a, b) log_counter((a), (b)) #define LOG_COUNTER(a, b) log_counter((a), (b))
#define RESET_COUNTER(a) reset_counter((a)) #define RESET_COUNTER(a) reset_counter((a))
#endif #endif

View File

@ -111,8 +111,8 @@ f32 evaluator_evaluate_function(const char* name, const char* args);
// Shunting-yard algorithm to evaluate the expression // Shunting-yard algorithm to evaluate the expression
f32 evaluator_evaluate_expression(const char* expr) { f32 evaluator_evaluate_expression(const char* expr) {
EvaluatorOperatorStack operators = { .top = -1 }; EvaluatorOperatorStack operators = { .items = {}, .top = -1 };
EvaluatorValueStack values = { .top = -1 }; EvaluatorValueStack values = { .items = {}, .top = -1 };
const char* ptr = expr; const char* ptr = expr;
while (*ptr) { while (*ptr) {

View File

@ -66,8 +66,6 @@ void chunk_alloc(ChunkMemory* buf, uint32 count, uint32 chunk_size, int32 alignm
buf->free = (uint64 *) ROUND_TO_NEAREST((uintptr_t) (buf->memory + count * chunk_size), alignment); buf->free = (uint64 *) ROUND_TO_NEAREST((uintptr_t) (buf->memory + count * chunk_size), alignment);
memset(buf->memory, 0, buf->size); memset(buf->memory, 0, buf->size);
LOG_1("Allocated ChunkMemory: %n B", {{LOG_DATA_UINT64, &buf->size}});
} }
inline inline
@ -110,8 +108,7 @@ void chunk_init(ChunkMemory* buf, byte* data, uint32 count, uint32 chunk_size, i
+ sizeof(uint64) * CEIL_DIV(count, alignment) // free + sizeof(uint64) * CEIL_DIV(count, alignment) // free
+ alignment * 2; // overhead for alignment + alignment * 2; // overhead for alignment
// @bug what if an alignment is defined? buf->memory = (byte *) ROUND_TO_NEAREST((uintptr_t) data, alignment);
buf->memory = data;
buf->count = count; buf->count = count;
buf->size = size; buf->size = size;
@ -307,7 +304,7 @@ int32 chunk_reserve(ChunkMemory* buf, uint32 elements = 1) noexcept
} }
if (free_element < 0) { if (free_element < 0) {
ASSERT_SIMPLE(false); LOG_3("No free chunk memory index found");
return -1; return -1;
} }

View File

@ -60,8 +60,6 @@ void ring_alloc(RingMemory* ring, uint64 size, uint32 alignment = 64)
ring->alignment = alignment; ring->alignment = alignment;
memset(ring->memory, 0, ring->size); memset(ring->memory, 0, ring->size);
LOG_1("Allocated RingMemory: %n B", {{LOG_DATA_UINT64, &ring->size}});
} }
inline inline

View File

@ -17,6 +17,7 @@
#include "../../utils/TestUtils.h" #include "../../utils/TestUtils.h"
#include "../../log/DebugMemory.h" #include "../../log/DebugMemory.h"
#include "../../log/Stats.h" #include "../../log/Stats.h"
#include "../../log/Log.h"
// @todo Currently alignment only effects the starting position, but it should also effect the ending/size // @todo Currently alignment only effects the starting position, but it should also effect the ending/size
@ -24,11 +25,16 @@
// does this have a negative impact on caching? // does this have a negative impact on caching?
// Our Memory doesn't start at the cache line beginning but at least offset by sizeof(size_t) // Our Memory doesn't start at the cache line beginning but at least offset by sizeof(size_t)
static int32 _page_size = 0;
inline inline
void* platform_alloc(size_t size) void* platform_alloc(size_t size)
{ {
ssize_t page_size = sysconf(_SC_PAGESIZE); if (!_page_size) {
size = (size + sizeof(size_t) + page_size - 1) & ~(page_size - 1); _page_size = (int32) sysconf(_SC_PAGESIZE);
}
size = ROUND_TO_NEAREST(size + sizeof(size_t), _page_size);
void* ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); void* ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
ASSERT_SIMPLE(ptr != MAP_FAILED); ASSERT_SIMPLE(ptr != MAP_FAILED);
@ -37,6 +43,7 @@ void* platform_alloc(size_t size)
DEBUG_MEMORY_INIT((uintptr_t) ptr, size); DEBUG_MEMORY_INIT((uintptr_t) ptr, size);
LOG_INCREMENT_BY(DEBUG_COUNTER_MEM_ALLOC, size); LOG_INCREMENT_BY(DEBUG_COUNTER_MEM_ALLOC, size);
LOG_3("Allocated %n B", {{LOG_DATA_UINT64, &size}});
return (void *) ((uintptr_t) ptr + sizeof(size_t)); return (void *) ((uintptr_t) ptr + sizeof(size_t));
} }
@ -44,13 +51,12 @@ void* platform_alloc(size_t size)
inline inline
void* platform_alloc_aligned(size_t size, int32 alignment) void* platform_alloc_aligned(size_t size, int32 alignment)
{ {
ssize_t page_size = sysconf(_SC_PAGESIZE); if (!_page_size) {
if (alignment < page_size) { _page_size = (int32) sysconf(_SC_PAGESIZE);
alignment = page_size;
} }
size = ROUND_TO_NEAREST(size, alignment); size = ROUND_TO_NEAREST(size + sizeof(void *) + sizeof(size_t) + alignment - 1, alignment);
size += alignment + sizeof(void *) + sizeof(size_t); size = ROUND_TO_NEAREST(size, _page_size);
void* ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); void* ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
ASSERT_SIMPLE(ptr != MAP_FAILED); ASSERT_SIMPLE(ptr != MAP_FAILED);
@ -60,13 +66,14 @@ void* platform_alloc_aligned(size_t size, int32 alignment)
// However, when freeing the pointer later on we need the actual start of the memory area, not the manually offset one. // However, when freeing the pointer later on we need the actual start of the memory area, not the manually offset one.
// We do the same with the size, which is required when freeing // We do the same with the size, which is required when freeing
uintptr_t raw_address = (uintptr_t) ptr + sizeof(void *) + sizeof(size_t); uintptr_t raw_address = (uintptr_t) ptr + sizeof(void *) + sizeof(size_t);
void* aligned_ptr = (void *) ((raw_address + alignment - 1) & ~(alignment - 1)); void* aligned_ptr = (void *) ROUND_TO_NEAREST(raw_address, alignment);
*((void **) ((uintptr_t) aligned_ptr - sizeof(void *) - sizeof(size_t))) = ptr; *((void **) ((uintptr_t) aligned_ptr - sizeof(void *) - sizeof(size_t))) = ptr;
*((size_t *) ((uintptr_t) aligned_ptr - sizeof(size_t))) = size; *((size_t *) ((uintptr_t) aligned_ptr - sizeof(size_t))) = size;
DEBUG_MEMORY_INIT((uintptr_t) aligned_ptr, size); DEBUG_MEMORY_INIT((uintptr_t) aligned_ptr, size);
LOG_INCREMENT_BY(DEBUG_COUNTER_MEM_ALLOC, size); LOG_INCREMENT_BY(DEBUG_COUNTER_MEM_ALLOC, size);
LOG_3("Aligned allocated %n B", {{LOG_DATA_UINT64, &size}});
return aligned_ptr; return aligned_ptr;
} }
@ -92,11 +99,14 @@ void platform_aligned_free(void** aligned_ptr) {
inline inline
void* platform_shared_alloc(int32* fd, const char* name, size_t size) void* platform_shared_alloc(int32* fd, const char* name, size_t size)
{ {
if (!_page_size) {
_page_size = (int32) sysconf(_SC_PAGESIZE);
}
*fd = shm_open(name, O_CREAT | O_RDWR, 0666); *fd = shm_open(name, O_CREAT | O_RDWR, 0666);
ASSERT_SIMPLE(*fd != -1); ASSERT_SIMPLE(*fd != -1);
ssize_t page_size = sysconf(_SC_PAGESIZE); size = ROUND_TO_NEAREST(size + sizeof(size_t), _page_size);
size = (size + sizeof(size_t) + page_size - 1) & ~(page_size - 1);
ftruncate(*fd, size); ftruncate(*fd, size);
@ -107,6 +117,7 @@ void* platform_shared_alloc(int32* fd, const char* name, size_t size)
DEBUG_MEMORY_INIT((uintptr_t) shm_ptr, size); DEBUG_MEMORY_INIT((uintptr_t) shm_ptr, size);
LOG_INCREMENT_BY(DEBUG_COUNTER_MEM_ALLOC, size); LOG_INCREMENT_BY(DEBUG_COUNTER_MEM_ALLOC, size);
LOG_3("Shared allocated %n B", {{LOG_DATA_UINT64, &size}});
return (void *) ((uintptr_t) shm_ptr + sizeof(size_t)); return (void *) ((uintptr_t) shm_ptr + sizeof(size_t));
} }
@ -117,11 +128,11 @@ void* platform_shared_open(int32* fd, const char* name, size_t size)
*fd = shm_open(name, O_RDWR, 0666); *fd = shm_open(name, O_RDWR, 0666);
ASSERT_SIMPLE(*fd != -1); ASSERT_SIMPLE(*fd != -1);
ssize_t page_size = sysconf(_SC_PAGESIZE); size = ROUND_TO_NEAREST(size + sizeof(size_t), _page_size);
size = (size + sizeof(size_t) + page_size - 1) & ~(page_size - 1);
void* shm_ptr = mmap(NULL, size, PROT_READ, MAP_SHARED, *fd, 0); void* shm_ptr = mmap(NULL, size, PROT_READ, MAP_SHARED, *fd, 0);
ASSERT_SIMPLE(shm_ptr); ASSERT_SIMPLE(shm_ptr);
LOG_3("Shared opened %n B", {{LOG_DATA_UINT64, &size}});
*((size_t *) shm_ptr) = size; *((size_t *) shm_ptr) = size;

View File

@ -39,9 +39,17 @@ int32 coms_pthread_create(coms_pthread_t* thread, void*, ThreadJobFunc start_rou
return 1; return 1;
} }
const uint64 stack_size = 1 * MEGABYTE;
thread->stack = platform_alloc_aligned(stack_size, 64);
int32 flags = CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD | CLONE_SYSVSEM; int32 flags = CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD | CLONE_SYSVSEM;
*thread = clone((int32 (*)(void*))start_routine, NULL, flags, arg);
if (*thread == -1) { // The + stack_size is required since the stack is used "downwards"
thread->h = clone((int32 (*)(void*))start_routine, (void *) ((uintptr_t) thread->stack + stack_size), flags, arg);
if (thread->h == -1) {
LOG_1("Thread creation faild with error %d", {{LOG_DATA_INT32, &errno}});
return 1; return 1;
} }
@ -50,9 +58,13 @@ int32 coms_pthread_create(coms_pthread_t* thread, void*, ThreadJobFunc start_rou
FORCE_INLINE FORCE_INLINE
int32 coms_pthread_join(coms_pthread_t thread, void** retval) { int32 coms_pthread_join(coms_pthread_t thread, void** retval) {
return syscall(SYS_waitid, P_PID, thread, retval, WEXITED, NULL) == -1 int32 res = syscall(SYS_waitid, P_PID, thread, retval, WEXITED, NULL) == -1
? 1 ? 1
: 0; : 0;
platform_aligned_free((void **) &thread.stack);
return res;
} }
FORCE_INLINE FORCE_INLINE
@ -126,7 +138,7 @@ int32 mutex_condimedwait(mutex_cond* cond, mutex* mutex, const struct timespec*)
return 0; return 0;
} }
inline FORCE_INLINE
int32 coms_pthread_cond_wait(mutex_cond* cond, mutex* mutex) { int32 coms_pthread_cond_wait(mutex_cond* cond, mutex* mutex) {
return mutex_condimedwait(cond, mutex, NULL); return mutex_condimedwait(cond, mutex, NULL);
} }

View File

@ -34,6 +34,9 @@ struct coms_pthread_rwlock_t {
bool exclusive; bool exclusive;
}; };
typedef int coms_pthread_t; struct coms_pthread_t {
int h;
void* stack;
};
#endif #endif

View File

@ -15,15 +15,26 @@
#include "../../utils/TestUtils.h" #include "../../utils/TestUtils.h"
#include "../../log/DebugMemory.h" #include "../../log/DebugMemory.h"
#include "../../log/Stats.h" #include "../../log/Stats.h"
#include "../../log/Log.h"
// @todo Currently alignment only effects the starting position, but it should also effect the ending/size // @todo Currently alignment only effects the starting position, but it should also effect the ending/size
static int32 _page_size = 0;
inline inline
void* platform_alloc(size_t size) void* platform_alloc(size_t size)
{ {
if (!_page_size) {
// @todo Fix and get system page size
_page_size = 1;
}
size = ROUND_TO_NEAREST(size, _page_size);
void* ptr = VirtualAlloc(NULL, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); void* ptr = VirtualAlloc(NULL, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
DEBUG_MEMORY_INIT((uintptr_t) ptr, size); DEBUG_MEMORY_INIT((uintptr_t) ptr, size);
LOG_INCREMENT_BY(DEBUG_COUNTER_MEM_ALLOC, size); LOG_INCREMENT_BY(DEBUG_COUNTER_MEM_ALLOC, size);
LOG_3("Allocated %n B", {{LOG_DATA_UINT64, &size}});
return ptr; return ptr;
} }
@ -31,7 +42,12 @@ void* platform_alloc(size_t size)
inline inline
void* platform_alloc_aligned(size_t size, int32 alignment) void* platform_alloc_aligned(size_t size, int32 alignment)
{ {
size = ROUND_TO_NEAREST(size, alignment); if (!_page_size) {
_page_size = 1;
}
size = ROUND_TO_NEAREST(size + sizeof(void*) + alignment - 1, alignment);
size = ROUND_TO_NEAREST(size, _page_size);
void* ptr = VirtualAlloc(NULL, size + alignment + sizeof(void*), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); void* ptr = VirtualAlloc(NULL, size + alignment + sizeof(void*), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
ASSERT_SIMPLE(ptr); ASSERT_SIMPLE(ptr);
@ -39,11 +55,12 @@ void* platform_alloc_aligned(size_t size, int32 alignment)
// We want an aligned memory area but mmap doesn't really support that. // We want an aligned memory area but mmap doesn't really support that.
// That's why we have to manually offset our memory area. // That's why we have to manually offset our memory area.
// However, when freeing the pointer later on we need the actual start of the memory area, not the manually offset one. // However, when freeing the pointer later on we need the actual start of the memory area, not the manually offset one.
void* aligned_ptr = (void *) (((uintptr_t) ptr + alignment + sizeof(void*) - 1) & ~(alignment - 1)); void* aligned_ptr = (void *) ROUND_TO_NEAREST((uintptr_t) ptr + sizeof(void*), alignment);
((void**) aligned_ptr)[-1] = ptr; ((void**) aligned_ptr)[-1] = ptr;
DEBUG_MEMORY_INIT((uintptr_t) aligned_ptr, size); DEBUG_MEMORY_INIT((uintptr_t) aligned_ptr, size);
LOG_INCREMENT_BY(DEBUG_COUNTER_MEM_ALLOC, size); LOG_INCREMENT_BY(DEBUG_COUNTER_MEM_ALLOC, size);
LOG_3("Aligned allocated %n B", {{LOG_DATA_UINT64, &size}});
return aligned_ptr; return aligned_ptr;
} }
@ -67,6 +84,12 @@ void platform_aligned_free(void** aligned_ptr) {
inline inline
void* platform_shared_alloc(HANDLE* fd, const char* name, size_t size) void* platform_shared_alloc(HANDLE* fd, const char* name, size_t size)
{ {
if (!_page_size) {
_page_size = 1;
}
size = ROUND_TO_NEAREST(size, _page_size);
*fd = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, (DWORD) size, name); *fd = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, (DWORD) size, name);
ASSERT_SIMPLE(*fd); ASSERT_SIMPLE(*fd);
@ -75,6 +98,7 @@ void* platform_shared_alloc(HANDLE* fd, const char* name, size_t size)
DEBUG_MEMORY_INIT((uintptr_t) shm_ptr, size); DEBUG_MEMORY_INIT((uintptr_t) shm_ptr, size);
LOG_INCREMENT_BY(DEBUG_COUNTER_MEM_ALLOC, size); LOG_INCREMENT_BY(DEBUG_COUNTER_MEM_ALLOC, size);
LOG_3("Shared allocated %n B", {{LOG_DATA_UINT64, &size}});
return shm_ptr; return shm_ptr;
} }
@ -87,6 +111,7 @@ void* platform_shared_open(HANDLE* fd, const char* name, size_t size)
void* shm_ptr = MapViewOfFile(*fd, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, (DWORD) size); void* shm_ptr = MapViewOfFile(*fd, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, (DWORD) size);
ASSERT_SIMPLE(shm_ptr); ASSERT_SIMPLE(shm_ptr);
LOG_3("Shared opened %n B", {{LOG_DATA_UINT64, &size}});
return shm_ptr; return shm_ptr;
} }

View File

@ -18,7 +18,12 @@ typedef CRITICAL_SECTION mutex;
typedef void mutexattr_t; typedef void mutexattr_t;
typedef void coms_pthread_condattr_t; typedef void coms_pthread_condattr_t;
typedef void coms_pthread_rwlockattr_t; typedef void coms_pthread_rwlockattr_t;
typedef HANDLE coms_pthread_t;
struct coms_pthread_t {
HANDLE h;
void* stack;
};
typedef CONDITION_VARIABLE mutex_cond; typedef CONDITION_VARIABLE mutex_cond;
// Thread local variable Already exists in c++11 // Thread local variable Already exists in c++11

View File

@ -153,7 +153,7 @@ void hashmap_alloc(HashMap* hm, int32 count, int32 element_size, int32 alignment
); );
hm->table = (uint16 *) data; hm->table = (uint16 *) data;
chunk_init(&hm->buf, data + sizeof(uint16) * count, count, element_size, 8); chunk_init(&hm->buf, data + sizeof(uint16) * count, count, element_size, alignment);
} }
inline inline
@ -161,21 +161,22 @@ void hashmap_alloc(HashMapRef* hmr, int32 count, int32 data_element_size, int32
{ {
int32 element_size = sizeof(HashEntryInt32Int32); int32 element_size = sizeof(HashEntryInt32Int32);
LOG_1("Allocate HashMap for %n elements with %n B per element", {{LOG_DATA_INT32, &count}, {LOG_DATA_INT32, &element_size}}); LOG_1("Allocate HashMap for %n elements with %n B per element", {{LOG_DATA_INT32, &count}, {LOG_DATA_INT32, &element_size}});
byte* data = (byte *) platform_alloc( byte* data = (byte *) platform_alloc_aligned(
count * (sizeof(uint16) + element_size) count * (sizeof(uint16) + element_size)
+ CEIL_DIV(count, alignment) * sizeof(hmr->hm.buf.free) + CEIL_DIV(count, alignment) * sizeof(hmr->hm.buf.free)
+ count * data_element_size + count * data_element_size,
alignment
); );
hmr->hm.table = (uint16 *) data; hmr->hm.table = (uint16 *) data;
chunk_init(&hmr->hm.buf, data + sizeof(uint16) * count, count, element_size, 8); chunk_init(&hmr->hm.buf, data + sizeof(uint16) * count, count, element_size, alignment);
chunk_init(&hmr->data, data + hmr->hm.buf.size, count, data_element_size); chunk_init(&hmr->data, data + hmr->hm.buf.size, count, data_element_size, alignment);
} }
inline inline
void hashmap_free(HashMap* hm) void hashmap_free(HashMap* hm)
{ {
platform_free((void **) &hm->table); platform_aligned_free((void **) &hm->table);
hm->table = NULL; hm->table = NULL;
hm->buf.size = 0; hm->buf.size = 0;
@ -194,7 +195,7 @@ void hashmap_create(HashMap* hm, int32 count, int32 element_size, RingMemory* ri
); );
hm->table = (uint16 *) data; hm->table = (uint16 *) data;
chunk_init(&hm->buf, data + sizeof(uint16) * count, count, element_size, 8); chunk_init(&hm->buf, data + sizeof(uint16) * count, count, element_size, alignment);
} }
// WARNING: element_size = element size + remaining HashEntry data size // WARNING: element_size = element size + remaining HashEntry data size
@ -209,16 +210,16 @@ void hashmap_create(HashMap* hm, int32 count, int32 element_size, BufferMemory*
); );
hm->table = (uint16 *) data; hm->table = (uint16 *) data;
chunk_init(&hm->buf, data + sizeof(uint16) * count, count, element_size, 8); chunk_init(&hm->buf, data + sizeof(uint16) * count, count, element_size, alignment);
} }
// WARNING: element_size = element size + remaining HashEntry data size // WARNING: element_size = element size + remaining HashEntry data size
inline inline
void hashmap_create(HashMap* hm, int32 count, int32 element_size, byte* buf) noexcept void hashmap_create(HashMap* hm, int32 count, int32 element_size, byte* buf, int32 alignment = 64) noexcept
{ {
LOG_1("Create HashMap for %n elements with %n B per element", {{LOG_DATA_INT32, &count}, {LOG_DATA_INT32, &element_size}}); LOG_1("Create HashMap for %n elements with %n B per element", {{LOG_DATA_INT32, &count}, {LOG_DATA_INT32, &element_size}});
hm->table = (uint16 *) buf; hm->table = (uint16 *) buf;
chunk_init(&hm->buf, buf + sizeof(uint16) * count, count, element_size, 8); chunk_init(&hm->buf, buf + sizeof(uint16) * count, count, element_size, alignment);
} }
inline inline
@ -256,6 +257,8 @@ void hashmap_insert(HashMap* hm, const char* key, int32 value) noexcept {
int32 element = chunk_reserve(&hm->buf, 1); int32 element = chunk_reserve(&hm->buf, 1);
HashEntryInt32* entry = (HashEntryInt32 *) chunk_get_element(&hm->buf, element, true); HashEntryInt32* entry = (HashEntryInt32 *) chunk_get_element(&hm->buf, element, true);
ASSERT_SIMPLE(((uintptr_t) entry) % 32 == 0);
// Ensure key length // Ensure key length
str_move_to_pos(&key, -HASH_MAP_MAX_KEY_LENGTH); str_move_to_pos(&key, -HASH_MAP_MAX_KEY_LENGTH);
str_copy_short(entry->key, key, HASH_MAP_MAX_KEY_LENGTH); str_copy_short(entry->key, key, HASH_MAP_MAX_KEY_LENGTH);
@ -908,7 +911,11 @@ int64 hashmap_dump(const HashMap* hm, byte* data, [[maybe_unused]] int32 steps =
} else if (value_size == 8) { } else if (value_size == 8) {
*((int64 *) data) = SWAP_ENDIAN_LITTLE(((HashEntryInt64 *) entry)->value); *((int64 *) data) = SWAP_ENDIAN_LITTLE(((HashEntryInt64 *) entry)->value);
} else { } else {
memcpy(data, entry->value, value_size); if (entry->value) {
memcpy(data, entry->value, value_size);
} else {
memset(data, 0, value_size);
}
} }
data += value_size; data += value_size;
} else { } else {

View File

@ -171,22 +171,23 @@ PerfectHashMap* perfect_hashmap_prepare(PerfectHashMap* hm, const char* keys, in
return NULL; return NULL;
} }
void perfect_hashmap_alloc(PerfectHashMap* hm, int32 count, int32 element_size) void perfect_hashmap_alloc(PerfectHashMap* hm, int32 count, int32 element_size, int32 alignment = 64)
{ {
LOG_1("Allocating PerfectHashMap for %n elements with %n B per element", {{LOG_DATA_INT32, &count}, {LOG_DATA_INT32, &element_size}}); LOG_1("Allocating PerfectHashMap for %n elements with %n B per element", {{LOG_DATA_INT32, &count}, {LOG_DATA_INT32, &element_size}});
hm->map_count = count; hm->map_count = count;
hm->entry_size = element_size; hm->entry_size = element_size;
hm->hash_entries = (byte *) platform_alloc(count * element_size); hm->hash_entries = (byte *) platform_alloc_aligned(count * element_size, alignment);
} }
void perfect_hashmap_alloc(PerfectHashMapRef* hmr, int32 count, int32 total_data_size) void perfect_hashmap_alloc(PerfectHashMapRef* hmr, int32 count, int32 total_data_size, int32 alignment = 64)
{ {
hmr->hm.entry_size = sizeof(PerfectHashEntryInt32Int32); hmr->hm.entry_size = sizeof(PerfectHashEntryInt32Int32);
LOG_1("Allocating PerfectHashMap for %n elements with %n B per element", {{LOG_DATA_INT32, &count}, {LOG_DATA_INT32, &hmr->hm.entry_size}}); LOG_1("Allocating PerfectHashMap for %n elements with %n B per element", {{LOG_DATA_INT32, &count}, {LOG_DATA_INT32, &hmr->hm.entry_size}});
hmr->hm.map_count = count; hmr->hm.map_count = count;
hmr->hm.hash_entries = (byte *) platform_alloc( hmr->hm.hash_entries = (byte *) platform_alloc_aligned(
count * hmr->hm.entry_size count * hmr->hm.entry_size
+ total_data_size + total_data_size,
alignment
); );
hmr->data_pos = 0; hmr->data_pos = 0;
@ -194,6 +195,14 @@ void perfect_hashmap_alloc(PerfectHashMapRef* hmr, int32 count, int32 total_data
hmr->data = hmr->hm.hash_entries + count * hmr->hm.entry_size; hmr->data = hmr->hm.hash_entries + count * hmr->hm.entry_size;
} }
void perfect_hashmap_free(PerfectHashMap* hm) {
platform_aligned_free((void **) &hm->hash_entries);
}
void perfect_hashmap_free(PerfectHashMapRef* hmr) {
platform_aligned_free((void **) &hmr->hm.hash_entries);
}
// WARNING: element_size = element size + remaining HashEntry data size // WARNING: element_size = element size + remaining HashEntry data size
void perfect_hashmap_create(PerfectHashMap* hm, int32 count, int32 element_size, BufferMemory* buf) void perfect_hashmap_create(PerfectHashMap* hm, int32 count, int32 element_size, BufferMemory* buf)
{ {
@ -203,7 +212,7 @@ void perfect_hashmap_create(PerfectHashMap* hm, int32 count, int32 element_size,
hm->hash_entries = buffer_get_memory( hm->hash_entries = buffer_get_memory(
buf, buf,
count * element_size, count * element_size,
0, true 64, true
); );
} }

View File

@ -9,6 +9,7 @@
#ifndef COMS_SYSTEM_INFO_C #ifndef COMS_SYSTEM_INFO_C
#define COMS_SYSTEM_INFO_C #define COMS_SYSTEM_INFO_C
#include <inttypes.h>
#if _WIN32 #if _WIN32
#include "../platform/win32/SystemInfo.cpp" #include "../platform/win32/SystemInfo.cpp"
#elif __linux__ #elif __linux__
@ -47,7 +48,7 @@ void system_info_render(char* buf, const SystemInfo* info) {
"L3: Size %u Line %u\n" "L3: Size %u Line %u\n"
"L4: Size %u Line %u\n" "L4: Size %u Line %u\n"
"\n" "\n"
"Features: %lld\n" "Features: %" PRId64 "\n"
"\n" "\n"
"GPU:\n" "GPU:\n"
"==============\n" "==============\n"
@ -78,7 +79,7 @@ void system_info_render(char* buf, const SystemInfo* info) {
info->cpu.cache[1].size, (uint32) info->cpu.cache[1].line_size, info->cpu.cache[1].size, (uint32) info->cpu.cache[1].line_size,
info->cpu.cache[2].size, (uint32) info->cpu.cache[2].line_size, info->cpu.cache[2].size, (uint32) info->cpu.cache[2].line_size,
info->cpu.cache[3].size, (uint32) info->cpu.cache[3].line_size, info->cpu.cache[3].size, (uint32) info->cpu.cache[3].line_size,
(long long) info->cpu.features, info->cpu.features,
info->gpu[0].name, info->gpu[0].vram, info->gpu[0].name, info->gpu[0].vram,
info->gpu_count < 2 ? "" : info->gpu[1].name, info->gpu_count < 2 ? 0 : info->gpu[1].vram, info->gpu_count < 2 ? "" : info->gpu[1].name, info->gpu_count < 2 ? 0 : info->gpu[1].vram,
info->gpu_count < 3 ? "" : info->gpu[2].name, info->gpu_count < 3 ? 0 : info->gpu[2].vram, info->gpu_count < 3 ? "" : info->gpu[2].name, info->gpu_count < 3 ? 0 : info->gpu[2].vram,

69
test.sh Executable file
View File

@ -0,0 +1,69 @@
#!/bin/bash
start_time=$(date +%s.%N)
clear
EXE_NAME="tests"
DESTINATION_DIR="../build/tests"
mkdir -p "../build"
mkdir -p "$DESTINATION_DIR"
# Clean up previous build files
rm -f "$DESTINATION_DIR"/*.pdb 2>/dev/null
rm -f "$DESTINATION_DIR"/*.idb 2>/dev/null
# Parse command-line arguments
BUILD_TYPE="DEBUG"
BUILD_FLAGS="-g -O0 -Wall -Werror -Wextra -DDEBUG -DDEBUG -ggdb -Werror"
DEBUG_DATA="-g"
if [ "$1" = "-r" ]; then
BUILD_TYPE="RELEASE"
BUILD_FLAGS="-O3 -DNDEBUG"
DEBUG_DATA=""
elif [ "$1" = "-d" ]; then
BUILD_TYPE="DEBUG"
BUILD_FLAGS="-g -O0 -Wall -Werror -Wextra -DDEBUG -ggdb -Werror"
DEBUG_DATA="-g"
fi
source ./check_cpu_features.sh
# Detect CPU features
CPU_FEATURES=$(check_cpu_features)
# Compile the test program
cd "$DESTINATION_DIR" || exit 1
g++ $BUILD_FLAGS -std=c++23 -m64 \
-DUNICODE -D_UNICODE -D__linux__ \
-Wno-unused-result \
-fsanitize=address \
-fsanitize=undefined \
${CPU_FEATURES} \
-o MainTest \
../../cOMS/tests/MainTest.cpp \
$DEBUG_DATA
# Check if the compilation was successful
if [ $? -ne 0 ]; then
echo "Compilation failed for MainTest.cpp"
exit 1
fi
# Run the compiled executable
./MainTest
# Calculate runtime
end_time=$(date +%s.%N)
elapsed_time=$(echo "$end_time - $start_time" | bc)
hours=$(printf "%.0f" $(echo "$elapsed_time / 3600" | bc))
remaining=$(echo "$elapsed_time % 3600" | bc)
minutes=$(printf "%.0f" $(echo "$remaining / 60" | bc))
seconds=$(echo "$remaining % 60" | bc)
# Format the output
printf "Test (incl. build) time %02d:%02d:%05.2f (%.2fs total)\n" \
$hours $minutes $seconds $elapsed_time

View File

@ -44,7 +44,7 @@ if "%1"=="-d" (
REM Compile each .cpp file into an executable REM Compile each .cpp file into an executable
cl ^ cl ^
%BUILD_FLAGS% /MT /nologo /Gm- /GR- /EHsc /W4 /wd4201 /wd4706 /wd4324 ^ %BUILD_FLAGS% /MT /nologo /Gm- /GR- /EHsc /W4 /wd4201 /wd4706 /wd4324 ^
/fp:precise /Zc:wchar_t /Zc:forScope /Zc:inline /std:c++20 ^ /fp:precise /Zc:wchar_t /Zc:forScope /Zc:inline /std:c++23 ^
/D WIN32 /D _WINDOWS /D _UNICODE /D UNICODE ^ /D WIN32 /D _WINDOWS /D _UNICODE /D UNICODE ^
/D _CRT_SECURE_NO_WARNINGS ^ /D _CRT_SECURE_NO_WARNINGS ^
/Fo"%DESTINATION_DIR%/" /Fe"%DESTINATION_DIR%\MainTest.exe" %DEBUG_DATA% ^ /Fo"%DESTINATION_DIR%/" /Fe"%DESTINATION_DIR%\MainTest.exe" %DEBUG_DATA% ^

View File

@ -1,16 +1,18 @@
#define UBER_TEST 1 #define UBER_TEST 1
#define PERFORMANCE_TEST 0
#include "math/EvaluatorTest.cpp" //#include "math/EvaluatorTest.cpp"
#include "memory/ChunkMemoryTest.cpp" #include "memory/ChunkMemoryTest.cpp"
#include "memory/RingMemoryTest.cpp" #include "memory/RingMemoryTest.cpp"
#include "stdlib/HashMapTest.cpp" #include "stdlib/HashMapTest.cpp"
#include "ui/UILayoutTest.cpp" //#include "ui/UILayoutTest.cpp"
#include "ui/UIThemeTest.cpp" //#include "ui/UIThemeTest.cpp"
#include "utils/BitUtilsTest.cpp" #include "utils/BitUtilsTest.cpp"
#include "utils/EndianUtilsTest.cpp" #include "utils/EndianUtilsTest.cpp"
#include "utils/StringUtilsTest.cpp" #include "utils/StringUtilsTest.cpp"
#include "utils/MathUtilsTest.cpp" #include "utils/MathUtilsTest.cpp"
#include "utils/UtilsTest.cpp" #include "utils/UtilsTest.cpp"
#include "utils/TimeUtilsTest.cpp"
#ifdef UBER_TEST #ifdef UBER_TEST
#ifdef main #ifdef main
@ -21,16 +23,18 @@
int main() { int main() {
TEST_HEADER(); TEST_HEADER();
MathEvaluatorTest(); //MathEvaluatorTest();
MemoryChunkMemoryTest(); MemoryChunkMemoryTest();
MemoryRingMemoryTest(); MemoryRingMemoryTest();
StdlibHashMapTest(); StdlibHashMapTest();
UIUILayoutTest(); //UIUILayoutTest();
UIUIThemeTest(); //UIUIThemeTest();
UtilsBitUtilsTest(); UtilsBitUtilsTest();
UtilsEndianUtilsTest();
UtilsStringUtilsTest(); UtilsStringUtilsTest();
UtilsMathUtilsTest(); UtilsMathUtilsTest();
UtilsUtilsTest(); UtilsUtilsTest();
UtilsTimeUtilsTest();
TEST_FOOTER(); TEST_FOOTER();

View File

@ -11,6 +11,7 @@
#include <stdio.h> #include <stdio.h>
#include <math.h> #include <math.h>
#include <inttypes.h>
#include "../architecture/Intrinsics.h" #include "../architecture/Intrinsics.h"
static char **_test_log; static char **_test_log;
@ -18,28 +19,31 @@ static int32_t _test_assert_count;
static int32_t _test_assert_error_count; static int32_t _test_assert_error_count;
static int32_t _test_count = -1; static int32_t _test_count = -1;
static int32_t _test_suit_count = 0;
static int32_t _test_global_assert_count = 0; static int32_t _test_global_assert_count = 0;
static int32_t _test_global_assert_error_count = 0; static int32_t _test_global_assert_error_count = 0;
static int32_t _test_global_count = 0; static int32_t _test_global_count = 0;
static int64_t _test_start; static int64_t _test_start;
static int64_t _test_total_start;
#define TEST_PROFILING_LOOPS 1000 #define TEST_PROFILING_LOOPS 1000
#define TEST_HEADER() \ #define TEST_HEADER() \
int64_t _test_total_start = test_start_time(); \ _test_total_start = test_start_time(); \
printf("\nStat Tests Assert(OK/NG) Time(ms) Details\n"); \ printf("\nStat Tests Assert(OK/NG) Time(ms) Details\n"); \
printf("========================================================================================================================\n") printf("========================================================================================================================\n")
#define TEST_FOOTER() \ #define TEST_FOOTER() \
printf("========================================================================================================================\n"); \ printf("========================================================================================================================\n"); \
printf( \ printf( \
"%s %5d (%5d/%5d) %8.0f\n\n", \ "%s %5d (%5d/%5d) %8.2f\n\n", \
_test_global_assert_count ? "[NG]" : "[OK]", \ _test_global_assert_count ? "[NG]" : "[OK]", \
_test_global_count, \ _test_global_count, \
_test_global_assert_count - _test_global_assert_error_count, \ _test_global_assert_count - _test_global_assert_error_count, \
_test_global_assert_count, \ _test_global_assert_count, \
test_duration_time(_test_total_start) / 1000000) test_duration_time(_test_total_start) / 1000000); \
printf("%d test suits\n\n", _test_suit_count)
#ifdef UBER_TEST #ifdef UBER_TEST
#define TEST_INIT_HEADER() (void)0 #define TEST_INIT_HEADER() (void)0
@ -70,11 +74,7 @@ int64_t test_start_time()
double test_duration_time(int64_t start) double test_duration_time(int64_t start)
{ {
LARGE_INTEGER frequency, end; return (double)(test_start_time() - start) / frequency.QuadPart;
QueryPerformanceFrequency(&frequency);
QueryPerformanceCounter(&end);
return (double)(end.QuadPart - start) * 1e9 / frequency.QuadPart;
} }
double test_measure_func_time_ns(void (*func)(volatile void *), volatile void *para) double test_measure_func_time_ns(void (*func)(volatile void *), volatile void *para)
@ -111,6 +111,7 @@ double test_measure_func_time_ns(void (*func)(volatile void *), volatile void *p
} while (0) } while (0)
#else #else
#include "../platform/linux/ExceptionHandler.h" #include "../platform/linux/ExceptionHandler.h"
#include <time.h>
void test_exception_handler(int signum) void test_exception_handler(int signum)
{ {
printf("Received signal: %d\n", signum); printf("Received signal: %d\n", signum);
@ -120,7 +121,7 @@ void test_exception_handler(int signum)
int64_t test_start_time() int64_t test_start_time()
{ {
struct timespec start, end; struct timespec start;
clock_gettime(CLOCK_MONOTONIC, &start); clock_gettime(CLOCK_MONOTONIC, &start);
return start.tv_sec * 1e9 + start.tv_nsec; return start.tv_sec * 1e9 + start.tv_nsec;
@ -128,14 +129,9 @@ int64_t test_start_time()
double test_duration_time(int64_t start) double test_duration_time(int64_t start)
{ {
LARGE_INTEGER frequency, end; return (double) (test_start_time() - start);
QueryPerformanceFrequency(&frequency);
QueryPerformanceCounter(&end);
return (double)(end.tv_sec * 1e9 + end.tv_nsec - start);
} }
#include <time.h>
double test_measure_func_time_ns(void (*func)(volatile void *), volatile void *para) double test_measure_func_time_ns(void (*func)(volatile void *), volatile void *para)
{ {
struct timespec start, end; struct timespec start, end;
@ -151,6 +147,7 @@ double test_measure_func_time_ns(void (*func)(volatile void *), volatile void *p
#define TEST_INIT(test_count) \ #define TEST_INIT(test_count) \
do \ do \
{ \ { \
++_test_suit_count; \
TEST_INIT_HEADER(); \ TEST_INIT_HEADER(); \
setvbuf(stdout, NULL, _IONBF, 0); \ setvbuf(stdout, NULL, _IONBF, 0); \
signal(SIGSEGV, test_exception_handler); \ signal(SIGSEGV, test_exception_handler); \
@ -176,7 +173,7 @@ double test_measure_func_time_ns(void (*func)(volatile void *), volatile void *p
if (_test_assert_error_count) \ if (_test_assert_error_count) \
{ \ { \
printf( \ printf( \
"[NG] %5d (%5d/%5d) %8.0f %s\n", \ "[NG] %5d (%5d/%5d) %8.2f %s\n", \
_test_count, _test_assert_count - _test_assert_error_count, _test_assert_count, test_duration_time(_test_start) / 1000000, __FILE__); \ _test_count, _test_assert_count - _test_assert_error_count, _test_assert_count, test_duration_time(_test_start) / 1000000, __FILE__); \
for (int i = 0; i < _test_assert_error_count; ++i) \ for (int i = 0; i < _test_assert_error_count; ++i) \
{ \ { \
@ -187,7 +184,7 @@ double test_measure_func_time_ns(void (*func)(volatile void *), volatile void *p
else \ else \
{ \ { \
printf( \ printf( \
"[OK] %5d (%5d/%5d) %8.0f %s\n", \ "[OK] %5d (%5d/%5d) %8.2f %s\n", \
_test_count, _test_assert_count - _test_assert_error_count, _test_assert_count, test_duration_time(_test_start) / 1000000, __FILE__); \ _test_count, _test_assert_count - _test_assert_error_count, _test_assert_count, test_duration_time(_test_start) / 1000000, __FILE__); \
} \ } \
fflush(stdout); \ fflush(stdout); \
@ -204,34 +201,62 @@ double test_measure_func_time_ns(void (*func)(volatile void *), volatile void *p
++_test_global_count; \ ++_test_global_count; \
func() func()
#define ASSERT_EQUALS(a, b) \ #define ASSERT_EQUALS(a, b) \
do \
{ \
++_test_assert_count; \
++_test_global_assert_count; \
if ((a) != (b)) \
{ \
++_test_global_assert_error_count; \
snprintf( \
_test_log[_test_assert_error_count++], 1024, \
"%4i: %" PRId64 " != %" PRId64, __LINE__, (int64)(a), (int64)(b)); \
} \
} while (0)
#define ASSERT_GREATER_THAN(a, b) \
do \ do \
{ \ { \
++_test_assert_count; \ ++_test_assert_count; \
++_test_global_assert_count; \ ++_test_global_assert_count; \
if ((a) != (b)) \ if ((a) <= (b)) \
{ \ { \
++_test_global_assert_error_count; \ ++_test_global_assert_error_count; \
snprintf( \ snprintf( \
_test_log[_test_assert_error_count++], 1024, \ _test_log[_test_assert_error_count++], 1024, \
"%4i: %lld != %lld", __LINE__, (int64)(a), (int64)(b)); \ "%4i: %.3f <= %.3f", __LINE__, (float)(a), (float)(b)); \
} \ } \
} while (0) } while (0)
#define ASSERT_NOT_EQUALS(a, b) \ #define ASSERT_LESSER_THAN(a, b) \
do \ do \
{ \ { \
++_test_assert_count; \ ++_test_assert_count; \
++_test_global_assert_count; \ ++_test_global_assert_count; \
if ((a) == (b)) \ if ((a) >= (b)) \
{ \ { \
++_test_global_assert_error_count; \ ++_test_global_assert_error_count; \
snprintf( \ snprintf( \
_test_log[_test_assert_error_count++], 1024, \ _test_log[_test_assert_error_count++], 1024, \
"%4i: %lld == %lld", __LINE__, (int64)(a), (int64)(b)); \ "%4i: %.3f >= %.3f", __LINE__, (float)(a), (float)(b)); \
} \ } \
} while (0) } while (0)
#define ASSERT_NOT_EQUALS(a, b) \
do \
{ \
++_test_assert_count; \
++_test_global_assert_count; \
if ((a) == (b)) \
{ \
++_test_global_assert_error_count; \
snprintf( \
_test_log[_test_assert_error_count++], 1024, \
"%4i: %" PRId64 " == %" PRId64, __LINE__, (int64)(a), (int64)(b)); \
} \
} while (0)
#define ASSERT_EQUALS_WITH_DELTA(a, b, delta) \ #define ASSERT_EQUALS_WITH_DELTA(a, b, delta) \
do \ do \
{ \ { \
@ -329,7 +354,7 @@ double test_measure_func_time_ns(void (*func)(volatile void *), volatile void *p
{ \ { \
++_test_global_assert_error_count; \ ++_test_global_assert_error_count; \
snprintf(_test_log[_test_global_assert_error_count], 1024, \ snprintf(_test_log[_test_global_assert_error_count], 1024, \
"%4i: mismatch at offset %lld", __LINE__, i); \ "%4i: mismatch at offset %" PRId64, __LINE__, i); \
break; \ break; \
} \ } \
} \ } \
@ -371,7 +396,7 @@ double test_measure_func_time_ns(void (*func)(volatile void *), volatile void *p
++_test_global_assert_error_count; \ ++_test_global_assert_error_count; \
snprintf( \ snprintf( \
_test_log[_test_assert_error_count++], 1024, \ _test_log[_test_assert_error_count++], 1024, \
"%4i: %.2f%% (%s: %llu cycles, %s: %llu cycles)", \ "%4i: %.2f%% (%s: %" PRIu64 " cycles, %s: %" PRIu64 " cycles)", \
__LINE__, percent_diff + 100.0f, #func1, (uint64_t)cycles_func1, #func2, (uint64_t)cycles_func2); \ __LINE__, percent_diff + 100.0f, #func1, (uint64_t)cycles_func1, #func2, (uint64_t)cycles_func2); \
} \ } \
ASSERT_TRUE((a && b) || a == b); \ ASSERT_TRUE((a && b) || a == b); \
@ -400,7 +425,7 @@ double test_measure_func_time_ns(void (*func)(volatile void *), volatile void *p
++_test_global_assert_error_count; \ ++_test_global_assert_error_count; \
snprintf( \ snprintf( \
_test_log[_test_assert_error_count++], 1024, \ _test_log[_test_assert_error_count++], 1024, \
"%4i: %.2f%% (%s: %llu cycles)", \ "%4i: %.2f%% (%s: %" PRIu64 " cycles)", \
__LINE__, percent_diff + 100.0f, #func, (uint64_t)cycles_func); \ __LINE__, percent_diff + 100.0f, #func, (uint64_t)cycles_func); \
} \ } \
} while (0) } while (0)

View File

@ -1,3 +1,4 @@
#include <string.h>
#include "../TestFramework.h" #include "../TestFramework.h"
#include "../../utils/EndianUtils.h" #include "../../utils/EndianUtils.h"
@ -85,7 +86,7 @@ static void test_endian_swap_uint64() {
static void test_endian_swap_int64() { static void test_endian_swap_int64() {
int64 val = 0x123456789ABCDEF0; int64 val = 0x123456789ABCDEF0;
int64 swapped = endian_swap(val); int64 swapped = endian_swap(val);
ASSERT_EQUALS(0xF0DEBC9A78563412, swapped); ASSERT_EQUALS((int64) 0xF0DEBC9A78563412, swapped);
} }
static void test_endian_swap_float() { static void test_endian_swap_float() {

View File

@ -136,6 +136,7 @@ static void test_pow_approx_f64() {
ASSERT_EQUALS_WITH_DELTA(pow_approx(10.0, 0.5), pow(10.0, 0.5), 0.001); ASSERT_EQUALS_WITH_DELTA(pow_approx(10.0, 0.5), pow(10.0, 0.5), 0.001);
} }
#if PERFORMANCE_TEST
// Performance tests for f32 (float) approximate functions // Performance tests for f32 (float) approximate functions
static void _sin_approx_f32(volatile void* val) { static void _sin_approx_f32(volatile void* val) {
f32* res = (f32*)val; f32* res = (f32*)val;
@ -555,6 +556,7 @@ static void test_pow_approx_performance_f64() {
COMPARE_FUNCTION_TEST_TIME(_pow_approx_f64, _pow_f64, 5.0); COMPARE_FUNCTION_TEST_TIME(_pow_approx_f64, _pow_f64, 5.0);
COMPARE_FUNCTION_TEST_CYCLE(_pow_approx_f64, _pow_f64, 5.0); COMPARE_FUNCTION_TEST_CYCLE(_pow_approx_f64, _pow_f64, 5.0);
} }
#endif
#ifdef UBER_TEST #ifdef UBER_TEST
#ifdef main #ifdef main
@ -592,31 +594,33 @@ int main() {
TEST_RUN(test_log_approx_f64); TEST_RUN(test_log_approx_f64);
TEST_RUN(test_pow_approx_f64); TEST_RUN(test_pow_approx_f64);
// Run performance tests for f32 functions #if PERFORMANCE_TEST
TEST_RUN(test_sin_approx_performance_f32); // Run performance tests for f32 functions
TEST_RUN(test_cos_approx_performance_f32); TEST_RUN(test_sin_approx_performance_f32);
TEST_RUN(test_tan_approx_performance_f32); TEST_RUN(test_cos_approx_performance_f32);
TEST_RUN(test_sqrt_approx_performance_f32); TEST_RUN(test_tan_approx_performance_f32);
TEST_RUN(test_asin_approx_performance_f32); TEST_RUN(test_sqrt_approx_performance_f32);
TEST_RUN(test_acos_approx_performance_f32); TEST_RUN(test_asin_approx_performance_f32);
TEST_RUN(test_atan_approx_performance_f32); TEST_RUN(test_acos_approx_performance_f32);
TEST_RUN(test_rsqrt_approx_performance_f32); TEST_RUN(test_atan_approx_performance_f32);
TEST_RUN(test_exp_approx_performance_f32); TEST_RUN(test_rsqrt_approx_performance_f32);
TEST_RUN(test_log_approx_performance_f32); TEST_RUN(test_exp_approx_performance_f32);
TEST_RUN(test_pow_approx_performance_f32); TEST_RUN(test_log_approx_performance_f32);
TEST_RUN(test_pow_approx_performance_f32);
// Run performance tests for f64 functions // Run performance tests for f64 functions
TEST_RUN(test_sin_approx_performance_f64); TEST_RUN(test_sin_approx_performance_f64);
TEST_RUN(test_cos_approx_performance_f64); TEST_RUN(test_cos_approx_performance_f64);
TEST_RUN(test_tan_approx_performance_f64); TEST_RUN(test_tan_approx_performance_f64);
TEST_RUN(test_sqrt_approx_performance_f64); TEST_RUN(test_sqrt_approx_performance_f64);
TEST_RUN(test_asin_approx_performance_f64); TEST_RUN(test_asin_approx_performance_f64);
TEST_RUN(test_acos_approx_performance_f64); TEST_RUN(test_acos_approx_performance_f64);
TEST_RUN(test_atan_approx_performance_f64); TEST_RUN(test_atan_approx_performance_f64);
TEST_RUN(test_rsqrt_approx_performance_f64); TEST_RUN(test_rsqrt_approx_performance_f64);
TEST_RUN(test_exp_approx_performance_f64); TEST_RUN(test_exp_approx_performance_f64);
TEST_RUN(test_log_approx_performance_f64); TEST_RUN(test_log_approx_performance_f64);
TEST_RUN(test_pow_approx_performance_f64); TEST_RUN(test_pow_approx_performance_f64);
#endif
TEST_FINALIZE(); TEST_FINALIZE();

View File

@ -1,3 +1,5 @@
#include <stdio.h>
#include <ctype.h>
#include "../TestFramework.h" #include "../TestFramework.h"
#include "../../utils/StringUtils.h" #include "../../utils/StringUtils.h"
#include "../../utils/EndianUtils.h" #include "../../utils/EndianUtils.h"
@ -83,6 +85,7 @@ static void test_str_length()
ASSERT_EQUALS(str_length("2asdf dw"), 8); ASSERT_EQUALS(str_length("2asdf dw"), 8);
} }
#if PERFORMANCE_TEST
static void _str_length(volatile void* val) { static void _str_length(volatile void* val) {
volatile int64* res = (volatile int64 *) val; volatile int64* res = (volatile int64 *) val;
@ -107,7 +110,9 @@ static void test_str_length_performance() {
COMPARE_FUNCTION_TEST_TIME(_str_length, _strlen, 5.0); COMPARE_FUNCTION_TEST_TIME(_str_length, _strlen, 5.0);
COMPARE_FUNCTION_TEST_CYCLE(_str_length, _strlen, 5.0); COMPARE_FUNCTION_TEST_CYCLE(_str_length, _strlen, 5.0);
} }
#endif
#if PERFORMANCE_TEST
static void _str_is_alphanum(volatile void* val) { static void _str_is_alphanum(volatile void* val) {
bool* res = (bool *) val; bool* res = (bool *) val;
srand(0); srand(0);
@ -136,6 +141,7 @@ static void test_str_is_alphanum_performance() {
COMPARE_FUNCTION_TEST_TIME(_str_is_alphanum, _isalnum, 5.0); COMPARE_FUNCTION_TEST_TIME(_str_is_alphanum, _isalnum, 5.0);
COMPARE_FUNCTION_TEST_CYCLE(_str_is_alphanum, _isalnum, 5.0); COMPARE_FUNCTION_TEST_CYCLE(_str_is_alphanum, _isalnum, 5.0);
} }
#endif
static void test_sprintf_fast() static void test_sprintf_fast()
{ {
@ -144,6 +150,7 @@ static void test_sprintf_fast()
ASSERT_TRUE(strcmp(buffer, "This 1337 is a test with 3.00000 values") == 0); ASSERT_TRUE(strcmp(buffer, "This 1337 is a test with 3.00000 values") == 0);
} }
#if PERFORMANCE_TEST
static void _sprintf_fast(volatile void* val) { static void _sprintf_fast(volatile void* val) {
volatile bool* res = (volatile bool *) val; volatile bool* res = (volatile bool *) val;
@ -164,6 +171,7 @@ static void test_sprintf_fast_performance() {
COMPARE_FUNCTION_TEST_TIME(_sprintf_fast, _sprintf, 5.0); COMPARE_FUNCTION_TEST_TIME(_sprintf_fast, _sprintf, 5.0);
COMPARE_FUNCTION_TEST_CYCLE(_sprintf_fast, _sprintf, 5.0); COMPARE_FUNCTION_TEST_CYCLE(_sprintf_fast, _sprintf, 5.0);
} }
#endif
static void test_str_to_float() static void test_str_to_float()
{ {
@ -179,8 +187,6 @@ static void test_str_to_float()
#define main UtilsStringUtilsTest #define main UtilsStringUtilsTest
#endif #endif
#include <windows.h>
int main() { int main() {
TEST_INIT(100); TEST_INIT(100);
@ -196,9 +202,11 @@ int main() {
TEST_RUN(test_str_length); TEST_RUN(test_str_length);
TEST_RUN(test_str_to_float); TEST_RUN(test_str_to_float);
TEST_RUN(test_str_length_performance); #if PERFORMANCE_TEST
TEST_RUN(test_str_is_alphanum_performance); TEST_RUN(test_str_length_performance);
TEST_RUN(test_sprintf_fast_performance); TEST_RUN(test_str_is_alphanum_performance);
TEST_RUN(test_sprintf_fast_performance);
#endif
TEST_FINALIZE(); TEST_FINALIZE();

View File

@ -0,0 +1,34 @@
#include <string.h>
#include "../TestFramework.h"
#include "../../utils/TimeUtils.h"
static void test_time_index() {
ASSERT_GREATER_THAN(time_index(), 0.0f);
}
static void test_system_time() {
ASSERT_GREATER_THAN(system_time(), 0.0f);
}
static void test_time_mu() {
ASSERT_GREATER_THAN(time_mu(), 0.0f);
}
#ifdef UBER_TEST
#ifdef main
#undef main
#endif
#define main UtilsTimeUtilsTest
#endif
int main() {
TEST_INIT(10);
TEST_RUN(test_time_index);
TEST_RUN(test_system_time);
TEST_RUN(test_time_mu);
TEST_FINALIZE();
return 0;
}

View File

@ -5,7 +5,6 @@ static void test_is_equal() {
uint8_t region1[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; uint8_t region1[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
uint8_t region2[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; uint8_t region2[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
uint8_t region3[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x09}; uint8_t region3[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x09};
uint8_t region4[] = {0x01, 0x02, 0x03, 0x04};
// Test equal regions // Test equal regions
ASSERT_TRUE(is_equal(region1, region2, sizeof(region1))); ASSERT_TRUE(is_equal(region1, region2, sizeof(region1)));
@ -54,6 +53,7 @@ static void test_is_empty() {
ASSERT_TRUE(is_empty(region1, 0)); ASSERT_TRUE(is_empty(region1, 0));
} }
#if PERFORMANCE_TEST
static void _is_equal(volatile void* val) { static void _is_equal(volatile void* val) {
volatile bool* res = (volatile bool *) val; volatile bool* res = (volatile bool *) val;
@ -124,6 +124,7 @@ static void test_is_empty_performance() {
COMPARE_FUNCTION_TEST_TIME(_is_empty2, _memcmp_empty2, 10.0); COMPARE_FUNCTION_TEST_TIME(_is_empty2, _memcmp_empty2, 10.0);
COMPARE_FUNCTION_TEST_CYCLE(_is_empty2, _memcmp_empty2, 10.0); COMPARE_FUNCTION_TEST_CYCLE(_is_empty2, _memcmp_empty2, 10.0);
} }
#endif
#ifdef UBER_TEST #ifdef UBER_TEST
#ifdef main #ifdef main
@ -138,8 +139,10 @@ int main() {
TEST_RUN(test_is_equal); TEST_RUN(test_is_equal);
TEST_RUN(test_is_empty); TEST_RUN(test_is_empty);
TEST_RUN(test_is_equal_performance); #if PERFORMANCE_TEST
TEST_RUN(test_is_empty_performance); TEST_RUN(test_is_equal_performance);
TEST_RUN(test_is_empty_performance);
#endif
TEST_FINALIZE(); TEST_FINALIZE();

View File

@ -48,7 +48,7 @@ for /R %%f in (*Test.cpp) do (
REM Compile each .cpp file into an executable REM Compile each .cpp file into an executable
cl ^ cl ^
%BUILD_FLAGS% /MT /nologo /Gm- /GR- /EHsc /W4 /wd4201 /wd4706 /wd4324 ^ %BUILD_FLAGS% /MT /nologo /Gm- /GR- /EHsc /W4 /wd4201 /wd4706 /wd4324 ^
/fp:precise /Zc:wchar_t /Zc:forScope /Zc:inline /std:c++20 ^ /fp:precise /Zc:wchar_t /Zc:forScope /Zc:inline /std:c++23 ^
/D WIN32 /D _WINDOWS /D _UNICODE /D UNICODE ^ /D WIN32 /D _WINDOWS /D _UNICODE /D UNICODE ^
/D _CRT_SECURE_NO_WARNINGS ^ /D _CRT_SECURE_NO_WARNINGS ^
/Fo"%DESTINATION_DIR%/" /Fe"%DESTINATION_DIR%\%BASENAME%.exe" %DEBUG_DATA% ^ /Fo"%DESTINATION_DIR%/" /Fe"%DESTINATION_DIR%\%BASENAME%.exe" %DEBUG_DATA% ^

70
tests_iter.sh Executable file
View File

@ -0,0 +1,70 @@
#!/bin/bash
clear
EXE_NAME="tests"
DESTINATION_DIR="../build/tests"
# Create build directories if they don't exist
mkdir -p "../build"
mkdir -p "$DESTINATION_DIR"
# Clean up previous build files
rm -f "$DESTINATION_DIR"/*.pdb 2>/dev/null
rm -f "$DESTINATION_DIR"/*.idb 2>/dev/null
# Default build configuration
BUILD_TYPE="DEBUG"
BUILD_FLAGS="-g -O0 -Wall -Werror -Wextra -DDEBUG -ggdb -Werror"
DEBUG_DATA="-g"
# Parse command-line arguments
if [ "$1" = "-r" ]; then
BUILD_TYPE="RELEASE"
BUILD_FLAGS="-O3 -DNDEBUG"
DEBUG_DATA=""
elif [ "$1" = "-d" ]; then
BUILD_TYPE="DEBUG"
BUILD_FLAGS="-g -O0 -Wall -Werror -Wextra -DDEBUG -ggdb -Werror"
DEBUG_DATA="-g"
fi
source ./check_cpu_features.sh
# Detect CPU features
CPU_FEATURES=$(check_cpu_features)
# Find all *Test.cpp files in the ./ directory and subdirectories
find ./ -name "*Test.cpp" | while read -r test_file; do
# Get the base name of the file without extension
basename=$(basename "$test_file" .cpp)
echo "Compiling $test_file..."
# Compile each test file
g++ $BUILD_FLAGS -std=c++23 -m64 \
-DUNICODE -D_UNICODE -D__linux__ \
-Wno-unused-result \
-fsanitize=address \
-fsanitize=undefined \
${CPU_FEATURES} \
-o "$DESTINATION_DIR/$basename" \
"$test_file" \
$DEBUG_DATA
# Check if compilation was successful
if [ $? -ne 0 ]; then
echo "Compilation failed for $test_file"
exit 1
fi
# Run the compiled test
echo "Running $basename..."
"$DESTINATION_DIR/$basename"
# Check if test ran successfully
if [ $? -ne 0 ]; then
echo "Test $basename failed"
exit 1
fi
done

View File

@ -12,6 +12,7 @@
#include <stdio.h> #include <stdio.h>
#include "../stdlib/Types.h" #include "../stdlib/Types.h"
#include "../log/Log.h" #include "../log/Log.h"
#include "../log/Stats.h"
#include "Atomic.h" #include "Atomic.h"
#if _WIN32 #if _WIN32
@ -26,14 +27,21 @@
void thread_create(Worker* worker, ThreadJobFunc routine, void* arg) void thread_create(Worker* worker, ThreadJobFunc routine, void* arg)
{ {
LOG_1("Thread starting"); LOG_1("Thread starting");
coms_pthread_create(&worker->thread, NULL, routine, arg); coms_pthread_create(&worker->thread, NULL, routine, arg);
LOG_INCREMENT(DEBUG_COUNTER_THREAD);
LOG_2("%d threads running", {{LOG_DATA_INT64, (void *) &_stats_counter[DEBUG_COUNTER_THREAD]}});
} }
void thread_stop(Worker* worker) void thread_stop(Worker* worker)
{ {
atomic_set_release(&worker->state, 0); atomic_set_release(&worker->state, 0);
coms_pthread_join(worker->thread, NULL); coms_pthread_join(worker->thread, NULL);
LOG_1("Thread ended"); LOG_1("Thread ended");
LOG_DECREMENT(DEBUG_COUNTER_THREAD);
LOG_2("%d threads running", {{LOG_DATA_INT64, (void *) &_stats_counter[DEBUG_COUNTER_THREAD]}});
} }
#endif #endif

View File

@ -16,6 +16,7 @@
#include "Thread.h" #include "Thread.h"
#include "Atomic.h" #include "Atomic.h"
#include "ThreadJob.h" #include "ThreadJob.h"
#include "../log/DebugContainer.h"
struct ThreadPool { struct ThreadPool {
// This is not a threaded queue since we want to handle the mutex in here, not in the queue for finer control // This is not a threaded queue since we want to handle the mutex in here, not in the queue for finer control
@ -25,29 +26,50 @@ struct ThreadPool {
mutex_cond work_cond; mutex_cond work_cond;
mutex_cond working_cond; mutex_cond working_cond;
// By design the working_cnt is <= thread_cnt
alignas(4) atomic_32 int32 working_cnt; alignas(4) atomic_32 int32 working_cnt;
alignas(4) atomic_32 int32 thread_cnt; alignas(4) atomic_32 int32 thread_cnt;
int32 size; int32 size;
int32 element_size; int32 element_size;
// 0 = down, 1 = shutting down, 2 = running
alignas(4) atomic_32 int32 state; alignas(4) atomic_32 int32 state;
alignas(4) atomic_32 int32 id_counter; alignas(4) atomic_32 int32 id_counter;
DebugContainer* debug_container;
}; };
// @performance Can we optimize this? This is a critical function // @performance Can we optimize this? This is a critical function.
// If we have a small worker the "spinup"/"re-activation" time is from utmost importance
static static
THREAD_RETURN thread_pool_worker(void* arg) THREAD_RETURN thread_pool_worker(void* arg)
{ {
ThreadPool* pool = (ThreadPool *) arg; ThreadPool* pool = (ThreadPool *) arg;
if (pool->debug_container) {
_log_fp = pool->debug_container->log_fp;
_log_memory = pool->debug_container->log_memory;
_dmc = pool->debug_container->dmc;
_perf_stats = pool->debug_container->perf_stats;
_perf_active = pool->debug_container->perf_active;
_stats_counter = pool->debug_container->stats_counter;
*_perf_active = *pool->debug_container->perf_active;
}
// @bug Why doesn't this work? There must be some threading issue
LOG_2("Thread pool worker starting up");
LOG_INCREMENT(DEBUG_COUNTER_THREAD);
PoolWorker* work; PoolWorker* work;
while (true) { while (true) {
mutex_lock(&pool->work_mutex); mutex_lock(&pool->work_mutex);
while (queue_is_empty(&pool->work_queue) && !pool->state) { while (pool->state > 1 && queue_is_empty(&pool->work_queue)) {
coms_pthread_cond_wait(&pool->work_cond, &pool->work_mutex); coms_pthread_cond_wait(&pool->work_cond, &pool->work_mutex);
} }
if (pool->state == 1) { if (pool->state < 2) {
mutex_unlock(&pool->work_mutex); mutex_unlock(&pool->work_mutex);
break; break;
@ -55,7 +77,8 @@ THREAD_RETURN thread_pool_worker(void* arg)
// We define a queue element as free based on it's id // We define a queue element as free based on it's id
// So even if we "keep" it in the queue the pool will not overwrite it as long as the id > 0 (see pool_add) // So even if we "keep" it in the queue the pool will not overwrite it as long as the id > 0 (see pool_add)
// This is only a ThreadPool specific queue behavior to avoid additional copies // This is only a ThreadPool specific queue behavior to avoid additional memory copy
// @bug this needs to be a threaded queue
work = (PoolWorker *) queue_dequeue_keep(&pool->work_queue); work = (PoolWorker *) queue_dequeue_keep(&pool->work_queue);
mutex_unlock(&pool->work_mutex); mutex_unlock(&pool->work_mutex);
@ -63,7 +86,7 @@ THREAD_RETURN thread_pool_worker(void* arg)
continue; continue;
} }
atomic_increment_relaxed(&pool->working_cnt); atomic_increment_release(&pool->working_cnt);
atomic_set_release(&work->state, 2); atomic_set_release(&work->state, 2);
LOG_2("ThreadPool worker started"); LOG_2("ThreadPool worker started");
work->func(work); work->func(work);
@ -79,15 +102,21 @@ THREAD_RETURN thread_pool_worker(void* arg)
atomic_set_release(&work->id, 0); atomic_set_release(&work->id, 0);
} }
atomic_decrement_relaxed(&pool->working_cnt); atomic_decrement_release(&pool->working_cnt);
if (atomic_get_relaxed(&pool->state) == 0 && atomic_get_relaxed(&pool->working_cnt) == 0) { // Signal that we ran out of work (maybe the main thread needs this info)
// This is not required for the thread pool itself but maybe some other part of the main thread wants to know
if (atomic_get_relaxed(&pool->working_cnt) == 0) {
coms_pthread_cond_signal(&pool->working_cond); coms_pthread_cond_signal(&pool->working_cond);
} }
} }
// We tell the thread pool taht this worker thread is shutting down
atomic_decrement_release(&pool->thread_cnt);
coms_pthread_cond_signal(&pool->working_cond); coms_pthread_cond_signal(&pool->working_cond);
atomic_decrement_relaxed(&pool->thread_cnt);
LOG_2("Thread pool worker shutting down");
LOG_DECREMENT(DEBUG_COUNTER_THREAD);
return (THREAD_RETURN) NULL; return (THREAD_RETURN) NULL;
} }
@ -119,11 +148,15 @@ void thread_pool_alloc(
coms_pthread_cond_init(&pool->work_cond, NULL); coms_pthread_cond_init(&pool->work_cond, NULL);
coms_pthread_cond_init(&pool->working_cond, NULL); coms_pthread_cond_init(&pool->working_cond, NULL);
pool->state = 2;
coms_pthread_t thread; coms_pthread_t thread;
for (pool->size = 0; pool->size < thread_count; ++pool->size) { for (pool->size = 0; pool->size < thread_count; ++pool->size) {
coms_pthread_create(&thread, NULL, thread_pool_worker, pool); coms_pthread_create(&thread, NULL, thread_pool_worker, pool);
coms_pthread_detach(thread); coms_pthread_detach(thread);
} }
LOG_2("%d threads running", {{LOG_DATA_INT64, (void *) &_stats_counter[DEBUG_COUNTER_THREAD]}});
} }
void thread_pool_create( void thread_pool_create(
@ -154,17 +187,23 @@ void thread_pool_create(
coms_pthread_cond_init(&pool->work_cond, NULL); coms_pthread_cond_init(&pool->work_cond, NULL);
coms_pthread_cond_init(&pool->working_cond, NULL); coms_pthread_cond_init(&pool->working_cond, NULL);
pool->state = 2;
coms_pthread_t thread; coms_pthread_t thread;
for (pool->size = 0; pool->size < thread_count; ++pool->size) { for (pool->size = 0; pool->size < thread_count; ++pool->size) {
coms_pthread_create(&thread, NULL, thread_pool_worker, pool); coms_pthread_create(&thread, NULL, thread_pool_worker, pool);
coms_pthread_detach(thread); coms_pthread_detach(thread);
} }
LOG_2("%d threads running", {{LOG_DATA_INT64, (void *) &_stats_counter[DEBUG_COUNTER_THREAD]}});
} }
void thread_pool_wait(ThreadPool* pool) void thread_pool_wait(ThreadPool* pool)
{ {
mutex_lock(&pool->work_mutex); mutex_lock(&pool->work_mutex);
while ((!pool->state && pool->working_cnt != 0) || (pool->state && pool->thread_cnt != 0)) { // @question We removed some state checks here, not sure if they were really necessary
// remove this comment once we are sure everything works as expected
while (pool->working_cnt != 0 || pool->thread_cnt != 0) {
coms_pthread_cond_wait(&pool->working_cond, &pool->work_mutex); coms_pthread_cond_wait(&pool->working_cond, &pool->work_mutex);
} }
mutex_unlock(&pool->work_mutex); mutex_unlock(&pool->work_mutex);
@ -184,6 +223,9 @@ void thread_pool_destroy(ThreadPool* pool)
mutex_destroy(&pool->work_mutex); mutex_destroy(&pool->work_mutex);
coms_pthread_cond_destroy(&pool->work_cond); coms_pthread_cond_destroy(&pool->work_cond);
coms_pthread_cond_destroy(&pool->working_cond); coms_pthread_cond_destroy(&pool->working_cond);
// This sets the state to "down"
atomic_set_release(&pool->state, 0);
} }
PoolWorker* thread_pool_add_work(ThreadPool* pool, const PoolWorker* job) PoolWorker* thread_pool_add_work(ThreadPool* pool, const PoolWorker* job)

View File

@ -89,7 +89,7 @@ void ui_layout_assign_children(
break; break;
} }
str_copy_move_until(&pos, block_name, ":"); str_copy_move_until(block_name, &pos, ":");
str_move_past(&pos, '\n'); str_move_past(&pos, '\n');
// Children array (located after the UIElement) // Children array (located after the UIElement)
@ -182,8 +182,8 @@ void layout_from_file_txt(
continue; continue;
} }
str_copy_move_until(&pos, block_name, ":"); ++pos; str_copy_move_until(block_name, &pos, ":"); ++pos;
str_copy_move_until(&pos, block_type, " \r\n"); str_copy_move_until(block_type, &pos, " \r\n");
str_move_past(&pos, '\n'); str_move_past(&pos, '\n');
// Insert new element // Insert new element
@ -235,7 +235,7 @@ void layout_from_file_txt(
continue; continue;
} }
str_copy_move_until(&pos, block_name, ":"); str_copy_move_until(block_name, &pos, ":");
str_move_past(&pos, '\n'); str_move_past(&pos, '\n');
UIElement* element = (UIElement *) (layout->data + ((HashEntryInt32 *) hashmap_get_entry(&layout->hash_map, block_name))->value); UIElement* element = (UIElement *) (layout->data + ((HashEntryInt32 *) hashmap_get_entry(&layout->hash_map, block_name))->value);
@ -268,7 +268,7 @@ void layout_from_file_txt(
continue; continue;
} }
str_copy_move_until(&pos, block_name, ":"); str_copy_move_until(block_name, &pos, ":");
str_move_past(&pos, '\n'); str_move_past(&pos, '\n');
root_children[child++] = ((HashEntryInt32 *) hashmap_get_entry(&layout->hash_map, block_name))->value; root_children[child++] = ((HashEntryInt32 *) hashmap_get_entry(&layout->hash_map, block_name))->value;
} }

View File

@ -137,7 +137,7 @@ void theme_from_file_txt(
// Handle new group // Handle new group
///////////////////////////////////////////////////////// /////////////////////////////////////////////////////////
if (!block_open) { if (!block_open) {
str_copy_move_until(&pos, block_name, " \r\n"); str_copy_move_until(block_name, &pos, " \r\n");
// All blocks need to start with #. In the past this wasn't the case and may not be in the future. This is why we keep this if here. // All blocks need to start with #. In the past this wasn't the case and may not be in the future. This is why we keep this if here.
if (*block_name == '#' || *block_name == '.') { if (*block_name == '#' || *block_name == '.') {
@ -164,7 +164,7 @@ void theme_from_file_txt(
// Handle new attribute for previously found group // Handle new attribute for previously found group
///////////////////////////////////////////////////////// /////////////////////////////////////////////////////////
str_copy_move_until(&pos, attribute_name, " :\n"); str_copy_move_until(attribute_name, &pos, " :\n");
// Skip any white spaces or other delimeter // Skip any white spaces or other delimeter
str_skip_list(&pos, " \t:", sizeof(" \t:") - 1); str_skip_list(&pos, " \t:", sizeof(" \t:") - 1);

View File

@ -209,7 +209,7 @@ void ui_attribute_parse_value(UIAttribute* attr, const char* attribute_name, con
attr->attribute_id = (UIAttributeType) ui_attribute_type_to_id(attribute_name); attr->attribute_id = (UIAttributeType) ui_attribute_type_to_id(attribute_name);
char value[64]; char value[64];
str_copy_until(pos, value, "\r\n"); str_copy_until(value, pos, "\r\n");
if (str_is_integer(value)) { if (str_is_integer(value)) {
attr->value_int = (int32) str_to_int(value); attr->value_int = (int32) str_to_int(value);
@ -222,7 +222,7 @@ void ui_attribute_parse_value(UIAttribute* attr, const char* attribute_name, con
hexstr_to_rgba(&attr->value_v4_f32, pos); hexstr_to_rgba(&attr->value_v4_f32, pos);
attr->datatype = UI_ATTRIBUTE_DATA_TYPE_V4_F32; attr->datatype = UI_ATTRIBUTE_DATA_TYPE_V4_F32;
} else { } else {
str_copy_until(value, attr->value_str, "\r\n"); str_copy_until(attr->value_str, value, "\r\n");
attr->datatype = UI_ATTRIBUTE_DATA_TYPE_STR; attr->datatype = UI_ATTRIBUTE_DATA_TYPE_STR;
} }
} }

View File

@ -23,7 +23,7 @@
#define BIT_SET_L2R(num, pos, bits) ((num) | (1U << ((bits - 1) - (pos)))) #define BIT_SET_L2R(num, pos, bits) ((num) | (1U << ((bits - 1) - (pos))))
#define BIT_UNSET_L2R(num, pos, bits) ((num) & ~(1U << ((bits - 1) - (pos)))) #define BIT_UNSET_L2R(num, pos, bits) ((num) & ~(1U << ((bits - 1) - (pos))))
#define BIT_FLIP_L2R(num, pos, bits) ((num) ^ (1U << ((bits - 1) - (pos)))) #define BIT_FLIP_L2R(num, pos, bits) ((num) ^ (1U << ((bits - 1) - (pos))))
#define BIT_SET_TO_L2R(num, pos, x, bits) ((num) & ~((uint32) 1 << ((bits - 1) - (pos))) | ((uint32) (x) << ((bits - 1) - (pos)))) #define BIT_SET_TO_L2R(num, pos, x, bits) (((num) & ~((uint32_t)(1) << ((bits) - 1 - (pos)))) | (((uint32_t)(x) & 1U) << ((bits) - 1 - (pos))))
#define BITS_GET_8_L2R(num, pos, to_read) (((num) >> (8 - (pos) - (to_read))) & ((1U << (to_read)) - 1)) #define BITS_GET_8_L2R(num, pos, to_read) (((num) >> (8 - (pos) - (to_read))) & ((1U << (to_read)) - 1))
#define BITS_GET_16_L2R(num, pos, to_read) (((num) >> (16 - (pos) - (to_read))) & ((1U << (to_read)) - 1)) #define BITS_GET_16_L2R(num, pos, to_read) (((num) >> (16 - (pos) - (to_read))) & ((1U << (to_read)) - 1))
#define BITS_GET_32_L2R(num, pos, to_read) (((num) >> (32 - (pos) - (to_read))) & ((1U << (to_read)) - 1)) #define BITS_GET_32_L2R(num, pos, to_read) (((num) >> (32 - (pos) - (to_read))) & ((1U << (to_read)) - 1))
@ -41,7 +41,7 @@
#define BIT_SET_R2L(num, pos) ((num) | ((uint32) 1 << (pos))) #define BIT_SET_R2L(num, pos) ((num) | ((uint32) 1 << (pos)))
#define BIT_UNSET_R2L(num, pos) ((num) & ~((uint32) 1 << (pos))) #define BIT_UNSET_R2L(num, pos) ((num) & ~((uint32) 1 << (pos)))
#define BIT_FLIP_R2L(num, pos) ((num) ^ ((uint32) 1 << (pos))) #define BIT_FLIP_R2L(num, pos) ((num) ^ ((uint32) 1 << (pos)))
#define BIT_SET_TO_R2L(num, pos, x) ((num) & ~((uint32) 1 << (pos)) | ((uint32) (x) << (pos))) #define BIT_SET_TO_R2L(num, pos, x) (((num) & ~((uint32_t)(1) << (pos))) | ((uint32_t)(x) << (pos)))
// @performance Try to use this version over the L2R version for performance reasons // @performance Try to use this version over the L2R version for performance reasons
#define BITS_GET_8_R2L(num, pos, to_read) (((num) >> (pos)) & ((1U << (to_read)) - 1)) #define BITS_GET_8_R2L(num, pos, to_read) (((num) >> (pos)) & ((1U << (to_read)) - 1))
#define BITS_GET_16_R2L(num, pos, to_read) (((num) >> (pos)) & ((1U << (to_read)) - 1)) #define BITS_GET_16_R2L(num, pos, to_read) (((num) >> (pos)) & ((1U << (to_read)) - 1))

View File

@ -941,7 +941,7 @@ void str_copy_long(char* __restrict dest, const char* __restrict src) noexcept
} }
inline inline
void str_copy_move_until(const char** __restrict src, char* __restrict dest, char delim) noexcept void str_copy_move_until(char* __restrict dest, const char* __restrict* __restrict src, char delim) noexcept
{ {
while (**src != delim && **src != '\0') { while (**src != delim && **src != '\0') {
*dest++ = **src; *dest++ = **src;
@ -952,7 +952,7 @@ void str_copy_move_until(const char** __restrict src, char* __restrict dest, cha
} }
inline inline
void str_copy_move_until(const char** __restrict src, char* __restrict dest, const char* __restrict delim) noexcept void str_copy_move_until(char* __restrict dest, const char* __restrict* __restrict src, const char* __restrict delim) noexcept
{ {
size_t len = str_length(delim); size_t len = str_length(delim);
@ -1055,7 +1055,7 @@ str_concat_append(char* dst, size_t dst_length, const char* src) noexcept
inline int64 inline int64
str_concat_new( str_concat_new(
char* dst, char* __restrict dst,
const char* src1, size_t src1_length, const char* src1, size_t src1_length,
const char* src2, size_t src2_length const char* src2, size_t src2_length
) noexcept { ) noexcept {
@ -1092,7 +1092,7 @@ void str_concat_append(
} }
inline void inline void
str_concat_new(char* dst, const char* src, int64 data) noexcept str_concat_new(char* __restrict dst, const char* __restrict src, int64 data) noexcept
{ {
size_t src_len = str_length(src); size_t src_len = str_length(src);
memcpy(dst, src, src_len); memcpy(dst, src, src_len);
@ -1115,7 +1115,7 @@ void str_remove(char* __restrict dst, size_t remove_pos, size_t remove_length) n
} }
inline inline
char* strtok(char* str, const char* __restrict delim, char* *key) noexcept { char* strtok(char* str, const char* __restrict delim, char** key) noexcept {
char* result; char* result;
if (str == NULL) { if (str == NULL) {
str = *key; str = *key;
@ -1139,7 +1139,7 @@ char* strtok(char* str, const char* __restrict delim, char* *key) noexcept {
} }
inline constexpr inline constexpr
bool str_contains(const char* haystack, const char* needle) noexcept bool str_contains(const char* __restrict haystack, const char* __restrict needle) noexcept
{ {
// @performance would it make sense to only check until haystack - strlen(needle)? // @performance would it make sense to only check until haystack - strlen(needle)?
// I'm not sure the strlen overhead is worth it // I'm not sure the strlen overhead is worth it
@ -1163,7 +1163,7 @@ bool str_contains(const char* haystack, const char* needle) noexcept
} }
inline constexpr inline constexpr
bool str_contains(const char* haystack, const char* needle, size_t length) noexcept bool str_contains(const char* __restrict haystack, const char* __restrict needle, size_t length) noexcept
{ {
while (*haystack != '\0' && length > 0) { while (*haystack != '\0' && length > 0) {
const char* p1 = haystack; const char* p1 = haystack;
@ -1320,7 +1320,7 @@ int32 str_compare_caseless(const char* str1, const char* str2, size_t n) noexcep
} }
inline constexpr inline constexpr
bool str_ends_with(const char* str, const char* suffix) noexcept { bool str_ends_with(const char* __restrict str, const char* __restrict suffix) noexcept {
if (!str || !suffix) { if (!str || !suffix) {
return false; return false;
} }
@ -1434,7 +1434,7 @@ void str_move_to_pos(const char** str, int32 pos) noexcept
} }
inline inline
void str_move_past(const char** str, char delim) noexcept void str_move_past(const char* __restrict* __restrict str, char delim) noexcept
{ {
while (**str != delim && **str != '\0') { while (**str != delim && **str != '\0') {
++(*str); ++(*str);
@ -1549,7 +1549,7 @@ void str_skip_until_list(const char** __restrict str, const char* __restrict del
} }
inline inline
void hexstr_to_rgba(v4_f32* rgba, const char* hex) noexcept void hexstr_to_rgba(v4_f32* __restrict rgba, const char* __restrict hex) noexcept
{ {
if (*hex == '#') { if (*hex == '#') {
++hex; ++hex;
@ -1886,7 +1886,7 @@ void sprintf_fast(char* __restrict buffer, int32 buffer_length, const char* __re
} }
// There are situations where you only want to replace a certain amount of % // There are situations where you only want to replace a certain amount of %
void sprintf_fast_iter(char* buffer, const char* format, ...) noexcept { void sprintf_fast_iter(char* __restrict buffer, const char* __restrict format, ...) noexcept {
va_list args; va_list args;
va_start(args, format); va_start(args, format);

View File

@ -10,13 +10,13 @@
#define COMS_UTILS_TEST_UTILS_H #define COMS_UTILS_TEST_UTILS_H
#if DEBUG #if DEBUG
#define ASSERT_SIMPLE(a) if (!(a)) { \ #define ASSERT_SIMPLE(a) if (!(a)) { \
/* cppcheck-suppress nullPointer */ \ /* cppcheck-suppress nullPointer */ \
*(volatile int *)0 = 0; \ *(volatile int *)0 = 0; \
} }
#define ASSERT_SIMPLE_CONST(a) if constexpr (!(a)) { \ #define ASSERT_SIMPLE_CONST(a) if constexpr (!(a)) { \
/* cppcheck-suppress nullPointer */ \ /* cppcheck-suppress nullPointer */ \
*(volatile int *)0 = 0; \ *(volatile int *)0 = 0; \
} }
#if DEBUG_STRICT #if DEBUG_STRICT