<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.
(Wolfgang Bangerth, Jö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>
* 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><<</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
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
}
-
/**
- * The standard log object of DEAL.
+ * The standard log object of deal.II:
*
* @author Guido Kanschat, 1999
*/
}
-
DEAL_II_NAMESPACE_CLOSE
#endif
}
+// 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
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
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();
}
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()
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)
{
*std_out << head << ':';
}
- if (file && (prefixes.size() <= file_depth))
+ if (file && (get_prefixes().size() <= file_depth))
{
if (print_utime)
{
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;
}