From b722e5cb3d3eac1093021f8bda85d362ee8eaa1d Mon Sep 17 00:00:00 2001 From: Rongsong Shen Date: Thu, 30 Apr 2026 06:04:21 +0000 Subject: [PATCH] feat: add sel_alloc_thread_id ownership check in eval_select and gc_sweep (6-10) --- src/include/resreq.h | 5 +++++ src/lib/testsuite/test_select.c | 29 +++++++++++++++++++++++++++++ src/lib/xce_select.c | 16 ++++++++++++++++ src/scheduler/src/main.c | 9 +++++++++ 4 files changed, 59 insertions(+) diff --git a/src/include/resreq.h b/src/include/resreq.h index 380e53e..39c7787 100644 --- a/src/include/resreq.h +++ b/src/include/resreq.h @@ -96,6 +96,11 @@ extern xce_select_node_t* makeSelectNode(sel_node_type_t type, extern void sel_optimize_set(int level); extern int sel_get_optimize_level(void); +/* Register the calling thread as the sole allocator/eval/GC thread. + * Must be called once at scheduler startup (before eval_select or + * gc_sweep_select_nodes). When unregistered (id==0) the checks are skipped. */ +extern void sel_register_alloc_thread(void); + /* Intern table lifecycle */ extern void sel_node_table_init(void); extern void sel_node_table_fini(void); diff --git a/src/lib/testsuite/test_select.c b/src/lib/testsuite/test_select.c index 736fc43..cfd475f 100644 --- a/src/lib/testsuite/test_select.c +++ b/src/lib/testsuite/test_select.c @@ -2226,6 +2226,32 @@ test_gc_clear_marks_exported(void) sel_optimize_test_reset(); } +/* ── Task 8: sel_alloc_thread_id ownership check ─────────────────────────*/ + +static void +test_sel_register_alloc_thread(void) +{ + sel_optimize_test_reset(); /* resets sel_alloc_thread_id to 0 */ + sel_optimize_set(1); + sel_node_table_init(); + + /* Should not crash when unregistered (id=0 means skip check) */ + xce_select_node_t* n = makeSelectNode(SEL_TRUE, 0, 0); + TEST_ASSERT_NOT_NULL(n); + + /* Register current thread */ + sel_register_alloc_thread(); + + /* makeSelectNode from same thread must still work */ + xce_select_node_t* n2 = makeSelectNode(SEL_FALSE, 0, 0); + TEST_ASSERT_NOT_NULL(n2); + + gc_clear_marks(); + gc_sweep_select_nodes(NULL, 0); + sel_node_table_fini(); + sel_optimize_test_reset(); +} + /* ── Top-level runner ─────────────────────────────────────────────────────*/ void @@ -2337,5 +2363,8 @@ test_select(void) /* ── Phase 6 prep: exported GC mark primitives ───────────────────── */ RUN_TEST(test_gc_clear_marks_exported); + /* ── Task 8: sel_alloc_thread_id ownership check ─────────────────── */ + RUN_TEST(test_sel_register_alloc_thread); + xstring_table_fini(); } diff --git a/src/lib/xce_select.c b/src/lib/xce_select.c index 2166a87..3c9920a 100644 --- a/src/lib/xce_select.c +++ b/src/lib/xce_select.c @@ -7,6 +7,7 @@ #define UNIT_TEST 1 /* expose sel_optimize_test_reset in resreq.h */ #include +#include #include #include #include @@ -23,6 +24,7 @@ /* ── optimize level ───────────────────────────────────────────────────── */ static _Atomic int g_sel_optimize_level = -1; /* -1 = unset */ +static pthread_t sel_alloc_thread_id; /* 0 = unregistered — skip checks */ void sel_optimize_set(int level) { @@ -184,6 +186,12 @@ void sel_optimize_test_reset(void) { sel_node_table_fini(); atomic_store_explicit(&g_sel_optimize_level, -1, memory_order_seq_cst); + sel_alloc_thread_id = 0; +} + +void sel_register_alloc_thread(void) +{ + sel_alloc_thread_id = pthread_self(); } /* ── node construction ───────────────────────────────────────────────── */ @@ -643,6 +651,10 @@ int eval_select(const xce_select_node_t* node, select_eval_ctx_t* ctx) if (sel_get_optimize_level() <= 0) { ERROR("eval_select called with optimize_level <= 0"); abort(); } + if (sel_alloc_thread_id != 0 && !pthread_equal(pthread_self(), sel_alloc_thread_id)) { + ERROR("eval_select called from non-allocator thread"); + return -1; + } /* bitmap cache hit */ if (node->node_id < ctx->bool_bits_cap) { @@ -863,6 +875,10 @@ void gc_sweep_select_nodes(const xce_sched_job_t** live_jobs, size_t njobs) if (sel_node_table.table == NULL) { return; } + if (sel_alloc_thread_id != 0 && !pthread_equal(pthread_self(), sel_alloc_thread_id)) { + ERROR("gc_sweep_select_nodes called from non-allocator thread"); + return; + } int caps = sel_node_table.nodes.caps; diff --git a/src/scheduler/src/main.c b/src/scheduler/src/main.c index 2e9bbfc..5cdd30e 100644 --- a/src/scheduler/src/main.c +++ b/src/scheduler/src/main.c @@ -10,6 +10,7 @@ #include "scheduler.h" +#include "resreq.h" #include "utils.h" extern int scheduler_req_handler(xce_request_t*, void*); @@ -118,6 +119,14 @@ int main(int argc, char* argv[]) while (1) { sched_update(&sched); + + /* Register this thread as the sole eval/GC allocator thread (write-once) */ + static int sel_thread_registered = 0; + if (!sel_thread_registered) { + sel_register_alloc_thread(); + sel_thread_registered = 1; + } + sched_begin_session(&sched); sched_go(&sched);