@@ -505,8 +505,19 @@ namespace Threads
505
505
if (MultithreadInfo::n_threads () > 1 )
506
506
{
507
507
#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
+ }
510
521
#elif defined(DEAL_II_WITH_TBB)
511
522
// Create a promise object and from it extract a future that
512
523
// we can use to refer to the outcome of the task. For reasons
@@ -574,6 +585,7 @@ namespace Threads
574
585
}
575
586
}
576
587
});
588
+ return ;
577
589
578
590
#else
579
591
// If no threading library is supported, just fall back onto C++11
@@ -596,40 +608,40 @@ namespace Threads
596
608
task_data = std::make_shared<TaskData>(
597
609
std::async (std::launch::async | std::launch::deferred,
598
610
function_object));
611
+ return ;
599
612
#endif
600
613
}
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
+ }
633
645
}
634
646
635
647
/* *
0 commit comments