#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include "synch.h"
#define NUM_PIRATES 5
struct lock *cutlass[NUM_PIRATES];
pthread_t pirate_thread[NUM_PIRATES];
void
rand_sleep(void)
{
usleep(100000 + (rand() % 200000)); /* Sleep for 100-250 ms */
}
void *
pirate(void *arg)
{
int i = *(int *)arg;
int left = i;
int right = (i + 1) % NUM_PIRATES;
while (1) {
/* Argue about treasure */
printf("Pirate %d be arguin' about the treasure...\n", i);
rand_sleep();
printf("Pirate %d be eyein' the cutlasses...\n", i);
/* Grab the left cutlass */
lock_acquire(cutlass[left]);
printf("Pirate %d grabbed the left cutlass %d!\n", i, left);
/* Momentary confusion from too much grog */
rand_sleep();
/* Grab the right cutlass */
lock_acquire(cutlass[right]);
printf("Pirate %d grabbed the right cutlass %d!\n", i, right);
/* Use both cutlasses */
printf("Pirate %d be eatin', swingin' both cutlasses!\n", i);
rand_sleep();
/* Release the cutlasses */
lock_release(cutlass[right]);
lock_release(cutlass[left]);
}
return NULL;
}
int
main(void)
{
int i;
int pirate_ids[NUM_PIRATES];
/* Initialize the locks */
for (i = 0; i < NUM_PIRATES; i++) {
char lock_name[20];
snprintf(lock_name, sizeof(lock_name), "cutlass_%d", i);
cutlass[i] = lock_create(lock_name);
if (cutlass[i] == NULL) {
fprintf(stderr, "Failed to create lock for cutlass %d\n", i);
exit(1);
}
}
/* 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);
/* ...then kill the fun in case you leave it running in OnlineGDB or the CS 134 server */
printf("Avast, ye scurvy dogs! The Royal Navy be here! The pirates be done for!\n");
/* We won't clean up since we're might be deadlocked, just exit */
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