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  }