The writer thread must block (wait) when the shared buffer is full
The reader thread must block (wait) when the shared buffer is empty
nItems = 0
buf: 0 1 2 3 4 ... N-1
+---+---+---+---+---+---+---+---+---+---+---+
| | | | | | | | | | | |
+---+---+---+---+---+---+---+---+---+---+---+
|
|
The pointers are initialized as follows:
nItems = 0
buf: 0 1 2 3 4 ... N-1
+---+---+---+---+---+---+---+---+---+---+---+
| | | | | | | | | | | |
+---+---+---+---+---+---+---+---+---+---+---+
^
|
readPtr
writePtr
|
nItems = 1
buf: 0 1 2 3 4 ... N-1
+---+---+---+---+---+---+---+---+---+---+---+
| x | | | | | | | | | | |
+---+---+---+---+---+---+---+---+---+---+---+
^ ^
| |
readPtr |
|
writePtr
|
nItems = 4
buf: 0 1 2 3 4 ... N-1
+---+---+---+---+---+---+---+---+---+---+---+
| x | x | x | x | | | | | | | |
+---+---+---+---+---+---+---+---+---+---+---+
^ ^
| |
readPtr |
|
writePtr
|
(Notice that the writePtr always points to the next empty slot.
The readPtr is on the next available item for reading)
nItems = 3
buf: 0 1 2 3 4 ... N-1
+---+---+---+---+---+---+---+---+---+---+---+
| | x | x | x | | | | | | | |
+---+---+---+---+---+---+---+---+---+---+---+
^ ^
| |
readPtr |
|
writePtr
|
Shared variables:
int MAX;
int nItems = 0; // Number of items in buffer
int buf[MAX];
|
1. Reader starts first 2. Reader locks the mutex 3. Reader finds buffer empty 4. Reader blocks 5. Writer starts 6. Writer tries to lock the mutex 7. Write blocks too |
The system is now dead locked because the writer cannot put an item in the buffer to release the reader
|
(Because if we fill the last empty slot, we will no be able to distinguish the filled state from the empty state)
(writePtr+1)%N == readPtr
|
Shared variables:
int MAX;
int nItems = 0; // Number of items in buffer
int buf[MAX];
|
1. Reader starts first 2. Reader finds buffer empty (readPtr == writePtr) 3. Reader blocks 4. Writer starts 5. Writer write an item to buffer 6. Writer detects a BLOCKED readerand UNBLOCKS the reader... |
Problem solved... or so it seems :-)
But creating a situation is more subtle.
1. Reader starts first
2. Reader finds buffer empty (readPtr == writePtr)
3. Writer starts !!!
4. Writer detects buffer not filled
5. Writer writes an item
4. Writer again detects buffer not filled
5. Writer again writes an item
....
and so on until buffer is filled completely
(All the while, the reader did not progress, so
reader is NOT BLOCKED)
k. Writer Blocks
k+1. Reader finally progresses and BLOCKS
|
The system is now dead locked
Any thread can perform operations on a semaphore
Here is a picture of a "real life" semaphore:
|
A semaphore is places at the entrance of stretch of single-tracked rail:
|
So there can only be one train traveling on the stretch of rail !!!
(Older semaphores will only cause the signal at the other end to set to the danger state and the conductor must manually stop the train....)
|
Example:
semaphore s(10); // s.count is set to 10
|
|
Furthermore: the P(s) and V(s) operations are atomic
That means all the effects of each operations are completed without any interruption (no intervening action of any kind).
Example code used to access a shared variable:
int x; // shared variable
semaphore s(1); // shared semaphore variable
thread i:
.....
P(s);
x = x + 1;
V(s);
|
Because the semaphore is initialed to one, the number of times that P(s) will succeed is one time only.
Therefore, only one thread can pass P(s) at any time...
Mutual exclusion is ensured !
Shared variables:
int MAX;
int nItems = 0; // Number of items in buffer
int buf[MAX];
semaphore sender(MAX); // Initially, let sender in MAX times
semaphore receiver(0); // Inirially, block receiver's entry
|
It's tricky:
|