fixed arena + some niceties

This commit is contained in:
snarmph 2025-10-21 10:31:58 +02:00
parent df0187bdb3
commit b601ace92b
3 changed files with 258 additions and 54 deletions

97
colla.c
View file

@ -762,6 +762,11 @@ strview_t istr_get_line(instream_t *ctx) {
return line; return line;
} }
strview_t istr_get_word(instream_t *ctx) {
strview_t word = istr_get_view_either(ctx, strv(" \t\v\r\n"));
return word;
}
// == OUTPUT STREAM ================================================ // == OUTPUT STREAM ================================================
outstream_t ostr_init(arena_t *exclusive_arena) { outstream_t ostr_init(arena_t *exclusive_arena) {
@ -802,6 +807,11 @@ strview_t ostr_as_view(outstream_t *ctx) {
return strv(ctx->beg, ostr_tell(ctx)); return strv(ctx->beg, ostr_tell(ctx));
} }
void ostr_rewind(outstream_t *ctx, usize from_beg) {
if (!ctx->arena) return;
ctx->arena->cur = (u8*)ctx->beg + from_beg;
}
void ostr_pop(outstream_t *ctx, usize count) { void ostr_pop(outstream_t *ctx, usize count) {
if (!ctx->arena) return; if (!ctx->arena) return;
arena_pop(ctx->arena, count); arena_pop(ctx->arena, count);
@ -1183,7 +1193,7 @@ static void *arena__alloc_common(const arena_alloc_desc_t *desc) {
if (new_cur > page_end) { if (new_cur > page_end) {
usize page_size = os_get_system_info().page_size; usize page_size = os_get_system_info().page_size;
usize prev_page = os_pad_to_page(allocated - page_size); usize prev_page = os_pad_to_page(allocated ? allocated - page_size : 0);
usize next_page = os_pad_to_page(new_cur); usize next_page = os_pad_to_page(new_cur);
usize num_of_pages = (next_page - prev_page) / page_size; usize num_of_pages = (next_page - prev_page) / page_size;
colla_assert(num_of_pages > 0); colla_assert(num_of_pages > 0);
@ -1257,13 +1267,13 @@ os_log_colour_e os_log_level_colours[LOG_COL__COUNT] = {
[LOG_FATAL] = LOG_COL_RED, [LOG_FATAL] = LOG_COL_RED,
}; };
os_log_options_e log_opts = OS_LOG_SIMPLE; os_log_options_e os__log_opts = OS_LOG_SIMPLE;
log_callback_t log_cbs[COLLA_LOG_MAX_CALLBACKS] = {0}; log_callback_t os__log_cbs[COLLA_LOG_MAX_CALLBACKS] = {0};
int log_cbs_count = 0; i64 os__log_cbs_count = 0;
void os_log__stdout(log_event_t *ev) { void os_log__stdout(log_event_t *ev) {
bool notime = log_opts & OS_LOG_NOTIME; bool notime = os__log_opts & OS_LOG_NOTIME;
bool nofile = log_opts & OS_LOG_NOFILE; bool nofile = os__log_opts & OS_LOG_NOFILE;
if (!notime) { if (!notime) {
os_log_set_colour(LOG_COL_DARK_GREY); os_log_set_colour(LOG_COL_DARK_GREY);
@ -1298,8 +1308,8 @@ void os_log__fp(log_event_t *ev) {
oshandle_t fp = {0}; oshandle_t fp = {0};
fp.data = (uptr)ev->udata; fp.data = (uptr)ev->udata;
bool notime = log_opts & OS_LOG_NOTIME; bool notime = os__log_opts & OS_LOG_NOTIME;
bool nofile = log_opts & OS_LOG_NOFILE; bool nofile = os__log_opts & OS_LOG_NOFILE;
if (!notime) { if (!notime) {
os_file_print( os_file_print(
@ -1324,9 +1334,17 @@ void os_log__fp(log_event_t *ev) {
os_file_putc(fp, '\n'); os_file_putc(fp, '\n');
} }
void os_log_set_options(os_log_options_e opt) {
os__log_opts = opt;
}
os_log_options_e os_log_get_options(void) {
return os__log_opts;
}
void os_log_add_callback(log_callback_t cb) { void os_log_add_callback(log_callback_t cb) {
colla_assert(log_cbs_count < arrlen(log_cbs)); colla_assert(os__log_cbs_count < arrlen(os__log_cbs));
log_cbs[log_cbs_count++] = cb; os__log_cbs[os__log_cbs_count++] = cb;
} }
void os_log_add_fp(oshandle_t fp, os_log_level_e level) { void os_log_add_fp(oshandle_t fp, os_log_level_e level) {
@ -1362,15 +1380,15 @@ void os_log_printv(const char *file, int line, os_log_level_e level, const char
}; };
localtime_s(&log_time, &t); localtime_s(&log_time, &t);
for (int i = 0; i < log_cbs_count; ++i) { for (int i = 0; i < os__log_cbs_count; ++i) {
if (log_cbs[i].level > level) { if (os__log_cbs[i].level > level) {
continue; continue;
} }
ev.udata = log_cbs[i].udata; ev.udata = os__log_cbs[i].udata;
log_cbs[i].fn(&ev); os__log_cbs[i].fn(&ev);
} }
bool nocrash = log_opts & OS_LOG_NOCRASH; bool nocrash = os__log_opts & OS_LOG_NOCRASH;
if (!nocrash && level == LOG_FATAL) { if (!nocrash && level == LOG_FATAL) {
os_abort(1); os_abort(1);
@ -1544,11 +1562,55 @@ usize os_pad_to_page(usize byte_count) {
return byte_count + padding; return byte_count + padding;
} }
// == THREAD ====================================
void os_barrier_sync(os_barrier_t *b) {
atomic_inc_i64(&b->thread_value);
while (true) {
if (atomic_cmp_i64(&b->has_completed, 1, 1)) {
break;
}
i64 completed =
atomic_cmp_i64(
&b->thread_value,
b->thread_count,
b->thread_count
) == b->thread_count;
if (completed) {
atomic_set_i64(&b->has_completed, completed);
break;
}
}
if (atomic_dec_i64(&b->thread_value) == 0) {
b->has_completed = 0;
}
}
i64range_t os_lane_range(u64 values_count) {
i64 thread_id = os_thread_id;
i64 thread_count = os_thread_count;
i64 values_per_thread = values_count / thread_count;
i64 leftover_values_count = values_count % thread_count;
bool thread_has_leftover = thread_id < leftover_values_count;
i64 leftover_before_this_thread_id = thread_has_leftover ?
thread_id : leftover_values_count;
i64 thread_first_value =
values_per_thread * thread_id + leftover_before_this_thread_id;
i64 thread_last_value =
thread_first_value + values_per_thread + thread_has_leftover;
return (i64range_t){ thread_first_value, thread_last_value };
}
#if !COLLA_NO_CONDITION_VARIABLE #if !COLLA_NO_CONDITION_VARIABLE
// == JOB QUEUE ================================= // == JOB QUEUE =================================
int jq__worker_function(u64 id, void *udata) { int jq__worker_function(u64 thread_id, void *udata) {
COLLA_UNUSED(thread_id);
job_queue_t *q = udata; job_queue_t *q = udata;
// TODO: is this safe to not have an atomic variable? // TODO: is this safe to not have an atomic variable?
@ -1617,6 +1679,7 @@ void jq_cleanup(job_queue_t *queue) {
void jq_push(arena_t *arena, job_queue_t *queue, job_func_f *func, void *userdata) { void jq_push(arena_t *arena, job_queue_t *queue, job_func_f *func, void *userdata) {
os_mutex_lock(queue->mutex); os_mutex_lock(queue->mutex);
job_t *job = queue->freelist; job_t *job = queue->freelist;
list_pop(queue->freelist);
if (!job) { if (!job) {
job = alloc(arena, job_t); job = alloc(arena, job_t);
} }
@ -1633,7 +1696,7 @@ job_t *jq_pop_job(job_queue_t *queue) {
while (!queue->jobs && !queue->should_stop && !queue->stop_when_finished) { while (!queue->jobs && !queue->should_stop && !queue->stop_when_finished) {
os_cond_wait(queue->condvar, queue->mutex, OS_WAIT_INFINITE); os_cond_wait(queue->condvar, queue->mutex, OS_WAIT_INFINITE);
} }
job = queue->jobs; job = queue->jobs ;
list_pop(queue->jobs); list_pop(queue->jobs);
os_mutex_unlock(queue->mutex); os_mutex_unlock(queue->mutex);
return job; return job;

110
colla.h
View file

@ -1,37 +1,24 @@
#ifndef COLLA_HEADER #ifndef COLLA_HEADER
#define COLLA_HEADER #define COLLA_HEADER
#if defined(_WIN32)
#define COLLA_WIN 1
#define COLLA_OSX 0
#define COLLA_LIN 0
#define COLLA_EMC 0
#elif defined(__EMSCRIPTEN__)
#define COLLA_WIN 0
#define COLLA_OSX 0
#define COLLA_LIN 0
#define COLLA_EMC 1
#elif defined(__linux__)
#define COLLA_WIN 0
#define COLLA_OSX 0
#define COLLA_LIN 1
#define COLLA_EMC 0
#elif defined(__APPLE__)
#define COLLA_WIN 0
#define COLLA_OSX 1
#define COLLA_LIN 0
#define COLLA_EMC 0
#endif
#if COLLA_LIN
#define _FILE_OFFSET_BITS 1
#endif
#include <stdint.h> #include <stdint.h>
#include <stddef.h> #include <stddef.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdarg.h> #include <stdarg.h>
#include <uchar.h>
// when where compiling with tcc, we need to do a couple things outselves
#ifdef __TINYC__
typedef unsigned short char16_t;
// NOT SAFE, afaik tcc doesn't support thread_local
#ifndef thread_local
#define thread_local
#endif
#else
#include <uchar.h>
#ifndef thread_local
#define thread_local _Thread_local
#endif
#endif
// LIBC FUNCTIONS /////////////////////////////// // LIBC FUNCTIONS ///////////////////////////////
@ -72,6 +59,28 @@ void colla_cleanup(void);
#define COLLA_RELEASE 1 #define COLLA_RELEASE 1
#endif #endif
#if defined(_WIN32)
#define COLLA_WIN 1
#define COLLA_OSX 0
#define COLLA_LIN 0
#define COLLA_EMC 0
#elif defined(__EMSCRIPTEN__)
#define COLLA_WIN 0
#define COLLA_OSX 0
#define COLLA_LIN 0
#define COLLA_EMC 1
#elif defined(__linux__)
#define COLLA_WIN 0
#define COLLA_OSX 0
#define COLLA_LIN 1
#define COLLA_EMC 0
#elif defined(__APPLE__)
#define COLLA_WIN 0
#define COLLA_OSX 1
#define COLLA_LIN 0
#define COLLA_EMC 0
#endif
#if defined(__COSMOPOLITAN__) #if defined(__COSMOPOLITAN__)
#define COLLA_COSMO 1 #define COLLA_COSMO 1
#else #else
@ -536,6 +545,7 @@ strview_t istr_get_view(instream_t *ctx, char delim);
strview_t istr_get_view_either(instream_t *ctx, strview_t chars); strview_t istr_get_view_either(instream_t *ctx, strview_t chars);
strview_t istr_get_view_len(instream_t *ctx, usize len); strview_t istr_get_view_len(instream_t *ctx, usize len);
strview_t istr_get_line(instream_t *ctx); strview_t istr_get_line(instream_t *ctx);
strview_t istr_get_word(instream_t *ctx);
// OUTPUT STREAM //////////////////////////////// // OUTPUT STREAM ////////////////////////////////
@ -554,6 +564,7 @@ char ostr_back(outstream_t *ctx);
str_t ostr_to_str(outstream_t *ctx); str_t ostr_to_str(outstream_t *ctx);
strview_t ostr_as_view(outstream_t *ctx); strview_t ostr_as_view(outstream_t *ctx);
void ostr_rewind(outstream_t *ctx, usize from_beg);
void ostr_pop(outstream_t *ctx, usize count); void ostr_pop(outstream_t *ctx, usize count);
void ostr_print(outstream_t *ctx, const char *fmt, ...); void ostr_print(outstream_t *ctx, const char *fmt, ...);
@ -798,6 +809,8 @@ typedef enum {
OS_LOG_SIMPLE = OS_LOG_NOTIME | OS_LOG_NOFILE, OS_LOG_SIMPLE = OS_LOG_NOTIME | OS_LOG_NOFILE,
} os_log_options_e; } os_log_options_e;
void os_log_set_options(os_log_options_e opt);
os_log_options_e os_log_get_options(void);
void os_log_add_callback(log_callback_t cb); void os_log_add_callback(log_callback_t cb);
void os_log_add_fp(oshandle_t fp, os_log_level_e level); void os_log_add_fp(oshandle_t fp, os_log_level_e level);
void os_log_print(const char *file, int line, os_log_level_e level, const char *fmt, ...); void os_log_print(const char *file, int line, os_log_level_e level, const char *fmt, ...);
@ -808,6 +821,10 @@ void os_log_set_colour_bg(os_log_colour_e foreground, os_log_colour_e background
oshandle_t os_stdout(void); oshandle_t os_stdout(void);
oshandle_t os_stdin(void); oshandle_t os_stdin(void);
// windows specific
oshandle_t os_win_conout(void);
oshandle_t os_win_conin(void);
#define print(...) fmt_print(__VA_ARGS__) #define print(...) fmt_print(__VA_ARGS__)
#define println(...) os_log_print(__FILE__, __LINE__, LOG_BASIC, __VA_ARGS__) #define println(...) os_log_print(__FILE__, __LINE__, LOG_BASIC, __VA_ARGS__)
#define debug(...) os_log_print(__FILE__, __LINE__, LOG_DEBUG, __VA_ARGS__) #define debug(...) os_log_print(__FILE__, __LINE__, LOG_DEBUG, __VA_ARGS__)
@ -823,6 +840,8 @@ typedef enum filemode_e {
OS_FILE_WRITE = 1 << 1, OS_FILE_WRITE = 1 << 1,
} filemode_e; } filemode_e;
str_t os_path_join(arena_t *arena, strview_t left, strview_t right);
bool os_file_exists(strview_t filename); bool os_file_exists(strview_t filename);
bool os_dir_exists(strview_t folder); bool os_dir_exists(strview_t folder);
bool os_file_or_dir_exists(strview_t path); bool os_file_or_dir_exists(strview_t path);
@ -927,6 +946,22 @@ usize os_pad_to_page(usize byte_count);
// == THREAD ==================================== // == THREAD ====================================
#ifndef thread_local
#define thread_local _Thread_local
#endif
typedef struct os_barrier_t os_barrier_t;
struct os_barrier_t {
i64 thread_count;
i64 thread_value;
i64 has_completed;
};
extern thread_local i64 os_thread_id;
extern i64 os_thread_count;
void os_barrier_sync(os_barrier_t *b);
typedef int (thread_func_t)(u64 thread_id, void *userdata); typedef int (thread_func_t)(u64 thread_id, void *userdata);
oshandle_t os_thread_launch(thread_func_t func, void *userdata); oshandle_t os_thread_launch(thread_func_t func, void *userdata);
@ -935,6 +970,14 @@ bool os_thread_join(oshandle_t thread, int *code);
u64 os_thread_get_id(oshandle_t thread); u64 os_thread_get_id(oshandle_t thread);
typedef struct i64range_t i64range_t;
struct i64range_t {
i64 min;
i64 max;
};
i64range_t os_lane_range(u64 values_count);
// == MUTEX ===================================== // == MUTEX =====================================
oshandle_t os_mutex_create(void); oshandle_t os_mutex_create(void);
@ -976,6 +1019,7 @@ struct job_queue_t {
oshandle_t mutex; oshandle_t mutex;
oshandle_t condvar; oshandle_t condvar;
oshandle_t *threads; oshandle_t *threads;
i64 *thread_ids;
int thread_count; int thread_count;
}; };
@ -989,6 +1033,18 @@ job_t *jq_pop_job(job_queue_t *queue);
#endif #endif
// == ATOMICS ========================================
i64 atomic_set_i64(i64 *dest, i64 val);
i64 atomic_add_i64(i64 *dest, i64 val);
i64 atomic_and_i64(i64 *dest, i64 val);
// if (*dest == cmp) *dest = val
i64 atomic_cmp_i64(i64 *dest, i64 val, i64 cmp);
i64 atomic_inc_i64(i64 *dest);
i64 atomic_dec_i64(i64 *dest);
i64 atomic_or_i64(i64 *dest, i64 val);
i64 atomic_xor_i64(i64 *dest, i64 val);
// PARSERS ////////////////////////////////////// // PARSERS //////////////////////////////////////
// == INI ============================================ // == INI ============================================

View file

@ -161,6 +161,8 @@ struct {
os_entity_t *entity_free; os_entity_t *entity_free;
oshandle_t hstdout; oshandle_t hstdout;
oshandle_t hstdin; oshandle_t hstdin;
oshandle_t hconin;
oshandle_t hconout;
WORD default_fg; WORD default_fg;
WORD default_bg; WORD default_bg;
} w32_data = {0}; } w32_data = {0};
@ -212,15 +214,23 @@ void os_init(void) {
info->machine_name = str_from_tstr(&w32_data.arena, (tstr_t){ namebuf, namebuflen}); info->machine_name = str_from_tstr(&w32_data.arena, (tstr_t){ namebuf, namebuflen});
HANDLE hstdout = CreateFile(TEXT("CONOUT$"), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); HANDLE hconout = CreateFile(TEXT("CONOUT$"), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
HANDLE hstdin = CreateFile(TEXT("CONIN$"), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); HANDLE hconin = CreateFile(TEXT("CONIN$"), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
HANDLE hstdout = GetStdHandle(STD_OUTPUT_HANDLE);
HANDLE hstdin = GetStdHandle(STD_INPUT_HANDLE);
if (hstdout == INVALID_HANDLE_VALUE) err("couldn't open CONOUT$"); if (hstdout == INVALID_HANDLE_VALUE) err("couldn't open STD_OUTPUT_HANDLE");
else w32_data.hstdout.data = (uptr)hstdout; else w32_data.hstdout.data = (uptr)hstdout;
if (hstdin == INVALID_HANDLE_VALUE) err("couldn't open CONIN$"); if (hstdin == INVALID_HANDLE_VALUE) err("couldn't open STD_INPUT_HANDLE");
else w32_data.hstdin.data = (uptr)hstdin; else w32_data.hstdin.data = (uptr)hstdin;
if (hconout == INVALID_HANDLE_VALUE) err("couldn't open CONOUT$");
else w32_data.hconout.data = (uptr)hconout;
if (hconin == INVALID_HANDLE_VALUE) err("couldn't open CONIN$");
else w32_data.hconin.data = (uptr)hconin;
CONSOLE_SCREEN_BUFFER_INFO console_info = {0}; CONSOLE_SCREEN_BUFFER_INFO console_info = {0};
if (GetConsoleScreenBufferInfo(hstdout, &console_info)) { if (GetConsoleScreenBufferInfo(hstdout, &console_info)) {
w32_data.default_fg = console_info.wAttributes & 0x0F; w32_data.default_fg = console_info.wAttributes & 0x0F;
@ -324,6 +334,14 @@ oshandle_t os_stdin(void) {
return w32_data.hstdin; return w32_data.hstdin;
} }
oshandle_t os_win_conout(void) {
return w32_data.hconout;
}
oshandle_t os_win_conin(void) {
return w32_data.hconin;
}
// == FILE ====================================== // == FILE ======================================
#define OS_SMALL_SCRATCH() \ #define OS_SMALL_SCRATCH() \
@ -343,6 +361,25 @@ DWORD os__win_mode_to_creation(filemode_e mode) {
return OPEN_ALWAYS; return OPEN_ALWAYS;
} }
str_t os_path_join(arena_t *arena, strview_t left, strview_t right) {
if (left.len == 0) {
return str(arena, right);
}
char a = strv_back(left);
char b = strv_front(right);
if (a == '/' || a == '\\') {
left.len--;
}
if (b == '/' || b == '\\') {
right = strv_remove_prefix(right, 1);
}
return str_fmt(arena, "%v/%v", left, right);
}
bool os_file_exists(strview_t path) { bool os_file_exists(strview_t path) {
OS_SMALL_SCRATCH(); OS_SMALL_SCRATCH();
tstr_t name = strv_to_tstr(&scratch, path); tstr_t name = strv_to_tstr(&scratch, path);
@ -695,8 +732,9 @@ oshandle_t os_run_cmd_async(arena_t scratch, os_cmd_t *cmd, os_cmd_options_t *op
ostr_pop(&cmdline, 1); ostr_pop(&cmdline, 1);
ostr_putc(&cmdline, '\0'); ostr_putc(&cmdline, '\0');
strview_t cmd_view = ostr_as_view(&cmdline); str_t cmd_str = ostr_to_str(&cmdline);
str16_t command = strv_to_str16(&scratch, cmd_view); debug("(%v)", cmd_str);
str16_t command = strv_to_str16(&scratch, strv(cmd_str));
WCHAR* env = (options && options->env) ? options->env->data : NULL; WCHAR* env = (options && options->env) ? options->env->data : NULL;
@ -731,7 +769,7 @@ oshandle_t os_run_cmd_async(arena_t scratch, os_cmd_t *cmd, os_cmd_options_t *op
} }
if (!success) { if (!success) {
err("couldn't create process (%v): %v", cmd_view, os_get_error_string(os_get_last_error())); err("couldn't create process (%v): %v", cmd_str, os_get_error_string(os_get_last_error()));
return os_handle_zero(); return os_handle_zero();
} }
@ -806,11 +844,17 @@ bool os_release(void *ptr, usize size) {
// == THREAD ==================================== // == THREAD ====================================
DWORD os__win_thread_entry_point(void *ptr) { thread_local i64 os_thread_id = 0;
i64 os_thread_count = 0;
DWORD WINAPI os__win_thread_entry_point(void *ptr) {
os_entity_t *entity = (os_entity_t *)ptr; os_entity_t *entity = (os_entity_t *)ptr;
thread_func_t *func = entity->thread.func; thread_func_t *func = entity->thread.func;
void *userdata = entity->thread.userdata; void *userdata = entity->thread.userdata;
u64 id = entity->thread.id; u64 id = entity->thread.id;
os_thread_id = atomic_inc_i64(&os_thread_count) - 1;
return func(id, userdata); return func(id, userdata);
} }
@ -926,6 +970,47 @@ void os_cond_wait(oshandle_t cond, oshandle_t mutex, int milliseconds) {
#endif #endif
// == ATOMICS ========================================
#if COLLA_TCC
#undef InterlockedExchangeAdd64
#define InterlockedExchangeAdd64(dst, val) *dst += val
#endif
i64 atomic_set_i64(i64 *dest, i64 val) {
return InterlockedExchange64(dest, val);
}
i64 atomic_add_i64(i64 *dest, i64 val) {
return InterlockedExchangeAdd64(dest, val);
}
i64 atomic_and_i64(i64 *dest, i64 val) {
return InterlockedAnd64(dest, val);
}
i64 atomic_cmp_i64(i64 *dest, i64 val, i64 cmp) {
return InterlockedCompareExchange64(dest, val, cmp);
}
i64 atomic_inc_i64(i64 *dest) {
return InterlockedIncrement64(dest);
}
i64 atomic_dec_i64(i64 *dest) {
return InterlockedDecrement64(dest);
}
i64 atomic_or_i64(i64 *dest, i64 val) {
return InterlockedOr64(dest, val);
}
i64 atomic_xor_i64(i64 *dest, i64 val) {
return InterlockedXor64(dest, val);
}
#if !COLLA_NO_NET #if !COLLA_NO_NET
struct { struct {