CS372: Solutions for Homework 8

Problem 1:

Consider the following program fragment:

P(s1);
a++;
P(s2);
v++;
V(s2);
V(s1);

(s1, s2 are semaphores). All variables are automatic. Now, consider two threads running this fragment of code
simultaneously, can there be a deadlock? Why, or why not?

Solution:

Both semaphores will be acquired in the same order by both threads. Since this is a linear order, there can be no situation where a deadlock could occur.

Problem 2:

Consider the following program fragment:

if(a > 0)
    P(s1);
else
    P(s2);
b++;
P(s3);
if(b < 0 && a <= 0)
    P(s1);
else if(b >= 0 && a > 0)
    P(s2);
else
    P(s4);
a++;
V(s4);
V(s3);
V(s2);
V(s1);

s1, s2, s3 and s4 are semaphores. All variables are automatic. Now, consider two threads running this fragment of code simultaneously, can there be a deadlock? Why, or why not?

Solution:

Yes, there can be a deadlock. Consider the scenario where thread 1 starts by locking semaphore s1 then gets switched out while thread 2 runs. Thread 2 may start running with variable a set to 0 (remember, because the variables are automatic, each thread has its own independent set). Thread 2 will acquire semaphore s2, then proceeds to acquire s3. Then, if (b<0 && a <=0), it will try to acquire s1. This forces thread 2 to wait for thread 1 which holds this semaphore. Now, thread 1 may proceed but will block when it tries to acquire semaphore s3. This forms a cyclical wait, and a deadlock occurs.

Problem 3

An angry mob goes after the five dining philosophers, who escape into a dark alley in an unfrequented part of the town. They found an old soup kitchen, 5 bowls, and a coin. They decide to adapt to a new lifestyle. A philosopher now goes through the sequence:
while(1)
{
    flip the coin
       if(head) Table.Work()
       else Table.Eat()
   Think()
}
where Table is a shared object that encapsulates the system's shared state. As a result of the function Table::Work(), the philosopher makes enough soup to fill a bowl. Similarly, the result of the function Table::Eat() is that a philosopher picks a bowl and consumes all the soup in it. Note that there is no correspondence between bowls and philosophers, i.e., a philosopher will eat from any available bowl that contains soup, and will fill any available empty bowl.
  1. Using locks and condition variables, create a Table object to organize the philosophers' working and eating. Specifically, write the Table constructor and the methods Table::Work(), Table::Eat.

    Solution:

    Table constructor
    lock = new Lock();
    bowlEmpty = new Condition();
    bowlfull = new Condition();
    int full = 0;

    Table::Work()
    lock.acquire();
    while(full == 5) {
        bowlEmpty.wait(&lock);
    }
    full++;
    bowlFull.signal();
    lock.release();

    Table::Eat()
    lock.acquire();
    while (full == 0) {
        bowlFull.wait(&lock);
    }
    full--;
    bowlEmpty.signal();
    lock.release();

  2. If the coin guarantees that no more than four heads or four tails can appear in a row, is it possible to have starvation in the system? Explain your answer.

    Solution:
    Yes. Suppose the bowls start empty and four tails come up in a row. Then four threads are waiting in Eat(). The next flip must be heads, and a thread will Work. Then, three threads must be waiting in Eat(), and 2 threads in the main loop. Now suppose two more tails come up in a row. Then all five threads are waiting in Eat(), and no thread remains to Work(). Silly philosophers.

Problem 4

True/False Starvation implies deadlock.

Solution:
False.

Problem 5

In the context of deadlock prevention, how does a safe state differ from an unsafe state?

Solution:
In a safe state there exists a safe sequence -- an ordered sequence of resource grants that would allow each process that currently holds any resources to acquire that process's maximum resource allocation. In an unsafe state, no such sequence exists. Thus, an OS can always prevent deadlock when a system is in a safe state by only granting resources that keep the system in a safe state. Conversely, if the system is in an unsafe state, there exists at least one sequence of requests that will deadlock the system regardless of what the OS does.

Problem 6

Define the term priority inversion.

Solution:
Priority inversion is a program bug in which a high priority thread waits for a lock that is held by a low priority thread and the low priority thread is unable to release the lock because a medium priority thread is using the CPU (or other required resources). In effect, the high priority thread is waiting for the medium priority thread.