]> https://gitweb.dealii.org/ - dealii.git/commitdiff
Make LogStream::push(...) and LogStream::pop() "thread safe"
authorMatthias Maier <tamiko@kyomu.43-1.org>
Thu, 18 Apr 2013 10:14:31 +0000 (10:14 +0000)
committerMatthias Maier <tamiko@kyomu.43-1.org>
Thu, 18 Apr 2013 10:14:31 +0000 (10:14 +0000)
git-svn-id: https://svn.dealii.org/trunk@29324 0785d39b-7218-0410-832d-ea1e28bc413d

deal.II/doc/news/changes.h
deal.II/include/deal.II/base/logstream.h
deal.II/include/deal.II/base/thread_local_storage.h
deal.II/source/base/logstream.cc

index 9bd5748caa5c7aed87df48c017796f900a00d334..7884f7c326812d8c19c6507ec97b78374dc88837 100644 (file)
@@ -110,6 +110,15 @@ this function.
 <h3>Specific improvements</h3>
 
 <ol>
+
+<li> Improved and Fixed: LogStream (and deallog) now respect std::flush in
+addition to std::endl to write out content to the console/file.
+Furthermore, LogStream::push(...) and LogStream::pop() now work in a thread
+safe manner.
+<br>
+(Matthias Maier, 2013/04/18)
+</li>
+
 <li> Fixed: The HalfHyperShellBoundary class got refining
 the edges that sit at the perimeter of the circular face of the domain
 wrong. This is now fixed.
@@ -117,12 +126,6 @@ wrong. This is now fixed.
 (Wolfgang Bangerth, J&ouml;rg Frohne, 2013/04/17)
 </li>
 
-<li> Improved: Logstream (and deallog) now respect std::flush in addition to std::endl
-to write out content to the console/file.
-<br>
-(Matthias Maier, 2013/04/17)
-</li>
-
 <li> New: Functions::FEFieldFunction can now deal with
 parallel::distributed::Triangulation objects.
 <br>
index d697ac038651a97ee30df425f13fc639261415f7..789a920a0c962d331d43cc8040868e84482367ab 100644 (file)
@@ -63,6 +63,24 @@ DEAL_II_NAMESPACE_OPEN
  * encountered, which will trigger a writeout to the console and, if set
  * up, the log file.
  *
+ * <h3>LogStream and thread safety</h3>
+ *
+ * In the vicinity of concurrent threads, LogStream behaves in the
+ * following manner:
+ * <ul>
+ * <li> Every write to a Logstream with operator <tt>&lt;&lt;</tt> (or with
+ * one of the special member functions) is buffered in a thread-local
+ * storage.
+ * <li> An <tt>std::flush</tt> or <tt>std::endl</tt> will trigger a
+ * writeout to the console and (if attached) to the file stream. This
+ * writeout is sequentialized so that output from concurrent threads don't
+ * interleave.
+ * <li> On a new thread, invoking a writeout, as well as a call to #push or
+ * #pop will copy the current prefix of the "blessed" thread that created
+ * the LogStream instance to a thread-local storage. After that prefixes
+ * are thread-local.
+ * </ul>
+ *
  * <h3>LogStream and reproducible regression test output</h3>
  *
  * Generating reproducible floating point output for regression tests
@@ -371,11 +389,21 @@ public:
 
 private:
 
+
+  /**
+   * Internal wrapper around thread-local prefixes. This private
+   * function will return the correct internal prefix stack. More
+   * important, a new thread-local stack will be copied from the current
+   * stack of the "blessed" thread that created this LogStream instance
+   * (usually, in the case of deallog, the "main" thread).
+   */
+  std::stack<std::string> &get_prefixes() const;
+
   /**
    * Stack of strings which are printed at the beginning of each line to
    * allow identification where the output was generated.
    */
-  std::stack<std::string> prefixes;
+  mutable Threads::ThreadLocalStorage<std::stack<std::string> > prefixes;
 
   /**
    * Default stream, where the output is to go to. This stream defaults to
@@ -575,9 +603,8 @@ LogStream::Prefix::~Prefix()
 }
 
 
-
 /**
- * The standard log object of DEAL.
+ * The standard log object of deal.II:
  *
  * @author Guido Kanschat, 1999
  */
@@ -593,7 +620,6 @@ LogStream::Prefix::Prefix(const std::string &text)
 }
 
 
-
 DEAL_II_NAMESPACE_CLOSE
 
 #endif
index 078266824a8ae387728114c45a072a8785468104..a97219c44d189b10c530d205e637fe180d4c604b 100644 (file)
@@ -98,6 +98,12 @@ namespace Threads
      */
     T &get ();
 
+    /**
+     * Same as above, except that @p exists is set to true if an element
+     * was already present for the current thread; false otherwise.
+     */
+    T &get (bool &exists);
+
     /**
      * Conversion operator that simply converts the thread-local object
      * to the data type that it stores. This function is equivalent to
@@ -120,6 +126,19 @@ namespace Threads
      **/
     ThreadLocalStorage<T> &operator = (const T &t);
 
+    /**
+     * Returns a reference to the internal implementation.
+     */
+#ifdef DEAL_II_WITH_THREADS
+    tbb::enumerable_thread_specific<T> &
+#else
+    T &
+#endif
+    get_implementation()
+    {
+      return data;
+    }
+
   private:
 #ifdef DEAL_II_WITH_THREADS
     /**
@@ -171,6 +190,19 @@ namespace Threads
   }
 
 
+  template <typename T>
+  inline
+  T &
+  ThreadLocalStorage<T>::get (bool &exists)
+  {
+#ifdef DEAL_II_WITH_THREADS
+    return data.local(exists);
+#else
+    exists = true;
+    return data;
+#endif
+  }
+
 
   template <typename T>
   inline
index bdd3bfe782d96f207dbae93de014aaeddfb1eaaf..c5af34880b0bf17b30a0720cf4a97b9c1d1c261d 100644 (file)
@@ -40,19 +40,27 @@ namespace
 }
 
 
+// The standard log object of deal.II:
 LogStream deallog;
 
 
 LogStream::LogStream()
   :
-  std_out(&std::cerr), file(0),
-  std_depth(10000), file_depth(10000),
-  print_utime(false), diff_utime(false),
-  last_time (0.), double_threshold(0.), float_threshold(0.),
-  offset(0), old_cerr(0), at_newline(true)
+  std_out(&std::cerr),
+  file(0),
+  std_depth(10000),
+  file_depth(10000),
+  print_utime(false),
+  diff_utime(false),
+  last_time (0.),
+  double_threshold(0.),
+  float_threshold(0.),
+  offset(0),
+  old_cerr(0),
+  at_newline(true)
 {
-  prefixes.push("DEAL:");
-  std_out->setf(std::ios::showpoint | std::ios::left);
+  get_prefixes().push("DEAL:");
+
 #if defined(HAVE_UNISTD_H) && defined(HAVE_TIMES)
   reference_time_val = 1./sysconf(_SC_CLK_TCK) * times(&reference_tms);
 #endif
@@ -144,10 +152,10 @@ LogStream::operator<< (std::ostream& (*p) (std::ostream &))
       if(p == p_endl)
         at_newline = true;
 
-      if (prefixes.size() <= std_depth)
+      if (get_prefixes().size() <= std_depth)
         *std_out << stream.str();
 
-      if (file && (prefixes.size() <= file_depth))
+      if (file && (get_prefixes().size() <= file_depth))
         *file << stream.str() << std::flush;
 
       // Start a new string
@@ -215,26 +223,24 @@ LogStream::has_file() const
 const std::string &
 LogStream::get_prefix() const
 {
-  return prefixes.top();
+  return get_prefixes().top();
 }
 
 
 void
 LogStream::push (const std::string &text)
 {
-  Threads::Mutex::ScopedLock lock(log_lock);
-  std::string pre=prefixes.top();
+  std::string pre=get_prefixes().top();
   pre += text;
   pre += std::string(":");
-  prefixes.push(pre);
+  get_prefixes().push(pre);
 }
 
 
 void LogStream::pop ()
 {
-  Threads::Mutex::ScopedLock lock(log_lock);
-  if (prefixes.size() > 1)
-    prefixes.pop();
+  if (get_prefixes().size() > 1)
+    get_prefixes().pop();
 }
 
 
@@ -303,6 +309,38 @@ LogStream::log_thread_id (const bool flag)
   return h;
 }
 
+std::stack<std::string> &
+LogStream::get_prefixes() const
+{
+#ifdef DEAL_II_WITH_THREADS
+  bool exists = false;
+  std::stack<std::string> &local_prefixes = prefixes.get(exists);
+
+  // If this is a new locally stored stack, copy the "blessed" prefixes
+  // from the initial thread that created logstream.
+  if(! exists)
+    {
+      const tbb::enumerable_thread_specific<std::stack<std::string> > &impl
+        = prefixes.get_implementation();
+
+      // The thread that created this LogStream object should be the first
+      // in tbb's enumerable_thread_specific containter.
+      const tbb::enumerable_thread_specific<std::stack<std::string> >::const_iterator first_elem
+        = impl.begin();
+
+      if (first_elem != impl.end())
+        {
+          local_prefixes = *first_elem;
+        }
+    }
+
+  return local_prefixes;
+
+#else
+  return prefixes.get();
+#endif
+}
+
 
 void
 LogStream::print_line_head()
@@ -360,7 +398,7 @@ LogStream::print_line_head()
   const std::string &head = get_prefix();
   const unsigned int thread = Threads::this_thread_id();
 
-  if (prefixes.size() <= std_depth)
+  if (get_prefixes().size() <= std_depth)
     {
       if (print_utime)
         {
@@ -377,7 +415,7 @@ LogStream::print_line_head()
       *std_out <<  head << ':';
     }
 
-  if (file && (prefixes.size() <= file_depth))
+  if (file && (get_prefixes().size() <= file_depth))
     {
       if (print_utime)
         {
@@ -419,17 +457,20 @@ LogStream::timestamp ()
 std::size_t
 LogStream::memory_consumption () const
 {
+  // TODO
+  Assert(false, ExcNotImplemented());
+
   std::size_t mem = sizeof(*this);
   // to determine size of stack
   // elements, we have to copy the
   // stack since we can't access
   // elements from further below
-  std::stack<std::string> tmp = prefixes;
-  while (tmp.empty() == false)
-    {
-      mem += MemoryConsumption::memory_consumption (tmp.top());
-      tmp.pop ();
-    }
+//   std::stack<std::string> tmp = prefixes;
+//   while (tmp.empty() == false)
+//     {
+//       mem += MemoryConsumption::memory_consumption (tmp.top());
+//       tmp.pop ();
+//     }
 
   return mem;
 }

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.