1 COMSATS UNIVERSITY ISLAMABAD OPERATING SYSTEMS LAB TERMINAL PROJECT Submitted by ABDUL REHMAN KHAN (SP21-BSE-002) ABU BAKAR (SP21-BSE-005) Class BSE-4B Submitted to MAM HAFIZA SUNDUS Date of Submission 8th June, 2023 2 Q1: Write a program (in C/C++ only) to implement the following scheme: A process first creates an empty array of size 10. It then creates two threads. Each thread takes input from user and places it in array. First thread is responsible of taking 5 inputs (one by one) from user and placing it in upper half of that array and second thread does the same for the lower half of the array. Both threads run parallel so they should be synchronized while accessing the shared array. The main process should wait for these two threads. After both threads are executed, the main process should add the array entries for each thread and display the number count for each thread. The output should look like: Array: 3 5 1 0 6 7 1 2 9 1 Thread 1 count: 15 Thread 2 count: 20 Solution: In this program, we use the mutex to ensure that both threads access the shared array in a synchronized manner. The first thread (inputThread1) takes input from the user and stores it in the upper half of the array, updating the thread1Count variable accordingly. Similarly, the second thread (inputThread2) takes input from the user and stores it in the lower half of the array, updating the thread2Count variable. After both threads have finished executing, the main process displays the contents of the array and the count for each thread. #include <iostream> #include <thread> #include <mutex> using namespace std; const int ARRAY_SIZE = 10; int sharedArray[ARRAY_SIZE]; // Shared array mutex mtx; // Mutex for protecting shared resources int thread1Count = 0; // Counter for Thread 1 3 int thread2Count = 0; // Counter for Thread 2 void inputThread1() { lock_guard<mutex> lock(mtx); // Lock the mutex cout << "Enter 5 integers for Thread 1: "; for (int i = 0; i < ARRAY_SIZE / 2; i++) { cin >> sharedArray[i]; // Read input for Thread 1 and store in shared array thread1Count += sharedArray[i]; // Update the count for Thread 1 } } void inputThread2() { lock_guard<mutex> lock(mtx); // Lock the mutex cout << "Enter 5 integers for Thread 2: "; for (int i = ARRAY_SIZE / 2; i < ARRAY_SIZE; i++) { cin >> sharedArray[i]; // Read input for Thread 2 and store in shared array thread2Count += sharedArray[i]; // Update the count for Thread 2 } } int main() { thread t2(inputThread2); // Create Thread 1 thread t1(inputThread1); // Create Thread 2 t2.join(); // Wait for Thread 2 to finish t1.join(); // Wait for Thread 1 to finish cout << "Array: "; 4 for (int i = 0; i < ARRAY_SIZE; i++) { cout << sharedArray[i] << "\t"; // Print the elements of the shared array } cout << endl; cout << "Thread 1 count: " << thread1Count << endl; // Print the count for Thread 1 cout << "Thread 2 count: " << thread2Count << endl; // Print the count for Thread 2 return 0; } OUTPUT: Q2: Write a C/C++ program that exhibits counting semaphores (i.e. resource counter) (15-marks): You are required to write a scenario where counting semaphores are needed. Explain the problem well and then write the code to solve the problem using counting semaphores. 5 Solution: Scenario where multiple threads need to access a shared printer: Scenario: Imagine a scenario where there is a shared printer that multiple threads need to use to print their respective documents. However, the printer can only handle a limited number of documents simultaneously. To ensure fair and controlled access to the printer, counting semaphores are used. Problem Explanation: The printer has a maximum capacity of 'N' documents that can be printed concurrently. If all 'N' slots are occupied by other threads, a requesting thread needs to wait until a slot becomes available. Once a slot becomes available, the requesting thread can access the printer to print its document. When a thread finishes printing its document, it releases the slot, allowing other threads to use it. Solution/Coding using Counting Semaphores: #include <iostream> #include <thread> #include <semaphore.h> #include <unistd.h> const int PRINTER_CAPACITY = 3; //const int PRINTING_TIME = 2; // Time to simulate printing in seconds sem_t countingSemaphore; // Semaphore for printer access void printDocument(int threadId, int documentId) { printf("Thread %d is requesting access to the printer for Document %d\n", threadId, documentId); sem_wait(&countingSemaphore); // Acquire a slot in the printer printf("Printer is printing Document %d for Thread %d\n", documentId, threadId); // Simulating the printing process //sleep(PRINTING_TIME); printf("Printer finished printing Document %d for Thread %d\n\n", documentId, threadId); sem_post(&countingSemaphore); // Release the slot in the printer } int main() { sem_init(&countingSemaphore, 0, PRINTER_CAPACITY); // Initialize the semaphore 6 std::thread threads[5]; for (int i = 0; i < 5; i++) { // Create threads for printing documents threads[i] = std::thread(printDocument, i, i + 1); } for (int i = 0; i < 5; i++) { // Wait for all threads to finish threads[i].join(); } sem_destroy(&countingSemaphore); // Destroy the semaphore return 0; } Further Explanation: The given code demonstrates the usage of counting semaphores in a scenario where multiple threads need to access a shared printer. The printer has a limited capacity defined by the constant PRINTER_CAPACITY, which is set to 3. The counting semaphore countingSemaphore is initialized to manage access to the printer using sem_init. The printDocument function represents the task of printing a document for a particular thread. Inside this function, each thread requests access to the printer by calling sem_wait on the counting semaphore. If the semaphore value is positive or zero (indicating available slots in the printer), the thread proceeds and acquires a slot by decrementing the semaphore value. If the value is negative, the thread is blocked and waits until a slot becomes available. Once a slot is acquired, the thread proceeds to print the document and simulates the printing process using sleep. After completing the printing, the thread releases the slot by calling sem_post to increment the semaphore value, allowing other threads to access the printer. In the main function, the counting semaphore is initialized with the printer capacity. Then, a set of threads is created to represent the employees. Each thread calls the printDocument function with a unique thread ID and document ID. Finally, the main function waits for all the threads to finish by calling join, and the counting semaphore is destroyed using sem_destroy. 7 OUTPUT: