From fa0ab070717f4a86e826b6a958c8f7773baedca8 Mon Sep 17 00:00:00 2001 From: Timo Heister Date: Wed, 11 Dec 2024 16:26:49 -0500 Subject: [PATCH] do not launch taskflow tasks from inside tasks This works around the deadlocks reported in #17669 --- include/deal.II/base/thread_management.h | 75 +++++++++++++----------- 1 file changed, 41 insertions(+), 34 deletions(-) diff --git a/include/deal.II/base/thread_management.h b/include/deal.II/base/thread_management.h index 99a96de26d..32364ddcba 100644 --- a/include/deal.II/base/thread_management.h +++ b/include/deal.II/base/thread_management.h @@ -505,8 +505,14 @@ namespace Threads if (MultithreadInfo::n_threads() > 1) { #ifdef DEAL_II_WITH_TASKFLOW - task_data = std::make_shared( - MultithreadInfo::get_taskflow_executor().async(function_object)); + + if (MultithreadInfo::get_taskflow_executor().this_worker_id() < 0) + { + task_data = std::make_shared( + MultithreadInfo::get_taskflow_executor().async( + function_object)); + return; + } #elif defined(DEAL_II_WITH_TBB) // Create a promise object and from it extract a future that // we can use to refer to the outcome of the task. For reasons @@ -574,6 +580,7 @@ namespace Threads } } }); + return; #else // If no threading library is supported, just fall back onto C++11 @@ -596,40 +603,40 @@ namespace Threads task_data = std::make_shared( std::async(std::launch::async | std::launch::deferred, function_object)); + return; #endif } - else - { - // Only one thread allowed. So let the task run to completion - // and just emplace a 'ready' future. - // - // The design of std::promise/std::future is unclear, but it - // seems that the intent is to obtain the std::future before - // we set the std::promise. So create the TaskData object at - // the top and then run the task and set the returned - // value. Since everything here happens sequentially, it - // really doesn't matter in which order all of this is - // happening. - std::promise promise; - task_data = std::make_shared(promise.get_future()); - try - { - internal::evaluate_and_set_promise(function_object, promise); - } - catch (...) - { - try - { - // store anything thrown in the promise - promise.set_exception(std::current_exception()); - } - catch (...) - { - // set_exception() may throw too. But ignore this on - // the task. - } - } - } + { + // Only one thread allowed. So let the task run to completion + // and just emplace a 'ready' future. + // + // The design of std::promise/std::future is unclear, but it + // seems that the intent is to obtain the std::future before + // we set the std::promise. So create the TaskData object at + // the top and then run the task and set the returned + // value. Since everything here happens sequentially, it + // really doesn't matter in which order all of this is + // happening. + std::promise promise; + task_data = std::make_shared(promise.get_future()); + try + { + internal::evaluate_and_set_promise(function_object, promise); + } + catch (...) + { + try + { + // store anything thrown in the promise + promise.set_exception(std::current_exception()); + } + catch (...) + { + // set_exception() may throw too. But ignore this on + // the task. + } + } + } } /** -- 2.39.5