github.com/egonelbre/exp@v0.0.0-20240430123955-ed1d3aa93911/queues/zfifo.go (about) 1 package goqueuestest 2 3 import ( 4 "sync/atomic" 5 "unsafe" 6 ) 7 8 // From https://github.com/anisus/queue/blob/master/queue.go 9 10 type ZFifo struct { 11 head unsafe.Pointer 12 tail unsafe.Pointer 13 } 14 15 // New returns an initialized queue 16 func NewZFifo() *ZFifo { 17 q := new(ZFifo) 18 // Creating an initial node 19 node := unsafe.Pointer(&lfNode{nil, unsafe.Pointer(q)}) 20 21 // Both head and tail point to the initial node 22 q.head = node 23 q.tail = node 24 return q 25 } 26 27 // Enqueue inserts the value at the tail of the queue 28 func (q *ZFifo) Enqueue(value interface{}) { 29 node := new(lfNode) // Allocate a new node from the free list 30 node.value = value // Copy enqueued value into node 31 node.next = unsafe.Pointer(q) 32 for { // Keep trying until Enqueue is done 33 tail := atomic.LoadPointer(&q.tail) 34 35 // Try to link in new node 36 if atomic.CompareAndSwapPointer(&(*lfNode)(tail).next, unsafe.Pointer(q), unsafe.Pointer(node)) { 37 // Enqueue is done. Try to swing tail to the inserted node. 38 atomic.CompareAndSwapPointer(&q.tail, tail, unsafe.Pointer(node)) 39 return 40 } 41 42 // Try to swing tail to the next node as the tail was not pointing to the last node 43 atomic.CompareAndSwapPointer(&q.tail, tail, (*lfNode)(tail).next) 44 } 45 } 46 47 // Dequeue returns the value at the head of the queue and true, or if the queue is empty, it returns a nil value and false 48 func (q *ZFifo) Dequeue() (value interface{}, ok bool) { 49 for { 50 head := atomic.LoadPointer(&q.head) // Read head pointer 51 tail := atomic.LoadPointer(&q.tail) // Read tail pointer 52 next := atomic.LoadPointer(&(*lfNode)(head).next) // Read head.next 53 if head != q.head { // Check head, tail, and next consistency 54 continue // Not consistent. Try again 55 } 56 57 if head == tail { // Is queue empty or tail failing behind 58 if next == unsafe.Pointer(q) { // Is queue empty? 59 return 60 } 61 // Try to swing tail to the next node as the tail was not pointing to the last node 62 atomic.CompareAndSwapPointer(&q.tail, tail, next) 63 } else { 64 // Read value before CAS 65 // Otherwise, another dequeue might free the next node 66 value = (*lfNode)(next).value 67 // Try to swing Head to the next node 68 if atomic.CompareAndSwapPointer(&q.head, head, next) { 69 ok = true 70 return 71 } 72 value = nil 73 } 74 } 75 return // Dummy return 76 }