github.com/loov/combiner@v0.1.0/extcombiner/basic_parking_uintptr.go (about) 1 package extcombiner 2 3 import ( 4 "runtime" 5 "sync" 6 "sync/atomic" 7 "unsafe" 8 ) 9 10 // BasicParkingUintptr is an unbounded non-spinning combiner queue using uintptr internally 11 // 12 // Based on https://software.intel.com/en-us/blogs/2013/02/22/combineraggregator-synchronization-primitive 13 type BasicParkingUintptr struct { 14 head uintptr // *basicParkingUintptrNode 15 _ [7]uint64 16 lock sync.Mutex 17 cond sync.Cond 18 _ [0]uint64 19 batcher Batcher 20 } 21 22 type basicParkingUintptrNode struct { 23 next uintptr // *basicParkingUintptrNode 24 argument interface{} 25 } 26 27 // NewBasicParkingUintptr creates a BasicParkingUintptr queue. 28 func NewBasicParkingUintptr(batcher Batcher) *BasicParkingUintptr { 29 c := &BasicParkingUintptr{ 30 batcher: batcher, 31 head: 0, 32 } 33 c.cond.L = &c.lock 34 return c 35 } 36 37 const basicParkingUintptrLocked = uintptr(1) 38 39 // Do passes value to Batcher and waits for completion 40 func (c *BasicParkingUintptr) Do(op interface{}) { 41 node := &basicParkingUintptrNode{argument: op} 42 43 var cmp uintptr 44 for { 45 cmp = atomic.LoadUintptr(&c.head) 46 xchg := basicParkingUintptrLocked 47 if cmp != 0 { 48 // There is already a combiner, enqueue itself. 49 xchg = uintptr(unsafe.Pointer(node)) 50 node.next = cmp 51 } 52 53 if atomic.CompareAndSwapUintptr(&c.head, cmp, xchg) { 54 break 55 } 56 } 57 58 if cmp != 0 { 59 for try := 0; try < busyspin; try++ { 60 if atomic.LoadUintptr(&node.next) == 0 { 61 return 62 } 63 runtime.Gosched() 64 } 65 66 c.lock.Lock() 67 for atomic.LoadUintptr(&node.next) != 0 { 68 c.cond.Wait() 69 } 70 c.lock.Unlock() 71 } else { 72 c.batcher.Start() 73 74 c.batcher.Do(node.argument) 75 76 for { 77 for { 78 cmp = atomic.LoadUintptr(&c.head) 79 // If there are some operations in the list, 80 // grab the list and replace with LOCKED. 81 // Otherwise, exchange to nil. 82 var xchg uintptr = 0 83 if cmp != basicParkingUintptrLocked { 84 xchg = basicParkingUintptrLocked 85 } 86 87 if atomic.CompareAndSwapUintptr(&c.head, cmp, xchg) { 88 break 89 } 90 } 91 92 // No more operations to combine, return. 93 if cmp == basicParkingUintptrLocked { 94 break 95 } 96 97 // Execute the list of operations. 98 for cmp != basicParkingUintptrLocked { 99 node = (*basicParkingUintptrNode)(unsafe.Pointer(cmp)) 100 cmp = node.next 101 102 c.batcher.Do(node.argument) 103 // Mark completion. 104 atomic.StoreUintptr(&node.next, 0) 105 } 106 } 107 108 c.batcher.Finish() 109 110 c.lock.Lock() 111 c.cond.Broadcast() 112 c.lock.Unlock() 113 } 114 }