UNIVERSITY OF WISCONSIN-MADISON
Computer Sciences Department
CS 537
Spring 2000
A. Arpaci-Dusseau
Quiz #2 Solutions: Wednesday, February 16

Problem 1: Short Answer [30 pts]

1. What are the three types of low-level operations that higher-level synchronization operations (e.g., locks, semaphores, and monitors) can be built upon?
  1. Load and store of a single word
  2. Disabling and enabling interrupts
  3. Special atomic instructions such as TestAndSet and Swap

Problem 2: Identifying Correct Implementations [60 pts: 15 pts each]

The following code segment describes a simple BankAccount class with a single method: deposit. In the main method of the program (not shown), a single BankAccount, b, is created for a customer. Two teller threads are created and started, each of which calls the method b.deposit().

Do the following code segments correctly implement a critical section? If not, list a critical section requirement that is violated (one of three) and show a sample interleaving of instructions that leads to that problem, clearly designating which teller thread is executing each stream of instructions. If the implementation is correct, you do not need to prove it, just state that it is.

class BankAccount {

    private boolean lock[] = {false, false};
    private int balance;
    private int accountNumber;
    
    BankAccount(int acct) {
        accountNumber = acct;
        balance = 0;
    } 

    // tellerID is either 0 or 1
    public void deposit(int amount, int tellerID) {
        lock[tellerID] = true;
        while (lock[1-tellerID]);
        balance += amount;
        lock[tellerID] = false;
    }
}
Does not guarantee progress (i.e., may cause deadlock).
Thread 0                        Thread 1

lock[0] = true;
                                lock[1] = true;
                                while (lock[0]); 
while (lock[1]); 
class BankAccount {

    private int turn = 0;
    private boolean lock[] = {false, false};
    private int balance;
    private int accountNumber;
    
    BankAccount(int acct) {
        accountNumber = acct;
        balance = 0;
    }

    // tellerID is either 0 or 1
    public void deposit(int amount, int tellerID) {
        lock[tellerID] = true;
        if (tellerID == 1) {
            turn = 1;
            while (lock[1-tellerID]);
        } else { /* tellerID == 2 */
            while (lock[1-tellerID] && turn == 1);
        }
        balance += amount;
        turn = 0;
        lock[tellerID] = false;
    }
}
Does not guarantee progress (i.e., may cause deadlock).
Thread 0                            Thread 1

lock[0] = true;
                                    lock[1] = true;
                                    turn = 1;
                                    while (lock[0]); 
while (lock[1] && turn == 1); 
class BankAccount {

    private int turn = 0;
    private boolean lock = {true, true};
    private int balance;
    private int accountNumber;
    
    BankAccount(int acct) {
        accountNumber = acct;
        balance = 0;
    }

    // tellerID is either 0 or 1
    public void deposit(int amount, int tellerID) {
        lock[tellerID] = true;
        turn = 1 - tellerID;
        while (lock[1-tellerID] && turn == (1 - tellerID));
        balance += amount;
        lock[tellerID] = false;
    }
}
Violates the progress requirement.
Thread 0                            Thread 1

lock[0] = true;
turn = 1;
while (lock[1] && turn == 1);
/* stuck until Thread 1 executes. */
Even though the example will work if Thread 1 tries to enter the critical section:
                                    lock[1] = true;
                                    turn = 0;
                                    while (lock[0] && turn == 0);
while (lock[1] && turn == 1);
/* continues */
it depends on Thread 1 eventually trying to enter the critical section for Thread 0 to proceed. This is precisely the same problem we saw in Lecture with the simple implementation using only the turn variable. The progress of Thread 0 cannot depend on a Thread that is executing elsewhere; it can depend only on threads in the entry or exit code of critical section or in the critical section itself.
class BankAccount {

    private boolean lock = false;
    private int balance;
    private int accountNumber;
    
    BankAccount(int acct) {
        accountNumber = acct;
        balance = 0;
    }

    public void deposit(int amount) {
        boolean key = true;
        do {
            Swap(lock, key);
        } while (key == true);
        balance += amount;
        lock = false;
    }
}
Does not guarantee bounded waiting: one process may starve
Thread 0                            Thread 1

do {
    Swap(lock == false, key == true);
    --> key = false;
} while (key == true);
                                    /* repeats until context-switch to T0 */
                                    do { 
                                       Swap(lock == true, key == true);
                                       --> key = true;
                                    } while (key == true); 

balance += amount;
lock = false;

/* if no context-switch to T1, will
   acquire lock next time too */
do {
    Swap(lock == false, key == true);
    --> key = false;
} while (key == true);
balance += amount;
/* pattern may repeat forever */