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  }