Or have I misunderstood restrict?
Yeah - it makes no guarantees of what goes on outside the function where you used it. So it cannot be used for synchronization or thread-safety purposes.
The TL;DR about restrict
is that *restrict ptr
guarantees that no other pointer access to the object that ptr
points at will be done from the block where ptr
was declared. If there are other pointers or object references present ("lvalue accesses"), the compiler may assume that they aren't modifying that pointed-at restricted object.
This is pretty much just a contract between the programmer and the compiler: "Dear compiler, I promise I won't do stupid things behind your back with the object that this pointer points at". But the compiler can't really check if the programmer broke this contract, because in order to do so it may have to go check across several different translation units at once.
Example code:
#include <stdio.h>
int a=0;
int b=1;
void copy_stuff (int* restrict pa, int* restrict pb)
{
*pa = *pb; // assign the value 1 to a
a=2; // undefined behavior, the programmer broke the restrict contract
printf("%d\n", *pa);
}
int main()
{
copy_stuff(&a, &b);
printf("%d\n", a);
}
This prints 1
then 2
with optimization on, on all mainstream x86 compilers. Because in the printf inside the function they are free to assume that *pa
has not been modified since *pa = *pb;
. Drop restrict
and they will print 2
then 2
, because the compiler will have to add an extra read instruction in the machine code.
So for restrict
to make sense, it needs to be compared with another reference to an object. That's not the case in your example, so restrict
fills no apparent purpose.
As for _Atomic
, it will always have to guarantee that the machine instructions in themselves are atomic, regardless of what else goes on in the program. It's not some semi-high level "nobody else seems to be using this variable anyway". But for the keyword to make sense for multi-threading and multi-core both, it would also have to act as a low level memory barrier to prevent pipelining, instruction re-ordering and data caching - which if I remember correctly is exactly what x86 lock
does.