From 93f1e82cedbf2fcd45ac413cbf2a654e0f65f0b3 Mon Sep 17 00:00:00 2001 From: Aargh Rai Date: Thu, 27 Aug 2026 22:21:14 +0530 Subject: uci tests --- STYLE_GUIDE.md | 123 +++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 116 insertions(+), 7 deletions(-) (limited to 'STYLE_GUIDE.md') diff --git a/STYLE_GUIDE.md b/STYLE_GUIDE.md index 2581b5b..a02c90c 100644 --- a/STYLE_GUIDE.md +++ b/STYLE_GUIDE.md @@ -12,7 +12,7 @@ Multiple `.c` files are aggregated into a single translation unit by `#include`-ing them, rather than compiling separately: - `src/engine.c` pulls in `engine/threads.c`, `engine/thread.c`, - `engine/moves.c`, `engine/ttable.c`. + `engine/moves.c`, `engine/ttable.c`, `engine/evaluate.c`, `engine/search.c`. - `src/ipc.c` pulls in `ipc/go_args.c`, `ipc/moves.c`, `ipc/engine_message.c`, `ipc/state.c`, `ipc/threads.c`. - `src/main.c` and `tests/main.c` append `#include "fen.c"`, @@ -35,7 +35,6 @@ consuming `.c` file `#define`s the sentinel before including the header: Other gates in use: - `MOVES_INTERNAL` — `include/engine/moves.h` (`__forloop_*` declarations). -- `IPC_INTERNAL` — `include/ipc/state.h`. ### Test gating @@ -139,6 +138,23 @@ Every "object" type follows an `init` / `deinit` pair. ## Macros +### File-local constants + +File-scope constants that don't warrant a `#define` use anonymous enums: + +```c +enum { ROOT_MAX_DEPTH = 100, MATE_WINDOW = 1000 }; +``` + +Function-scope constants use the same pattern: + +```c +for (int depth = 1; ...) { + enum { WORKERS = 4 }; + ... +} +``` + ### Expression macros (no statement semantics) Written without surrounding parentheses, relying on call-site precedence — @@ -247,15 +263,60 @@ enum { ## Atomics -Use `` primitives directly: +Use `` primitives directly. The engine's threading model is +**lock-free**: no `pthread_mutex` or `pthread_cond` is used anywhere. All +inter-thread communication goes through atomics, with `sched_yield()` for +back-off in spin loops. + +### Types in use + +- `atomic_int` — flags, counters, scores (`stop`, `finished`, `best_score`, + `workers`, `next_move_idx`, `nodes_searched`). +- `atomic_bool` — boolean stop flags in `search_ctx_t` and local variables + in `child_thread`/`root_thread`. +- `atomic_flag` — spinlock for the global `engine_message_handler_t` lock. +- `atomic_uint_fast64_t` — transposition table keys. + +### Operations in use + +- `atomic_init(&x, v)` — before `pthread_create` (main thread). +- `atomic_store(&x, v)` — at thread entry or to signal state changes. +- `atomic_load(&x)` — to poll for state changes (stop signals, split-point + state). +- `atomic_fetch_add(&x, 1)` / `atomic_fetch_sub(&x, 1)` — lock-free counters + (worker registration in `split_point_t.workers`, move claiming via + `next_move_idx`). +- `atomic_compare_exchange_weak(&x, &expected, desired)` — lock-free pool + allocation (`__pool_alloc`) and best-score sharing (`split_point_share`). +- `atomic_flag_test_and_set(&flag)` / `atomic_flag_clear(&flag)` — spinlock + acquire/release for engine message handlers. + +### Spin-wait pattern + +Threads poll with `sched_yield()` in tight loops: + +```c +while (atomic_load(&sp->state) == SPLITPOINT_STATE_ACTIVE) { + sched_yield(); +} +``` + +### Two-phase initialization + +Atomics used across threads are initialized in two phases: + +1. `atomic_init(&x, v)` on the main thread before `pthread_create`. +2. `atomic_store(&x, v)` at thread entry to ensure visibility. -- `atomic_int`, `atomic_uint_fast64_t`, `atomic_flag`. -- `atomic_init(&x, v)`, `atomic_load(&x)`, `atomic_store(&x, v)`. -- `atomic_flag_test_and_set(&flag)` / `atomic_flag_clear(&flag)` for spinlocks. +### Note on `_Atomic bool` vs `atomic_bool` + +Both forms appear in the codebase: `thread.c` uses the `` typedef +`atomic_bool` while `search.c` tests use the C11 keyword `_Atomic bool`. They +are semantically equivalent. Prefer `atomic_bool` (the typedef) for consistency. ## Test Functions -Embedded test functions: +### Signature and convention - Signature: `bool test_(void)`. - Return `true` = pass, `false` = fail. @@ -280,6 +341,54 @@ bool test_uci_cmd_t() { } ``` +### Test block structure + +Test blocks live at the bottom of source files under `#ifdef TEST_MOD` and may +need additional includes beyond the file's normal headers: + +```c +#ifdef TEST_MOD +#include +#include +#include +#include "fen.h" + +// Cross-module .c inclusion for IPC types needed by tests +#include "../src/ipc/go_args.c" +#include "../src/ipc/moves.c" +#include "../src/ipc/engine_message.c" + +bool test_example() { ... } +#endif +``` + +### Anti-double-inclusion guard + +When a leaf `.c` file is compiled standalone *and* included from an aggregated +TU, use `#ifndef TEST_MOD` to prevent duplicate symbols: + +```c +#ifndef TEST_MOD +#include "moves/king.c" +#endif +``` + +### Test helper naming + +- `__double_prefix` for test helper structs: `__child_test_env_t`, + `__root_split_env_t`, `__claim_thread_args_t`. +- `__double_prefix` for test helper functions: `__root_thread_run`, + `__child_test_setup`, `__wait_split_point_done`. +- Local-scope constants via anonymous enum: `enum { WORKERS = 4 };`. + +### Thread-safety for debug counters + +Per-thread debug counters use `_Thread_local` storage to avoid races: + +```c +static _Thread_local int spins = 0; +``` + ## Build system (`build.c`) - A self-rebuilding `nob.h`-style driver. -- cgit v1.2.3