feat: assignment 3
This commit is contained in:
parent
06fbd2d6ad
commit
3cc3f6332d
7 changed files with 281 additions and 0 deletions
21
assignment3/default.nix
Normal file
21
assignment3/default.nix
Normal file
|
|
@ -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";
|
||||
}
|
||||
22
assignment3/src/buffer.h
Normal file
22
assignment3/src/buffer.h
Normal file
|
|
@ -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_
|
||||
91
assignment3/src/consumer.c
Normal file
91
assignment3/src/consumer.c
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
#include "buffer.h"
|
||||
#include <fcntl.h>
|
||||
#include <semaphore.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/shm.h>
|
||||
#include <unistd.h>
|
||||
|
||||
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 <id> <number of msgs>\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;
|
||||
}
|
||||
99
assignment3/src/producer.c
Normal file
99
assignment3/src/producer.c
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
#include "buffer.h"
|
||||
#include <fcntl.h>
|
||||
#include <semaphore.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/shm.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
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 <id> <number of msgs>\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;
|
||||
}
|
||||
38
assignment3/test
Executable file
38
assignment3/test
Executable file
|
|
@ -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
|
||||
9
assignment3/test.nix
Normal file
9
assignment3/test.nix
Normal file
|
|
@ -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 ${../.}
|
||||
'';
|
||||
}
|
||||
|
|
@ -11,6 +11,7 @@
|
|||
subdirs = [
|
||||
"assignment1"
|
||||
"assignment2"
|
||||
"assignment3"
|
||||
"project1"
|
||||
"project2"
|
||||
];
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue