mirror of
https://github.com/Karaka-Management/cOMS.git
synced 2026-01-10 19:08:39 +00:00
fixing bugs and adding some test scripts
This commit is contained in:
parent
2883ca0841
commit
eb9a135ca7
|
|
@ -17,8 +17,8 @@
|
|||
// Only allowed for data >= 64 bits
|
||||
bool is_empty(const byte* region, uint64 size, int32 steps = 8)
|
||||
{
|
||||
// Quick check of first 8 bytes
|
||||
if (*((uint64 *) region) != 0) {
|
||||
// Quick check of first byte
|
||||
if (*region != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
58
check_cpu_features.sh
Normal file
58
check_cpu_features.sh
Normal 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"
|
||||
}
|
||||
|
|
@ -34,7 +34,7 @@ struct DatabasePool {
|
|||
void db_pool_alloc(DatabasePool* pool, uint8 count) {
|
||||
ASSERT_SIMPLE(count);
|
||||
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)
|
||||
+ 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->free = (uint64 *) ROUND_TO_NEAREST((uintptr_t) (pool->connections + count * sizeof(DatabaseConnection)), 64);
|
||||
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 {
|
||||
|
|
@ -53,6 +51,8 @@ void db_pool_add(DatabasePool* __restrict pool, DatabaseConnection* __restrict d
|
|||
}
|
||||
|
||||
void db_pool_free(DatabasePool* pool) {
|
||||
LOG_1("Freeing DatabasePool");
|
||||
|
||||
for (int32 i = 0; i < pool->count; ++i) {
|
||||
db_close(&pool->connections[i]);
|
||||
}
|
||||
|
|
@ -60,8 +60,6 @@ void db_pool_free(DatabasePool* pool) {
|
|||
platform_aligned_free((void **) &pool->connections);
|
||||
pool->free = NULL;
|
||||
pool->count = 0;
|
||||
|
||||
LOG_1("Freed DatabasePool");
|
||||
}
|
||||
|
||||
// Returns free database connection or null if none could be found
|
||||
|
|
|
|||
13
log/Log.h
13
log/Log.h
|
|
@ -67,7 +67,9 @@ enum LogDataType {
|
|||
LOG_DATA_NONE,
|
||||
LOG_DATA_VOID,
|
||||
LOG_DATA_BYTE,
|
||||
LOG_DATA_INT16,
|
||||
LOG_DATA_INT32,
|
||||
LOG_DATA_UINT16,
|
||||
LOG_DATA_UINT32,
|
||||
LOG_DATA_INT64,
|
||||
LOG_DATA_UINT64,
|
||||
|
|
@ -100,7 +102,7 @@ struct LogDataArray{
|
|||
LogData data[LOG_DATA_ARRAY];
|
||||
};
|
||||
|
||||
// @bug This probably requires thread safety
|
||||
// @bug This needs to be thread safe
|
||||
byte* log_get_memory() noexcept
|
||||
{
|
||||
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)
|
||||
// @bug This needs to be thread safe
|
||||
void log_to_file()
|
||||
{
|
||||
// we don't log an empty log pool
|
||||
|
|
@ -157,6 +160,7 @@ void log_flush()
|
|||
_log_memory->pos = 0;
|
||||
}
|
||||
|
||||
// @bug This needs to be thread safe
|
||||
void log(const char* str, const char* file, const char* function, int32 line)
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (!_log_memory) {
|
||||
|
|
@ -236,6 +241,12 @@ void log(const char* format, LogDataArray data, const char* file, const char* fu
|
|||
case LOG_DATA_BYTE: {
|
||||
sprintf_fast_iter(msg->message, temp_format, (int32) *((byte *) data.data[i].value));
|
||||
} 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: {
|
||||
sprintf_fast_iter(msg->message, temp_format, *((int32 *) data.data[i].value));
|
||||
} break;
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@
|
|||
#include "../utils/TimeUtils.h"
|
||||
#include "../thread/Spinlock.cpp"
|
||||
#include "../thread/Atomic.h"
|
||||
#include "../system/Allocator.h"
|
||||
#include "../hash/GeneralHash.h"
|
||||
#include "../architecture/Intrinsics.h"
|
||||
#include "../compiler/CompilerUtils.h"
|
||||
|
|
@ -24,6 +23,7 @@
|
|||
enum TimingStats {
|
||||
PROFILE_TEMP,
|
||||
|
||||
PROFILE_MEMORY_ALLOC,
|
||||
PROFILE_FILE_UTILS,
|
||||
PROFILE_BUFFER_ALLOC,
|
||||
PROFILE_CHUNK_ALLOC,
|
||||
|
|
|
|||
14
log/Stats.h
14
log/Stats.h
|
|
@ -16,6 +16,8 @@
|
|||
DEBUG_COUNTER_DRIVE_READ,
|
||||
DEBUG_COUNTER_DRIVE_WRITE,
|
||||
|
||||
DEBUG_COUNTER_THREAD,
|
||||
|
||||
DEBUG_COUNTER_GPU_VERTEX_UPLOAD,
|
||||
DEBUG_COUNTER_GPU_UNIFORM_UPLOAD,
|
||||
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);
|
||||
}
|
||||
|
||||
inline
|
||||
void log_decrement(int32 id, int64 by = 1) noexcept
|
||||
{
|
||||
if (!_stats_counter) {
|
||||
return;
|
||||
}
|
||||
|
||||
atomic_sub_acquire(&_stats_counter[id], by);
|
||||
}
|
||||
|
||||
inline
|
||||
void log_counter(int32 id, int64 value) noexcept
|
||||
{
|
||||
|
|
@ -63,11 +75,13 @@ void log_counter(int32 id, int64 value) noexcept
|
|||
#if (!DEBUG && !INTERNAL) || RELEASE
|
||||
#define LOG_INCREMENT(a) ((void) 0)
|
||||
#define LOG_INCREMENT_BY(a, b) ((void) 0)
|
||||
#define LOG_DECREMENT(a) ((void) 0)
|
||||
#define LOG_COUNTER(a, b) ((void) 0)
|
||||
#define RESET_COUNTER(a) ((void) 0)
|
||||
#else
|
||||
#define LOG_INCREMENT(a) log_increment((a), 1)
|
||||
#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 RESET_COUNTER(a) reset_counter((a))
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -111,8 +111,8 @@ f32 evaluator_evaluate_function(const char* name, const char* args);
|
|||
|
||||
// Shunting-yard algorithm to evaluate the expression
|
||||
f32 evaluator_evaluate_expression(const char* expr) {
|
||||
EvaluatorOperatorStack operators = { .top = -1 };
|
||||
EvaluatorValueStack values = { .top = -1 };
|
||||
EvaluatorOperatorStack operators = { .items = {}, .top = -1 };
|
||||
EvaluatorValueStack values = { .items = {}, .top = -1 };
|
||||
|
||||
const char* ptr = expr;
|
||||
while (*ptr) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
memset(buf->memory, 0, buf->size);
|
||||
|
||||
LOG_1("Allocated ChunkMemory: %n B", {{LOG_DATA_UINT64, &buf->size}});
|
||||
}
|
||||
|
||||
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
|
||||
+ alignment * 2; // overhead for alignment
|
||||
|
||||
// @bug what if an alignment is defined?
|
||||
buf->memory = data;
|
||||
buf->memory = (byte *) ROUND_TO_NEAREST((uintptr_t) data, alignment);
|
||||
|
||||
buf->count = count;
|
||||
buf->size = size;
|
||||
|
|
@ -307,7 +304,7 @@ int32 chunk_reserve(ChunkMemory* buf, uint32 elements = 1) noexcept
|
|||
}
|
||||
|
||||
if (free_element < 0) {
|
||||
ASSERT_SIMPLE(false);
|
||||
LOG_3("No free chunk memory index found");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -60,8 +60,6 @@ void ring_alloc(RingMemory* ring, uint64 size, uint32 alignment = 64)
|
|||
ring->alignment = alignment;
|
||||
|
||||
memset(ring->memory, 0, ring->size);
|
||||
|
||||
LOG_1("Allocated RingMemory: %n B", {{LOG_DATA_UINT64, &ring->size}});
|
||||
}
|
||||
|
||||
inline
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
#include "../../utils/TestUtils.h"
|
||||
#include "../../log/DebugMemory.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
|
||||
|
||||
|
|
@ -24,11 +25,16 @@
|
|||
// 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)
|
||||
|
||||
static int32 _page_size = 0;
|
||||
|
||||
inline
|
||||
void* platform_alloc(size_t size)
|
||||
{
|
||||
ssize_t page_size = sysconf(_SC_PAGESIZE);
|
||||
size = (size + sizeof(size_t) + page_size - 1) & ~(page_size - 1);
|
||||
if (!_page_size) {
|
||||
_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);
|
||||
ASSERT_SIMPLE(ptr != MAP_FAILED);
|
||||
|
|
@ -37,6 +43,7 @@ void* platform_alloc(size_t size)
|
|||
|
||||
DEBUG_MEMORY_INIT((uintptr_t) ptr, 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));
|
||||
}
|
||||
|
|
@ -44,13 +51,12 @@ void* platform_alloc(size_t size)
|
|||
inline
|
||||
void* platform_alloc_aligned(size_t size, int32 alignment)
|
||||
{
|
||||
ssize_t page_size = sysconf(_SC_PAGESIZE);
|
||||
if (alignment < page_size) {
|
||||
alignment = page_size;
|
||||
if (!_page_size) {
|
||||
_page_size = (int32) sysconf(_SC_PAGESIZE);
|
||||
}
|
||||
|
||||
size = ROUND_TO_NEAREST(size, alignment);
|
||||
size += alignment + sizeof(void *) + sizeof(size_t);
|
||||
size = ROUND_TO_NEAREST(size + sizeof(void *) + sizeof(size_t) + alignment - 1, alignment);
|
||||
size = ROUND_TO_NEAREST(size, _page_size);
|
||||
|
||||
void* ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
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.
|
||||
// We do the same with the size, which is required when freeing
|
||||
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;
|
||||
*((size_t *) ((uintptr_t) aligned_ptr - sizeof(size_t))) = size;
|
||||
|
||||
DEBUG_MEMORY_INIT((uintptr_t) aligned_ptr, size);
|
||||
LOG_INCREMENT_BY(DEBUG_COUNTER_MEM_ALLOC, size);
|
||||
LOG_3("Aligned allocated %n B", {{LOG_DATA_UINT64, &size}});
|
||||
|
||||
return aligned_ptr;
|
||||
}
|
||||
|
|
@ -92,11 +99,14 @@ void platform_aligned_free(void** aligned_ptr) {
|
|||
inline
|
||||
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);
|
||||
ASSERT_SIMPLE(*fd != -1);
|
||||
|
||||
ssize_t page_size = sysconf(_SC_PAGESIZE);
|
||||
size = (size + sizeof(size_t) + page_size - 1) & ~(page_size - 1);
|
||||
size = ROUND_TO_NEAREST(size + sizeof(size_t), _page_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);
|
||||
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));
|
||||
}
|
||||
|
|
@ -117,11 +128,11 @@ void* platform_shared_open(int32* fd, const char* name, size_t size)
|
|||
*fd = shm_open(name, O_RDWR, 0666);
|
||||
ASSERT_SIMPLE(*fd != -1);
|
||||
|
||||
ssize_t page_size = sysconf(_SC_PAGESIZE);
|
||||
size = (size + sizeof(size_t) + page_size - 1) & ~(page_size - 1);
|
||||
size = ROUND_TO_NEAREST(size + sizeof(size_t), _page_size);
|
||||
|
||||
void* shm_ptr = mmap(NULL, size, PROT_READ, MAP_SHARED, *fd, 0);
|
||||
ASSERT_SIMPLE(shm_ptr);
|
||||
LOG_3("Shared opened %n B", {{LOG_DATA_UINT64, &size}});
|
||||
|
||||
*((size_t *) shm_ptr) = size;
|
||||
|
||||
|
|
|
|||
|
|
@ -39,9 +39,17 @@ int32 coms_pthread_create(coms_pthread_t* thread, void*, ThreadJobFunc start_rou
|
|||
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;
|
||||
*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;
|
||||
}
|
||||
|
||||
|
|
@ -50,9 +58,13 @@ int32 coms_pthread_create(coms_pthread_t* thread, void*, ThreadJobFunc start_rou
|
|||
|
||||
FORCE_INLINE
|
||||
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
|
||||
: 0;
|
||||
|
||||
platform_aligned_free((void **) &thread.stack);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
FORCE_INLINE
|
||||
|
|
@ -126,7 +138,7 @@ int32 mutex_condimedwait(mutex_cond* cond, mutex* mutex, const struct timespec*)
|
|||
return 0;
|
||||
}
|
||||
|
||||
inline
|
||||
FORCE_INLINE
|
||||
int32 coms_pthread_cond_wait(mutex_cond* cond, mutex* mutex) {
|
||||
return mutex_condimedwait(cond, mutex, NULL);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,6 +34,9 @@ struct coms_pthread_rwlock_t {
|
|||
bool exclusive;
|
||||
};
|
||||
|
||||
typedef int coms_pthread_t;
|
||||
struct coms_pthread_t {
|
||||
int h;
|
||||
void* stack;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -15,15 +15,26 @@
|
|||
#include "../../utils/TestUtils.h"
|
||||
#include "../../log/DebugMemory.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
|
||||
|
||||
static int32 _page_size = 0;
|
||||
|
||||
inline
|
||||
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);
|
||||
DEBUG_MEMORY_INIT((uintptr_t) ptr, size);
|
||||
LOG_INCREMENT_BY(DEBUG_COUNTER_MEM_ALLOC, size);
|
||||
LOG_3("Allocated %n B", {{LOG_DATA_UINT64, &size}});
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
|
@ -31,7 +42,12 @@ void* platform_alloc(size_t size)
|
|||
inline
|
||||
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);
|
||||
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.
|
||||
// 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.
|
||||
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;
|
||||
|
||||
DEBUG_MEMORY_INIT((uintptr_t) aligned_ptr, size);
|
||||
LOG_INCREMENT_BY(DEBUG_COUNTER_MEM_ALLOC, size);
|
||||
LOG_3("Aligned allocated %n B", {{LOG_DATA_UINT64, &size}});
|
||||
|
||||
return aligned_ptr;
|
||||
}
|
||||
|
|
@ -67,6 +84,12 @@ void platform_aligned_free(void** aligned_ptr) {
|
|||
inline
|
||||
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);
|
||||
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);
|
||||
LOG_INCREMENT_BY(DEBUG_COUNTER_MEM_ALLOC, size);
|
||||
LOG_3("Shared allocated %n B", {{LOG_DATA_UINT64, &size}});
|
||||
|
||||
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);
|
||||
ASSERT_SIMPLE(shm_ptr);
|
||||
LOG_3("Shared opened %n B", {{LOG_DATA_UINT64, &size}});
|
||||
|
||||
return shm_ptr;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,12 @@ typedef CRITICAL_SECTION mutex;
|
|||
typedef void mutexattr_t;
|
||||
typedef void coms_pthread_condattr_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;
|
||||
|
||||
// Thread local variable Already exists in c++11
|
||||
|
|
|
|||
|
|
@ -153,7 +153,7 @@ void hashmap_alloc(HashMap* hm, int32 count, int32 element_size, int32 alignment
|
|||
);
|
||||
|
||||
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
|
||||
|
|
@ -161,21 +161,22 @@ void hashmap_alloc(HashMapRef* hmr, int32 count, int32 data_element_size, int32
|
|||
{
|
||||
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}});
|
||||
byte* data = (byte *) platform_alloc(
|
||||
byte* data = (byte *) platform_alloc_aligned(
|
||||
count * (sizeof(uint16) + element_size)
|
||||
+ CEIL_DIV(count, alignment) * sizeof(hmr->hm.buf.free)
|
||||
+ count * data_element_size
|
||||
+ count * data_element_size,
|
||||
alignment
|
||||
);
|
||||
|
||||
hmr->hm.table = (uint16 *) data;
|
||||
chunk_init(&hmr->hm.buf, data + sizeof(uint16) * count, count, element_size, 8);
|
||||
chunk_init(&hmr->data, data + hmr->hm.buf.size, count, data_element_size);
|
||||
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, alignment);
|
||||
}
|
||||
|
||||
inline
|
||||
void hashmap_free(HashMap* hm)
|
||||
{
|
||||
platform_free((void **) &hm->table);
|
||||
platform_aligned_free((void **) &hm->table);
|
||||
|
||||
hm->table = NULL;
|
||||
hm->buf.size = 0;
|
||||
|
|
@ -194,7 +195,7 @@ void hashmap_create(HashMap* hm, int32 count, int32 element_size, RingMemory* ri
|
|||
);
|
||||
|
||||
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
|
||||
|
|
@ -209,16 +210,16 @@ void hashmap_create(HashMap* hm, int32 count, int32 element_size, BufferMemory*
|
|||
);
|
||||
|
||||
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
|
||||
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}});
|
||||
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
|
||||
|
|
@ -256,6 +257,8 @@ void hashmap_insert(HashMap* hm, const char* key, int32 value) noexcept {
|
|||
int32 element = chunk_reserve(&hm->buf, 1);
|
||||
HashEntryInt32* entry = (HashEntryInt32 *) chunk_get_element(&hm->buf, element, true);
|
||||
|
||||
ASSERT_SIMPLE(((uintptr_t) entry) % 32 == 0);
|
||||
|
||||
// Ensure key length
|
||||
str_move_to_pos(&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) {
|
||||
*((int64 *) data) = SWAP_ENDIAN_LITTLE(((HashEntryInt64 *) entry)->value);
|
||||
} else {
|
||||
if (entry->value) {
|
||||
memcpy(data, entry->value, value_size);
|
||||
} else {
|
||||
memset(data, 0, value_size);
|
||||
}
|
||||
}
|
||||
data += value_size;
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -171,22 +171,23 @@ PerfectHashMap* perfect_hashmap_prepare(PerfectHashMap* hm, const char* keys, in
|
|||
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}});
|
||||
hm->map_count = count;
|
||||
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);
|
||||
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.hash_entries = (byte *) platform_alloc(
|
||||
hmr->hm.hash_entries = (byte *) platform_alloc_aligned(
|
||||
count * hmr->hm.entry_size
|
||||
+ total_data_size
|
||||
+ total_data_size,
|
||||
alignment
|
||||
);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
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(
|
||||
buf,
|
||||
count * element_size,
|
||||
0, true
|
||||
64, true
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
#ifndef COMS_SYSTEM_INFO_C
|
||||
#define COMS_SYSTEM_INFO_C
|
||||
|
||||
#include <inttypes.h>
|
||||
#if _WIN32
|
||||
#include "../platform/win32/SystemInfo.cpp"
|
||||
#elif __linux__
|
||||
|
|
@ -47,7 +48,7 @@ void system_info_render(char* buf, const SystemInfo* info) {
|
|||
"L3: Size %u Line %u\n"
|
||||
"L4: Size %u Line %u\n"
|
||||
"\n"
|
||||
"Features: %lld\n"
|
||||
"Features: %" PRId64 "\n"
|
||||
"\n"
|
||||
"GPU:\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[2].size, (uint32) info->cpu.cache[2].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_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,
|
||||
|
|
|
|||
69
test.sh
Executable file
69
test.sh
Executable 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
|
||||
|
|
@ -44,7 +44,7 @@ if "%1"=="-d" (
|
|||
REM Compile each .cpp file into an executable
|
||||
cl ^
|
||||
%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 _CRT_SECURE_NO_WARNINGS ^
|
||||
/Fo"%DESTINATION_DIR%/" /Fe"%DESTINATION_DIR%\MainTest.exe" %DEBUG_DATA% ^
|
||||
|
|
|
|||
|
|
@ -1,16 +1,18 @@
|
|||
#define UBER_TEST 1
|
||||
#define PERFORMANCE_TEST 0
|
||||
|
||||
#include "math/EvaluatorTest.cpp"
|
||||
//#include "math/EvaluatorTest.cpp"
|
||||
#include "memory/ChunkMemoryTest.cpp"
|
||||
#include "memory/RingMemoryTest.cpp"
|
||||
#include "stdlib/HashMapTest.cpp"
|
||||
#include "ui/UILayoutTest.cpp"
|
||||
#include "ui/UIThemeTest.cpp"
|
||||
//#include "ui/UILayoutTest.cpp"
|
||||
//#include "ui/UIThemeTest.cpp"
|
||||
#include "utils/BitUtilsTest.cpp"
|
||||
#include "utils/EndianUtilsTest.cpp"
|
||||
#include "utils/StringUtilsTest.cpp"
|
||||
#include "utils/MathUtilsTest.cpp"
|
||||
#include "utils/UtilsTest.cpp"
|
||||
#include "utils/TimeUtilsTest.cpp"
|
||||
|
||||
#ifdef UBER_TEST
|
||||
#ifdef main
|
||||
|
|
@ -21,16 +23,18 @@
|
|||
int main() {
|
||||
TEST_HEADER();
|
||||
|
||||
MathEvaluatorTest();
|
||||
//MathEvaluatorTest();
|
||||
MemoryChunkMemoryTest();
|
||||
MemoryRingMemoryTest();
|
||||
StdlibHashMapTest();
|
||||
UIUILayoutTest();
|
||||
UIUIThemeTest();
|
||||
//UIUILayoutTest();
|
||||
//UIUIThemeTest();
|
||||
UtilsBitUtilsTest();
|
||||
UtilsEndianUtilsTest();
|
||||
UtilsStringUtilsTest();
|
||||
UtilsMathUtilsTest();
|
||||
UtilsUtilsTest();
|
||||
UtilsTimeUtilsTest();
|
||||
|
||||
TEST_FOOTER();
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include <inttypes.h>
|
||||
#include "../architecture/Intrinsics.h"
|
||||
|
||||
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_count = -1;
|
||||
|
||||
static int32_t _test_suit_count = 0;
|
||||
static int32_t _test_global_assert_count = 0;
|
||||
static int32_t _test_global_assert_error_count = 0;
|
||||
static int32_t _test_global_count = 0;
|
||||
|
||||
static int64_t _test_start;
|
||||
static int64_t _test_total_start;
|
||||
|
||||
#define TEST_PROFILING_LOOPS 1000
|
||||
|
||||
#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("========================================================================================================================\n")
|
||||
|
||||
#define TEST_FOOTER() \
|
||||
printf("========================================================================================================================\n"); \
|
||||
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_count, \
|
||||
_test_global_assert_count - _test_global_assert_error_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
|
||||
#define TEST_INIT_HEADER() (void)0
|
||||
|
|
@ -70,11 +74,7 @@ int64_t test_start_time()
|
|||
|
||||
double test_duration_time(int64_t start)
|
||||
{
|
||||
LARGE_INTEGER frequency, end;
|
||||
QueryPerformanceFrequency(&frequency);
|
||||
QueryPerformanceCounter(&end);
|
||||
|
||||
return (double)(end.QuadPart - start) * 1e9 / frequency.QuadPart;
|
||||
return (double)(test_start_time() - start) / frequency.QuadPart;
|
||||
}
|
||||
|
||||
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)
|
||||
#else
|
||||
#include "../platform/linux/ExceptionHandler.h"
|
||||
#include <time.h>
|
||||
void test_exception_handler(int signum)
|
||||
{
|
||||
printf("Received signal: %d\n", signum);
|
||||
|
|
@ -120,7 +121,7 @@ void test_exception_handler(int signum)
|
|||
|
||||
int64_t test_start_time()
|
||||
{
|
||||
struct timespec start, end;
|
||||
struct timespec start;
|
||||
clock_gettime(CLOCK_MONOTONIC, &start);
|
||||
|
||||
return start.tv_sec * 1e9 + start.tv_nsec;
|
||||
|
|
@ -128,14 +129,9 @@ int64_t test_start_time()
|
|||
|
||||
double test_duration_time(int64_t start)
|
||||
{
|
||||
LARGE_INTEGER frequency, end;
|
||||
QueryPerformanceFrequency(&frequency);
|
||||
QueryPerformanceCounter(&end);
|
||||
|
||||
return (double)(end.tv_sec * 1e9 + end.tv_nsec - start);
|
||||
return (double) (test_start_time() - start);
|
||||
}
|
||||
|
||||
#include <time.h>
|
||||
double test_measure_func_time_ns(void (*func)(volatile void *), volatile void *para)
|
||||
{
|
||||
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) \
|
||||
do \
|
||||
{ \
|
||||
++_test_suit_count; \
|
||||
TEST_INIT_HEADER(); \
|
||||
setvbuf(stdout, NULL, _IONBF, 0); \
|
||||
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) \
|
||||
{ \
|
||||
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__); \
|
||||
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 \
|
||||
{ \
|
||||
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__); \
|
||||
} \
|
||||
fflush(stdout); \
|
||||
|
|
@ -214,7 +211,35 @@ double test_measure_func_time_ns(void (*func)(volatile void *), volatile void *p
|
|||
++_test_global_assert_error_count; \
|
||||
snprintf( \
|
||||
_test_log[_test_assert_error_count++], 1024, \
|
||||
"%4i: %lld != %lld", __LINE__, (int64)(a), (int64)(b)); \
|
||||
"%4i: %" PRId64 " != %" PRId64, __LINE__, (int64)(a), (int64)(b)); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define ASSERT_GREATER_THAN(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: %.3f <= %.3f", __LINE__, (float)(a), (float)(b)); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define ASSERT_LESSER_THAN(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: %.3f >= %.3f", __LINE__, (float)(a), (float)(b)); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
|
@ -228,7 +253,7 @@ double test_measure_func_time_ns(void (*func)(volatile void *), volatile void *p
|
|||
++_test_global_assert_error_count; \
|
||||
snprintf( \
|
||||
_test_log[_test_assert_error_count++], 1024, \
|
||||
"%4i: %lld == %lld", __LINE__, (int64)(a), (int64)(b)); \
|
||||
"%4i: %" PRId64 " == %" PRId64, __LINE__, (int64)(a), (int64)(b)); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
|
@ -329,7 +354,7 @@ double test_measure_func_time_ns(void (*func)(volatile void *), volatile void *p
|
|||
{ \
|
||||
++_test_global_assert_error_count; \
|
||||
snprintf(_test_log[_test_global_assert_error_count], 1024, \
|
||||
"%4i: mismatch at offset %lld", __LINE__, i); \
|
||||
"%4i: mismatch at offset %" PRId64, __LINE__, i); \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
|
|
@ -371,7 +396,7 @@ double test_measure_func_time_ns(void (*func)(volatile void *), volatile void *p
|
|||
++_test_global_assert_error_count; \
|
||||
snprintf( \
|
||||
_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); \
|
||||
} \
|
||||
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; \
|
||||
snprintf( \
|
||||
_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); \
|
||||
} \
|
||||
} while (0)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#include <string.h>
|
||||
#include "../TestFramework.h"
|
||||
#include "../../utils/EndianUtils.h"
|
||||
|
||||
|
|
@ -85,7 +86,7 @@ static void test_endian_swap_uint64() {
|
|||
static void test_endian_swap_int64() {
|
||||
int64 val = 0x123456789ABCDEF0;
|
||||
int64 swapped = endian_swap(val);
|
||||
ASSERT_EQUALS(0xF0DEBC9A78563412, swapped);
|
||||
ASSERT_EQUALS((int64) 0xF0DEBC9A78563412, swapped);
|
||||
}
|
||||
|
||||
static void test_endian_swap_float() {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
#if PERFORMANCE_TEST
|
||||
// Performance tests for f32 (float) approximate functions
|
||||
static void _sin_approx_f32(volatile void* 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_CYCLE(_pow_approx_f64, _pow_f64, 5.0);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef UBER_TEST
|
||||
#ifdef main
|
||||
|
|
@ -592,6 +594,7 @@ int main() {
|
|||
TEST_RUN(test_log_approx_f64);
|
||||
TEST_RUN(test_pow_approx_f64);
|
||||
|
||||
#if PERFORMANCE_TEST
|
||||
// Run performance tests for f32 functions
|
||||
TEST_RUN(test_sin_approx_performance_f32);
|
||||
TEST_RUN(test_cos_approx_performance_f32);
|
||||
|
|
@ -617,6 +620,7 @@ int main() {
|
|||
TEST_RUN(test_exp_approx_performance_f64);
|
||||
TEST_RUN(test_log_approx_performance_f64);
|
||||
TEST_RUN(test_pow_approx_performance_f64);
|
||||
#endif
|
||||
|
||||
TEST_FINALIZE();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include "../TestFramework.h"
|
||||
#include "../../utils/StringUtils.h"
|
||||
#include "../../utils/EndianUtils.h"
|
||||
|
|
@ -83,6 +85,7 @@ static void test_str_length()
|
|||
ASSERT_EQUALS(str_length("2asdf dw"), 8);
|
||||
}
|
||||
|
||||
#if PERFORMANCE_TEST
|
||||
static void _str_length(volatile void* 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_CYCLE(_str_length, _strlen, 5.0);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if PERFORMANCE_TEST
|
||||
static void _str_is_alphanum(volatile void* val) {
|
||||
bool* res = (bool *) val;
|
||||
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_CYCLE(_str_is_alphanum, _isalnum, 5.0);
|
||||
}
|
||||
#endif
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
#if PERFORMANCE_TEST
|
||||
static void _sprintf_fast(volatile void* 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_CYCLE(_sprintf_fast, _sprintf, 5.0);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void test_str_to_float()
|
||||
{
|
||||
|
|
@ -179,8 +187,6 @@ static void test_str_to_float()
|
|||
#define main UtilsStringUtilsTest
|
||||
#endif
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
int main() {
|
||||
TEST_INIT(100);
|
||||
|
||||
|
|
@ -196,9 +202,11 @@ int main() {
|
|||
TEST_RUN(test_str_length);
|
||||
TEST_RUN(test_str_to_float);
|
||||
|
||||
#if PERFORMANCE_TEST
|
||||
TEST_RUN(test_str_length_performance);
|
||||
TEST_RUN(test_str_is_alphanum_performance);
|
||||
TEST_RUN(test_sprintf_fast_performance);
|
||||
#endif
|
||||
|
||||
TEST_FINALIZE();
|
||||
|
||||
|
|
|
|||
34
tests/utils/TimeUtilsTest.cpp
Normal file
34
tests/utils/TimeUtilsTest.cpp
Normal 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;
|
||||
}
|
||||
|
|
@ -5,7 +5,6 @@ static void test_is_equal() {
|
|||
uint8_t region1[] = {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 region4[] = {0x01, 0x02, 0x03, 0x04};
|
||||
|
||||
// Test equal regions
|
||||
ASSERT_TRUE(is_equal(region1, region2, sizeof(region1)));
|
||||
|
|
@ -54,6 +53,7 @@ static void test_is_empty() {
|
|||
ASSERT_TRUE(is_empty(region1, 0));
|
||||
}
|
||||
|
||||
#if PERFORMANCE_TEST
|
||||
static void _is_equal(volatile void* 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_CYCLE(_is_empty2, _memcmp_empty2, 10.0);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef UBER_TEST
|
||||
#ifdef main
|
||||
|
|
@ -138,8 +139,10 @@ int main() {
|
|||
TEST_RUN(test_is_equal);
|
||||
TEST_RUN(test_is_empty);
|
||||
|
||||
#if PERFORMANCE_TEST
|
||||
TEST_RUN(test_is_equal_performance);
|
||||
TEST_RUN(test_is_empty_performance);
|
||||
#endif
|
||||
|
||||
TEST_FINALIZE();
|
||||
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ for /R %%f in (*Test.cpp) do (
|
|||
REM Compile each .cpp file into an executable
|
||||
cl ^
|
||||
%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 _CRT_SECURE_NO_WARNINGS ^
|
||||
/Fo"%DESTINATION_DIR%/" /Fe"%DESTINATION_DIR%\%BASENAME%.exe" %DEBUG_DATA% ^
|
||||
|
|
|
|||
70
tests_iter.sh
Executable file
70
tests_iter.sh
Executable 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
|
||||
|
|
@ -12,6 +12,7 @@
|
|||
#include <stdio.h>
|
||||
#include "../stdlib/Types.h"
|
||||
#include "../log/Log.h"
|
||||
#include "../log/Stats.h"
|
||||
#include "Atomic.h"
|
||||
|
||||
#if _WIN32
|
||||
|
|
@ -26,14 +27,21 @@
|
|||
void thread_create(Worker* worker, ThreadJobFunc routine, void* arg)
|
||||
{
|
||||
LOG_1("Thread starting");
|
||||
|
||||
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)
|
||||
{
|
||||
atomic_set_release(&worker->state, 0);
|
||||
coms_pthread_join(worker->thread, NULL);
|
||||
|
||||
LOG_1("Thread ended");
|
||||
LOG_DECREMENT(DEBUG_COUNTER_THREAD);
|
||||
LOG_2("%d threads running", {{LOG_DATA_INT64, (void *) &_stats_counter[DEBUG_COUNTER_THREAD]}});
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -16,6 +16,7 @@
|
|||
#include "Thread.h"
|
||||
#include "Atomic.h"
|
||||
#include "ThreadJob.h"
|
||||
#include "../log/DebugContainer.h"
|
||||
|
||||
struct ThreadPool {
|
||||
// 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 working_cond;
|
||||
|
||||
// By design the working_cnt is <= thread_cnt
|
||||
alignas(4) atomic_32 int32 working_cnt;
|
||||
alignas(4) atomic_32 int32 thread_cnt;
|
||||
|
||||
int32 size;
|
||||
int32 element_size;
|
||||
|
||||
// 0 = down, 1 = shutting down, 2 = running
|
||||
alignas(4) atomic_32 int32 state;
|
||||
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
|
||||
THREAD_RETURN thread_pool_worker(void* 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;
|
||||
|
||||
while (true) {
|
||||
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);
|
||||
}
|
||||
|
||||
if (pool->state == 1) {
|
||||
if (pool->state < 2) {
|
||||
mutex_unlock(&pool->work_mutex);
|
||||
|
||||
break;
|
||||
|
|
@ -55,7 +77,8 @@ THREAD_RETURN thread_pool_worker(void* arg)
|
|||
|
||||
// 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)
|
||||
// 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);
|
||||
mutex_unlock(&pool->work_mutex);
|
||||
|
||||
|
|
@ -63,7 +86,7 @@ THREAD_RETURN thread_pool_worker(void* arg)
|
|||
continue;
|
||||
}
|
||||
|
||||
atomic_increment_relaxed(&pool->working_cnt);
|
||||
atomic_increment_release(&pool->working_cnt);
|
||||
atomic_set_release(&work->state, 2);
|
||||
LOG_2("ThreadPool worker started");
|
||||
work->func(work);
|
||||
|
|
@ -79,15 +102,21 @@ THREAD_RETURN thread_pool_worker(void* arg)
|
|||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
atomic_decrement_relaxed(&pool->thread_cnt);
|
||||
|
||||
LOG_2("Thread pool worker shutting down");
|
||||
LOG_DECREMENT(DEBUG_COUNTER_THREAD);
|
||||
|
||||
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->working_cond, NULL);
|
||||
|
||||
pool->state = 2;
|
||||
|
||||
coms_pthread_t thread;
|
||||
for (pool->size = 0; pool->size < thread_count; ++pool->size) {
|
||||
coms_pthread_create(&thread, NULL, thread_pool_worker, pool);
|
||||
coms_pthread_detach(thread);
|
||||
}
|
||||
|
||||
LOG_2("%d threads running", {{LOG_DATA_INT64, (void *) &_stats_counter[DEBUG_COUNTER_THREAD]}});
|
||||
}
|
||||
|
||||
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->working_cond, NULL);
|
||||
|
||||
pool->state = 2;
|
||||
|
||||
coms_pthread_t thread;
|
||||
for (pool->size = 0; pool->size < thread_count; ++pool->size) {
|
||||
coms_pthread_create(&thread, NULL, thread_pool_worker, pool);
|
||||
coms_pthread_detach(thread);
|
||||
}
|
||||
|
||||
LOG_2("%d threads running", {{LOG_DATA_INT64, (void *) &_stats_counter[DEBUG_COUNTER_THREAD]}});
|
||||
}
|
||||
|
||||
void thread_pool_wait(ThreadPool* pool)
|
||||
{
|
||||
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);
|
||||
}
|
||||
mutex_unlock(&pool->work_mutex);
|
||||
|
|
@ -184,6 +223,9 @@ void thread_pool_destroy(ThreadPool* pool)
|
|||
mutex_destroy(&pool->work_mutex);
|
||||
coms_pthread_cond_destroy(&pool->work_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)
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ void ui_layout_assign_children(
|
|||
break;
|
||||
}
|
||||
|
||||
str_copy_move_until(&pos, block_name, ":");
|
||||
str_copy_move_until(block_name, &pos, ":");
|
||||
str_move_past(&pos, '\n');
|
||||
|
||||
// Children array (located after the UIElement)
|
||||
|
|
@ -182,8 +182,8 @@ void layout_from_file_txt(
|
|||
continue;
|
||||
}
|
||||
|
||||
str_copy_move_until(&pos, block_name, ":"); ++pos;
|
||||
str_copy_move_until(&pos, block_type, " \r\n");
|
||||
str_copy_move_until(block_name, &pos, ":"); ++pos;
|
||||
str_copy_move_until(block_type, &pos, " \r\n");
|
||||
str_move_past(&pos, '\n');
|
||||
|
||||
// Insert new element
|
||||
|
|
@ -235,7 +235,7 @@ void layout_from_file_txt(
|
|||
continue;
|
||||
}
|
||||
|
||||
str_copy_move_until(&pos, block_name, ":");
|
||||
str_copy_move_until(block_name, &pos, ":");
|
||||
str_move_past(&pos, '\n');
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
str_copy_move_until(&pos, block_name, ":");
|
||||
str_copy_move_until(block_name, &pos, ":");
|
||||
str_move_past(&pos, '\n');
|
||||
root_children[child++] = ((HashEntryInt32 *) hashmap_get_entry(&layout->hash_map, block_name))->value;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -137,7 +137,7 @@ void theme_from_file_txt(
|
|||
// Handle new group
|
||||
/////////////////////////////////////////////////////////
|
||||
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.
|
||||
if (*block_name == '#' || *block_name == '.') {
|
||||
|
|
@ -164,7 +164,7 @@ void theme_from_file_txt(
|
|||
|
||||
// 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
|
||||
str_skip_list(&pos, " \t:", sizeof(" \t:") - 1);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
char value[64];
|
||||
|
||||
str_copy_until(pos, value, "\r\n");
|
||||
str_copy_until(value, pos, "\r\n");
|
||||
|
||||
if (str_is_integer(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);
|
||||
attr->datatype = UI_ATTRIBUTE_DATA_TYPE_V4_F32;
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
#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_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_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))
|
||||
|
|
@ -41,7 +41,7 @@
|
|||
#define BIT_SET_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_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
|
||||
#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))
|
||||
|
|
|
|||
|
|
@ -941,7 +941,7 @@ void str_copy_long(char* __restrict dest, const char* __restrict src) noexcept
|
|||
}
|
||||
|
||||
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') {
|
||||
*dest++ = **src;
|
||||
|
|
@ -952,7 +952,7 @@ void str_copy_move_until(const char** __restrict src, char* __restrict dest, cha
|
|||
}
|
||||
|
||||
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);
|
||||
|
||||
|
|
@ -1055,7 +1055,7 @@ str_concat_append(char* dst, size_t dst_length, const char* src) noexcept
|
|||
|
||||
inline int64
|
||||
str_concat_new(
|
||||
char* dst,
|
||||
char* __restrict dst,
|
||||
const char* src1, size_t src1_length,
|
||||
const char* src2, size_t src2_length
|
||||
) noexcept {
|
||||
|
|
@ -1092,7 +1092,7 @@ void str_concat_append(
|
|||
}
|
||||
|
||||
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);
|
||||
memcpy(dst, src, src_len);
|
||||
|
|
@ -1139,7 +1139,7 @@ char* strtok(char* str, const char* __restrict delim, char* *key) noexcept {
|
|||
}
|
||||
|
||||
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)?
|
||||
// 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
|
||||
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) {
|
||||
const char* p1 = haystack;
|
||||
|
|
@ -1320,7 +1320,7 @@ int32 str_compare_caseless(const char* str1, const char* str2, size_t n) noexcep
|
|||
}
|
||||
|
||||
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) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -1434,7 +1434,7 @@ void str_move_to_pos(const char** str, int32 pos) noexcept
|
|||
}
|
||||
|
||||
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') {
|
||||
++(*str);
|
||||
|
|
@ -1549,7 +1549,7 @@ void str_skip_until_list(const char** __restrict str, const char* __restrict del
|
|||
}
|
||||
|
||||
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 == '#') {
|
||||
++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 %
|
||||
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_start(args, format);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user