summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAargh Rai <aargh.rai+git@gmail.com>2026-08-15 14:28:35 +0530
committerAargh Rai <aargh.rai+git@gmail.com>2026-08-15 14:28:35 +0530
commitf97ea63ac29c08530b1aaff89277262b44ef7a11 (patch)
tree2534d597b2356de9dda0412665cf8ba06870526c
parent2bf47937bcc5cd29cf8081f922b7615b7e57ed57 (diff)
phase A
-rw-r--r--PLAN.md8
-rw-r--r--build.c2
-rw-r--r--include/engine/threads.h8
-rw-r--r--src/engine/thread.c14
-rw-r--r--src/engine/threads.c94
-rw-r--r--src/main.c8
-rw-r--r--src/uci/command.c5
-rw-r--r--tests/generated.c13
8 files changed, 122 insertions, 30 deletions
diff --git a/PLAN.md b/PLAN.md
index 2b3a05d..7581307 100644
--- a/PLAN.md
+++ b/PLAN.md
@@ -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.
diff --git a/build.c b/build.c
index d7914d1..5b73d58 100644
--- a/build.c
+++ b/build.c
@@ -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
diff --git a/src/main.c b/src/main.c
index 5ece480..61a3850 100644
--- a/src/main.c
+++ b/src/main.c
@@ -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",