summaryrefslogtreecommitdiff
path: root/STYLE_GUIDE.md
diff options
context:
space:
mode:
Diffstat (limited to 'STYLE_GUIDE.md')
-rw-r--r--STYLE_GUIDE.md123
1 files changed, 116 insertions, 7 deletions
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 `<stdatomic.h>` primitives directly:
+Use `<stdatomic.h>` 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 `<stdatomic.h>` 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_<thing>(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 <stdbool.h>
+#include <stdlib.h>
+#include <pthread.h>
+#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.