Assignment 4: Implementing Locks and Condition Variables, and Reflecting on Trust
In this assignment you will extend your work in Assignment 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.1246/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 (if this is the
case, the course staff is happy to give you lots of help to finish
Assignment 3).
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 Assignment 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.
A special unit in lecture introduced some basic concepts related to
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 the notion 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 exhibited 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
andsync.cc
: flesh out theMutex
andCondition
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!