CS 475, Spring 2018, Homework 2, Due Feb 21, 2pm Feb 23, 11:59pm
Start by downloading the handout:
Clarifications:
- 2/19: Added hints for collection exceptions from threads; copying the lists when returning
- 2/18: Strike the sentence “Finally, check that the total number of tags on all of those files matches the count returned by listTags.” from testP2ConcurrentDeleteTagTagFile
- 2/17: Clarify in ‘help’ output, that add tag is used to “Add a tag to the list of known tags” (like the addTag method)
- 2/14: Clarify: listAllFiles should not throw any exception (previously said “throws exception if tag does not exist”, but does not take an argument)
- 2/13: You do not need to use any of the fields that I included on Tag or TaggedFile – this was accidental. You may find it easier to (ncurrrectly) manage relationships between tags and files a different way – you should feel free to use or remove those fields.
- 2/13: Revised testP1AddAndListTag spec
- 2/12: Added hint code in part 4 (Java snippet that creates a bunch of threads)
- 2/11: catall should print the contents of each file with a line break in between. You should not print the name of each file.
- 2/10: You are free to use any standard (java.* or javax.*) API/library
- 2/10: You are free to add new methods or member variables to FileTagManager, Tag, TaggedFile, or ConcurrentTests. But: you must make sure that your ConcurrentTests only references the interface/abstract methods (so that OUR ConcurrentTests can run on your code)
Please post questions on Piazza
The purpose of this assignment is to get you familiar with basic concurrency control topics in Java. We will build on the concepts of HW1 (the shell), focusing specifically on building a file management system. In particular: it has recently become more and more popular to organize files by keywords and tags, rather than by their location in a hierarchical filesystem. Users of Mac OS X might find themselves turning more to using Spotlight to open documents (rather than manually locating them in a hierarchical path), and Windows users might find themselves using Cortana to do the same.
The remainder of the assignments (and project) in this class will involve building parts of an intelligent file management tool, which you could use to browse and search through your files. We will call this tool VCFS (VeryCoolFileSystem).
For this first assignment, your task will be to build a Java application that allows users to list and tag their files. You will build it so that multiple users can interact with it simultaneously without corrupting any of the internal structures. In the next assignment (HW3), we will re-envision VCFS to use a client-server architecture.
Your primary interface for debugging your VCFS will be a shell driver that we have provided. We have provided stubs for all of the various commands you will need to implement, with a wrapper so that you can interact with VCFS interactively. When you run the compiled jar file, you’ll get a command prompt, like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
cs475sh help
AVAILABLE COMMANDS
Built-In Commands
clear: Clear the shell screen.
exit, quit: Exit the shell.
help: Display help about available commands.
script: Read and execute commands from a file.
stacktrace: Display the full stacktrace of the last error.
Command
add-tag: Add a tag to the list of known tags
cat: Cat a file
cat-all: Cat all files matching a tag
delete-tag: Delete a tag
echo: Echo text into a file
echo-all: Echo text into all files matching a tag
edit-tag: Edit tag name
get-tags: List all tags on a file
list-files: List the files
remove-tag: Remove a tag from a file
tag-file: Add a tag to a file
tags: List the tags
|
As you implement the various functionality in the parts below, the commands above will begin to work.
We have also provided a baseline suite of unit tests, which you can execute by calling mvn -Ptest test
General requirements:
We have provided you a baseline implementation of VCFS that handles all user interaction, but does not do anything (that is, you will not need to implement any UI or command processors). You may not include any additional libraries (e.g. download and and require additional JARs), although feel free to use any of the various libraries included with Java 8 or already included by the starter project.
Your VCFS will be compiled and tested using apache maven, which automatically downloads the various dependencies for VCFS and executes the provided JUnit tests. Please install Maven on your computer. Unfortunately, Maven is not installed on Zeus, however you can download the most recent version (e.g. apache-maven-3.5.2-bin.zip) and unzip it in your home directory. Then, to run maven, type ~/apache-maven-3.5.2/bin/mvn in the snippets below (instead of just mvn). Note that you can easily import maven projects into eclipse and IntelliJ.
To compile and run your shell, run mvn package and then run java -jar target/filemanager-0.0.1-SNAPSHOT.jar. You’ll notice that the text-mode interface we’ve provided for you has a handy help command
Your shell will be automatically graded for correctness (note that there will be a manual grading phase to check hard-to-automatically-catch concurrency issues). Included with your handout is a simple version of the (large) test script that we will use to grade your assignment. Upon submitting your assignment, our server will automatically compile and test your assignment and provide you with test results. You can resubmit an unlimited number of times until the deadline. To run these tests, simply execute mvn -Ptest test (of course, if you do this first off, you’ll see that they all fail!)
Note: Your code must compile and run on the autograder, under Java 8. It is unlikely that you will have any difficulties developing on a Mac, Linux or Windows. When you feel satisfied with implementing one phase of the assignment, submit to AutoLab and verify that AutoLab agrees.
Tips and References
You might find it useful to reference the official Java tutorial on concurrency, which does a nice job outlining at a high level how you can create threads and locks. If you find any useful references, please feel free to share them on Piazza and we will update this page as well.
Part 1: The Tag Database (20%)
Your VCFS tool will allow users to maintain a list of Tags. In part 2, we’ll look at labeling files with tags. Users can interact with VCFS to create new tags, rename existing tags, and delete existing tags. The key focus for this part of the assignment will be to implement this functionality Thread Safe. You will likely find it very easy to implement this behavior that will work in a single-threaded situation, but it can become tricky when multiple threads are interacting with the same VCFS. Of the 20% that Part 1 will count towards your grade on this assignment, the breakdown will be roughly: 5% for implementing this functionality, and 15% for implementing it with correct concurrency control.
You will implement this behavior in edu.gmu.cs475.FileTagManager, specifically in the following four methods:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
/**
* List all currently known tags.
*
* @return List of tags (in any order)
*/
public abstract Iterable<? extends ITag> listTags();
/**
* Add a new tag to the list of known tags
*
* @param name
* Name of tag
* @return The newly created Tag object
* @throws TagExistsException
* If a tag already exists with this name
*/
public abstract ITag addTag(String name) throws TagExistsException;
/**
* Update the name of a tag, also updating any references to that tag to
* point to the new one
*
* @param oldTagName
* Old name of tag
* @param newTagName
* New name of tag
* @return The newly updated Tag object
* @throws TagExistsException
* If a tag already exists with the newly requested name
* @throws NoSuchTagException
* If no tag exists with the old name
*/
public abstract ITag editTag(String oldTagName, String newTagName) throws TagExistsException, NoSuchTagException;
/**
* Delete a tag by name
*
* @param tagName
* Name of tag to delete
* @return The Tag object representing the tag that was deleted
* @throws NoSuchTagException
* If no tag exists with that name
* @throws DirectoryNotEmptyException
* If tag currently has files still associated with it
*/
public abstract ITag deleteTag(String tagName) throws NoSuchTagException, DirectoryNotEmptyException;
|
Note the following requirements:
- You should feel free to add whatever methods you would like to the Tag or FileTagManager classes
- Your code must be thread-safe: concurrent calls to any of these methods (or any other method in FileTagManager) should not have any races. If two concurrent calls are made to addTag to add the same tag, exactly one of them must throw a TagExistsException, and the other must succeed (similarly for editTag and deleteTag). listTags must be tolerant of other threads concurrently calling add/edit/deleteTag.
- Your code must not over synchronize. We will deduct marks for solutions that include unnecessary locks.
- You must not store any state in static fields
- You do not need to use any of the fields that I included on Tag or TaggedFile – this was accidental. You may find it easier to (correctly) manage relationships between tags and files a different way – you should feel free to use or remove those fields.
Hint: In order to make it thread-safe to iterate over the result of listTags, you will need to return a new collection containing the entire contents of the original
Precise grading breakdown:
- Non-Concurrent issues: 5 points
- 8 JUnit tests, 5/8 points each
- Concurrent issues: 15 points
- 3 JUnit tests, 3 points each
- 6 points from manual grading
Part 2: File Operations (20%)
Once you allow users to create and edit tags, you will implement file-oriented functionality, allowing users to browse files, tag them, and edit those tags. The first method you will implement is init(List<Path> files);
1
2
3
4
5
6
7
|
/**
* Initialize your FileTagManager with the starting set of files.
* You do not need to persist tags on files from run to run.
* Each file should start with the default tag "untagged"
* @param files Starting list of files to consider
*/
public abstract void init(List<Path> files);
|
This function will be called automatically when VCFS starts up. In init, you will initialize VCFS, creating a special starting tag “untagged.” Every file that exists (when VCFS starts) should receive this tag.
Next, implement the following five core file-oriented methods:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
|
/**
* List all files, regardless of their tag
*
* @return A list of all files. Each file must appear exactly once in this
* list.
*/
public abstract Iterable<? extends ITaggedFile> listAllFiles();
/**
* List all files that have a given tag
*
* @param tag
* Tag to look for
* @return A list of all files that have been labeled with the specified tag
* @throws NoSuchTagException
* If no tag exists with that name
*/
public abstract Iterable<? extends ITaggedFile> listFilesByTag(String tag) throws NoSuchTagException;
/**
* Label a file with a tag
*
* Files can have any number of tags - this tag will simply be appended to
* the collection of tags that the file has. However, files can be tagged
* with a given tag exactly once: repeatedly tagging a file with the same
* tag should return "false" on subsequent calls.
*
* If the file currently has the special tag "untagged" then that tag should
* be removed - otherwise, this tag is appended to the collection of tags on
* this file.
*
* @param file
* Path to file to tag
* @param tag
* The desired tag
* @returns true if succeeding tagging the file, false if the file already
* has that tag
* @throws NoSuchFileException
* If no file exists with the given name/path
* @throws NoSuchTagException
* If no tag exists with the given name
*/
public abstract boolean tagFile(String file, String tag) throws NoSuchFileException, NoSuchTagException;
/**
* Remove a tag from a file
*
* If removing this tag causes the file to no longer have any tags, then the
* special "untagged" tag should be added.
*
* The "untagged" tag can not be removed (return should be false)
*
* @param file
* Path to file to untag
* @param tag
* The desired tag to remove from that file
* @returns True if the tag was successfully removed, false if there was no
* tag by that name on the specified file
* @throws NoSuchFileException
* If no file exists with the given name/path
* @throws NoSuchTagException
* If no tag exists with the given name
*/
public abstract boolean removeTag(String file, String tag) throws NoSuchFileException, NoSuchTagException;
/**
* List all of the tags that are applied to a file
*
* @param file
* The file to inspect
* @return A list of all tags that have been applied to that file in any
* order
* @throws NoSuchFileException
* If the file specified does not exist
*/
public abstract Iterable<? extends ITag> getTags(String file) throws NoSuchFileException;
|
As with Part 1, Of the 20% that Part 2 will count towards your grade on this assignment, the breakdown will be roughly: 5% for implementing this functionality, and 15% for implementing it with correct concurrency control. Note the following requirements (generally the same as from part 1):
- You should feel free to add whatever methods you would like to the Tag, TaggedFile or FileTagManager classes
- Your code must be thread-safe: concurrent calls to any of these methods (or any other method in FileTagManager) should not have any races. If two concurrent calls are made to tagFile to add the same tag, exactly one of them must return true and the other must return false (similarly for removeTag). getTags and listFilesByTag must be tolerant to other threads tagging files concurrently.
- Your code must not over synchronize. We will deduct marks for solutions that include unnecessary locks.
- You must not store any state in static fields
Precise grading breakdown:
- Non-Concurrent issues: 5 points
- 8 JUnit tests, 5/8 points each
- Concurrent issues: 15 points
- 3 JUnit tests, 3 points each
- 6 points from manual grading
Part 3: Bulk Operations (20%)
There are four remaining methods that you will need to implement, which will support the bulk operations ( echo-all and cat-all). Note the documentation on these two methods:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
/**
* Prints out all files that have a given tqg. Must internally synchronize
* to guarantee that the list of files with the given tag does not change
* during its call, and that each file printed does not change during its
* execution (using a read/write lock). You should acquire all of the locks,
* then read all of the files and release the locks. Your code should not
* deadlock while waiting to acquire locks.
*
* @param tag
* Tag to query for
* @return The concatenation of all of the files
* @throws NoSuchTagException
* If no tag exists with the given name
* @throws IOException
* if any IOException occurs in the underlying write
*
*/
public abstract String catAllFiles(String tag) throws NoSuchTagException, IOException;
/**
* Echos some content into all files that have a given tag. Must internally
* synchronize to guarantee that the list of files with the given tag does
* not change during its call, and that each file being printed to does not
* change during its execution (using a read/write lock)
*
* Given two concurrent calls to echoToAllFiles, it will be indeterminite
* which call happens first and which happens last. But what you can (and
* must) guarantee is that all files will have the *same* value (and not
* some the result of the first, qnd some the result of the second). Your
* code should not deadlock while waiting to acquire locks.
*
* @param tag
* Tag to query for
* @param content
* The content to write out to each file
* @throws NoSuchTagException
* If no tag exists with the given name
* @throws IOException
* if any IOException occurs in the underlying write
*
*/
public abstract void echoToAllFiles(String tag, String content) throws NoSuchTagException, IOException;
|
These methods will require you to utilize Java ReadWriteLocks. We ask you to put your lock and unlock code in two specific methods (for ease of testing):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
/**
* Acquires a read or write lock for a given file.
*
* @param name
* File to lock
* @param forWrite
* True if a write lock is requested, else false
* @return A stamp representing the lock owner (e.g. from a StampedLock)
* @throws NoSuchFileException
* If the file doesn't exist
*/
public abstract long lockFile(String name, boolean forWrite) throws NoSuchFileException;
/**
* Releases a read or write lock for a given file.
*
* @param name
* File to lock
* @param stamp
* the Stamp representing the lock owner (returned from lockFile)
* @param forWrite
* True if a write lock is requested, else false
* @throws NoSuchFileException
* If the file doesn't exist
*/
public abstract void unLockFile(String name, long stamp, boolean forWrite) throws NoSuchFileException;
|
Of the 30% that Part 2 will count towards your grade on this assignment, the breakdown will be roughly: 10% for implementing this functionality, and 20% for implementing it with correct concurrency control. Note the following requirements (generally the same as from part 1/2):
Again:
- You should feel free to add whatever methods you would like to the Tag, TaggedFile or FileTagManager classes. You must not change any other files.
- You should use the existing writeFile and readFile methods (in AbstractFileTagManager) to do the actual reading and writing. You should not be concerned with the thread-safety of writeFile and readFile (they are not thread safe). It is up to you to take out whatever synchronization is necessary before calling them.
- Your code must be thread-safe: concurrent calls to any of these methods (or any other method in FileTagManager) should not have any races. Of particular note: we require that you guarantee that if there are two concurrent calls to echoToAllFiles (writing to the same values), all of those files will have the same value at the end, rather than some files getting overwritten by one call, then others by another call.
- Your code must not over synchronize. We will deduct marks for solutions that include unnecessary locks.
- You must not store any state in static fields
Precise grading breakdown:
- Non-Concurrent issues: 5 points
- 4 JUnit tests, 5/4 points each
- Concurrent issues: 15 points
- 2 JUnit tests, 4 points each
- 7 points from manual grading
Part 4: Multi-threaded test cases (40%)
We have included several functional tests that simply test the behavior of each of these methods. We have not provided any tests (for you to see) that test the behavior of your software under concurrency. One of your tasks is to write such tests.
Note that it is generally impossible to automatically prove that concurrent programs have no races, it is often to test for low-hanging fruit. For instance, you might create a handful of threads, and then repeatedly perform some operations in each thread, checking the system state to make sure that it doesn’t become corrupt. It can be difficult to get such tests to expose issues. We have written eight pretty good tests, which we used in the prior sections to grade the concurrency aspects of your code.
We are asking you to implement the same eight JUnit test cases (in the class edu.gmu.cs475.ConcurrentTests), using the inline documentation as a guide for the specification of each:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
|
/**
* Create N_THREADS threads, with half of the threads adding new tags and
* half iterating over the list of tags and counting the total number of
* tags. Each thread should do its work (additions or iterations) 1,000
* times. Assert that the additions all succeed and that the counting
* doesn't throw any ConcurrentModificationException. There is no need to
* make any assertions on the number of tags in each step.
*/
@Test
public void testP1AddAndListTag() {
}
/**
* Create N_THREADS threads, and have each thread add 1,000 different tags;
* assert that each thread creating a different tag succeeds, and that at
* the end, the list of tags contains all of tags that should exist
*/
@Test
public void testP1ConcurrentAddTagDifferentTags() {
}
/**
* Create N_THREADS threads. Each thread should try to add the same 1,000
* tags of your choice. Assert that each unique tag is added exactly once
* (there will be N_THREADS attempts to add each tag). At the end, assert
* that all tags that you created exist by iterating over all tags returned
* by listTags()
*/
@Test
public void testP1ConcurrentAddTagSameTags() {
}
/**
* Create 1000 tags. Save the number of files (returned by listFiles()) to a
* local variable.
*
* Then create N_THREADS threads. Each thread should iterate over all files
* (from listFiles()). For each file, it should select a tag and random from
* the list returned by listTags(). Then, it should tag that file with that
* tag. Then (regardless of the tagging sucedding or not), it should pick
* another random tag, and delete it. You do not need to care if the
* deletions pass or not either.
*
*
* At the end (once all threads are completed) you should check that the
* total number of files reported by listFiles matches what it was at the
* beginning. Then, you should list all of the tags, and all of the files
* that have each tag, making sure that the total number of files reported
* this way also matches the starting count.
*
*/
@Test
public void testP2ConcurrentDeleteTagTagFile() throws Exception {
}
/**
* Create a tag. Add each tag to every file. Then, create N_THREADS and have
* each thread iterate over all of the files returned by listFiles(),
* calling removeTag on each to remove that newly created tag from each.
* Assert that each removeTag succeeds exactly once.
*
* @throws Exception
*/
@Test
public void testP2RemoveTagWhileRemovingTags() throws Exception {
}
/**
* Create N_THREADS threads and N_THREADS/2 tags. Half of the threads will
* attempt to tag every file with (a different) tag. The other half of the
* threads will count the number of files currently having each of those
* N_THREADS/2 tags. Assert that there all operations succeed, and that
* there are no ConcurrentModificationExceptions. Do not worry about how
* many files there are of each tag at each step (no need to assert on
* this).
*/
@Test
public void testP2TagFileAndListFiles() throws Exception {
}
/**
* Create N_THREADS threads, and have each try to echo some text into all of
* the files using echoAll. At the end, assert that all files have the same
* text.
*/
@Test
public void testP3ConcurrentEchoAll() throws Exception {
}
/**
* Create N_THREADS threads, and have half of those threads try to echo some
* text into all of the files. The other half should try to cat all of the
* files, asserting that all of the files should always have the same
* content.
*/
@Test
public void testP3EchoAllAndCatAll() throws Exception {
}
|
Note that we have configured this test runner automatically to (1) automatically time out each test after 10 seconds, (2) automatically detect (most) deadlocks, and (3) to repeatedly rerun tests that passed in order to attempt to increase the odds of seeing it fail.
You should perform all of your testing on the provided FileManager instance ( edu.gmu.cs475.NonConcurrentTests.fileManager) – this field will be automatically reset for each execution of each test. You must only reference methods in AbstractFileManager, ITag and ITaggedFile (NOT FileManager, Tag, and TaggedFile) – since we will be grading the quality of your tests by running them on implementations of VCFS with known concurrency bugs. When we run our own test suite against these known buggy implementations, every test fails on every implementation. This is what you should strive for.
We can not provide the code for the buggy implementations to you because that might (1) give you tricks to make parts 1-3 easier, and (2) once you know what is buggy, you might find it easier to write the tests. However, you are free to submit as often as you like on AutoLab and see your marks.
Hints: Here is an example Java snippet that creates a bunch of threads, then waits for them to complete. This snippet also demonstrates keeping track of the number of successful operations, the number of failed operations, and the number of exceptions reported in each thread.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
Thread[] threads = new Thread[N_THREADS];
AtomicInteger nSuccess = new AtomicInteger(0);
AtomicInteger nErrors = new AtomicInteger(0);
AtomicInteger nExceptions = new AtomicInteger(0);
for (int i = 0; i < N_THREADS; i++) { final int tNum = i; Runnable r = () -> {
try {
System.out.println("Hello from thread " + tNum + "!");
if (tNum % 2 == 0) // Even thread number
nSuccess.incrementAndGet();
else
nErrors.incrementAndGet();
} catch (Throwable t) {
t.printStackTrace();
nExceptions.incrementAndGet();
}
};
threads[i] = new Thread(r);
threads[i].start();
}
for (Thread t : threads){
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
fail("Thread died");
}
}
assertEquals("Expected no exceptions", 0, nExceptions.get());
|
Also: Keep in mind that for the autograder to detect that your test failed, you need to actually call assert*** or fail() from JUnit. You can see examples in the NonConcurrentTests.
Precise grading breakdown for part 4:
- Tests all pass on our own (correct) implementation: 8 pts
- 8 tests, 1 point for each test that passes
- 3 Expected tests fail on buggy version 1: 6 pts
- Expected failures are:
- testP2TagFileAndListFiles: 2 pts
- testP3ConcurrentEchoAll: 2 pts
- testP3EchoAllAndCatAll: 2 pts
- All tests fail on buggy version 2: 16 pts
- 8 tests, all should fail. 2 points for each test that fails
- Manual inspection of test quality: 10 pts
No marks will be awarded for empty tests, or tests that have no assertions (note that otherwise, these tests would all pass on our correct implementation).
Grading
Your assignment will be graded on a series of functional tests, making sure that it implements the specification above. Your tests will be graded by running them on purposely buggy code and verifying that they throw some errors, and then manually inspecting them for completeness. We have provided a (not exhaustive) test suite which should help you judge (on your own) how well you have done with the functional requirements (although not the concurrent parts). We may add additional tests beyond what we are providing you with now, so please do not rely entirely on them (in particular, again, the automated tests do not verify that your code is correctly synchronized).
Hand In Instructions
You must turn in your assignment using Autolab (You MUST be on the campus network, or connected to the GMU VPN to connect to Autolab). If you did not receive a confirmation email from Autolab to set a password, enter your @gmu.edu (NOT @masonlive) email, and click “forgot password” to get a new password.
Create a zip file of only the src directory in your assignment (please: .zip, not .tgz or .7z etc). When you upload your assignment, Autolab will automatically compile and test it. You should verify that the result that Autolab generates is what you expect. Your code is built and tested in a Linux VM. Assignments that do not compile using our build script will receive a maximum of 50%. Note that we have provided ample resources for you to verify that our view of your assignment is the same as your own: you will see the result of the compilation and test execution for your assignment when you submit it.
You can resubmit your assignment an unlimited number of times before the deadline. Note the course late-submission policy: assignments will be accepted up until 24 hours past the deadline at a penalty of 10%; after 24 hours, no late assignments will be accepted, no exceptions.
Understanding the Autolab output
You MUST be on the campus network, or connected to GMU VPN to connect to Autolab.
Autolab will give you a score across a variety of problems, with each problem scored between 0-1. This score will help you understand how well you have done implementing the assignment, but you should realize that it does not directly map to a grade on the assignment (for one thing, the test cases don’t map to the weightings provided above). We will not provide you with copies of the complete test suite this time. Since we will grade the quality of your concurrent test cases by running them on our own (correct and buggy) code, we can not distribute the auto grading script without also distributing a correct and complete implementation of parts 1-3.
Questions
Please post questions on Piazza