Skip to content

Commit d712924

Browse files
committed
no tasks from inside tasks
1 parent a692dee commit d712924

File tree

1 file changed

+46
-34
lines changed

1 file changed

+46
-34
lines changed

include/deal.II/base/thread_management.h

Lines changed: 46 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -505,8 +505,19 @@ namespace Threads
505505
if (MultithreadInfo::n_threads() > 1)
506506
{
507507
#ifdef DEAL_II_WITH_TASKFLOW
508-
task_data = std::make_shared<TaskData>(
509-
MultithreadInfo::get_taskflow_executor().async(function_object));
508+
// Detect if we are running in a TaskFlow worker thread or on
509+
// the main thread. We only schedule an async task in the latter
510+
// case and will eagerly run the task otherwise. This is to avoid
511+
// potential deadlocks with locks being taken within tasks.
512+
// Performance gain from scheduling tasks from within tasks is
513+
// questionable at best anyways.
514+
if (MultithreadInfo::get_taskflow_executor().this_worker_id() < 0)
515+
{
516+
task_data = std::make_shared<TaskData>(
517+
MultithreadInfo::get_taskflow_executor().async(
518+
function_object));
519+
return;
520+
}
510521
#elif defined(DEAL_II_WITH_TBB)
511522
// Create a promise object and from it extract a future that
512523
// we can use to refer to the outcome of the task. For reasons
@@ -574,6 +585,7 @@ namespace Threads
574585
}
575586
}
576587
});
588+
return;
577589

578590
#else
579591
// If no threading library is supported, just fall back onto C++11
@@ -596,40 +608,40 @@ namespace Threads
596608
task_data = std::make_shared<TaskData>(
597609
std::async(std::launch::async | std::launch::deferred,
598610
function_object));
611+
return;
599612
#endif
600613
}
601-
else
602-
{
603-
// Only one thread allowed. So let the task run to completion
604-
// and just emplace a 'ready' future.
605-
//
606-
// The design of std::promise/std::future is unclear, but it
607-
// seems that the intent is to obtain the std::future before
608-
// we set the std::promise. So create the TaskData object at
609-
// the top and then run the task and set the returned
610-
// value. Since everything here happens sequentially, it
611-
// really doesn't matter in which order all of this is
612-
// happening.
613-
std::promise<RT> promise;
614-
task_data = std::make_shared<TaskData>(promise.get_future());
615-
try
616-
{
617-
internal::evaluate_and_set_promise(function_object, promise);
618-
}
619-
catch (...)
620-
{
621-
try
622-
{
623-
// store anything thrown in the promise
624-
promise.set_exception(std::current_exception());
625-
}
626-
catch (...)
627-
{
628-
// set_exception() may throw too. But ignore this on
629-
// the task.
630-
}
631-
}
632-
}
614+
{
615+
// Only one thread allowed. So let the task run to completion
616+
// and just emplace a 'ready' future.
617+
//
618+
// The design of std::promise/std::future is unclear, but it
619+
// seems that the intent is to obtain the std::future before
620+
// we set the std::promise. So create the TaskData object at
621+
// the top and then run the task and set the returned
622+
// value. Since everything here happens sequentially, it
623+
// really doesn't matter in which order all of this is
624+
// happening.
625+
std::promise<RT> promise;
626+
task_data = std::make_shared<TaskData>(promise.get_future());
627+
try
628+
{
629+
internal::evaluate_and_set_promise(function_object, promise);
630+
}
631+
catch (...)
632+
{
633+
try
634+
{
635+
// store anything thrown in the promise
636+
promise.set_exception(std::current_exception());
637+
}
638+
catch (...)
639+
{
640+
// set_exception() may throw too. But ignore this on
641+
// the task.
642+
}
643+
}
644+
}
633645
}
634646

635647
/**

0 commit comments

Comments
 (0)