Completed code for assignment 4 of Operating Systems.

parent d3c10b1d
#include "stdio.h"
#include "stdlib.h"
#include "pthread.h"
/*
* 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.
*/
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <time.h>
#include <sys/time.h>
#include <sys/times.h>
#include "main.h"
int forks_len;
pthread_mutex_t *forks;
pthread_cond_t wait_threshold;
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;
#define TIME_LIMIT 2.f
/*
* Release fork taken by a philosopher.
*/
static inline void release_fork(diner_stats *stats, int f) {
if(diner_finished)
return;
stats->locked ^= 1 << f;
pthread_mutex_unlock(&forks[f]);
// 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 % 2);
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) {
if(diner_finished)
return;
// Lock left or right fork.
pthread_mutex_lock(forks+f);
stats->locked |= 1 << f;
stats->locked |= 1 << (f % 2);
stats->forks++;
}
#define PHILO_EATING \
if(diner_finished) \
break; \
return 1; \
\
stats->meals++;
void philo_left(diner_stats *stats, int id) {
while(!diner_finished) {
// Let the philosopher take two forks.
static const char *philo_type_names[] = {"left", "right", "optimistic", "shy",
"random"};
static const philo_t philo_types[] = {&philo_left, &philo_right, &philo_shy, \
&philo_optimistic, &philo_random};
static const int philo_type_count = sizeof(philo_types) / sizeof(philo_t);
/*
* 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) {
// Let the philosopher take two forks.
/*
* Philosopher favoring the right fork above the left fork. He will wait for
* both forks, and wouldn't release the first while he is waiting on the second.
*/
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;
// Release both forks.
release_fork(stats, id);
release_fork(stats, (id+1) % forks_len);
}
return 0;
}
void philo_optimistic(diner_stats *stats, int id) {
while(!diner_finished) {
// Let the philosopher take one of the two forks.
if(!pthread_mutex_trylock(&forks[id])) {
/*
* Philosopher with optimistic behaviour: try to take the left or right fork,
* and if one fork is taken succesfully, wait till the second fork is released.
*/
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)
break;
return 1;
if(!pthread_mutex_trylock(&forks[(id+1) % forks_len])) {
// Left fork is already taken, so try to take the right fork.
if(!try_take_fork(stats, (id+1) % forks_len)) {
if(diner_finished)
break;
// No fork available, be faster next time!
return 1;
// No forks available, be faster next time!
sched_yield();
}
else {
stats->locked |= 1 << ((id+1) % forks_len);
stats->forks++;
// Grabbed the right fork successfully.
if(diner_finished)
break;
return 1;
take_fork(stats, id);
PHILO_EATING;
// Release both forks.
release_fork(stats, id);
release_fork(stats, (id+1) % forks_len);
}
}
else {
stats->locked |= 1 << id;
stats->forks++;
// Grabbed the left fork successfully.
take_fork(stats, (id+1) % forks_len);
PHILO_EATING;
// Release both forks.
release_fork(stats, id);
release_fork(stats, (id+1) % forks_len);
}
}
return 0;
}
void philo_shy(diner_stats *stats, int id) {
/*
* 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;
typedef void(*philo_t)(diner_stats *, int);
// 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;
static const char *philo_type_names[] = {"left", "right", "optimistic"};//, "shy"};
static const philo_t philo_types[] = {&philo_left, &philo_right, \
&philo_optimistic};//, &philo_shy};
// Release both forks.
release_fork(stats, id);
release_fork(stats, (id+1) % forks_len);
}
return 0;
}
/*
* Philosopher with random behaviour: change each iteration to one of the other
* 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) {
diner_stats *stats = (diner_stats *) raw_stats;
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]);
// Philosophers wait for each other, before they can start eating diner.
pthread_barrier_wait(&wait_barrier);
(*philo_types[type])(stats, id);
while(!diner_finished && !(*philo_types[type])(stats, id));
printf("P #%u: i'm leaving, after paying the bill.\n", id);
pthread_exit(NULL);
}
......@@ -189,25 +271,26 @@ void host_start(int philos) {
gettimeofday(&wct, NULL);
double start_time = wct.tv_sec + wct.tv_usec / 1e6;
// Duration of the diner party is 10 seconds. This loop will prevent the
// philosphers from exceeding this time limit.
// Duration of the diner party is 10 seconds (see main.h). This loop will
// prevent the philosphers from exceeding this time limit.
do {
usleep(10);
// Check if deadlock occured. If an deadlock occured (all philosophers
// are waiting on their second fork), finish the diner immidiately.
// Check each 10 ms for deadlock.
usleep(10000);
// If an deadlock occured (all philosophers are waiting on their left or
// 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;
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 ) {
puts("Host: deadlock occured.");
diner_finished = 1;
for(p = 0; p < philos; p++) {
pthread_mutex_unlock(&forks[p]);
stats[p].locked = 0;
}
break;
}
......@@ -218,28 +301,22 @@ void host_start(int philos) {
puts("Host: diner is finished.");
// Wait on the other philosophers.
for(int i = 0; i < philos; i++) {
// Reclaim all cutlery (mutexes). This is done twice, because some
// philosophers can continue take an fork while the host is still
// reclaiming the other forks.
for(int p = 0; p < philos; p++)
pthread_mutex_unlock(&forks[p]);
for(int p = 0; p < philos; p++)
// Reclaim all cutlery (mutexes) in reverse order.
for(int p = philos; p >= i; p--)
pthread_mutex_unlock(&forks[p]);
// Wait on the other philosophers.
if( (rc = pthread_join(threads[i], &status)) ) {
fprintf(stderr, "return code from pthread_join() is %d\n", rc);
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);
}
for(int i = 0; i < philos; i++)
pthread_mutex_destroy(&forks[i]);
}
puts("Host: philosophers are done.");
......@@ -249,7 +326,15 @@ void host_start(int philos) {
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) {
// Get system clock's milliseconds and set it as seed to ensure a
// more "pseudo-random" simulation.
struct timeval wtc;
gettimeofday(&wtc, NULL);
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