From 3cd825acd609daf71e31a0d214a7c2198e34f91f Mon Sep 17 00:00:00 2001 From: maier Date: Thu, 18 Apr 2013 10:14:31 +0000 Subject: [PATCH] Make LogStream::push(...) and LogStream::pop() "thread safe" git-svn-id: https://svn.dealii.org/trunk@29324 0785d39b-7218-0410-832d-ea1e28bc413d --- deal.II/doc/news/changes.h | 15 ++-- deal.II/include/deal.II/base/logstream.h | 34 ++++++- .../deal.II/base/thread_local_storage.h | 32 +++++++ deal.II/source/base/logstream.cc | 89 ++++++++++++++----- 4 files changed, 136 insertions(+), 34 deletions(-) diff --git a/deal.II/doc/news/changes.h b/deal.II/doc/news/changes.h index 9bd5748caa..7884f7c326 100644 --- a/deal.II/doc/news/changes.h +++ b/deal.II/doc/news/changes.h @@ -110,6 +110,15 @@ this function.

Specific improvements

    + +
  1. 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. +
    +(Matthias Maier, 2013/04/18) +
  2. +
  3. 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örg Frohne, 2013/04/17)
  4. -
  5. Improved: Logstream (and deallog) now respect std::flush in addition to std::endl -to write out content to the console/file. -
    -(Matthias Maier, 2013/04/17) -
  6. -
  7. New: Functions::FEFieldFunction can now deal with parallel::distributed::Triangulation objects.
    diff --git a/deal.II/include/deal.II/base/logstream.h b/deal.II/include/deal.II/base/logstream.h index d697ac0386..789a920a0c 100644 --- a/deal.II/include/deal.II/base/logstream.h +++ b/deal.II/include/deal.II/base/logstream.h @@ -63,6 +63,24 @@ DEAL_II_NAMESPACE_OPEN * encountered, which will trigger a writeout to the console and, if set * up, the log file. * + *

    LogStream and thread safety

    + * + * In the vicinity of concurrent threads, LogStream behaves in the + * following manner: + *
      + *
    • Every write to a Logstream with operator << (or with + * one of the special member functions) is buffered in a thread-local + * storage. + *
    • An std::flush or std::endl 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. + *
    • 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. + *
    + * *

    LogStream and reproducible regression test output

    * * 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 &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 prefixes; + mutable Threads::ThreadLocalStorage > 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 diff --git a/deal.II/include/deal.II/base/thread_local_storage.h b/deal.II/include/deal.II/base/thread_local_storage.h index 078266824a..a97219c44d 100644 --- a/deal.II/include/deal.II/base/thread_local_storage.h +++ b/deal.II/include/deal.II/base/thread_local_storage.h @@ -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 &operator = (const T &t); + /** + * Returns a reference to the internal implementation. + */ +#ifdef DEAL_II_WITH_THREADS + tbb::enumerable_thread_specific & +#else + T & +#endif + get_implementation() + { + return data; + } + private: #ifdef DEAL_II_WITH_THREADS /** @@ -171,6 +190,19 @@ namespace Threads } + template + inline + T & + ThreadLocalStorage::get (bool &exists) + { +#ifdef DEAL_II_WITH_THREADS + return data.local(exists); +#else + exists = true; + return data; +#endif + } + template inline diff --git a/deal.II/source/base/logstream.cc b/deal.II/source/base/logstream.cc index bdd3bfe782..c5af34880b 100644 --- a/deal.II/source/base/logstream.cc +++ b/deal.II/source/base/logstream.cc @@ -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 & +LogStream::get_prefixes() const +{ +#ifdef DEAL_II_WITH_THREADS + bool exists = false; + std::stack &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 > &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 >::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 tmp = prefixes; - while (tmp.empty() == false) - { - mem += MemoryConsumption::memory_consumption (tmp.top()); - tmp.pop (); - } +// std::stack tmp = prefixes; +// while (tmp.empty() == false) +// { +// mem += MemoryConsumption::memory_consumption (tmp.top()); +// tmp.pop (); +// } return mem; } -- 2.39.5