From 554a7b9a7c038e180ce62c213c19102e77cd8f60 Mon Sep 17 00:00:00 2001 From: Jacalz Date: Tue, 26 Dec 2023 16:47:26 +0100 Subject: [PATCH] Rework async queue to not use unsafe --- internal/async/gen.go | 181 -------------------- internal/async/queue_canvasobject.go | 55 ++++-- internal/async/queue_pure_canvasobject.go | 43 ----- internal/async/queue_unsafe_canvasobject.go | 43 ----- 4 files changed, 39 insertions(+), 283 deletions(-) delete mode 100755 internal/async/queue_pure_canvasobject.go delete mode 100755 internal/async/queue_unsafe_canvasobject.go diff --git a/internal/async/gen.go b/internal/async/gen.go index cee1d73726..2ff74fa252 100644 --- a/internal/async/gen.go +++ b/internal/async/gen.go @@ -45,40 +45,6 @@ func main() { Imports: "", }, }, - queueImpl: { - "queue_canvasobject.go": { - Type: "fyne.CanvasObject", - Name: "CanvasObject", - Imports: `import ( - "sync" - - "fyne.io/fyne/v2" - )`, - }, - }, - queueUnsafeStructImpl: { - "queue_unsafe_canvasobject.go": { - Type: "fyne.CanvasObject", - Name: "CanvasObject", - Imports: `import ( - "sync/atomic" - "unsafe" - - "fyne.io/fyne/v2" - )`, - }, - }, - queuePureStructImpl: { - "queue_pure_canvasobject.go": { - Type: "fyne.CanvasObject", - Name: "CanvasObject", - Imports: `import ( - "sync/atomic" - - "fyne.io/fyne/v2" - )`, - }, - }, } for tmpl, types := range codes { @@ -201,150 +167,3 @@ func (ch *Unbounded{{.Name}}Chan) closed() { close(ch.close) } `)) - -var queuePureStructImpl = template.Must(template.New("queue").Parse(`// Code generated by go run gen.go; DO NOT EDIT. -//go:build js -// +build js - -package async - -{{.Imports}} - -// {{.Name}}Queue implements lock-free FIFO freelist based queue. -// -// Reference: https://dl.acm.org/citation.cfm?doid=248052.248106 -type {{.Name}}Queue struct { - head *item{{.Name}} - tail *item{{.Name}} - len atomic.Uint64 -} - -// New{{.Name}}Queue returns a queue for caching values. -func New{{.Name}}Queue() *{{.Name}}Queue { - head := &item{{.Name}}{next: nil, v: nil} - return &{{.Name}}Queue{ - tail: head, - head: head, - } -} - -type item{{.Name}} struct { - next *item{{.Name}} - v {{.Type}} -} - -func load{{.Name}}Item(p **item{{.Name}}) *item{{.Name}} { - return *p -} - -func cas{{.Name}}Item(p **item{{.Name}}, _, new *item{{.Name}}) bool { - *p = new - return true -} -`)) - -var queueUnsafeStructImpl = template.Must(template.New("queue").Parse(`// Code generated by go run gen.go; DO NOT EDIT. -//go:build !js -// +build !js - -package async - -{{.Imports}} - -// {{.Name}}Queue implements lock-free FIFO freelist based queue. -// -// Reference: https://dl.acm.org/citation.cfm?doid=248052.248106 -type {{.Name}}Queue struct { - head unsafe.Pointer - tail unsafe.Pointer - len atomic.Uint64 -} - -// New{{.Name}}Queue returns a queue for caching values. -func New{{.Name}}Queue() *{{.Name}}Queue { - head := &item{{.Name}}{next: nil, v: nil} - return &{{.Name}}Queue{ - tail: unsafe.Pointer(head), - head: unsafe.Pointer(head), - } -} - -type item{{.Name}} struct { - next unsafe.Pointer - v {{.Type}} -} - -func load{{.Name}}Item(p *unsafe.Pointer) *item{{.Name}} { - return (*item{{.Name}})(atomic.LoadPointer(p)) -} - -func cas{{.Name}}Item(p *unsafe.Pointer, old, new *item{{.Name}}) bool { - return atomic.CompareAndSwapPointer(p, unsafe.Pointer(old), unsafe.Pointer(new)) -} -`)) - -var queueImpl = template.Must(template.New("queue").Parse(`// Code generated by go run gen.go; DO NOT EDIT. - -package async - -{{.Imports}} - -var item{{.Name}}Pool = sync.Pool{ - New: func() interface{} { return &item{{.Name}}{next: nil, v: nil} }, -} - -// In puts the given value at the tail of the queue. -func (q *{{.Name}}Queue) In(v {{.Type}}) { - i := item{{.Name}}Pool.Get().(*item{{.Name}}) - i.next = nil - i.v = v - - var last, lastnext *item{{.Name}} - for { - last = load{{.Name}}Item(&q.tail) - lastnext = load{{.Name}}Item(&last.next) - if load{{.Name}}Item(&q.tail) == last { - if lastnext == nil { - if cas{{.Name}}Item(&last.next, lastnext, i) { - cas{{.Name}}Item(&q.tail, last, i) - q.len.Add(1) - return - } - } else { - cas{{.Name}}Item(&q.tail, last, lastnext) - } - } - } -} - -// Out removes and returns the value at the head of the queue. -// It returns nil if the queue is empty. -func (q *{{.Name}}Queue) Out() {{.Type}} { - var first, last, firstnext *item{{.Name}} - for { - first = load{{.Name}}Item(&q.head) - last = load{{.Name}}Item(&q.tail) - firstnext = load{{.Name}}Item(&first.next) - if first == load{{.Name}}Item(&q.head) { - if first == last { - if firstnext == nil { - return nil - } - cas{{.Name}}Item(&q.tail, last, firstnext) - } else { - v := firstnext.v - if cas{{.Name}}Item(&q.head, first, firstnext) { - q.len.Add(^uint64(0)) - item{{.Name}}Pool.Put(first) - return v - } - } - } - } -} - -// Len returns the length of the queue. -func (q *{{.Name}}Queue) Len() uint64 { - return q.len.Load() -} -`)) diff --git a/internal/async/queue_canvasobject.go b/internal/async/queue_canvasobject.go index 1af42f28df..5ccd42bf6e 100755 --- a/internal/async/queue_canvasobject.go +++ b/internal/async/queue_canvasobject.go @@ -1,36 +1,58 @@ -// Code generated by go run gen.go; DO NOT EDIT. - package async import ( "sync" + "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 = sync.Pool{ - New: func() interface{} { return &itemCanvasObject{next: nil, v: nil} }, + New: func() interface{} { return &itemCanvasObject{} }, } // In puts the given value at the tail of the queue. func (q *CanvasObjectQueue) In(v fyne.CanvasObject) { i := itemCanvasObjectPool.Get().(*itemCanvasObject) - i.next = nil + i.next.Store(nil) i.v = v var last, lastnext *itemCanvasObject for { - last = loadCanvasObjectItem(&q.tail) - lastnext = loadCanvasObjectItem(&last.next) - if loadCanvasObjectItem(&q.tail) == last { + last = q.tail.Load() + lastnext = last.next.Load() + if q.tail.Load() == last { if lastnext == nil { - if casCanvasObjectItem(&last.next, lastnext, i) { - casCanvasObjectItem(&q.tail, last, i) + if last.next.CompareAndSwap(lastnext, i) { + q.tail.CompareAndSwap(last, i) q.len.Add(1) return } } else { - casCanvasObjectItem(&q.tail, last, lastnext) + q.tail.CompareAndSwap(last, lastnext) } } } @@ -41,18 +63,19 @@ func (q *CanvasObjectQueue) In(v fyne.CanvasObject) { func (q *CanvasObjectQueue) Out() fyne.CanvasObject { var first, last, firstnext *itemCanvasObject for { - first = loadCanvasObjectItem(&q.head) - last = loadCanvasObjectItem(&q.tail) - firstnext = loadCanvasObjectItem(&first.next) - if first == loadCanvasObjectItem(&q.head) { + 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 } - casCanvasObjectItem(&q.tail, last, firstnext) + + q.tail.CompareAndSwap(last, firstnext) } else { v := firstnext.v - if casCanvasObjectItem(&q.head, first, firstnext) { + if q.head.CompareAndSwap(first, firstnext) { q.len.Add(^uint64(0)) itemCanvasObjectPool.Put(first) return v diff --git a/internal/async/queue_pure_canvasobject.go b/internal/async/queue_pure_canvasobject.go deleted file mode 100755 index e2f44fc10c..0000000000 --- a/internal/async/queue_pure_canvasobject.go +++ /dev/null @@ -1,43 +0,0 @@ -// Code generated by go run gen.go; DO NOT EDIT. -//go:build js -// +build js - -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 *itemCanvasObject - tail *itemCanvasObject - len atomic.Uint64 -} - -// NewCanvasObjectQueue returns a queue for caching values. -func NewCanvasObjectQueue() *CanvasObjectQueue { - head := &itemCanvasObject{next: nil, v: nil} - return &CanvasObjectQueue{ - tail: head, - head: head, - } -} - -type itemCanvasObject struct { - next *itemCanvasObject - v fyne.CanvasObject -} - -func loadCanvasObjectItem(p **itemCanvasObject) *itemCanvasObject { - return *p -} - -func casCanvasObjectItem(p **itemCanvasObject, _, new *itemCanvasObject) bool { - *p = new - return true -} diff --git a/internal/async/queue_unsafe_canvasobject.go b/internal/async/queue_unsafe_canvasobject.go deleted file mode 100755 index 386434f406..0000000000 --- a/internal/async/queue_unsafe_canvasobject.go +++ /dev/null @@ -1,43 +0,0 @@ -// Code generated by go run gen.go; DO NOT EDIT. -//go:build !js -// +build !js - -package async - -import ( - "sync/atomic" - "unsafe" - - "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 unsafe.Pointer - tail unsafe.Pointer - len atomic.Uint64 -} - -// NewCanvasObjectQueue returns a queue for caching values. -func NewCanvasObjectQueue() *CanvasObjectQueue { - head := &itemCanvasObject{next: nil, v: nil} - return &CanvasObjectQueue{ - tail: unsafe.Pointer(head), - head: unsafe.Pointer(head), - } -} - -type itemCanvasObject struct { - next unsafe.Pointer - v fyne.CanvasObject -} - -func loadCanvasObjectItem(p *unsafe.Pointer) *itemCanvasObject { - return (*itemCanvasObject)(atomic.LoadPointer(p)) -} - -func casCanvasObjectItem(p *unsafe.Pointer, old, new *itemCanvasObject) bool { - return atomic.CompareAndSwapPointer(p, unsafe.Pointer(old), unsafe.Pointer(new)) -}