The program below has different behaviors with different option levels. When I compile it with -O3
, it will never terminate. when I compile it with -O0
, it will always terminate very soon.
#include <stdio.h>
#include <pthread.h>
void *f(void *v) {
int *i = (int *)v;
*i = 0;
printf("set to 0!\n");
return NULL;
}
int main() {
const int c = 1;
int i = 0;
pthread_t thread;
void *ptr = (void *)&c;
while (c) {
i++;
if (i == 1000) {
pthread_create(&thread, NULL, &f, ptr);
}
}
printf("done\n");
}
This is the result of running it with different optimization flags.
username@hostname:/src$ gcc -O0 main.c -o main
username@hostname:/src$ ./main
done
set to 0!
set to 0!
username@hostname:/src$ gcc -O3 main.c -o main
username@hostname:/src$ ./main
set to 0!
set to 0!
set to 0!
set to 0!
set to 0!
set to 0!
^C
username@hostname:/src$
The answer given by the professor's slide is like this:
Will it always terminate?
Depends of gcc options
With –O3 (all optimisations): no
Why?
- The variable
c
is likely to stay local in a register, hence it will not be shared.
Solution « volatile
»
Thank you for your replies. I now realize that volatile
is a keyword in C
. The description of the volatile
keyword:
A
volatile
specifier is a hint to a compiler that an object may change its values in ways not specified by the language so that aggressive optimizations must be avoided.
According to my understanding, there is a shared register that stores the c
value when we use -O3
flag. So the main thread and sub-thread will share it. In this case, if a sub-thread modifies c
to 0
, the main thread will get 0 when it wants to read c
to compare in the while(c)
statement. Then, the loop stops.
There is no register storing c
that can be shared by the main thread and sub-threads when we use -O0
flag. Though the c
is modified by a sub-thread, this change may not be written to memory and just be stored in a register, or it is written to memory while the main thread just uses the old value which is read and saved in a register. As a result, the loop is infinite.
If I declared the c
value with const
: const volatile int c = 1;
, the program will terminate finally even if we compiled it with -O3
. I guess all threads will read c
from the main memory and write back to the main memory if they change the c
value.
I know, according to the specifications or rules about C language, we are not allowed to modify a value that is declared by the const
keyword. But I don't understand what is un behavior.
I wrote a test program:
#include "stdio.h"
int main() {
const int c = 1;
int *i = &c;
*i = 2;
printf("c is : %d\n", c);
}
output
username@hostname:/src$ gcc test.c -o test
test.c: In function ‘main’:
test.c:9:14: warning: initialization discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
9 | int *i = &c;
| ^
username@hostname:/src$ ./test
c is : 2
username@hostname:/src$
The result is 2
which means a variable declared with the const
can be modified but this behavior is not suggested, right?
I also tried changing the judgment condition. If it is changed to while (1){
from while(c){
, the loop will be an infinite one no matter using -O0
or -O3
This program is not a good one as it violates the specifications or rules of C language. Actually it comes from the lecture about software security.
Can I just understand like this? All threads share the same register storing c
when we compile the program with -O0
.
While the value c
is in un-shared registers, so main thread is not informed when sub-threads modify value c
when we use -O3
. Or, while(c){
is replaced by while(1){
when we use -O3
so the loop is infinite.
I know this question can be solved easily if I check the generated assembly code. But I am not good at it.