From c923a53abc0afe9b314137d972bddc46e9aadabc Mon Sep 17 00:00:00 2001 From: bangerth Date: Mon, 4 Jul 2011 16:38:59 +0000 Subject: [PATCH] Allow calling join() on a thread object more than once. git-svn-id: https://svn.dealii.org/trunk@23917 0785d39b-7218-0410-832d-ea1e28bc413d --- deal.II/doc/news/changes.h | 14 +++ .../include/deal.II/base/thread_management.h | 97 ++++++++++++++----- tests/base/thread_validity_11.cc | 50 ++++++++++ tests/base/thread_validity_11/cmp/generic | 4 + tests/base/thread_validity_12.cc | 49 ++++++++++ tests/base/thread_validity_12/cmp/generic | 4 + 6 files changed, 196 insertions(+), 22 deletions(-) create mode 100644 tests/base/thread_validity_11.cc create mode 100644 tests/base/thread_validity_11/cmp/generic create mode 100644 tests/base/thread_validity_12.cc create mode 100644 tests/base/thread_validity_12/cmp/generic diff --git a/deal.II/doc/news/changes.h b/deal.II/doc/news/changes.h index 9839e06036..8a0a0fe2ba 100644 --- a/deal.II/doc/news/changes.h +++ b/deal.II/doc/news/changes.h @@ -216,6 +216,20 @@ should be fixed now.

