/**
* 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