Lab 2: Threads & Semaphores
Operating Systems
Due: Wednesday, October 27th, 12:20 PM

(VIP students have 2 weeks from when you receive it.)


We are using the Nachos educational software which is intended to provide basic OS functionality that may be extended to enable students to develop and implement OS components. This handout describes how to set up your software environment, which files you need to read and understand for this assignment, the lab 2 assignment, how to turn in this assignment (as well as others), some programming tips, and sample output for this assignment.

Getting Started

Understanding the Nachos software

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 two synchronization problems.

Here are some pointers to overviews of the thread and synchronization mechanisms in Nachos (these are hot links if you read this on the Web):

After installing the software, your first step is to read and understand the partial thread system we have written for you. This thread system implements thread fork, thread completion, along with semaphores for synchronization. Run the program nachos in the threads directory for a simple test of our code. Trace the execution path (by hand) for the simple test case we provide. (The main program for threads/nachos is in threads/main.cc. It has been compiled with THREADS defined, but USER_PROGRAM, FILESYS, and NETWORK undefined.)

 When you trace the execution path, it is helpful to keep track of the state of each thread and which procedures are on each thread's execution stack. You will notice that when one thread calls SWITCH, another thread starts running, and the first thing the new thread does is to return from SWITCH. We realize this comment will seem cryptic to you at this point, but you will understand threads once you understand why the SWITCH that gets called is different from the SWITCH that returns. (Note: because gdb does not understand threads, you will get bizarre results if you try to trace in gdb across a call to SWITCH.)

 The files you need to make sure you understand completely for this assignment are listed below.

main.cc, threadtest.cc (in the threads directory)
a simple test of our thread routines.

 
synch.h, synch.cc (in the threads directory)
synchronization routines: semaphores, locks, and condition variables.


In addition, you need to use (but not modify) parts of some other files:

interrupt.h (in the machine directory)
manage enabling and disabling interrupts as part of the machine emulation.

 
list.h (in the threads directory)
generic list management.

 
scheduler.h (in the threads directory)
manages the list of threads that are ready to run. You will need to use the ReadyToRun method to indicate that a thread is ready to run.

 
system.h (in the threads directory)
Nachos startup/shutdown routines. You will need to use the variable currentThread to identify the thread currently running.

 
thread.h (in the threads directory)
thread data structures and thread operations such as thread fork, thread sleep and thread finish.

 
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 help you test your programs, the 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 one, create multiple writers and readers 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 limitation 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 defined 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.

The Assignment

For this assignment and all subsequent ones, you may work in groups of 1 or 2.
  1. (15 points). Implement locks. We have provided the public interface to locks 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 locks. You should only need to modify synch.h and synch.cc. You should not need to modify the public parts of synch.h. (Hint: You may find semaphores and primitive thread routines useful as building blocks. Look at the Salsa tutorial for additional hints.)

  2.  
  3. (35 points). Implement reader/writer communication using semaphores and/or locks. (A solution to the readers and writers problem is described in Silberschatz, Peterson and Galvin, using semaphores.)

  4. A data object is to be shared among several concurrent processes. Some of the processes - Readers want only to read the content of the shared object. When a reader, reads the content of the shared object(a single character), it should produce the following output to the screen:

      Reader <id> reading <character>
    where <id> is the unique identifier of the Reader.

    The Writer processes want to update (i.e. to read and write) the shared object (a single character which it obtains from a file called writer_input.txt). When a writer updates the shared object, it should produce the following output on the screen:

      Writer <id> writing <character>
    where <id> is the unique identifier of the writer and <character> is the character just written on to the shared object.

    Test your solution with multiple readers and writers. Of course, with multiple readers and writers, the output will illustrate the concurrency provided by threads.

    Your code should prompt for the number of readers and the number of writers, in that order. You can assume that a valid integer is provided by the user for each of these. You should be sure that your code works for different input files as we may test it with files other than what you provide.

     The skeleton code to get you started is in /courses/cs300/cs377/cs377/public_html/labs/writer.cc.
     

  5. (15 points).Condition variables. Implement condition variables. We have provided the public interface to 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 condition variables. You should only need to modify synch.h and synch.cc. You should not need to modify the public parts of synch.h.
  6. (35 points). The Cigarette-Smokers Problem. Implement a solution to the following problem using locks and condition variables. Consider a system with three smoker processes and one agent process. Each smoker continously rolls a cigarette and then smokes it. But to roll and smoke a cigarette, the smoker needs three ingredients : tobacco, paper and matches. One of the smoker processes has paper, another has tobacco, and the third has matches. The agent has an infinite supply of all the three materials. The agent places any two of the ingredients on the table(randomly). The smoker who has the remaining ingredient then makes and smokes a cigarette, signalling the agent on completion. The agent then puts out another two of the three ingredients, and the cycle repeats. Write a program to synchronize the smokers and the agent. When an agent places ingredients on the table, it outputs "Agent putting  <item a> and <item b>" where <item a> and <item b> are the two items selected by the agent. If Smoker A gets to smoke, it outputs, "Smoker A smoking".

  7. The skeleton code to get you started is in /courses/cs300/cs377/cs377/public_html/labs/smoker.cc.
     

How to Turn in Lab 2

All of the following files and hard copies must be turned in to get full credit for a question.
  1. Create a directory in your course directory called ~/cs377/lab2. Make the directory group writable (chmod g+w ~/cs377/lab2). Put the following files in this directory.

  2. For part 1 and 3: synch.h, synch.cc
    For part 2:  reader.cc, reader
    For part 4: smoker.cc, smoker
    README

     To create your solutions for parts 2 and 4, do the following in the threads directory:

  3. Hand in a hard copy of all the files that you modified.
  4. Hand in a hard copy of a README file identifying your lab partner (if you have one) and containing an outline of what you did for the assignment. It should also explain and motivate your design choices. Keep it short and to the point. Your ~/cs377/lab2 directory should also contain this file.

  5. If your implementation does not work, you should also document the problems in the README, preferably with your explanation of why it does not work and how you would solve it if you had more time. Of course, you should also comment your code. We can't give you credit for something we don't understand!
  6. Hand in a hard copy showing sample output from your programs.
  7. Individual Group Assessment (for students working in groups only)
  8. 10 percent of your lab grade will come from your participation in this project as a member of your two person group.
    What you need to turn in (each person individually):
    Turn in a hard copy of your assessment of the division of labor in the group in the format shown below.  If you give yourself a 50% and your partner gives you a 50%, you will get the whole 10 points.  If you give your partner a 40% and your partner gives himself or herself a 40%, he or she will get 8 points.  And so on...
    Work Distribution
    My Name:  ABC 60%
    My Partner:  XYZ 40%

Tips

Sample Output

> reader -rs 23232
Please enter the number of readers:  4
Please enter the number of writers:  2
writer 0 incremented integer to 1
writer 0 incremented integer to 2
writer 0 incremented integer to 3
writer 1 incremented integer to 4
reader 0 integer value is 4
reader 0 integer value is 4
writer 0 incremented integer to 5
writer 0 incremented integer to 6
writer 1 incremented integer to 7
reader 0 integer value is 7
reader 0 integer value is 7
reader 1 integer value is 7
reader 1 integer value is 7
reader 1 integer value is 7
writer 0 incremented integer to 8
writer 1 incremented integer to 9
writer 1 incremented integer to 10
reader 0 integer value is 10
reader 0 integer value is 10
reader 1 integer value is 10
writer 0 incremented integer to 11
writer 1 incremented integer to 12
writer 1 incremented integer to 13
writer 1 incremented integer to 14
reader 0 integer value is 14
reader 0 integer value is 14
writer 0 incremented integer to 15
writer 0 incremented integer to 16
reader 0 integer value is 16
reader 0 integer value is 16
reader 0 integer value is 16
reader 1 integer value is 16
^C (to halt)

> smoker -rs 2
Agent putting  tobacco and matches
Smoker 2 smoking
Agent putting paper and matches
Smoker 1 smoking
Agent putting paper and matches
Smoker 2 smoking
Agent putting tobacco and paper
Smoker 3 smoking
Agent putting tobacco and matches
Smoker 1 smoking
Agent putting tobacco and paper
Smoker 1 smoking
Agent putting paper and matches
Smoker 3 smoking
Agent putting tobacco and matches
Smoker 2 smoking
Agent putting paper and matches
Smoker 2 smoking
Agent putting paper and matches
Smoker 3 smoking
Agent putting tobacco and matches
Smoker 1 smoking
^C