summaryrefslogtreecommitdiff
path: root/src/engine/threads.c
diff options
context:
space:
mode:
authorAargh Rai <aargh.rai+git@gmail.com>2026-08-27 17:41:07 +0530
committerAargh Rai <aargh.rai+git@gmail.com>2026-08-27 17:41:07 +0530
commit5a02743c0c9563f41a8c3821a7bbce4fc3b0b839 (patch)
treef36aa805b8c90a8d786e8561a9d84f57e6191c19 /src/engine/threads.c
parentd87b4744bed8f0c43e11ea12508ef2941bdd7186 (diff)
bug fixes
Diffstat (limited to 'src/engine/threads.c')
-rw-r--r--src/engine/threads.c105
1 files changed, 101 insertions, 4 deletions
diff --git a/src/engine/threads.c b/src/engine/threads.c
index fe38013..060afb9 100644
--- a/src/engine/threads.c
+++ b/src/engine/threads.c
@@ -73,11 +73,24 @@ bool threads_all_done(struct threads *handler) {
return true;
}
+void threads_stop_all(struct threads *handler) {
+ int running_count = handler->running_count;
+ for (int i = 0; i < running_count; i++) {
+ atomic_store(&handler->args[i].root_thread_args.stop, 1);
+ }
+}
+
+void threads_join(struct threads *handler) {
+ int running_count = handler->running_count;
+ for (int i = 0; i < running_count; i++) {
+ pthread_join(handler->threads[i], NULL);
+ }
+}
+
void threads_cleanup(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_cancel(handler->threads[i]);
}
handler->running_count = 0;
}
@@ -111,9 +124,7 @@ void __threads_args_init(
args->ttable = &handler->ttable;
args->split_points = handler->split_point_pool;
args->sp_pool_capacity = handler->capacity;
- args->workers_stop = handler->running_count > 1
- ? &handler->args[1].child_thread_args.stop
- : NULL;
+ args->handler = handler;
args->workers_count = handler->running_count - 1;
}
@@ -129,6 +140,7 @@ split_point_t *__pool_alloc(split_point_t *pool, size_t capacity) {
atomic_store(&sp->best_score, -SEARCH_INF);
// workers counts only registered helpers; the creator registers itself
atomic_store(&sp->workers, 0);
+ atomic_store(&sp->nodes_searched, 0);
sp->best_move = (move_t) { 0 };
// recycled block: release the previous occupant's move list
moves_deinit(sp->moves);
@@ -179,6 +191,13 @@ void split_point_share(split_point_t *split_point, i32 score, move_t move) {
}
}
+// ยง2.1 thrashing guard: true if this split point has been searched enough
+// to be worth publishing for HELP. Prevents the end-of-iteration collapse
+// where N idle threads all try to split a single shallow node.
+bool split_point_worth_sharing(split_point_t *split_point) {
+ return atomic_load(&split_point->nodes_searched) >= SPLITPOINT_THRASH_THRESHOLD;
+}
+
split_point_t *__pool_find_work(split_point_t *pool, size_t capacity) {
for (size_t i = 0; i < capacity; i++) {
split_point_t *sp = pool + i;
@@ -194,6 +213,8 @@ split_point_t *threads_find_work(struct threads *handler) {
}
#ifdef TEST_MOD
+#include <time.h>
+
bool test_threads_args_init_root_propagates() {
engine_message_handler_t dummy[2];
engine_message_handler_t *dummy_ptrs[2] = { &dummy[0], &dummy[1] };
@@ -401,4 +422,80 @@ bool test_help_scan_finds_published_work() {
threads_deinit(&handler);
return ok;
}
+
+bool test_thrashing_counter_tracks_nodes() {
+ struct threads handler;
+ threads_init(&handler, 4);
+
+ split_point_t *sp = split_point_alloc(&handler);
+ atomic_store(&sp->nodes_searched, 0);
+
+ // initially not worth sharing
+ bool ok = true;
+ ok &= !split_point_worth_sharing(sp);
+
+ // simulate some work
+ atomic_fetch_add(&sp->nodes_searched, 100);
+ ok &= !split_point_worth_sharing(sp);
+
+ // cross the threshold
+ atomic_fetch_add(&sp->nodes_searched, 200);
+ ok &= split_point_worth_sharing(sp);
+
+ moves_deinit(sp->moves);
+ threads_deinit(&handler);
+ return ok;
+}
+
+bool test_cooperative_stop_sets_finished() {
+ struct threads handler;
+ threads_init(&handler, 2);
+ ttable_init(&handler.ttable, 2);
+
+ // set up messages
+ engine_message_handler_t msg_handlers[2];
+ engine_message_handler_t *msg_ptrs[2] = { &msg_handlers[0], &msg_handlers[1] };
+ engine_messages_t messages = { .data = msg_ptrs, .count = 2, .capacity = 2 };
+ for (int i = 0; i < 2; i++) {
+ engine_message_handler_init(&msg_handlers[i]);
+ }
+ handler.engine_messages = &messages;
+
+ go_args_t go_args = { .depth = 1 };
+ position_t pos = position_starting();
+ handler.go_args = &go_args;
+ handler.position = &pos;
+ handler.running_count = 2;
+
+ for (int i = 0; i < 2; i++) {
+ __threads_args_init(handler.args + i, &handler, i);
+ }
+
+ // start threads
+ threads_go(&handler);
+
+ // give threads time to start
+ struct timespec ts = { .tv_nsec = 10000000 }; // 10ms
+ nanosleep(&ts, NULL);
+
+ // cooperative stop
+ threads_stop_all(&handler);
+ threads_join(&handler);
+
+ bool ok = true;
+ for (int i = 0; i < 2; i++) {
+ ok &= atomic_load(&handler.args[i].root_thread_args.finished) == 1;
+ }
+
+ threads_cleanup(&handler);
+ for (int i = 0; i < 2; i++) {
+ while (engine_message_handler_can_pop(&msg_handlers[i])) {
+ engine_message_deinit(engine_message_handler_pop(&msg_handlers[i]));
+ }
+ engine_message_handler_deinit(&msg_handlers[i]);
+ }
+ ttable_deinit(handler.ttable);
+ threads_deinit(&handler);
+ return ok;
+}
#endif