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  }