19/10/20 

## The Dining Philosophers Problem

<img src="/lectures/osc/assets/w.png" alt="img" style="zoom:67%;" />

The problem is defined as:

* **Five philosophers** are sitting on a round table
* Each one has a plate of spaghetti
* The spaghetti is too slippery, and each philosopher **needs 2 forks** to be able to eat
* When hungry, the philosopher tries to acquire the forks on his left and right.

Note that this reflects the general problem of **sharing a limited set** of resources (forks) between a **number of processes** (philosophers).

### Solution 1

**Forks** are represented by **semaphores** (initialised to 1)

* 1 if the fork is available: the philosopher can continue.
* 0 if the fork is unavailable: the philosopher goes to **sleep** if trying to acquire it.

Solution: Every philosopher picks up one fork and waits for the second fork to become available (without putting the first one down).

This solution will **deadlock** every time.

> * The deadlock can be avoided by exponential decay. This is where a philosopher puts down their fork and waits for a random amount of time. (this is how Ethernet systems avoid data collisions)
> * Just **add another fork**

### Solution 2

**One global mutex** set by a philosopher when they want to eat (only **one can eat at a time**)

*Question*: Can I initialise the value of the `eating` semaphore to 2 to create more parallelism?

Setting the semaphore to 2 allows the possibility of 2 philosophers to eat at one time. If these two philosophers are sitting next to each other then they will try to grab the same fork. The code will not deadlock, however only one (sometimes two) philosopher(s) is able to eat.



### Solution 3

A more sophisticated solution is necessary to allow **maximum parallelism**

The solution uses:

> * `state[N]` : one **state variable** for every philosopher (`THINKING` `HUNGRY` and `EATING`)
> * `phil[N] ` : one **semaphore per philosopher** (i.e. **not forks** initialised to 0)
>   * The philosopher goes to sleep if one of their neighbours are eating
>   * The neighbours wake up the philosopher if they have finished eating
> * `sync` : one **semaphore/mutex** to enforce **mutual exclusion** of the critical section (while updating the **states** of `hungry` `thinking` and `eating`)
> * A philosopher can only **start eating** if their neighbours are **not eating**.

![img](/lectures/osc/assets/x.png)

#### Code for Solution 3

```c
#define N 5
#define THINKING 1
#define HUNGRY 2
#define EATING 3

int state[N] = {THINKING, THINKING, THINKING, THINKING, THINKING};
sem_t phil[N]; // sends philosopher to sleep
sem_t sync;

void * philosopher(void * id) {
    int i = *((int *) id);
    
    while(1) {
        printf("%d is thinking\n", i);
        take_forks(i);
        printf("%d is eating\n", i);
        put_forks(i);
    }
}

void take_forks(int i) {
    sem_wait(&sync);
    state[i] = HUNGRY;
    test(i); //checks surrounding philosophers to see if its ok to eat
    sem_post(&sync);
    sem_wait(&phil[i]); //1 -> 0
}

void test(int i) {
    int left = (i + N - 1) % N;
    int right = (i + 1) % N;
    if(state[i] == HUNGRY && state[left] != EATING && state[right] != EATING) {
        state[i] = EATING;
        sem_post(&phil[i]); //0 -> 1
    }
}

void put_forks(int i) {
    int left = (i + N - 1) % N;
    int right = (i + 1) % N;
    sem_wait(&sync);
    state[i] = THINKING;
    test(left);
    test(right);
    sem_post(&sync);
}

void test(int i) {
    int left = (i + N - 1) % N;
    int right = (i + 1) % N;
    if(state[i] == HUNGRY && state[left] != EATING && state[right] != EATING) {
        state[i] = EATING;
        sem_post(&phil[i]);
    }
}
```

