diff options
| author | Aargh Rai <aargh.rai+git@gmail.com> | 2026-08-15 14:28:35 +0530 |
|---|---|---|
| committer | Aargh Rai <aargh.rai+git@gmail.com> | 2026-08-15 14:28:35 +0530 |
| commit | f97ea63ac29c08530b1aaff89277262b44ef7a11 (patch) | |
| tree | 2534d597b2356de9dda0412665cf8ba06870526c | |
| parent | 2bf47937bcc5cd29cf8081f922b7615b7e57ed57 (diff) | |
phase A
| -rw-r--r-- | PLAN.md | 8 | ||||
| -rw-r--r-- | build.c | 2 | ||||
| -rw-r--r-- | include/engine/threads.h | 8 | ||||
| -rw-r--r-- | src/engine/thread.c | 14 | ||||
| -rw-r--r-- | src/engine/threads.c | 94 | ||||
| -rw-r--r-- | src/main.c | 8 | ||||
| -rw-r--r-- | src/uci/command.c | 5 | ||||
| -rw-r--r-- | tests/generated.c | 13 |
8 files changed, 122 insertions, 30 deletions
@@ -293,15 +293,15 @@ These are concrete, ordered steps. Each should be a single commit/patch and build (`just test`) green before the next. ### Phase A — Data-structure fixes -- [ ] **A1.** In `include/engine/threads.h`, rename the duplicate +- [x] **A1.** In `include/engine/threads.h`, rename the duplicate `move_t best_score` on `split_point_t` → `move_t best_move`. -- [ ] **A2.** Finish `struct child_thread_args`: add +- [x] **A2.** Finish `struct child_thread_args`: add `engine_message_handler_t *message;` field; remove or complete the dangling `int sp_stack;`. -- [ ] **A3.** In `src/main.c`, add `ttable_init(&ipc_state.threads.ttable, +- [x] **A3.** In `src/main.c`, add `ttable_init(&ipc_state.threads.ttable, <log2 of Hash option>)` before `threads_link` (use the `hash` spin option, default 256 → pick a sensible log2, e.g. 18 for ~256 MiB buckets, clamped). -- [ ] **A4.** In `src/engine/threads.c`, make `__threads_args_init` copy +- [x] **A4.** In `src/engine/threads.c`, make `__threads_args_init` copy `handler->ttable` and `handler->go_args` into each `thread_args_t` (currently `go_args`/`ttable` are set on `handler` but never propagated to per-thread args). Build + existing tests still pass. @@ -8,7 +8,7 @@ #define GLOBAL_INCLUDE "-Iinclude/" -#define CC "gcc", "-fsanitize=address", "-g", GLOBAL_INCLUDE +#define CC "gcc", "-fsanitize=address", "-g", "-pthread", GLOBAL_INCLUDE #define ENGINE_O "output/engine.o" #define UCI_O "output/uci.o" diff --git a/include/engine/threads.h b/include/engine/threads.h index b5f2163..da221a8 100644 --- a/include/engine/threads.h +++ b/include/engine/threads.h @@ -17,7 +17,7 @@ typedef struct split_point { atomic_int alpha, beta; atomic_int best_score; atomic_int workers; - move_t best_score; + move_t best_move; struct split_point *parent; int ply; @@ -37,9 +37,10 @@ struct child_thread_args { int id; go_args_t *go_args; - position_t position; + position_t *position; + ttable_t *ttable; int sp_stack; - int + engine_message_handler_t *message; }; struct root_thread_args { // signal sent by threads manager @@ -52,6 +53,7 @@ struct root_thread_args { position_t *position; go_args_t *go_args; engine_message_handler_t *message; + ttable_t *ttable; }; typedef union { diff --git a/src/engine/thread.c b/src/engine/thread.c index 25e583e..912b70f 100644 --- a/src/engine/thread.c +++ b/src/engine/thread.c @@ -1,6 +1,20 @@ +#define ENGINE_THREADS_INTERNAL #include "engine/threads.h" #include <stdio.h> +void threads_go(struct threads *handler) { + int running_count = handler->running_count; + for (int i = 0; i < running_count; i++) { + __threads_args_init(handler->args + i, handler, i); + pthread_create( + handler->threads + i, + NULL, + i == 0 ? root_thread : child_thread, + handler->args + i + ); + } +} + void *child_thread(void *_args) { struct child_thread_args *args = (struct child_thread_args*)_args; args->stop = 0; diff --git a/src/engine/threads.c b/src/engine/threads.c index 541efb1..5bf630a 100644 --- a/src/engine/threads.c +++ b/src/engine/threads.c @@ -41,28 +41,18 @@ void threads_reserve(struct threads *handler, size_t capacity) { handler->capacity = capacity; } -void threads_go(struct threads *handler) { - int running_count = handler->running_count; - for (int i = 0; i < running_count; i++) { - __threads_args_init(handler->args + i, handler, i); - pthread_create( - handler->threads + i, - NULL, - i == 0 ? root_thread : child_thread, - handler->args + i - ); - } -} - bool threads_go_loop(struct threads *handler) { // idk maybe do something while waiting for calculations to flow in + return false; } bool threads_all_done(struct threads *handler) { int running_count = handler->running_count; assert(running_count > 0); for (int i = 0; i < running_count; i++) { - if (atomic_load(&handler->args[i].finished) == 1) continue; + // `finished` is part of the common initial sequence of both union + // members (stop, finished), so reading it via either member is well-defined. + if (atomic_load(&handler->args[i].root_thread_args.finished) == 1) continue; return false; } return true; @@ -83,21 +73,93 @@ void __threads_args_init( int i ) { if (i > 0) { - struct child_thread_args *args = _args; + struct child_thread_args *args = (struct child_thread_args*)_args; atomic_init(&args->stop, 0); atomic_init(&args->finished, 0); args->id = i; args->position = handler->position; args->go_args = handler->go_args; + args->ttable = &handler->ttable; + args->sp_stack = 0; args->message = handler->engine_messages->data[i]; return; } - struct root_thread_args *args = _args; + struct root_thread_args *args = (struct root_thread_args*)_args; atomic_init(&args->stop, 0); atomic_init(&args->finished, 0); args->id = i; args->position = handler->position; args->go_args = handler->go_args; args->message = handler->engine_messages->data[i]; + args->ttable = &handler->ttable; +} + +#ifdef TEST_MOD +bool test_threads_args_init_root_propagates() { + engine_message_handler_t dummy[2]; + engine_message_handler_t *dummy_ptrs[2] = { &dummy[0], &dummy[1] }; + engine_messages_t messages = { + .data = dummy_ptrs, + .count = 2, + .capacity = 2, + }; + + go_args_t go_args = {0}; + position_t pos = {0}; + + struct threads handler = {0}; + handler.engine_messages = &messages; + handler.go_args = &go_args; + handler.position = &pos; + ttable_init(&handler.ttable, 2); + + thread_args_t args; + __threads_args_init(&args, &handler, 0); + + bool ok = true; + ok &= args.root_thread_args.id == 0; + ok &= atomic_load(&args.root_thread_args.stop) == 0; + ok &= atomic_load(&args.root_thread_args.finished) == 0; + ok &= args.root_thread_args.ttable == &handler.ttable; + ok &= args.root_thread_args.go_args == &go_args; + ok &= args.root_thread_args.position == &pos; + ok &= args.root_thread_args.message == &dummy[0]; + + ttable_deinit(handler.ttable); + return ok; +} + +bool test_threads_args_init_child_propagates() { + engine_message_handler_t dummy[2]; + engine_message_handler_t *dummy_ptrs[2] = { &dummy[0], &dummy[1] }; + engine_messages_t messages = { + .data = dummy_ptrs, + .count = 2, + .capacity = 2, + }; + + go_args_t go_args = {0}; + position_t pos = {0}; + + struct threads handler = {0}; + handler.engine_messages = &messages; + handler.go_args = &go_args; + handler.position = &pos; + ttable_init(&handler.ttable, 2); + + thread_args_t args; + __threads_args_init(&args, &handler, 1); + + bool ok = true; + ok &= args.child_thread_args.id == 1; + ok &= args.child_thread_args.ttable == &handler.ttable; + ok &= args.child_thread_args.go_args == &go_args; + ok &= args.child_thread_args.position == &pos; + ok &= args.child_thread_args.message == &dummy[1]; + ok &= args.child_thread_args.sp_stack == 0; + + ttable_deinit(handler.ttable); + return ok; } +#endif @@ -30,6 +30,14 @@ int main(int argc, char** argv) { threads_init(&engine_threads, ipc_state.uci_state.threads); threads_link(&engine_threads, &ipc_state); + // initialise the transposition table from the UCI "Hash" option (in MiB). + // pick the smallest power of two (>= 2^4) that fills at least that much memory. + size_t hash_mb = (size_t)ipc_state.uci_state.hash; + u64 buckets = (hash_mb * 1024 * 1024) / sizeof(tbucket_t); + int hash_log2 = 4; + while ((1ULL << (hash_log2 + 1)) <= buckets && hash_log2 < 30) hash_log2++; + ttable_init(&engine_threads.ttable, hash_log2); + engine_messages_init(&ipc_state.engine_messages); goto skip_refresh; waiting_for_go: diff --git a/src/uci/command.c b/src/uci/command.c index 327e0e2..17a0c9a 100644 --- a/src/uci/command.c +++ b/src/uci/command.c @@ -6,7 +6,7 @@ void uci_cmd_init(uci_cmd_t *cmd) { cmd->empty = true; cmd->args_capacity = 32; - cmd->args = calloc(sizeof(char) * MAX_TOKEN_SIZE, output.args_capacity); + cmd->args = calloc(sizeof(char) * MAX_TOKEN_SIZE, cmd->args_capacity); } bool is_valid_cmd(const char *token) { @@ -91,7 +91,8 @@ void uci_cmd_deinit(uci_cmd_t cmd) { #include <string.h> bool test_uci_cmd_t() { - uci_cmd_t cmd = uci_cmd_init(); + uci_cmd_t cmd; + uci_cmd_init(&cmd); uci_cmd_set_root(&cmd, "root"); uci_cmd_append_arg(&cmd, "arg1"); uci_cmd_append_arg(&cmd, "arg2"); diff --git a/tests/generated.c b/tests/generated.c index a605554..8a9014f 100644 --- a/tests/generated.c +++ b/tests/generated.c @@ -1,11 +1,12 @@ #include "../src/fen.c" #include "../src/engine/ttable.c" +#include "../src/engine/threads.c" #include "../src/engine/moves.c" #include "../src/engine/moves/king.c" #include "../src/uci/command.c" -int total_test_count = 17; -bool (*tests[17])(void) = { +int total_test_count = 19; +bool (*tests[19])(void) = { test_fen_no_passant, test_fen_passant, test_starting_position, @@ -16,6 +17,8 @@ bool (*tests[17])(void) = { test_bucket_collision, test_ttable_replace_shallowest, test_ttable_many_entries, + test_threads_args_init_root_propagates, + test_threads_args_init_child_propagates, test_perft_starting_position, test_perft_kiwipete_position, test_perft_position3, @@ -25,8 +28,8 @@ bool (*tests[17])(void) = { test_invalid_cmd_uci_cmd_t, }; -int max_test_name_size = 25; -char test_names[17][100] = { +int max_test_name_size = 34; +char test_names[19][100] = { "fen_no_passant", "fen_passant", "starting_position", @@ -37,6 +40,8 @@ char test_names[17][100] = { "bucket_collision", "ttable_replace_shallowest", "ttable_many_entries", + "threads_args_init_root_propagates", + "threads_args_init_child_propagates", "perft_starting_position", "perft_kiwipete_position", "perft_position3", |
