Wednesday, August 12, 2020

Digest for comp.lang.c++@googlegroups.com - 13 updates in 4 topics

woodbrian77@gmail.com: Aug 11 06:27PM -0700

Shalom
 
What do you think is the worst thing about 2020 C++?
I'd say it's the lack of static exceptions. Are Clang
or Gcc making any progress with that in spite of 2020
C++?
 
 
Brian
Ebenezer Enterprises - Enjoying programming again.
https://github.com/Ebenezer-group/onwards
Sam <sam@email-scan.com>: Aug 12 06:28PM -0400

> I'd say it's the lack of static exceptions. Are Clang
> or Gcc making any progress with that in spite of 2020
> C++?
 
Co-routines is the undisputed winner, hands down. A solution in search of a
problem.
 
If I was on the committee and ate some mushrooms, this is what I would come
up with.
Frederick Gotham <cauldwell.thomas@gmail.com>: Aug 12 03:09AM -0700

Before I begin, I have deliberately left out necessary code in order to make this as simplistic as possible (for example I've neglected to join all std::thread objects before destroying them).
 
I have the following global constant, along with an atomic counter variable:
 
unsigned constexpr g_N_threads = 6u;
 
std::atomic<unsigned> g_counter{0};
 
And then I have the following global array to store the thread ID's:
 
std::array< std::atomic<std::thread::id>, g_N_threads > g_ids_for_threads{};
 
In my main thread, I have this code in 'main':
 
int main(void)
{
array<std::thread,g_N_threads> threads; /* Create std::thread objects without starting threads yet */
 
for ( unsigned i = 0; i != g_N_threads; ++i )
{
threads[i] = std::thread(Thread_Entry_Point);
}

//Give threads a chance to start
std::this_thread::sleep_for(std::chrono::seconds(2));
 
//Right now, g_counter is still 0
for ( unsigned i = 0; i != g_N_threads; ++i )
{
assert( std::thread::id{} != threads[i].get_id() );

g_ids_for_threads[i] = threads[i].get_id();
}
 
++g_counter;
}
 
And then for each of the 6 threads, I first have this helper function:
 
unsigned Get_Thread_Index_From_StdID(std::thread::id const arg_id)
{
assert( std::thread::id{} != arg_id );
 
for (unsigned i = 0; i != g_N_threads; ++i)
{
if ( arg_id == g_ids_for_threads[i] )
return i;
}
 
assert( nullptr == "Control should never reach here!" );
}
 
And here's the entry point to the thread:
 
void Thread_Entry_Point(void)
{
unsigned counter = 0;
 
while ( counter >= g_counter ); //spin until g_counter increments
 
static thread_local SomeType some_object{
Get_Thread_Index_From_StdID( std::this_thread::get_id() )
};
 
for (; /* ever */ ;)
{
/* Do Something */
}
}
 
When I build and run this, the error I'm getting is as follows:
 
gotham: gotham.cpp:147: unsigned int Get_Thread_Index_From_StdID(std::thread::id): Assertion `nullptr == "Control should never reach here!"' failed.
 
This means that there's a problem with how I'm retrieving and storing the thread ID's.
 
Can anyone see what's wrong here?
Paavo Helde <eesnimi@osa.pri.ee>: Aug 12 01:54PM +0300

12.08.2020 13:09 Frederick Gotham kirjutas:
> Before I begin, I have deliberately left out necessary code in order to make this as simplistic as possible (for example I've neglected to join all std::thread objects before destroying
them).
 
[random uncompilable code snippets clipped]
 
 
> gotham: gotham.cpp:147: unsigned int Get_Thread_Index_From_StdID(std::thread::id): Assertion `nullptr == "Control should never reach here!"' failed.
 
> This means that there's a problem with how I'm retrieving and storing the thread ID's.
 
> Can anyone see what's wrong here?
 
There are lots of things wrong with your code (hint: random sleeping and
spin-waiting are pretty bad ideas), but the most obvious is that you
have not provided a compilable and runnable example which would
demonstrate the problem.
 
I copy-pasted the pieces in some order which compiled, fixed some syntax
errors, added needed includes and missing thread joining. The final
program worked without problems with two different compilers. I suspect
the problem is in your code which you have not shown or have altered
while posting.
Ben Bacarisse <ben.usenet@bsb.me.uk>: Aug 12 12:02PM +0100

> }
> }
 
> When I build and run this, the error I'm getting is as follows:
 
It would be helpful if you posted something we could build and run.
When I make minimal additions to make it a whole program, I don't get
the behaviour that you see:
 
 
> This means that there's a problem with how I'm retrieving and storing
> the thread ID's.
 
> Can anyone see what's wrong here?
 
I haven't looked at the program because it's often hard (and sometimes
pointless) to debug code when you can't re-recreate the problem.
 
--
Ben.
Frederick Gotham <cauldwell.thomas@gmail.com>: Aug 12 04:33AM -0700

On Wednesday, August 12, 2020 at 11:54:54 AM UTC+1, Paavo Helde wrote:
 
> program worked without problems with two different compilers. I suspect
> the problem is in your code which you have not shown or have altered
> while posting.
 
 
I've put it all together as compilable code:
 
#include <cassert>
#include <array>
#include <atomic>
#include <thread>
#include <iostream>
 
using namespace std;
 
unsigned constexpr g_N_threads = 6u;
 
std::atomic<unsigned> g_counter{0};
 
std::array< std::atomic<std::thread::id>, g_N_threads > g_ids_for_threads{};
 
unsigned Get_Thread_Index_From_StdID(std::thread::id const arg_id)
{
assert( std::thread::id{} != arg_id );
 
for (unsigned i = 0; i != g_N_threads; ++i)
{
if ( arg_id == g_ids_for_threads[i] )
return i;
}
 
assert( nullptr == "Control should never reach here!" );
}
 
void Thread_Entry_Point(void)
{
struct SomeType { unsigned i; SomeType(unsigned arg) : i(arg) {} };
 
unsigned counter = 0;
 
while ( counter >= g_counter ); //spin until g_counter increments
 
static thread_local SomeType some_object{
Get_Thread_Index_From_StdID( std::this_thread::get_id() )
};
 
for (; /* ever */ ;)
{
/* Do Something */
}
}
 
int main(void)
{
array<std::thread,g_N_threads> threads; /* Create std::thread objects without starting threads yet */
 
for ( unsigned i = 0; i != g_N_threads; ++i )
{
threads[i] = std::thread(Thread_Entry_Point);
}
 
//Give threads a chance to start
std::this_thread::sleep_for(std::chrono::seconds(2));
 
//Right now, g_counter is still 0
for ( unsigned i = 0; i != g_N_threads; ++i )
{
assert( std::thread::id{} != threads[i].get_id() );
 
g_ids_for_threads[i] = threads[i].get_id();
}
 
++g_counter;

cout << "Last line of main\n"; // expect error about unjoined threads after this
}
 
 
And unfortunately it works fine.
 
I therefore don't know where the problem is in my code, I'll have to look more closely.
Frederick Gotham <cauldwell.thomas@gmail.com>: Aug 12 04:39AM -0700

On Wednesday, August 12, 2020 at 12:33:23 PM UTC+1, Frederick Gotham wrote:
 
> I therefore don't know where the problem is in my code, I'll have to look more closely.
 
 
I found out the problem by doing this:
 
std::thread::id g_main_thread_id;
 
int main() {
g_main_thread_id = std::this_thread::get_id();
/* */
}
 
It turns out that the main thread was calling a function which I didn't think it was calling (so the ID of the main thread didn't exist in the array of thread ID's).
James Kuyper <jameskuyper@alumni.caltech.edu>: Aug 12 10:15AM -0400

On 8/12/20 6:54 AM, Paavo Helde wrote:
> 12.08.2020 13:09 Frederick Gotham kirjutas:
>> Before I begin, I have deliberately left out necessary code in order to make this as simplistic as possible (for example I've neglected to join all std::thread objects before destroying
> them).
...
> program worked without problems with two different compilers. I suspect
> the problem is in your code which you have not shown or have altered
> while posting.
 
It seems paradoxical, but empirically, people who post only the part of
their code that they think is relevant to the problem are very likely to
have removed the part that caused the problem.
 
If you can't post the entire program where you ran into your problem,
either because it's too big and complicated, or proprietary or
classified, what you should do is put together simplified version (with
any proprietary or classified features removed) that is complete and
compilable.
Most importantly, before posting your simplified version - confirm that
it still demonstrates the problem.
 
Sometimes it seems impossible to put together such a simplified version,
but there's a straightforward approach to systematically simplifying a
program that demonstrates a problem into a much simpler program that
still demonstrates it:
 
You'll need to have two copies of your code, WORK, which you do your
work on, and FAIL, which has been confirmed to fail. Initialize both of
them by copying the original program.
 
1. Identify a portion of WORK that should be removable without
interfering with the failure, preferably as large as possible. A good
starting candidate is all code that should have executed after the code
which produced the first symptom that something had gone wrong, but
toward the end of the process you might be stuck with removing only a
single line of code. If you can't find anything more to remove, you're
done - you should post the entire code of FAIL along with your question.
2. Remove that portion from WORK.
3. MOST IMPORTANT: check that WORK still demonstrates the problem. If it
does, replace FAIL with a copy of WORK. If not, think very carefully
about the part you just removed. You thought that it wouldn't remove the
problem, but it did - that's a very important clue. Replace WORK with a
copy of FAIL, and go back to step 1 - but make sure to remove a
different part of the program the next time, generally a smaller one.
 
It's quite common for people following this procedure to end up finding
the problem by themselves, because of the clues they accumulate from
changes that fixed the problem.
 
