Specifications

In this assignment, you will solve the Reader-Writer problem using the PThreads library. You will get to practice using semaphores in the PThreads library.

Project Description

Your program will create several reader threads and several writer threads. Each thread loops for several iterations for writing or reading. The numbers of threads and the number of iterations need to be passed in as command line arguments for the program -- So your program takes in three command line arguments: number of reader threads, number of writer threads, and number of iterations in a reader/writer thread, in that order.

The readers read a shared string and print the contents to the standard output. The writers write to the same string. The synchronization requirement is the same as explained in class (only one writer can write at any time. Readers can access concurrently, but a writer needs mutual exclusion with other writers and all readers).

Your code needs to initialize the shared string, which is a global variable shared by all threads. You can initialize the shared string in any way. For demonstration, let us say we set it to "All work and no play makes Jack a dull boy."

The main() function will do the initialization to initialize the semaphores (which are also shared by all threads). It also creates the separate reader and writer threads, with number of threads based on command line arguments. Once it has created the threads, the main() function will wait for the threads to finish (using pthread_join()),

A skeleton for main() is:

int main(int argc, char *argv[])
{
/* Get command line arguments.
Initialization of semaphores.
Create reader and writer threads.
Wait for reader threads to finish.
Wait for writer threads to finish.
Cleanup and exit.
*/
}

The writer thread will alternate between writing and sleeping for 1 second. It will do so for NUM_ITERATION times (taken from command line argument). In each iteration, it modifies the current contents of the string by chopping the last character of it, until it is an empty string. E.g.: if the current string is "All work", the writer will change it to All wor. It calls sleep(1) to sleep for 1 second between iterations. A skeleton for writer is:

void *writer(void *param)
{
//some local variables
//loop for NUM_ITERATION times:
{
//writing (chopping the last character of the string)
//print out a message saying that it is writing
//sleep for 1 second
}
}

The above skeleton does NOT yet have any synchronization logic. You need to add semaphores at the correct places.

The reader thread has the similar loop structures. It prints out the content of the shared string. In addition, it also prints out the current readcount value when the value increments or decrements. When the readcount is 0, the reader calls sem_post() on one semaphore (the rw_sem as described below) to signal the writer. You should also print out the value of that semaphore before and after the sem_post(). You can call sem_getvalue() to get the value of a semaphore.

Pthread semaphores

The structures of the reader and the writers are the same as described in the lecture and textbook, which uses two semaphores, and a readcount to keep track of readers. Make sure you call the related wait and signal/post functions in a correct order.

You will need two semaphores for thread synchronization. You can name them differently.

sem_t rw_sem; //used by both readers and writers
sem_t cs_sem; //used for protecting critical sections of readers

Semaphores need to be initialized to 1. You can do so using the following (assuming unnamed semaphore):

sem_init(&rw_sem, 0, 1);
sem_init(&cs_sym, 0, 1);

You must check the return value in your program to know if the operation was successful. Note: PThread semaphore is not supported on all platforms since it is just a specification (e.g. Mac OS X does not provide implementation of PThread unnamed semaphore). Although it is supported on turing, checking return values is an important practice in general for such operations. You can use perror() or strerror() to get the details of the error if the operation fails. Check the related manual for usage.

Cleanup

You need to clean up the resources in main() by destroying the semaphore which can be done by calling sem_destroy().

When printing to the standard output, use printf(), or prebuild the string before printing, to avoid confusing interleaving of outputs among concurrent threads. You can optionally use fflush(stdout) after the call of printf() to flush the output buffer.

Output

Your program will be run in a way like this:

$ ./z1234567 10 3 10

If there is one reader and one writer, and 43 iterations (which should be the default setting without arguments), the output from the reader can look similar as this -

Sample output (Other printouts of the semaphore values and readcount, and printouts from the writers are omitted):

All work and no play makes Jack a dull boy.
All work and no play makes Jack a dull boy
All work and no play makes Jack a dull bo
All work and no play makes Jack a dull b
All work and no play makes Jack a dull
All work and no play makes Jack a dull
All work and no play makes Jack a dul
All work and no play makes Jack a du
All work and no play makes Jack a d
All work and no play makes Jack a
All work and no play makes Jack a
All work and no play makes Jack
All work and no play makes Jack
All work and no play makes Jac
All work and no play makes Ja
All work and no play makes J
All work and no play makes
All work and no play makes
All work and no play make
All work and no play mak
All work and no play ma
All work and no play m
All work and no play
All work and no play
All work and no pla
All work and no pl
All work and no
All work and no
All work and n
All work and
All work and
All work an
All work a
All work
All work
All wor
All wo
All w
All
All
Al
A

Your output does not need to be exactly as above since the reader thread and the writer thread are not strictly alternating in the problem setting of the assignment.

You will see different outputs with different numbers of readers and writers. Try 10 readers, 3 writers and 10 iterations. (Or 50 readers, 10 writers and 10 iterations.) You can see some interesting behavior - for example, with more readers, you may see that the readers will keep reading for a while with the reader count being incremented before the writers get to write. Some possible outputs are provided, but your output may vary since the order of printout is based on CPU scheduling. Note that the read count is in the critical section, so its value should be consistent and should never be negative. It should not suddenly increase or decrease more than one.

For the value of the semaphore rw_sem, in most cases, you should see:

Last reader. Semaphore value before signaling writer: 0.
Last reader. Semaphore value after signaling writer: 1.

Occasionally you may see that the semaphore value after signaling is also 0, that is because the value has been changed by another thread that grabs the lock before the sem_getvalue() returns.

Additional suggestions and extra credits

1. Tackle the problem step by step. First of all make sure that your threads are all created successfully. Then let each thread do something trivial, e.g. print out its own id. Finally add the semaphores to solve the readers/writers problem.

2. As an experiment, you may try to test what would happen if the critical section is not protected, check if you would see inconsistency printout. You don't need to submit the result of this test.

3. This assignment's specification does not ensure strict alternating of writers and readers. As a result, some updates of the writers may be missed by readers. And some contents may be read more than once. What if you want strict alternating? Think about it.

Academic Honesty!
It is not our intention to break the school's academic policy. Posted solutions are meant to be used as a reference and should not be submitted as is. We are not held liable for any misuse of the solutions. Please see the frequently asked questions page for further questions and inquiries.
Kindly complete the form. Please provide a valid email address and we will get back to you within 24 hours. Payment is through PayPal, Buy me a Coffee or Cryptocurrency. We are a nonprofit organization however we need funds to keep this organization operating and to be able to complete our research and development projects.