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 }