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  }