github.com/alphadose/zenq/v2@v2.8.4/select_list.go (about) 1 package zenq 2 3 import ( 4 "sync" 5 "sync/atomic" 6 "unsafe" 7 ) 8 9 // global memory pool for storing and leasing node objects 10 var ( 11 nodePool = sync.Pool{New: func() any { return new(node) }} 12 nodeGet = nodePool.Get 13 nodePut = nodePool.Put 14 ) 15 16 // List is a lock-free linked list 17 // theory -> https://www.cs.rochester.edu/u/scott/papers/1996_PODC_queues.pdf 18 // pseudocode -> https://www.cs.rochester.edu/research/synchronization/pseudocode/queues.html 19 type List struct { 20 head atomic.Pointer[node] 21 tail atomic.Pointer[node] 22 } 23 24 // NewList returns a new list 25 func NewList() List { 26 n := nodeGet().(*node) 27 n.threadPtr, n.dataOut = nil, nil 28 n.next.Store(nil) 29 var ptr atomic.Pointer[node] 30 ptr.Store(n) 31 return List{head: ptr, tail: ptr} 32 } 33 34 // a single node in the linked list 35 type node struct { 36 next atomic.Pointer[node] 37 threadPtr *unsafe.Pointer 38 dataOut *any 39 } 40 41 // Enqueue inserts a value into the list 42 func (l *List) Enqueue(threadPtr *unsafe.Pointer, dataOut *any) { 43 var ( 44 n = nodeGet().(*node) 45 tail, next *node 46 ) 47 n.threadPtr, n.dataOut = threadPtr, dataOut 48 for { 49 tail = l.tail.Load() 50 next = tail.next.Load() 51 if tail == l.tail.Load() { // are tail and next consistent? 52 if next == nil { 53 if tail.next.CompareAndSwap(next, n) { 54 l.tail.CompareAndSwap(tail, n) // Enqueue is done. try to swing tail to the inserted node 55 return 56 } 57 } else { // tail was not pointing to the last node 58 // try to swing Tail to the next node 59 l.tail.CompareAndSwap(tail, next) 60 } 61 } 62 } 63 } 64 65 // Dequeue removes and returns the value at the head of the queue to the memory pool 66 // It returns nil if the list is empty 67 func (l *List) Dequeue() (threadPtr *unsafe.Pointer, dataOut *any) { 68 var head, tail, next *node 69 for { 70 head = l.head.Load() 71 tail = l.tail.Load() 72 next = head.next.Load() 73 if head == l.head.Load() { // are head, tail, and next consistent? 74 if head == tail { // is list empty or tail falling behind? 75 if next == nil { // is list empty? 76 return nil, nil 77 } 78 // tail is falling behind. try to advance it 79 l.tail.CompareAndSwap(tail, next) 80 } else { 81 // read value before CAS_node otherwise another dequeue might free the next node 82 threadPtr, dataOut = next.threadPtr, next.dataOut 83 if l.head.CompareAndSwap(head, next) { 84 // sysFreeOS(unsafe.Pointer(head), nodeSize) 85 head.threadPtr, head.dataOut = nil, nil 86 head.next.Store(nil) 87 nodePut(head) 88 return // Dequeue is done. return 89 } 90 } 91 } 92 } 93 }