diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index c6704a8a1..6edb3d413 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -55,7 +55,7 @@ jobs: echo "=== riscv64 System Test Run $i/$SYSTEM_TEST_RUNS ===" timeout 300 cmake --build build_riscv64 --target system_test_run > /tmp/st_out_$i.txt 2>&1 || true cat /tmp/st_out_$i.txt - if ! grep -q "Failed: 0" /tmp/st_out_$i.txt; then + if ! grep -q "RESULT: ALL TESTS PASSED" /tmp/st_out_$i.txt; then echo "riscv64 system test run $i/$SYSTEM_TEST_RUNS FAILED" exit 1 fi @@ -107,7 +107,7 @@ jobs: echo "=== aarch64 System Test Run $i/$SYSTEM_TEST_RUNS ===" timeout 300 cmake --build build_aarch64 --target system_test_run > /tmp/st_out_$i.txt 2>&1 || true cat /tmp/st_out_$i.txt - if ! grep -q "Failed: 0" /tmp/st_out_$i.txt; then + if ! grep -q "RESULT: ALL TESTS PASSED" /tmp/st_out_$i.txt; then echo "aarch64 system test run $i/$SYSTEM_TEST_RUNS FAILED" exit 1 fi diff --git a/tests/system_test/balance_test.cpp b/tests/system_test/balance_test.cpp index fcba8bd12..b6f7a182a 100644 --- a/tests/system_test/balance_test.cpp +++ b/tests/system_test/balance_test.cpp @@ -29,15 +29,19 @@ std::atomic g_tests_failed{0}; // every 64 ticks) should steal tasks to idle cores. // // Strategy: -// 1. Spawn N long-lived unpinned workers (they sleep in a loop so Balance() -// has time to migrate them). +// 1. Spawn N long-lived unpinned workers that yield in a loop. // 2. Each worker periodically records which core it runs on via atomic OR. // 3. After all workers complete, verify the combined core mask has >= 2 bits // set — meaning at least one task was migrated by Balance(). // -// Workers must live long enough for multiple Balance() intervals (64 ticks -// each). Using sys_sleep(50) in a loop ensures they remain in the scheduler -// queues across multiple ticks. +// IMPORTANT: Workers use sys_yield() instead of sys_sleep() because +// sys_sleep() moves tasks to a separate sleeping_tasks queue that is +// invisible to Balance(). Balance() only checks GetQueueSize() on the +// scheduler's ready queue, so sleeping tasks appear as zero load and +// never get migrated. sys_yield() keeps tasks in the ready queue. +// +// Workers need enough iterations to survive multiple Balance() intervals +// (64 ticks each at SIMPLEKERNEL_TICK=1000 → 64ms per interval). // =========================================================================== constexpr int kImbalanceWorkerCount = 8; @@ -45,13 +49,17 @@ std::atomic g_imbalance_done{0}; std::atomic g_cores_used_mask{0}; void imbalance_worker(void* /*arg*/) { - // Stay alive across multiple Balance() intervals. - // Record core ID on each wakeup — after migration we'll see a different - // core. - for (int i = 0; i < 20; ++i) { - auto core_id = cpu_io::GetCurrentCoreId(); - g_cores_used_mask.fetch_or(1UL << core_id, std::memory_order_relaxed); - (void)sys_sleep(10); + // Batch yields keep the task visible in the ready queue for Balance(), + // while periodic sleeps reduce scheduling pressure to avoid overwhelming + // the scheduler lock (which can trigger recursive-lock panics under + // extreme contention). + for (int round = 0; round < 200; ++round) { + for (int j = 0; j < 10; ++j) { + auto core_id = cpu_io::GetCurrentCoreId(); + g_cores_used_mask.fetch_or(1UL << core_id, std::memory_order_relaxed); + (void)sys_yield(); + } + (void)sys_sleep(1); } g_imbalance_done.fetch_add(1, std::memory_order_release); @@ -134,10 +142,13 @@ std::atomic g_affinity_done{0}; std::atomic g_affinity_cores_mask{0}; void affinity_pinned_worker(void* /*arg*/) { - for (int i = 0; i < 10; ++i) { - auto core_id = cpu_io::GetCurrentCoreId(); - g_affinity_cores_mask.fetch_or(1UL << core_id, std::memory_order_relaxed); - (void)sys_sleep(10); + for (int round = 0; round < 100; ++round) { + for (int j = 0; j < 10; ++j) { + auto core_id = cpu_io::GetCurrentCoreId(); + g_affinity_cores_mask.fetch_or(1UL << core_id, std::memory_order_relaxed); + (void)sys_yield(); + } + (void)sys_sleep(1); } g_affinity_done.fetch_add(1, std::memory_order_release); diff --git a/tests/system_test/main.cpp b/tests/system_test/main.cpp index e1159b7e6..a64bdd331 100644 --- a/tests/system_test/main.cpp +++ b/tests/system_test/main.cpp @@ -47,6 +47,7 @@ std::array test_cases = { test_case{"clone_test", clone_test, false}, test_case{"exit_test", exit_test, false}, test_case{"cross_core_test", cross_core_test, false}, + test_case{"balance_test", balance_test, false}, test_case{"mutex_test", mutex_test, false}, test_case{"yield_test", yield_test, false}, test_case{"fork_test", fork_test, false}, @@ -57,7 +58,6 @@ std::array test_cases = { test_case{"stress_test", stress_test, false}, test_case{"ramfs_test", ramfs_test, false}, test_case{"fatfs_test", fatfs_test, false}, - test_case{"balance_test", balance_test, false}, }; std::array test_results{}; @@ -223,6 +223,10 @@ void test_runner_entry(void* /*arg*/) { } } + if (all_passed) { + klog::Info("RESULT: ALL TESTS PASSED"); + } + QemuExit(all_passed); }