github.com/egonelbre/exp@v0.0.0-20240430123955-ed1d3aa93911/combiner/remote.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 maxProcessors = 16 12 13 type Remote struct { 14 batcher Batcher 15 done int64 16 _ [7]uint64 17 proc [maxProcessors]remoteProc 18 } 19 20 type remoteNode struct { 21 next unsafe.Pointer // *remoteNode 22 argument interface{} 23 } 24 25 type remoteProc struct { 26 queue int64 27 done int64 28 _ [6]uint64 29 req unsafe.Pointer // *remoteNode 30 _ [7]uint64 31 } 32 33 func NewRemote(bat Batcher) *Remote { 34 return &Remote{batcher: bat} 35 } 36 37 func (c *Remote) Run() { 38 var toRelease [maxProcessors]*unsafe.Pointer 39 for atomic.LoadInt64(&c.done) == 0 { 40 k := 0 41 c.batcher.Start() 42 for i := 0; i < maxProcessors; i++ { 43 proc := &c.proc[i] 44 pnode := atomic.LoadPointer(&proc.req) 45 if pnode != nil { 46 node := (*remoteNode)(pnode) 47 c.batcher.Include(node.argument) 48 toRelease[k] = &proc.req 49 k++ 50 } 51 } 52 c.batcher.Finish() 53 for _, p := range toRelease[:k] { 54 atomic.StorePointer(p, nil) 55 } 56 } 57 } 58 59 func (c *Remote) Close() { atomic.StoreInt64(&c.done, 1) } 60 61 func (c *Remote) Do(arg interface{}) { 62 node := &remoteNode{argument: arg} 63 64 pid := runtime2.ProcessorHint() 65 proc := &c.proc[pid%maxProcessors] 66 67 ticket := atomic.AddInt64(&proc.queue, 1) 68 for ticket != atomic.LoadInt64(&proc.done)+1 { 69 runtime.Gosched() 70 } 71 72 atomic.StorePointer(&proc.req, unsafe.Pointer(node)) 73 74 for atomic.LoadPointer(&proc.req) != nil { 75 runtime.Gosched() 76 } 77 78 atomic.StoreInt64(&proc.done, ticket) 79 }