More Synchronization

January 31 2007

Leftovers

  1. What's the difference between a thread and a process?
  2. Suppose I have a global counter called globalCount initialized to zero. I have 10 threads that execute the following code:
    globalCount++;
    
    When the 10 threads exit, globalCount might not be 10. Why?
  3. Consider the following code:
    void Transfer(Account* from, Account* to, int amount) {
      from->lock->Acquire();
      to->lock->Acquire();
    
      from->balance -= amount;
      to->balance += amount;
    
      to->lock->Release();
      from->lock->Release();
    }
    
    This code can deadlock. What must happen for deadlock to occur? How do you fix it?

Project 1 Questions

Got any?

Lecture Review

  1. Semaphores and Monitors

Synchronization Primitives

You should know the following synchronization primitives like the back of your hand:

Lock::Acquire()
Enter a critical section. Each Lock instance creates a new critical section
while(lock is held)
  block;
lock = held;
Lock::Release()
Leave a critical section
if(another thread is blocked in Acquire())
  unblock other thread;
lock = unheld;
Semaphore::P()
If semaphore value is zero, then block until value is greater than zero. Always decrement semaphore value.
while(value == 0)
  block;
value--;
Semaphore::V()
Increment semaphore value. If a P() is blocked on this semaphore, un-block it.
if(another thread is blocked in P())
  unblock other thread;
value++;
Condition::Wait(monitorLock)
Release() monitor lock, block until signaled, re-Acquire() monitor lock
monitorLock->Release();
block;
monitorLock->Acquire();
Condition::Signal(monitorLock)
Un-block one Wait()ing thread
if(another thread is blocked in Wait())
  unblock other thread;
Condition::Broadcast(monitorLock)
Un-block all Wait()ing threads
for each thread blocked in Wait()
  unblock other thread;

Classic Synchronization Problems

This class covers several classic synchronization problems

  1. Readers and Writers (in lecture)
  2. Bounded Buffer / Producers and Consumers (in lecture)
  3. Dining Philosophers (on homework)
  4. Rendezvous (Mailboxes and Whales in Project 1)

It's very important that you understand how to solve these problems. These problems are considered classic simply because a lot of the synchronization problems you'll run into are variations on these problems. Knowing how to solve these problems will be helpful for homeworks, exams, and life beyond CS120.

Studying solutions to these standard synchronization problems is like studying proofs in CS theory: It is more important to understand the idea behind the solution, instead of memorizing the solution itself. You probably won't get a midterm question that says "Use semaphores to solve bounded buffer." On the other hand, you just might get a midterm question that says "Use semaphores to solve this problem that looks sorta like bounded buffer." Not necessarily bounded buffer of course, but you get the idea.

Questions

  1. Which of the synchronization primitives must be atomic? What could go wrong if they weren't atomic?
  2. Vote Counting: Solve Keith's vote counting problem with semaphores instead of monitors. Here's the problem spec again: We have a population of N voters who asynchronously invoke int vote(int zero_or_one) to vote for their favorite number (0 or 1). vote blocks until a winner can be declared by simple majority. All invocations of vote return the winning number.
  3. Restaurant: We have a restaurant with N chairs and no tables. Customers come and go, but they arrive in parties of M customers. A party must always be seated at the same time, so if there are 4 chairs available, and a party of 5 arrives, the party must wait. On the other hand, if a party of 3 arrives, and 4 chairs are available, they can pull together 3 chairs and sit.

    After the party is seated, they eat for a while, and then they leave, allowing more customers to enter. Clearly, there can never be more than N seated customers at any time, because the restaurant only has N chairs.

    Each party of M customers is represented by a thread that executes the following code:

    SitDown(M)
    // eat
    StandUp(M)
    

    SitDown(M) indicates that a party of size M wants to sit down. A party will start eating as soon as SitDown returns, so SitDown must block until M seats are available. StandUp(M) indicates that a party of size M wants to stand up, freeing M chairs for waiting customers.

    Define SitDown and StandUp first using monitors, then using semaphores.

    Does your solution guarantee that parties of the same size are served in FIFO order? That is, if a party of 2 arrives, then later another party of 2 arrives, can you guarantee that the first party of 2 will always sit first? If not, how would you enforce this policy?