We're preparing for an exam at the moment, and our lecturer has given us a sample problem to work on. I have it completed, but would like to know a) If it is actually doing what it's supposed to, and b) if it could be done more efficiently or there are any questionable coding aspects. It certainly seems to be working, and GDB tells me the extra threads are definitely bring created.
The question:
Implement a multithreaded car park simulator in C. One thread should move cars into the car park and another thread should take cars out of the car park (these steps can be simulated by simply inserting integers into/removing integers from a buffer).
The capacity of the car park should be supplied as a command line parameter to your program.
A monitor thread should periodically print out the number of cars currently in the car park.
Requirements:
- Implement mutual exclusion where appropriate
- Do not remove cars from an empty car park
- Do not add cars to a full car park
- Avoid busy-waiting
- Have each producer/consumer thread pause for a random period (up to 1s) between inserting/removing a car
- Have the monitor thread periodically print out the number of cars currently in the car park
Part 2 - Add one additional producer thread and one additional consumer thread to your solution to Q1. Use
pthread_barrier_init
andpthread_barrier_wait
to ensure that all producer/consumer threads begin producing/consuming at the same time.
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#define ONE_SECOND 1000000
#define RANGE 10
#define PERIOD 2
#define NUM_THREADS 4
typedef struct {
int *carpark;
int capacity;
int occupied;
int nextin;
int nextout;
int cars_in;
int cars_out;
pthread_mutex_t lock;
pthread_cond_t space;
pthread_cond_t car;
pthread_barrier_t bar;
} cp_t;
static void * car_in_handler(void *cp_in);
static void * car_out_handler(void *cp_in);
static void * monitor(void *cp_in);
static void initialise(cp_t *cp, int size);
int main(int argc, char *argv[]) {
if (argc != 2) {
printf("Usage: %s carparksize\n", argv[0]);
exit(1);
}
cp_t ourpark;
initialise(&ourpark, atoi(argv[1]));
pthread_t car_in, car_out, m;
pthread_t car_in2, car_out2;
pthread_create(&car_in, NULL, car_in_handler, (void *) &ourpark);
pthread_create(&car_out, NULL, car_out_handler, (void *) &ourpark);
pthread_create(&car_in2, NULL, car_in_handler, (void *) &ourpark);
pthread_create(&car_out2, NULL, car_out_handler, (void *) &ourpark);
pthread_create(&m, NULL, monitor, (void *) &ourpark);
pthread_join(car_in, NULL);
pthread_join(car_out, NULL);
pthread_join(car_in2, NULL);
pthread_join(car_out2, NULL);
pthread_join(m, NULL);
exit(0);
}
static void initialise(cp_t *cp, int size) {
cp->occupied = cp->nextin = cp->nextout = cp->cars_in = cp->cars_out = 0;
cp->capacity = size;
cp->carpark = (int *)malloc(cp->capacity * sizeof(*cp->carpark));
pthread_barrier_init(&cp->bar, NULL, NUM_THREADS);
if(cp->carpark == NULL) {
perror("malloc()");
exit(1);
}
srand((unsigned int)getpid());
pthread_mutex_init(&cp->lock, NULL);
pthread_cond_init(&cp->space, NULL);
pthread_cond_init(&cp->car, NULL);
}
static void* car_in_handler(void *carpark_in) {
cp_t *temp;
unsigned int seed;
temp = (cp_t *)carpark_in;
pthread_barrier_wait(&temp->bar);
while(1) {
usleep(rand_r(&seed) % ONE_SECOND);
pthread_mutex_lock(&temp->lock);
//while full wait until there is room available
while (temp->occupied == temp->capacity)
pthread_cond_wait(&temp->space, &temp->lock);
//insert an item
temp->carpark[temp->nextin] = rand_r(&seed) % RANGE;
//increment counters
temp->occupied++;
temp->nextin++;
temp->nextin %= temp->capacity; //circular buffer here then
temp->cars_in++;
//someone may be waiting on data to become available
pthread_cond_signal(&temp->car);
//release the lock
pthread_mutex_unlock(&temp->lock);
}
return ((void *)NULL);
}
static void* car_out_handler(void *carpark_out) {
cp_t *temp;
unsigned int seed;
temp = (cp_t *)carpark_out;
pthread_barrier_wait(&temp->bar);
for(; ;) {
usleep(rand_r(&seed) % ONE_SECOND);
//acquire the lock
pthread_mutex_lock(&temp->lock);
while(temp->occupied == 0)
pthread_cond_wait(&temp->car, &temp->lock);
//increment counters
temp->occupied--;
temp->nextout++;
temp->nextout %= temp->capacity;
temp->cars_out++;
//somebody may be waiting on toom to become available
pthread_cond_signal(&temp->space);
//release the locl
pthread_mutex_unlock(&temp->lock);
}
return ((void *)NULL);
}
static void *monitor(void *carpark_in) {
cp_t *temp;
temp = (cp_t *)carpark_in;
for(; ;) {
sleep(PERIOD);
//acquire the lock
pthread_mutex_lock(&temp->lock);
printf("Delta: %d\n", temp->cars_in - temp->cars_out - temp->occupied);
printf("Number of cars in carpark: %d\n", temp->occupied);
//release the lock
pthread_mutex_unlock(&temp->lock);
}
return ((void *)NULL);
}