Commit 3efbe80e by Christian Kern

For the michael scott queue, follow the exact implementation of Paper:

Michael, Maged M. "Hazard pointers: Safe memory reclamation for lock-free objects." Parallel and Distributed Systems, IEEE Transactions on 15.6 (2004): 491-504.

Might fix bug, where the MPMC queue hang (very very spuriously), because tail->GetNext() == tail...
parent 55e782ae
...@@ -116,28 +116,28 @@ bool LockFreeMPMCQueue<T, ValuePool>::TryEnqueue(T const& element) { ...@@ -116,28 +116,28 @@ bool LockFreeMPMCQueue<T, ValuePool>::TryEnqueue(T const& element) {
internal::LockFreeMPMCQueueNode<T>* my_tail; internal::LockFreeMPMCQueueNode<T>* my_tail;
for (;;) { for (;;) {
my_tail = tail; my_tail = tail;
internal::LockFreeMPMCQueueNode<T>* my_tail_next = my_tail->GetNext();
hazardPointer.GuardPointer(0, my_tail); hazardPointer.GuardPointer(0, my_tail);
// Check if pointer is still valid after guarding. // Check if pointer is still valid after guarding.
if (my_tail != tail) { if (my_tail != tail) {
hazardPointer.GuardPointer(0, NULL);
continue; // Hazard pointer outdated, retry continue; // Hazard pointer outdated, retry
} }
internal::LockFreeMPMCQueueNode<T>* my_tail_next = my_tail->GetNext();
if (my_tail == tail) { if (my_tail == tail) {
// If the next pointer of the tail node is null, the tail pointer // If the next pointer of the tail node is null, the tail pointer
// points to the last object. We try to set the next pointer of the // points to the last object. We try to set the next pointer of the
// tail node to our new node. // tail node to our new node.
if (my_tail_next == NULL) { if (my_tail_next == NULL) {
internal::LockFreeMPMCQueueNode<T>* expected = NULL;
// This fails if the next pointer of the "cached" tail is not null // This fails if the next pointer of the "cached" tail is not null
// anymore, i.e., another thread added a node before we could complete. // anymore, i.e., another thread added a node before we could complete.
if (my_tail->GetNext().CompareAndSwap(my_tail_next, node)) if (my_tail->GetNext().CompareAndSwap(expected, node))
break; // We successfully added our node.
// We successfully added our node. Still missing: increase tail pointer. //Still missing: increase tail pointer.
break; // The tail pointer points not to the last object, first increase
// The tail pointer points not to the last object, first increase
} else { } else {
// Try to increase the tail pointer. // Try to increase the tail pointer.
tail.CompareAndSwap(my_tail, my_tail_next); tail.CompareAndSwap(my_tail, my_tail_next);
...@@ -147,66 +147,45 @@ bool LockFreeMPMCQueue<T, ValuePool>::TryEnqueue(T const& element) { ...@@ -147,66 +147,45 @@ bool LockFreeMPMCQueue<T, ValuePool>::TryEnqueue(T const& element) {
// We added our node. Try to update tail pointer. Need not succeed, if we // We added our node. Try to update tail pointer. Need not succeed, if we
// fail, another thread will help us. // fail, another thread will help us.
tail.CompareAndSwap(my_tail, node); tail.CompareAndSwap(my_tail, node);
// Release guard
hazardPointer.GuardPointer(0, NULL);
return true; return true;
} }
template< typename T, typename ValuePool > template< typename T, typename ValuePool >
bool LockFreeMPMCQueue<T, ValuePool>::TryDequeue(T & element) { bool LockFreeMPMCQueue<T, ValuePool>::TryDequeue(T & element) {
T value; internal::LockFreeMPMCQueueNode<T>* my_head;
internal::LockFreeMPMCQueueNode<T>* my_tail;
internal::LockFreeMPMCQueueNode<T>* my_next;
internal::LockFreeMPMCQueueNode<T>* expected;
T data;
for (;;) { for (;;) {
internal::LockFreeMPMCQueueNode<T>* my_head = head; my_head = head;
internal::LockFreeMPMCQueueNode<T>* my_tail = tail; hazardPointer.GuardPointer(0, my_head);
internal::LockFreeMPMCQueueNode<T>* my_head_next = my_head->GetNext(); if (my_head != head) continue;
// Head did not change
if (my_head == head) {
// Guard head
hazardPointer.GuardPointer(0, my_head);
// Check if pointer is still valid after guarding. This check is
// essential, tests really crash if missing
if (my_head != head) {
hazardPointer.GuardPointer(0, NULL);
continue; // Hazard pointer outdated, retry
}
// Check if pointer is still valid after guarding. This check is
// essential, tests really crash if missing
hazardPointer.GuardPointer(1, my_head_next);
if (my_head_next != my_head->GetNext()) {
hazardPointer.GuardPointer(1, NULL);
continue; // Hazard pointer outdated, retry
}
if (my_tail == my_head) { my_tail = tail;
if (my_head_next == NULL) { my_next = my_head->GetNext();
// Queue is empty. Release guards and return false. hazardPointer.GuardPointer(1, my_next);
if (head != my_head) continue;
hazardPointer.GuardPointer(0, NULL); if (my_next == NULL)
hazardPointer.GuardPointer(1, NULL); return false;
// Queue is empty; if (my_head == my_tail) {
return false; expected = my_tail;
} tail.CompareAndSwap(expected, my_next);
// Tail is not pointing to last element, help to increase continue;
tail.CompareAndSwap(my_head, my_head_next);
} else {
value = my_head_next->GetElement();
if (head.CompareAndSwap(my_head, my_head_next)) {
// It's our element. Release guard and enqueue my_head for
// deletion and leave.
hazardPointer.GuardPointer(0, NULL);
hazardPointer.GuardPointer(1, NULL);
hazardPointer.EnqueuePointerForDeletion(my_head);
break;
}
}
} }
data = my_next->GetElement();
expected = my_head;
if (head.CompareAndSwap(expected, my_next))
break;
} }
element = value;
hazardPointer.EnqueuePointerForDeletion(my_head);
element = data;
return true; return true;
} }
} // namespace containers } // namespace containers
......
...@@ -74,8 +74,7 @@ class LockFreeMPMCQueueNode { ...@@ -74,8 +74,7 @@ class LockFreeMPMCQueueNode {
*/ */
LockFreeMPMCQueueNode( LockFreeMPMCQueueNode(
T const& element T const& element
/**< [IN] The element of this queue node */ /**< [IN] The element of this queue node */);
);
/** /**
* Returns the next pointer * Returns the next pointer
...@@ -121,14 +120,14 @@ class LockFreeMPMCQueue { ...@@ -121,14 +120,14 @@ class LockFreeMPMCQueue {
* Callback to the method that is called by hazard pointers if a pointer is * Callback to the method that is called by hazard pointers if a pointer is
* not hazardous anymore, i.e., can safely be reused. * not hazardous anymore, i.e., can safely be reused.
*/ */
embb::base::Function<void, internal::LockFreeMPMCQueueNode<T>*> embb::base::Function < void, internal::LockFreeMPMCQueueNode<T>* >
delete_pointer_callback; delete_pointer_callback;
/** /**
* The hazard pointer object, used for memory management. * The hazard pointer object, used for memory management.
*/ */
embb::containers::internal::HazardPointer<internal::LockFreeMPMCQueueNode<T>*> embb::containers::internal::HazardPointer
hazardPointer; < internal::LockFreeMPMCQueueNode<T>* > hazardPointer;
/** /**
* The object pool, used for lock-free memory allocation. * The object pool, used for lock-free memory allocation.
...@@ -200,8 +199,7 @@ class LockFreeMPMCQueue { ...@@ -200,8 +199,7 @@ class LockFreeMPMCQueue {
*/ */
bool TryEnqueue( bool TryEnqueue(
T const& element T const& element
/**< [IN] Const reference to the element that shall be enqueued */ /**< [IN] Const reference to the element that shall be enqueued */);
);
/** /**
* Tries to dequeue an element from the queue. * Tries to dequeue an element from the queue.
...@@ -215,9 +213,9 @@ class LockFreeMPMCQueue { ...@@ -215,9 +213,9 @@ class LockFreeMPMCQueue {
*/ */
bool TryDequeue( bool TryDequeue(
T & element T & element
/**< [IN,OUT] Reference to the dequeued element. Unchanged, if the operation /**< [IN, OUT] Reference to the dequeued element.
was not successful. */ Unchanged, if the operation
); was not successful. */);
}; };
} // namespace containers } // namespace containers
} // namespace embb } // namespace embb
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment