Exercise both native owners throughout concurrent radio rebinding

This commit is contained in:
evgeny committed 2026-10-28 00:59:00 +01:00
1 parent 6063d3eb2c
commit d9abb1f025
1 file changed
+28 -8
+28 -8
View File
@@ -53,7 +53,12 @@ extern "C" void __wrap_u_free_impl(void* pointer, const char* source) {
extern "C" void __real_radio_call_trampoline(void*);
extern "C" void __wrap_radio_call_trampoline(void* arg) {
auto* request = static_cast<radio_call_request*>(arg);
bindingValid &= request->inst == executingCore; observed.append(*request);
if (request->inst != executingCore) {
bindingValid = false;
DEBUG_ERROR(DEBUG_CATEGORY_RADIO, "Radio command binding mismatch id=%llu request_inst=%p executing_core=%p",
(unsigned long long)request->id, static_cast<void*>(request->inst), static_cast<void*>(executingCore));
}
observed.append(*request);
__real_radio_call_trampoline(arg);
}
// Настоящий mixer остаётся включённым; null backend устраняет зависимость от аппаратных устройств.
@@ -199,18 +204,33 @@ private slots:
#ifdef UTUN_TEST_RADIO_HOOKS
watching = true;
#endif
std::thread producer([] {
for (uint64_t id = 1; id <= 96; ++id) gui_bridge_radio_call(GUI_RADIO_CALL_CANCEL, id, 0, 0);
std::atomic<unsigned> turn{0}, submitted{0};
std::thread producer([&] {
for (unsigned id = 1; id <= 96; ++id) {
while (turn < id) std::this_thread::yield();
gui_bridge_radio_call(GUI_RADIO_CALL_CANCEL, id, 0, 0); submitted = id;
}
});
for (unsigned i = 0; i < 64; ++i) {
gui_bridge_set_inst(nullptr); auto& core = i % 2 ? first : second; core.attach(); core.poll();
bool controlsAccepted = true;
for (unsigned i = 1; i <= 96; ++i) {
turn = i;
gui_bridge_set_inst(nullptr); auto& core = i % 2 ? first : second; core.attach();
while (submitted < i) std::this_thread::yield();
first.poll(); second.poll();
// Контроль исполняется у каждого владельца даже при отклонении/отмене конкурирующей команды.
controlsAccepted &= gui_bridge_radio_call(GUI_RADIO_CALL_CANCEL, 1000 + i, 0, 0) == 0; core.poll();
}
producer.join(); gui_bridge_set_inst(nullptr); first.attach();
QCOMPARE(gui_bridge_radio_call(GUI_RADIO_CALL_START, 1000, 7654321, 42), 0); first.poll();
QVERIFY(bindingValid); QCOMPARE(pending(first.ua), 0u); QCOMPARE(pending(second.ua), 0u);
QCOMPARE(gui_bridge_radio_call(GUI_RADIO_CALL_START, 2000, 7654321, 42), 0); first.poll();
QVERIFY(controlsAccepted); QVERIFY(bindingValid); QCOMPARE(pending(first.ua), 0u); QCOMPARE(pending(second.ua), 0u);
QCOMPARE(first.results, 1u); QCOMPARE(second.results, 0u); QVERIFY(first.resultValid);
#ifdef UTUN_TEST_RADIO_HOOKS
QVERIFY(!observed.isEmpty()); QCOMPARE(observed.back().inst, first.inst); QCOMPARE(observed.back().id, uint64_t(1000));
size_t firstCount = 0, secondCount = 0;
for (const auto& request : observed) { firstCount += request.inst == first.inst; secondCount += request.inst == second.inst; }
QVERIFY(firstCount >= 49); QVERIFY(secondCount >= 48);
QCOMPARE(observed.back().inst, first.inst); QCOMPARE(observed.back().id, uint64_t(2000));
DEBUG_INFO(DEBUG_CATEGORY_RADIO, "Radio binding verified: first_executed=%zu second_executed=%zu binding_valid=%d",
firstCount, secondCount, bindingValid);
#endif
}
};