\documentstyle{article}

\begin{document}

\begin{flushleft}
{\Large \bf CS-230 Programming Assignment \#1} \\
\vspace*{0.2in}
Due date: Friday, November 11, 1994 in class.
\end{flushleft}

\vspace{.2in}

In this assignment, we give you part of a working thread system; 
your job is to complete it, and then to use it to solve
several synchronization problems.  


Properly synchronized code should work no matter what order the 
scheduler chooses to run the threads on the ready list.  In other 
words, we should be able to put a call to Thread::Yield (causing the scheduler
to choose another thread to run) anywhere in your code where interrupts 
are enabled without changing the correctness of your code.   
You will be asked to write properly synchronized code as part of the 
later assignments, so understanding how to do this is crucial to
being able to do the project.

To aid you in this, code linked in with Nachos will cause Thread::Yield 
to be called on your behalf in a repeatable but unpredictable way.
Nachos code is repeatable in that if you call it repeatedly with the 
same arguments, it will do exactly the same thing each time.
However, if you invoke ``nachos -rs \#'', with a different number each
time, calls to Thread::Yield will be inserted at different places in the code.

Make sure to run various test cases against your solutions to 
these problems; for instance, for part two, create multiple producers
and consumers and demonstrate that the output can vary, within certain 
boundaries.

%Warning: in our implementation of threads, each thread is assigned a 
%small, fixed-size execution stack.  This may cause bizarre problems 
%(such as segmentation faults at strange lines of code) if you declare 
%large data structures to be automatic variables (e.g., ``int buf[1000];'').
%You will probably not notice this during the semester, but if you do,
%you may change the size of the stack by modifying the StackSize define in 
%switch.h.

%Although the solutions can be written as normal C routines, you will 
%find organizing your code to be easier if you structure your code
%as C++ classes.  Also, 
There should be no busy-waiting in any of your 
solutions to this assignment.  

\begin{description}

\item{1.}
Implement locks and condition variables.  You may either use semaphores 
as a building block, or you may use more primitive thread routines (such
as Thread::Sleep).  We have provided the public interface to locks 
and condition variables in synch.h.  You need to define the private 
data and implement the interface.  Note that it should not take you very 
much code to implement either locks or condition variables.

A lock is a variable that handles the operations Acquire() and Release().
When lock::Acquire() is executed and the lock is free, the lock state is
simply set to busy and the function returns.  If the lock is busy, then
the thread calling Acquire() blocks until the lock is free.  When 
lock->Release() is called the lock is set to free and a process
blocked on the lock is made ready.  If a thread releases a lock it has
not acuqired, the lock does not change.

A condition variable handles the operations Wait(lock),
Signal(lock), and Broadcast(lock).  
When a thread executes condition::Wait(lock), it
releases the lock, sleeps and then acquires the lock again.
When a thread executes condition::Signal(lock) a thread waiting on 
the condition is made ready but the lock is {\em not} released.
Executing condition::Broadcast(lock) wakes up all threads waiting on
the condition.

\item{2.}
Implement producer/consumer communication through a bounded buffer,  
using locks and condition variables.  (A solution to the bounded buffer 
problem is described in Silberschatz and Galvin, using 
semaphores.)

The producer places characters from the string "Hello world" into the 
buffer one character at a time; it must wait if the buffer is full.
The consumer pulls characters out of the buffer one at a time
and prints them to the screen; it must wait if the buffer is empty.
Test your solution with a multi-character buffer and with multiple 
producers and consumers.  Of course, with multiple producers or consumers,
the output display will be gobbledygook; the point is to illustrate
that multiple producers and consumers can share the same buffer.

\item{3.} The local laundromat has just entered the computer age.
As each customer enters, he or she puts coins into
slots at one of two stations and types in
the number of washing machines he/she will need.  The stations
are connected to a central computer that
automatically assigns available machines and outputs
tokens that identify the machines to be used.  The
customer puts laundry into the machines and inserts each
token into the machine indicated on the token.  When a machine finishes
its cycle, it informs the computer that it is available again.
The computer maintains an array {\em available[NMACHINES]} whose elements are
non-zero if the corresponding machines are available (NMACHINES is
a constant indicating how many machines there are in the
laundromat), and a semaphore {\em nfree} that indicates how many 
machines are available.

The code to allocate and release machines is as follows:

\begin{verbatim}
int allocate()	/* Returns index of available machine.*/
{
  int i;

  P(nfree);	/* Wait until a machine is available */
  for (i=0; i < NMACHINES; i++)
    if (available[i] != 0) {
      available[i] = 0;
      return i;
    }
}

release(int machine)	/* Releases machine */
{
  available[machine] = 1;
  V(nfree);
}
\end{verbatim}

The {\em available} array is initialized to all ones, and {\em nfree} is
initialized to NMACHINES.

\begin{description}

\item{(a)} It seems that
if two people make requests at the two stations at the same time, they
will occasionally be assigned the same machine.  This has resulted in
several brawls in the laundromat, and you have been called in by the
owner to fix the problem.  Assume that one thread handles each
customer station.  Explain how the same washing machine can be assigned
to two different customers.

\item{(b)} Modify the code to eliminate the problem.

\item{(c)} Re-write the code to solve the synchronization problem 
using locks and condition variables instead of semaphores.

\end{description}


\item{4.} You've just been hired by Mother Nature to help her out with the
chemical reaction to form water, which she doesn't seem to be
able to get right due to synchronization problems.  The trick is to
get two H atoms and one O atom all together at the same time.  The
atoms are threads.  Each H atom invokes a procedure {\em hReady} when it's
ready to react, and each O atom invokes a procedure {\em oReady}
when it's ready.  For this problem, you are to write the code for
{\em hReady} and {\em oReady}.  The procedures must delay until there
are at least two H atoms and one O atom present, and then one
of the procedures must call the procedure {\em makeWater} (which
just prints out a debug message that water was made).
After the {\em makeWater} call, two 
instances of {\em hReady} and one instance of {\em oReady} should return.
Write the code for {\em hReady} and {\em oReady} using
either semaphores or locks and condition variables for synchronization.
Your solution must avoid starvation and busy-waiting.


\end{description}

\end{document}
