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  }