I'm working on an exercise (see bold text below) on semaphores and synchronization for my Operative System course. The text of the exercise is this:
Pthread semaphores and mutexes
The C program gen_binary_numbers.c receives on the command line an integer n, and uses recursion to generate and display all binary numbers of n bits.Transform the recursive program into a concurrent one, replacing the recursive procedure with the generation of an appropriate number of processes that display the binary numbers (in any order).
This is my code, actually:
#include <stdio.h>
#include <stdlib.h>
#include <semaphore.h>
#include <pthread.h>
int num, r, c;
pthread_mutex_t mutex;
void *genBin(void *arg);
int main (int argc, char **argv) {
if (argc != 2) {
fprintf(stdout, "\nUSAGE: %s <n>\n\n", argv[0]);
exit(EXIT_FAILURE);
}
int i;
num = atoi(argv[1]);
c = num;
r = 2;
for (i=1; i<num; i++) {
r=r*2;
}
pthread_mutex_init(&mutex, NULL);
pthread_t* p;
p = malloc(r*sizeof(pthread_t));
for (i=0;i<r;i++) {
if (pthread_create(&p[i], NULL, genBin, &i)) {
fprintf(stderr, "Error creating thread.\n");
exit(EXIT_FAILURE);
}
}
pthread_exit(0);
}
void *genBin (void *arg) {
int x;
int i=0;
x = *((int*)arg);
pthread_mutex_lock(&mutex);
while (i<num) {
if(x!=0) {
fprintf(stdout, "%d", x%2);
}
else {
fprintf(stdout, "0");
}
i++;
x/=2;
}
fprintf(stdout, "\n");
pthread_mutex_unlock(&mutex);
pthread_exit(0);
}
I think that the code should return the right solution, but sometimes the output doesn't return the correct number.
Example of correct output:
./genBin 3
100
101
010
110
001
011
111
000
Example of wrong output (because of duplicates):
./genBin 3
110
110
110
001
011
111
111
000
I think that the problem is in the synchronization between the mutex and the printf. Is there an alternative solution to avoid confusing results?
Your code contains a race condition. In main, you pass the address of your iteration variable, i
, as the thread function's argument. Each new thread then races with the main thread to read the value of i
(via the provided pointer) before the main thread increments it. One way you could address that problem would be to use a semaphore to make the main thread wait after creating each thread until that thread has dereferenced its argument.
Also, I don't think you need to use a mutex in genBin()
. The only shared data it accesses is stdout
, via fprintf()
, and that function operates as if it locks an exclusive lock associated with the specified stream. Moreover, with the mutex you get essentially no actual concurrency because each thread holds the mutex locked for almost the complete duration of its execution.