]> https://gitweb.dealii.org/ - dealii.git/commitdiff
Generalize new_task and new_thread using variadic templates
authorDaniel Arndt <daniel.arndt@iwr.uni-heidelberg.de>
Thu, 31 Aug 2017 22:24:11 +0000 (00:24 +0200)
committerDaniel Arndt <daniel.arndt@iwr.uni-heidelberg.de>
Thu, 31 Aug 2017 23:33:09 +0000 (01:33 +0200)
include/deal.II/base/thread_management.h

index 3124845537f07fca0747f506bd875c4acd2bb427..cc7c19246581ad6adc21fd48a5bc031e3181512e 100644 (file)
@@ -1823,2344 +1823,701 @@ namespace Threads
 
   /**
    * Overload of the new_thread function for non-member or static member
-   * functions with no arguments.
+   * functions.
    *
    * @ingroup threads
    */
-  template <typename RT>
-  inline
-  Thread<RT>
-  new_thread (RT (*fun_ptr)())
-  {
-    return new_thread (std::function<RT ()> (fun_ptr));
-  }
-
-
-  /**
-   * Overload of the non-const new_thread function for member functions with
-   * no arguments.
-   *
-   * @ingroup threads
-   */
-  template <typename RT, typename C>
-  inline
-  Thread<RT>
-  new_thread (RT (C::*fun_ptr)(),
-              typename identity<C>::type &c)
-  {
-    return
-      new_thread (std::function<RT ()>
-                  (std::bind(fun_ptr, std::ref(c))));
-  }
-
-
-#ifndef DEAL_II_CONST_MEMBER_DEDUCTION_BUG
-  /**
-   * Overload of the new_thread function for const member functions with no
-   * arguments.
-   *
-   * @ingroup threads
-   */
-  template <typename RT, typename C>
-  inline
-  Thread<RT>
-  new_thread (RT (C::*fun_ptr)() const,
-              const typename identity<C>::type &c)
-  {
-    return
-      new_thread (std::function<RT ()>
-                  (std::bind(fun_ptr, std::cref(c))));
-  }
-#endif
-
-
-
-// ----------- thread starters for unary functions
-
-  /**
-   * Overload of the new_thread function for non-member or static member
-   * functions with 1 argument.
-   *
-   * @ingroup threads
-   */
-  template <typename RT, typename Arg1>
+  template <typename RT, typename... Args>
   inline
   Thread<RT>
-  new_thread (RT (*fun_ptr)(Arg1),
-              typename identity<Arg1>::type arg1)
+  new_thread (RT (*fun_ptr)(Args...),
+              typename identity<Args>::type... args)
   {
     return
       new_thread (std::function<RT ()>
                   (std::bind(fun_ptr,
-                             internal::maybe_make_ref<Arg1>::act(arg1))));
+                             internal::maybe_make_ref<Args>::act(args)...)));
   }
 
 
 
   /**
-   * Overload of the non-const new_thread function for member functions with 1
-   * argument.
+   * Overload of the non-const new_thread function for member functions.
    *
    * @ingroup threads
    */
-  template <typename RT, typename C, typename Arg1>
+  template <typename RT, typename C, typename... Args>
   inline
   Thread<RT>
-  new_thread (RT (C::*fun_ptr)(Arg1),
+  new_thread (RT (C::*fun_ptr)(Args...),
               typename identity<C>::type &c,
-              typename identity<Arg1>::type arg1)
+              typename identity<Args>::type... args)
   {
     return
       new_thread (std::function<RT ()>
                   (std::bind(fun_ptr, std::ref(c),
-                             internal::maybe_make_ref<Arg1>::act(arg1))));
+                             internal::maybe_make_ref<Args>::act(args)...)));
   }
 
 #ifndef DEAL_II_CONST_MEMBER_DEDUCTION_BUG
   /**
-   * Overload of the new_thread function for const member functions with 1
-   * argument.
+   * Overload of the new_thread function for const member functions.
    *
    * @ingroup threads
    */
-  template <typename RT, typename C, typename Arg1>
+  template <typename RT, typename C, typename... Args>
   inline
   Thread<RT>
-  new_thread (RT (C::*fun_ptr)(Arg1) const,
+  new_thread (RT (C::*fun_ptr)(Args...) const,
               typename identity<const C>::type &c,
-              typename identity<Arg1>::type arg1)
+              typename identity<Args>::type... args)
   {
     return
       new_thread (std::function<RT ()>
                   (std::bind(fun_ptr, std::cref(c),
-                             internal::maybe_make_ref<Arg1>::act(arg1))));
+                             internal::maybe_make_ref<Args>::act(args)...)));
   }
 #endif
 
-// ----------- thread starters for binary functions
+// ------------------------ ThreadGroup -------------------------------------
 
   /**
-   * Overload of the new_thread function for non-member or static member
-   * functions with 2 arguments.
+   * A container for thread objects. Allows to add new thread objects and wait
+   * for them all together. The thread objects need to have the same return
+   * value for the called function.
    *
+   * @author Wolfgang Bangerth, 2003
    * @ingroup threads
    */
-  template <typename RT, typename Arg1, typename Arg2>
-  inline
-  Thread<RT>
-  new_thread (RT (*fun_ptr)(Arg1,Arg2),
-              typename identity<Arg1>::type arg1,
-              typename identity<Arg2>::type arg2)
+  template <typename RT = void>
+  class ThreadGroup
   {
-    return
-      new_thread (std::function<RT ()>
-                  (std::bind(fun_ptr,
-                             internal::maybe_make_ref<Arg1>::act(arg1),
-                             internal::maybe_make_ref<Arg2>::act(arg2))));
-  }
+  public:
+    /**
+     * Add another thread object to the collection.
+     */
+    ThreadGroup &operator += (const Thread<RT> &t)
+    {
+      threads.push_back (t);
+      return *this;
+    }
 
+    /**
+     * Wait for all threads in the collection to finish. It is not a problem
+     * if some of them have already been waited for, i.e. you may call this
+     * function more than once, and you can also add new thread objects
+     * between subsequent calls to this function if you want.
+     */
+    void join_all () const
+    {
+      for (typename std::list<Thread<RT> >::const_iterator
+           t=threads.begin(); t!=threads.end(); ++t)
+        t->join ();
+    }
 
+  private:
+    /**
+     * List of thread objects.
+     */
+    std::list<Thread<RT> > threads;
+  };
 
-  /**
-   * Overload of the non-const new_thread function for member functions with 2
-   * arguments.
-   *
-   * @ingroup threads
-   */
-  template <typename RT, typename C, typename Arg1, typename Arg2>
-  inline
-  Thread<RT>
-  new_thread (RT (C::*fun_ptr)(Arg1,Arg2),
-              typename identity<C>::type &c,
-              typename identity<Arg1>::type arg1,
-              typename identity<Arg2>::type arg2)
-  {
-    return
-      new_thread (std::function<RT ()>
-                  (std::bind(fun_ptr, std::ref(c),
-                             internal::maybe_make_ref<Arg1>::act(arg1),
-                             internal::maybe_make_ref<Arg2>::act(arg2))));
-  }
 
-#ifndef DEAL_II_CONST_MEMBER_DEDUCTION_BUG
-  /**
-   * Overload of the new_thread function for const member functions with 2
-   * arguments.
-   *
-   * @ingroup threads
-   */
-  template <typename RT, typename C, typename Arg1, typename Arg2>
-  inline
-  Thread<RT>
-  new_thread (RT (C::*fun_ptr)(Arg1,Arg2) const,
-              typename identity<const C>::type &c,
-              typename identity<Arg1>::type arg1,
-              typename identity<Arg2>::type arg2)
-  {
-    return
-      new_thread (std::function<RT ()>
-                  (std::bind(fun_ptr, std::cref(c),
-                             internal::maybe_make_ref<Arg1>::act(arg1),
-                             internal::maybe_make_ref<Arg2>::act(arg2))));
-  }
-#endif
+  template <typename> class Task;
 
-// ----------- thread starters for ternary functions
 
-  /**
-   * Overload of the new_thread function for non-member or static member
-   * functions with 3 arguments.
-   *
-   * @ingroup threads
-   */
-  template <typename RT,
-            typename Arg1, typename Arg2, typename Arg3>
-  inline
-  Thread<RT>
-  new_thread (RT (*fun_ptr)(Arg1,Arg2,Arg3),
-              typename identity<Arg1>::type arg1,
-              typename identity<Arg2>::type arg2,
-              typename identity<Arg3>::type arg3)
+  namespace internal
   {
-    return
-      new_thread (std::function<RT ()>
-                  (std::bind(fun_ptr,
-                             internal::maybe_make_ref<Arg1>::act(arg1),
-                             internal::maybe_make_ref<Arg2>::act(arg2),
-                             internal::maybe_make_ref<Arg3>::act(arg3))));
-  }
+#ifdef DEAL_II_WITH_THREADS
 
+    template <typename> struct TaskDescriptor;
 
+    /**
+     * The task class for TBB that is used by the TaskDescriptor class.
+     */
+    template <typename RT>
+    struct TaskEntryPoint : public tbb::task
+    {
+      TaskEntryPoint (TaskDescriptor<RT> &task_descriptor)
+        :
+        task_descriptor (task_descriptor)
+      {}
 
-  /**
-   * Overload of the non-const new_thread function for member functions with 3
-   * arguments.
-   *
-   * @ingroup threads
-   */
-  template <typename RT, typename C,
-            typename Arg1, typename Arg2, typename Arg3>
-  inline
-  Thread<RT>
-  new_thread (RT (C::*fun_ptr)(Arg1,Arg2,Arg3),
-              typename identity<C>::type &c,
-              typename identity<Arg1>::type arg1,
-              typename identity<Arg2>::type arg2,
-              typename identity<Arg3>::type arg3)
-  {
-    return
-      new_thread (std::function<RT ()>
-                  (std::bind(fun_ptr, std::ref(c),
-                             internal::maybe_make_ref<Arg1>::act(arg1),
-                             internal::maybe_make_ref<Arg2>::act(arg2),
-                             internal::maybe_make_ref<Arg3>::act(arg3))));
-  }
+      virtual tbb::task *execute ()
+      {
+        // call the function object and put the return value into the
+        // proper place
+        try
+          {
+            call (task_descriptor.function, task_descriptor.ret_val);
+          }
+        catch (const std::exception &exc)
+          {
+            internal::handle_std_exception (exc);
+          }
+        catch (...)
+          {
+            internal::handle_unknown_exception ();
+          }
+        return nullptr;
+      }
 
-#ifndef DEAL_II_CONST_MEMBER_DEDUCTION_BUG
-  /**
-   * Overload of the new_thread function for const member functions with 3
-   * arguments.
-   *
-   * @ingroup threads
-   */
-  template <typename RT, typename C,
-            typename Arg1, typename Arg2, typename Arg3>
-  inline
-  Thread<RT>
-  new_thread (RT (C::*fun_ptr)(Arg1,Arg2,Arg3) const,
-              typename identity<const C>::type &c,
-              typename identity<Arg1>::type arg1,
-              typename identity<Arg2>::type arg2,
-              typename identity<Arg3>::type arg3)
-  {
-    return
-      new_thread (std::function<RT ()>
-                  (std::bind(fun_ptr, std::cref(c),
-                             internal::maybe_make_ref<Arg1>::act(arg1),
-                             internal::maybe_make_ref<Arg2>::act(arg2),
-                             internal::maybe_make_ref<Arg3>::act(arg3))));
-  }
-#endif
+      /**
+       * A reference to the descriptor object of this task.
+       */
+      TaskDescriptor<RT> &task_descriptor;
+    };
 