Specific improvements

    +
  1. Fixed: Under some circumstances, the Threads::Thread::join() could only be +called once and would generate a system exception when called a second time. +Since it is often useful to not track whether this function had already been +called, this is now worked around in such a way that one can always call +the function multiple times. +
    +(Wolfgang Bangerth 2011/07/03) + +
  2. New: The Threads::Thread::join() function can now also be called even +if no thread has been assigned to this thread object. The function then simply +does nothing. +
    +(Wolfgang Bangerth 2011/07/03) +
  3. New: There is now a new function Threads::Thread::valid that can be used to query whether the thread object has been assigned a thread.
    diff --git a/deal.II/include/deal.II/base/thread_management.h b/deal.II/include/deal.II/base/thread_management.h index 2b8fc82093..b74443d6d2 100644 --- a/deal.II/include/deal.II/base/thread_management.h +++ b/deal.II/include/deal.II/base/thread_management.h @@ -1298,6 +1298,63 @@ namespace Threads */ return_value ret_val; + /** + * A bool variable that is initially + * false, is set to true when a new + * thread is started, and is set back + * to false once join() has been + * called. + * + * We use this variable to make sure + * we can call join() twice on the + * same thread. For some reason, the + * C++ standard library throws a + * std::system_error exception if one + * tries to call std::thread::join + * twice (and in fact, before the + * second call, std::thread::joinable + * returns false) but this is a + * somewhat desirable thing to do + * because one doesn't have to keep + * track whether join() has been + * called before. Using this + * variable, whenever we have called + * join() before, the variable is set + * to true and we can skip over + * calling std::thread::join() a + * second time. + * + * Further, note that we need no + * mutex for this variable: threads + * can only be join from the thread + * that created it + * originally. Consequently, + * everything that happens in a + * function that does not create + * threads (such as the join() + * function below) looks atomic to + * the outside world. Since we clear + * and test thread_is_active in the + * same function as we call + * std::thread::join, these actions + * are atomic and need no mutex. Of + * course, two threads may call + * join() on the same thread object + * at the same time, but this action + * is undefined anyway since they can + * not both join the same thread. + */ + bool thread_is_active; + + /** + * Default constructor. + */ + ThreadDescriptor () + : + thread_is_active (false) + {} + + /** * Start the thread and let * it put its return value @@ -1330,6 +1387,7 @@ namespace Threads // and then start the // thread self = this->shared_from_this (); + thread_is_active = true; thread = std_cxx1x::thread (thread_entry_point, function, this); } @@ -1339,8 +1397,15 @@ namespace Threads */ void join () { - Assert (thread.joinable(), ExcInternalError()); - thread.join (); + // see if the thread hasn't been + // joined yet. if it has, then + // join() is a no-op + if (thread_is_active == true) + { + Assert (thread.joinable(), ExcInternalError()); + thread.join (); + thread_is_active = false; + } } private: @@ -1518,21 +1583,17 @@ namespace Threads Thread () {} /** - * Join the thread represented - * by this object, i.e. wait - * for it to finish. You can't - * call this function if you - * have used the default - * constructor of this class - * and have not assigned a - * thread object to it (i.e. if - * the valid() function would - * return false). + * Join the thread represented by this + * object, i.e. wait for it to + * finish. If you have used the default + * constructor of this class and have + * not assigned a thread object to it, + * then this function is a no-op. */ void join () const { - AssertThrow (valid(), ExcNoThread()); - thread_descriptor->join (); + if (thread_descriptor) + thread_descriptor->join (); } /** @@ -1576,14 +1637,6 @@ namespace Threads return thread_descriptor == t.thread_descriptor; } - /** @addtogroup Exceptions - * @{ */ - - /** - * Exception - */ - DeclException0 (ExcNoThread); - //@} private: /** * Shared pointer to the object diff --git a/tests/base/thread_validity_11.cc b/tests/base/thread_validity_11.cc new file mode 100644 index 0000000000..4d8b2e9d7c --- /dev/null +++ b/tests/base/thread_validity_11.cc @@ -0,0 +1,50 @@ +//----------------------------------------------------------------------------- +// $Id$ +// Version: $Name$ +// +// Copyright (C) 2008, 2011 by the deal.II authors +// +// This file is subject to QPL and may not be distributed +// without copyright and license information. Please refer +// to the file deal.II/doc/license.html for the text and +// further information on this license. +// +//----------------------------------------------------------------------------- + +// It looks like we can't call std::thread::join() twice -- the second call +// produces a std::system_error exception, and this can in fact be verified +// because std::thread::joinable() returns false after the first call to +// join() + +#include "../tests.h" +#include +#include + +#include + +void execute () +{} + + +void test () +{ + Threads::Thread<> t = Threads::spawn (&execute)(); + deallog << "Before first join()" << std::endl; + t.join (); + deallog << "Between join()s" << std::endl; + t.join (); + deallog << "After second join()" << std::endl; +} + + + + +int main() +{ + std::ofstream logfile("thread_validity_11/output"); + deallog.attach(logfile); + deallog.depth_console(0); + deallog.threshold_double(1.e-10); + + test (); +} diff --git a/tests/base/thread_validity_11/cmp/generic b/tests/base/thread_validity_11/cmp/generic new file mode 100644 index 0000000000..9a8778923e --- /dev/null +++ b/tests/base/thread_validity_11/cmp/generic @@ -0,0 +1,4 @@ + +DEAL::Before first join() +DEAL::Between join()s +DEAL::After second join() diff --git a/tests/base/thread_validity_12.cc b/tests/base/thread_validity_12.cc new file mode 100644 index 0000000000..2816c47ab7 --- /dev/null +++ b/tests/base/thread_validity_12.cc @@ -0,0 +1,49 @@ +//----------------------------------------------------------------------------- +// $Id$ +// Version: $Name$ +// +// Copyright (C) 2008, 2011 by the deal.II authors +// +// This file is subject to QPL and may not be distributed +// without copyright and license information. Please refer +// to the file deal.II/doc/license.html for the text and +// further information on this license. +// +//----------------------------------------------------------------------------- + +// Make sure we can call Threads::Thread::join on objects that haven't even +// been assigned a thread + +#include "../tests.h" +#include +#include + +#include + +void execute () +{} + + +void test () +{ + // use a default constructed object + Threads::Thread<> t; + deallog << "Before first join()" << std::endl; + t.join (); + deallog << "Between join()s" << std::endl; + t.join (); + deallog << "After second join()" << std::endl; +} + + + + +int main() +{ + std::ofstream logfile("thread_validity_12/output"); + deallog.attach(logfile); + deallog.depth_console(0); + deallog.threshold_double(1.e-10); + + test (); +} diff --git a/tests/base/thread_validity_12/cmp/generic b/tests/base/thread_validity_12/cmp/generic new file mode 100644 index 0000000000..9a8778923e --- /dev/null +++ b/tests/base/thread_validity_12/cmp/generic @@ -0,0 +1,4 @@ + +DEAL::Before first join() +DEAL::Between join()s +DEAL::After second join() -- 2.39.5