aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2017-03-23 22:42:48 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2017-03-24 23:17:13 +0300
commit4f1dd8760ad4442d24cc7e67092a9ac50a36ccd0 (patch)
treeaeb249281a0d25d9b963e10e19e808ae4f535a8f
parent615d333787c1d8dc08df5e30c60ec20600a74b85 (diff)
Fix diagnostics interleaving characters
-rw-r--r--butl/diagnostics15
-rw-r--r--butl/diagnostics.cxx24
2 files changed, 37 insertions, 2 deletions
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 <typename> struct diag_prologue;
template <typename> 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 <butl/diagnostics>
+#include <mutex>
#include <iostream> // 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;