Skip to content

Commit 23e0602

Browse files
test(core): route qa_ManagedSubGraph sub-scheduler to its own thread pool
qa_ManagedSubGraph deliberately caps the default_cpu pool at 2 threads to mimic the GitHub CI's two-core runner. Under multiThreaded-over-multiThreaded nesting this becomes a structural deadlock: the outer scheduler holds both slots running its workers, the sub-scheduler dispatches its own poolWorker, no thread is available, and the outer worker stays stuck inside the sub-scheduler block waiting for the sub-scheduler to start. Register a second 2-thread pool ("sub_scheduler_cpu") and pass its name into the SchedulerWrapper via the `poolName` setting, so the sub-scheduler always has its own slots to spin up in and outer and inner never contend for the same resources. Signed-off-by: Ralph J. Steinhagen <r.steinhagen@gsi.de>
1 parent 6bb83ff commit 23e0602

1 file changed

Lines changed: 12 additions & 1 deletion

File tree

core/test/qa_ManagedSubGraph.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,12 @@ struct DemoSubSchedulerResult {
4040

4141
DemoSubSchedulerResult() {}
4242

43+
// Dedicated pool for nested sub-schedulers: prevents deadlock when the outer
44+
// multiThreaded scheduler already holds every slot of the 2-thread default_cpu pool.
45+
static constexpr std::string_view kSubSchedulerPoolId = "sub_scheduler_cpu";
46+
4347
void setGraph(gr::Graph&& graph) {
44-
scheduler = std::static_pointer_cast<BlockModel>(std::make_shared<Wrapper>());
48+
scheduler = std::static_pointer_cast<BlockModel>(std::make_shared<Wrapper>(gr::property_map{{"poolName", std::string(kSubSchedulerPoolId)}}));
4549
schedulerUniqueName = scheduler->uniqueName();
4650
wrapper = static_cast<Wrapper*>(scheduler.get());
4751
wrapper->setGraph(std::move(graph));
@@ -95,6 +99,13 @@ const boost::ut::suite BasicSchedulerWrapperTests = [] {
9599
void setCustomDefaultThreadPool() {
96100
auto cpu = std::make_shared<thread_pool::ThreadPoolWrapper>(std::make_unique<thread_pool::BasicThreadPool>(std::string(thread_pool::kDefaultCpuPoolId), thread_pool::TaskType::CPU_BOUND, 2U, 2U), "CPU");
97101
gr::thread_pool::Manager::instance().replacePool(std::string(thread_pool::kDefaultCpuPoolId), std::move(cpu));
102+
103+
auto subCpu = std::make_shared<thread_pool::ThreadPoolWrapper>(std::make_unique<thread_pool::BasicThreadPool>(std::string(DemoSubSchedulerResult<float>::kSubSchedulerPoolId), thread_pool::TaskType::CPU_BOUND, 2U, 2U), "CPU");
104+
try {
105+
gr::thread_pool::Manager::instance().registerPool(std::string(DemoSubSchedulerResult<float>::kSubSchedulerPoolId), std::move(subCpu));
106+
} catch (const std::invalid_argument&) {
107+
// already registered by a previous test suite — reuse existing instance
108+
}
98109
}
99110

100111
const boost::ut::suite ManagedSubGraph = [] {

0 commit comments

Comments
 (0)