Last active
July 16, 2021 06:26
-
-
Save exbotanical/021e47b7f4b345fc71a2c0372ff8178c to your computer and use it in GitHub Desktop.
The Dining Philosopher
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
gcc dining_philosopher.c -o main -lpthread | |
./main |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "dining_philosopher.h" | |
#include <assert.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <unistd.h> | |
/* Globals */ | |
static philosopher_t philosophers[N_PHILOSOPHERS]; | |
static spoon_t spoons[N_PHILOSOPHERS]; | |
/* Main Thread */ | |
int main(int argc, char* argv[]) { | |
pthread_attr_t attr; | |
/* initialize spoons */ | |
int i; | |
for (i = 0; i < N_PHILOSOPHERS; i++) { | |
spoons[i].id = i; | |
spoons[i].in_use = false; | |
spoons[i].philosopher = NULL; | |
pthread_mutex_init(&spoons[i].mutex, NULL); | |
pthread_cond_init(&spoons[i].cv, NULL); | |
} | |
// default attrs of philosopher threads | |
pthread_attr_init(&attr); | |
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); | |
/* initialize philosophers */ | |
for (i = 0; i < N_PHILOSOPHERS; i++) { | |
pthread_t philos; | |
philosophers[i].id = i; | |
philosophers[i].eat_count = 0; | |
pthread_create( | |
&philosophers[i].thread, | |
&attr, | |
philosopher_routine, | |
&philosophers[i] | |
); | |
} | |
pthread_exit(0); | |
return EXIT_SUCCESS; | |
} | |
/** | |
* @brief Philosopher thread routine | |
* | |
* @param arg | |
* @return void* | |
*/ | |
void* philosopher_routine(void* arg) { | |
philosopher_t* philos = (philosopher_t*)arg; | |
printf("[philosopher %d] takes turn\n", philos->id); | |
while (1) { | |
if (can_acquire_both_spoons(philos)) { | |
philos_dine(philos); | |
philos_release_spoons(philos); | |
// satisfy constraint | |
sleep(1); | |
} | |
} | |
} | |
/** | |
* @brief Get the right spoon object | |
* | |
* @return spoon_t* | |
*/ | |
static spoon_t* get_r_spoon(philosopher_t* philos) { | |
int philos_id = philos->id; | |
if (philos_id == 0) return &spoons[N_PHILOSOPHERS - 1]; | |
return &spoons[philos_id - 1]; | |
} | |
/** | |
* @brief Get the left spoon object | |
* | |
* @return spoon_t* | |
*/ | |
static spoon_t* get_l_spoon(philosopher_t* philos) { | |
return &spoons[philos->id]; | |
} | |
void philos_dine(philosopher_t* philos) { | |
spoon_t *l_spoon = get_l_spoon(philos), | |
*r_spoon = get_r_spoon(philos); | |
assert(l_spoon->philosopher == philos); | |
assert(r_spoon->philosopher == philos); | |
assert(l_spoon->in_use == true); | |
assert(r_spoon->in_use == true); | |
printf( | |
"[philosopher %d] dining on the communal cake with spoons [%d,%d]\n", | |
philos->id, | |
l_spoon->id, | |
r_spoon->id | |
); | |
philos->eat_count++; | |
// satisfy constraint | |
sleep(1); | |
} | |
/** | |
* @brief Release spoons in use by a given philosopher (if applicable); | |
* the philosopher here enacts the role of Producer, making available | |
* conditionally locked spoons | |
* | |
* @param philos | |
*/ | |
void philos_release_spoons(philosopher_t* philos) { | |
spoon_t* l_spoon = get_l_spoon(philos); | |
spoon_t* r_spoon = get_r_spoon(philos); | |
pthread_mutex_lock(&l_spoon->mutex); | |
pthread_mutex_lock(&r_spoon->mutex); | |
printf( | |
"[philosopher %d] locks left spoon %d mutex preparing to release\n", | |
philos->id, | |
l_spoon->id | |
); | |
printf( | |
"[philosopher %d] locks right spoon %d mutex preparing to release\n", | |
philos->id, | |
r_spoon->id | |
); | |
assert(l_spoon->philosopher == philos); | |
assert(l_spoon->in_use == true); | |
assert(r_spoon->philosopher == philos); | |
assert(r_spoon->in_use == true); | |
l_spoon->philosopher = NULL; | |
l_spoon->in_use = false; | |
printf( | |
"[philosopher %d] releases left spoon %d, unlocking its mutex and signaling its condition variable\n", | |
philos->id, | |
l_spoon->id | |
); | |
// send signal to condition variable, unblocking philosopher(s) awaiting the spoon | |
pthread_cond_signal(&l_spoon->cv); | |
pthread_mutex_unlock(&l_spoon->mutex); | |
r_spoon->philosopher = NULL; | |
r_spoon->in_use = false; | |
printf( | |
"[philosopher %d] releases right spoon %d, unlocking its mutex and signaling its condition variable\n", | |
philos->id, | |
r_spoon->id | |
); | |
pthread_cond_signal(&r_spoon->cv); | |
pthread_mutex_unlock(&r_spoon->mutex); | |
} | |
/** | |
* @brief Attempt to acquire both spoons for a given philosopher; | |
* the philosopher here enacts the role of Consumer, awaiting | |
* the availability of each spoon | |
* | |
* @param philos | |
* @return true | |
* @return false | |
*/ | |
bool can_acquire_both_spoons(philosopher_t *philos) { | |
spoon_t* l_spoon = get_l_spoon(philos); | |
spoon_t* r_spoon = get_r_spoon(philos); | |
printf( | |
"[philosopher %d] waits for left spoon %d mutex\n", | |
philos->id, | |
l_spoon->id | |
); | |
pthread_mutex_lock(&l_spoon->mutex); | |
printf( | |
"[philosopher %d] locks left spoon %d mutex\n", | |
philos->id, | |
l_spoon->id | |
); | |
// spoon in use, wait for cv signal | |
while (l_spoon->in_use && l_spoon->philosopher != philos) { | |
printf( | |
"[philosopher %d] blocks while waiting for left spoon %d to become available\n", | |
philos->id, | |
l_spoon->id | |
); | |
// await signal | |
pthread_cond_wait(&l_spoon->cv, &l_spoon->mutex); | |
printf( | |
"[philosopher %d] receives cv signal for left spoon %d\n", | |
philos->id, | |
l_spoon->id | |
); | |
} | |
// acquire spoon now that it is not in use | |
l_spoon->in_use == true; | |
l_spoon->philosopher == philos; | |
pthread_mutex_unlock(&l_spoon->mutex); | |
printf( | |
"[philosopher %d] acquires left spoon %d and unlocks mutex\n", | |
philos->id, | |
l_spoon->id | |
); | |
// and now the right spoon... | |
printf( | |
"[philosopher %d] waits for right spoon %d mutex\n", | |
philos->id, | |
l_spoon->id | |
); | |
pthread_mutex_lock(&r_spoon->mutex); | |
printf( | |
"[philosopher %d] locks right spoon %d mutex\n", | |
philos->id, | |
l_spoon->id | |
); | |
if (r_spoon->in_use == false) { | |
r_spoon->philosopher == philos; | |
r_spoon->in_use == true; | |
pthread_mutex_unlock(&r_spoon->mutex); | |
printf( | |
"[philosopher %d] acquires right spoon %d and unlocks mutex\n", | |
philos->id, | |
l_spoon->id | |
); | |
return true; | |
} else if (r_spoon->in_use == true) { | |
if (r_spoon->philosopher != philos) { | |
pthread_mutex_lock(&l_spoon->mutex); | |
assert(l_spoon->in_use == true); | |
assert(l_spoon->philosopher == philos); | |
l_spoon->in_use = false; | |
l_spoon->philosopher = NULL; | |
pthread_mutex_unlock(&l_spoon->mutex); | |
pthread_mutex_unlock(&r_spoon->mutex); | |
printf( | |
"[philosopher %d] releases left spoon %d because right spoon %d is not yet available\n", | |
philos->id, | |
l_spoon->id, | |
r_spoon->id | |
); | |
return false; | |
} else { | |
pthread_mutex_unlock(&r_spoon->mutex); | |
printf( | |
"[philosopher %d] already possesses right spoon %d\n", | |
philos->id, | |
r_spoon->id | |
); | |
return true; | |
} | |
} | |
// we'll never reach this point, but to make the compiler happy... | |
assert(0); | |
return true; | |
} | |
/* | |
o philosopher (pN) | |
|* spoon (sN) | |
s0|* p1 |*s1 p2 | |
____o_________o_____ | |
p0 | | | |
o | .i.i. | | |
| ||| | |* s2 | |
|___________________| | |
|* o o p3 | |
s4 p4 |*s3 | |
*/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef DINING_PHILOS_H | |
#define DINING_PHILOS_H | |
#include <pthread.h> | |
#include <stdbool.h> | |
#define N_PHILOSOPHERS 5 | |
/* Structures */ | |
typedef struct philosopher { | |
int id; | |
int eat_count; /* N times this thread has accessed the 'cake' resource */ | |
pthread_t thread; | |
} philosopher_t; | |
typedef struct spoon { | |
int id; | |
bool in_use; /* Indicative of this resource's status */ | |
philosopher_t* philosopher; /* The philosopher currently in possesion of this resource */ | |
pthread_mutex_t mutex; /* For mutual exclusion */ | |
pthread_cond_t cv; /* For thread synchronization for this resource */ | |
} spoon_t; | |
/* Functions */ | |
void* philosopher_routine(void* arg); | |
static spoon_t* get_right_spoon(); | |
static spoon_t* get_left_spoon(); | |
void philos_dine(philosopher_t* philos); | |
void philos_release_spoons(philosopher_t* philos); | |
bool can_acquire_both_spoons(philosopher_t *philos); | |
#endif /* DINING_PHILOS_H */ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment