diff options
| author | Aargh Rai <aargh.rai+git@gmail.com> | 2026-08-27 17:41:07 +0530 |
|---|---|---|
| committer | Aargh Rai <aargh.rai+git@gmail.com> | 2026-08-27 17:41:07 +0530 |
| commit | 5a02743c0c9563f41a8c3821a7bbce4fc3b0b839 (patch) | |
| tree | f36aa805b8c90a8d786e8561a9d84f57e6191c19 /src/engine/threads.c | |
| parent | d87b4744bed8f0c43e11ea12508ef2941bdd7186 (diff) | |
bug fixes
Diffstat (limited to 'src/engine/threads.c')
| -rw-r--r-- | src/engine/threads.c | 105 |
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 |
