2

I have to use two threads; one to do various operations on matrices, and the other to monitor virtual memory at various points in the matrix operation process. This method is required to use a global state variable 'flag'.

So far I have the following (leaving some out for brevity):

int flag = 0;

int allocate_matrices(int dimension)
{
    while (flag == 0) {} //busy wait while main prints memory state

    int *matrix = (int *) malloc(sizeof(int)*dimension*dimension);
    int *matrix2 = (int *) malloc(sizeof(int)*dimension*dimension);

    flag = 0;
    while (flag == 0) {} //busy wait while main prints memory state

    // more similar actions...
}

int memory_stats()
{
    while (flag == 0)
    { system("top"); flag = 1; }
}

int main()
{ //threads are created and joined for these two functions }

As you might expect, the system("top") call happens once, the the matrices are allocated, then the program falls into an infinite loop. It seems apparent to me that this is because the thread assigned to the memory_stats function has already completed its duty, so flag will never be updated again.

Is there an elegant way around this? I know I have to print memory stats four times, so it occurs to me that I could write four while loops in the memory_stats function with busy waiting contingent on the global flag in between each of them, but that seems clunky to me. Any help or pointers would be appreciated.

aquemini
  • 950
  • 2
  • 13
  • 32
  • Not use threads? Because in this case it doesn't make any sense. Or use mutex, or atomic primitives. –  Apr 09 '13 at 19:25
  • You should be using Pthreads for this. https://computing.llnl.gov/tutorials/pthreads/ – Robert Harvey Apr 09 '13 at 19:26
  • @VladLazarenko You're right, it doesn't make sense. I realize that all of this could easily be done sequentially and would do it that way if I had the choice. If I used a mutex, which occurred to me as well, would the theory be any different? That is, would I have a more elegant solution than writing four loops with intermittent busy waiting? – aquemini Apr 09 '13 at 19:27
  • @RobertHarvey I am using pthreads. In the main function, pthreads are created and joined. – aquemini Apr 09 '13 at 19:28

4 Answers4

6

You can lock it with mutex. I assume you use pthread.

pthread_mutex_t mutex;

pthread_mutex_lock(&mutex);
flag=1;
pthread_mutex_unlock (&mutex);

Here is a very good tutorial about pthreads, mutexes and other stuff: https://computing.llnl.gov/tutorials/pthreads/

cgon
  • 1,955
  • 2
  • 23
  • 37
  • 1
    Keep in mind that C11 actually has a optional [thread support library](http://en.cppreference.com/w/c/thread). – Zeta Apr 09 '13 at 19:34
  • I assume I would use the mutex in `memory_stats()`, outside the `while` loop. Does that allow the function to be executed again by the thread? – aquemini Apr 09 '13 at 19:37
  • You can lock it outside while loop but why would you do that? It seems you just read some stuff at the beginning of the while loop. The critical region is reached when you are setting flag ? – cgon Apr 09 '13 at 19:44
  • @Zeta, C11 also has `atomic_flag` which could be use to implement exactly the scheme from the question – Jens Gustedt Apr 09 '13 at 20:50
6

One of the possible reasons for the hang is that flag is a regular variable and the compiler sees that it's never set to a non-zero value between flag = 0; and while (flag == 0) {} or in this while inside allocate_matrices(). And so it "thinks" the variable stays 0 and the loop becomes infinite. The compiler is entirely oblivious to your threads.

You could define flag as volatile to prevent the above from happening, but you'll likely run into other issues after adding volatile. For one thing, volatile does not guarantee atomicity of variable modifications.

Another issue is that if the compiler sees an infinite loop that has no side effects, it may be considered undefined behavior and anything could happen, or, at least, not what you're thinking should, also this.

You need to use proper synchronization primitives like mutexes.

Community
  • 1
  • 1
Alexey Frunze
  • 61,140
  • 12
  • 83
  • 180
0

Your problem could be solved with a C compiler that follows the latest C standard, C11. C11 has threads and a data type called atomic_flag, that can basically used for a spin lock as you have it in your question.

Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177
-1

First of all, the variable flag needs to be declared volatile or else the compiler has license to omit reads to it after the first one.

With that out of the way, a sequencer/event_counter can be used: one thread may increment the variable when it's odd, the other when it's even. Since one thread always "owns" the variable, and transfers the ownership with the increment, there is no race condition.

Doug Currie
  • 40,708
  • 1
  • 95
  • 119
  • 1
    `volatile` in C is not sufficient to guarantee atomic access so the solution you propose would still have a race condition. – Gabriel Southern Apr 09 '13 at 19:52
  • Hardware assures atomic access if the value is small enough, e.g., 32 bits. There is no race if only one of the two threads can write the value in any state. – Doug Currie Apr 09 '13 at 22:16
  • 1
    `Hardware assures atomic access if the value is small enough` - That sounds like an platform-dependent behavior. Not great if you want your code to be platform independent. – Robert Harvey Apr 10 '13 at 00:07
  • 2
    Do you know any machine where a one byte access is not atomic? The algorithm I proposed only needs one bit. – Doug Currie Apr 10 '13 at 00:11