While threads are convenient to work concurrently on a task, they present synchronization problems. In our cases, the threads will be working on a single shared ring buffer, and access must be synchronized else the program will not work as expected. The idea is to consider the ring buffer data structure as a lockable object, and to demand that threads acquire the lock before working on values on the data structure, and to be sure to release lock when those critical actions are completed.
A monitor is a software object that you can,
Please reference POSIX Threads Programming from the Lawrence Livermore National Laboratory.
You are to wrap the ring buffer code in a monitor wrapper. Provided is the workable but not safe example. The code in monitor.c calls the ring buffer, with a bit of conditions to see if the ring buffer is empty or full.
This is not thread safe. Stage one is to use the mutex to lock the ring buffer data structure on entering the monitor code, and unlocking the code on leaving. You need to consider what to do if the ring buffer is locked, but is not ready to receive or provide a character. Hint: drop the lock (so other threads can have a chance at the buffer) then try again.
Stage two is to be more fancy, and once having acquired the lock, if the ring buffer is not ready, to wait, rather then leave. This is a more complicated solution which needs some questions answered. Where, for instance, do we signal? Should we signal or broadcast? The solution suggest a single condition variable. Should there be more? What can be assume upon reentering the monitor after being revived from a wake?
The problem you are being asked to solved is called the Producer–Consumer Problem. Each thread is either a producer, which adds characters to the ring buffer, or a consumer, which takes characters from the ring buffer. The producer might find the buffer full. Then it must wait. The consumer might find the ring buffer empty. Then it must wait.
A waiting consumer should be signaled at least by a producer putting something in the ring buffer. Likewise, a waiting producer should be signaled at least by a consumer taking something from the ring buffer. It is possible that signals are not created at the proper time, and the monitor will have a sort of deadlock. Threads will wait forever, and your program does not exit.
The code takes a command sequence that gives instructions to producer and consumer threads. For w producer threads and r consumer threads, the command line is,
w-delay_1 repeat_1 characters_1 ... w-delay_w repeat_w characters_w r-delay_1 ... r-delay_rthe -w option is required and gives the number w of producer threads.
The i-th producer thread will wait w-delay_i seconds before starting, then enqueue all the characters in characters_i, and do that repeat_i time, then enqueue a null character to the ring buffer.
The i-th consumer thread will wait r-delay_i seconds before starting, then dequeue from the ring buffer until some null characters are received. The rules are automatic that there must be no more consumer threads than producer threads, that all but the r-th consumer thread exits on the first null character consumed, and the r-th consumer threads exists after consuming exactly the null characters needed to balance the total null bytes enqueue by the producer threads.
The first thing to note is that the given code does not work correctly. If you run the count target, it shows that on completion of the run, typically fewer characters were dequeued than enqueued. Because of concurrency, some characters were lost.
Correctness by testing is more random with this sort of code. A correct program should always terminate cleanly, with out hanging until a control-C, and the number of enqueues should equal the number of dequeues. There are obviously more requirements for correctness — that the dequeue stream logically follows from the enqueue stream. The tests provided do not test that requirement.
However, while it is easy to show that some codes are incorrect, some incorrect codes are correct enough that failure is rare. A proper test does stress the code, in the hope of uncovering a defect.
author: burton rosenberg
created: 7 oct 2021
update: 7 oct 2021