Setting a global variable into a thread

Hi I have a global variable (my_var) initialized to zero and from the main thread another thread is created which only sets the global variable to one. After creating the secondary thread, the main thread waits for the global variable to be set to one like this:

while (0 == my_var);

Even if the global variable is set in the secondary thread, the main thread remains blocked forever in the while loop. If I use instead:

while (0 == my_var) { sleep(1); }

the main thread detects that the global variable has been set. Could someone explain what happens in the first case and why the global variable is not detected when it is set ?

Reply to
Bogdan
Loading thread data ...

int local_var;

while (0 == local_var) { sleep(1);

rw_block(my_var);

local_var = my_var;

rw_unblock(my_var);

}
Reply to
Pavel Vasilyev

Ooops

int local_var = 0;

while (0 == local_var) { sleep(1);

write_block(my_var); local_var = my_var; write_unblock(my_var); }

The same is true in other places where you use this variable.

Reply to
Pavel Vasilyev

Did you remember to declare the variable 'volatile'?

--
Tauno Voipio
tauno voipio (at) iki fi
 Click to see the full signature
Reply to
Tauno Voipio

the

Indeed, if the global variable is declared volatile, the main thread detects when the global variable is set without using sleep in the while loop. It seems that the compiler optimizations mess something. Thank you for your help.

Reply to
Bogdan

What's in your "write_block" and "write_unblock" functions/macros?

And how is this better than making my_var volatile?

There are times when rtos-style locking and synchronisation tools are important, and times when they are way over-kill. If he wanted an rtos solution, my_var would be a semaphore.

Reply to
David Brown

You misunderstand - the compiler did not "mess something up". /You/ messed up. This is exactly the sort of thing volatile is used for, and you need to read about and understand "volatile" before writing another line of your multi-threaded code - otherwise you will have a lot more problems down the line.

Reply to
David Brown

Just example.

Not work.

#include #include #include #include #include #include

sem_t a, b; int global = 10;

void *one(void *arg __attribute__((unused))) {

int local;

do { sem_wait(&b); local = global--; printf("A = %d\n", local); sem_post(&a); } while (local > 0);

pthread_exit(NULL); }

void *two(void *arg __attribute__((unused))) {

int local;

do { sem_wait(&a); local = global--; printf("B = %d\n",local); sem_post(&b); } while (local > 0);

pthread_exit(NULL); }

int main(void) {

pthread_t pth[2]; pthread_attr_t pattr;

pthread_attr_init(&pattr); pthread_attr_setdetachstate(&pattr, PTHREAD_CREATE_DETACHED);

sem_init(&a, 0, 0); sem_init(&b, 0, 1);

pthread_create(&pth[0], &pattr, &one, NULL); pthread_create(&pth[1], &pattr, &two, NULL);

pthread_detach(pth[0]); pthread_detach(pth[1]);

sem_destroy (&a); sem_destroy (&b);

return 0; }

Reply to
Pavel Vasilyev

Or better;

#include #include #include #include #include #include

sem_t a, b; int global = 10;

void *one(void *arg __attribute__((unused))) {

int local;

do { sem_wait(&b); local = global--; printf("A = %d\n", local); sem_post(&a); } while (local >= 0);

pthread_exit(NULL); }

void *two(void *arg __attribute__((unused))) {

int local;

do { sem_wait(&a); local = global--; printf("B = %d\n",local); sem_post(&b); } while (local >= 0);

pthread_exit(NULL); }

int main(void) {

pthread_t pth[2]; pthread_attr_t pattr;

pthread_attr_init(&pattr); pthread_attr_setdetachstate(&pattr, PTHREAD_CREATE_JOINABLE);

sem_init(&a, 0, 0); sem_init(&b, 0, 1);

pthread_create(&pth[0], &pattr, &one, NULL); pthread_create(&pth[1], &pattr, &two, NULL);

pthread_join(pth[0], (void **)&one); pthread_join(pth[0], (void **)&two);

sem_destroy (&a); sem_destroy (&b);

return 0; }

Somehow, the extra cycles, can not find where. :(

Reply to
Pavel Vasilyev

The truth is that if he wants to synchronize the threads this way he should use proper pthreads API (cond vars, sems, spin locks etc. depending on the requirements). If this var has some "other purpose", then the right way is to make it volatile AND use the atomic incrementing/ API that gcc provides (e.g. __sync_fetch_and_add)

Asking such questions without defining the actual purpose of the code always leads to flooding with almost no usable info :(

Reply to
sasho popov

My experience with this sort of thing is mostly with small microcontrollers, rather than Linux - most of my Linux programming is in Python rather than C, which handles this sort of thing differently.

Can you tell me /why/ this would not work:

static volatile int my_var;

void threadOne(void) { while (0 == my_var) ; doSomething(); }

void threadTwo(void) { doSomething(); my_var = 1; doSomething(); }

Obviously the first thread will busy-wait in the while loop, which is seldom a good idea. But as long as priorities and/or SMP allow threadTwo to run, it will work.

mvh.,

David

Reply to
David Brown

from the

Well, I have checked that if my program is compiled with optimizations turn off (-O0) the global variable change is detected without needing to be declared volatile, while with optimizations turned on (-O3) the global variable change is not detected. Here is the program I have used for these tests:

char test =3D 0;//should be declared volatile

void* thread_starter(void *data) { test =3D 1; syslog(LOG_INFO, "test is set"); return NULL; }

int main() { pthread_t tid; void *ret_val =3D NULL;

if (0 !=3D pthread_create(&tid, NULL, thread_starter, NULL)) { perror("pthread_create"); return -1; }

while (0 =3D=3D test);

if (0 !=3D pthread_join(tid, &ret_val)) { perror("pthread_join"); return -1; } return 0; }

I am aware about synchronization mechanisms for threads, but this simple situation doesn't need in my opinion such mechanisms.

Reply to
Bogdan

If your code works when not optimised, but fails when optimised, then your code is broken.

Re-read that last sentence, and think about it.

A compiler's job is to generate object code that will run according to your source code. Normally it will try to generate the best object code it can - optimisation flags are just hints as to how hard it should try. Higher optimisation settings do not affect the correctness of this process (assuming, of course, that you don't hit a bug in the compiler). But higher optimisation settings are less forgiving of errors in the source code.

When "test" is declared without "volatile", then the compiler can make assumptions about when "test" can change. That's the way C works. The compiler uses these assumptions to generate better code when optimising, such as only reading it once at the start of the "while" loop. If that is not the functionality you wanted, then you have to tell the compiler what you /do/ want - you want "test" to be "volatile".

(It is seldom worth using -O3 - it may even slow code down by making it bigger. Use -O2, and if you want particular extra optimisations for particular code, add the flags explicitly).

I agree here - making "test" volatile is the appropriate fix for this program.

Reply to
David Brown
[skip]

while (NULL != &test && 0 == test )

:)

Reply to
Pavel Vasilyev

"&test" cannot be null - "test" a file scope variable.

Reply to
David Brown

Very bad idea.

Use semaphores (e.g. pthread_mutex).

-Michael

Reply to
Michael Schnell

d from the

ad

n
d

nd

L))

It does. If synchronizing is what you intend to do you should do it the right way. Otherwise a "simple solution" like this can show up in a week and lead to a LOT of trouble (beside being hard to find in many cases). Also think about running this code on a different platform/ architecture. Is there a guarantee it will work? NO! Use pthreads syncs when you want to sync threads. That will save you a lot of stress in the best case and money/ reputation/both in the worst.

Reply to
sasho popov

ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here. All logos and trade names are the property of their respective owners.