91 lines
2.0 KiB
Go
91 lines
2.0 KiB
Go
package async
|
|
|
|
import (
|
|
"sync/atomic"
|
|
|
|
"fyne.io/fyne/v2"
|
|
)
|
|
|
|
// CanvasObjectQueue implements lock-free FIFO freelist based queue.
|
|
//
|
|
// Reference: https://dl.acm.org/citation.cfm?doid=248052.248106
|
|
type CanvasObjectQueue struct {
|
|
head atomic.Pointer[itemCanvasObject]
|
|
tail atomic.Pointer[itemCanvasObject]
|
|
len atomic.Uint64
|
|
}
|
|
|
|
// NewCanvasObjectQueue returns a queue for caching values.
|
|
func NewCanvasObjectQueue() *CanvasObjectQueue {
|
|
head := &itemCanvasObject{}
|
|
queue := &CanvasObjectQueue{}
|
|
queue.head.Store(head)
|
|
queue.tail.Store(head)
|
|
return queue
|
|
}
|
|
|
|
type itemCanvasObject struct {
|
|
next atomic.Pointer[itemCanvasObject]
|
|
v fyne.CanvasObject
|
|
}
|
|
|
|
var itemCanvasObjectPool = Pool[*itemCanvasObject]{
|
|
New: func() *itemCanvasObject { return &itemCanvasObject{} },
|
|
}
|
|
|
|
// In puts the given value at the tail of the queue.
|
|
func (q *CanvasObjectQueue) In(v fyne.CanvasObject) {
|
|
i := itemCanvasObjectPool.Get()
|
|
i.next.Store(nil)
|
|
i.v = v
|
|
|
|
var last, lastnext *itemCanvasObject
|
|
for {
|
|
last = q.tail.Load()
|
|
lastnext = last.next.Load()
|
|
if q.tail.Load() == last {
|
|
if lastnext == nil {
|
|
if last.next.CompareAndSwap(lastnext, i) {
|
|
q.tail.CompareAndSwap(last, i)
|
|
q.len.Add(1)
|
|
return
|
|
}
|
|
} else {
|
|
q.tail.CompareAndSwap(last, lastnext)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Out removes and returns the value at the head of the queue.
|
|
// It returns nil if the queue is empty.
|
|
func (q *CanvasObjectQueue) Out() fyne.CanvasObject {
|
|
var first, last, firstnext *itemCanvasObject
|
|
for {
|
|
first = q.head.Load()
|
|
last = q.tail.Load()
|
|
firstnext = first.next.Load()
|
|
if first == q.head.Load() {
|
|
if first == last {
|
|
if firstnext == nil {
|
|
return nil
|
|
}
|
|
|
|
q.tail.CompareAndSwap(last, firstnext)
|
|
} else {
|
|
v := firstnext.v
|
|
if q.head.CompareAndSwap(first, firstnext) {
|
|
q.len.Add(^uint64(0))
|
|
itemCanvasObjectPool.Put(first)
|
|
return v
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Len returns the length of the queue.
|
|
func (q *CanvasObjectQueue) Len() uint64 {
|
|
return q.len.Load()
|
|
}
|