[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?
- Re: [lmi] Parallel blues,
Greg Chicares <=