You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

84 lines
1.4 KiB
Go

package mpsc
import (
"sync/atomic"
"unsafe"
)
type (
Queue[T any] struct {
head unsafe.Pointer
tail unsafe.Pointer
length int64
}
Node[T any] struct {
value T
next unsafe.Pointer
}
)
func New[T any]() *Queue[T] {
n := unsafe.Pointer(new(Node[T]))
return &Queue[T]{
head: n,
tail: n,
}
}
func (q *Queue[T]) Push(v T) {
n := &Node[T]{value: v}
for {
tail := load[T](&q.tail)
next := load[T](&tail.next)
if tail == load[T](&q.tail) {
atomic.AddInt64(&q.length, 1)
if next == nil {
if cas(&tail.next, next, n) {
cas(&q.tail, tail, n)
return
}
} else {
cas(&q.tail, tail, next)
}
}
}
}
func (q *Queue[T]) Pop() (v T) {
for {
head := load[T](&q.head)
tail := load[T](&q.tail)
next := load[T](&head.next)
if head == load[T](&q.head) {
if head == tail {
if next == nil {
return
}
cas(&q.tail, tail, next)
} else {
v = next.value
atomic.AddInt64(&q.length, -1)
if cas(&q.head, head, next) {
return
}
}
}
}
}
func (q *Queue[T]) Length() int64 {
return q.length
}
func (q *Queue[T]) Empty() bool {
return q.length == 0
}
func load[T any](p *unsafe.Pointer) *Node[T] {
return (*Node[T])(atomic.LoadPointer(p))
}
func cas[T any](p *unsafe.Pointer, old, new *Node[T]) bool {
return atomic.CompareAndSwapPointer(p,
unsafe.Pointer(old), unsafe.Pointer(new))
}