Completed code for assignment 4 of Operating Systems.

parent d3c10b1d
#include "stdio.h" /*
#include "stdlib.h" * Assignment 4 of Operating Systems: PThread simulation.
#include "pthread.h" *
* Sander van Veen (6167969) and Taddeus Kroes (6054129).
* <sandervv@gmail.com> and <taddeuskroes@hotmail.com>.
*
* Submission date: 21 november 2010.
*/
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h> #include <unistd.h>
#include <time.h> #include <time.h>
#include <sys/time.h> #include <sys/time.h>
#include <sys/times.h> #include <sys/times.h>
#include "main.h"
int forks_len; int forks_len;
pthread_mutex_t *forks; pthread_mutex_t *forks;
pthread_cond_t wait_threshold; pthread_cond_t wait_threshold;
pthread_barrier_t wait_barrier; pthread_barrier_t wait_barrier;
typedef struct diner_stats {
unsigned int meals;
unsigned int forks;
unsigned int id;
unsigned int locked;
} diner_stats;
int diner_finished = 0; int diner_finished = 0;
#define TIME_LIMIT 2.f /*
* Release fork taken by a philosopher.
*/
static inline void release_fork(diner_stats *stats, int f) { static inline void release_fork(diner_stats *stats, int f) {
if(diner_finished) if(diner_finished)
return; return;
// Unlock left or right fork.
stats->locked ^= 1 << (f % 2);
pthread_mutex_unlock(forks+f);
}
/*
* Try to claim a fork, but do not wait if the fork cannot be claimed.
* This call is therefore non-blocking.
*/
static inline int try_take_fork(diner_stats *stats, int f) {
// Try to lock left or right fork.
if(diner_finished || !pthread_mutex_trylock(forks+f))
return 1;
stats->locked ^= 1 << f; stats->locked |= 1 << (f % 2);
pthread_mutex_unlock(&forks[f]); stats->forks++;
return 0;
} }
/*
* Claim a fork, and wait (blocking) if the fork is already taken.
*/
static inline void take_fork(diner_stats *stats, int f) { static inline void take_fork(diner_stats *stats, int f) {
if(diner_finished) if(diner_finished)
return; return;
// Lock left or right fork.
pthread_mutex_lock(forks+f); pthread_mutex_lock(forks+f);
stats->locked |= 1 << f; stats->locked |= 1 << (f % 2);
stats->forks++; stats->forks++;
} }
#define PHILO_EATING \ #define PHILO_EATING \
if(diner_finished) \ if(diner_finished) \
break; \ return 1; \
\ \
stats->meals++; stats->meals++;
void philo_left(diner_stats *stats, int id) { static const char *philo_type_names[] = {"left", "right", "optimistic", "shy",
while(!diner_finished) { "random"};
// Let the philosopher take two forks. static const philo_t philo_types[] = {&philo_left, &philo_right, &philo_shy, \
take_fork(stats, id); &philo_optimistic, &philo_random};
take_fork(stats, (id+1) % forks_len);
PHILO_EATING; static const int philo_type_count = sizeof(philo_types) / sizeof(philo_t);
release_fork(stats, (id+1) % forks_len); /*
release_fork(stats, id); * Philosopher favoring the left fork above the right fork. He will wait for
} * both forks, and wouldn't release the first while he is waiting on the second.
*/
int philo_left(diner_stats *stats, int id) {
// Let the philosopher take two forks (first left, then right).
take_fork(stats, id);
take_fork(stats, (id+1) % forks_len);
PHILO_EATING;
// Release both forks.
release_fork(stats, (id+1) % forks_len);
release_fork(stats, id);
return 0;
} }
void philo_right(diner_stats *stats, int id) { /*
while(!diner_finished) { * Philosopher favoring the right fork above the left fork. He will wait for
// Let the philosopher take two forks. * both forks, and wouldn't release the first while he is waiting on the second.
take_fork(stats, (id+1) % forks_len); */
take_fork(stats, id); int philo_right(diner_stats *stats, int id) {
// Let the philosopher take two forks (first right, then left).
take_fork(stats, (id+1) % forks_len);
take_fork(stats, id);
PHILO_EATING; PHILO_EATING;
// Release both forks.
release_fork(stats, id);
release_fork(stats, (id+1) % forks_len);
release_fork(stats, id); return 0;
release_fork(stats, (id+1) % forks_len);
}
} }
void philo_optimistic(diner_stats *stats, int id) { /*
while(!diner_finished) { * Philosopher with optimistic behaviour: try to take the left or right fork,
// Let the philosopher take one of the two forks. * and if one fork is taken succesfully, wait till the second fork is released.
if(!pthread_mutex_trylock(&forks[id])) { */
int philo_optimistic(diner_stats *stats, int id) {
// Let the philosopher take the left fork of the two forks.
if(!try_take_fork(stats, id)) {
if(diner_finished)
return 1;
// Left fork is already taken, so try to take the right fork.
if(!try_take_fork(stats, (id+1) % forks_len)) {
if(diner_finished) if(diner_finished)
break; return 1;
// No forks available, be faster next time!
if(!pthread_mutex_trylock(&forks[(id+1) % forks_len])) { sched_yield();
if(diner_finished)
break;
// No fork available, be faster next time!
sched_yield();
}
else {
stats->locked |= 1 << ((id+1) % forks_len);
stats->forks++;
if(diner_finished)
break;
take_fork(stats, id);
PHILO_EATING;
release_fork(stats, id);
release_fork(stats, (id+1) % forks_len);
}
} }
else { else {
stats->locked |= 1 << id; // Grabbed the right fork successfully.
stats->forks++; if(diner_finished)
take_fork(stats, (id+1) % forks_len); return 1;
take_fork(stats, id);
PHILO_EATING; PHILO_EATING;
// Release both forks.
release_fork(stats, id); release_fork(stats, id);
release_fork(stats, (id+1) % forks_len); release_fork(stats, (id+1) % forks_len);
} }
} }
} else {
// Grabbed the left fork successfully.
take_fork(stats, (id+1) % forks_len);
void philo_shy(diner_stats *stats, int id) { PHILO_EATING;
// Release both forks.
release_fork(stats, id);
release_fork(stats, (id+1) % forks_len);
}
return 0;
} }
typedef void(*philo_t)(diner_stats *, int); /*
* Philosopher with shy behaviour: try again next time, when taking one of the
* forks fails.
*/
int philo_shy(diner_stats *stats, int id) {
// Try to take the right fork.
if(!try_take_fork(stats, (id+1) % forks_len)) {
if(diner_finished)
return 1;
// Failed to take fork, so try again next time.
sched_yield();
}
// Let the philosopher try to take the left fork.
else if(!try_take_fork(stats, id)) {
if(diner_finished)
return 1;
// Release the right fork and try again next time.
release_fork(stats, (id+1) % forks_len);
sched_yield();
}
else {
// Grabbed the left and right fork successfully.
PHILO_EATING;
// Release both forks.
release_fork(stats, id);
release_fork(stats, (id+1) % forks_len);
}
return 0;
}
static const char *philo_type_names[] = {"left", "right", "optimistic"};//, "shy"}; /*
static const philo_t philo_types[] = {&philo_left, &philo_right, \ * Philosopher with random behaviour: change each iteration to one of the other
&philo_optimistic};//, &philo_shy}; * philosopher behaviours.
*/
int philo_random(diner_stats *stats, int id) {
// Pick a behaviour randomly (except for the "random" behaviour, since that
// would be recursive and thus useless to simulate).
return (*philo_types[rand() % (philo_type_count - 1)])(stats, id);
}
void *philo_start(void *raw_stats) { void *philo_start(void *raw_stats) {
diner_stats *stats = (diner_stats *) raw_stats; diner_stats *stats = (diner_stats *) raw_stats;
int id = stats->id, int id = stats->id,
type = rand() % (sizeof(philo_types) / sizeof(philo_t)); type = rand() % philo_type_count;
printf("P #%u: hello everybody, i'm %s!\n", id, philo_type_names[type]); printf("P #%u: hello everybody, i'm %s!\n", id, philo_type_names[type]);
// Philosophers wait for each other, before they can start eating diner. // Philosophers wait for each other, before they can start eating diner.
pthread_barrier_wait(&wait_barrier); pthread_barrier_wait(&wait_barrier);
while(!diner_finished && !(*philo_types[type])(stats, id));
(*philo_types[type])(stats, id); printf("P #%u: i'm leaving, after paying the bill.\n", id);
pthread_exit(NULL); pthread_exit(NULL);
} }
...@@ -189,25 +271,26 @@ void host_start(int philos) { ...@@ -189,25 +271,26 @@ void host_start(int philos) {
gettimeofday(&wct, NULL); gettimeofday(&wct, NULL);
double start_time = wct.tv_sec + wct.tv_usec / 1e6; double start_time = wct.tv_sec + wct.tv_usec / 1e6;
// Duration of the diner party is 10 seconds. This loop will prevent the // Duration of the diner party is 10 seconds (see main.h). This loop will
// philosphers from exceeding this time limit. // prevent the philosphers from exceeding this time limit.
do { do {
usleep(10); // Check each 10 ms for deadlock.
usleep(10000);
// Check if deadlock occured. If an deadlock occured (all philosophers // If an deadlock occured (all philosophers are waiting on their left or
// are waiting on their second fork), finish the diner immidiately. // right fork), finish the diner party immidiately. It is unclear in the
// assignment, if the philosophers should leave the diner party, or if
// they only had to release their claimed forks. I choose the first
// assumption: leave the party. If they start arguing which fork is
// theirs, there's no party anymore.
int p; int p;
for(p = 0; p < philos && stats[p].locked & (1 << p); p++); for(p = 0; p < philos && (stats[p].locked & 1
|| stats[p].locked & 2); p++);
if( p == philos ) { if( p == philos ) {
puts("Host: deadlock occured."); puts("Host: deadlock occured.");
diner_finished = 1; diner_finished = 1;
for(p = 0; p < philos; p++) {
pthread_mutex_unlock(&forks[p]);
stats[p].locked = 0;
}
break; break;
} }
...@@ -218,28 +301,22 @@ void host_start(int philos) { ...@@ -218,28 +301,22 @@ void host_start(int philos) {
puts("Host: diner is finished."); puts("Host: diner is finished.");
// Wait on the other philosophers.
for(int i = 0; i < philos; i++) { for(int i = 0; i < philos; i++) {
// Reclaim all cutlery (mutexes). This is done twice, because some // Reclaim all cutlery (mutexes) in reverse order.
// philosophers can continue take an fork while the host is still for(int p = philos; p >= i; p--)
// reclaiming the other forks.
for(int p = 0; p < philos; p++)
pthread_mutex_unlock(&forks[p]);
for(int p = 0; p < philos; p++)
pthread_mutex_unlock(&forks[p]); pthread_mutex_unlock(&forks[p]);
// Wait on the other philosophers.
if( (rc = pthread_join(threads[i], &status)) ) { if( (rc = pthread_join(threads[i], &status)) ) {
fprintf(stderr, "return code from pthread_join() is %d\n", rc); fprintf(stderr, "return code from pthread_join() is %d\n", rc);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
printf("Philo #%d ate %d meal(s) and grabbed %d forks.\n", printf("Philo #%d ate %d meal(s) and grabbed %d fork(s).\n",
i, stats[i].meals, stats[i].forks); i, stats[i].meals, stats[i].forks);
}
for(int i = 0; i < philos; i++)
pthread_mutex_destroy(&forks[i]); pthread_mutex_destroy(&forks[i]);
}
puts("Host: philosophers are done."); puts("Host: philosophers are done.");
...@@ -249,7 +326,15 @@ void host_start(int philos) { ...@@ -249,7 +326,15 @@ void host_start(int philos) {
pthread_exit(NULL); pthread_exit(NULL);
} }
/*
* Start the philosopher diner problem simulation. By default two philosophers
* are simulated. The first commandline argument changes the total philosophers
* being simulated. If the argument is less than two, the philosopher count is
* set to the default count of two.
*/
int main(int argc, const char **argv) { int main(int argc, const char **argv) {
// Get system clock's milliseconds and set it as seed to ensure a
// more "pseudo-random" simulation.
struct timeval wtc; struct timeval wtc;
gettimeofday(&wtc, NULL); gettimeofday(&wtc, NULL);
srand(wtc.tv_usec); srand(wtc.tv_usec);
......
/*
* Assignment 4 of Operating Systems: PThread simulation.
*
* Sander van Veen (6167969) and Taddeus Kroes (6054129).
* <sandervv@gmail.com> and <taddeuskroes@hotmail.com>.
*
* Submission date: 21 november 2010.
*/
typedef struct diner_stats {
unsigned int meals;
unsigned int forks;
unsigned int id;
unsigned int locked;
} diner_stats;
/*
* Duration of the diner party (in seconds). When this time limit is reached,
* all philosophers are requested to pay their bill and leave the party.
*/
#define TIME_LIMIT 10.f
int philo_left(diner_stats *stats, int id);
int philo_right(diner_stats *stats, int id);
int philo_optimistic(diner_stats *stats, int id);
int philo_shy(diner_stats *stats, int id);
int philo_random(diner_stats *stats, int id);
typedef int(*philo_t)(diner_stats *, int);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment