mirror of
https://github.com/torlando-tech/pyxis.git
synced 2026-08-03 09:19:49 +00:00
test: strengthen call admission contention coverage
This commit is contained in:
@@ -10,9 +10,10 @@
|
||||
namespace UI {
|
||||
namespace LXMF {
|
||||
|
||||
// Lock-free admission guard for call setup. Each successful reservation owns a
|
||||
// Atomic admission guard for call setup. Each successful reservation owns a
|
||||
// generation until that exact generation releases it; generation zero always
|
||||
// means "unowned".
|
||||
// means "unowned". Generations eventually repeat after the finite token space
|
||||
// wraps, so a token retained for a full cycle can exhibit ABA.
|
||||
class CallGenerationGuard {
|
||||
public:
|
||||
static constexpr uint32_t MAX_GENERATION = 0x7fffffffu;
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@ System Python 3.9 has pytest pre-installed; Homebrew Python does not.
|
||||
- `native/test_ring_buffers.{cpp,py}` — PCM + encoded SPSC ring buffers, including 100k-frame multithreaded producer/consumer stress
|
||||
- `native/test_audio_filters.{cpp,py}` — VoiceFilterChain frequency response, peak limiting, multichannel
|
||||
- `native/test_call_command_mailbox.{cpp,py}` — generation-scoped LXST hangup/mute command handoff and producer/consumer stress
|
||||
- `native/test_call_generation_guard.{cpp,py}` — lock-free, generation-scoped call admission, stale-owner protection, and two-thread reservation races
|
||||
- `native/test_call_generation_guard.{cpp,py}` — atomic, generation-scoped call admission, stale-owner protection, and repeated two-thread reservation races
|
||||
|
||||
### Adding a new native C++ test
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <functional>
|
||||
#include <stdexcept>
|
||||
#include <thread>
|
||||
|
||||
@@ -89,23 +90,34 @@ static void next_reservation_has_distinct_generation() {
|
||||
}
|
||||
|
||||
static void exactly_one_thread_wins_reservation_race() {
|
||||
CallGenerationGuard guard;
|
||||
std::atomic<bool> start{false};
|
||||
uint32_t first = 0;
|
||||
uint32_t second = 0;
|
||||
std::thread a([&] {
|
||||
while (!start.load(std::memory_order_acquire)) std::this_thread::yield();
|
||||
first = guard.tryReserve();
|
||||
});
|
||||
std::thread b([&] {
|
||||
while (!start.load(std::memory_order_acquire)) std::this_thread::yield();
|
||||
second = guard.tryReserve();
|
||||
});
|
||||
start.store(true, std::memory_order_release);
|
||||
a.join();
|
||||
b.join();
|
||||
EXPECT_TRUE((first == 0) != (second == 0));
|
||||
EXPECT_EQ(guard.current(), first != 0 ? first : second);
|
||||
constexpr int kRaceIterations = 1000;
|
||||
for (int iteration = 0; iteration < kRaceIterations; ++iteration) {
|
||||
CallGenerationGuard guard;
|
||||
std::atomic<unsigned> ready{0};
|
||||
std::atomic<bool> start{false};
|
||||
uint32_t first = 0;
|
||||
uint32_t second = 0;
|
||||
|
||||
auto reserve = [&](uint32_t& result) {
|
||||
ready.fetch_add(1, std::memory_order_release);
|
||||
while (!start.load(std::memory_order_acquire)) {
|
||||
std::this_thread::yield();
|
||||
}
|
||||
result = guard.tryReserve();
|
||||
};
|
||||
|
||||
std::thread a(reserve, std::ref(first));
|
||||
std::thread b(reserve, std::ref(second));
|
||||
while (ready.load(std::memory_order_acquire) != 2) {
|
||||
std::this_thread::yield();
|
||||
}
|
||||
start.store(true, std::memory_order_release);
|
||||
a.join();
|
||||
b.join();
|
||||
|
||||
EXPECT_TRUE((first == 0) != (second == 0));
|
||||
EXPECT_EQ(guard.current(), first != 0 ? first : second);
|
||||
}
|
||||
}
|
||||
|
||||
static void stale_token_cannot_release_new_winner() {
|
||||
|
||||
@@ -20,6 +20,7 @@ def test_call_generation_guard(tmp_path):
|
||||
"-std=c++17",
|
||||
"-Wall",
|
||||
"-Wextra",
|
||||
"-Werror",
|
||||
"-pthread",
|
||||
str(TEST_SOURCE),
|
||||
"-o",
|
||||
|
||||
Reference in New Issue
Block a user