]> https://gitweb.dealii.org/ - dealii-svn.git/commitdiff
Allow calling join() on a thread object more than once.
authorbangerth <bangerth@0785d39b-7218-0410-832d-ea1e28bc413d>
Mon, 4 Jul 2011 16:38:59 +0000 (16:38 +0000)
committerbangerth <bangerth@0785d39b-7218-0410-832d-ea1e28bc413d>
Mon, 4 Jul 2011 16:38:59 +0000 (16:38 +0000)
git-svn-id: https://svn.dealii.org/trunk@23917 0785d39b-7218-0410-832d-ea1e28bc413d

deal.II/doc/news/changes.h
deal.II/include/deal.II/base/thread_management.h
tests/base/thread_validity_11.cc [new file with mode: 0644]
tests/base/thread_validity_11/cmp/generic [new file with mode: 0644]
tests/base/thread_validity_12.cc [new file with mode: 0644]
tests/base/thread_validity_12/cmp/generic [new file with mode: 0644]

index 9839e060362d13f5eef4f9d498692fe113680af1..8a0a0fe2ba751a54029a9bfd3507b2fe155e286a 100644 (file)
@@ -216,6 +216,20 @@ should be fixed now.
 <h3>Specific improvements</h3>
 
 <ol>
+<li> 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.
+<br>
+(Wolfgang Bangerth 2011/07/03)
+
+<li> 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.
+<br>
+(Wolfgang Bangerth 2011/07/03)
+
 <li> 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.
 <br>
index 2b8fc82093c228eb190072777306640c73df9820..b74443d6d2bdc23f7ceb41b00aa47b16eb69076b 100644 (file)
@@ -1298,6 +1298,63 @@ namespace Threads
                                          */
         return_value<RT> 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 (file)
index 0000000..4d8b2e9
--- /dev/null
@@ -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 <iomanip>
+#include <fstream>
+
+#include <deal.II/base/thread_management.h>
+
+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 (file)
index 0000000..9a87789
--- /dev/null
@@ -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 (file)
index 0000000..2816c47
--- /dev/null
@@ -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 <iomanip>
+#include <fstream>
+
+#include <deal.II/base/thread_management.h>
+
+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 (file)
index 0000000..9a87789
--- /dev/null
@@ -0,0 +1,4 @@
+
+DEAL::Before first join()
+DEAL::Between join()s
+DEAL::After second join()

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

Douglas Adams


Typeset in Trocchi and Trocchi Bold Sans Serif.