Lockless structure use atomic instruction to acquire ownership of resources. Atomic instruction lock the variable it's working at CPU cache level, witch assure you that another cores can't interfere with the operation.
Let's say you have these atomic instruction:
- read(A) -> A
- compare_and_swap(A, B, C) -> oldA = A; if (A == B) { A = C }; return oldA;
With these instruction you can simply create a stack:
template<typename T, size_t SIZE>
struct LocklessStack
{
public:
LocklessStack() : top(0)
{
}
void push(const T& a)
{
int slot;
do
{
do
{
slot = read(top);
if (slot == SIZE)
{
throw StackOverflow();
}
}while(compare_and_swap(top, slot, slot+1) == slot);
// NOTE: If this thread stop here. Another thread pop and push
// a value, this thread will overwrite that value [ABA Problem].
// This solution is for illustrative porpoise only
data[slot] = a;
}while( compare_and_swap(top, slot, slot+1) == slot );
}
T pop()
{
int slot;
T temp;
do
{
slot = read(top);
if (slot == 0)
{
throw StackUnderflow();
}
temp = data[slot-1];
}while(compare_and_swap(top, slot, slot-1) == slot);
return temp;
}
private:
volatile int top;
T data[SIZE];
};
volatile is required so compiler don't mess the order of operation during optimization.
Two concurrent push occur:
The first one enter in the while loop and read slot, then the second push arrive, read top, the compare and swap (CAS) succeed and increment top. The other thread wake up, the CAS fail and read another time top..
Two concurrent pop occur:
Really similar to the previous case. Need to read the value as well.
One pop and push occur simultaneously:
pop read the top, read temp.. push enter and modify top and push a new value. Pop CAS fail, pop() will do the cycle again and read a new value
or
push read the top and acquire a slot, pop enter and modify the top value. push CAS fail and have to cycle again pushing on a lower index.
Obviously this is not true in a concurrent environment
stack.push(A);
B = stack.pop();
assert(A == B); // may fail
cause while push is atomic and pop is atomic the combination of them is not atomic.
First chapter of Game programming gem 6 is a nice reference.
Note the code is NOT TESTED and atomic can be really nasty.