|
@@ -1,145 +1,151 @@
|
|
| 1 |
// Copyright 2021 Jeisson Hidalgo-Cespedes <jeisson.hidalgo@ucr.ac.cr> CC-BY-4
|
| 2 |
|
| 3 |
#include <assert.h>
|
| 4 |
#include <errno.h>
|
| 5 |
#include <pthread.h>
|
| 6 |
#include <stdlib.h>
|
| 7 |
#include <sys/random.h>
|
| 8 |
#include <stdio.h>
|
| 9 |
|
| 10 |
#include "common.h"
|
| 11 |
#include "consumer.h"
|
| 12 |
#include "producer.h"
|
| 13 |
#include "simulation.h"
|
| 14 |
|
| 15 |
int analyze_arguments(simulation_t* simulation, int argc, char* argv[]);
|
| 16 |
int create_consumers_producers(simulation_t* simulation);
|
| 17 |
int join_threads(size_t count, pthread_t* threads);
|
| 18 |
|
| 19 |
simulation_t* simulation_create() {
|
| 20 |
simulation_t* simulation = (simulation_t*) calloc(1, sizeof(simulation_t));
|
| 21 |
if (simulation) {
|
| 22 |
simulation->unit_count = 0;
|
| 23 |
simulation->producer_count = 0;
|
| 24 |
simulation->consumer_count = 0;
|
| 25 |
simulation->producer_min_delay = 0;
|
| 26 |
simulation->producer_max_delay = 0;
|
| 27 |
simulation->consumer_min_delay = 0;
|
| 28 |
simulation->consumer_max_delay = 0;
|
| 29 |
queue_init(&simulation->queue);
|
|
|
|
| 30 |
simulation->next_unit = 0;
|
|
|
|
|
|
|
| 31 |
simulation->consumed_count = 0;
|
| 32 |
}
|
| 33 |
return simulation;
|
| 34 |
}
|
| 35 |
|
| 36 |
void simulation_destroy(simulation_t* simulation) {
|
| 37 |
assert(simulation);
|
|
|
|
|
|
|
|
|
|
| 38 |
queue_destroy(&simulation->queue);
|
| 39 |
free(simulation);
|
| 40 |
}
|
| 41 |
|
| 42 |
int simulation_run(simulation_t* simulation, int argc, char* argv[]) {
|
| 43 |
int error = analyze_arguments(simulation, argc, argv);
|
| 44 |
if (error == EXIT_SUCCESS) {
|
| 45 |
unsigned int seed = 0;
|
| 46 |
getrandom(&seed, sizeof(seed), GRND_NONBLOCK);
|
| 47 |
srandom(seed);
|
| 48 |
|
| 49 |
struct timespec start_time;
|
| 50 |
clock_gettime(/*clk_id*/CLOCK_MONOTONIC, &start_time);
|
| 51 |
|
| 52 |
error = create_consumers_producers(simulation);
|
| 53 |
|
| 54 |
struct timespec finish_time;
|
| 55 |
clock_gettime(/*clk_id*/CLOCK_MONOTONIC, &finish_time);
|
| 56 |
|
| 57 |
double elapsed = (finish_time.tv_sec - start_time.tv_sec) +
|
| 58 |
(finish_time.tv_nsec - start_time.tv_nsec) * 1e-9;
|
| 59 |
printf("execution time: %.9lfs\n", elapsed);
|
| 60 |
}
|
| 61 |
return error;
|
| 62 |
}
|
| 63 |
|
| 64 |
int analyze_arguments(simulation_t* simulation, int argc, char* argv[]) {
|
| 65 |
int error = EXIT_SUCCESS;
|
| 66 |
if (argc == 8) {
|
| 67 |
if (sscanf(argv[1], "%zu", &simulation->unit_count) != 1
|
| 68 |
|| simulation->unit_count == 0) {
|
| 69 |
fprintf(stderr, "error: invalid unit count\n");
|
| 70 |
error = ERR_UNIT_COUNT;
|
| 71 |
} else if (sscanf(argv[2], "%zu", &simulation->producer_count) != 1
|
| 72 |
|| simulation->producer_count == 0) {
|
| 73 |
fprintf(stderr, "error: invalid producer count\n");
|
| 74 |
error = ERR_PRODUCER_COUNT;
|
| 75 |
} else if (sscanf(argv[3], "%zu", &simulation->consumer_count) != 1
|
| 76 |
|| simulation->consumer_count == 0) {
|
| 77 |
fprintf(stderr, "error: invalid consumer count\n");
|
| 78 |
error = ERR_CONSUMER_COUNT;
|
| 79 |
} else if (sscanf(argv[4], "%u", &simulation->producer_min_delay) != 1) {
|
| 80 |
fprintf(stderr, "error: invalid min producer delay\n");
|
| 81 |
error = ERR_MIN_PROD_DELAY;
|
| 82 |
} else if (sscanf(argv[5], "%u", &simulation->producer_max_delay) != 1) {
|
| 83 |
fprintf(stderr, "error: invalid max producer delay\n");
|
| 84 |
error = ERR_MAX_PROD_DELAY;
|
| 85 |
} else if (sscanf(argv[6], "%u", &simulation->consumer_min_delay) != 1) {
|
| 86 |
fprintf(stderr, "error: invalid min consumer delay\n");
|
| 87 |
error = ERR_MIN_CONS_DELAY;
|
| 88 |
} else if (sscanf(argv[7], "%u", &simulation->consumer_max_delay) != 1) {
|
| 89 |
fprintf(stderr, "error: invalid max consumer delay\n");
|
| 90 |
error = ERR_MAX_CONS_DELAY;
|
| 91 |
}
|
| 92 |
} else {
|
| 93 |
fprintf(stderr, "usage: producer_consumer buffer_capacity rounds"
|
| 94 |
" producer_min_delay producer_max_delay"
|
| 95 |
" consumer_min_delay consumer_max_delay\n");
|
| 96 |
error = ERR_NO_ARGS;
|
| 97 |
}
|
| 98 |
return error;
|
| 99 |
}
|
| 100 |
|
| 101 |
pthread_t* create_threads(size_t count, void*(*subroutine)(void*), void* data) {
|
| 102 |
pthread_t* threads = (pthread_t*) calloc(count, sizeof(pthread_t));
|
| 103 |
if (threads) {
|
| 104 |
for (size_t index = 0; index < count; ++index) {
|
| 105 |
if (pthread_create(&threads[index], /*attr*/ NULL, subroutine, data)
|
| 106 |
== EXIT_SUCCESS) {
|
| 107 |
} else {
|
| 108 |
fprintf(stderr, "error: could not create thread %zu\n", index);
|
| 109 |
join_threads(index, threads);
|
| 110 |
return NULL;
|
| 111 |
}
|
| 112 |
}
|
| 113 |
}
|
| 114 |
return threads;
|
| 115 |
}
|
| 116 |
|
| 117 |
int join_threads(size_t count, pthread_t* threads) {
|
| 118 |
int error = EXIT_SUCCESS;
|
| 119 |
for (size_t index = 0; index < count; ++index) {
|
| 120 |
// todo: sum could not be right
|
| 121 |
error += pthread_join(threads[index], /*value_ptr*/ NULL);
|
| 122 |
}
|
| 123 |
free(threads);
|
| 124 |
return error;
|
| 125 |
}
|
| 126 |
|
| 127 |
int create_consumers_producers(simulation_t* simulation) {
|
| 128 |
assert(simulation);
|
| 129 |
int error = EXIT_SUCCESS;
|
| 130 |
|
| 131 |
pthread_t* producers = create_threads(simulation->producer_count, produce
|
| 132 |
, simulation);
|
| 133 |
pthread_t* consumers = create_threads(simulation->consumer_count, consume
|
| 134 |
, simulation);
|
| 135 |
|
| 136 |
if (producers && consumers) {
|
| 137 |
join_threads(simulation->producer_count, producers);
|
| 138 |
join_threads(simulation->consumer_count, consumers);
|
| 139 |
} else {
|
| 140 |
fprintf(stderr, "error: could not create threads\n");
|
| 141 |
error = ERR_CREATE_THREAD;
|
| 142 |
}
|
| 143 |
|
| 144 |
return error;
|
| 145 |
}
|
| 1 |
// Copyright 2021 Jeisson Hidalgo-Cespedes <jeisson.hidalgo@ucr.ac.cr> CC-BY-4
|
| 2 |
|
| 3 |
#include <assert.h>
|
| 4 |
#include <errno.h>
|
| 5 |
#include <pthread.h>
|
| 6 |
#include <stdlib.h>
|
| 7 |
#include <sys/random.h>
|
| 8 |
#include <stdio.h>
|
| 9 |
|
| 10 |
#include "common.h"
|
| 11 |
#include "consumer.h"
|
| 12 |
#include "producer.h"
|
| 13 |
#include "simulation.h"
|
| 14 |
|
| 15 |
int analyze_arguments(simulation_t* simulation, int argc, char* argv[]);
|
| 16 |
int create_consumers_producers(simulation_t* simulation);
|
| 17 |
int join_threads(size_t count, pthread_t* threads);
|
| 18 |
|
| 19 |
simulation_t* simulation_create() {
|
| 20 |
simulation_t* simulation = (simulation_t*) calloc(1, sizeof(simulation_t));
|
| 21 |
if (simulation) {
|
| 22 |
simulation->unit_count = 0;
|
| 23 |
simulation->producer_count = 0;
|
| 24 |
simulation->consumer_count = 0;
|
| 25 |
simulation->producer_min_delay = 0;
|
| 26 |
simulation->producer_max_delay = 0;
|
| 27 |
simulation->consumer_min_delay = 0;
|
| 28 |
simulation->consumer_max_delay = 0;
|
| 29 |
queue_init(&simulation->queue);
|
| 30 |
+
pthread_mutex_init(&simulation->can_access_next_unit, /* attr */ NULL);
|
| 31 |
simulation->next_unit = 0;
|
| 32 |
+
sem_init(&simulation->can_consume, /* pshared */ 0, /* value */ 0);
|
| 33 |
+
pthread_mutex_init(&simulation->can_access_consumed_count, /* attr */ NULL);
|
| 34 |
simulation->consumed_count = 0;
|
| 35 |
}
|
| 36 |
return simulation;
|
| 37 |
}
|
| 38 |
|
| 39 |
void simulation_destroy(simulation_t* simulation) {
|
| 40 |
assert(simulation);
|
| 41 |
+
pthread_mutex_destroy(&simulation->can_access_consumed_count);
|
| 42 |
+
sem_destroy(&simulation->can_consume);
|
| 43 |
+
pthread_mutex_destroy(&simulation->can_access_next_unit);
|
| 44 |
queue_destroy(&simulation->queue);
|
| 45 |
free(simulation);
|
| 46 |
}
|
| 47 |
|
| 48 |
int simulation_run(simulation_t* simulation, int argc, char* argv[]) {
|
| 49 |
int error = analyze_arguments(simulation, argc, argv);
|
| 50 |
if (error == EXIT_SUCCESS) {
|
| 51 |
unsigned int seed = 0;
|
| 52 |
getrandom(&seed, sizeof(seed), GRND_NONBLOCK);
|
| 53 |
srandom(seed);
|
| 54 |
|
| 55 |
struct timespec start_time;
|
| 56 |
clock_gettime(/*clk_id*/CLOCK_MONOTONIC, &start_time);
|
| 57 |
|
| 58 |
error = create_consumers_producers(simulation);
|
| 59 |
|
| 60 |
struct timespec finish_time;
|
| 61 |
clock_gettime(/*clk_id*/CLOCK_MONOTONIC, &finish_time);
|
| 62 |
|
| 63 |
double elapsed = (finish_time.tv_sec - start_time.tv_sec) +
|
| 64 |
(finish_time.tv_nsec - start_time.tv_nsec) * 1e-9;
|
| 65 |
printf("execution time: %.9lfs\n", elapsed);
|
| 66 |
}
|
| 67 |
return error;
|
| 68 |
}
|
| 69 |
|
| 70 |
int analyze_arguments(simulation_t* simulation, int argc, char* argv[]) {
|
| 71 |
int error = EXIT_SUCCESS;
|
| 72 |
if (argc == 8) {
|
| 73 |
if (sscanf(argv[1], "%zu", &simulation->unit_count) != 1
|
| 74 |
|| simulation->unit_count == 0) {
|
| 75 |
fprintf(stderr, "error: invalid unit count\n");
|
| 76 |
error = ERR_UNIT_COUNT;
|
| 77 |
} else if (sscanf(argv[2], "%zu", &simulation->producer_count) != 1
|
| 78 |
|| simulation->producer_count == 0) {
|
| 79 |
fprintf(stderr, "error: invalid producer count\n");
|
| 80 |
error = ERR_PRODUCER_COUNT;
|
| 81 |
} else if (sscanf(argv[3], "%zu", &simulation->consumer_count) != 1
|
| 82 |
|| simulation->consumer_count == 0) {
|
| 83 |
fprintf(stderr, "error: invalid consumer count\n");
|
| 84 |
error = ERR_CONSUMER_COUNT;
|
| 85 |
} else if (sscanf(argv[4], "%u", &simulation->producer_min_delay) != 1) {
|
| 86 |
fprintf(stderr, "error: invalid min producer delay\n");
|
| 87 |
error = ERR_MIN_PROD_DELAY;
|
| 88 |
} else if (sscanf(argv[5], "%u", &simulation->producer_max_delay) != 1) {
|
| 89 |
fprintf(stderr, "error: invalid max producer delay\n");
|
| 90 |
error = ERR_MAX_PROD_DELAY;
|
| 91 |
} else if (sscanf(argv[6], "%u", &simulation->consumer_min_delay) != 1) {
|
| 92 |
fprintf(stderr, "error: invalid min consumer delay\n");
|
| 93 |
error = ERR_MIN_CONS_DELAY;
|
| 94 |
} else if (sscanf(argv[7], "%u", &simulation->consumer_max_delay) != 1) {
|
| 95 |
fprintf(stderr, "error: invalid max consumer delay\n");
|
| 96 |
error = ERR_MAX_CONS_DELAY;
|
| 97 |
}
|
| 98 |
} else {
|
| 99 |
fprintf(stderr, "usage: producer_consumer buffer_capacity rounds"
|
| 100 |
" producer_min_delay producer_max_delay"
|
| 101 |
" consumer_min_delay consumer_max_delay\n");
|
| 102 |
error = ERR_NO_ARGS;
|
| 103 |
}
|
| 104 |
return error;
|
| 105 |
}
|
| 106 |
|
| 107 |
pthread_t* create_threads(size_t count, void*(*subroutine)(void*), void* data) {
|
| 108 |
pthread_t* threads = (pthread_t*) calloc(count, sizeof(pthread_t));
|
| 109 |
if (threads) {
|
| 110 |
for (size_t index = 0; index < count; ++index) {
|
| 111 |
if (pthread_create(&threads[index], /*attr*/ NULL, subroutine, data)
|
| 112 |
== EXIT_SUCCESS) {
|
| 113 |
} else {
|
| 114 |
fprintf(stderr, "error: could not create thread %zu\n", index);
|
| 115 |
join_threads(index, threads);
|
| 116 |
return NULL;
|
| 117 |
}
|
| 118 |
}
|
| 119 |
}
|
| 120 |
return threads;
|
| 121 |
}
|
| 122 |
|
| 123 |
int join_threads(size_t count, pthread_t* threads) {
|
| 124 |
int error = EXIT_SUCCESS;
|
| 125 |
for (size_t index = 0; index < count; ++index) {
|
| 126 |
// todo: sum could not be right
|
| 127 |
error += pthread_join(threads[index], /*value_ptr*/ NULL);
|
| 128 |
}
|
| 129 |
free(threads);
|
| 130 |
return error;
|
| 131 |
}
|
| 132 |
|
| 133 |
int create_consumers_producers(simulation_t* simulation) {
|
| 134 |
assert(simulation);
|
| 135 |
int error = EXIT_SUCCESS;
|
| 136 |
|
| 137 |
pthread_t* producers = create_threads(simulation->producer_count, produce
|
| 138 |
, simulation);
|
| 139 |
pthread_t* consumers = create_threads(simulation->consumer_count, consume
|
| 140 |
, simulation);
|
| 141 |
|
| 142 |
if (producers && consumers) {
|
| 143 |
join_threads(simulation->producer_count, producers);
|
| 144 |
join_threads(simulation->consumer_count, consumers);
|
| 145 |
} else {
|
| 146 |
fprintf(stderr, "error: could not create threads\n");
|
| 147 |
error = ERR_CREATE_THREAD;
|
| 148 |
}
|
| 149 |
|
| 150 |
return error;
|
| 151 |
}
|