github.com/mitghi/x@v0.0.0-20191206171256-71e86edf750d/pointers/pointers.go (about) 1 /* MIT License 2 * 3 * Copyright (c) 2018 Mike Taghavi <mitghi[at]gmail.com> 4 * 5 * Permission is hereby granted, free of charge, to any person obtaining a copy 6 * of this software and associated documentation files (the "Software"), to deal 7 * in the Software without restriction, including without limitation the rights 8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 * copies of the Software, and to permit persons to whom the Software is 10 * furnished to do so, subject to the following conditions: 11 * The above copyright notice and this permission notice shall be included in all 12 * copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 * SOFTWARE. 21 */ 22 23 package pointers 24 25 import ( 26 "sync/atomic" 27 "unsafe" 28 ) 29 30 // - MARK: Atomics section. 31 32 // CASSliceSlot is a function that performs a CAS operation 33 // on a given slice slot by performing pointer arithmitic 34 // to find slot address. `addr` is a pointer to slice, 35 // `data` is a pointer to old value to be compared, 36 // `target` is a pointer to the new value, `index` is 37 // the slot number and `ptrsize` is the slice value size. 38 // It returns true when succesfull. 39 func CASSliceSlot(addr unsafe.Pointer, data unsafe.Pointer, target unsafe.Pointer, index int, ptrsize uintptr) bool { 40 var ( 41 tptr *unsafe.Pointer 42 cptr unsafe.Pointer 43 ) 44 tptr = (*unsafe.Pointer)(unsafe.Pointer(*(*uintptr)(addr) + (ptrsize * uintptr(index)))) 45 cptr = unsafe.Pointer(tptr) 46 return atomic.CompareAndSwapPointer( 47 (*unsafe.Pointer)(unsafe.Pointer(cptr)), 48 (unsafe.Pointer)(unsafe.Pointer(target)), 49 (unsafe.Pointer)(unsafe.Pointer(data)), 50 ) 51 } 52 53 // CASSliceSlotPtr is a function that performs a CAS operation 54 // on a given slice slot by performing pointer arithmitic 55 // to find slot pointer address. `addr` is a pointer to slice, 56 // `data` is a pointer to old value to be compared, 57 // `target` is a pointer to the new value, `index` is 58 // the slot number and `ptrsize` is the slice value size. 59 // It returns true when succesfull. 60 func CASSliceSlotPtr(addr unsafe.Pointer, data unsafe.Pointer, target unsafe.Pointer, index int, ptrsize uintptr) bool { 61 var ( 62 tptr *unsafe.Pointer 63 cptr unsafe.Pointer 64 ) 65 tptr = (*unsafe.Pointer)(unsafe.Pointer((uintptr)(addr) + (ptrsize * uintptr(index)))) 66 cptr = unsafe.Pointer(tptr) 67 return atomic.CompareAndSwapPointer( 68 (*unsafe.Pointer)(unsafe.Pointer(cptr)), 69 (unsafe.Pointer)(unsafe.Pointer(target)), 70 (unsafe.Pointer)(unsafe.Pointer(data)), 71 ) 72 } 73 74 // CASArraySlot is a function that performs a CAS operation 75 // on a given array slot by performing pointer arithmitic 76 // to find slot address. `addr` is a pointer to array, 77 // `data` is a pointer to old value to be compared, 78 // `target` is a pointer to the new value, `index` is 79 // the slot number and `ptrsize` is the slice value size. 80 // It returns true when succesfull. 81 func CASArraySlot(addr unsafe.Pointer, data unsafe.Pointer, target unsafe.Pointer, index int, ptrsize uintptr) bool { 82 var ( 83 tptr *unsafe.Pointer 84 cptr unsafe.Pointer 85 ) 86 tptr = (*unsafe.Pointer)(unsafe.Pointer((uintptr)(addr) + (ptrsize * uintptr(index)))) 87 cptr = unsafe.Pointer(tptr) 88 return atomic.CompareAndSwapPointer((*unsafe.Pointer)(unsafe.Pointer(cptr)), 89 (unsafe.Pointer)(unsafe.Pointer(target)), 90 (unsafe.Pointer)(unsafe.Pointer(data)), 91 ) 92 } 93 94 // OffsetArraySlot takes a array pointer and returns 95 // slot address by adding `index` times `ptrsize` bytes 96 // to slice data pointer. 97 func OffsetArraySlot(addr unsafe.Pointer, index int, ptrsize uintptr) unsafe.Pointer { 98 return unsafe.Pointer((*unsafe.Pointer)(unsafe.Pointer((uintptr)(addr) + (ptrsize * uintptr(index))))) 99 } 100 101 // OffsetSliceSlot takes a slice pointer and returns 102 // slot address by adding `index` times `ptrsize` bytes 103 // to slice data pointer. 104 func OffsetSliceSlot(addr unsafe.Pointer, index int, ptrsize uintptr) unsafe.Pointer { 105 return unsafe.Pointer(*(*uintptr)(addr) + (ptrsize * uintptr(index))) 106 } 107 108 // SetSliceSlot is a wrapper function that writes `d` 109 // to the given slice slot iff its nil and returns 110 // true when succesfull. 111 func SetSliceSlot(addr unsafe.Pointer, index int, ptrsize uintptr, d unsafe.Pointer) bool { 112 return CASSliceSlot(addr, d, nil, index, ptrsize) 113 } 114 115 // SetSliceSlotPtr is a wrapper function that writes `d` 116 // to the given slice slot opinter iff its nil and returns 117 // true when succesfull. 118 func SetSliceSlotPtr(addr unsafe.Pointer, index int, ptrsize uintptr, d unsafe.Pointer) bool { 119 return CASSliceSlotPtr(addr, d, nil, index, ptrsize) 120 } 121 122 // SetSliceSlotI is a wrapper function that writes `d` 123 // to the given slice slot iff its nil and return 124 // true when succesfull. Note, it differs from 125 // `SetSliceSlot` because `d` is written as a pointer 126 // to `interface{}`. 127 func SetSliceSlotI(addr unsafe.Pointer, index int, ptrsize uintptr, d interface{}) bool { 128 return CASSliceSlot(addr, unsafe.Pointer(&d), nil, index, ptrsize) 129 } 130 131 // SetArraySlot is a wrapper function that writes `d` 132 // to the given array slot iff its nil. It returns 133 // true when succesfull. 134 func SetArraySlot(addr unsafe.Pointer, index int, ptrsize uintptr, d unsafe.Pointer) bool { 135 return CASArraySlot(addr, d, nil, index, ptrsize) 136 } 137 138 // LoadArraySlot takes a array pointer and loads 139 // slot address by adding `index` times `ptrsize` bytes 140 // to slice data pointer. 141 func LoadArraySlot(addr unsafe.Pointer, index int, ptrsize uintptr) unsafe.Pointer { 142 var ( 143 tptr *unsafe.Pointer 144 ) 145 tptr = (*unsafe.Pointer)(unsafe.Pointer((uintptr)(addr) + (ptrsize * uintptr(index)))) 146 return atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(tptr))) 147 } 148 149 // LoadSliceSlot takes a slice pointer and loads 150 // slot address by adding `index` times `ptrsize` bytes 151 // to slice data pointer. 152 func LoadSliceSlot(addr unsafe.Pointer, index int, ptrsize uintptr) unsafe.Pointer { 153 var ( 154 bin *unsafe.Pointer 155 ) 156 bin = (*unsafe.Pointer)(unsafe.Pointer(*(*uintptr)(addr) + (ptrsize * uintptr(index)))) 157 return atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(bin))) 158 } 159 160 // PopArraySlot is a wrapper function that pops 161 // `index` slot of array iff its nil. It returns 162 // a pointer and true when succesfull. 163 func PopArraySlot(addr unsafe.Pointer, index int, ptrsize uintptr) (unsafe.Pointer, bool) { 164 var ( 165 slot unsafe.Pointer = LoadArraySlot(addr, index, ptrsize) 166 ) 167 if !CASArraySlot(addr, nil, slot, index, ptrsize) { 168 return nil, false 169 } 170 171 return slot, true 172 } 173 174 // PopSliceSlot is a wrapper function that pops 175 // `index` slot of slice iff its nil. It returns 176 // a pointer and true when succesfull. 177 func PopSliceSlot(addr unsafe.Pointer, index int, ptrsize uintptr) (unsafe.Pointer, bool) { 178 var ( 179 slot unsafe.Pointer = LoadSliceSlot(addr, index, ptrsize) 180 ) 181 if !CASSliceSlot(addr, nil, slot, index, ptrsize) { 182 return nil, false 183 } 184 185 return slot, true 186 } 187 188 // CompareAndSwapPointerTag performs CAS operation 189 // and swaps `source` to `source` with new tag 190 // when comparision is successfull. It reutrns a 191 // pointer and boolean to to indicate its success. 192 func CompareAndSwapPointerTag(source unsafe.Pointer, oldtag uint, newtag uint) (unsafe.Pointer, bool) { 193 if oldtag > ArchMAXTAG || newtag > ArchMAXTAG { 194 panic(EPTRINVALT) 195 } 196 var ( 197 sraw unsafe.Pointer = Untag(source) 198 sptr unsafe.Pointer 199 target unsafe.Pointer 200 ) 201 sptr, _ = TaggedPointer(sraw, oldtag) 202 target, _ = TaggedPointer(sraw, newtag) 203 if atomic.CompareAndSwapPointer( 204 (*unsafe.Pointer)(unsafe.Pointer(&sptr)), 205 (unsafe.Pointer)(source), 206 (unsafe.Pointer)(target), 207 ) { 208 return target, true 209 } 210 211 return nil, false 212 }