-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.c
444 lines (365 loc) · 12.8 KB
/
main.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
#include <pthread.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <semaphore.h>
#define BUFFER_SIZE 5
#define MAX_THREADS 32768
void *producer(void *param);
void *consumer(void *param);
/* Shared variables */
int maxSleepTime = 0;
int buffer[BUFFER_SIZE] = {-1, -1, -1, -1, -1};
int in = 0;
int out = 0;
/* Thread stats */
int totalProduced = 0;
int totalConsumed = 0;
int producePerThread[MAX_THREADS];
int consumePerThread[MAX_THREADS];
/* Shared flags */
int exitFlag = 0;
int showBufferStats = 0;
/* Misc stats */
int timesFull = 0;
int timesEmpty = 0;
pthread_mutex_t mutex;
sem_t empty;
sem_t full;
//********************************************************************
//
// Get Buffers Occupied
//
// This function returns the amount of buffers occupied when the index
// is not equal to -1.
//
// Return Value
// ------------
// int Amount of occupied buffers by a number not -1
//*******************************************************************
int get_buffers_occupied() {
int buffersOccupied = 0;
for (int i = 0; i < BUFFER_SIZE; i++)
{
if (buffer[i] != -1)
{
buffersOccupied++;
}
}
return buffersOccupied;
}
//********************************************************************
//
// Is Buffer Full
//
// This function determines if the buffer is full by comparing the amount of occupied
// buffers to the specified BUFFER_SIZE.
//
//
// Return Value
// ------------
// int Returns 1 if the buffer is full and 0 if the buffer has spaces avaliable
//*******************************************************************
int is_buffer_full() {
if (get_buffers_occupied() == BUFFER_SIZE) {
return 1;
}
return 0;
}
//********************************************************************
//
// Is Buffer Empty
//
// This function determines if the buffer is empty by comparing the amount of occupied
// buffers to zero.
//
//
// Return Value
// ------------
// int Returns 1 if the buffer has zero occupied buffers and 0 if the buffer has more than zero occupied buffers
//*******************************************************************
int is_buffer_empty() {
if (get_buffers_occupied() == 0) {
return 1;
}
return 0;
}
//********************************************************************
//
// Buffer Insert Item
//
// This function will take a value and insert it into the buffer and increment
// the amount produced for the specified thread id and the global total produced amount.
//
//
// Value Parameters
// ------------
// tid int The thread index
// nextProduced int The generated value to store into the buffer from the producer runner
//*******************************************************************
void buffer_insert_item(int tid, int nextProduced) {
buffer[in] = nextProduced;
in = (in + 1) % BUFFER_SIZE;
producePerThread[tid]++;
totalProduced++;
}
//********************************************************************
//
// Buffer Remove Item
//
// This function will remove a value from the buffer and increment the amount consumed
// for the specified thread id and the global total consumed amount.
//
//
// Value Parameters
// ----------------
// tid int The thread index
// nextConsumed int The value to consume and remove from the buffer provided by the consumer runner
//*******************************************************************
void buffer_remove_item(int tid, int nextConsumed) {
buffer[out] = -1;
out = (out + 1) % BUFFER_SIZE;
consumePerThread[tid]++;
totalConsumed++;
}
//********************************************************************
//
// Print Buffer
//
// This function will print the amount of occupied buffers and the
// current buffer and all of it's indexes and where the next write
// and read is going to occur.
//*******************************************************************
void print_buffer() {
printf("(buffers occupied: %d)", get_buffers_occupied());
printf("\nbuffers: ");
int i;
for(i = 0; i < BUFFER_SIZE; i++)
{
printf("%d ", buffer[i]);
}
printf("\n");
printf(" ---- ---- ---- ---- ----\n");
printf(" ");
for(i = 0; i < BUFFER_SIZE; i++)
{
if (in == i && out == i) {
printf("WR ");
} else if (in == i) {
printf("W ");
} else if (out == i) {
printf("R ");
} else {
printf(" ");
}
}
printf("\n\n");
}
//********************************************************************
//
// Print Simulation Stats
//
// This function prints the entire calculated simulation stats which
// includes the simulation time, max sleep time, number of producer and consumer threads,
// the size of buffer, per thread statistics for produce/consume amount, and number of times the buffer was empty/full.
//*******************************************************************
void print_simulation_stats(int simTime, int producerThreads, int consumerThreads) {
printf("\n\nPRODUCER / CONSUMER SIMULATION COMPLETE\n");
printf("=======================================\n");
printf("Simulation Time: %d\n", simTime);
printf("Maximum Thread Sleep Time: %d\n", maxSleepTime);
printf("Number of Producer Threads: %d\n", producerThreads);
printf("Number of Consumer Threads: %d\n", consumerThreads);
printf("Size of Buffer: %d\n\n", BUFFER_SIZE);
printf("Total Number of Items Produced: %d\n", totalProduced);
for (int i = 0; i < producerThreads; i++) {
printf(" Thread %d: %d\n", i, producePerThread[i]);
}
printf("\n\n");
printf("Total Number of Items Consumed: %d\n", totalConsumed);
for (int i = 0; i < consumerThreads; i++) {
printf(" Thread %d: %d\n", i, consumePerThread[i]);
}
printf("\n\n");
printf("Number Of Items Remaining in Buffer: %d\n", get_buffers_occupied());
printf("Number Of Times Buffer Was Full: %d\n", timesFull);
printf("Number Of Times Buffer Was Empty: %d\n", timesEmpty);
}
//********************************************************************
//
// Producer Runner
//
// This function is the runner for the producer threads that creates an indefinite while loop
// with a parameter that contains the current thread id. This function will sleep for a random amount of time
// based on the maxSleepTime variable and then creates a random number betwen 0 and 99 and stores it in the buffer if a slot
// in the buffer is avaliable and the mutex lock is free.
//
// Reference Parameters
// --------------------
// param int The thread index that was made at the creation of the thread.
//
// Local Variables
// ------------
// int tid An integer containing the thread index id that was made at the creation of the thread.
// int timeToSleep An integer that is a random number between 0 and the maxSleepTime global variable which is used for sleeping the thread.
// int nextProduced An integer generated between 0 and 99 that is stored in the buffer.
//*******************************************************************
void *producer(void *param)
{
while(1 && !exitFlag)
{
int tid = (int)(intptr_t)param;
int timeToSleep = rand() % maxSleepTime;
sleep(timeToSleep);
int nextProduced = rand() % 100;
/* If the buffer is full, increment timesFull for simulation stats.*/
if (is_buffer_full()) {
if (showBufferStats) {
printf("All buffers full. Producer is waiting.\n\n");
}
timesFull++;
}
sem_wait(&empty);
pthread_mutex_lock(&mutex);
/* Critical Section */
buffer_insert_item(tid, nextProduced);
if (showBufferStats) {
printf("Producer %d writes %d\n", tid, nextProduced);
/* Print the current buffer */
print_buffer();
}
pthread_mutex_unlock( &mutex );
sem_post(&full);
}
pthread_exit(0);
}
//********************************************************************
//
// Consumer Runner
//
// This function is the runner for the consumer threads that creates an indefinite while loop
// with a parameter that contains the current thread id. This function will sleep for a random amount of time
// based on the maxSleepTime variable and then take a value out of the buffer and read it, determine if the value is prime and consume it if the buffer is currently full
// and the mutex lock is free.
//
// Reference Parameters
// --------------------
// param int The thread index id that was made at the creation of the thread.
//
// Local Variables
// ------------
// int tid An integer containing the thread index id that was made at the creation of the thread.
// int timeToSleep An integer that is a random number between 0 and the maxSleepTime global variable which is used for sleeping the thread.
// int nextConsumed An integer that is read and consumed from the buffer
//*******************************************************************
void *consumer(void *param)
{
while(1 && !exitFlag)
{
int tid = (int)(intptr_t)param;
int timeToSleep = rand() % maxSleepTime;
sleep(timeToSleep);
if (is_buffer_empty()) {
if (showBufferStats) {
printf("All buffers empty. Consumer is waiting.\n\n");
}
timesEmpty++;
}
sem_wait(&full);
pthread_mutex_lock(&mutex);
int nextConsumed = buffer[out];
buffer_remove_item(tid, nextConsumed);
if (showBufferStats) {
// Check if the nextConsumed number is prime.
if (nextConsumed % 2 == 0 || nextConsumed % 3 == 0) {
printf("Consumer %d reads %d\n", tid, nextConsumed);
} else {
printf("Consumer %d reads %d * * * PRIME * * *\n", tid, nextConsumed);
}
/* Print the current buffer */
print_buffer();
}
pthread_mutex_unlock(&mutex);
sem_post(&empty);
}
pthread_exit(0);
}
//********************************************************************
//
// Main function
//
// This function is the main body of the process and handles obtaining all of the command line parameters, creating the semaphore/mutex locks,
// creating the producer and consumer threads based on the input amount, then sleeping for the specified simulatiom time
// and afterwards joining all of the threads together and printing the simulation's stats.
//
// Value Parameters
// ----------------
// argc int The amount of parameters passed to the process.
//
// Reference Parameters
// --------------------
// argv char An array of all of the passed parameters to the process.
//
// Local Variables
// ------------
// int simTime An integer containing the duration that the simulation should run before exiting the program.
// int numProducerThreads An integer that is the number of Producer threads that will be created.
// int numConsumerThreads An integer that is the number of Consumer threads that will be created.
// pthread_t pNPThreads A datatype that contains all of the identifiers for the created Producer threads.
// pthread_t pNCThreads A datatype that contains all of the identifiers for the created Consumer threads.
// pthread_attr_t attr A struct that is used to determine each of the threads attributes at creation.
//*******************************************************************
int main(int argc, char *argv[])
{
if (argc < 6) {
printf("Simulation usage: <simTime> <maxSleepTime> <numProducers> <numConsumers> <showIndividualBufferStats>\n");
exit(1);
}
int simTime = atoi(argv[1]);
maxSleepTime = atoi(argv[2]);
int numProducerThreads = atoi(argv[3]);
int numConsumerThreads = atoi(argv[4]);
/* Enable individual buffer snapshots if the last argument is "yes" */
if (strcmp(argv[5], "yes") == 0) {
showBufferStats = 1;
}
/* local variables */
pthread_t pNPThreads[numProducerThreads], pNCThreads[numConsumerThreads];
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_mutex_init(&mutex, NULL); /* create the mutex lock */
sem_init( &empty, 0, 5 );
sem_init( &full, 0, 0 );
printf("Starting threads...\n");
/* Show the initial buffer and the number of occupied threads at the start of the simulation. */
if (showBufferStats) {
print_buffer();
}
for (int i = 0; i < numProducerThreads; i++)
{
pthread_create(&pNPThreads[i], &attr, producer, (void*)(intptr_t)i);
}
for (int i = 0; i < numConsumerThreads; i++)
{
pthread_create(&pNCThreads[i], &attr, consumer, (void*)(intptr_t)i);
}
sleep(simTime);
/* Signal all threads to end their while loop and exit. */
exitFlag = 1;
for (int i = 0; i < numProducerThreads; i++)
{
pthread_join(pNPThreads[i], NULL);
}
for (int i = 0; i < numConsumerThreads; i++)
{
pthread_join(pNCThreads[i], NULL);
}
sem_destroy(&empty);
sem_destroy(&full);
pthread_mutex_destroy(&mutex);
/* Simulation complete */
print_simulation_stats(simTime, numProducerThreads, numConsumerThreads);
return 0;
}