177 lines
4.2 KiB
C
177 lines
4.2 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <pthread.h>
|
|
#include <semaphore.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
|
|
#define POISON -1
|
|
|
|
typedef struct {
|
|
int *buf;
|
|
int n;
|
|
int outpos;
|
|
int inpos;
|
|
sem_t mutex;
|
|
sem_t slots;
|
|
sem_t items;
|
|
} sbuf_t;
|
|
|
|
long long produced_sum = 0;
|
|
long long consumed_sum = 0;
|
|
sem_t sum_mutex;
|
|
int consumed_count = 0;
|
|
int total_items;
|
|
sem_t count_mutex;
|
|
|
|
void sbuf_init(sbuf_t *sp, int n) {
|
|
sp->buf = (int *)malloc(n * sizeof(int));
|
|
sp->n = n;
|
|
sp->outpos = 0;
|
|
sp->inpos = 0;
|
|
sem_init(&sp->mutex, 0, 1);
|
|
sem_init(&sp->slots, 0, n);
|
|
sem_init(&sp->items, 0, 0);
|
|
}
|
|
|
|
void sbuf_deinit(sbuf_t *sp) {
|
|
free(sp->buf);
|
|
sem_destroy(&sp->mutex);
|
|
sem_destroy(&sp->slots);
|
|
sem_destroy(&sp->items);
|
|
}
|
|
|
|
void sbuf_insert(sbuf_t *sp, int item) {
|
|
sem_wait(&sp->slots);
|
|
sem_wait(&sp->mutex);
|
|
sp->buf[sp->inpos] = item;
|
|
sp->inpos = (sp->inpos + 1) % sp->n;
|
|
sem_post(&sp->mutex);
|
|
sem_post(&sp->items);
|
|
}
|
|
|
|
int sbuf_remove(sbuf_t *sp) {
|
|
sem_wait(&sp->items);
|
|
sem_wait(&sp->mutex);
|
|
int item = sp->buf[sp->outpos];
|
|
sp->outpos = (sp->outpos + 1) % sp->n;
|
|
sem_post(&sp->mutex);
|
|
sem_post(&sp->slots);
|
|
return item;
|
|
}
|
|
|
|
typedef struct {
|
|
sbuf_t *sp;
|
|
int num_items;
|
|
int id;
|
|
} producer_arg_t;
|
|
|
|
typedef struct {
|
|
sbuf_t *sp;
|
|
int id;
|
|
} consumer_arg_t;
|
|
|
|
void *producer(void *arg) {
|
|
producer_arg_t *pa = (producer_arg_t *)arg;
|
|
for (int i = 0; i < pa->num_items; i++) {
|
|
int val = rand() % 1000;
|
|
sbuf_insert(pa->sp, val);
|
|
sem_wait(&sum_mutex);
|
|
produced_sum += val;
|
|
sem_post(&sum_mutex);
|
|
printf("[Producer %d] produced %d\n", pa->id, val);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void *consumer(void *arg) {
|
|
consumer_arg_t *ca = (consumer_arg_t *)arg;
|
|
while (1) {
|
|
int item = sbuf_remove(ca->sp);
|
|
if (item == POISON) {
|
|
/* Put poison back for other consumers, then exit */
|
|
sbuf_insert(ca->sp, POISON);
|
|
break;
|
|
}
|
|
sem_wait(&sum_mutex);
|
|
consumed_sum += item;
|
|
sem_post(&sum_mutex);
|
|
sem_wait(&count_mutex);
|
|
consumed_count++;
|
|
sem_post(&count_mutex);
|
|
printf("[Consumer %d] consumed %d\n", ca->id, item);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
int main(int argc, char *argv[]) {
|
|
if (argc != 5) {
|
|
fprintf(stderr, "Usage: %s <k_producers> <items_per_producer> <m_consumers> <N_buffer>\n", argv[0]);
|
|
return 1;
|
|
}
|
|
|
|
int k = atoi(argv[1]);
|
|
int items_per_producer = atoi(argv[2]);
|
|
int m = atoi(argv[3]);
|
|
int N = atoi(argv[4]);
|
|
total_items = k * items_per_producer;
|
|
|
|
srand(time(NULL));
|
|
sem_init(&sum_mutex, 0, 1);
|
|
sem_init(&count_mutex, 0, 1);
|
|
|
|
sbuf_t buf;
|
|
sbuf_init(&buf, N);
|
|
|
|
pthread_t *producers = malloc(k * sizeof(pthread_t));
|
|
pthread_t *consumers = malloc(m * sizeof(pthread_t));
|
|
producer_arg_t *pargs = malloc(k * sizeof(producer_arg_t));
|
|
consumer_arg_t *cargs = malloc(m * sizeof(consumer_arg_t));
|
|
|
|
for (int i = 0; i < k; i++) {
|
|
pargs[i].sp = &buf;
|
|
pargs[i].num_items = items_per_producer;
|
|
pargs[i].id = i + 1;
|
|
pthread_create(&producers[i], NULL, producer, &pargs[i]);
|
|
}
|
|
|
|
for (int i = 0; i < m; i++) {
|
|
cargs[i].sp = &buf;
|
|
cargs[i].id = i + 1;
|
|
pthread_create(&consumers[i], NULL, consumer, &cargs[i]);
|
|
}
|
|
|
|
/* Wait for all producers to finish */
|
|
for (int i = 0; i < k; i++) {
|
|
pthread_join(producers[i], NULL);
|
|
}
|
|
|
|
/* Insert one poison pill to start the termination chain */
|
|
sbuf_insert(&buf, POISON);
|
|
|
|
/* Wait for all consumers to finish */
|
|
for (int i = 0; i < m; i++) {
|
|
pthread_join(consumers[i], NULL);
|
|
}
|
|
|
|
printf("\n=== Verification ===\n");
|
|
printf("Produced sum : %lld\n", produced_sum);
|
|
printf("Consumed sum : %lld\n", consumed_sum);
|
|
if (produced_sum == consumed_sum) {
|
|
printf("SUCCESS: Sums match! Program is correct.\n");
|
|
} else {
|
|
printf("ERROR: Sum mismatch! Difference = %lld\n",
|
|
produced_sum - consumed_sum);
|
|
}
|
|
|
|
free(producers);
|
|
free(consumers);
|
|
free(pargs);
|
|
free(cargs);
|
|
sbuf_deinit(&buf);
|
|
sem_destroy(&sum_mutex);
|
|
sem_destroy(&count_mutex);
|
|
|
|
return 0;
|
|
}
|