This is a variation of the Dining Philosophers Problem. The task is to coordinate several students inside a gym. All students try to obtain their desired training weights from a shared weight rack. During runtime, the user can issue commands to:
- block a student (b + student id)
- unblock a student (u + student id)
- proceed (p + student id) (ends a student's workout/rest loop)
- end the program (q or Q)
I'm a beginner in C and would appreciate opinions and pointers on how to improve my code.
main.c
#include <pthread.h>
#include "main.h"
#include "gym_monitor.h"
/*
*
* Main module of the Thread-Coordination Exercise. This modul contains
* the main function which creates the threads. After creation all threads
* enter the gym_routine function.
*
*/
#define REST_LOOP 1000000000
#define WORKOUT_LOOP 500000000
#define WEIGHTS_ANNA 6
#define WEIGHTS_BERND 8
#define WEIGHTS_CLARA_DIRK 12
#define WEIGHTS_EMMA 14
#define MAX_INPUT_SIZE 3
#define WEIGHT_RACK_DEF {4,4,5}
static pthread_barrier_t gym_routine_barrier;
static void workout(Student* student) {
for( int i = 0; i < WORKOUT_LOOP; i++ ) {
if(student->status == BLOCKED) {
rest_student(student);
}else if(student->status == PROCEED) {
student->status = NORMAL;
break;
}
}
}
static void rest(Student* student) {
for( int i = 0; i < REST_LOOP; i++ ) {
if(student->status == BLOCKED) {
rest_student(student);
}else if(student->status == PROCEED) {
student->status = NORMAL;
break;
}
}
}
static void* gym_routine(void* stud) {
pthread_barrier_wait(&gym_routine_barrier);
Student* student = (Student*) stud;
while(student->status != QUIT) {
get_weights(student);
workout(student);
put_weights(student);
rest(student);
}
return NULL;
}
int main(void) {
char available_weights[] = WEIGHT_RACK_DEF;
int students_weights[] = {WEIGHTS_ANNA,WEIGHTS_BERND,WEIGHTS_CLARA_DIRK,
WEIGHTS_CLARA_DIRK,WEIGHTS_EMMA};
Student students[NR_STUDENTS];
monitor_vars* monitor = init_monitor();
pthread_barrier_init(&gym_routine_barrier, NULL, NR_STUDENTS);
int res;
for( int i = 0; i < NR_STUDENTS; i++ ) {
students[i].thread_id = i;
students[i].weight_plan = students_weights[i];
students[i].status = NORMAL;
for(int j = 0; j < NR_WEIGHTS; j++) {
students[i].current_weight[j] = 0;
}
students[i].mon = monitor;
students[i].sem_student = init_sem_student();
students[i].other_students = students;
students[i].weight_rack = available_weights;
res = pthread_create(&students[i].thread, NULL, gym_routine,
(void*) &students[i]);
if(res != 0) {
perror("Thread creation failed");
exit(EXIT_FAILURE);
}
}
/*Handling user input*/
char input[MAX_INPUT_SIZE] = {0};
while(strncasecmp(fgets(input, MAX_INPUT_SIZE, stdin),"q", 1)) {
/* trying to get rid of newline from input
this is the only 'solution' that works so far*/
if(input[0] == '\n' || input[0] == '\0') {
continue;
}
fflush(stdout);
if((input[0] - '0') >= 0 && (input[0] - '0') < NR_STUDENTS
&& strchr("bpu", input[1]) && strlen(input) == 2 ) {
int student_id = input[0] - '0';
students[student_id].status = input[1];
if(students[student_id].status == UNBLOCK) {
wake_student(&(students[student_id]));
students[student_id].status = NORMAL;
}
}else {
printf("Not a valid instruction\n");
fflush(stdout);
}
}
/*updating student status*/
for(int i = 0; i < NR_STUDENTS; i++) {
wake_student(&students[i]); //students can only quit if they are not asleep
students[i].status = QUIT;
}
for(int i = 0; i < NR_STUDENTS; i++) {
pthread_join(students[i].thread,NULL);
destroy_sem_student(&students[i]);
free(students[i].sem_student);
}
destroy_monitor(monitor);
exit(EXIT_SUCCESS);
}
main.h
#ifndef MAIN_H
#define MAIN_H
#include <stdlib.h>
#include <pthread.h>
#include <string.h>
#include <strings.h>
#include "gym_monitor.h"
enum weight_names{
KG_2,
KG_3,
KG_5
};
#define BLOCKED 'b'
#define PROCEED 'p'
#define NORMAL 'n'
#define UNBLOCK 'u'
#define QUIT 'q'
#define NR_WEIGHTS 3
#define NR_STUDENTS 5
typedef struct Student Student;
struct Student {
pthread_t thread;
int thread_id;
int weight_plan;
char status;
char training_state;
char current_weight[NR_WEIGHTS];
monitor_vars* mon;
sem_t* sem_student;
Student* other_students;
char* weight_rack;
};
#endif
gym_Monitor.c
#include "gym_monitor.h"
#include "main.h"
/*
* Monitor module - Encapsulates all objects and functions that manage
* thread-coordination.
*
*/
#define MAX_2KG_3KG 4
#define MAX_5KG 5
#define RED "\x1B[31m"
#define RESET "\x1B[0m"
const int weight_arr[] = {
[KG_2] = 2,
[KG_3] = 3,
[KG_5] = 5
};
static int calculate_weight(Student* student, int weight) {
if(weight == 0) {
return 1;
}
if(weight >= weight_arr[KG_2] && student->weight_rack[KG_2] > 0) {
student->weight_rack[KG_2] -= 1;
student->current_weight[KG_2] += 1;
if(!calculate_weight(student, weight - weight_arr[KG_2])) {
student->weight_rack[KG_2] += 1;
student->current_weight[KG_2] -= 1;
}else {
return 1;
}
if(weight >= weight_arr[KG_3] && student->weight_rack[KG_3] > 0) {
student->weight_rack[KG_3] -= 1;
student->current_weight[KG_3] += 1;
if(!calculate_weight(student, weight - weight_arr[KG_3])) {
student->weight_rack[KG_3] += 1;
student->current_weight[KG_3] -= 1;
}else {
return 1;
}
}
if(weight >= weight_arr[KG_5] && student->weight_rack[KG_5] > 0) {
student->weight_rack[KG_5] -= 1;
student->current_weight[KG_5] += 1;
if(!calculate_weight(student, weight - weight_arr[KG_5])) {
student->weight_rack[KG_5] += 1;
student->current_weight[KG_5] -= 1;
}else {
return 1;
}
}
}
return 0;
}
static void display__status(Student* student) {
int consistency_check[] = {0,0,0};
for(int i = 0; i < NR_STUDENTS; i++) {
printf("%d(%d)%c:%c:[%d, %d, %d] ",student->other_students[i].thread_id,
student->other_students[i].weight_plan,
student->other_students[i].status,
student->other_students[i].training_state,
student->other_students[i].current_weight[KG_2],
student->other_students[i].current_weight[KG_3],
student->other_students[i].current_weight[KG_5]);
consistency_check[KG_2] += student->other_students[i].current_weight[KG_2];
consistency_check[KG_3] += student->other_students[i].current_weight[KG_3];
consistency_check[KG_5] += student->other_students[i].current_weight[KG_5];
}
if( consistency_check[KG_2] > MAX_2KG_3KG
|| consistency_check[KG_3] > MAX_2KG_3KG
|| consistency_check[KG_5] > MAX_5KG ) {
printf(RED "Inconsistent State\n" );
printf("[%d, %d, %d]\n" RESET,consistency_check[KG_2],
consistency_check[KG_3],
consistency_check[KG_5]);
}else {
printf("Supply: [%d, %d, %d]\n", student->weight_rack[KG_2],
student->weight_rack[KG_3],
student->weight_rack[KG_5]);
}
fflush(stdout);
}
void get_weights(Student* student) {
pthread_mutex_lock(&student->mon->lock);
student->training_state = GET_WEIGHTS;
while(!calculate_weight(student, student->weight_plan)) {
student->training_state = BLOCKED;
display__status(student);
pthread_cond_wait(&student->mon->insufficient_weight, &student->mon->lock);
if(student->status != QUIT) {
student->status = NORMAL;
}
}
display__status(student);
student->training_state = WORKOUT;
pthread_mutex_unlock(&student->mon->lock);
}
void put_weights(Student* student) {
pthread_mutex_lock(&student->mon->lock);
student->training_state = PUT_WEIGHTS;
student->weight_rack[KG_2] += student->current_weight[KG_2];
student->weight_rack[KG_3] += student->current_weight[KG_3];
student->weight_rack[KG_5] += student->current_weight[KG_5];
for(int i = 0; i < NR_WEIGHTS; i++) {
student->current_weight[i] = 0;
}
display__status(student);
pthread_cond_signal(&student->mon->insufficient_weight);
student->training_state = REST;
pthread_mutex_unlock(&student->mon->lock);
}
void rest_student(Student* student) {
sem_wait(student->sem_student);
}
void wake_student(Student* student) {
sem_post(student->sem_student);
}
sem_t* init_sem_student() {
int res = 0;
sem_t* sem_student = malloc(sizeof(sem_t));
if(sem_student == NULL) {
perror("malloc failed, exiting...");
exit(EXIT_FAILURE);
}
res = sem_init(sem_student,0,1);
if(res != 0) {
perror("Semaphore creation failed");
exit(EXIT_FAILURE);
}
return sem_student;
}
void destroy_sem_student(Student* student) {
int res = 0;
res = sem_destroy(student->sem_student);
if(res != 0) {
perror("Destroying semaphore failed");
exit(EXIT_FAILURE);
}
}
monitor_vars* init_monitor() {
int res = 0;
monitor_vars* monitor_vars_ptr = malloc(sizeof(monitor_vars));
if(monitor_vars_ptr == NULL) {
perror("malloc failed, exiting...");
exit(EXIT_FAILURE);
}
res = pthread_mutex_init(&monitor_vars_ptr->lock, NULL);
if(res != 0) {
perror("Mutex creation failed");
exit(EXIT_FAILURE);
}
res = pthread_cond_init(&monitor_vars_ptr->insufficient_weight, NULL);
if(res != 0) {
perror("Condition Variable creation failed");
exit(EXIT_FAILURE);
}
return monitor_vars_ptr;
}
void destroy_monitor(monitor_vars* monitor_vars_ptr) {
int res = 0;
res = pthread_mutex_destroy(&monitor_vars_ptr->lock);
if(res != 0) {
perror("Destroying mutex failed");
exit(EXIT_FAILURE);
}
res = pthread_cond_destroy(&monitor_vars_ptr->insufficient_weight);
if(res != 0) {
perror("Destroying condition variable failed");
exit(EXIT_FAILURE);
}
free(monitor_vars_ptr);
}
gym_monitor.h
#ifndef MONITOR_H
#define MONITOR_H
#include <pthread.h>
#include <stdio.h>
#include <semaphore.h>
#define REST 'R'
#define WORKOUT 'W'
#define GET_WEIGHTS 'G'
#define PUT_WEIGHTS 'P'
struct Student;
typedef struct {
pthread_mutex_t lock;
pthread_cond_t insufficient_weight;
}monitor_vars;
/*
* Manages the weight distribution among the students.
* Students enter the function, lock the mutex and try to obtain
* their corresponding weight. If the weight is not obtainable
* the student is waiting on a condition variable. Otherwise the
* student picks the weight and unlocks the mutex.
*
* parameter - the student
* return - none
*/
void get_weights(struct Student* stud);
/*
* Coordinates the transfer of weights from students back to the
* weight rack inside a monitor.
*
* parameter - the student
* return - none
*/
void put_weights(struct Student* stud);
/*
* Student waits on a semaphore
*
* parameter - the student
* return - none
*/
void rest_student(struct Student* stud);
/*
* Student waiting on a semaphore is unblocked
*
* parameter - the student
* return - none
*/
void wake_student(struct Student* stud);
/*
* Creates, initializes and returns a struct of monitor_vars.
* The struct contains a mutex and a condition variable.
* Exits the program if either memory allocation or initializing fails.
*
* parameter - none
* return - the mutex and condition variable inside a monitor_vars struct
*/
monitor_vars* init_monitor();
/*
* Destroys the supplied monitor struct.
* Exits the program if destroying the monitor fails.
*
* parameter - the monitor struct to be destroyed
* return - none
*/
void destroy_monitor(monitor_vars* mon);
/*
* Creates, initializes and returns a semaphore.
* Exits the program if either memory allocation or initializing fails.
*
* parameter - none
* return - the initialized semaphore
*/
sem_t* init_sem_student();
/*
* Destroys the semaphore of the supplied student.
* Exits the program if destroying the semaphore fails.
*
* parameter - the student
* return - none
*/
void destroy_sem_student(struct Student* student);
#endif