diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..fd5c523 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "files.associations": { + "unistd.h": "c", + "errno.h": "c" + } +} \ No newline at end of file diff --git a/5_Multithreaded-Programming/consumer.c b/5_Multithreaded-Programming/consumer.c new file mode 100644 index 0000000..b44f1a5 --- /dev/null +++ b/5_Multithreaded-Programming/consumer.c @@ -0,0 +1,160 @@ +// consumer.c +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // For FLT_MAX, FLT_MIN (or DBL_MAX etc.) +#include // For signal handling + +#include "data_structures.h" // Common header + +SharedData *shared_data_ptr = NULL; +int shm_fd = -1; +sem_t *mutex_sem = SEM_FAILED; +volatile sig_atomic_t keep_running = 1; + +void cleanup_ipc_consumer() { + printf("\nConsumer cleaning up...\n"); + if (shared_data_ptr != MAP_FAILED && shared_data_ptr != NULL) { + if (munmap(shared_data_ptr, sizeof(SharedData)) == -1) { + perror("munmap"); + } + } + if (shm_fd != -1) { + close(shm_fd); + // Consumer does NOT unlink shared memory, producer owns it + } + if (mutex_sem != SEM_FAILED) { + sem_close(mutex_sem); + // Consumer does NOT unlink semaphore, producer owns it + } + printf("Consumer cleanup complete.\n"); +} + +void sigint_handler_consumer(int sig) { + printf("\nConsumer received SIGINT. Shutting down...\n"); + keep_running = 0; +} + +int main() { + struct sigaction sa; + sa.sa_handler = sigint_handler_consumer; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + if (sigaction(SIGINT, &sa, NULL) == -1) { + perror("sigaction"); + return EXIT_FAILURE; + } + if (sigaction(SIGTERM, &sa, NULL) == -1) { + perror("sigaction for SIGTERM"); + return EXIT_FAILURE; + } + + // 1. Open existing POSIX shared memory object (DO NOT CREATE) + shm_fd = shm_open(SHM_NAME, O_RDWR, 0666); // O_RDONLY if only reading + if (shm_fd == -1) { + perror("shm_open (Is the producer.c program running?)"); + return EXIT_FAILURE; + } + + // 2. Map the shared memory object into the process's address space + // Note: Size is known from SharedData struct. ftruncate is not needed here. + shared_data_ptr = mmap(0, sizeof(SharedData), PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0); // PROT_READ if only reading + if (shared_data_ptr == MAP_FAILED) { + perror("mmap"); + close(shm_fd); + return EXIT_FAILURE; + } + printf("Shared memory opened and mapped successfully.\n"); + + // 3. Open existing POSIX named semaphore (DO NOT CREATE) + mutex_sem = sem_open(SEM_MUTEX_NAME, 0); // Flags argument is 0 when opening existing + if (mutex_sem == SEM_FAILED) { + perror("sem_open (Is the producer.c program running and semaphore created?)"); + munmap(shared_data_ptr, sizeof(SharedData)); + close(shm_fd); + return EXIT_FAILURE; + } + printf("Mutex semaphore opened successfully.\n"); + + printf("Monitoring room display started. Press Ctrl+C to exit.\n"); + printf("Will display data every %d seconds.\n\n", MONITOR_DISPLAY_INTERVAL); + + // 4. Monitoring loop + while (keep_running) { + // Wait for semaphore + if (sem_wait(mutex_sem) == -1) { + if (keep_running) + perror("sem_wait in consumer"); // Don't print error if shutting down + break; // Exit loop on error or interruption + } + + float max_temp = -FLT_MAX; + float min_temp = FLT_MAX; + int hottest_workshop_idx = -1; + int coldest_workshop_idx = -1; + int valid_data_found = 0; + + for (int i = 0; i < NUM_WORKSHOPS; ++i) { + // Check if the workshop data looks initialized (not default 0.0 or some other sentinel) + // For this example, any non-zero temp can be considered, or rely on producer to fill. + // A more robust way would be a 'valid' flag per workshop or timestamp. + if (shared_data_ptr->workshops[i].temperature != 0.0f || shared_data_ptr->workshops[i].humidity != 0.0f) { + valid_data_found = 1; // At least one workshop has some data + } + + float current_temp = shared_data_ptr->workshops[i].temperature; + + if (current_temp > max_temp) { + max_temp = current_temp; + hottest_workshop_idx = i; + } + if (current_temp < min_temp) { + min_temp = current_temp; + coldest_workshop_idx = i; + } + } + + // Release semaphore + if (sem_post(mutex_sem) == -1) { + perror("sem_post in consumer"); + break; // Exit loop on error + } + + // Display data + if (hottest_workshop_idx != -1 && coldest_workshop_idx != -1 && valid_data_found) { + printf("--- Monitoring Update (%s", ctime(&(time_t){ time(NULL) })); // ctime adds newline + printf(" Highest Temp: Workshop %d (%.2f C, %.2f %% Humidity)\n", + shared_data_ptr->workshops[hottest_workshop_idx].workshop_id, + max_temp, + shared_data_ptr->workshops[hottest_workshop_idx].humidity); + printf(" Lowest Temp: Workshop %d (%.2f C, %.2f %% Humidity)\n", + shared_data_ptr->workshops[coldest_workshop_idx].workshop_id, + min_temp, + shared_data_ptr->workshops[coldest_workshop_idx].humidity); + printf("---\n\n"); + } + else if (!valid_data_found) { + printf("[%s] Waiting for initial data from workshops...\n\n", ctime(&(time_t){ time(NULL) })); + } + else { + printf("[%s] No valid temperature extremes found yet (all workshops might have same temp or no data).\n\n", ctime(&(time_t){ time(NULL) })); + } + fflush(stdout); + + // Sleep for the display interval + for (int i = 0; i < MONITOR_DISPLAY_INTERVAL && keep_running; ++i) { + sleep(1); + } + } + + // 5. Cleanup: Unmap shared memory, close file descriptor, close semaphore + cleanup_ipc_consumer(); + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/5_Multithreaded-Programming/data_structures.h b/5_Multithreaded-Programming/data_structures.h new file mode 100644 index 0000000..e337047 --- /dev/null +++ b/5_Multithreaded-Programming/data_structures.h @@ -0,0 +1,37 @@ +// data_structures.h +#ifndef DATA_STRUCTURES_H +#define DATA_STRUCTURES_H + +#include // For pthread_t, though not strictly needed in Program 2 if monitor is in main +#include // For sem_t +#include // For time_t (optional) + +#define NUM_WORKSHOPS 10 +#define SHM_NAME "/workshop_monitoring_shm" +#define SEM_MUTEX_NAME "/workshop_monitoring_mutex_sem" + +// Min/Max values for simulation +#define MIN_TEMP 10.0f +#define MAX_TEMP 40.0f +#define MIN_HUMIDITY 20.0f +#define MAX_HUMIDITY 80.0f + +// Interval for workshop data generation (seconds) +#define WORKSHOP_UPDATE_INTERVAL 2 +// Interval for monitoring display (seconds) +#define MONITOR_DISPLAY_INTERVAL 5 + +typedef struct { + int workshop_id; + float temperature; + float humidity; + // time_t last_updated; // Optional: timestamp of last update +} WorkshopData; + +typedef struct { + WorkshopData workshops[NUM_WORKSHOPS]; + // Could add a generation counter or other metadata if needed + // int data_ready_count; // Could be used with condition variables if within one process +} SharedData; + +#endif // DATA_STRUCTURES_H \ No newline at end of file diff --git a/5_Multithreaded-Programming/parity_checker.c b/5_Multithreaded-Programming/parity_checker.c new file mode 100644 index 0000000..8448cc7 --- /dev/null +++ b/5_Multithreaded-Programming/parity_checker.c @@ -0,0 +1,73 @@ +#include +#include +#include +#include +#include + +// Shared variables +int total_count = 0; +int even_count = 0; +pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; +bool running = true; + +// Thread to report total count every 3 seconds +void* report_total(void* arg) { + while(running) { + sleep(3); + pthread_mutex_lock(&mutex); + printf("Total numbers entered so far: %d\n", total_count); + pthread_mutex_unlock(&mutex); + } + return NULL; +} + +// Thread to report even count every 5 seconds +void* report_even(void* arg) { + while(running) { + sleep(5); + pthread_mutex_lock(&mutex); + printf("Total even numbers entered so far: %d\n", even_count); + pthread_mutex_unlock(&mutex); + } + return NULL; +} + +int main() { + pthread_t total_thread, even_thread; + int num; + + // Create the reporting threads + pthread_create(&total_thread, NULL, report_total, NULL); + pthread_create(&even_thread, NULL, report_even, NULL); + + printf("Enter integers (enter a negative number to exit):\n"); + + while(1) { + scanf("%d", &num); + + if(num < 0) { + printf("Negative number entered. Exiting program.\n"); + break; + } + + pthread_mutex_lock(&mutex); + total_count++; + + if(num % 2 == 0) { + even_count++; + printf("%d is an even number.\n", num); + } else { + printf("%d is an odd number.\n", num); + } + pthread_mutex_unlock(&mutex); + } + + // Signal threads to terminate and wait for them + running = false; + pthread_join(total_thread, NULL); + pthread_join(even_thread, NULL); + + pthread_mutex_destroy(&mutex); + + return 0; +} \ No newline at end of file diff --git a/5_Multithreaded-Programming/producer.c b/5_Multithreaded-Programming/producer.c new file mode 100644 index 0000000..3779f77 --- /dev/null +++ b/5_Multithreaded-Programming/producer.c @@ -0,0 +1,218 @@ +// producer.c +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // For signal handling +#include // For FLT_MAX etc. (not strictly needed here but good for min/max init) +#include // For intptr_t + +#include "data_structures.h" // Common header + +SharedData *shared_data_ptr = NULL; +int shm_fd = -1; +sem_t *mutex_sem = SEM_FAILED; +pthread_t workshop_threads[NUM_WORKSHOPS]; +volatile sig_atomic_t keep_running = 1; + +void cleanup_ipc() { + printf("\nProducer cleaning up IPC...\n"); + if (shared_data_ptr != MAP_FAILED && shared_data_ptr != NULL) { + if (munmap(shared_data_ptr, sizeof(SharedData)) == -1) { + perror("munmap"); + } + } + if (shm_fd != -1) { + close(shm_fd); + if (shm_unlink(SHM_NAME) == -1) { + // perror("shm_unlink"); // Might have been unlinked by another instance or if it never fully started + } + } + if (mutex_sem != SEM_FAILED) { + sem_close(mutex_sem); + if (sem_unlink(SEM_MUTEX_NAME) == -1) { + // perror("sem_unlink"); // Similar to shm_unlink + } + } + printf("Producer IPC cleanup complete.\n"); +} + +void sigint_handler(int sig) { + printf("\nProducer received SIGINT. Shutting down...\n"); + keep_running = 0; + // Give threads a moment to notice keep_running flag + // In a more robust system, you'd signal threads to exit using pthread_cancel or condition variables. + // For this example, they will check keep_running. +} + +// Function for each workshop thread +void *workshop_thread_func(void *arg) { + int workshop_id = (int)(intptr_t)arg; // Safer than &i from main loop + srand(time(NULL) ^ pthread_self()); // Seed random number generator per thread + + printf("Workshop thread %d started.\n", workshop_id); + + while (keep_running) { + // Simulate data generation + float current_temp = MIN_TEMP + ((float)rand() / RAND_MAX) * (MAX_TEMP - MIN_TEMP); + float current_humidity = MIN_HUMIDITY + ((float)rand() / RAND_MAX) * (MAX_HUMIDITY - MIN_HUMIDITY); + + // Acquire semaphore + if (sem_wait(mutex_sem) == -1) { + perror("sem_wait in workshop thread"); + pthread_exit(NULL); + } + + // Write data to shared memory + shared_data_ptr->workshops[workshop_id].temperature = current_temp; + shared_data_ptr->workshops[workshop_id].humidity = current_humidity; + // shared_data_ptr->workshops[workshop_id].last_updated = time(NULL); // Optional + + printf("Workshop %d: Temp = %.2f C, Humidity = %.2f %%\n", + workshop_id, current_temp, current_humidity); + + // Release semaphore + if (sem_post(mutex_sem) == -1) { + perror("sem_post in workshop thread"); + // Continue, but this is problematic + } + + // Sleep for a while + for (int i = 0; i < WORKSHOP_UPDATE_INTERVAL && keep_running; ++i) { + sleep(1); + } + } + + printf("Workshop thread %d exiting.\n", workshop_id); + pthread_exit(NULL); +} + +int main() { + // Register signal handler for Ctrl+C + struct sigaction sa; + sa.sa_handler = sigint_handler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; // or SA_RESTART to restart syscalls if interrupted by this signal + if (sigaction(SIGINT, &sa, NULL) == -1) { + perror("sigaction"); + return EXIT_FAILURE; + } + if (sigaction(SIGTERM, &sa, NULL) == -1) { + perror("sigaction for SIGTERM"); + return EXIT_FAILURE; + } + + // 1. Create or open POSIX shared memory object + // O_EXCL can be used to ensure we are the first to create it, + // but for robust restart, we might remove it and handle existing SHM. + // For this assignment, let's assume clean startup. + shm_fd = shm_open(SHM_NAME, O_CREAT | O_RDWR | O_EXCL, 0666); + if (shm_fd == -1) { + perror("shm_open (Is another producer instance running or /dev/shm full?)"); + // Attempt to open if it already exists (for cleanup or if O_EXCL was removed) + shm_fd = shm_open(SHM_NAME, O_RDWR, 0666); + if (shm_fd == -1) { + perror("shm_open (secondary attempt)"); + return EXIT_FAILURE; + } + // If opened existing, we might not want to ftruncate, but for a fresh start this is okay + // Or better, unlink first if it exists from a previous bad run. + printf("Warning: Shared memory %s already existed. Unlinking and recreating.\n", SHM_NAME); + shm_unlink(SHM_NAME); // Clean up if it exists + shm_fd = shm_open(SHM_NAME, O_CREAT | O_RDWR, 0666); + if (shm_fd == -1) { + perror("shm_open (after unlink)"); + return EXIT_FAILURE; + } + } + + // 2. Set the size of the shared memory object + if (ftruncate(shm_fd, sizeof(SharedData)) == -1) { + perror("ftruncate"); + close(shm_fd); + shm_unlink(SHM_NAME); // Clean up + return EXIT_FAILURE; + } + + // 3. Map the shared memory object into the process's address space + shared_data_ptr = mmap(0, sizeof(SharedData), PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0); + if (shared_data_ptr == MAP_FAILED) { + perror("mmap"); + close(shm_fd); + shm_unlink(SHM_NAME); // Clean up + return EXIT_FAILURE; + } + printf("Shared memory created/opened and mapped successfully.\n"); + + // Initialize workshop IDs and default data + for (int i = 0; i < NUM_WORKSHOPS; ++i) { + shared_data_ptr->workshops[i].workshop_id = i; + shared_data_ptr->workshops[i].temperature = 0.0f; // Initial value + shared_data_ptr->workshops[i].humidity = 0.0f; // Initial value + } + + // 4. Create or open a POSIX named semaphore (acting as a mutex) + // O_EXCL can be used here as well for similar reasons to shm_open. + mutex_sem = sem_open(SEM_MUTEX_NAME, O_CREAT | O_EXCL, 0666, 1); // Initial value 1 (unlocked) + if (mutex_sem == SEM_FAILED) { + perror("sem_open (Is another producer instance running with this semaphore?)"); + sem_unlink(SEM_MUTEX_NAME); // Attempt to clean up if it exists + mutex_sem = sem_open(SEM_MUTEX_NAME, O_CREAT, 0666, 1); + if (mutex_sem == SEM_FAILED) { + perror("sem_open (after unlink)"); + cleanup_ipc(); // Full cleanup + return EXIT_FAILURE; + } + printf("Warning: Semaphore %s already existed. Unlinking and recreating.\n", SEM_MUTEX_NAME); + } + printf("Mutex semaphore created/opened successfully.\n"); + + // Seed random number generator for main (primarily for workshop thread seeding) + srand(time(NULL)); + + // 5. Create workshop threads + printf("Creating %d workshop threads...\n", NUM_WORKSHOPS); + for (long i = 0; i < NUM_WORKSHOPS; ++i) { + // Note: Casting 'i' to void* and back to int (via intptr_t) is a common shorthand + // for passing small integer IDs. For complex arguments, a struct pointer is better. + if (pthread_create(&workshop_threads[i], NULL, workshop_thread_func, (void *)i) != 0) { + perror("pthread_create"); + keep_running = 0; // Signal other threads to stop + // Join already created threads before exiting + for (long j = 0; j < i; ++j) { + pthread_join(workshop_threads[j], NULL); + } + cleanup_ipc(); + return EXIT_FAILURE; + } + } + + printf("All workshop threads created. Producer is running. Press Ctrl+C to exit.\n"); + + // Keep the main thread alive while workshop threads run + // The threads will check 'keep_running' which is modified by the signal handler + while (keep_running) { + sleep(1); // Check periodically + } + + // 6. Wait for all workshop threads to complete + printf("Waiting for workshop threads to exit...\n"); + for (int i = 0; i < NUM_WORKSHOPS; ++i) { + if (pthread_join(workshop_threads[i], NULL) != 0) { + perror("pthread_join"); + } + } + printf("All workshop threads have exited.\n"); + + // 7. Cleanup: Unmap, close, and unlink shared memory and semaphore + // This will be called by the signal handler or at the end of normal execution + cleanup_ipc(); + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/5_Multithreaded-Programming/thread_reverse_order.c b/5_Multithreaded-Programming/thread_reverse_order.c new file mode 100644 index 0000000..b0f49ec --- /dev/null +++ b/5_Multithreaded-Programming/thread_reverse_order.c @@ -0,0 +1,75 @@ +#include +#include +#include +#include +#include +#include + +#define NUM_THREADS 5 + +// Semaphores to control output order +sem_t semaphores[NUM_THREADS]; + +// Thread function +void* thread_function(void* arg) { + int thread_num = *(int*)arg; + int thread_creation_number = thread_num + 1; + + // Random delay between 1-5 seconds + int delay = rand() % 5 + 1; + printf("Thread %d created, will delay for %d seconds\n", thread_creation_number, delay); + sleep(delay); + + // Wait for signal to ensure proper output order + sem_wait(&semaphores[thread_num]); + + // Output thread information + printf("Thread ID: %lu, Creation Number: %d\n", pthread_self(), thread_creation_number); + + // Signal the next thread in reverse order + if (thread_num > 0) { + sem_post(&semaphores[thread_num - 1]); + } + + free(arg); + return NULL; +} + +int main() { + pthread_t threads[NUM_THREADS]; + int i; + + // Seed the random number generator + srand(time(NULL)); + + // Initialize all semaphores + for (i = 0; i < NUM_THREADS; i++) { + sem_init(&semaphores[i], 0, 0); + } + + // Create all threads + printf("Creating threads...\n"); + for (i = 0; i < NUM_THREADS; i++) { + int* thread_num = malloc(sizeof(int)); + *thread_num = i; + pthread_create(&threads[i], NULL, thread_function, thread_num); + usleep(100000); // Small delay between thread creation for clarity + } + + // Signal the last created thread to start the reverse order output + sem_post(&semaphores[NUM_THREADS - 1]); + + // Wait for all threads to complete + for (i = 0; i < NUM_THREADS; i++) { + pthread_join(threads[i], NULL); + } + + // Clean up semaphores + for (i = 0; i < NUM_THREADS; i++) { + sem_destroy(&semaphores[i]); + } + + printf("All threads completed execution.\n"); + + return 0; +} \ No newline at end of file