Producer-Consumer Problem Solution ------------------------------------------
Assumptions: Loads and Stores Are Atomic.
(We'll learn about that in Chapter 6. )
------------------------------------------
/* The variables defined in this section are placed in a block of
memory that is shared by the producer and consumer processes */
/* This declares BUFFER_SIZE and sets it equal to 10.
BUFFER_SIZE is used as a constant in the program.
It represents the size of the buffer shared by the two
processes. */
#define BUFFER_SIZE 10
/* The buffer will be an array of individual pieces of data,
called 'items.' Here we define the data type of an item,
and name the data type 'itemType.' (In the C programming
language, a struct is like a class with data members
only. A struct has no methods.) */
typedef struct
(
/* If this was real code, here we would define whatever fields
of data we want our data items to have. Because this is just
an example, we don't put anything specific here. */
) itemType ;
/* This declares an array named 'buffer' with slots that
are objects of type itemType. The number of slots in
the array is BUFFER_SIZE. */
itemType buffer [BUFFER_SIZE] ;
/* The next two statements declare a couple of variables.
The first represents the location in the buffer into which
the producer will place its next item.
The second variable represents the location from which the
consumer will take its next item. Both variables are initialized
to the value 0. */
int in = 0 ;
int out = 0 ;
/* To communicate, each process executes its own piece of code.
The producer process repeatedly makes an item and copies it
into the next available slot in the buffer. The consumer process
repeatedly reads an item from the next available slot. When either
process gets to the end of the array, it starts over at the beginning
of the array, continuing with the same actions.
*/
/* As a simplification for purposes of this example, we use
"while (true)" to make infinite loops. That way
the producer and consumer "do their dance eternally."
In a real application, we'd customize the code to stop
the processes when required. */
--------------------------------
(producer code)
while (true)
{
/* The producer creates the next item with.
the local function 'makeItem' and stores the
item in a local variable 'nextProduced.' This
function and variable are not in the
shared memory. */
nextProduced = makeItem() ;
/* This may look mysterious, but the loop condition
below just means that there's only one empty slot
left in the buffer. So the meaning of the while-loop
below is that if there a too few empty slots, the
producer will be delayed until there are more empty slots.
In other words, if it looks like the producer is close to
catching up with the consumer, then there's a danger that
the producer could start overwriting data in the buffer
- data that the consumer has not yet read.
So the producer waits until the consumer has read
more items from the buffer. */
while ( ( (in+1) % BUFFER_SIZE ) == out )
/* do nothing */ ;
/* Copy the item into the next available slot in the buffer.
The buffer is in shared memory. */
buffer[in] = nextProduced ;
/* Advance the variable that tracks the next slot to use. The
use of the % operator assures that the value of the
variable will be set back to 0 after reaching the end of the
buffer. */
in = (in+1) % BUFFER_SIZE ;
}
--------------------------------
(consumer code)
while (true)
{
/* delay while the buffer is 'empty,'
meaning that there are no new items
to read. In this situation, the consumer
is waiting, if necessary, for the producer
to catch up. */
while ( in == out )
/* do nothing */ ;
/* Copy the next available item into a local variable. */
nextConsumed = buffer[out] ;
/* Advance the variable that tracks the next slot to use. */
out = (out+1) % BUFFER_SIZE ;
/* Call a local function that 'consumes' the item,
making whatever use of the item the consumer process
needs. */
consume(nextConsumed) ;
}
--------------------------------