github.com/asynkron/protoactor-go@v0.0.0-20240308120642-ef91a6abee75/internal/queue/mpsc/mpsc.go (about) 1 // Package mpsc provides an efficient implementation of a multi-producer, single-consumer lock-free queue. 2 // 3 // The Push function is safe to call from multiple goroutines. The Pop and Empty APIs must only be 4 // called from a single, consumer goroutine. 5 package mpsc 6 7 // This implementation is based on http://www.1024cores.net/home/lock-free-algorithms/queues/non-intrusive-mpsc-node-based-queue 8 9 import ( 10 "sync/atomic" 11 "unsafe" 12 ) 13 14 type node struct { 15 next *node 16 val interface{} 17 } 18 19 type Queue struct { 20 head, tail *node 21 } 22 23 func New() *Queue { 24 q := &Queue{} 25 stub := &node{} 26 q.head = stub 27 q.tail = stub 28 return q 29 } 30 31 // Push adds x to the back of the queue. 32 // 33 // Push can be safely called from multiple goroutines 34 func (q *Queue) Push(x interface{}) { 35 n := new(node) 36 n.val = x 37 // current producer acquires head node 38 prev := (*node)(atomic.SwapPointer((*unsafe.Pointer)(unsafe.Pointer(&q.head)), unsafe.Pointer(n))) 39 40 // release node to consumer 41 atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&prev.next)), unsafe.Pointer(n)) 42 } 43 44 // Pop removes the item from the front of the queue or nil if the queue is empty 45 // 46 // Pop must be called from a single, consumer goroutine 47 func (q *Queue) Pop() interface{} { 48 tail := q.tail 49 next := (*node)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&tail.next)))) // acquire 50 if next != nil { 51 q.tail = next 52 v := next.val 53 next.val = nil 54 return v 55 } 56 return nil 57 } 58 59 // Empty returns true if the queue is empty 60 // 61 // Empty must be called from a single, consumer goroutine 62 func (q *Queue) Empty() bool { 63 tail := q.tail 64 next := (*node)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&tail.next)))) 65 return next == nil 66 }