Once you're ready to post your simplified example, there's a few
additional facts you need to provide with your question:
 
1. Identify the precise tool chain you used to create the code,
including which options you chose. It's often the case that people will
be unable to duplicate your problem if they use a different tool chain
or make a different choices for one or more of the options.
 
2. Identify which result you expected to get. That might seem obvious to
you, but I've frequently seen people post code that contained no obvious
defect, and which produced exactly the results that I would expect it to
produce - the problem was that the person who wrote it was expecting
different results.
 
3. Identify the actual results you got. If at all possible, they should
be cut-and-pasted from the actual output. You don't want us to waste
time trying to figure out a typo that was introduced by you when you
retyped the output.
 
Warning: this process is not guaranteed to work. The worst problem I
ever used this approach on, I was only able to remove 52% of the code,
everything else I tried to remove made the problem disappear (and it was
unacceptable to deliver the code with that part removed). I was unable
to remove dependencies on three different third-party libraries. I sent
the simplified version to the help desk for the library that seemed most
relevant, and worked together with them for three months trying to track
down the problem - without success. The program still periodically fails
due to that problem.
"Öö Tiib" <ootiib@hot.ee>: Aug 12 10:52AM -0700

On Wednesday, 12 August 2020 14:39:38 UTC+3, Frederick Gotham wrote:
> /* */
> }
 
> It turns out that the main thread was calling a function which I didn't think it was calling (so the ID of the main thread didn't exist in the array of thread ID's).
 
Yes, usually by making code that really compiles and runs (and
demonstrates the problem that they think there is) people find
out what they really did wrongly.
 
But when they post some kind of pseudo-code without ever
trying it then it is as rule about something that does not
demonstrate the issue.
 
There was very similar post only recently:
<https://groups.google.com/d/msg/comp.lang.c++/Y1J8W4q4Vo8/VLUBXiR3BAAJ>
"Chris M. Thomasson" <chris.m.thomasson.1@gmail.com>: Aug 12 01:15PM -0700

On 8/12/2020 4:33 AM, Frederick Gotham wrote:
> struct SomeType { unsigned i; SomeType(unsigned arg) : i(arg) {} };
 
> unsigned counter = 0;
 
> while ( counter >= g_counter ); //spin until g_counter increments
 
Strange!
 
 
> }
 
> //Give threads a chance to start
> std::this_thread::sleep_for(std::chrono::seconds(2));
 
This is very strange to me.
 
 
 
> }
 
> ++g_counter;
 
> cout << "Last line of main\n"; // expect error about unjoined threads after this
 
You need to join the threads.
 
 
 
Also, why not pass in a thread id that you created before you even
create a thread?
 
 
// pseudo code
void thread_entry(unsigned long id)
{
//[...]
}
 
 
Why are you trying to extract their ids after they are created?
 
 
 
 
Lynn McGuire <lynnmcguire5@gmail.com>: Aug 12 01:54PM -0500

"Why the C Language Will Never Stop You from Making Mistakes" by
JeanHeyd Meneide

https://thephd.github.io/your-c-compiler-and-standard-library-will-not-help-you
 
"Short answer: because we said so."
 
Yup.
 
"The C Language"
 
"Or, as I like to call it, "The Type Safety Is For Losers Language". Of
course, that's a mouthful, so "C" will have to do. You may be wondering
why I would call a language like C as having no type safety."
 
Cool, them are fighting words !
 
Oh wait, he is correct.
 
Lynn
"Chris M. Thomasson" <chris.m.thomasson.1@gmail.com>: Aug 11 04:27PM -0700

On 8/11/2020 1:07 PM, Chris M. Thomasson wrote:
> Relacy and  Address Sanitizer. We had the best conversations way back on
> comp.programming.threads. Back when it was not a spam infested
> nightmare. Thanks to animer... ramine the plague!
 
I know he is on the development team over at Google.
legalize+jeeves@mail.xmission.com (Richard): Aug 12 03:50PM

[Please do not mail me a copy of your followup]
 
Lynn McGuire <lynnmcguire5@gmail.com> spake the secret code
>Purify on Sun Workstations back in the 1990s. It dragged our software
>down horribly and made our software unusable with it on. But it helped
>immensely.
 
Purify worked by instrumenting every load and store instruction
from/to memory. I believe modern approaches use data flow analysis to
minimize the amount of instrumentation applied in order to ameliorate
the affect on performance.
--
"The Direct3D Graphics Pipeline" free book <http://tinyurl.com/d3d-pipeline>
The Terminals Wiki <http://terminals-wiki.org>
The Computer Graphics Museum <http://computergraphicsmuseum.org>
Legalize Adulthood! (my blog) <http://legalizeadulthood.wordpress.com>
You received this digest because you're subscribed to updates for this group. You can change your settings on the group membership page.
To unsubscribe from this group and stop receiving emails from it send an email to comp.lang.c+++unsubscribe@googlegroups.com.

No comments: