github.com/egonelbre/exp@v0.0.0-20240430123955-ed1d3aa93911/queues/zfifo_freechan.go (about) 1 package goqueuestest 2 3 import ( 4 "sync/atomic" 5 "unsafe" 6 ) 7 8 type ZFifoFreechan struct { 9 head unsafe.Pointer 10 tail unsafe.Pointer 11 freelist chan *lfNode 12 } 13 14 // New returns an initialized queue 15 func NewZFifoFreechan() *ZFifoFreechan { 16 q := new(ZFifoFreechan) 17 // Creating an initial node 18 node := unsafe.Pointer(&lfNode{nil, unsafe.Pointer(q)}) 19 20 // Both head and tail point to the initial node 21 q.head = node 22 q.tail = node 23 24 // freelist for elements 25 q.freelist = make(chan *lfNode, 1000) 26 return q 27 } 28 29 func (q *ZFifoFreechan) newNode() *lfNode { 30 select { 31 case element := <-q.freelist: 32 return element 33 default: 34 } 35 return &lfNode{} 36 } 37 38 func (q *ZFifoFreechan) freeNode(elem *lfNode) { 39 select { 40 case q.freelist <- elem: 41 default: 42 } 43 } 44 45 // Enqueue inserts the value at the tail of the queue 46 func (q *ZFifoFreechan) Enqueue(value interface{}) { 47 node := q.newNode() 48 //new(lfNode) // Allocate a new node from the free list 49 node.value = value // Copy enqueued value into node 50 node.next = unsafe.Pointer(q) 51 for { // Keep trying until Enqueue is done 52 tail := atomic.LoadPointer(&q.tail) 53 54 // Try to link in new node 55 if atomic.CompareAndSwapPointer(&(*lfNode)(tail).next, unsafe.Pointer(q), unsafe.Pointer(node)) { 56 // Enqueue is done. Try to swing tail to the inserted node. 57 atomic.CompareAndSwapPointer(&q.tail, tail, unsafe.Pointer(node)) 58 return 59 } 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, (*lfNode)(tail).next) 63 } 64 } 65 66 // 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 67 func (q *ZFifoFreechan) Dequeue() (value interface{}, ok bool) { 68 for { 69 head := atomic.LoadPointer(&q.head) // Read head pointer 70 tail := atomic.LoadPointer(&q.tail) // Read tail pointer 71 next := atomic.LoadPointer(&(*lfNode)(head).next) // Read head.next 72 if head != q.head { // Check head, tail, and next consistency 73 continue // Not consistent. Try again 74 } 75 76 if head == tail { // Is queue empty or tail failing behind 77 if next == unsafe.Pointer(q) { // Is queue empty? 78 return 79 } 80 // Try to swing tail to the next node as the tail was not pointing to the last node 81 atomic.CompareAndSwapPointer(&q.tail, tail, next) 82 } else { 83 // Read value before CAS 84 // Otherwise, another dequeue might free the next node 85 value = (*lfNode)(next).value 86 // Try to swing Head to the next node 87 if atomic.CompareAndSwapPointer(&q.head, head, next) { 88 ok = true 89 q.freeNode((*lfNode)(head)) 90 return 91 } 92 value = nil 93 } 94 } 95 return // Dummy return 96 }