github.com/alexyer/taggedptr@v0.0.0-20160103160510-29e0338f9028/taggedptr.go (about)

     1  // This module contains methods to tag the pointer.
     2  // Tagged pointer is a pointer with additional data associate with it.
     3  // It's possible because data must be word aligned,
     4  // hence least significant bits could be used to store some data.
     5  // It's useful in lock-free programming,
     6  // to store some data in pointer atomically using CAS instructions.
     7  package taggedptr
     8  
     9  import (
    10  	"errors"
    11  	"sync/atomic"
    12  	"unsafe"
    13  )
    14  
    15  const MAX_TAG_SIZE = 3
    16  
    17  // Return tagged pointer.
    18  func Tag(ptr unsafe.Pointer, tag uint) (unsafe.Pointer, error) {
    19  	if tag > MAX_TAG_SIZE {
    20  		return nil, errors.New("Too large tag")
    21  	}
    22  
    23  	ptr = unsafe.Pointer(uintptr(ptr) | uintptr(tag))
    24  
    25  	return ptr, nil
    26  }
    27  
    28  // Get the current value of the pointer.
    29  func GetPointer(ptr unsafe.Pointer) unsafe.Pointer {
    30  	return unsafe.Pointer(uintptr(ptr) &^ uintptr(MAX_TAG_SIZE))
    31  }
    32  
    33  // Return the current value of the tag.
    34  func GetTag(ptr unsafe.Pointer) uint {
    35  	return uint(uintptr(ptr) & uintptr(MAX_TAG_SIZE))
    36  }
    37  
    38  // Get pair of pointer and tag values.
    39  func Get(ptr unsafe.Pointer) (unsafe.Pointer, uint) {
    40  	return GetPointer(ptr), GetTag(ptr)
    41  }
    42  
    43  // Atomically tag pointer.
    44  func AttemptTag(addr *unsafe.Pointer, expectedPtr unsafe.Pointer, tag uint) bool {
    45  	taggedPtr, err := Tag(expectedPtr, tag)
    46  
    47  	if err != nil {
    48  		return false
    49  	}
    50  
    51  	return atomic.CompareAndSwapPointer(addr, expectedPtr, taggedPtr)
    52  }
    53  
    54  // Compare and swap tagged pointer.
    55  func CompareAndSwap(addr *unsafe.Pointer, oldPtr, newPtr unsafe.Pointer, oldTag, newTag uint) bool {
    56  	var err error
    57  
    58  	if oldPtr, err = Tag(oldPtr, oldTag); err != nil {
    59  		return false
    60  	}
    61  
    62  	if newPtr, err = Tag(newPtr, newTag); err != nil {
    63  		return false
    64  	}
    65  
    66  	return atomic.CompareAndSwapPointer(addr, oldPtr, newPtr)
    67  }