I have implemented a Michael and Scott Queue (a concurrent lockfree queue) and I have a problem with dequeue operation code duplication. This question in general is not about the queue algorithm itself but how to cleanly implement several variations of functions that mostly have identical structure. The example I say:
bool dequeue() {
while(true) {
// [atomically load head, tail and head->next]
auto head = m_head.load(std::memory_order_acquire);
auto head_ptr = head.get();
auto tail = m_tail.load(std::memory_order_acquire);
auto next = head_ptr->next.load(std::memory_order_acquire);
auto next_ptr = next.get();
// Are head, tail, and next consistent?
if(head == m_head.load(std::memory_order_acquire)) {
// Is queue empty or tail falling behind?
if(head_ptr == tail.get()) {
// Is queue empty?
if(!next_ptr) {
return false;
}
// tail is falling behind. Try to advance it
m_tail.compare_exchange_strong(tail, tail(next_ptr));
} else if(next_ptr){
// [ above check is result free list interaction, not part of orginal algo ]
// [Read value from next_ptr->data]
// <<<variant of operation here>>>>
}
}
}
}
I have planned to implement various operations in place of <<<variant of operation here>>>>
including logic, if-else code exiting the loop and such and I would like to avoid duplicating the main
body of the function. How should I proceed? I'm using at least C++14 standard.
The background story is that boost::lockfree::queue<> was too restricted and
I would like to implement such operations as pop_if(), compare_front(), begin() that all
share the same basic dequeue operation code logic except for the <<<variant operation here>>> part.
head,head_ptr,tail,next,next_ptretc. The code variants are depend on all of these. Best I could think of would be to put all local variables in a struct (derived from the main container class to allow accessing member variables) and put the base code there as member function. Then most derived class would define more specialised operations that operate on thelocals?