I am trying to solve the dining philosophers problem (the problem: https://en.wikipedia.org/wiki/Dining_philosophers_problem) and I found the solution with code below. Solution uses semaphores and one mutex. I implemented busy waiting simple semaphores myself, cause C++ does not provide semaphores. I can't understand what's the purpose of mutex locking in take_forks
and put_forks
functions.
I tried to find the answer to my question, but I couldn't. So I am asking in stackoverflow.
take_forks
and put_forks
functions? (What can cause for race condition to occur?)#include <mutex>
#include <thread>
#define N 5
#define THINKING 0
#define HUNGRY 1
#define EATING 2
typedef int sem_t;
void sem_up(sem_t* semaphore) {
(*semaphore)++;
}
void sem_down(sem_t* semaphore) {
while (*semaphore == 0) {}
(*semaphore)--;
}
std::mutex mutualExclusion;
char philosopherState[N] = {THINKING};
sem_t philosopherSemaphore[N] = { 0 };
void test(short i) {
if (philosopherState[i] == HUNGRY && philosopherState[(i + 1) % N] != EATING && philosopherState[(i + N - 1) % N] != EATING) {
philosopherState[i] = EATING;
sem_up(&philosopherSemaphore[i]);
}
}
void think(short p) {
//some code
}
void eat() {
//some code
}
void take_forks(short i) {
::mutualExclusion.lock();
philosopherState[i] = HUNGRY;
test(i);
::mutualExclusion.unlock();
sem_down(&philosopherSemaphore[i]);
}
void put_forks(short i) {
::mutualExclusion.lock();
philosopherState[i] = THINKING;
test((i + 1) % N);
test((i + N - 1) % N);
::mutualExclusion.unlock();
}
void philosopher(short i) {
while (1) {
think();
take_forks(i);
eat();
put_forks(i);
}
}
I expected that mutex locking must be in test
function, because that's the only cause for race condition that I found.
This solution is introduced by A. Tanenbaum and is one of several solutions that are called arbitration. By this approach it is guaranteed that a philosopher can only pick up either forks or none by introducing an arbitrator, e.g., a waiter.In order to pick up the forks, a philosopher must ask permission of the waiter. The waiter gives permission to only one philosopher at a time until he has picked up both his forks. The waiter can be implemented as a mutex. Thus, the operation of checking and updating the array is atomic and exclusive access to forks is guaranteed. (That's the purpose of mutex)