The submission should include
code along with the inputs and outputs.
detailed explanation of the code.
Screenshots of code execution showing outputs.
The Dining Philosophers Problem We have previously explored the concept of synchronization in the context of operating systems and the different ways of sharing resources among processes and threads. In this task, we will delve further into this topic by examining a classical synchronization problem, along with its implementation in C code and an explanation of how it works. Your goal is to understand the problem theoretically and implement the provided program. Once you have completed this example, you will be asked to complete a related assignment given at the end. The Dining Philosophers problem is a classic problem in computer science that illustrates the challenges of managing shared resources in a concurrent system. It involves a group of philosophers who are seated around a table with a chopstick between each pair of philosophers. At any given time, a philosopher may be either hungry or satiated. If a philosopher is hungry, they will try to pick up the chopsticks on either side of them in order to eat. However, if another philosopher is already holding one of the chopsticks that the hungry philosopher needs, the hungry philosopher will be blocked and will have to wait until the chopstick becomes available. The problem is to design a protocol that allows the philosophers to eat without any of them becoming starved. This problem is challenging because it involves multiple threads of execution (the philosophers) competing for shared resources (the chopsticks) and because it is possible for the system to reach a state in which no progress can be made (a deadlock). One solution to the Dining Philosophers problem is to use a semaphore, which is a synchronization object that controls access to a shared resource. The semaphore can be used to ensure that at most N philosophers are holding chopsticks at any given time, where N is the number of chopsticks. This prevents deadlocks by limiting the number of philosophers that can be blocked waiting for chopsticks. \#define THINKING 2 \#define HUNGRY 1 \#define EATING 0 \#define LEFT (num_of_philosopher +4 ) \% N \#define RIGHT (num_of_philosopher +1 ) %N int state[N]; int phil[N] ={0,1,2,3,4}; I/ sem_t is a typedef defined in the header file as (apparently) some variety of integer. sem_t mutex; sem_t S[N]; void test(int num_of_philosopher) \{ if (state[num_of_philosopher] = = HUNGRY \&\& state[LEFT] != EATING \& \& state[RIGHT] 1= EATING) \{ II state that eating state[num_of_philosopher] = EATING; sleep(2); printf("Philosopher \%d takes fork \%d and \%dln". num_of_philosopher +1, LEFT +1, num_of_philosopher +1 ); printf("Philosopher \%d is Eatingln", num_of_philosopher +1 ); / sem_post(\&S[num_of_philosopher]) has no effect during takefork used to wake up hungry philosophers during putfork */ sem_post(\&S[num_of_philosopher]): 3 3 I/ take up Forks void take_fork(int num_of_philosopher) \{ sem_wait(\&mutex); II state that hungry state[num_of_philosopher] = HUNGRY; printf("Philosopher \%d is Hungryln", num_of_philosopher +1 ); II eat if neighbours are not eating test(num_of philosopher); sem_post(\&mutex): II if unable to eat wait to be signalled sem_wait(\&S[num_of_philosopher]); sleep (1); \} I/ put down Forks void put_fork(int num_of_philosopher) \{ sem_wait(\&mutex); II state that thinking state[num_of_philosopher] = THINKING; printf("Philosopher \%d putting fork \%d and \%d downin", num_of_philosopher +1, LEFT +1, num_of_philosopher +1 ); printf("Philosopher \%d is thinkingin", num_of_philosopher +1) : test(LEFT); test(RIGHT): sem_post(\&mutex): 1 void* philosopher(void* num) \{ while (1) f int* i= num; sleep(1): take_fork("i); sieep (0) : put fork(*i): 3 Explanation: The initial code, given before the void test0 function, include the necessary header files and define several constants and variables. The cstdio.h> and esemaphore h> headers are included for standard input/output functions and semaphore functions, respectively. The pthreadh> header is included for pthread functions. The N macro is defined as 5 , which specifies the number of philosophers in the simulation. The THINKING, HUNGRY, and EATING constants are used to represent the states of the philosophers. The IEFT and RIGHT macros are used to calculate the indices of the left and right chopsticks for a given philosopher. The state and phil arrays are used to store the states and IDs of the philosophers, respectively. The mutex and S variables are semaphores that are used to synchronize access to shared resources. The testl) function checks if the philosopher with the given ID can acquire their chopsticks and start eating. If the philosopher is hungry and their left and right chopsticks are not being used by other philosophers, the function sets the philosopher's state to EATING, simulates the philosopher eating for 2 seconds using sleep0. and prints a message to the console. It then signals the philosopher's semaphore using sem_post(] to wake them up if they are waiting- The take, forkf) function is used to simulate a philosopher trying to acquire their chopsticks. It begins by acquiring the mutex semaphore using sem_waiti] to ensure that the state array is not modified by another thread. It then sets the state of the philosopher to HUNGRY and prints a message to the console. Next, it calls the test() function to see if the philosopher can start eating. If the philosopher can eat, the test() function will set the philosopher's state to EATING and signal their semaphore, If the philosopher cannot eat, they will be blocked on the sem_wait() call until they are signaled by the test() function. Finally, the function sleeps for 1 second before returning. The put_fork() function is used to simulate a philosopher putting down their chopsticks. It begins by acquiring the mutex semaphore using sem_wait] to ensure that the state array is not modified by another thread. It then sets the state of the philosopher to THINKING and prints a message to the console. Next, it calls the test(0) function for the philosopher's left and right neighbors to see if they can start eating. If either of the neighbors can eat, the test() function will set their state to EATING and signal their semaphore. Finally, the function releases the mutex semaphore using sem_post() and returns. The philosopher() function is the main function for each philosopher thread. It begins by casting the void pointer argument num to an int* and dereferencing it to obtain the ID of the philosopher. It then enters an infinite loop. Inside the loop, the function sleeps for 1 second, calls the take_forki) function to acquire its chopsticks, sleeps. for 0 seconds, and then calls the put_fork|) function to release its chopsticks. This simulates the philosopher thinking, acquiring their chopsticks, eating, and then releasing their chopsticks in a continuous cycle. This is the main0 function of the program. It begins by declaring an array of pthread_t variables to store the IDs of the philosopher threads and initializing the mutex and 5 semaphores. The mutex semaphore is initialized with a value of 1 , which allows it to be acquired by a single thread at a time. The 5 semaphores are initialized with a value of 0 , which means that any thread that tries to acquire them will be blocked until they are signaled by another thread. Next, the program creates a philosopher thread for each philosopher using a loop and the pthread_createll function. The philosopher[) function is specified as the start routine for each thread, and the ID of the philosopher is passed as an argument. Assignment "Implement the readers-writers problem using mutex locks in C or C++. Your program should simulate the behavior of the readers, writers, and the shared resource, and it should ensure that no deadiock or starvation occurs." The readers-writers problem is a classic synchronization problem that involves multiple readers and writers accessing a shared resource. The problem is to ensure that the readers and writers can access the resource concurrently, but that no writer is allowed to write to the resource while it is being read by a reader. In this assignment, you will be asked to implement a program in C or C++that simulates the behavior of the readers, writers, and the shared resource. Your program should use mutex locks to synchronize the access to the shared resource. Here are the steps you should follow to complete this assignment: 1. Familiarize yourself with the readers-writers problem and the various approaches that have been taken to solve it. 2. Design a solution to the problem using mutex locks. Your solution should ensure that no deadlock or starvation occurs. 3. Implement your solution in C or C+4. You can use threads or processes to simulate the behavior of the readers and writers. 4. Test your program with different configurations to ensure that it works correctly. 5. Write a report on your solution to the readers-writers problem, In your report, discuss the design of your solution and how it ensures that no deadlock or starvation occurs. Here is a sample reader A reader is a process or thread that reads data from a shared resource. Here is an example of a reader in C+4: Following code shows a simple reader, acquires the mutex lock before reading data from the shared resource. This ensures that no other process or thread can write to the resource while the reader is accessing it. After reading the data, the reader releases the mutex lock and processes the data. Note that this is just one possible implementation of a reader. The exact implementation may vary depending on the specific requirements of the problem. void reader[) 1 mutex.lock0: // read data from shared resource int data = read_datal); mutex.unlockli: // process data grocess_data(datal: 1