+    /**
+     * @internal
+     *
+     * Base class describing a task. This is the basic class abstracting the
+     * Threading Building Blocks implementation of tasks.  It provides a
+     * mechanism to start a new task, as well as for joining it.
+     *
+     * Internally, the way things are implemented is that all Task<> objects
+     * keep a shared pointer to the task descriptor. When the last Task<>
+     * object goes out of scope, the destructor of the descriptor is called.
+     * Since tasks can not be abandoned, the destructor makes sure that the
+     * task is finished before it can continue to destroy the object.
+     *
+     * Note that unlike threads, tasks are not always started right away, and
+     * so the starting thread can't rely on the fact that the started task can
+     * copy things off the spawning thread's stack frame. As a consequence,
+     * the task description needs to include a way to store the function and
+     * its arguments that shall be run on the task.
+     *
+     * @author Wolfgang Bangerth, 2009
+     */
+    template <typename RT>
+    struct TaskDescriptor
+    {
+    private:
+      /**
+       * The function and its arguments that are to be run on the task.
+       */
+      std::function<RT ()> function;
 
-// ----------- thread starters for functions with 4 arguments
+      /**
+       * Variable holding the data the TBB needs to work with a task. Set by
+       * the queue_up_task() function. Note that the object behind this
+       * pointer will be deleted upon termination of the task, so we do not
+       * have to do so ourselves. In particular, if all objects with pointers
+       * to this task_description object go out of scope then no action is
+       * needed on our behalf.
+       */
+      tbb::task *task;
 
-  /**
-   * Overload of the new_thread function for non-member or static member
-   * functions with 4 arguments.
-   *
-   * @ingroup threads
-   */
-  template <typename RT,
-            typename Arg1, typename Arg2, typename Arg3, typename Arg4>
-  inline
-  Thread<RT>
-  new_thread (RT (*fun_ptr)(Arg1,Arg2,Arg3,Arg4),
-              typename identity<Arg1>::type arg1,
-              typename identity<Arg2>::type arg2,
-              typename identity<Arg3>::type arg3,
-              typename identity<Arg4>::type arg4)
-  {
-    return
-      new_thread (std::function<RT ()>
-                  (std::bind(fun_ptr,
-                             internal::maybe_make_ref<Arg1>::act(arg1),
-                             internal::maybe_make_ref<Arg2>::act(arg2),
-                             internal::maybe_make_ref<Arg3>::act(arg3),
-                             internal::maybe_make_ref<Arg4>::act(arg4))));
-  }
+      /**
+       * A place where the task will deposit its return value.
+       */
+      return_value<RT> ret_val;
 
+      /**
+       * A flag indicating whether the task has terminated.
+       */
+      bool task_is_done;
 
+    public:
 
-  /**
-   * Overload of the non-const new_thread function for member functions with 4
-   * arguments.
-   *
-   * @ingroup threads
-   */
-  template <typename RT, typename C,
-            typename Arg1, typename Arg2, typename Arg3, typename Arg4>
-  inline
-  Thread<RT>
-  new_thread (RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4),
-              typename identity<C>::type &c,
-              typename identity<Arg1>::type arg1,
-              typename identity<Arg2>::type arg2,
-              typename identity<Arg3>::type arg3,
-              typename identity<Arg4>::type arg4)
-  {
-    return
-      new_thread (std::function<RT ()>
-                  (std::bind(fun_ptr, std::ref(c),
-                             internal::maybe_make_ref<Arg1>::act(arg1),
-                             internal::maybe_make_ref<Arg2>::act(arg2),
-                             internal::maybe_make_ref<Arg3>::act(arg3),
-                             internal::maybe_make_ref<Arg4>::act(arg4))));
-  }
+      /**
+       * Constructor. Take the function to be run on this task as argument.
+       */
+      TaskDescriptor (const std::function<RT ()> &function);
 
-#ifndef DEAL_II_CONST_MEMBER_DEDUCTION_BUG
-  /**
-   * Overload of the new_thread function for const member functions with 4
-   * arguments.
-   *
-   * @ingroup threads
-   */
-  template <typename RT, typename C,
-            typename Arg1, typename Arg2, typename Arg3, typename Arg4>
-  inline
-  Thread<RT>
-  new_thread (RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4) const,
-              typename identity<const C>::type &c,
-              typename identity<Arg1>::type arg1,
-              typename identity<Arg2>::type arg2,
-              typename identity<Arg3>::type arg3,
-              typename identity<Arg4>::type arg4)
-  {
-    return
-      new_thread (std::function<RT ()>
-                  (std::bind(fun_ptr, std::cref(c),
-                             internal::maybe_make_ref<Arg1>::act(arg1),
-                             internal::maybe_make_ref<Arg2>::act(arg2),
-                             internal::maybe_make_ref<Arg3>::act(arg3),
-                             internal::maybe_make_ref<Arg4>::act(arg4))));
-  }
-#endif
+      /**
+       * Default constructor. Throws an exception since we want to queue a
+       * task immediately upon construction of these objects to make sure that
+       * each TaskDescriptor object corresponds to exactly one task.
+       */
+      TaskDescriptor ();
 
-// ----------- thread starters for functions with 5 arguments
+      /**
+       * Copy constructor. Throws an exception since we want to make sure that
+       * each TaskDescriptor object corresponds to exactly one task.
+       */
+      TaskDescriptor (const TaskDescriptor &);
 
-  /**
-   * Overload of the new_thread function for non-member or static member
-   * functions with 5 arguments.
-   *
-   * @ingroup threads
-   */
-  template <typename RT,
-            typename Arg1, typename Arg2, typename Arg3,
-            typename Arg4, typename Arg5>
-  inline
-  Thread<RT>
-  new_thread (RT (*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5),
-              typename identity<Arg1>::type arg1,
-              typename identity<Arg2>::type arg2,
-              typename identity<Arg3>::type arg3,
-              typename identity<Arg4>::type arg4,
-              typename identity<Arg5>::type arg5)
-  {
-    return
-      new_thread (std::function<RT ()>
-                  (std::bind(fun_ptr,
-                             internal::maybe_make_ref<Arg1>::act(arg1),
-                             internal::maybe_make_ref<Arg2>::act(arg2),
-                             internal::maybe_make_ref<Arg3>::act(arg3),
-                             internal::maybe_make_ref<Arg4>::act(arg4),
-                             internal::maybe_make_ref<Arg5>::act(arg5))));
-  }
+      /**
+       * Destructor.
+       */
+      ~TaskDescriptor ();
 
+      /**
+       * Copy operator. Throws an exception since we want to make sure that
+       * each TaskDescriptor object corresponds to exactly one task.
+       */
+      TaskDescriptor &
+      operator = (const TaskDescriptor &);
 
+      /**
+       * Queue up the task to the scheduler. We need to do this in a separate
+       * function since the new tasks needs to access objects from the current
+       * object and that can only reliably happen if the current object is
+       * completely constructed already.
+       */
+      void queue_task ();
 
-  /**
-   * Overload of the non-const new_thread function for member functions with 5
-   * arguments.
-   *
-   * @ingroup threads
-   */
-  template <typename RT, typename C,
-            typename Arg1, typename Arg2, typename Arg3,
-            typename Arg4, typename Arg5>
-  inline
-  Thread<RT>
-  new_thread (RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5),
-              typename identity<C>::type &c,
-              typename identity<Arg1>::type arg1,
-              typename identity<Arg2>::type arg2,
-              typename identity<Arg3>::type arg3,
-              typename identity<Arg4>::type arg4,
-              typename identity<Arg5>::type arg5)
-  {
-    return
-      new_thread (std::function<RT ()>
-                  (std::bind(fun_ptr, std::ref(c),
-                             internal::maybe_make_ref<Arg1>::act(arg1),
-                             internal::maybe_make_ref<Arg2>::act(arg2),
-                             internal::maybe_make_ref<Arg3>::act(arg3),
-                             internal::maybe_make_ref<Arg4>::act(arg4),
-                             internal::maybe_make_ref<Arg5>::act(arg5))));
-  }
+      /**
+       * Join a task, i.e. wait for it to finish. This function can safely be
+       * called from different threads at the same time, and can also be
+       * called more than once.
+       */
+      void join ();
 
-#ifndef DEAL_II_CONST_MEMBER_DEDUCTION_BUG
-  /**
-   * Overload of the new_thread function for const member functions with 5
-   * arguments.
-   *
-   * @ingroup threads
-   */
-  template <typename RT, typename C,
-            typename Arg1, typename Arg2, typename Arg3,
-            typename Arg4, typename Arg5>
-  inline
-  Thread<RT>
-  new_thread (RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5) const,
-              typename identity<const C>::type &c,
-              typename identity<Arg1>::type arg1,
-              typename identity<Arg2>::type arg2,
-              typename identity<Arg3>::type arg3,
-              typename identity<Arg4>::type arg4,
-              typename identity<Arg5>::type arg5)
-  {
-    return
-      new_thread (std::function<RT ()>
-                  (std::bind(fun_ptr, std::cref(c),
-                             internal::maybe_make_ref<Arg1>::act(arg1),
-                             internal::maybe_make_ref<Arg2>::act(arg2),
-                             internal::maybe_make_ref<Arg3>::act(arg3),
-                             internal::maybe_make_ref<Arg4>::act(arg4),
-                             internal::maybe_make_ref<Arg5>::act(arg5))));
-  }
-#endif
 
-// ----------- thread starters for functions with 6 arguments
+      template <typename> friend struct TaskEntryPoint;
+      friend class dealii::Threads::Task<RT>;
+    };
 
-  /**
-   * Overload of the new_thread function for non-member or static member
-   * functions with 6 arguments.
-   *
-   * @ingroup threads
-   */
-  template <typename RT,
-            typename Arg1, typename Arg2, typename Arg3,
-            typename Arg4, typename Arg5, typename Arg6>
-  inline
-  Thread<RT>
-  new_thread (RT (*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5,Arg6),
-              typename identity<Arg1>::type arg1,
-              typename identity<Arg2>::type arg2,
-              typename identity<Arg3>::type arg3,
-              typename identity<Arg4>::type arg4,
-              typename identity<Arg5>::type arg5,
-              typename identity<Arg6>::type arg6)
-  {
-    return
-      new_thread (std::function<RT ()>
-                  (std::bind(fun_ptr,
-                             internal::maybe_make_ref<Arg1>::act(arg1),
-                             internal::maybe_make_ref<Arg2>::act(arg2),
-                             internal::maybe_make_ref<Arg3>::act(arg3),
-                             internal::maybe_make_ref<Arg4>::act(arg4),
-                             internal::maybe_make_ref<Arg5>::act(arg5),
-                             internal::maybe_make_ref<Arg6>::act(arg6))));
-  }
 
 
+    template <typename RT>
+    inline
+    TaskDescriptor<RT>::TaskDescriptor (const std::function<RT ()> &function)
+      :
+      function (function),
+      task(nullptr),
+      task_is_done (false)
+    {}
 
-  /**
-   * Overload of the non-const new_thread function for member functions with 6
-   * arguments.
-   *
-   * @ingroup threads
-   */
-  template <typename RT, typename C,
-            typename Arg1, typename Arg2, typename Arg3,
-            typename Arg4, typename Arg5, typename Arg6>
-  inline
-  Thread<RT>
-  new_thread (RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5,Arg6),
-              typename identity<C>::type &c,
-              typename identity<Arg1>::type arg1,
-              typename identity<Arg2>::type arg2,
-              typename identity<Arg3>::type arg3,
-              typename identity<Arg4>::type arg4,
-              typename identity<Arg5>::type arg5,
-              typename identity<Arg6>::type arg6)
-  {
-    return
-      new_thread (std::function<RT ()>
-                  (std::bind(fun_ptr, std::ref(c),
-                             internal::maybe_make_ref<Arg1>::act(arg1),
-                             internal::maybe_make_ref<Arg2>::act(arg2),
-                             internal::maybe_make_ref<Arg3>::act(arg3),
-                             internal::maybe_make_ref<Arg4>::act(arg4),
-                             internal::maybe_make_ref<Arg5>::act(arg5),
-                             internal::maybe_make_ref<Arg6>::act(arg6))));
-  }
 
-#ifndef DEAL_II_CONST_MEMBER_DEDUCTION_BUG
-  /**
-   * Overload of the new_thread function for const member functions with 6
-   * arguments.
-   *
-   * @ingroup threads
-   */
-  template <typename RT, typename C,
-            typename Arg1, typename Arg2, typename Arg3,
-            typename Arg4, typename Arg5, typename Arg6>
-  inline
-  Thread<RT>
-  new_thread (RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5,Arg6) const,
-              typename identity<const C>::type &c,
-              typename identity<Arg1>::type arg1,
-              typename identity<Arg2>::type arg2,
-              typename identity<Arg3>::type arg3,
-              typename identity<Arg4>::type arg4,
-              typename identity<Arg5>::type arg5,
-              typename identity<Arg6>::type arg6)
-  {
-    return
-      new_thread (std::function<RT ()>
-                  (std::bind(fun_ptr, std::cref(c),
-                             internal::maybe_make_ref<Arg1>::act(arg1),
-                             internal::maybe_make_ref<Arg2>::act(arg2),
-                             internal::maybe_make_ref<Arg3>::act(arg3),
-                             internal::maybe_make_ref<Arg4>::act(arg4),
-                             internal::maybe_make_ref<Arg5>::act(arg5),
-                             internal::maybe_make_ref<Arg6>::act(arg6))));
-  }
+    template <typename RT>
+    inline
+    void
+    TaskDescriptor<RT>::queue_task ()
+    {
+      // use the pattern described in the TBB book on pages 230/231
+      // ("Start a large task in parallel with the main program")
+      task = new (tbb::task::allocate_root()) tbb::empty_task;
+      task->set_ref_count (2);
+
+      tbb::task *worker = new (task->allocate_child()) TaskEntryPoint<RT>(*this);
+
+      // in earlier versions of the TBB, task::spawn was a regular
+      // member function; however, in later versions, it was converted
+      // into a static function. we could always call it as a regular member
+      // function of *task, but that appears to confuse the NVidia nvcc
+      // compiler. consequently, the following work-around:
+#if TBB_VERSION_MAJOR >= 4
+      tbb::task::spawn (*worker);
+#else
+      task->spawn (*worker);
 #endif
+    }
 
-// ----------- thread starters for functions with 7 arguments
 
-  /**
-   * Overload of the new_thread function for non-member or static member
-   * functions with 7 arguments.
-   *
-   * @ingroup threads
-   */
-  template <typename RT,
-            typename Arg1, typename Arg2, typename Arg3,
-            typename Arg4, typename Arg5, typename Arg6,
-            typename Arg7>
-  inline
-  Thread<RT>
-  new_thread (RT (*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5,Arg6,Arg7),
-              typename identity<Arg1>::type arg1,
-              typename identity<Arg2>::type arg2,
-              typename identity<Arg3>::type arg3,
-              typename identity<Arg4>::type arg4,
-              typename identity<Arg5>::type arg5,
-              typename identity<Arg6>::type arg6,
-              typename identity<Arg7>::type arg7)
-  {
-    return
-      new_thread (std::function<RT ()>
-                  (std::bind(fun_ptr,
-                             internal::maybe_make_ref<Arg1>::act(arg1),
-                             internal::maybe_make_ref<Arg2>::act(arg2),
-                             internal::maybe_make_ref<Arg3>::act(arg3),
-                             internal::maybe_make_ref<Arg4>::act(arg4),
-                             internal::maybe_make_ref<Arg5>::act(arg5),
-                             internal::maybe_make_ref<Arg6>::act(arg6),
-                             internal::maybe_make_ref<Arg7>::act(arg7))));
-  }
 
+    template <typename RT>
+    TaskDescriptor<RT>::TaskDescriptor ()
+      :
+      task_is_done (false)
+    {
+      Assert (false, ExcInternalError());
+    }
 
 
-  /**
-   * Overload of the non-const new_thread function for member functions with 7
-   * arguments.
-   *
-   * @ingroup threads
-   */
-  template <typename RT, typename C,
-            typename Arg1, typename Arg2, typename Arg3,
-            typename Arg4, typename Arg5, typename Arg6,
-            typename Arg7>
-  inline
-  Thread<RT>
-  new_thread (RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5,Arg6,Arg7),
-              typename identity<C>::type &c,
-              typename identity<Arg1>::type arg1,
-              typename identity<Arg2>::type arg2,
-              typename identity<Arg3>::type arg3,
-              typename identity<Arg4>::type arg4,
-              typename identity<Arg5>::type arg5,
-              typename identity<Arg6>::type arg6,
-              typename identity<Arg7>::type arg7)
-  {
-    return
-      new_thread (std::function<RT ()>
-                  (std::bind(fun_ptr, std::ref(c),
-                             internal::maybe_make_ref<Arg1>::act(arg1),
-                             internal::maybe_make_ref<Arg2>::act(arg2),
-                             internal::maybe_make_ref<Arg3>::act(arg3),
-                             internal::maybe_make_ref<Arg4>::act(arg4),
-                             internal::maybe_make_ref<Arg5>::act(arg5),
-                             internal::maybe_make_ref<Arg6>::act(arg6),
-                             internal::maybe_make_ref<Arg7>::act(arg7))));
-  }
 
-#ifndef DEAL_II_CONST_MEMBER_DEDUCTION_BUG
-  /**
-   * Overload of the new_thread function for const member functions with 7
-   * arguments.
-   *
-   * @ingroup threads
-   */
-  template <typename RT, typename C,
-            typename Arg1, typename Arg2, typename Arg3,
-            typename Arg4, typename Arg5, typename Arg6,
-            typename Arg7>
-  inline
-  Thread<RT>
-  new_thread (RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5,Arg6,Arg7) const,
-              typename identity<const C>::type &c,
-              typename identity<Arg1>::type arg1,
-              typename identity<Arg2>::type arg2,
-              typename identity<Arg3>::type arg3,
-              typename identity<Arg4>::type arg4,
-              typename identity<Arg5>::type arg5,
-              typename identity<Arg6>::type arg6,
-              typename identity<Arg7>::type arg7)
-  {
-    return
-      new_thread (std::function<RT ()>
-                  (std::bind(fun_ptr, std::cref(c),
-                             internal::maybe_make_ref<Arg1>::act(arg1),
-                             internal::maybe_make_ref<Arg2>::act(arg2),
-                             internal::maybe_make_ref<Arg3>::act(arg3),
-                             internal::maybe_make_ref<Arg4>::act(arg4),
-                             internal::maybe_make_ref<Arg5>::act(arg5),
-                             internal::maybe_make_ref<Arg6>::act(arg6),
-                             internal::maybe_make_ref<Arg7>::act(arg7))));
-  }
-#endif
+    template <typename RT>
+    TaskDescriptor<RT>::TaskDescriptor (const TaskDescriptor &)
+      :
+      task_is_done (false)
+    {
+      // we shouldn't be getting here -- task descriptors
+      // can't be copied
+      Assert (false, ExcInternalError());
+    }
 
-// ----------- thread starters for functions with 8 arguments
 
-  /**
-   * Overload of the new_thread function for non-member or static member
-   * functions with 8 arguments.
-   *
-   * @ingroup threads
-   */
-  template <typename RT,
-            typename Arg1, typename Arg2, typename Arg3,
-            typename Arg4, typename Arg5, typename Arg6,
-            typename Arg7, typename Arg8>
-  inline
-  Thread<RT>
-  new_thread (RT (*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5,
-                            Arg6,Arg7,Arg8),
-              typename identity<Arg1>::type arg1,
-              typename identity<Arg2>::type arg2,
-              typename identity<Arg3>::type arg3,
-              typename identity<Arg4>::type arg4,
-              typename identity<Arg5>::type arg5,
-              typename identity<Arg6>::type arg6,
-              typename identity<Arg7>::type arg7,
-              typename identity<Arg8>::type arg8)
-  {
-    return
-      new_thread (std::function<RT ()>
-                  (std::bind(fun_ptr,
-                             internal::maybe_make_ref<Arg1>::act(arg1),
-                             internal::maybe_make_ref<Arg2>::act(arg2),
-                             internal::maybe_make_ref<Arg3>::act(arg3),
-                             internal::maybe_make_ref<Arg4>::act(arg4),
-                             internal::maybe_make_ref<Arg5>::act(arg5),
-                             internal::maybe_make_ref<Arg6>::act(arg6),
-                             internal::maybe_make_ref<Arg7>::act(arg7),
-                             internal::maybe_make_ref<Arg8>::act(arg8))));
-  }
 
+    template <typename RT>
+    inline
+    TaskDescriptor<RT>::~TaskDescriptor ()
+    {
+      // wait for the task to complete for sure
+      join ();
 
+      // now destroy the empty task structure. the book recommends to
+      // spawn it as well and let the scheduler destroy the object
+      // when done, but this has the disadvantage that the scheduler
+      // may not get to actually finishing the task before it goes out
+      // of scope (at the end of the program, or if a thread is done
+      // on which it was run) and then we would get a hard-to-decipher
+      // warning about unfinished tasks when the scheduler "goes out
+      // of the arena". rather, let's explicitly destroy the empty
+      // task object. before that, make sure that the task has been
+      // shut down, expressed by a zero reference count
+      AssertNothrow (task != nullptr, ExcInternalError());
+      AssertNothrow (task->ref_count()==0, ExcInternalError());
+      task->destroy (*task);
+    }
 
-  /**
-   * Overload of the non-const new_thread function for member functions with 8
-   * arguments.
-   *
-   * @ingroup threads
-   */
-  template <typename RT, typename C,
-            typename Arg1, typename Arg2, typename Arg3,
-            typename Arg4, typename Arg5, typename Arg6,
-            typename Arg7, typename Arg8>
-  inline
-  Thread<RT>
-  new_thread (RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5,
-                               Arg6,Arg7,Arg8),
-              typename identity<C>::type &c,
-              typename identity<Arg1>::type arg1,
-              typename identity<Arg2>::type arg2,
-              typename identity<Arg3>::type arg3,
-              typename identity<Arg4>::type arg4,
-              typename identity<Arg5>::type arg5,
-              typename identity<Arg6>::type arg6,
-              typename identity<Arg7>::type arg7,
-              typename identity<Arg8>::type arg8)
-  {
-    return
-      new_thread (std::function<RT ()>
-                  (std::bind(fun_ptr, std::ref(c),
-                             internal::maybe_make_ref<Arg1>::act(arg1),
-                             internal::maybe_make_ref<Arg2>::act(arg2),
-                             internal::maybe_make_ref<Arg3>::act(arg3),
-                             internal::maybe_make_ref<Arg4>::act(arg4),
-                             internal::maybe_make_ref<Arg5>::act(arg5),
-                             internal::maybe_make_ref<Arg6>::act(arg6),
-                             internal::maybe_make_ref<Arg7>::act(arg7),
-                             internal::maybe_make_ref<Arg8>::act(arg8))));
-  }
 
