github.com/zhiqiangxu/util@v0.0.0-20230112053021-0a7aee056cd5/lf/mcas/mcas.go (about) 1 package mcas 2 3 import ( 4 "sort" 5 "sync/atomic" 6 "unsafe" 7 ) 8 9 type mcDesc struct { 10 a []*unsafe.Pointer 11 e []unsafe.Pointer 12 n []unsafe.Pointer 13 s uint32 14 } 15 16 const ( 17 undecided uint32 = iota 18 failed 19 successful 20 ) 21 22 func isMCDesc(v unsafe.Pointer) bool { 23 return uintptr(v)&addrMask == mcDescAddr 24 } 25 26 const ( 27 mcDescAddr = 1 28 ccDescAddr = 2 29 addrMask = 3 30 ) 31 32 func mcfromPointer(v unsafe.Pointer) *mcDesc { 33 ptr := uintptr(v) 34 ptr = ptr & ^uintptr(addrMask) 35 return (*mcDesc)(are.getPointer(ptr)) 36 } 37 38 func (d *mcDesc) toPointer() unsafe.Pointer { 39 return unsafe.Pointer(uintptr(unsafe.Pointer(d)) + uintptr(mcDescAddr)) 40 } 41 42 func (d *mcDesc) sortAddr() { 43 sort.Slice(d.a, func(i, j int) bool { 44 return uintptr(unsafe.Pointer(d.a[i])) < uintptr(unsafe.Pointer(d.a[j])) 45 }) 46 } 47 48 func (d *mcDesc) status() uint32 { 49 return atomic.LoadUint32(&d.s) 50 } 51 52 func (d *mcDesc) mcasHelp() (suc bool) { 53 ds := failed 54 var ( 55 v unsafe.Pointer 56 ) 57 /* PHASE 1: Attempt to acquire each location in turn. */ 58 for i := range d.a { 59 for { 60 ccas(d.a[i], d.e[i], d.toPointer(), &d.s) 61 v = atomic.LoadPointer(d.a[i]) 62 if v == d.toPointer() { 63 break 64 } 65 66 if isCCDesc(v) { 67 ccfromPointer(v).ccasHelp() 68 continue 69 } else if isMCDesc(v) { 70 mcfromPointer(v).mcasHelp() 71 continue 72 } 73 74 // v is plain pointer value 75 76 if v == d.e[i] && d.status() == undecided { 77 continue 78 } 79 80 goto decision_point 81 } 82 } 83 84 ds = successful 85 86 decision_point: 87 88 atomic.CompareAndSwapUint32(&d.s, undecided, ds) 89 90 /* PHASE 2: Release each location that we hold. */ 91 suc = atomic.LoadUint32(&d.s) == successful 92 for i := range d.a { 93 if suc { 94 atomic.CompareAndSwapPointer(d.a[i], d.toPointer(), d.n[i]) 95 } else { 96 atomic.CompareAndSwapPointer(d.a[i], d.toPointer(), d.e[i]) 97 } 98 } 99 100 return 101 }