mirror of
https://github.com/caperren/school_archives.git
synced 2025-11-09 13:41:13 +00:00
263 lines
7.3 KiB
C
263 lines
7.3 KiB
C
/*
|
|
* File: main.c
|
|
* Author: caperren
|
|
*
|
|
* Created on June 1st, 2018
|
|
*/
|
|
#define _POSIX_C_SOURCE 199309L
|
|
|
|
#include "mt19937ar.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdint.h>
|
|
#include <sys/param.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <pthread.h>
|
|
#include <semaphore.h>
|
|
#include <time.h>
|
|
|
|
// Program defines you might want to change
|
|
#define NUM_BARBERSHOP_WAITING_ROOM_CHAIRS 5
|
|
|
|
// Program defines that don't get changed
|
|
#define NUM_CUSTOMER_THREADS (NUM_BARBERSHOP_WAITING_ROOM_CHAIRS * 2)
|
|
|
|
#define MIN_RANDOM_WAIT_CUSTOMER_RESPAWN 4
|
|
#define MAX_RANDOM_WAIT_CUSTOMER_RESPAWN 8
|
|
|
|
#define MIN_RANDOM_HAIRCUT_TIME 2
|
|
#define MAX_RANDOM_HAIRCUT_TIME 4
|
|
|
|
//RDRAND value to check for
|
|
#define RDRAND_FLAG (1 << 30)
|
|
|
|
// Global Variables
|
|
sem_t waiting_room_chairs_semaphore;
|
|
sem_t ticket_dispenser_semaphore;
|
|
sem_t barber_chair_semaphore;
|
|
|
|
uint16_t current_ticket_number = 1;
|
|
uint16_t barber_servicing_ticket = 0;
|
|
|
|
uint8_t haircut_started = 0;
|
|
uint8_t haircut_over = 0;
|
|
|
|
struct timespec thread_sleep_time;
|
|
|
|
///////////////////////////////////////////////////////////////////
|
|
// Get random number based on whether rdrand is supported.
|
|
uint8_t get_random_number(uint8_t min, uint8_t max) {
|
|
uint8_t rand_val;
|
|
unsigned long seed;
|
|
|
|
unsigned int eax;
|
|
unsigned int ebx;
|
|
unsigned int ecx;
|
|
unsigned int edx;
|
|
|
|
eax = 0x01;
|
|
|
|
// ASM for checking if RDRAND supported
|
|
__asm__ __volatile__("cpuid"
|
|
: "=a" (eax),
|
|
"=b" (ebx),
|
|
"=c" (ecx),
|
|
"=d" (edx)
|
|
: "a" (1),
|
|
"c" (0)
|
|
);
|
|
|
|
// Seed random with /dev/random
|
|
int random = open("/dev/random", O_RDONLY);
|
|
read(random, &seed, sizeof (seed));
|
|
|
|
unsigned int random_value;
|
|
|
|
if ((ecx & RDRAND_FLAG) == RDRAND_FLAG) {
|
|
char return_code = 0;
|
|
char count = 0;
|
|
|
|
// ASM for RDRAND
|
|
while(return_code != 1 && count < 10){
|
|
__asm__ volatile(
|
|
"rdrand %0 ; setc %1"
|
|
: "=r" (random_value), "=qm" (return_code)
|
|
);
|
|
|
|
count++;
|
|
}
|
|
} else {
|
|
// Twister if RDRAND not supported
|
|
init_genrand(seed);
|
|
random_value = genrand_int32();
|
|
}
|
|
|
|
return (random_value % (max + 1 - min)) + min;
|
|
}
|
|
|
|
void get_hair_cut(uint16_t customer_thread_id, uint16_t ticket_id){
|
|
|
|
while(!haircut_started){
|
|
nanosleep(&thread_sleep_time, NULL);
|
|
}
|
|
|
|
printf("Customer heading to barbers chair with ticket number %u.\n", ticket_id);
|
|
fflush(stdout);
|
|
|
|
sem_wait(&barber_chair_semaphore);
|
|
|
|
printf("Customer with thread id %u and ticket number %u sat down and getting haircut.\n", customer_thread_id, ticket_id);
|
|
fflush(stdout);
|
|
|
|
while(!haircut_over){
|
|
nanosleep(&thread_sleep_time, NULL);
|
|
}
|
|
|
|
sem_post(&barber_chair_semaphore);
|
|
|
|
}
|
|
|
|
void cut_hair(){
|
|
uint16_t sleep_time = get_random_number(MIN_RANDOM_HAIRCUT_TIME, MAX_RANDOM_HAIRCUT_TIME);
|
|
int customer_in_chair = 1;
|
|
|
|
haircut_over = 0;
|
|
haircut_started = 1;
|
|
|
|
printf("Barber waiting for customer to sit down with ticket number %d.\n", barber_servicing_ticket);
|
|
fflush(stdout);
|
|
while(customer_in_chair > 0){
|
|
sem_getvalue(&barber_chair_semaphore, &customer_in_chair);
|
|
nanosleep(&thread_sleep_time, NULL);
|
|
|
|
}
|
|
|
|
printf("Barber giving haircut to ticket number %d for %d seconds.\n", barber_servicing_ticket, sleep_time);
|
|
fflush(stdout);
|
|
|
|
sleep(sleep_time);
|
|
|
|
haircut_started = 0;
|
|
haircut_over = 1;
|
|
|
|
while(customer_in_chair <= 0){
|
|
sem_getvalue(&barber_chair_semaphore, &customer_in_chair);
|
|
nanosleep(&thread_sleep_time, NULL);
|
|
}
|
|
|
|
printf("Barber finished haircut for ticket number %d.\n", barber_servicing_ticket);
|
|
fflush(stdout);
|
|
}
|
|
|
|
// If waiting room is empty, sleep until woken up by customer
|
|
// If customer, cut_hair
|
|
void *barber(){
|
|
printf("Barber thread started.\n");
|
|
fflush(stdout);
|
|
|
|
int used_waiting_room_chairs = 0;
|
|
|
|
while(1){
|
|
// Check if anyone is in the waiting room
|
|
sem_getvalue(&waiting_room_chairs_semaphore, &used_waiting_room_chairs);
|
|
|
|
if(used_waiting_room_chairs == 5){
|
|
nanosleep(&thread_sleep_time, NULL);
|
|
continue;
|
|
}
|
|
|
|
// Tell customers next ticket number
|
|
barber_servicing_ticket++;
|
|
|
|
// Service the customer
|
|
cut_hair();
|
|
}
|
|
}
|
|
|
|
// If the waiting room is full, customer leaves
|
|
// If waiting room has empty chair, sits in chair, unless barber is ready
|
|
// When ready, sits in chair and runs get_hair_cut, then leaves
|
|
void *customer(void *thread_id_number){
|
|
uint16_t thread_id = *((uint16_t *)thread_id_number);
|
|
|
|
printf("Customer thread with id %d started.\n", thread_id);
|
|
fflush(stdout);
|
|
|
|
int used_waiting_room_chairs = 0;
|
|
uint16_t customer_ticket_number = 0;
|
|
|
|
while(1){
|
|
// Wait before deciding to enter store
|
|
uint16_t sleep_time = get_random_number(MIN_RANDOM_WAIT_CUSTOMER_RESPAWN, MAX_RANDOM_WAIT_CUSTOMER_RESPAWN);
|
|
sleep(sleep_time);
|
|
|
|
// Check if seats are full, exit store if full
|
|
sem_getvalue(&waiting_room_chairs_semaphore, &used_waiting_room_chairs);
|
|
|
|
if(used_waiting_room_chairs == 0){
|
|
printf("Customer thread with id %d entered store, but was full. Leaving.\n", thread_id);
|
|
fflush(stdout);
|
|
continue;
|
|
}
|
|
|
|
// There's space in the waiting room, take a seat and a number
|
|
sem_wait(&waiting_room_chairs_semaphore);
|
|
sem_wait(&ticket_dispenser_semaphore);
|
|
customer_ticket_number = current_ticket_number;
|
|
current_ticket_number++;
|
|
sem_post(&ticket_dispenser_semaphore);
|
|
|
|
printf("Customer thread with id %d has ticket number %u.\n", thread_id, current_ticket_number);
|
|
fflush(stdout);
|
|
|
|
// Wait until our number is called
|
|
while(barber_servicing_ticket != customer_ticket_number){
|
|
nanosleep(&thread_sleep_time, NULL);
|
|
}
|
|
|
|
// It's time for a haircut, give up our seat in the waiting room
|
|
sem_post(&waiting_room_chairs_semaphore);
|
|
|
|
// Get a haircut
|
|
get_hair_cut(thread_id, customer_ticket_number);
|
|
}
|
|
}
|
|
|
|
int main(int argc, char **argv) {
|
|
// Setup thread sleep variable
|
|
thread_sleep_time.tv_nsec = 10000000;
|
|
thread_sleep_time.tv_sec = 0;
|
|
|
|
// Storage for threads
|
|
pthread_t barber_thread;
|
|
pthread_t potential_customers_threads[NUM_CUSTOMER_THREADS];
|
|
uint16_t customers_thread_ids[NUM_CUSTOMER_THREADS];
|
|
|
|
|
|
// Initialize semaphores
|
|
sem_init(&waiting_room_chairs_semaphore, 1, NUM_BARBERSHOP_WAITING_ROOM_CHAIRS);
|
|
sem_init(&ticket_dispenser_semaphore, 1, 1);
|
|
sem_init(&barber_chair_semaphore, 1, 1);
|
|
|
|
// Start barber thread
|
|
pthread_create(&barber_thread, NULL, barber, NULL);
|
|
|
|
// Start potential custom threads
|
|
for(uint16_t i = 0 ; i < NUM_CUSTOMER_THREADS ; i++){
|
|
customers_thread_ids[i] = i;
|
|
pthread_create(&potential_customers_threads[i], NULL, customer, &customers_thread_ids[i]);
|
|
}
|
|
|
|
// Wait for barber thread to finish
|
|
pthread_join(barber_thread, NULL);
|
|
|
|
// Wait for customer threads to finish
|
|
for(uint8_t i = 0 ; i < NUM_CUSTOMER_THREADS ; i++){
|
|
pthread_join(potential_customers_threads[i], NULL);
|
|
}
|
|
}
|
|
|