Homework 6 - Creating Concurrency with Semaphores

As we had mentioned in class, you can create many synchronization primitives simply by using other synchronization primitives. In this assignment we will be creating many of those.

The public git repository includes skeleton implementations, as well as a number of test-cases. Make sure to study these skeletons - their interfaces provide hints on how you should structure your solution. A correct solution passes every test case. Except where noted, your solution must have no loops: for, while, goto or recursive! Note that for each sub-problem, you must pass ALL test cases for full credit, as passing some-but-not-all does not ensure correctness.

To run the tests, type make test in your turn-in folder.

Mutual exclusion (mutex.c) (5 points)

This is a warm-up exercise.

Reusable Barrier (barrier.c) (5 points)

Barriers are instantiated with a number of threads which will synchronize on that barrier, and will not let any thread continue execution until all have arrived at the barrier. Implement first a disposable barrier, then a reusable barrier using semaphores.

Worker Threads 1 (worker1.c) (5 points)

Here the scenario is a multi-threaded web server. To avoid the overhead of thread creation, and the risk of overloading the CPU, threads are created on startup only. As requests come in, they are served by existing threads as they become available.

Implement the functions wait_for_service() and wait_for_request(). Each function should call pair() once, and synchronize with the other threads such that for every two calls to pair(), there is one worker and one browser. Ordering between the two is not important.

Worker Threads 2 (worker2.c) (5 points)

In this version, the server has two types of threads - front-end threads that communicate with the remote browser and apply templates to produce the final HTML document, and back-end threads that perform computational tasks and deliver raw content to the front-end thread.

Threads call the functions front_ready() and back_ready() when they have finished processing the last request. These in turn call group() to create a grouping of front/back end threads for the next request.

Every request requires two back-end threads and one front-end. A correct solution must make sure that every group has two back-ends and one front-end. That is, if we print out the thread type for every call to group() and divide the resulting list into groups of three, every group should have two back-ends and one front-end.

Read-write locks (rwlock.c) (5 points)

Read-write locks allow multiple readers to have concurrent access to a critical section, but ensure that only ONE writer is allowed in at a time - thus, there is mutual exclusion between writers, and if ANY reader is reading, NO writer can write. This implementation specifies a number of readers to allow parallel access to, which simplifies the implementation greatly. Do not attempt to implement any starvation prevention for writers or readers.

Alternating workloads (alternating.c) (5 bonus points)

A new type of storage has been released that offers very fast read and write speeds. However, switching between reading and writing is somewhat slow. Your application has two types of threads - readers and writers. When they are ready to read or write, the call, read_ready() and write_ready(), which in turn call do_read() or do_write(). To make the best use of this new technology, design a synchronization scheme that obeys the following rules:

  • no concurrent mixing of read()/write() operations
  • switch from writing to reading when the number of waiting readers exceed the number of running writers, and vice versa.

Turn-in instructions

Note that this assignment will REPLACE your lowest homework score. There is also a chance for extra credit if you complete the test cases for alternating workloads in alternating.c.

Do not edit the files named *_test.c, and do not use any built-in synchronization operator other than semaphores. Feel free to build and use your own operators out of semaphores though, or use previously implemented ones (i.e. use the mutex you create to implement barrier, etc).

This is the same as every previous homework. Make sure you run make test on your solution, on your Amazon EC2 VM, before committing your final version.

Due date: 11:59 PM Friday May 1.


Always, always, always check that you have submitted the code through git by doing a git fetch/pull in a temporary directory and testing your code in the clean checkout directory. You will not receive credit for unpushed code. You will not receive credit for emailed code.

If you have any questions about the lab requirements or specification, please post on Piazza.