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