From b601ace92b23a3db88f642bb83a5026de0867dd4 Mon Sep 17 00:00:00 2001 From: snarmph Date: Tue, 21 Oct 2025 10:31:58 +0200 Subject: [PATCH] fixed arena + some niceties --- colla.c | 97 ++++++++++++++++++++++++++++++++++++-------- colla.h | 110 +++++++++++++++++++++++++++++++++++++------------- colla_win32.c | 105 ++++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 258 insertions(+), 54 deletions(-) diff --git a/colla.c b/colla.c index 6f2ea19..22a63d0 100644 --- a/colla.c +++ b/colla.c @@ -762,6 +762,11 @@ strview_t istr_get_line(instream_t *ctx) { 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 ================================================ 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)); } +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) { if (!ctx->arena) return; 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) { 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 num_of_pages = (next_page - prev_page) / page_size; 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, }; -os_log_options_e log_opts = OS_LOG_SIMPLE; -log_callback_t log_cbs[COLLA_LOG_MAX_CALLBACKS] = {0}; -int log_cbs_count = 0; +os_log_options_e os__log_opts = OS_LOG_SIMPLE; +log_callback_t os__log_cbs[COLLA_LOG_MAX_CALLBACKS] = {0}; +i64 os__log_cbs_count = 0; void os_log__stdout(log_event_t *ev) { - bool notime = log_opts & OS_LOG_NOTIME; - bool nofile = log_opts & OS_LOG_NOFILE; + bool notime = os__log_opts & OS_LOG_NOTIME; + bool nofile = os__log_opts & OS_LOG_NOFILE; if (!notime) { os_log_set_colour(LOG_COL_DARK_GREY); @@ -1298,8 +1308,8 @@ void os_log__fp(log_event_t *ev) { oshandle_t fp = {0}; fp.data = (uptr)ev->udata; - bool notime = log_opts & OS_LOG_NOTIME; - bool nofile = log_opts & OS_LOG_NOFILE; + bool notime = os__log_opts & OS_LOG_NOTIME; + bool nofile = os__log_opts & OS_LOG_NOFILE; if (!notime) { os_file_print( @@ -1324,9 +1334,17 @@ void os_log__fp(log_event_t *ev) { 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) { - colla_assert(log_cbs_count < arrlen(log_cbs)); - log_cbs[log_cbs_count++] = cb; + colla_assert(os__log_cbs_count < arrlen(os__log_cbs)); + os__log_cbs[os__log_cbs_count++] = cb; } 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); - for (int i = 0; i < log_cbs_count; ++i) { - if (log_cbs[i].level > level) { + for (int i = 0; i < os__log_cbs_count; ++i) { + if (os__log_cbs[i].level > level) { continue; } - ev.udata = log_cbs[i].udata; - log_cbs[i].fn(&ev); + ev.udata = os__log_cbs[i].udata; + 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) { os_abort(1); @@ -1544,11 +1562,55 @@ usize os_pad_to_page(usize byte_count) { 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 // == 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; // 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) { os_mutex_lock(queue->mutex); job_t *job = queue->freelist; + list_pop(queue->freelist); if (!job) { 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) { os_cond_wait(queue->condvar, queue->mutex, OS_WAIT_INFINITE); } - job = queue->jobs; + job = queue->jobs ; list_pop(queue->jobs); os_mutex_unlock(queue->mutex); return job; diff --git a/colla.h b/colla.h index b4dd73b..ba74684 100644 --- a/colla.h +++ b/colla.h @@ -1,37 +1,24 @@ #ifndef 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 #include #include #include -#include + +// 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 + #ifndef thread_local + #define thread_local _Thread_local + #endif +#endif // LIBC FUNCTIONS /////////////////////////////// @@ -72,6 +59,28 @@ void colla_cleanup(void); #define COLLA_RELEASE 1 #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__) #define COLLA_COSMO 1 #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_len(instream_t *ctx, usize len); strview_t istr_get_line(instream_t *ctx); +strview_t istr_get_word(instream_t *ctx); // OUTPUT STREAM //////////////////////////////// @@ -554,6 +564,7 @@ char ostr_back(outstream_t *ctx); str_t ostr_to_str(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_print(outstream_t *ctx, const char *fmt, ...); @@ -798,6 +809,8 @@ typedef enum { OS_LOG_SIMPLE = OS_LOG_NOTIME | OS_LOG_NOFILE, } 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_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, ...); @@ -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_stdin(void); +// windows specific +oshandle_t os_win_conout(void); +oshandle_t os_win_conin(void); + #define print(...) fmt_print(__VA_ARGS__) #define println(...) os_log_print(__FILE__, __LINE__, LOG_BASIC, __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, } 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_dir_exists(strview_t folder); bool os_file_or_dir_exists(strview_t path); @@ -927,6 +946,22 @@ usize os_pad_to_page(usize byte_count); // == 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); 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); +typedef struct i64range_t i64range_t; +struct i64range_t { + i64 min; + i64 max; +}; + +i64range_t os_lane_range(u64 values_count); + // == MUTEX ===================================== oshandle_t os_mutex_create(void); @@ -976,6 +1019,7 @@ struct job_queue_t { oshandle_t mutex; oshandle_t condvar; oshandle_t *threads; + i64 *thread_ids; int thread_count; }; @@ -989,6 +1033,18 @@ job_t *jq_pop_job(job_queue_t *queue); #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 ////////////////////////////////////// // == INI ============================================ diff --git a/colla_win32.c b/colla_win32.c index e27e92b..6143460 100644 --- a/colla_win32.c +++ b/colla_win32.c @@ -161,6 +161,8 @@ struct { os_entity_t *entity_free; oshandle_t hstdout; oshandle_t hstdin; + oshandle_t hconin; + oshandle_t hconout; WORD default_fg; WORD default_bg; } w32_data = {0}; @@ -212,14 +214,22 @@ void os_init(void) { 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 hstdin = CreateFile(TEXT("CONIN$"), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + HANDLE hconout = CreateFile(TEXT("CONOUT$"), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE, 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$"); - else w32_data.hstdout.data = (uptr)hstdout; + if (hstdout == INVALID_HANDLE_VALUE) err("couldn't open STD_OUTPUT_HANDLE"); + else w32_data.hstdout.data = (uptr)hstdout; - if (hstdin == INVALID_HANDLE_VALUE) err("couldn't open CONIN$"); - else w32_data.hstdin.data = (uptr)hstdin; + if (hstdin == INVALID_HANDLE_VALUE) err("couldn't open STD_INPUT_HANDLE"); + 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}; if (GetConsoleScreenBufferInfo(hstdout, &console_info)) { @@ -324,6 +334,14 @@ oshandle_t os_stdin(void) { 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 ====================================== #define OS_SMALL_SCRATCH() \ @@ -343,6 +361,25 @@ DWORD os__win_mode_to_creation(filemode_e mode) { 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) { OS_SMALL_SCRATCH(); 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_putc(&cmdline, '\0'); - strview_t cmd_view = ostr_as_view(&cmdline); - str16_t command = strv_to_str16(&scratch, cmd_view); + str_t cmd_str = ostr_to_str(&cmdline); + debug("(%v)", cmd_str); + str16_t command = strv_to_str16(&scratch, strv(cmd_str)); 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) { - 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(); } @@ -806,11 +844,17 @@ bool os_release(void *ptr, usize size) { // == 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; thread_func_t *func = entity->thread.func; void *userdata = entity->thread.userdata; u64 id = entity->thread.id; + + os_thread_id = atomic_inc_i64(&os_thread_count) - 1; + return func(id, userdata); } @@ -926,6 +970,47 @@ void os_cond_wait(oshandle_t cond, oshandle_t mutex, int milliseconds) { #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 struct {