fix: use _Atomic flag for sel_alloc_thread_id registration, move reg before loop, add wrong-thread guard test

This commit is contained in:
Rongsong Shen 2026-04-30 06:15:01 +00:00
parent b722e5cb3d
commit 1eb4eb1335
3 changed files with 61 additions and 13 deletions

View file

@ -6,6 +6,7 @@
*/
#define UNIT_TEST 1
#include <pthread.h>
#include <stdatomic.h>
#include <stdint.h>
#include <stdio.h>
@ -2252,6 +2253,52 @@ test_sel_register_alloc_thread(void)
sel_optimize_test_reset();
}
/* ── Task 8: wrong-thread ownership guard test ───────────────────────────*/
/* shared state for the wrong-thread sweep test */
static int g_wrong_thread_sweep_ran = 0;
static void* wrong_thread_sweep(void* arg)
{
(void)arg;
/* calling gc_sweep from the wrong thread must hit the ownership guard
* and return without sweeping — nodes must remain intact */
gc_sweep_select_nodes(NULL, 0);
g_wrong_thread_sweep_ran = 1;
return NULL;
}
static void
test_sel_wrong_thread_guard(void)
{
sel_optimize_test_reset();
sel_optimize_set(1);
sel_node_table_init();
/* Register THIS thread as the allocator */
sel_register_alloc_thread();
/* Allocate a node that should survive the wrong-thread sweep */
xce_select_node_t* n = makeSelectNode(SEL_TRUE, 0, 0);
TEST_ASSERT_NOT_NULL(n);
/* Spawn a different thread to call gc_sweep — guard must block it */
pthread_t tid;
g_wrong_thread_sweep_ran = 0;
int rc = pthread_create(&tid, NULL, wrong_thread_sweep, NULL);
TEST_ASSERT_EQUAL_INT(0, rc);
pthread_join(tid, NULL);
TEST_ASSERT_EQUAL_INT(1, g_wrong_thread_sweep_ran);
/* Node must still exist (wrong-thread sweep was blocked) */
/* GC from correct thread: mark then sweep normally */
gc_clear_marks();
gc_sweep_select_nodes(NULL, 0);
sel_node_table_fini();
sel_optimize_test_reset();
}
/* ── Top-level runner ─────────────────────────────────────────────────────*/
void
@ -2365,6 +2412,7 @@ test_select(void)
/* ── Task 8: sel_alloc_thread_id ownership check ─────────────────── */
RUN_TEST(test_sel_register_alloc_thread);
RUN_TEST(test_sel_wrong_thread_guard);
xstring_table_fini();
}

View file

@ -23,8 +23,9 @@
/* ── optimize level ───────────────────────────────────────────────────── */
static _Atomic int g_sel_optimize_level = -1; /* -1 = unset */
static pthread_t sel_alloc_thread_id; /* 0 = unregistered — skip checks */
static _Atomic int g_sel_optimize_level = -1; /* -1 = unset */
static pthread_t sel_alloc_thread_id;
static _Atomic int sel_alloc_thread_registered = 0; /* 1 after sel_register_alloc_thread() */
void sel_optimize_set(int level)
{
@ -186,12 +187,14 @@ 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;
atomic_store_explicit(&sel_alloc_thread_registered, 0, memory_order_seq_cst);
/* sel_alloc_thread_id left as-is; only checked when registered flag is set */
}
void sel_register_alloc_thread(void)
{
sel_alloc_thread_id = pthread_self();
atomic_store_explicit(&sel_alloc_thread_registered, 1, memory_order_release);
}
/* ── node construction ───────────────────────────────────────────────── */
@ -651,7 +654,8 @@ 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)) {
if (atomic_load_explicit(&sel_alloc_thread_registered, memory_order_acquire)
&& !pthread_equal(pthread_self(), sel_alloc_thread_id)) {
ERROR("eval_select called from non-allocator thread");
return -1;
}
@ -875,7 +879,8 @@ 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)) {
if (atomic_load_explicit(&sel_alloc_thread_registered, memory_order_acquire)
&& !pthread_equal(pthread_self(), sel_alloc_thread_id)) {
ERROR("gc_sweep_select_nodes called from non-allocator thread");
return;
}

View file

@ -117,16 +117,11 @@ int main(int argc, char* argv[])
INFO("xce scheduler is running");
/* Register this thread as the sole eval/GC allocator thread (write-once) */
sel_register_alloc_thread();
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);