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