Michael &Scott 无锁队列 C++ 实现
最近在研究无锁算法, 参照Michael and Scott的伪码,实现了个c++版本。
参考 http://www.cs.rochester.edu/research/synchronization/pseudocode/queues.html
伪代码是:
structure pointer_t {ptr: pointer to node_t, count: unsigned integer}
structure node_t {value: data type, next: pointer_t}
structure queue_t {Head: pointer_t, Tail: pointer_t}
initialize(Q: pointer to queue_t)
node = new_node() // Allocate a free node
node->next.ptr = NULL // Make it the only node in the linked list
Q->Head.ptr = Q->Tail.ptr = node // Both Head and Tail point to it
enqueue(Q: pointer to queue_t, value: data type)
E1: node = new_node() // Allocate a new node from the free list
E2: node->value = value // Copy enqueued value into node
E3: node->next.ptr = NULL // Set next pointer of node to NULL
E4: loop // Keep trying until Enqueue is done
E5: tail = Q->Tail // Read Tail.ptr and Tail.count together
E6: next = tail.ptr->next // Read next ptr and count fields together
E7: if tail == Q->Tail // Are tail and next consistent?
// Was Tail pointing to the last node?
E8: if next.ptr == NULL
// Try to link node at the end of the linked list
E9: if CAS(&tail.ptr->next, next, <node, next.count+1>)
E10: break // Enqueue is done. Exit loop
E11: endif
E12: else // Tail was not pointing to the last node
// Try to swing Tail to the next node
E13: CAS(&Q->Tail, tail, <next.ptr, tail.count+1>)
E14: endif
E15: endif
E16: endloop
// Enqueue is done. Try to swing Tail to the inserted node
E17: CAS(&Q->Tail, tail, <node, tail.count+1>)
dequeue(Q: pointer to queue_t, pvalue: pointer to data type): boolean
D1: loop // Keep trying until Dequeue is done
D2: head = Q->Head // Read Head
D3: tail = Q->Tail // Read Tail
D4: next = head.ptr->next // Read Head.ptr->next
D5: if head == Q->Head // Are head, tail, and next consistent?
D6: if head.ptr == tail.ptr // Is queue empty or Tail falling behind?
D7: if next.ptr == NULL // Is queue empty?
D8: return FALSE // Queue is empty, couldn't dequeue
D9: endif
// Tail is falling behind. Try to advance it
D10: CAS(&Q->Tail, tail, <next.ptr, tail.count+1>)
D11: else // No need to deal with Tail
// Read value before CAS
// Otherwise, another dequeue might free the next node
D12: *pvalue = next.ptr->value
// Try to swing Head to the next node
D13: if CAS(&Q->Head, head, <next.ptr, head.count+1>)
D14: break // Dequeue is done. Exit loop
D15: endif
D16: endif
D17: endif
D18: endloop
D19: free(head.ptr) // It is safe now to free the old node
D20: return TRUE // Queue was not empty, dequeue succeeded
structure node_t {value: data type, next: pointer_t}
structure queue_t {Head: pointer_t, Tail: pointer_t}
initialize(Q: pointer to queue_t)
node = new_node() // Allocate a free node
node->next.ptr = NULL // Make it the only node in the linked list
Q->Head.ptr = Q->Tail.ptr = node // Both Head and Tail point to it
enqueue(Q: pointer to queue_t, value: data type)
E1: node = new_node() // Allocate a new node from the free list
E2: node->value = value // Copy enqueued value into node
E3: node->next.ptr = NULL // Set next pointer of node to NULL
E4: loop // Keep trying until Enqueue is done
E5: tail = Q->Tail // Read Tail.ptr and Tail.count together
E6: next = tail.ptr->next // Read next ptr and count fields together
E7: if tail == Q->Tail // Are tail and next consistent?
// Was Tail pointing to the last node?
E8: if next.ptr == NULL
// Try to link node at the end of the linked list
E9: if CAS(&tail.ptr->next, next, <node, next.count+1>)
E10: break // Enqueue is done. Exit loop
E11: endif
E12: else // Tail was not pointing to the last node
// Try to swing Tail to the next node
E13: CAS(&Q->Tail, tail, <next.ptr, tail.count+1>)
E14: endif
E15: endif
E16: endloop
// Enqueue is done. Try to swing Tail to the inserted node
E17: CAS(&Q->Tail, tail, <node, tail.count+1>)
dequeue(Q: pointer to queue_t, pvalue: pointer to data type): boolean
D1: loop // Keep trying until Dequeue is done
D2: head = Q->Head // Read Head
D3: tail = Q->Tail // Read Tail
D4: next = head.ptr->next // Read Head.ptr->next
D5: if head == Q->Head // Are head, tail, and next consistent?
D6: if head.ptr == tail.ptr // Is queue empty or Tail falling behind?
D7: if next.ptr == NULL // Is queue empty?
D8: return FALSE // Queue is empty, couldn't dequeue
D9: endif
// Tail is falling behind. Try to advance it
D10: CAS(&Q->Tail, tail, <next.ptr, tail.count+1>)
D11: else // No need to deal with Tail
// Read value before CAS
// Otherwise, another dequeue might free the next node
D12: *pvalue = next.ptr->value
// Try to swing Head to the next node
D13: if CAS(&Q->Head, head, <next.ptr, head.count+1>)
D14: break // Dequeue is done. Exit loop
D15: endif
D16: endif
D17: endif
D18: endloop
D19: free(head.ptr) // It is safe now to free the old node
D20: return TRUE // Queue was not empty, dequeue succeeded
我的 C++实现:
#ifndef __FIFO_LOCK_FREE_H__ #define __FIFO_LOCK_FREE_H__ #include <stddef.h> #include <stdint.h> #include <stdlib.h> #include <malloc.h> namespace conet { class fifo_lockfree_t { public: typedef void * data_type; struct node_t; struct pointer_t { node_t *ptr; uint64_t tag; pointer_t() { ptr = NULL; tag = 0; } pointer_t(node_t *a_ptr, uint64_t a_tag) { ptr = a_ptr; tag=a_tag; } pointer_t(pointer_t const & a) { ptr = a.ptr; tag = a.tag; } } __attribute__ ((packed, aligned (16))) ; struct node_t { volatile pointer_t next; void * value; node_t() { value = NULL; // dummy_val next.ptr = NULL; next.tag = 0; } void reinit(void *val = NULL) { next.ptr = NULL; next.tag = 0; value = val; } }; static inline bool CAS2(pointer_t volatile *addr, pointer_t &old_value, pointer_t &new_value) { bool ret; __asm__ __volatile__( "lock cmpxchg16b %1;\n" "sete %0;\n" :"=m"(ret),"+m" (*(volatile pointer_t *) (addr)) :"a" (old_value.ptr), "d" (old_value.tag), "b" (new_value.ptr), "c" (new_value.tag)); return ret; } public: //var members volatile pointer_t tail_; volatile pointer_t head_; public: fifo_lockfree_t() { } static node_t * alloc_node() { node_t *nd = (node_t *)memalign(16, sizeof(node_t)); nd->reinit(NULL); return nd; } static void free_node(node_t *nd) { free(nd); } void init() { node_t *nd = alloc_node(); head_.ptr = nd; head_.tag = 0; tail_.ptr = nd; tail_.tag = 0; } void push(node_t *nd, void * val) { pointer_t tail, next; nd->value = val; nd->next.ptr = NULL; while(1) { tail.ptr = this->tail_.ptr; tail.tag = this->tail_.tag; next.ptr = tail.ptr->next.ptr; next.tag = tail.ptr->next.tag; if ((tail.ptr == this->tail_.ptr) && (tail.tag == this->tail_.tag)) { if(next.ptr == NULL) { pointer_t new_pt; new_pt.ptr = (node_t *)nd; new_pt.tag = next.tag+1; nd->next.tag = new_pt.tag; if(CAS2(&(this->tail_.ptr->next), next, new_pt)){ break; // Enqueue done! } }else { pointer_t new_pt(next.ptr, tail.tag+1); nd->next.tag = new_pt.tag; CAS2(&(this->tail_), tail, new_pt); } } } pointer_t new_pt(nd, tail.tag+1); CAS2(&(this->tail_), tail, new_pt); } node_t * pop() { pointer_t tail, head, next; void * value = NULL; while(1) { head.ptr = this->head_.ptr; head.tag = this->head_.tag; tail.ptr = this->tail_.ptr; tail.tag = this->tail_.tag; next.ptr = (head.ptr)->next.ptr; next.tag = (head.ptr)->next.tag; if ( (head.ptr == this->head_.ptr) && (head.tag == this->head_.tag) ) { if(head.ptr == tail.ptr){ if (next.ptr == NULL){ return NULL; } pointer_t new_pt(next.ptr, tail.tag+1); CAS2(&(this->tail_), tail, new_pt); } else{ value = next.ptr->value; pointer_t new_pt(next.ptr, head.tag+1); if(CAS2(&(this->head_), head, new_pt)){ break; } } } } node_t *nd = head.ptr; nd->value = value; return nd; } }; } #endif
gcc 4.1.2 编译, Linux下 经过框架测试没有问题。