From 4f1dd8760ad4442d24cc7e67092a9ac50a36ccd0 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Thu, 23 Mar 2017 22:42:48 +0300 Subject: Fix diagnostics interleaving characters --- butl/diagnostics | 15 ++++++++++++++- butl/diagnostics.cxx | 24 +++++++++++++++++++++++- 2 files changed, 37 insertions(+), 2 deletions(-) (limited to 'butl') diff --git a/butl/diagnostics b/butl/diagnostics index 56219ce..0c22337 100644 --- a/butl/diagnostics +++ b/butl/diagnostics @@ -21,10 +21,23 @@ namespace butl // // Diagnostics destination stream (std::cerr by default). Note that its - // modification is not MT-safe. + // modification is not MT-safe. Also note that concurrent writing to the + // stream from multiple threads can result in interleaved characters. To + // prevent this an object of diag_lock type (see below) must be created prior + // to write operation. // LIBBUTL_EXPORT extern std::ostream* diag_stream; + // Acquire the diagnostics exclusive access mutex in ctor, release in dtor. + // An object of the type must be created prior to writing to diag_stream (see + // above). + // + struct LIBBUTL_EXPORT diag_lock + { + diag_lock (); + ~diag_lock (); + }; + struct diag_record; template struct diag_prologue; template struct diag_mark; diff --git a/butl/diagnostics.cxx b/butl/diagnostics.cxx index 7dc8bd0..b692777 100644 --- a/butl/diagnostics.cxx +++ b/butl/diagnostics.cxx @@ -4,6 +4,7 @@ #include +#include #include // cerr using namespace std; @@ -12,6 +13,18 @@ namespace butl { ostream* diag_stream = &cerr; + static mutex diag_mutex; + + diag_lock::diag_lock () + { + diag_mutex.lock (); + } + + diag_lock::~diag_lock () + { + diag_mutex.unlock (); + } + void diag_record:: flush () const { @@ -20,7 +33,16 @@ namespace butl if (epilogue_ == nullptr) { os.put ('\n'); - *diag_stream << os.str (); + + { + diag_lock l; + *diag_stream << os.str (); + } + + // We can endup flushing the result of several writes. The last one may + // possibly be incomplete, but that's not a problem as it will also be + // followed by the flush() call. + // diag_stream->flush (); empty_ = true; -- cgit v1.1