These problems should be done on your own. We're not going to be grading them strictly (we'll mainly look at whether you attempted them). But they will be reinforcing knowledge and skills, so you should totally work through them carefully.
Time-of-check-to-time-of-use (TOCTTOU) bugs
Alice and Bob each have an account in a bank. Bob wants to transfer money to Alice. (We write the code below in terms of the synchronization primitives that you will see in Lab 3.)
// assume all the variables are initialized correctly
double alice_balance, bob_balance;
smutex_t mtx;
bool
transferBob2Alice(double trans) {
if (bob_balance > trans) {
smutex_lock(&mtx);
bob_balance = bob_balance - trans;
alice_balance = alice_balance + trans;
smutex_unlock(&mtx);
return true;
}
return false;
}
The implementation of function transferBob2Alice
is not correct.
- What's wrong? (Give a problematic interleaving.)
- State the fix in one sentence.
Deadlock
The bank decides to use fine-grained locking. Here is its implementation:
// assume all the variables are initialized correctly
double balance[2]; // 0 for alice, 1 for bob
smutex_t mtx[2]; // 0 for alice, 1 for bob
bool transfer(int from, int to, double trans) {
smutex_lock(&mtx[from]);
smutex_lock(&mtx[to]);
bool result = false;
if (balance[from] > trans) {
balance[from] = balance[from] - trans;
balance[to] = balance[to] + trans;
result = true;
}
smutex_unlock(&mtx[to]);
smutex_unlock(&mtx[from]);
return result;
}
- Write down an interleaving that results in deadlock.
- Keeping the same data structures, rewrite
transfer()
to eliminate the possibility of deadlock
Sleeping barber
This is a potentially useful (if convoluted) example to exercise your understanding of how to use mutexes and conditional variables. It is a well-known concurrency problem. The writeup and solution are due to Mike Dahlin (who used to be on the faculty at The University of Texas at Austin). He asked this question on a midterm in 2002.
Work through the problem on your own; we will post the solution next week.
A shop has a barber, a barber chair, and a waiting room with NCHAIRS chairs. If there are no customers present, the barber sits in the chair and falls asleep. When a customer arrives, the customer wakes the sleeping barber. If an additional customer arrives while the barber is cutting hair, the customer sits in a waiting room chair if one is available. If no chairs are available, the customer leaves the shop. When the barber finishes cutting a customer’s hair, the barber tells the customer to leave; then, if there are any customers in the waiting room, the barber announces that the next customer can sit down. Customers in the waiting room get their hair cut in FIFO order.
The barber shop can be modeled as 2 shared objects:
A
BarberChair
, with the methodsnapInChair()
,wakeBarber()
,sitInChair()
,cutHair()
, andtellCustomerDone()
. The BarberChair must have a state variable with the following states:EMPTY
,BARBER_IN_CHAIR
,LONG_HAIR_CUSTOMER_IN_CHAIR
,SHORT_HAIR_CUSTOMER_IN_CHAIR
.Note that neither a customer or barber should sit down until the previous customer is out of the chair (
state == EMPTY
).Note that
cutHair()
must not return until the customer is sitting in the chair (LONG_HAIR_CUSTOMER_IN_CHAIR
).Note that a customer should not get out of the chair (that is, return from
sitInChair()
) until the customer’s hair is cut (SHORT_HAIR_CUSTOMER_IN_CHAIR
).The barber should get in the chair (
BARBER_IN_CHAIR
) only if no customers are waiting.You may need additional state variables.
A
WaitingRoom
, with the methodsenter()
andcallNextCustomer()
.enter()
returnsWR_FULL
if the waiting room is full or (immediately or eventually) returnsMY_TURN
when it is the caller’s turn to get their hair cutcallNextCustomer()
returnsWR_BUSY
orWR_EMPTY
depending on if there is a customer in the waiting room or not. Customers are served in FIFO order.
Thus, each customer thread executes the code:
Customer(WaitingRoom *wr, BarberChair *bc)
{
status = wr->enter();
if (status == WR_FULL) {
return;
}
bc->wakeBarber();
bc->sitInChair(); // Wait for chair to be EMPTY
// Make state LONG_HAIR_CUSTOMER_IN_CHAIR
// Wait until SHORT_HAIR_CUSTOMER_IN_CHAIR
// then make chair EMPTY and return
return;
}
The barber thread executes the code:
Barber(WaitingRoom *wr, BarberChair *bc)
{
while (1) { // A barber’s work is never done
status = wr->callNextCustomer();
if (status == WR_EMPTY) {
bc->napInChair(); // Set state to BARBER_IN_CHAIR; return with state EMPTY
}
bc->cutHair(); // Block until LONG_HAIR_CUSTOMER_IN_CHAIR;
// Return with SHORT_HAIR_CUSTOMER_IN_CHAIR
bc->tellCustomerDone(); // Return when EMPTY
}
}
Write the code for the WaitingRoom
class and the BarberChair
class. Use locks and condition variables for synchronization. Follow the coding standards specified in Mike Dahlin’s Coding Standards for Threads Programming, which you will also follow in Lab 3.
Hints (and requirement reminders):
remember to start by asking for each method “when can a thread wait?” and writing down a synchronization variable for each such situation.
List the member variables of class
WaitingRoom
including their type, their name, and their initial value. Then write the methods forWaitingRoom
.List the member variables of class
BarberChair
including their type, their name, and their initial value. Then write the methods forBarberChair
.
Handing in the homework
Use Gradescope; you can enroll in our course with entry code 4J462V.