From 3cc3f6332d5614e0aecbafa52cb0b0f7c59ca30c Mon Sep 17 00:00:00 2001 From: Kiana Sheibani Date: Sat, 1 Nov 2025 22:14:35 -0400 Subject: [PATCH] feat: assignment 3 --- assignment3/default.nix | 21 ++++++++ assignment3/src/buffer.h | 22 +++++++++ assignment3/src/consumer.c | 91 +++++++++++++++++++++++++++++++++++ assignment3/src/producer.c | 99 ++++++++++++++++++++++++++++++++++++++ assignment3/test | 38 +++++++++++++++ assignment3/test.nix | 9 ++++ flake.nix | 1 + 7 files changed, 281 insertions(+) create mode 100644 assignment3/default.nix create mode 100644 assignment3/src/buffer.h create mode 100644 assignment3/src/consumer.c create mode 100644 assignment3/src/producer.c create mode 100755 assignment3/test create mode 100644 assignment3/test.nix diff --git a/assignment3/default.nix b/assignment3/default.nix new file mode 100644 index 0000000..a331454 --- /dev/null +++ b/assignment3/default.nix @@ -0,0 +1,21 @@ +let + basicC = subdir: name: { stdenv }: + stdenv.mkDerivation { + inherit name; + src = ./.; + buildPhase = '' + runHook preBuild + gcc ${subdir}/${name}.c -o ${name}.bin -pthread -lrt + runHook postBuild + ''; + installPhase = '' + runHook preInstall + mkdir -p $out/bin + install ${name}.bin $out/bin/${name} + runHook postInstall + ''; + }; +in { + a3-producer = basicC "src" "producer"; + a3-consumer = basicC "src" "consumer"; +} diff --git a/assignment3/src/buffer.h b/assignment3/src/buffer.h new file mode 100644 index 0000000..f4f7ecf --- /dev/null +++ b/assignment3/src/buffer.h @@ -0,0 +1,22 @@ +#ifndef BUFFER_H_ +#define BUFFER_H_ + +#define BUFFER_SIZE 10 +#define SHM_KEY 0x1234 +#define SEM_MUTEX "/sem_mutex" +#define SEM_EMPTY "/sem_empty" +#define SEM_FULL "/sem_full" + +typedef struct item_t { + int value; // Data value + int producer_id; // Which producer created this +} item_t; + +typedef struct shared_buffer_t { + item_t buffer[BUFFER_SIZE]; + int head; // Next write position + int tail; // Next read position + int count; // Current items in buffer +} shared_buffer_t; + +#endif // BUFFER_H_ diff --git a/assignment3/src/consumer.c b/assignment3/src/consumer.c new file mode 100644 index 0000000..4a4b029 --- /dev/null +++ b/assignment3/src/consumer.c @@ -0,0 +1,91 @@ +#include "buffer.h" +#include +#include +#include +#include +#include +#include + +shared_buffer_t *buffer = NULL; +sem_t *mutex = NULL; +sem_t *empty = NULL; +sem_t *full = NULL; +int shm_id = -1; + +void cleanup() { + // Detach shared memory + if (buffer != NULL) { + shmdt(buffer); + } + + // Close semaphores (don't unlink - other processes may be using) + if (mutex != SEM_FAILED) + sem_close(mutex); + if (empty != SEM_FAILED) + sem_close(empty); + if (full != SEM_FAILED) + sem_close(full); +} + +void signal_handler(int sig) { + printf("\nProducer: Caught signal %d, cleaning up...\n", sig); + cleanup(); + exit(2); +} + +int main(int argc, char *argv[]) { + // Argument parsing + if (argc != 3) { + printf("Usage: %s \n", argv[0]); + return 1; + } + int id = atoi(argv[1]); + int num_items = atoi(argv[2]); + + // Setup shared memory + shm_id = shmget(SHM_KEY, sizeof(shared_buffer_t), IPC_CREAT | 0666); + if (shm_id < 0) { + perror("shmget-failed"); + return 1; + } + + buffer = (shared_buffer_t *)shmat(shm_id, NULL, 0); + if (buffer == (void *)-1) { + perror("shmat-failed"); + return 1; + } + + // Open semaphores + mutex = sem_open(SEM_MUTEX, 0, 0644, 1); + empty = sem_open(SEM_EMPTY, 0, 0644, BUFFER_SIZE); + full = sem_open(SEM_FULL, 0, 0644, 0); + + if (mutex == SEM_FAILED || empty == SEM_FAILED || full == SEM_FAILED) { + perror("sem_open-failed"); + return 1; + } + + // Primary logic + for (int i = 0; i < num_items; i++) { + sem_wait(full); // Wait for item + sem_wait(mutex); // Enter critical section + + // Remove from buffer + item_t item = buffer->buffer[buffer->tail]; + buffer->tail = (buffer->tail + 1) % BUFFER_SIZE; + buffer->count--; + + printf("Consumer %d: Consumed value %d from Producer %d\n", id, item.value, + item.producer_id); + + sem_post(mutex); // Exit critical section + sem_post(empty); // Signal slot available + + usleep(rand() % 100000); // Simulate work + } + + // Cleanup + printf("Consumer %d: Finished consuming %d items\n", id, num_items); + cleanup(); + return 0; +} diff --git a/assignment3/src/producer.c b/assignment3/src/producer.c new file mode 100644 index 0000000..189f936 --- /dev/null +++ b/assignment3/src/producer.c @@ -0,0 +1,99 @@ +#include "buffer.h" +#include +#include +#include +#include +#include +#include +#include +#include + +shared_buffer_t *buffer = NULL; +sem_t *mutex = NULL; +sem_t *empty = NULL; +sem_t *full = NULL; +int shm_id = -1; + +void cleanup() { + // Detach shared memory + if (buffer != NULL) { + shmdt(buffer); + } + + // Close semaphores (don't unlink - other processes may be using) + if (mutex != SEM_FAILED) + sem_close(mutex); + if (empty != SEM_FAILED) + sem_close(empty); + if (full != SEM_FAILED) + sem_close(full); +} + +void signal_handler(int sig) { + printf("\nProducer: Caught signal %d, cleaning up...\n", sig); + cleanup(); + exit(2); +} + +int main(int argc, char *argv[]) { + // Argument parsing + if (argc != 3) { + printf("Usage: %s \n", argv[0]); + return 1; + } + int id = atoi(argv[1]); + int num_items = atoi(argv[2]); + + // Signal handlers + signal(SIGINT, signal_handler); + signal(SIGTERM, signal_handler); + + // Setup shared memory + shm_id = shmget(SHM_KEY, sizeof(shared_buffer_t), IPC_CREAT | 0666); + if (shm_id < 0) { + perror("shmget-failed"); + return 1; + } + + buffer = (shared_buffer_t *)shmat(shm_id, NULL, 0); + if (buffer == (void *)-1) { + perror("shmat-failed"); + return 1; + } + + // Open semaphores + mutex = sem_open(SEM_MUTEX, O_CREAT, 0644, 1); + empty = sem_open(SEM_EMPTY, O_CREAT, 0644, BUFFER_SIZE); + full = sem_open(SEM_FULL, O_CREAT, 0644, 0); + + if (mutex == SEM_FAILED || empty == SEM_FAILED || full == SEM_FAILED) { + perror("sem_open-failed"); + return 1; + } + + // Primary logic + unsigned int seed = time(NULL) + id; + for (int i = 0; i < num_items; i++) { + item_t item = {.producer_id = id, .value = rand_r(&seed) % 1000}; + + sem_wait(empty); // Wait for empty slot + sem_wait(mutex); // Enter critical section + + // Add to buffer + buffer->buffer[buffer->head] = item; + buffer->head = (buffer->head + 1) % BUFFER_SIZE; + buffer->count++; + + printf("Producer %d: Produced value %d\n", id, item.value); + + sem_post(mutex); // Exit critical section + sem_post(full); // Signal item available + + usleep(rand_r(&seed) % 100000); // Simulate work + } + + // Cleanup + printf("Producer %d: Finished producing %d items\n", id, num_items); + cleanup(); + return 0; +} diff --git a/assignment3/test b/assignment3/test new file mode 100755 index 0000000..4439b9e --- /dev/null +++ b/assignment3/test @@ -0,0 +1,38 @@ +#!/usr/bin/env bash + +echo "Building executables..." + +cd "${FLAKE:-$(dirname $0)}" +if command -v nix; then + producer="$(nix build --no-link --print-out-paths .#a3-producer)/bin/producer" + consumer="$(nix build --no-link --print-out-paths .#a3-consumer)/bin/consumer" +else + mkdir build + gcc src/producer.c -o build/producer -pthread -lrt + gcc src/consumer.c -o build/consumer -pthread -lrt + producer="./build/producer" + consumer="./build/consumer" +fi + +echo -e "\nTEST - Basic Functionality" +"${producer}" 1 5 & +"${consumer}" 1 5 & +wait + +echo -e "\nTEST - Multiple Producers" +"${producer}" 1 10 & +"${producer}" 2 10 & +"${producer}" 3 10 & +"${consumer}" 1 30 & +wait + +echo -e "\nTEST - Multiple Consumers" +"${producer}" 1 20 & +"${consumer}" 1 10 & +"${consumer}" 2 10 & +wait + +# Cleanup +ipcrm -M 0x1234 +rm /dev/shm/sem* +rm -rf build diff --git a/assignment3/test.nix b/assignment3/test.nix new file mode 100644 index 0000000..ff2d650 --- /dev/null +++ b/assignment3/test.nix @@ -0,0 +1,9 @@ +{ + a3 = { runCommandLocal, makeWrapper }: + runCommandLocal "a3-test" { nativeBuildInputs = [ makeWrapper ]; } + '' + mkdir -p $out/bin + install ${./test} $out/bin/a3-test + wrapProgram "$out/bin/a3-test" --set FLAKE ${../.} + ''; +} diff --git a/flake.nix b/flake.nix index e095e24..709bd0a 100644 --- a/flake.nix +++ b/flake.nix @@ -11,6 +11,7 @@ subdirs = [ "assignment1" "assignment2" + "assignment3" "project1" "project2" ];