github.com/egonelbre/exp@v0.0.0-20240430123955-ed1d3aa93911/combiner/flat.go (about) 1 package combiner 2 3 import ( 4 "runtime" 5 "sync/atomic" 6 "unsafe" 7 8 "github.com/egonelbre/exp/sync2/runtime2" 9 ) 10 11 const ( 12 max_cores = 64 13 max_items = 256 14 max_spin = 256 15 ) 16 17 type Flat struct { 18 proc [maxProcessors]flatProc 19 tail unsafe.Pointer 20 } 21 22 type flatProc struct { 23 root flatNode 24 _ [7]int64 25 } 26 27 type flatNode struct { 28 request func() 29 wait int64 30 complete int64 31 next unsafe.Pointer 32 } 33 34 func (q *Flat) Do(request func()) { 35 pid := runtime2.ProcessorHint() 36 nextNode := &q.proc[pid%maxProcessors].root 37 nextNode.complete = 0 38 atomic.StoreInt64(&nextNode.wait, 1) 39 40 var curNode *flatNode 41 for { 42 curNode = (*flatNode)(atomic.LoadPointer(&q.tail)) 43 if atomic.CompareAndSwapPointer(&q.tail, (unsafe.Pointer)(curNode), (unsafe.Pointer)(nextNode)) { 44 break 45 } 46 } 47 48 curNode.request = request 49 atomic.StorePointer((*unsafe.Pointer)(&curNode.next), (unsafe.Pointer)(nextNode)) 50 51 spin := 0 52 for atomic.LoadInt64(&curNode.wait) == 1 { 53 if spin++; spin >= max_spin { 54 runtime.Gosched() 55 spin = 0 56 } 57 } 58 59 if curNode.complete == 1 { 60 return 61 } 62 63 n := curNode 64 for combined := 0; combined < max_items; combined++ { 65 n.request() 66 nn := (*flatNode)(atomic.LoadPointer(&n.next)) 67 atomic.StorePointer(&n.next, nil) 68 n.request = nil 69 n.complete = 1 70 atomic.StoreInt64(&n.wait, 0) 71 72 n = nn 73 if n == nil { 74 break 75 } 76 } 77 }