]> https://gitweb.dealii.org/ - dealii.git/commitdiff
Ensure propagation of exceptions from tasks.
authorWolfgang Bangerth <bangerth@colostate.edu>
Wed, 27 May 2020 20:32:53 +0000 (14:32 -0600)
committerWolfgang Bangerth <bangerth@colostate.edu>
Wed, 27 May 2020 20:32:53 +0000 (14:32 -0600)
include/deal.II/base/thread_management.h
tests/base/task_01_exception.cc [new file with mode: 0644]
tests/base/task_01_exception.with_threads=on.output [new file with mode: 0644]
tests/base/task_01_exception_02.cc [new file with mode: 0644]
tests/base/task_01_exception_02.with_threads=on.output [new file with mode: 0644]

index 73bc007643f848059cc6a2c9a304ddaac315262f..bb7d2a59ff108fee9f983e3a4ae5b6e15ba9c570 100644 (file)
@@ -1004,6 +1004,26 @@ namespace Threads
      * may block until the task has completed running, all successive attempts
      * to join will return immediately).
      *
+     * If the operation that was executed on the task with which this
+     * object was initialized throws an exception instead of returning
+     * regularly, then calling the current join() function will first
+     * wait for that task to finish, and then in turn throw the
+     * exception that the task operation had thrown originally. This
+     * allows for the propagation of exceptions from tasks executed on
+     * a separate thread to the calling thread.
+     *
+     * (This behavior differs from that of
+     * [`std::future`](https://en.cppreference.com/w/cpp/thread/future),
+     * where the `std::future::wait()` function only waits for
+     * completion of the operation, whereas the exception is
+     * propagated only once one calls `std::future::get()`. However,
+     * this is awkward when putting `void` functions onto separate
+     * tasks because these do not actually return anything;
+     * consequently, it is more natural to call `std::task::wait()`
+     * for such tasks than the `std::task::get()` function since the
+     * latter does not, actually, return anything that could be
+     * gotten.)
+     *
      * @pre You can't call this function if you have used the default
      * constructor of this class and have not assigned a task object to it. In
      * other words, the function joinable() must return true.
@@ -1075,6 +1095,12 @@ namespace Threads
      * to be moved, and in order to be moved, the current function needs to
      * return a writable (non-@p const) reference.
      *
+     * This function internally calls the join() member function. As a
+     * consequence, and as explained there, if the packaged task
+     * throws an exception that is then re-thrown by the join()
+     * function and consequently also the current function if you have
+     * not previously called join().
+     *
      * @pre You can't call this function if you have used the default
      * constructor of this class and have not assigned a task object to it. In
      * other words, the function joinable() must return true.
diff --git a/tests/base/task_01_exception.cc b/tests/base/task_01_exception.cc
new file mode 100644 (file)
index 0000000..3659075
--- /dev/null
@@ -0,0 +1,52 @@
+// ---------------------------------------------------------------------
+//
+// Copyright (C) 2009 - 2018 by the deal.II authors
+//
+// This file is part of the deal.II library.
+//
+// The deal.II library is free software; you can use it, redistribute
+// it, and/or modify it under the terms of the GNU Lesser General
+// Public License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+// The full text of the license can be found in the file LICENSE.md at
+// the top level directory of deal.II.
+//
+// ---------------------------------------------------------------------
+
+
+// Like the _01 test, but check that if the function called on the
+// task throws an exception, that Task::wait() continues to work but
+// Task::return_value() throws the exception.
+
+#include <deal.II/base/thread_management.h>
+
+#include "../tests.h"
+
+
+int
+test()
+{
+  std::this_thread::sleep_for(std::chrono::seconds(3));
+
+  throw 42;
+}
+
+
+int
+main()
+{
+  initlog();
+
+  Threads::Task<int> t = Threads::new_task(test);
+
+  // Here, an exception should be triggered:
+  try
+    {
+      t.join();
+    }
+  catch (int i)
+    {
+      Assert(i == 42, ExcInternalError());
+      deallog << "OK" << std::endl;
+    }
+}
diff --git a/tests/base/task_01_exception.with_threads=on.output b/tests/base/task_01_exception.with_threads=on.output
new file mode 100644 (file)
index 0000000..0fd8fc1
--- /dev/null
@@ -0,0 +1,2 @@
+
+DEAL::OK
diff --git a/tests/base/task_01_exception_02.cc b/tests/base/task_01_exception_02.cc
new file mode 100644 (file)
index 0000000..895c9d1
--- /dev/null
@@ -0,0 +1,52 @@
+// ---------------------------------------------------------------------
+//
+// Copyright (C) 2009 - 2018 by the deal.II authors
+//
+// This file is part of the deal.II library.
+//
+// The deal.II library is free software; you can use it, redistribute
+// it, and/or modify it under the terms of the GNU Lesser General
+// Public License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+// The full text of the license can be found in the file LICENSE.md at
+// the top level directory of deal.II.
+//
+// ---------------------------------------------------------------------
+
+
+// Like the _01 test, but check that if the function called on the
+// task throws an exception, that Task::wait() continues to work but
+// Task::return_value() throws the exception.
+
+#include <deal.II/base/thread_management.h>
+
+#include "../tests.h"
+
+
+int
+test()
+{
+  std::this_thread::sleep_for(std::chrono::seconds(3));
+
+  throw 42;
+}
+
+
+int
+main()
+{
+  initlog();
+
+  Threads::Task<int> t = Threads::new_task(test);
+
+  // Here, an exception should be triggered:
+  try
+    {
+      t.return_value();
+    }
+  catch (int i)
+    {
+      Assert(i == 42, ExcInternalError());
+      deallog << "OK" << std::endl;
+    }
+}
diff --git a/tests/base/task_01_exception_02.with_threads=on.output b/tests/base/task_01_exception_02.with_threads=on.output
new file mode 100644 (file)
index 0000000..0fd8fc1
--- /dev/null
@@ -0,0 +1,2 @@
+
+DEAL::OK

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.