I'm new to C and learning about semaphores. I'm trying to implement a sort of complex dining philosophers situation using pthreads, mutex, and semaphores.
The idea is that one semaphore, representing a server, seats people at two tables (4 places each, 8 total). Each table is also controlled by a counting semaphore. Each plate is controlled by a mutex to avoid race conditions. Each person is represented by a thread.
I cannot see why in my code the same customer keeps eating, and does not seem to increment.
Code:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
sem_t server_sem;
int server_pshared;
int server_ret;
int server_count = 10;
sem_t tablea_sem;
int tablea_pshared;
int tablea_ret;
int tablea_count = 4;
sem_t tableb_sem;
int tableb_pshared;
int tableb_ret;
int tableb_count = 4;
//server_ret = serm_open("serverSem", O_CREAT | O_EXCL, 0644, server_count);
int customer_count = 10;
pthread_t customer[10];
//pthread_t plates[8]
int plate_count = 8;
pthread_mutex_t plates[8];
void *eat(void *i) {
int n = *((int *) i);
pthread_mutex_lock(&plates[n]);
printf("Customer %d is eating\n", n);
sleep(5);
pthread_mutex_unlock(&plates[n]);
printf("Customer %d is finished eating\n", n);
return (NULL);
}
int main() {
server_ret = sem_init(&server_sem, 1, server_count);
tablea_ret = sem_init(&tablea_sem, 1, tablea_count);
tableb_ret = sem_init(&tableb_sem, 1, tableb_count);
//customer = (pthread_t[10] *)malloc(sizeof(customer));
printf ("starting thread, semaphore is unlocked.\n");
int i;
int j;
int k;
for(i=0;i<plate_count;i++) {
pthread_mutex_init(&plates[i],NULL);
printf("Creating mutex for plate %d\n", i);
}
sem_wait(&server_sem);
for (j=0;j<customer_count;j++) {
//pthread_create(&customer[j],NULL,(void *)eat,&j);
if (j<4) {
sem_wait(&tablea_sem);
sem_post(&tableb_sem);
pthread_create(&customer[j],NULL,(void *)eat,&j);
printf("Creating thread for customer %d\n", j);
}
else {
sem_post(&tablea_sem);
sem_wait(&tableb_sem);
pthread_create(&customer[j],NULL,(void *)eat,&j);
printf("Creating thread for customer %d\n", j);
}
}
for(k=0;k<customer_count;k++) {
pthread_join(customer[k],NULL);
printf("Joining thread %d\n", k);
}
for(i=0;i<plate_count;i++) {
pthread_mutex_destroy(&plates[i]);
}
return 0;
}
Console output:
starting thread, semaphore is unlocked.
Creating mutex for plate 0
Creating mutex for plate 1
Creating mutex for plate 2
Creating mutex for plate 3
Creating mutex for plate 4
Creating mutex for plate 5
Creating mutex for plate 6
Creating mutex for plate 7
Creating thread for customer 0
Creating thread for customer 1
Creating thread for customer 2
Creating thread for customer 3
Creating thread for customer 4
Creating thread for customer 5
Creating thread for customer 6
Creating thread for customer 7
Creating thread for customer 8
Creating thread for customer 9
Customer 10 is eating
Customer 10 is eating
Customer 10 is eating
Customer 10 is eating
Customer 10 is eating
Customer 10 is eating
Customer 10 is eating
Customer 10 is eating
Customer 10 is eating
Customer 10 is eating
Customer 10 is finished eating
Customer 10 is finished eating
Customer 10 is finished eating
Customer 10 is finished eating
Customer 10 is finished eating
Customer 10 is finished eating
Customer 10 is finished eating
Customer 10 is finished eating
Customer 10 is finished eating
Customer 10 is finished eating
Joining thread 0
Joining thread 1
Joining thread 2
Joining thread 3
Joining thread 4
Joining thread 5
Joining thread 6
Joining thread 7
Joining thread 8
Joining thread 9
EDIT:
Updating the last parameter of pthread_create solves the issue of customers incrementing :
pthread_create(&customer[j],NULL,(void *)eat,(void *) (intptr_t) j);
However, it starts at 6, and then there's index out of bounds type of issue.
Console output now:
starting thread, semaphore is unlocked.
Creating mutex for plate 0
Creating mutex for plate 1
Creating mutex for plate 2
Creating mutex for plate 3
Creating mutex for plate 4
Creating mutex for plate 5
Creating mutex for plate 6
Creating mutex for plate 7
Creating thread for customer 0
Creating thread for customer 1
Creating thread for customer 2
Creating thread for customer 3
Creating thread for customer 4
Creating thread for customer 5
Creating thread for customer 6
Creating thread for customer 7
Creating thread for customer 8
Creating thread for customer 9
Customer 6 is eating
Customer 7 is eating
Illegal instruction (core dumped)
With the line
pthread_create(&customer[j],NULL,(void *)eat,&j);
you pass the address of the variable j
to the thread as argument. And you pass the same address to the same variable to all threads.
When the loop is over, the value of j
will be equal to customer_count
and that's what the threads might use as index into the array. And it's of curseo out of bounds leading to undefined behavior.
As for a possible solution, this is actually one place where it's accepted to cast an integer value into a pointer, and then in the thread cast it back. This has to be done in a couple of steps though. First when creating the thread:
pthread_create(&customer[j],NULL,(void *)eat, (void *) (intptr_t) j);
Then in the thread itself:
int n = (int) (intptr_t) i;