lmi
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [lmi] Parallel blues


From: Greg Chicares
Subject: Re: [lmi] Parallel blues
Date: Sat, 7 Aug 2021 23:18:50 +0000
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Thunderbird/78.11.0

On 2017-06-20 17:09, Vadim Zeitlin wrote:
[...parallelizing 'calendar_date_test'...> 
>  Unfortunately, it doesn't work and it took me slightly longer to find out
> why does it crash when I run it. It turns out, that test code calls
> {min,max}imum_birthdate(), which calls birthdate_limit::operator(), which
> calls decimal_root() which outputs some (debugging?) messages to the
> provided iteration_stream which is by default just null_stream(). And
> null_stream() returns a reference to a static object, which ends up being
> used from multiple threads at once -- hence the crash.
After writing commit message 100320168adb ...

| Given
|   foo(arg1, arg2, std::ostream& os=some_default())
| it's not possible to write some_default() in a way that serves up an
| already-constructed (hence, static or global) stream while avoiding the
| peril that its streambuf can be replaced (with global effect). These
| techniques don't work:
|  - Derive from std::ostream, and override rdbuf()...which isn't virtual,
|    so the std::ostream& argument doesn't know about it.
|  - Look for a virtual function that might be overridden, at least to
|    assert that the streambuf hasn't been altered...but there is none in
|    std::ostream, and overriding a streambuf function doesn't help when
|    the streambuf has been replaced.
|  - Try messing with unique_ptr...which means changing the problem to
|      foo(arg1, arg2, SomeNewCustomStreamClass& os=some_default())
|    but that's too much work.
|  - Move constructor...nope:
|      https://cplusplus.github.io/LWG/lwg-defects.html#911

...I looked though the history to make sure I hadn't missed any
known problems, and found this email thread that mentions a
known problem that I had missed.

Let me start by asking a question about this:

> null_stream() returns a reference to a static object, which ends up being
> used from multiple threads at once -- hence the crash.

Would it have crashed if std::cout had been used instead of null_stream()?
If the answer is "No", then that must be because of some magical property
that std::cout possesses, and the question becomes how to imbue this
iteration-tracing stream with similar magic. Is std::osyncstream the
C++20 voodoo that makes this work? (The goal of this email thread was to
parallelize a unit test which always uses a "null stream", so it doesn't
matter if characters written to it by different threads are interleaved,
because no such character will actually be written.)

Otherwise, I fear I've painted myself into a corner, out of which I was
hoping I could teleport without leaving a real problem behind. I suppose
this could be resolved by overloading:

- void foo(int arg0, double arg1, std::ostream& os=null_stream()) {...}

+ void foo(int arg0, double arg1, std::ostream& os) {...}
+ void foo(int arg0, double arg1)
+ {
+     std::ostream os(&null_streambuf());
+     os.setstate(std::ios::badbit);
+     foo(arg0, arg1, os);
+ }

That's only somewhat ugly. I like it better than

- void foo(int arg0, double arg1, std::ostream& os=null_stream()) {...}
+ void foo(int arg0, double arg1, std::ostream* os=nullptr)
+ {
+     if(nullptr == os)
+         {
+         std::ostream ephemeral_os(&null_streambuf());
+         ephemeral_os.setstate(std::ios::badbit);
+         os = &ephemeral_os;
+         }
+ }

What do you think?


reply via email to

[Prev in Thread] Current Thread [Next in Thread]