]> https://gitweb.dealii.org/ - dealii.git/commitdiff
Use taskflow to create asynchronous tasks. 16976/head
authorTimo Heister <timo.heister@gmail.com>
Mon, 3 Jul 2023 21:15:33 +0000 (17:15 -0400)
committerWolfgang Bangerth <bangerth@colostate.edu>
Wed, 8 May 2024 01:57:55 +0000 (07:27 +0530)
include/deal.II/base/thread_management.h

index da68b4520afbbed36f8e25dec98ab9fdd4284e2c..db0a9b7a60e8c7d08ef3d8889ef967ac20d4bce3 100644 (file)
 #include <deal.II/base/mutex.h>
 #include <deal.II/base/template_constraints.h>
 
+#ifdef DEAL_II_WITH_TASKFLOW
+#  include <taskflow/taskflow.hpp>
+#endif
+
 #include <atomic>
 #include <functional>
 #include <future>
@@ -498,7 +502,10 @@ namespace Threads
     {
       if (MultithreadInfo::n_threads() > 1)
         {
-#ifdef DEAL_II_WITH_TBB
+#ifdef DEAL_II_WITH_TASKFLOW
+          task_data = std::make_shared<TaskData>(
+            MultithreadInfo::get_taskflow_executor().async(function_object));
+#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
           // explained below, we can't just create a std::promise object,
@@ -939,7 +946,42 @@ namespace Threads
           return;
         else
           {
-#ifdef DEAL_II_WITH_TBB
+#ifdef DEAL_II_WITH_TASKFLOW
+            // We want to call executor.corun_until() to keep scheduling tasks
+            // until the task we are waiting for has actually finished. The
+            // problem is that TaskFlow documents that you can only call
+            // corun_until() on a worker of the executor. In other words, we
+            // can call it from *inside* other tasks, but not from the main
+            // thread (or other threads that might have been created outside
+            // of TaskFlow).
+            //
+            // Fortunately, we can check whether we are on a worker thread:
+            if (MultithreadInfo::get_taskflow_executor().this_worker_id() >= 0)
+              MultithreadInfo::get_taskflow_executor().corun_until([this]() {
+                return (future.wait_for(std::chrono::seconds(0)) ==
+                        std::future_status::ready);
+              });
+            else
+              // We are on a thread not managed by TaskFlow. In that case, we
+              // can simply stop the current thread to wait for the task to
+              // finish (i.e., for the std::future object to become ready). We
+              // can do this because we need not fear that this leads to a
+              // deadlock: The current threads is waiting for completion of a
+              // task that is running on a completely different set of
+              // threads, and so not making any progress here can not deprive
+              // these other threads of the ability to schedule their tasks.
+              //
+              // Indeed, this is even true if the current thread is a worker
+              // of one executor and we are waiting for a task running on a
+              // different executor: The current task being stopped may block
+              // the current executor from scheduling more tasks, but it is
+              // unrelated to the tasks of the scheduler for which we are
+              // waiting for something, and so that other executor will
+              // eventually get arond to scheduling the task we are waiting
+              // for, at which point the current task will also complete.
+              future.wait();
+
+#elif defined(DEAL_II_WITH_TBB)
             // If we build on the TBB, then we can't just wait for the
             // std::future object to get ready. Apparently the TBB happily
             // enqueues a task into an arena and then just sits on it without

In the beginning the Universe was created. This has made a lot of people very angry and has been widely regarded as a bad move.

Douglas Adams


Typeset in Trocchi and Trocchi Bold Sans Serif.