#include <stdio.h>
#include <stdbool.h>
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
#include "synch.h"
#define NUM_PIRATES 5
enum pirate_state { ARGUING, HUNGRY, EATING };
struct lock *table_lock;
struct cv *pirate_jab[NUM_PIRATES];
volatile enum pirate_state state[NUM_PIRATES];
volatile bool royal_navy = false;
pthread_t pirate_thread[NUM_PIRATES];
void
rand_sleep(void)
{
usleep(100000 + (rand() % 200000)); /* Sleep for 100-250 ms */
}
void
pirate_maybe_escape(int pirate_id)
{
if (royal_navy) {
printf("Pirate %d be seein' the Royal Navy and be escapin'!\n",
pirate_id);
lock_release(table_lock);
pthread_exit(NULL);
}
}
#define left(i) ((i + NUM_PIRATES - 1) % NUM_PIRATES)
#define right(i) ((i + 1) % NUM_PIRATES)
bool
should_eat(int i)
{
return state[i] == HUNGRY && state[left(i)] != EATING &&
state[right(i)] != EATING;
}
void
take_cutlasses(int pirate_id)
{
lock_acquire(table_lock);
pirate_maybe_escape(pirate_id);
state[pirate_id] = HUNGRY;
printf("Pirate %d be hungry and eyein' the cutlasses...\n", pirate_id);
while (!should_eat(pirate_id)) {
printf("Pirate %d be waitin' for cutlasses...\n", pirate_id);
cv_wait(pirate_jab[pirate_id], table_lock);
printf("Pirate %d be wakin' up and eyein' the cutlasses...\n",
pirate_id);
pirate_maybe_escape(pirate_id);
}
state[pirate_id] = EATING;
printf("Pirate %d be eatin' with both cutlasses!\n", pirate_id);
lock_release(table_lock);
}
void
drop_cutlasses(int pirate_id)
{
lock_acquire(table_lock);
state[pirate_id] = ARGUING;
printf("Pirate %d be done eatin' and ready to argue again!\n",
pirate_id);
cv_signal(pirate_jab[left(pirate_id)], table_lock);
cv_signal(pirate_jab[right(pirate_id)], table_lock);
pirate_maybe_escape(pirate_id);
lock_release(table_lock);
}
void *
pirate(void *arg)
{
int pirate_id = *(int *) arg;
while (1) {
/* Argue about treasure */
printf("Pirate %d be arguin' about the treasure...\n",
pirate_id);
rand_sleep();
take_cutlasses(pirate_id);
/* Eat and swing cutlasses */
rand_sleep();
drop_cutlasses(pirate_id);
}
return NULL;
}
int
main(void)
{
int i;
int pirate_ids[NUM_PIRATES];
/* Initialize the table lock */
table_lock = lock_create("table_lock");
/* Initialize the condition variables and states */
for (i = 0; i < NUM_PIRATES; i++) {
char cv_name[20];
snprintf(cv_name, sizeof(cv_name), "pirate_cv_%d", i);
pirate_jab[i] = cv_create(cv_name);
state[i] = ARGUING;
}
/* Create the pirate threads */
for (i = 0; i < NUM_PIRATES; i++) {
pirate_ids[i] = i;
if (pthread_create(&pirate_thread[i], NULL, pirate,
&pirate_ids[i]) != 0) {
fprintf(stderr,
"Failed to create thread for pirate %d\n", i);
exit(1);
}
}
/* Allow ten seconds of pirate fun... */
sleep(10);
printf("Avast, ye scurvy dogs! The Royal Navy be here! Arrr!\n");
/* Alert the pirates that the Royal Navy is here */
lock_acquire(table_lock);
royal_navy = true;
lock_release(table_lock);
/* Wait for the threads to finish */
for (i = 0; i < NUM_PIRATES; i++) {
pthread_join(pirate_thread[i], NULL);
}
lock_destroy(table_lock);
for (i = 0; i < NUM_PIRATES; i++) {
cv_destroy(pirate_jab[i]);
}
return 0;
}
#ifndef SYNCH_H
#define SYNCH_H
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#define ERR_CHECK(result, msg, ...) \
if (result != 0) { \
fprintf(stderr, "Error: " msg ": %s\n", ##__VA_ARGS__, \
strerror(result)); \
exit(1); \
}
#define COND_CHECK(cond, msg, ...) \
if (!(cond)) { \
fprintf(stderr, "Error: " msg "\n", ##__VA_ARGS__); \
exit(1); \
}
struct lock {
const char *name;
pthread_mutex_t posix_mutex;
pthread_t lock_owner;
};
struct lock *
lock_create(const char *name)
{
struct lock *lock = malloc(sizeof(struct lock));
COND_CHECK(lock != NULL, "Failed to allocate memory for lock");
lock->name = name;
int result = pthread_mutex_init(&(lock->posix_mutex), NULL);
ERR_CHECK(result, "Failed to initialize mutex");
lock->lock_owner = (pthread_t) 0;
return lock;
}
int
lock_do_i_hold(struct lock *lock)
{
COND_CHECK(lock != NULL, "Attempted to check NULL lock");
return pthread_self() == lock->lock_owner;
}
void
lock_acquire(struct lock *lock)
{
COND_CHECK(lock != NULL, "Attempted to acquire NULL lock");
int result = pthread_mutex_lock(&(lock->posix_mutex));
ERR_CHECK(result, "Failed to acquire lock %s", lock->name);
lock->lock_owner = pthread_self();
}
void
lock_release(struct lock *lock)
{
COND_CHECK(lock != NULL, "Attempted to release NULL lock");
COND_CHECK(lock_do_i_hold(lock),
"Attempted to release lock %s not held by current thread",
lock->name);
lock->lock_owner = (pthread_t) 0;
int result = pthread_mutex_unlock(&(lock->posix_mutex));
ERR_CHECK(result, "Failed to release lock %s", lock->name);
}
void
lock_destroy(struct lock *lock)
{
COND_CHECK(lock != NULL, "Attempted to destroy NULL lock");
int result = pthread_mutex_destroy(&(lock->posix_mutex));
ERR_CHECK(result, "Failed to destroy lock %s", lock->name);
free(lock);
}
/* --- CONDITION VARIABLES ----------------------------------------------- */
struct cv {
const char *name;
pthread_cond_t posix_cv;
};
struct cv *
cv_create(const char *name)
{
struct cv *cv = malloc(sizeof(struct cv));
COND_CHECK(cv != NULL, "Failed to allocate memory for cv %s", name);
int result = pthread_cond_init(&(cv->posix_cv), NULL);
ERR_CHECK(result, "Failed to initialize condition variable %s", name);
cv->name = name;
return cv;
}
void
cv_wait(struct cv *cv, struct lock *lock)
{
COND_CHECK(cv != NULL, "Attempted to wait on NULL cv");
COND_CHECK(lock != NULL, "Attempted to wait on NULL lock");
COND_CHECK(lock_do_i_hold(lock),
"Attempted to wait on cv %s without holding lock %s",
cv->name, lock->name);
lock->lock_owner = (pthread_t) 0;
int result = pthread_cond_wait(&(cv->posix_cv), &(lock->posix_mutex));
ERR_CHECK(result, "Failed to wait on cv %s", cv->name);
lock->lock_owner = pthread_self();
}
void
cv_signal(struct cv *cv, struct lock *lock)
{
COND_CHECK(cv != NULL, "Attempted to signal NULL cv");
COND_CHECK(lock != NULL, "Attempted to signal on NULL lock");
COND_CHECK(lock_do_i_hold(lock),
"Attempted to signal cv %s without holding lock %s",
cv->name, lock->name);
int result = pthread_cond_signal(&(cv->posix_cv));
ERR_CHECK(result, "Failed to signal cv %s", cv->name);
}
void
cv_broadcast(struct cv *cv, struct lock *lock)
{
COND_CHECK(cv != NULL, "Attempted to broadcast NULL cv");
COND_CHECK(lock != NULL, "Attempted to broadcast on NULL lock");
COND_CHECK(lock_do_i_hold(lock),
"Attempted to broadcast cv %s without holding lock %s",
cv->name, lock->name);
int result = pthread_cond_broadcast(&(cv->posix_cv));
ERR_CHECK(result, "Failed to broadcast cv %s", cv->name);
}
void
cv_destroy(struct cv *cv)
{
COND_CHECK(cv != NULL, "Attempted to destroy NULL cv");
int result = pthread_cond_destroy(&(cv->posix_cv));
ERR_CHECK(result, "Failed to destroy cv %s", cv->name);
free(cv);
}
#endif // SYNCH_H