#1

The problem with the Dining Philosophers Problem is that when all the philosophers pick up the left chopstick at the same time, everyone will try to pick up the right
chopstick and deadlock occurs.

This problem can be solved by using an ordered resource policy, namely, by enforcing every philosopher to first pick up the lower-numbered chopstick and then pick up the high-numbered chopstick. The implementation is as follows:

Semaphore[0..4] chopstick;

repeat
    if (i < (i+1) mod 5){
        chopstick[i].p(); // Pick up left chopstick
        chopstick[(i+1) mod 5].p(); // Pick up right chopstick
    }
    else{
        chopstick[(i+1) mod 5].p(); // Pick up right chopstick
        chopstick[i].p(); // Pick up left chopstick
    }
    // EAT
    signal(chopstick[i]); // Put down left chopstick
    signal(chopstick[i+1 mod 5]); // Put down right chopstick
    // THINK
until false;

#2(a)

If we remove restriction (i) from the Executer thread, it means that at one time multiple Executers could be requesting storage from the Storage_Manager, implemented as a chunking semaphore. Deadlock cannot occur because each Executer requests storage for one job and then it calls Release() on Storage_Manager to release all of the allocated storage back before making another Request(). This implies that there is no way an Executer can make a request while holding resources. Thus two of the necessary and sufficient conditions – hold-and-wait and circular wait – for deadlocks don’t hold. Hence deadlocks cannot occur.

On the other hand, it is possible for an Executer to starve. Here is an example:

Say Storage_Manager has 220 blocks to begin with.
E1 requests and is granted 100 blocks.
E2 requests 150 blocks. It is blocked because there is not enough available.
E3 requests for 100 blocks and is granted.
E1 finishes its job and releases 100 blocks.
E1 requests for 100 blocks and is granted 100 blocks.
E3 finishes its job and releases 100 blocks.
E3 requests for 100 blocks and is granted 100 blocks.

As indicated above, E1 and E3 and alternatively request and release some storage fewer than 120, all the while keeping E2 blocked. Thus E2 is starved.

#2(b)
If each Executer can make multiple requests while holding some resource, then the hold-and-wait condition or circular wait is no longer satisfied and deadlock can indeed occur. To see how, here is an example:

E1 already holds 3 blocks and asks for one more before it is willing to give up its resources. Similarly E2 is holding 2 blocks and is asking for one more. Deadlock thus occurs.

#3
Say in the current state, process p holds 1 unit of R1 and 1 unit of R2; q holds 1 unit of R2 and requests 1 unit of R1. The resource allocation is as follows:

Using Banker’s algorithm, there are the values for the arrays:

available[0..1] = [1, 0] and the rest three tables are as follows:
 
 
Max
Allocation
Need
  P Q P Q P Q
R1 1 2 1 0 0 2
R2 2 1 1 1 1 0

Assume that q’s request of 1 unit of R1 is satisfied, then the resulting values are:

available[0..1] = [0, 0] and
 
 
Max
Allocation
Need
  P Q P Q P Q
R1 1 2 1 1 0 1
R2 2 1 1 1 1 0

Now perform the safety test:

For each process, check to see if need[i] <= available
Since available = [0, 0] and both p and q could potentially ask for more to complete its job, no process can be assumed to be able to finish its job and return resource back. Thus granting q’s request is deemed to be "unsafe" using Banker’s algorithm.

Without Banker’s algorithm, we could potentially get into a deadlock situation if, after q is granted 1 unit of R1 and then asks for one more unit of R1, while p asks for one more unit of R2. (graph omitted)