-#ifndef DEAL_II_CONST_MEMBER_DEDUCTION_BUG
-  /**
-   * Overload of the new_thread function for const member functions with 8
-   * arguments.
-   *
-   * @ingroup threads
-   */
-  template <typename RT, typename C,
-            typename Arg1, typename Arg2, typename Arg3,
-            typename Arg4, typename Arg5, typename Arg6,
-            typename Arg7, typename Arg8>
-  inline
-  Thread<RT>
-  new_thread (RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5,
-                               Arg6,Arg7,Arg8) const,
-              typename identity<const C>::type &c,
-              typename identity<Arg1>::type arg1,
-              typename identity<Arg2>::type arg2,
-              typename identity<Arg3>::type arg3,
-              typename identity<Arg4>::type arg4,
-              typename identity<Arg5>::type arg5,
-              typename identity<Arg6>::type arg6,
-              typename identity<Arg7>::type arg7,
-              typename identity<Arg8>::type arg8)
-  {
-    return
-      new_thread (std::function<RT ()>
-                  (std::bind(fun_ptr, std::cref(c),
-                             internal::maybe_make_ref<Arg1>::act(arg1),
-                             internal::maybe_make_ref<Arg2>::act(arg2),
-                             internal::maybe_make_ref<Arg3>::act(arg3),
-                             internal::maybe_make_ref<Arg4>::act(arg4),
-                             internal::maybe_make_ref<Arg5>::act(arg5),
-                             internal::maybe_make_ref<Arg6>::act(arg6),
-                             internal::maybe_make_ref<Arg7>::act(arg7),
-                             internal::maybe_make_ref<Arg8>::act(arg8))));
-  }
-#endif
-
-// ----------- thread starters for functions with 9 arguments
-
-  /**
-   * Overload of the new_thread function for non-member or static member
-   * functions with 9 arguments.
-   *
-   * @ingroup threads
-   */
-  template <typename RT,
-            typename Arg1, typename Arg2, typename Arg3,
-            typename Arg4, typename Arg5, typename Arg6,
-            typename Arg7, typename Arg8, typename Arg9>
-  inline
-  Thread<RT>
-  new_thread (RT (*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5,
-                            Arg6,Arg7,Arg8,Arg9),
-              typename identity<Arg1>::type arg1,
-              typename identity<Arg2>::type arg2,
-              typename identity<Arg3>::type arg3,
-              typename identity<Arg4>::type arg4,
-              typename identity<Arg5>::type arg5,
-              typename identity<Arg6>::type arg6,
-              typename identity<Arg7>::type arg7,
-              typename identity<Arg8>::type arg8,
-              typename identity<Arg9>::type arg9)
-  {
-    return
-      new_thread (std::function<RT ()>
-                  (std::bind(fun_ptr,
-                             internal::maybe_make_ref<Arg1>::act(arg1),
-                             internal::maybe_make_ref<Arg2>::act(arg2),
-                             internal::maybe_make_ref<Arg3>::act(arg3),
-                             internal::maybe_make_ref<Arg4>::act(arg4),
-                             internal::maybe_make_ref<Arg5>::act(arg5),
-                             internal::maybe_make_ref<Arg6>::act(arg6),
-                             internal::maybe_make_ref<Arg7>::act(arg7),
-                             internal::maybe_make_ref<Arg8>::act(arg8),
-                             internal::maybe_make_ref<Arg9>::act(arg9))));
-  }
-
-
-
-  /**
-   * Overload of the non-const new_thread function for member functions with 9
-   * arguments.
-   *
-   * @ingroup threads
-   */
-  template <typename RT, typename C,
-            typename Arg1, typename Arg2, typename Arg3,
-            typename Arg4, typename Arg5, typename Arg6,
-            typename Arg7, typename Arg8, typename Arg9>
-  inline
-  Thread<RT>
-  new_thread (RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5,
-                               Arg6,Arg7,Arg8,Arg9),
-              typename identity<C>::type &c,
-              typename identity<Arg1>::type arg1,
-              typename identity<Arg2>::type arg2,
-              typename identity<Arg3>::type arg3,
-              typename identity<Arg4>::type arg4,
-              typename identity<Arg5>::type arg5,
-              typename identity<Arg6>::type arg6,
-              typename identity<Arg7>::type arg7,
-              typename identity<Arg8>::type arg8,
-              typename identity<Arg9>::type arg9)
-  {
-    return
-      new_thread (std::function<RT ()>
-                  (std::bind(fun_ptr, std::ref(c),
-                             internal::maybe_make_ref<Arg1>::act(arg1),
-                             internal::maybe_make_ref<Arg2>::act(arg2),
-                             internal::maybe_make_ref<Arg3>::act(arg3),
-                             internal::maybe_make_ref<Arg4>::act(arg4),
-                             internal::maybe_make_ref<Arg5>::act(arg5),
-                             internal::maybe_make_ref<Arg6>::act(arg6),
-                             internal::maybe_make_ref<Arg7>::act(arg7),
-                             internal::maybe_make_ref<Arg8>::act(arg8),
-                             internal::maybe_make_ref<Arg9>::act(arg9))));
-  }
-
-#ifndef DEAL_II_CONST_MEMBER_DEDUCTION_BUG
-  /**
-   * Overload of the new_thread function for const member functions with 9
-   * arguments.
-   *
-   * @ingroup threads
-   */
-  template <typename RT, typename C,
-            typename Arg1, typename Arg2, typename Arg3,
-            typename Arg4, typename Arg5, typename Arg6,
-            typename Arg7, typename Arg8, typename Arg9>
-  inline
-  Thread<RT>
-  new_thread (RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5,
-                               Arg6,Arg7,Arg8,Arg9) const,
-              typename identity<const C>::type &c,
-              typename identity<Arg1>::type arg1,
-              typename identity<Arg2>::type arg2,
-              typename identity<Arg3>::type arg3,
-              typename identity<Arg4>::type arg4,
-              typename identity<Arg5>::type arg5,
-              typename identity<Arg6>::type arg6,
-              typename identity<Arg7>::type arg7,
-              typename identity<Arg8>::type arg8,
-              typename identity<Arg9>::type arg9)
-  {
-    return
-      new_thread (std::function<RT ()>
-                  (std::bind(fun_ptr, std::cref(c),
-                             internal::maybe_make_ref<Arg1>::act(arg1),
-                             internal::maybe_make_ref<Arg2>::act(arg2),
-                             internal::maybe_make_ref<Arg3>::act(arg3),
-                             internal::maybe_make_ref<Arg4>::act(arg4),
-                             internal::maybe_make_ref<Arg5>::act(arg5),
-                             internal::maybe_make_ref<Arg6>::act(arg6),
-                             internal::maybe_make_ref<Arg7>::act(arg7),
-                             internal::maybe_make_ref<Arg8>::act(arg8),
-                             internal::maybe_make_ref<Arg9>::act(arg9))));
-  }
-#endif
-
-// ------------------------ ThreadGroup -------------------------------------
-
-  /**
-   * A container for thread objects. Allows to add new thread objects and wait
-   * for them all together. The thread objects need to have the same return
-   * value for the called function.
-   *
-   * @author Wolfgang Bangerth, 2003
-   * @ingroup threads
-   */
-  template <typename RT = void>
-  class ThreadGroup
-  {
-  public:
-    /**
-     * Add another thread object to the collection.
-     */
-    ThreadGroup &operator += (const Thread<RT> &t)
+    template <typename RT>
+    TaskDescriptor<RT> &
+    TaskDescriptor<RT>::operator = (const TaskDescriptor &)
     {
-      threads.push_back (t);
+      // we shouldn't be getting here -- task descriptors
+      // can't be copied
+      Assert (false, ExcInternalError());
       return *this;
     }
 
-    /**
-     * Wait for all threads in the collection to finish. It is not a problem
-     * if some of them have already been waited for, i.e. you may call this
-     * function more than once, and you can also add new thread objects
-     * between subsequent calls to this function if you want.
-     */
-    void join_all () const
-    {
-      for (typename std::list<Thread<RT> >::const_iterator
-           t=threads.begin(); t!=threads.end(); ++t)
-        t->join ();
-    }
-
-  private:
-    /**
-     * List of thread objects.
-     */
-    std::list<Thread<RT> > threads;
-  };
-
-
-  template <typename> class Task;
-
-
-  namespace internal
-  {
-#ifdef DEAL_II_WITH_THREADS
-
-    template <typename> struct TaskDescriptor;
 
-    /**
-     * The task class for TBB that is used by the TaskDescriptor class.
-     */
     template <typename RT>
-    struct TaskEntryPoint : public tbb::task
+    inline
+    void
+    TaskDescriptor<RT>::join ()
     {
-      TaskEntryPoint (TaskDescriptor<RT> &task_descriptor)
-        :
-        task_descriptor (task_descriptor)
-      {}
+      // if the task is already done, just return. this makes sure we
+      // call tbb::Task::wait_for_all() exactly once, as required by
+      // TBB. we could also get the reference count of task for doing
+      // this, but that is usually slower. note that this does not
+      // work when the thread calling this function is not the same as
+      // the one that initialized the task.
+      //
+      // TODO: can we assert that no other thread tries to end the
+      // task?
+      if (task_is_done == true)
+        return;
 
-      virtual tbb::task *execute ()
-      {
-        // call the function object and put the return value into the
-        // proper place
-        try
-          {
-            call (task_descriptor.function, task_descriptor.ret_val);
-          }
-        catch (const std::exception &exc)
-          {
-            internal::handle_std_exception (exc);
-          }
-        catch (...)
-          {
-            internal::handle_unknown_exception ();
-          }
-        return nullptr;
-      }
+      // let TBB wait for the task to complete.
+      task_is_done = true;
+      task->wait_for_all();
+    }
 
-      /**
-       * A reference to the descriptor object of this task.
-       */
-      TaskDescriptor<RT> &task_descriptor;
-    };
+
+
+#else        // no threading enabled
 
     /**
-     * @internal
-     *
-     * Base class describing a task. This is the basic class abstracting the
-     * Threading Building Blocks implementation of tasks.  It provides a
-     * mechanism to start a new task, as well as for joining it.
-     *
-     * Internally, the way things are implemented is that all Task<> objects
-     * keep a shared pointer to the task descriptor. When the last Task<>
-     * object goes out of scope, the destructor of the descriptor is called.
-     * Since tasks can not be abandoned, the destructor makes sure that the
-     * task is finished before it can continue to destroy the object.
-     *
-     * Note that unlike threads, tasks are not always started right away, and
-     * so the starting thread can't rely on the fact that the started task can
-     * copy things off the spawning thread's stack frame. As a consequence,
-     * the task description needs to include a way to store the function and
-     * its arguments that shall be run on the task.
-     *
-     * @author Wolfgang Bangerth, 2009
+     * A way to describe tasks. Since we are in non-MT mode at this place,
+     * things are a lot simpler than in MT mode.
      */
     template <typename RT>
     struct TaskDescriptor
     {
-    private:
-      /**
-       * The function and its arguments that are to be run on the task.
-       */
-      std::function<RT ()> function;
-
-      /**
-       * Variable holding the data the TBB needs to work with a task. Set by
-       * the queue_up_task() function. Note that the object behind this
-       * pointer will be deleted upon termination of the task, so we do not
-       * have to do so ourselves. In particular, if all objects with pointers
-       * to this task_description object go out of scope then no action is
-       * needed on our behalf.
-       */
-      tbb::task *task;
-
       /**
        * A place where the task will deposit its return value.
        */
       return_value<RT> ret_val;
 
       /**
-       * A flag indicating whether the task has terminated.
-       */
-      bool task_is_done;
-
-    public:
-
-      /**
-       * Constructor. Take the function to be run on this task as argument.
-       */
-      TaskDescriptor (const std::function<RT ()> &function);
-
-      /**
-       * Default constructor. Throws an exception since we want to queue a
-       * task immediately upon construction of these objects to make sure that
-       * each TaskDescriptor object corresponds to exactly one task.
-       */
-      TaskDescriptor ();
-
-      /**
-       * Copy constructor. Throws an exception since we want to make sure that
-       * each TaskDescriptor object corresponds to exactly one task.
-       */
-      TaskDescriptor (const TaskDescriptor &);
-
-      /**
-       * Destructor.
-       */
-      ~TaskDescriptor ();
-
-      /**
-       * Copy operator. Throws an exception since we want to make sure that
-       * each TaskDescriptor object corresponds to exactly one task.
+       * Constructor. Call the given function and emplace the return value
+       * into the slot reserved for this purpose.
        */
-      TaskDescriptor &
-      operator = (const TaskDescriptor &);
+      TaskDescriptor (const std::function<RT ()> &function)
+      {
+        call (function, ret_val);
+      }
 
       /**
-       * Queue up the task to the scheduler. We need to do this in a separate
-       * function since the new tasks needs to access objects from the current
-       * object and that can only reliably happen if the current object is
-       * completely constructed already.
+       * Wait for the task to return. Since we are in non-MT mode here, there
+       * is nothing to do.
        */
-      void queue_task ();
-
-      /**
-       * Join a task, i.e. wait for it to finish. This function can safely be
-       * called from different threads at the same time, and can also be
-       * called more than once.
-       */
-      void join ();
-
-
-      template <typename> friend struct TaskEntryPoint;
-      friend class dealii::Threads::Task<RT>;
-    };
-
-
-
-    template <typename RT>
-    inline
-    TaskDescriptor<RT>::TaskDescriptor (const std::function<RT ()> &function)
-      :
-      function (function),
-      task(nullptr),
-      task_is_done (false)
-    {}
-
-
-    template <typename RT>
-    inline
-    void
-    TaskDescriptor<RT>::queue_task ()
-    {
-      // use the pattern described in the TBB book on pages 230/231
-      // ("Start a large task in parallel with the main program")
-      task = new (tbb::task::allocate_root()) tbb::empty_task;
-      task->set_ref_count (2);
-
-      tbb::task *worker = new (task->allocate_child()) TaskEntryPoint<RT>(*this);
-
-      // in earlier versions of the TBB, task::spawn was a regular
-      // member function; however, in later versions, it was converted
-      // into a static function. we could always call it as a regular member
-      // function of *task, but that appears to confuse the NVidia nvcc
-      // compiler. consequently, the following work-around:
-#if TBB_VERSION_MAJOR >= 4
-      tbb::task::spawn (*worker);
-#else
-      task->spawn (*worker);
-#endif
-    }
-
-
-
-    template <typename RT>
-    TaskDescriptor<RT>::TaskDescriptor ()
-      :
-      task_is_done (false)
-    {
-      Assert (false, ExcInternalError());
-    }
-
-
-
-    template <typename RT>
-    TaskDescriptor<RT>::TaskDescriptor (const TaskDescriptor &)
-      :
-      task_is_done (false)
-    {
-      // we shouldn't be getting here -- task descriptors
-      // can't be copied
-      Assert (false, ExcInternalError());
-    }
-
-
-
-    template <typename RT>
-    inline
-    TaskDescriptor<RT>::~TaskDescriptor ()
-    {
-      // wait for the task to complete for sure
-      join ();
-
-      // now destroy the empty task structure. the book recommends to
-      // spawn it as well and let the scheduler destroy the object
-      // when done, but this has the disadvantage that the scheduler
-      // may not get to actually finishing the task before it goes out
-      // of scope (at the end of the program, or if a thread is done
-      // on which it was run) and then we would get a hard-to-decipher
-      // warning about unfinished tasks when the scheduler "goes out
-      // of the arena". rather, let's explicitly destroy the empty
-      // task object. before that, make sure that the task has been
-      // shut down, expressed by a zero reference count
-      AssertNothrow (task != nullptr, ExcInternalError());
-      AssertNothrow (task->ref_count()==0, ExcInternalError());
-      task->destroy (*task);
-    }
-
-
-    template <typename RT>
-    TaskDescriptor<RT> &
-    TaskDescriptor<RT>::operator = (const TaskDescriptor &)
-    {
-      // we shouldn't be getting here -- task descriptors
-      // can't be copied
-      Assert (false, ExcInternalError());
-      return *this;
-    }
-
-
-    template <typename RT>
-    inline
-    void
-    TaskDescriptor<RT>::join ()
-    {
-      // if the task is already done, just return. this makes sure we
-      // call tbb::Task::wait_for_all() exactly once, as required by
-      // TBB. we could also get the reference count of task for doing
-      // this, but that is usually slower. note that this does not
-      // work when the thread calling this function is not the same as
-      // the one that initialized the task.
-      //
-      // TODO: can we assert that no other thread tries to end the
-      // task?
-      if (task_is_done == true)
-        return;
-
-      // let TBB wait for the task to complete.
-      task_is_done = true;
-      task->wait_for_all();
-    }
-
-
-
-#else        // no threading enabled
-
-    /**
-     * A way to describe tasks. Since we are in non-MT mode at this place,
-     * things are a lot simpler than in MT mode.
-     */
-    template <typename RT>
-    struct TaskDescriptor
-    {
-      /**
-       * A place where the task will deposit its return value.
-       */
-      return_value<RT> ret_val;
-
-      /**
-       * Constructor. Call the given function and emplace the return value
-       * into the slot reserved for this purpose.
-       */
-      TaskDescriptor (const std::function<RT ()> &function)
-      {
-        call (function, ret_val);
-      }
-
-      /**
-       * Wait for the task to return. Since we are in non-MT mode here, there
-       * is nothing to do.
-       */
-      static void join () {}
+      static void join () {}
 
       /**
        * Run the task. Since we are here in non-MT mode, there is nothing to
        * do that the constructor hasn't already done.
-       */
-      static void queue_task () {}
-    };
-
-#endif
-
-  }
-
-
-
-  /**
-   * Describes one task object based on the Threading Building Blocks' Task.
-   * Note that the call to join() must be executed on the same thread as the
-   * call to the constructor. Otherwise, there might be a deadlock. In other
-   * words, a Task object should never passed on to another task for calling
-   * the join() method.
-   *
-   * @author Wolfgang Bangerth, 2009
-   * @ingroup threads
-   */
-  template <typename RT = void>
-  class Task
-  {
-  public:
-    /**
-     * Construct a task object given a function object to execute on the task,
-     * and then schedule this function for execution.
-     *
-     * @post Using this constructor automatically makes the task object
-     * joinable().
-     */
-    Task (const std::function<RT ()> &function_object)
-    {
-      // create a task descriptor and tell it to queue itself up with
-      // the scheduling system
-      task_descriptor.reset (new internal::TaskDescriptor<RT>(function_object));
-      task_descriptor->queue_task ();
-    }
-
-
-    /**
-     * Copy constructor.
-     *
-     * @post Using this constructor automatically makes the task object
-     * joinable().
-     */
-    Task (const Task<RT> &t)
-      :
-      task_descriptor (t.task_descriptor)
-    {}
-
-
-    /**
-     * Default constructor. You can't do much with a task object constructed
-     * this way, except for assigning it a task object that holds data created
-     * by the Threads::new_task() functions.
-     *
-     * @post Using this constructor leaves the object in an unjoinable state,
-     * i.e., joinable() will return false.
-     */
-    Task () = default;
-
-    /**
-     * Join the task represented by this object, i.e. wait for it to finish.
-     *
-     * A task can be joined multiple times (while the first join() operation
-     * may block until the task has completed running, all successive attempts
-     * to join will return immediately).
-     *
-     * @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.
-     */
-    void join () const
-    {
-      AssertThrow (joinable(), ExcNoTask());
-      task_descriptor->join ();
-    }
-
-    /**
-     * Return whether the current object can be joined. You can join a task
-     * object once a task (typically created with Threads::new_task()) has
-     * actually been assigned to it. On the other hand, the function returns
-     * false if the object has been default constructed.
-     *
-     * A task can be joined multiple times (while the first join() operation
-     * may block until the task has completed running, all successive attempts
-     * to join will return immediately). Consequently, if this function
-     * returns true, it will continue to return true until the task object it
-     * reports on is assigned to from another object.
-     */
-    bool joinable () const
-    {
-      return (task_descriptor !=
-              std::shared_ptr<internal::TaskDescriptor<RT> >());
-    }
-
-
-    /**
-     * Get the return value of the function of the task. Since this is only
-     * available once the task finishes, this implicitly also calls join().
-     * You can call this function multiple times as long as the object refers
-     * to the same task, and expect to get the same return value every time.
-     *
-     * @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.
-     */
-    RT return_value ()
-    {
-      join ();
-      return task_descriptor->ret_val.get();
-    }
-
-
-    /**
-     * Check for equality of task objects. Since objects of this class store
-     * an implicit pointer to an object that exists exactly once for each
-     * task, the check is simply to compare these pointers.
-     */
-    bool operator == (const Task &t) const
-    {
-      AssertThrow (joinable(), ExcNoTask());
-      return task_descriptor == t.task_descriptor;
-    }
-
-    /**
-     * @addtogroup Exceptions
-     * @{
-     */
-
-    /**
-     * Exception
-     */
-    DeclExceptionMsg (ExcNoTask,
-                      "The current object is not associated with a task that "
-                      "can be joined. It may have been detached, or you "
-                      "may have already joined it in the past.");
-    //@}
-  private:
-    /**
-     * Shared pointer to the object representing the task. Boost's shared
-     * pointer implementation will make sure that that object lives as long as
-     * there is at least one subscriber to it.
-     */
-    std::shared_ptr<internal::TaskDescriptor<RT> > task_descriptor;
-  };
-
-
-
-  /**
-   * Overload of the new_task function for objects that can be converted to
-   * std::function<RT ()>, i.e. anything that can be called like a
-   * function object without arguments and returning an object of type RT (or
-   * void).
-   *
-   * @ingroup threads
-   */
-  template <typename RT>
-  inline
-  Task<RT>
-  new_task (const std::function<RT ()> &function)
-  {
-    return Task<RT>(function);
-  }
-
-
-
-  /**
-   * Overload of the new_task function for objects that can be called like a
-   * function object without arguments. In particular, this function allows
-   * calling Threads::new_task() with either objects that result from using
-   * std::bind, or using lambda functions. For example, this function is called
-   * when writing code such as
-   * @code
-   * Threads::Task<int>
-   *   task = Threads::new_task ( [] () {
-   *                                      do_this();
-   *                                      then_do_that();
-   *                                      return 42;
-   *                                    });
-   * @endcode
-   * Here, we schedule the call to the sequence of functions
-   * <code>do_this()</code> and <code>then_do_that()</code> on
-   * a separate task, by making the lambda function declared here the
-   * function to execute on the task. The lambda function then returns
-   * 42 (which is a bit pointless here, but it could of course be some
-   * computed number), and this is going to be the returned value you
-   * can later retrieve via <code>task.return_value()</code> once the
-   * task (i.e., the body of the lambda function) has completed.
-   *
-   * @note Every lambda function (or whatever else it is you pass to
-   *   the new_task() function here, for example the result of a
-   *   std::bind() expression) has a return type and consequently
-   *   returns an object of this type. This type can be inferred
-   *   using the C++11 <code>decltype</code> statement used in the
-   *   declaration of this function, and it is then used as the template
-   *   argument of the Threads::Task object returned by the current function.
-   *   In the example above, because the lambda function returns 42
-   *   (which in C++ has data type <code>int</code>), the inferred
-   *   type is <code>int</code> and the task object will have type
-   *   <code>Task@<int@></code>. In other words, it is not <i>necessary</i>
-   *   to explicitly specify in user code what that return type
-   *   of the lambda or std::bind expression will be, though it is
-   *   possible to explicitly do so by (entirely equivalently) writing
-   *   @code
-   *   Threads::Task<int>
-   *     task = Threads::new_task ( [] () -> int {
-   *                                               do_this();
-   *                                               then_do_that();
-   *                                               return 42;
-   *                                             });
-   *   @endcode
-   *
-   * @note In practice, the lambda functions you will pass to
-   *   new_task() will of course typically be more complicated.
-   *   In particular, they will likely <i>capture</i> variables
-   *   from the surrounding context and use them within the lambda.
-   *   See https://en.wikipedia.org/wiki/Anonymous_function#C.2B.2B_.28since_C.2B.2B11.29
-   *   for more on how lambda functions work.
-   *
-   * @note If you pass a lambda function as an argument to the
-   *   current function that captures a variable <i>by reference</i>,
-   *   or if you use a std::bind that binds a function argument to
-   *   a reference variable using std::ref() or std::cref(), then
-   *   obviously you can only do this if the variables you reference
-   *   or capture have a lifetime that extends at least until the time
-   *   where the task finishes.
-   *
-   * @ingroup CPP11
-   */
-  template <typename FunctionObjectType>
-  inline
-  auto
-  new_task (FunctionObjectType function_object)
-  -> Task<decltype(function_object())>
-  {
-    typedef decltype(function_object()) return_type;
-    return Task<return_type>(std::function<return_type ()>(function_object));
-  }
-
-
-
-  /**
-   * Overload of the new_task function for non-member or static member
-   * functions with no arguments.
-   *
-   * @ingroup threads
-   */
-  template <typename RT>
-  inline
-  Task<RT>
-  new_task (RT (*fun_ptr)())
-  {
-    return new_task (std::function<RT ()>(fun_ptr));
-  }
-
-
-  /**
-   * Overload of the non-const new_task function for member functions with no
-   * arguments.
-   *
-   * @ingroup threads
-   */
-  template <typename RT, typename C>
-  inline
-  Task<RT>
-  new_task (RT (C::*fun_ptr)(),
-            typename identity<C>::type &c)
-  {
-    return
-      new_task (std::function<RT ()>
-                (std::bind(fun_ptr, std::ref(c))));
-  }
-
-#ifndef DEAL_II_CONST_MEMBER_DEDUCTION_BUG
-  /**
-   * Overload of the new_task function for const member functions with no
-   * arguments.
-   *
-   * @ingroup threads
-   */
-  template <typename RT, typename C>
-  inline
-  Task<RT>
-  new_task (RT (C::*fun_ptr)() const,
-            const typename identity<C>::type &c)
-  {
-    return
-      new_task (std::function<RT ()>
-                (std::bind(fun_ptr, std::cref(c))));
-  }
-#endif
-
-
-
-// ----------- thread starters for unary functions
-
-  /**
-   * Overload of the new_task function for non-member or static member
-   * functions with 1 argument.
-   *
-   * @ingroup threads
-   */
-  template <typename RT, typename Arg1>
-  inline
-  Task<RT>
-  new_task (RT (*fun_ptr)(Arg1),
-            typename identity<Arg1>::type arg1)
-  {
-    return
-      new_task (std::function<RT ()>
-                (std::bind(fun_ptr,
-                           internal::maybe_make_ref<Arg1>::act(arg1))));
-  }
-
-
-
-  /**
-   * Overload of the non-const new_task function for member functions with 1
-   * argument.
-   *
-   * @ingroup threads
-   */
-  template <typename RT, typename C, typename Arg1>
-  inline
-  Task<RT>
-  new_task (RT (C::*fun_ptr)(Arg1),
-            typename identity<C>::type &c,
-            typename identity<Arg1>::type arg1)
-  {
-    return
-      new_task (std::function<RT ()>
-                (std::bind(fun_ptr, std::ref(c),
-                           internal::maybe_make_ref<Arg1>::act(arg1))));
-  }
-
-#ifndef DEAL_II_CONST_MEMBER_DEDUCTION_BUG
-  /**
-   * Overload of the new_task function for const member functions with 1
-   * argument.
-   *
-   * @ingroup threads
-   */
-  template <typename RT, typename C, typename Arg1>
-  inline
-  Task<RT>
-  new_task (RT (C::*fun_ptr)(Arg1) const,
-            typename identity<const C>::type &c,
-            typename identity<Arg1>::type arg1)
-  {
-    return
-      new_task (std::function<RT ()>
-                (std::bind(fun_ptr, std::cref(c),
-                           internal::maybe_make_ref<Arg1>::act(arg1))));
-  }
-#endif
-
-// ----------- thread starters for binary functions
-
-  /**
-   * Overload of the new_task function for non-member or static member
-   * functions with 2 arguments.
-   *
-   * @ingroup threads
-   */
-  template <typename RT, typename Arg1, typename Arg2>
-  inline
-  Task<RT>
-  new_task (RT (*fun_ptr)(Arg1,Arg2),
-            typename identity<Arg1>::type arg1,
-            typename identity<Arg2>::type arg2)
-  {
-    return
-      new_task (std::function<RT ()>
-                (std::bind(fun_ptr,
-                           internal::maybe_make_ref<Arg1>::act(arg1),
-                           internal::maybe_make_ref<Arg2>::act(arg2))));
-  }
-
-
-
-  /**
-   * Overload of the non-const new_task function for member functions with 2
-   * arguments.
-   *
-   * @ingroup threads
-   */
-  template <typename RT, typename C, typename Arg1, typename Arg2>
-  inline
-  Task<RT>
-  new_task (RT (C::*fun_ptr)(Arg1,Arg2),
-            typename identity<C>::type &c,
-            typename identity<Arg1>::type arg1,
-            typename identity<Arg2>::type arg2)
-  {
-    return
-      new_task (std::function<RT ()>
-                (std::bind(fun_ptr, std::ref(c),
-                           internal::maybe_make_ref<Arg1>::act(arg1),
-                           internal::maybe_make_ref<Arg2>::act(arg2))));
-  }
-
-#ifndef DEAL_II_CONST_MEMBER_DEDUCTION_BUG
-  /**
-   * Overload of the new_task function for const member functions with 2
-   * arguments.
-   *
-   * @ingroup threads
-   */
-  template <typename RT, typename C, typename Arg1, typename Arg2>
-  inline
-  Task<RT>
-  new_task (RT (C::*fun_ptr)(Arg1,Arg2) const,
-            typename identity<const C>::type &c,
-            typename identity<Arg1>::type arg1,
-            typename identity<Arg2>::type arg2)
-  {
-    return
-      new_task (std::function<RT ()>
-                (std::bind(fun_ptr, std::cref(c),
-                           internal::maybe_make_ref<Arg1>::act(arg1),
-                           internal::maybe_make_ref<Arg2>::act(arg2))));
-  }
-#endif
-
-// ----------- thread starters for ternary functions
-
-  /**
-   * Overload of the new_task function for non-member or static member
-   * functions with 3 arguments.
-   *
-   * @ingroup threads
-   */
-  template <typename RT,
-            typename Arg1, typename Arg2, typename Arg3>
-  inline
-  Task<RT>
-  new_task (RT (*fun_ptr)(Arg1,Arg2,Arg3),
-            typename identity<Arg1>::type arg1,
-            typename identity<Arg2>::type arg2,
-            typename identity<Arg3>::type arg3)
-  {
-    return
-      new_task (std::function<RT ()>
-                (std::bind(fun_ptr,
-                           internal::maybe_make_ref<Arg1>::act(arg1),
-                           internal::maybe_make_ref<Arg2>::act(arg2),
-                           internal::maybe_make_ref<Arg3>::act(arg3))));
-  }
-
-
-
-  /**
-   * Overload of the non-const new_task function for member functions with 3
-   * arguments.
-   *
-   * @ingroup threads
-   */
-  template <typename RT, typename C,
-            typename Arg1, typename Arg2, typename Arg3>
-  inline
-  Task<RT>
-  new_task (RT (C::*fun_ptr)(Arg1,Arg2,Arg3),
-            typename identity<C>::type &c,
-            typename identity<Arg1>::type arg1,
-            typename identity<Arg2>::type arg2,
-            typename identity<Arg3>::type arg3)
-  {
-    return
-      new_task (std::function<RT ()>
-                (std::bind(fun_ptr, std::ref(c),
-                           internal::maybe_make_ref<Arg1>::act(arg1),
-                           internal::maybe_make_ref<Arg2>::act(arg2),
-                           internal::maybe_make_ref<Arg3>::act(arg3))));
-  }
-
-#ifndef DEAL_II_CONST_MEMBER_DEDUCTION_BUG
-  /**
-   * Overload of the new_task function for const member functions with 3
-   * arguments.
-   *
-   * @ingroup threads
-   */
-  template <typename RT, typename C,
-            typename Arg1, typename Arg2, typename Arg3>
-  inline
-  Task<RT>
-  new_task (RT (C::*fun_ptr)(Arg1,Arg2,Arg3) const,
-            typename identity<const C>::type &c,
-            typename identity<Arg1>::type arg1,
-            typename identity<Arg2>::type arg2,
-            typename identity<Arg3>::type arg3)
-  {
-    return
-      new_task (std::function<RT ()>
-                (std::bind(fun_ptr, std::cref(c),
-                           internal::maybe_make_ref<Arg1>::act(arg1),
-                           internal::maybe_make_ref<Arg2>::act(arg2),
-                           internal::maybe_make_ref<Arg3>::act(arg3))));
-  }
-#endif
-
-
-// ----------- thread starters for functions with 4 arguments
-
-  /**
-   * Overload of the new_task function for non-member or static member
-   * functions with 4 arguments.
-   *
-   * @ingroup threads
-   */
-  template <typename RT,
-            typename Arg1, typename Arg2, typename Arg3, typename Arg4>
-  inline
-  Task<RT>
-  new_task (RT (*fun_ptr)(Arg1,Arg2,Arg3,Arg4),
-            typename identity<Arg1>::type arg1,
-            typename identity<Arg2>::type arg2,
-            typename identity<Arg3>::type arg3,
-            typename identity<Arg4>::type arg4)
-  {
-    return
-      new_task (std::function<RT ()>
-                (std::bind(fun_ptr,
-                           internal::maybe_make_ref<Arg1>::act(arg1),
-                           internal::maybe_make_ref<Arg2>::act(arg2),
-                           internal::maybe_make_ref<Arg3>::act(arg3),
-                           internal::maybe_make_ref<Arg4>::act(arg4))));
-  }
-
-
-
-  /**
-   * Overload of the non-const new_task function for member functions with 4
-   * arguments.
-   *
-   * @ingroup threads
-   */
-  template <typename RT, typename C,
-            typename Arg1, typename Arg2, typename Arg3, typename Arg4>
-  inline
-  Task<RT>
-  new_task (RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4),
-            typename identity<C>::type &c,
-            typename identity<Arg1>::type arg1,
-            typename identity<Arg2>::type arg2,
-            typename identity<Arg3>::type arg3,
-            typename identity<Arg4>::type arg4)
-  {
-    return
-      new_task (std::function<RT ()>
-                (std::bind(fun_ptr, std::ref(c),
-                           internal::maybe_make_ref<Arg1>::act(arg1),
-                           internal::maybe_make_ref<Arg2>::act(arg2),
-                           internal::maybe_make_ref<Arg3>::act(arg3),
-                           internal::maybe_make_ref<Arg4>::act(arg4))));
-  }
-
-#ifndef DEAL_II_CONST_MEMBER_DEDUCTION_BUG
-  /**
-   * Overload of the new_task function for const member functions with 4
-   * arguments.
-   *
-   * @ingroup threads
-   */
-  template <typename RT, typename C,
-            typename Arg1, typename Arg2, typename Arg3, typename Arg4>
-  inline
-  Task<RT>
-  new_task (RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4) const,
-            typename identity<const C>::type &c,
-            typename identity<Arg1>::type arg1,
-            typename identity<Arg2>::type arg2,
-            typename identity<Arg3>::type arg3,
-            typename identity<Arg4>::type arg4)
-  {
-    return
-      new_task (std::function<RT ()>
-                (std::bind(fun_ptr, std::cref(c),
-                           internal::maybe_make_ref<Arg1>::act(arg1),
-                           internal::maybe_make_ref<Arg2>::act(arg2),
-                           internal::maybe_make_ref<Arg3>::act(arg3),
-                           internal::maybe_make_ref<Arg4>::act(arg4))));
-  }
-#endif
-
-// ----------- thread starters for functions with 5 arguments
-
-  /**
-   * Overload of the new_task function for non-member or static member
-   * functions with 5 arguments.
-   *
-   * @ingroup threads
-   */
-  template <typename RT,
-            typename Arg1, typename Arg2, typename Arg3,
-            typename Arg4, typename Arg5>
-  inline
-  Task<RT>
-  new_task (RT (*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5),
-            typename identity<Arg1>::type arg1,
-            typename identity<Arg2>::type arg2,
-            typename identity<Arg3>::type arg3,
-            typename identity<Arg4>::type arg4,
-            typename identity<Arg5>::type arg5)
-  {
-    return
-      new_task (std::function<RT ()>
-                (std::bind(fun_ptr,
-                           internal::maybe_make_ref<Arg1>::act(arg1),
-                           internal::maybe_make_ref<Arg2>::act(arg2),
-                           internal::maybe_make_ref<Arg3>::act(arg3),
-                           internal::maybe_make_ref<Arg4>::act(arg4),
-                           internal::maybe_make_ref<Arg5>::act(arg5))));
-  }
-
-
-
-  /**
-   * Overload of the non-const new_task function for member functions with 5
-   * arguments.
-   *
-   * @ingroup threads
-   */
-  template <typename RT, typename C,
-            typename Arg1, typename Arg2, typename Arg3,
-            typename Arg4, typename Arg5>
-  inline
-  Task<RT>
-  new_task (RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5),
-            typename identity<C>::type &c,
-            typename identity<Arg1>::type arg1,
-            typename identity<Arg2>::type arg2,
-            typename identity<Arg3>::type arg3,
-            typename identity<Arg4>::type arg4,
-            typename identity<Arg5>::type arg5)
-  {
-    return
-      new_task (std::function<RT ()>
-                (std::bind(fun_ptr, std::ref(c),
-                           internal::maybe_make_ref<Arg1>::act(arg1),
-                           internal::maybe_make_ref<Arg2>::act(arg2),
-                           internal::maybe_make_ref<Arg3>::act(arg3),
-                           internal::maybe_make_ref<Arg4>::act(arg4),
-                           internal::maybe_make_ref<Arg5>::act(arg5))));
-  }
-
-#ifndef DEAL_II_CONST_MEMBER_DEDUCTION_BUG
-  /**
-   * Overload of the new_task function for const member functions with 5
-   * arguments.
-   *
-   * @ingroup threads
-   */
-  template <typename RT, typename C,
-            typename Arg1, typename Arg2, typename Arg3,
-            typename Arg4, typename Arg5>
-  inline
-  Task<RT>
-  new_task (RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5) const,
-            typename identity<const C>::type &c,
-            typename identity<Arg1>::type arg1,
-            typename identity<Arg2>::type arg2,
-            typename identity<Arg3>::type arg3,
-            typename identity<Arg4>::type arg4,
-            typename identity<Arg5>::type arg5)
-  {
-    return
-      new_task (std::function<RT ()>
-                (std::bind(fun_ptr, std::cref(c),
-                           internal::maybe_make_ref<Arg1>::act(arg1),
-                           internal::maybe_make_ref<Arg2>::act(arg2),
-                           internal::maybe_make_ref<Arg3>::act(arg3),
-                           internal::maybe_make_ref<Arg4>::act(arg4),
-                           internal::maybe_make_ref<Arg5>::act(arg5))));
-  }
-#endif
-
-// ----------- thread starters for functions with 6 arguments
-
-  /**
-   * Overload of the new_task function for non-member or static member
-   * functions with 6 arguments.
-   *
-   * @ingroup threads
-   */
-  template <typename RT,
-            typename Arg1, typename Arg2, typename Arg3,
-            typename Arg4, typename Arg5, typename Arg6>
-  inline
-  Task<RT>
-  new_task (RT (*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5,Arg6),
-            typename identity<Arg1>::type arg1,
-            typename identity<Arg2>::type arg2,
-            typename identity<Arg3>::type arg3,
-            typename identity<Arg4>::type arg4,
-            typename identity<Arg5>::type arg5,
-            typename identity<Arg6>::type arg6)
-  {
-    return
-      new_task (std::function<RT ()>
-                (std::bind(fun_ptr,
-                           internal::maybe_make_ref<Arg1>::act(arg1),
-                           internal::maybe_make_ref<Arg2>::act(arg2),
-                           internal::maybe_make_ref<Arg3>::act(arg3),
-                           internal::maybe_make_ref<Arg4>::act(arg4),
-                           internal::maybe_make_ref<Arg5>::act(arg5),
-                           internal::maybe_make_ref<Arg6>::act(arg6))));
-  }
-
-
-
-  /**
-   * Overload of the non-const new_task function for member functions with 6
-   * arguments.
-   *
-   * @ingroup threads
-   */
-  template <typename RT, typename C,
-            typename Arg1, typename Arg2, typename Arg3,
-            typename Arg4, typename Arg5, typename Arg6>
-  inline
-  Task<RT>
-  new_task (RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5,Arg6),
-            typename identity<C>::type &c,
-            typename identity<Arg1>::type arg1,
-            typename identity<Arg2>::type arg2,
-            typename identity<Arg3>::type arg3,
-            typename identity<Arg4>::type arg4,
-            typename identity<Arg5>::type arg5,
-            typename identity<Arg6>::type arg6)
-  {
-    return
-      new_task (std::function<RT ()>
-                (std::bind(fun_ptr, std::ref(c),
-                           internal::maybe_make_ref<Arg1>::act(arg1),
-                           internal::maybe_make_ref<Arg2>::act(arg2),
-                           internal::maybe_make_ref<Arg3>::act(arg3),
-                           internal::maybe_make_ref<Arg4>::act(arg4),
-                           internal::maybe_make_ref<Arg5>::act(arg5),
-                           internal::maybe_make_ref<Arg6>::act(arg6))));
-  }
-
-#ifndef DEAL_II_CONST_MEMBER_DEDUCTION_BUG
-  /**
-   * Overload of the new_task function for const member functions with 6
-   * arguments.
-   *
-   * @ingroup threads
-   */
-  template <typename RT, typename C,
-            typename Arg1, typename Arg2, typename Arg3,
-            typename Arg4, typename Arg5, typename Arg6>
-  inline
-  Task<RT>
-  new_task (RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5,Arg6) const,
-            typename identity<const C>::type &c,
-            typename identity<Arg1>::type arg1,
-            typename identity<Arg2>::type arg2,
-            typename identity<Arg3>::type arg3,
-            typename identity<Arg4>::type arg4,
-            typename identity<Arg5>::type arg5,
-            typename identity<Arg6>::type arg6)
-  {
-    return
-      new_task (std::function<RT ()>
-                (std::bind(fun_ptr, std::cref(c),
-                           internal::maybe_make_ref<Arg1>::act(arg1),
-                           internal::maybe_make_ref<Arg2>::act(arg2),
-                           internal::maybe_make_ref<Arg3>::act(arg3),
-                           internal::maybe_make_ref<Arg4>::act(arg4),
-                           internal::maybe_make_ref<Arg5>::act(arg5),
-                           internal::maybe_make_ref<Arg6>::act(arg6))));
-  }
-#endif
-
-// ----------- thread starters for functions with 7 arguments
-
-  /**
-   * Overload of the new_task function for non-member or static member
-   * functions with 7 arguments.
-   *
-   * @ingroup threads
-   */
-  template <typename RT,
-            typename Arg1, typename Arg2, typename Arg3,
-            typename Arg4, typename Arg5, typename Arg6,
-            typename Arg7>
-  inline
-  Task<RT>
-  new_task (RT (*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5,Arg6,Arg7),
-            typename identity<Arg1>::type arg1,
-            typename identity<Arg2>::type arg2,
-            typename identity<Arg3>::type arg3,
-            typename identity<Arg4>::type arg4,
-            typename identity<Arg5>::type arg5,
-            typename identity<Arg6>::type arg6,
-            typename identity<Arg7>::type arg7)
-  {
-    return
-      new_task (std::function<RT ()>
-                (std::bind(fun_ptr,
-                           internal::maybe_make_ref<Arg1>::act(arg1),
-                           internal::maybe_make_ref<Arg2>::act(arg2),
-                           internal::maybe_make_ref<Arg3>::act(arg3),
-                           internal::maybe_make_ref<Arg4>::act(arg4),
-                           internal::maybe_make_ref<Arg5>::act(arg5),
-                           internal::maybe_make_ref<Arg6>::act(arg6),
-                           internal::maybe_make_ref<Arg7>::act(arg7))));
+       */
+      static void queue_task () {}
+    };
+
+#endif
+
   }
 
 
 
   /**
-   * Overload of the non-const new_task function for member functions with 7
-   * arguments.
+   * Describes one task object based on the Threading Building Blocks' Task.
+   * Note that the call to join() must be executed on the same thread as the
+   * call to the constructor. Otherwise, there might be a deadlock. In other
+   * words, a Task object should never passed on to another task for calling
+   * the join() method.
    *
+   * @author Wolfgang Bangerth, 2009
    * @ingroup threads
    */
-  template <typename RT, typename C,
-            typename Arg1, typename Arg2, typename Arg3,
-            typename Arg4, typename Arg5, typename Arg6,
-            typename Arg7>
-  inline
-  Task<RT>
-  new_task (RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5,Arg6,Arg7),
-            typename identity<C>::type &c,
-            typename identity<Arg1>::type arg1,
-            typename identity<Arg2>::type arg2,
-            typename identity<Arg3>::type arg3,
-            typename identity<Arg4>::type arg4,
-            typename identity<Arg5>::type arg5,
-            typename identity<Arg6>::type arg6,
-            typename identity<Arg7>::type arg7)
+  template <typename RT = void>
+  class Task
   {
-    return
-      new_task (std::function<RT ()>
-                (std::bind(fun_ptr, std::ref(c),
-                           internal::maybe_make_ref<Arg1>::act(arg1),
-                           internal::maybe_make_ref<Arg2>::act(arg2),
-                           internal::maybe_make_ref<Arg3>::act(arg3),
-                           internal::maybe_make_ref<Arg4>::act(arg4),
-                           internal::maybe_make_ref<Arg5>::act(arg5),
-                           internal::maybe_make_ref<Arg6>::act(arg6),
-                           internal::maybe_make_ref<Arg7>::act(arg7))));
-  }
+  public:
+    /**
+     * Construct a task object given a function object to execute on the task,
+     * and then schedule this function for execution.
+     *
+     * @post Using this constructor automatically makes the task object
+     * joinable().
+     */
+    Task (const std::function<RT ()> &function_object)
+    {
+      // create a task descriptor and tell it to queue itself up with
+      // the scheduling system
+      task_descriptor.reset (new internal::TaskDescriptor<RT>(function_object));
+      task_descriptor->queue_task ();
+    }
 
-#ifndef DEAL_II_CONST_MEMBER_DEDUCTION_BUG
-  /**
-   * Overload of the new_task function for const member functions with 7
-   * arguments.
-   *
-   * @ingroup threads
-   */
-  template <typename RT, typename C,
-            typename Arg1, typename Arg2, typename Arg3,
-            typename Arg4, typename Arg5, typename Arg6,
-            typename Arg7>
-  inline
-  Task<RT>
-  new_task (RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5,Arg6,Arg7) const,
-            typename identity<const C>::type &c,
-            typename identity<Arg1>::type arg1,
-            typename identity<Arg2>::type arg2,
-            typename identity<Arg3>::type arg3,
-            typename identity<Arg4>::type arg4,
-            typename identity<Arg5>::type arg5,
-            typename identity<Arg6>::type arg6,
-            typename identity<Arg7>::type arg7)
-  {
-    return
-      new_task (std::function<RT ()>
-                (std::bind(fun_ptr, std::cref(c),
-                           internal::maybe_make_ref<Arg1>::act(arg1),
-                           internal::maybe_make_ref<Arg2>::act(arg2),
-                           internal::maybe_make_ref<Arg3>::act(arg3),
-                           internal::maybe_make_ref<Arg4>::act(arg4),
-                           internal::maybe_make_ref<Arg5>::act(arg5),
-                           internal::maybe_make_ref<Arg6>::act(arg6),
-                           internal::maybe_make_ref<Arg7>::act(arg7))));
-  }
-#endif
 
-// ----------- thread starters for functions with 8 arguments
+    /**
+     * Copy constructor.
+     *
+     * @post Using this constructor automatically makes the task object
+     * joinable().
+     */
+    Task (const Task<RT> &t)
+      :
+      task_descriptor (t.task_descriptor)
+    {}
 
-  /**
-   * Overload of the new_task function for non-member or static member
-   * functions with 8 arguments.
-   *
-   * @ingroup threads
-   */
-  template <typename RT,
-            typename Arg1, typename Arg2, typename Arg3,
-            typename Arg4, typename Arg5, typename Arg6,
-            typename Arg7, typename Arg8>
-  inline
-  Task<RT>
-  new_task (RT (*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5,
-                          Arg6,Arg7,Arg8),
-            typename identity<Arg1>::type arg1,
-            typename identity<Arg2>::type arg2,
-            typename identity<Arg3>::type arg3,
-            typename identity<Arg4>::type arg4,
-            typename identity<Arg5>::type arg5,
-            typename identity<Arg6>::type arg6,
-            typename identity<Arg7>::type arg7,
-            typename identity<Arg8>::type arg8)
-  {
-    return
-      new_task (std::function<RT ()>
-                (std::bind(fun_ptr,
-                           internal::maybe_make_ref<Arg1>::act(arg1),
-                           internal::maybe_make_ref<Arg2>::act(arg2),
-                           internal::maybe_make_ref<Arg3>::act(arg3),
-                           internal::maybe_make_ref<Arg4>::act(arg4),
-                           internal::maybe_make_ref<Arg5>::act(arg5),
-                           internal::maybe_make_ref<Arg6>::act(arg6),
-                           internal::maybe_make_ref<Arg7>::act(arg7),
-                           internal::maybe_make_ref<Arg8>::act(arg8))));
-  }
+
+    /**
+     * Default constructor. You can't do much with a task object constructed
+     * this way, except for assigning it a task object that holds data created
+     * by the Threads::new_task() functions.
+     *
+     * @post Using this constructor leaves the object in an unjoinable state,
+     * i.e., joinable() will return false.
+     */
+    Task () = default;
+
+    /**
+     * Join the task represented by this object, i.e. wait for it to finish.
+     *
+     * A task can be joined multiple times (while the first join() operation
+     * may block until the task has completed running, all successive attempts
+     * to join will return immediately).
+     *
+     * @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.
+     */
+    void join () const
+    {
+      AssertThrow (joinable(), ExcNoTask());
+      task_descriptor->join ();
+    }
+
+    /**
+     * Return whether the current object can be joined. You can join a task
+     * object once a task (typically created with Threads::new_task()) has
+     * actually been assigned to it. On the other hand, the function returns
+     * false if the object has been default constructed.
+     *
+     * A task can be joined multiple times (while the first join() operation
+     * may block until the task has completed running, all successive attempts
+     * to join will return immediately). Consequently, if this function
+     * returns true, it will continue to return true until the task object it
+     * reports on is assigned to from another object.
+     */
+    bool joinable () const
+    {
+      return (task_descriptor !=
+              std::shared_ptr<internal::TaskDescriptor<RT> >());
+    }
+
+
+    /**
+     * Get the return value of the function of the task. Since this is only
+     * available once the task finishes, this implicitly also calls join().
+     * You can call this function multiple times as long as the object refers
+     * to the same task, and expect to get the same return value every time.
+     *
+     * @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.
+     */
+    RT return_value ()
+    {
+      join ();
+      return task_descriptor->ret_val.get();
+    }
+
+
+    /**
+     * Check for equality of task objects. Since objects of this class store
+     * an implicit pointer to an object that exists exactly once for each
+     * task, the check is simply to compare these pointers.
+     */
+    bool operator == (const Task &t) const
+    {
+      AssertThrow (joinable(), ExcNoTask());
+      return task_descriptor == t.task_descriptor;
+    }
+
+    /**
+     * @addtogroup Exceptions
+     * @{
+     */
+
+    /**
+     * Exception
+     */
+    DeclExceptionMsg (ExcNoTask,
+                      "The current object is not associated with a task that "
+                      "can be joined. It may have been detached, or you "
+                      "may have already joined it in the past.");
+    //@}
+  private:
+    /**
+     * Shared pointer to the object representing the task. Boost's shared
+     * pointer implementation will make sure that that object lives as long as
+     * there is at least one subscriber to it.
+     */
+    std::shared_ptr<internal::TaskDescriptor<RT> > task_descriptor;
+  };
 
 
 
   /**
-   * Overload of the non-const new_task function for member functions with 8
-   * arguments.
+   * Overload of the new_task function for objects that can be converted to
+   * std::function<RT ()>, i.e. anything that can be called like a
+   * function object without arguments and returning an object of type RT (or
+   * void).
    *
    * @ingroup threads
    */
-  template <typename RT, typename C,
-            typename Arg1, typename Arg2, typename Arg3,
-            typename Arg4, typename Arg5, typename Arg6,
-            typename Arg7, typename Arg8>
+  template <typename RT>
   inline
   Task<RT>
-  new_task (RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5,
-                             Arg6,Arg7,Arg8),
-            typename identity<C>::type &c,
-            typename identity<Arg1>::type arg1,
-            typename identity<Arg2>::type arg2,
-            typename identity<Arg3>::type arg3,
-            typename identity<Arg4>::type arg4,
-            typename identity<Arg5>::type arg5,
-            typename identity<Arg6>::type arg6,
-            typename identity<Arg7>::type arg7,
-            typename identity<Arg8>::type arg8)
+  new_task (const std::function<RT ()> &function)
   {
-    return
-      new_task (std::function<RT ()>
-                (std::bind(fun_ptr, std::ref(c),
-                           internal::maybe_make_ref<Arg1>::act(arg1),
-                           internal::maybe_make_ref<Arg2>::act(arg2),
-                           internal::maybe_make_ref<Arg3>::act(arg3),
-                           internal::maybe_make_ref<Arg4>::act(arg4),
-                           internal::maybe_make_ref<Arg5>::act(arg5),
-                           internal::maybe_make_ref<Arg6>::act(arg6),
-                           internal::maybe_make_ref<Arg7>::act(arg7),
-                           internal::maybe_make_ref<Arg8>::act(arg8))));
+    return Task<RT>(function);
   }
 
-#ifndef DEAL_II_CONST_MEMBER_DEDUCTION_BUG
+
+
   /**
-   * Overload of the new_task function for const member functions with 8
-   * arguments.
+   * Overload of the new_task function for objects that can be called like a
+   * function object without arguments. In particular, this function allows
+   * calling Threads::new_task() with either objects that result from using
+   * std::bind, or using lambda functions. For example, this function is called
+   * when writing code such as
+   * @code
+   * Threads::Task<int>
+   *   task = Threads::new_task ( [] () {
+   *                                      do_this();
+   *                                      then_do_that();
+   *                                      return 42;
+   *                                    });
+   * @endcode
+   * Here, we schedule the call to the sequence of functions
+   * <code>do_this()</code> and <code>then_do_that()</code> on
+   * a separate task, by making the lambda function declared here the
+   * function to execute on the task. The lambda function then returns
+   * 42 (which is a bit pointless here, but it could of course be some
+   * computed number), and this is going to be the returned value you
+   * can later retrieve via <code>task.return_value()</code> once the
+   * task (i.e., the body of the lambda function) has completed.
    *
-   * @ingroup threads
+   * @note Every lambda function (or whatever else it is you pass to
+   *   the new_task() function here, for example the result of a
+   *   std::bind() expression) has a return type and consequently
+   *   returns an object of this type. This type can be inferred
+   *   using the C++11 <code>decltype</code> statement used in the
+   *   declaration of this function, and it is then used as the template
+   *   argument of the Threads::Task object returned by the current function.
+   *   In the example above, because the lambda function returns 42
+   *   (which in C++ has data type <code>int</code>), the inferred
+   *   type is <code>int</code> and the task object will have type
+   *   <code>Task@<int@></code>. In other words, it is not <i>necessary</i>
+   *   to explicitly specify in user code what that return type
+   *   of the lambda or std::bind expression will be, though it is
+   *   possible to explicitly do so by (entirely equivalently) writing
+   *   @code
+   *   Threads::Task<int>
+   *     task = Threads::new_task ( [] () -> int {
+   *                                               do_this();
+   *                                               then_do_that();
+   *                                               return 42;
+   *                                             });
+   *   @endcode
+   *
+   * @note In practice, the lambda functions you will pass to
+   *   new_task() will of course typically be more complicated.
+   *   In particular, they will likely <i>capture</i> variables
+   *   from the surrounding context and use them within the lambda.
+   *   See https://en.wikipedia.org/wiki/Anonymous_function#C.2B.2B_.28since_C.2B.2B11.29
+   *   for more on how lambda functions work.
+   *
+   * @note If you pass a lambda function as an argument to the
+   *   current function that captures a variable <i>by reference</i>,
+   *   or if you use a std::bind that binds a function argument to
+   *   a reference variable using std::ref() or std::cref(), then
+   *   obviously you can only do this if the variables you reference
+   *   or capture have a lifetime that extends at least until the time
+   *   where the task finishes.
+   *
+   * @ingroup CPP11
    */
-  template <typename RT, typename C,
-            typename Arg1, typename Arg2, typename Arg3,
-            typename Arg4, typename Arg5, typename Arg6,
-            typename Arg7, typename Arg8>
+  template <typename FunctionObjectType>
   inline
-  Task<RT>
-  new_task (RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5,
-                             Arg6,Arg7,Arg8) const,
-            typename identity<const C>::type &c,
-            typename identity<Arg1>::type arg1,
-            typename identity<Arg2>::type arg2,
-            typename identity<Arg3>::type arg3,
-            typename identity<Arg4>::type arg4,
-            typename identity<Arg5>::type arg5,
-            typename identity<Arg6>::type arg6,
-            typename identity<Arg7>::type arg7,
-            typename identity<Arg8>::type arg8)
+  auto
+  new_task (FunctionObjectType function_object)
+  -> Task<decltype(function_object())>
   {
-    return
-      new_task (std::function<RT ()>
-                (std::bind(fun_ptr, std::cref(c),
-                           internal::maybe_make_ref<Arg1>::act(arg1),
-                           internal::maybe_make_ref<Arg2>::act(arg2),
-                           internal::maybe_make_ref<Arg3>::act(arg3),
-                           internal::maybe_make_ref<Arg4>::act(arg4),
-                           internal::maybe_make_ref<Arg5>::act(arg5),
-                           internal::maybe_make_ref<Arg6>::act(arg6),
-                           internal::maybe_make_ref<Arg7>::act(arg7),
-                           internal::maybe_make_ref<Arg8>::act(arg8))));
+    typedef decltype(function_object()) return_type;
+    return Task<return_type>(std::function<return_type ()>(function_object));
   }
-#endif
 
-// ----------- thread starters for functions with 9 arguments
+
 
   /**
    * Overload of the new_task function for non-member or static member
-   * functions with 9 arguments.
+   * functions.
    *
    * @ingroup threads
    */
-  template <typename RT,
-            typename Arg1, typename Arg2, typename Arg3,
-            typename Arg4, typename Arg5, typename Arg6,
-            typename Arg7, typename Arg8, typename Arg9>
+  template <typename RT, typename... Args>
   inline
   Task<RT>
-  new_task (RT (*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5,
-                          Arg6,Arg7,Arg8,Arg9),
-            typename identity<Arg1>::type arg1,
-            typename identity<Arg2>::type arg2,
-            typename identity<Arg3>::type arg3,
-            typename identity<Arg4>::type arg4,
-            typename identity<Arg5>::type arg5,
-            typename identity<Arg6>::type arg6,
-            typename identity<Arg7>::type arg7,
-            typename identity<Arg8>::type arg8,
-            typename identity<Arg9>::type arg9)
+  new_task (RT (*fun_ptr)(Args...),
+            typename identity<Args>::type... args)
   {
     return
       new_task (std::function<RT ()>
                 (std::bind(fun_ptr,
-                           internal::maybe_make_ref<Arg1>::act(arg1),
-                           internal::maybe_make_ref<Arg2>::act(arg2),
-                           internal::maybe_make_ref<Arg3>::act(arg3),
-                           internal::maybe_make_ref<Arg4>::act(arg4),
-                           internal::maybe_make_ref<Arg5>::act(arg5),
-                           internal::maybe_make_ref<Arg6>::act(arg6),
-                           internal::maybe_make_ref<Arg7>::act(arg7),
-                           internal::maybe_make_ref<Arg8>::act(arg8),
-                           internal::maybe_make_ref<Arg9>::act(arg9))));
+                           internal::maybe_make_ref<Args>::act(args)...)));
   }
 
 
 
   /**
-   * Overload of the non-const new_task function for member functions with 9
-   * arguments.
+   * Overloads of the non-const new_task function.
    *
    * @ingroup threads
    */
-  template <typename RT, typename C,
-            typename Arg1, typename Arg2, typename Arg3,
-            typename Arg4, typename Arg5, typename Arg6,
-            typename Arg7, typename Arg8, typename Arg9>
+  template <typename RT, typename C, typename... Args>
   inline
   Task<RT>
-  new_task (RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5,
-                             Arg6,Arg7,Arg8,Arg9),
+  new_task (RT (C::*fun_ptr)(Args...),
             typename identity<C>::type &c,
-            typename identity<Arg1>::type arg1,
-            typename identity<Arg2>::type arg2,
-            typename identity<Arg3>::type arg3,
-            typename identity<Arg4>::type arg4,
-            typename identity<Arg5>::type arg5,
-            typename identity<Arg6>::type arg6,
-            typename identity<Arg7>::type arg7,
-            typename identity<Arg8>::type arg8,
-            typename identity<Arg9>::type arg9)
+            typename identity<Args>::type... args)
   {
     return
       new_task (std::function<RT ()>
                 (std::bind(fun_ptr, std::ref(c),
-                           internal::maybe_make_ref<Arg1>::act(arg1),
-                           internal::maybe_make_ref<Arg2>::act(arg2),
-                           internal::maybe_make_ref<Arg3>::act(arg3),
-                           internal::maybe_make_ref<Arg4>::act(arg4),
-                           internal::maybe_make_ref<Arg5>::act(arg5),
-                           internal::maybe_make_ref<Arg6>::act(arg6),
-                           internal::maybe_make_ref<Arg7>::act(arg7),
-                           internal::maybe_make_ref<Arg8>::act(arg8),
-                           internal::maybe_make_ref<Arg9>::act(arg9))));
+                           internal::maybe_make_ref<Args>::act(args)...)));
   }
 
 #ifndef DEAL_II_CONST_MEMBER_DEDUCTION_BUG
   /**
-   * Overload of the new_task function for const member functions with 9
-   * arguments.
+   * Overloads of the new_task function.
    *
    * @ingroup threads
    */
-  template <typename RT, typename C,
-            typename Arg1, typename Arg2, typename Arg3,
-            typename Arg4, typename Arg5, typename Arg6,
-            typename Arg7, typename Arg8, typename Arg9>
+  template <typename RT, typename C, typename... Args>
   inline
   Task<RT>
-  new_task (RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5,
-                             Arg6,Arg7,Arg8,Arg9) const,
+  new_task (RT (C::*fun_ptr)(Args...) const,
             typename identity<const C>::type &c,
-            typename identity<Arg1>::type arg1,
-            typename identity<Arg2>::type arg2,
-            typename identity<Arg3>::type arg3,
-            typename identity<Arg4>::type arg4,
-            typename identity<Arg5>::type arg5,
-            typename identity<Arg6>::type arg6,
-            typename identity<Arg7>::type arg7,
-            typename identity<Arg8>::type arg8,
-            typename identity<Arg9>::type arg9)
+            typename identity<Args>::type... args)
   {
     return
       new_task (std::function<RT ()>
                 (std::bind(fun_ptr, std::cref(c),
-                           internal::maybe_make_ref<Arg1>::act(arg1),
-                           internal::maybe_make_ref<Arg2>::act(arg2),
-                           internal::maybe_make_ref<Arg3>::act(arg3),
-                           internal::maybe_make_ref<Arg4>::act(arg4),
-                           internal::maybe_make_ref<Arg5>::act(arg5),
-                           internal::maybe_make_ref<Arg6>::act(arg6),
-                           internal::maybe_make_ref<Arg7>::act(arg7),
-                           internal::maybe_make_ref<Arg8>::act(arg8),
-                           internal::maybe_make_ref<Arg9>::act(arg9))));
+                           internal::maybe_make_ref<Args>::act(args)...)));
   }
 #endif
 

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.