Exercise

Enhance your understanding and skills by attempting this exercise on the usage of thread API.

We'll cover the following

Simulator

In this section, we’ll write some simple multi-threaded programs and use a specific tool, called helgrind, to find problems in these programs.

Press run in the code widget below, you will get a terminal. Based on the questions given below, run the commands and make observations.

#include <stdio.h>

#include "mythreads.h"

int balance = 10;

void* worker(void* arg) {
    balance++; // unprotected access 
    return NULL;
}

int main(int argc, char *argv[]) {
    pthread_t p;
    Pthread_create(&p, NULL, worker, NULL);
    balance++; // unprotected access
    Pthread_join(p, NULL);
    return 0;
}

Questions

  1. First, build main-race.c by running make main-race. Examine the code so you can see the (hopefully obvious) data race in the code. Now run helgrind (by typing valgrind --tool=helgrind ./main-race) to see how it reports the race. Does it point to the right lines of code? What other information does it give to you?

  2. What happens when you remove one of the offending lines of code? Now add a lock around one of the updates to the shared variable, and then around both. What does helgrind report in each of these cases?

  3. Now let’s look at main-deadlock.c. Examine the code. This code has a problem known as deadlock (which we discuss in much more depth in a forthcoming chapter). Can you see what problem it might have?

  4. Now run helgrind on this code. What does helgrind report?

  5. Now run helgrind on main-deadlock-global.c. Examine the code; does it have the same problem that main-deadlock.c has? Should helgrind be reporting the same error? What does this tell you about tools like helgrind?

  6. Let’s next look at main-signal.c. This code uses a variable (done) to signal that the child is done and that the parent can now continue. Why is this code inefficient? (what does the parent end up spending its time doing, particularly if the child thread takes a long time to complete?)

  7. Now run helgrind on this program. What does it report? Is the code correct?

  8. Now, look at a slightly modified version of the code, which is found in main-signal-cv.c. This version uses a condition variable to do the signaling (and associated lock). Why is this code preferred to the previous version? Is it correctness, or performance, or both?

  9. Once again run helgrind on main-signal-cv. Does it report any errors?

Get hands-on with 1400+ tech skills courses.