#include #include #include #include #include #include #include #include #include namespace { struct test_device final : kapi::devices::device { using kapi::devices::device::device; }; } // namespace constexpr auto thread_count = 8; constexpr auto devices_per_thread = 200; SCENARIO("Concurrent attach/detach/lookup on a bus is race-free") { GIVEN("A bus shared by several threads") { auto shared_bus = kstd::make_shared("stress_test_bus"); kapi::devices::get_root_bus()->add_child(shared_bus); WHEN("each thread repeatedly attaches, detaches, and looks up devices concurrently with the others") { auto threads = std::vector{}; threads.reserve(thread_count); auto failure_count = std::atomic{0}; for (auto thread_index = 0uz; thread_index < thread_count; ++thread_index) { threads.emplace_back([&shared_bus, &failure_count, thread_index] { for (auto i = 0uz; i < devices_per_thread; ++i) { auto name = kstd::format("stress_test_device_{}_{}", thread_index, i); auto device = kstd::make_shared(name); shared_bus->add_child(device); std::ignore = kapi::devices::device_registry::get().find(name); std::ignore = kapi::devices::device_registry::get().all(); std::ignore = shared_bus->children().size(); if (!shared_bus->remove_child(*device)) { ++failure_count; }; } }); } threads.clear(); THEN("every attach was matched by a successful removal") { REQUIRE(failure_count == 0); } THEN("the bus has no children") { REQUIRE(shared_bus->children().empty()); } } } }