github.com/asynkron/protoactor-go@v0.0.0-20240308120642-ef91a6abee75/internal/queue/goring/queue.go (about)

     1  package goring
     2  
     3  import (
     4  	"sync"
     5  	"sync/atomic"
     6  )
     7  
     8  type ringBuffer struct {
     9  	buffer []interface{}
    10  	head   int64
    11  	tail   int64
    12  	mod    int64
    13  }
    14  
    15  type Queue struct {
    16  	len     int64
    17  	content *ringBuffer
    18  	lock    sync.Mutex
    19  }
    20  
    21  func New(initialSize int64) *Queue {
    22  	return &Queue{
    23  		content: &ringBuffer{
    24  			buffer: make([]interface{}, initialSize),
    25  			head:   0,
    26  			tail:   0,
    27  			mod:    initialSize,
    28  		},
    29  		len: 0,
    30  	}
    31  }
    32  
    33  func (q *Queue) Push(item interface{}) {
    34  	q.lock.Lock()
    35  	c := q.content
    36  	c.tail = (c.tail + 1) % c.mod
    37  	if c.tail == c.head {
    38  		var fillFactor int64 = 2
    39  		// we need to resize
    40  
    41  		newLen := c.mod * fillFactor
    42  		newBuff := make([]interface{}, newLen)
    43  
    44  		for i := int64(0); i < c.mod; i++ {
    45  			buffIndex := (c.tail + i) % c.mod
    46  			newBuff[i] = c.buffer[buffIndex]
    47  		}
    48  		// set the new buffer and reset head and tail
    49  		newContent := &ringBuffer{
    50  			buffer: newBuff,
    51  			head:   0,
    52  			tail:   c.mod,
    53  			mod:    newLen,
    54  		}
    55  		q.content = newContent
    56  	}
    57  	atomic.AddInt64(&q.len, 1)
    58  	q.content.buffer[q.content.tail] = item
    59  	q.lock.Unlock()
    60  }
    61  
    62  func (q *Queue) Length() int64 {
    63  	return atomic.LoadInt64(&q.len)
    64  }
    65  
    66  func (q *Queue) Empty() bool {
    67  	return q.Length() == 0
    68  }
    69  
    70  // single consumer
    71  func (q *Queue) Pop() (interface{}, bool) {
    72  	if q.Empty() {
    73  		return nil, false
    74  	}
    75  	// as we are a single consumer, no other thread can have poped the items there are guaranteed to be items now
    76  
    77  	q.lock.Lock()
    78  	c := q.content
    79  	c.head = (c.head + 1) % c.mod
    80  	res := c.buffer[c.head]
    81  	c.buffer[c.head] = nil
    82  	atomic.AddInt64(&q.len, -1)
    83  	q.lock.Unlock()
    84  	return res, true
    85  }
    86  
    87  func (q *Queue) PopMany(count int64) ([]interface{}, bool) {
    88  	if q.Empty() {
    89  		return nil, false
    90  	}
    91  
    92  	q.lock.Lock()
    93  	c := q.content
    94  
    95  	if count >= q.len {
    96  		count = q.len
    97  	}
    98  	atomic.AddInt64(&q.len, -count)
    99  
   100  	buffer := make([]interface{}, count)
   101  	for i := int64(0); i < count; i++ {
   102  		pos := (c.head + 1 + i) % c.mod
   103  		buffer[i] = c.buffer[pos]
   104  		c.buffer[pos] = nil
   105  	}
   106  	c.head = (c.head + count) % c.mod
   107  
   108  	q.lock.Unlock()
   109  	return buffer, true
   110  }