I have a program which nearly immediately finishes with -O0
on gcc, but hangs forever with gcc and -O3
. It also exits immediately if I remove the [[gnu::pure]]
function attribute, even though the function does not modify global state. The program is in three files:
thread.hpp
#include <atomic>
extern ::std::atomic<bool> stopthread;
extern void threadloop();
[[gnu::pure]] extern int get_value_plus(int x);
thread.cpp
#include <thread>
#include <atomic>
#include "thread.hpp"
namespace {
::std::atomic<int> val;
}
::std::atomic<bool> stopthread;
void threadloop()
{
while (!stopthread.load())
{
++val;
}
}
[[gnu::pure]] int get_value_plus(int x)
{
return val.load() + x;
}
main.cpp
#include <thread>
#include "thread.hpp"
int main()
{
stopthread.store(false);
::std::thread loop(threadloop);
while ((get_value_plus(5) + get_value_plus(5)) % 2 == 0)
;
stopthread.store(true);
loop.join();
return 0;
}
Is this a compiler bug? A lack of documentation for the proper caveats to using [[gnu::pure]]
? A misreading of the documentation for [[gnu::pure]]
such that I've coded a bug?
As it turns out, I misread the documentation. From the online documentation about the
pure
attribute in gcc:and a different paragraph:
These two paragraphs make it clear that I've been lying to the compiler, and the function I wrote does not qualify as being 'pure' because it depends on a variable that might change at any time.
The reason I asked this question is because the answers to this question: __attribute__((const)) vs __attribute__((pure)) in GNU C didn't address this problem at all (at the time I asked my question anyway). And a recent C++ Weekly episode had a comment asking about threads and pure functions. So it's clear there's some confusion out there.
So the criteria for a function that qualifies for this marker is that it must not modify global state, though it is allowed to read it. But, if it does read global state, it is not allowed to read any global state that could be considered 'volatile', and this is best understood as state that might change between two immediately successive calls to the function, i.e. if the state it's reading can change in a situation like this: