Assignment 4: Implementing Locks and Condition Variables, and Reflecting on Trust

Assignment 4: Implementing Locks and Condition Variables, and Reflecting on Trust

In this assignment you will extend your work in Project 3 by adding locks and condition variables. In addition, you will begin to think about ethical issues related to trust. Here are the learning goals for this assignment:

  • Learn how locks and condition variables can be implemented in a singe-core system.
  • Learn about techniques for establishing trust.

Getting Started

To get started on this assignment, login to the myth cluster and clone the starter repo with this command:

git clone /afs/ir/class/archive/cs/cs111/cs111.1236/repos/assign4/$USER assign4

This will create a new directory assign4 in your current directory and it will clone a Git starter repository into that directory. This assignment builds on Assignment 3, so once you've cloned the starter repo, cd into the assign4 directory and invoke the following command:

make copy_thread_sources

This will copy your code from Assignment 3 into the assign4 directory so that you can use it for this assignment. The command assumes that your work for Assignment 3 is in a directory named assign3 with the same parent directory as assign4. If this isn't the case, the command above will fail; ask for help on Ed if that happens. The files sync.hh and sync.cc contain a skeleton for all of the code you need to write. You will complete the declarations of Mutex and Condition in sync.hh and fill in the bodies of the methods in sync.cc. If you didn't get Assignment 3 fully working, you may need to keep working on it in order to complete this assignment.

The directory also contains a Makefile; if you type make, it will compile your code for both problems together with a test program test.cc, producing an executable file test. You can invoke ./test with an argument giving the name of a test to run (invoke it with no arguments to see a list of available tests). You can also invoke the command tools/sanitycheck to run a series of basic tests on your solution. Try this now: the starter code should compile but almost all the tests will fail.

Mutex and Condition APIs

You will create two C++ classes: Mutex and Condition. The Mutex class has the following methods:

Mutex()
This is the constructor for Mutex objects. It has no arguments.

void lock()
Locks the Mutex. When this method returns, the Mutex must be locked and the calling thread must be the owner. You can assume that the thread does not already own the lock when it invokes this method. If the Mutex is already owned by a different thread when lock is invoked, lock must block until the Mutex is unlocked. If multiple threads block while waiting for a Mutex, they should return from lock in FIFO order.

void unlock()
If there are threads waiting to lock the Mutex, passes ownership of the Mutex to the first waiting thread and wakes it up. If there are no threads waiting, releases the lock.

bool mine()
Returns true if the calling thread owns the lock, false otherwise.

The Condition class has the following methods:

Condition()
This is the constructor for Condition objects.

void wait(Mutex &m)
Releases the Mutex and blocks the thread until notify_one or notify_all has been invoked, then reacquires the Mutex.

void notify_one()
If any threads are blocked on the condition variable, wakes up the first blocked thread in FIFO order. If no threads are blocked, then this method does nothing.

void notify_all()
Wakes up all of the threads currently blocked on the condition variable.

Implementation Milestones

Before you start coding, think a bit about what sort of information you'll need to keep in Mutex and Condition structures. For example, you'll need queues to keep track of threads that have blocked, so you can wake them up in the right order, but how many queues? Once you have thought about this, answer Question 1 in questions.txt.

Milestone 1

Complete the declaration of the Mutex class in sync.hh, and fill in the bodies of the Mutex constructor and the methods lock, unlock, and mine.

You may assume that your code will only run on single-core systems, so disabling interrupts ensures that no other thread will run (you do not need to use spin locks for this assignment). Your solution should look similar to the uniprocessor implementation described in lecture.

As part of this assignment, you will need to use the methods you implemented in Project 3. For example, a thread can block itself by calling Thread::redispatch (with interrupts disabled, of course). Later on, some other thread can wake it up by invoking the schedule method on the blocked sync. It's worth reviewing all of the Thread methods now; you'll need to use most of them for this assignment.

At this point the tests mutex_basic, mutex_many_threads, and mine should pass.

Milestone 2

Complete the declaration of the Condition class in sync.hh, and fill in the bodies of the Condition constructor and the methods wait, notify_one, and notify_all.

At this point the remaining tests (cond_basic, two_conds, notify_all, and lock_vs_notify) should pass.

Note: there should be no need for any of the Condition methods to access the fields of a Mutex object, or vice versa.

Testing

The testing infrastructure for this assignment is similar to what you have used for earlier assignments. The sanitycheck program will run all of the tests, and you can also run tests individually, using gdb when needed. The output of sanitycheck shows the command to run for each test; for example,

./test mutex_basic

will run the first test, in which two threads take turns acquiring a Mutex. Try invoking this command: it prints out a series of messages indicating what the test is doing and any errors it encounters. You can compare this to the output of the sample solution in samples/test_soln. When you're debugging, you'll also find it useful to look at the code in the test harness so you can see exactly what is going on. For example, the code for the mutex_basic test is in the method mutex_basic in test.cc; each test is implemented by a method with the same name as the test.

As always, we do not guarantee that the tests we have provided are exhaustive, so passing all of the tests is not necessarily sufficient to ensure a perfect score. We may run additional tests during grading, and CAs may discover other problems in reading through your code.

Reflecting on and Communicating Trust

As you may know, the Computer Science Department has begun adding short units on ethics to many of its classes. For CS 111 the ethics topic is trust. The overall goal of this topic is to raise your awareness of a variety of issues relating to trust. For this assignment you will consider techniques for establishing trust in a system.

We will use part of lecture on Friday, April 28, to introduce the issues around trust. After you have listened to that material, answer the following questions in questions.txt:

Question 2: suppose you needed to convince another student in CS 111 that they can trust your code for Assignment 3 and 4; how would you go about that? Note: for someone else to trust your code, you may want to consider how you can increase the legitimacy of either the code itself or the creator of the code.

Question 3: suppose that you wanted to have a higher level of trust in your solutions to Assignments 3 and 4 than sanitycheck provides. What additional steps could you take to increase your level of trust that the code will perform as expected?

Software bugs are examples of situations where systems are less trustworthy than we think or would like. They are to difficult to test for, yet they can have significant real-world impact. In 2020, Natalie Silvanovich of Google Project Zero discovered a race condition with Google’s video calling service, Duo. Please read this document, which describes the problem, and then apply Nguyen’s framework of trust as an unquestioning attitude to a real world race condition by answering the questions below in questions.txt:

Question 4: describe the race condition and its impact.

Question 5: in 1-3 sentences, describe one way developers of the Google Duo bug exhibit agential gullibility.

Question 6: in 1-3 sentences, identify two ways a mobile phone user could infer trust in Google Duo.

Question 7: in 1-3 sentences, identify one way a mobile phone user could substitute the need to trust Google Duo.

Submitting Your Work

Once you are finished working and have saved all your changes, submit by running tools/submit. Make sure that you have answered the questions in questions.txt before submitting.

We recommend you do a trial submission in advance of the deadline to allow time to work through any snags. You may submit as many times as you like; we will grade the latest submission. Submitting a stable but unpolished/unfinished version is like an insurance policy. If the unexpected happens and you miss the deadline to submit your final version, the earlier submit will earn points. Without a submission, we cannot grade your work. You can confirm the timestamp of your latest submission in your course gradebook.

Grading

Here is a recap of the work that will be graded on this assignment:

  • questions.txt: answer the questions, including the question about queues in the implementation of Mutex and Condition as well as the questions about trust.
  • sync.hh and sync.cc: flesh out the Mutex and Condition classes.

We will grade your code using the provided sanity check tests and possible additional autograder tests. We will also review your code for style and complexity. Check out our course style guide for tips and guidelines for writing code with good style!