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  }