I am trying to implement a high-performance lock queue supported by a circular buffer on top of pthreads, semaphore.h and gcc atomic builtins. The queue should process several simulated readers and writers from different threads.
I highlighted some kind of race condition, and I'm not sure if this is an erroneous assumption about the behavior of some atomic operations and semaphores, or my design is fundamentally wrong.
I extracted and simplified it to a separate example. I would expect this program to never return. However, it returns after several hundred thousand iterations with the detection of corruption in the queue.
In the example below (for presentation), it actually does not store anything, it simply sets 0 to represent the empty cell in the cell in which the actual data will be stored. There is a counting semaphore (vacancy) representing the number of vacant cells, and another counting semaphore (tenants) representing the number of occupied cells.
Writers do the following:
- job cuts
- atomically get the next main index (mod queue size)
- write him
- passenger growth
Readers do the opposite:
- reduced passengers
- atomically get the next tail index (mod queue size)
- read it
- additional vacancies
I would expect that, given the above, exactly one stream can read or write any given cell at a time.
Any ideas on why this is not working or debugging strategies are appreciated. Code and output below ...
#include <stdlib.h>
Compile the above:
$ g++ -pthread AboveCode.cpp $ ./a.out
The output is different each time, but here is one example:
corruption detected action = get i = 6 head = 122685 tail = 122685 cell[0] = 0 cell[1] = 0 cell[2] = 1 cell[3] = 0 cell[4] = 1 cell[5] = 0 cell[6] = 1 cell[7] = 1
My system is Ubuntu 11.10 on Intel Core 2:
$ uname -a Linux 3.0.0-14-generic
Thanks Andrew.