github.com/hslam/atomic@v1.0.0/value.go (about)

     1  // Copyright (c) 2020 Meng Huang (mhboy@outlook.com)
     2  // This package is licensed under a MIT license that can be found in the LICENSE file.
     3  
     4  package atomic
     5  
     6  import (
     7  	"sync/atomic"
     8  	"unsafe"
     9  )
    10  
    11  // AddFunc is a add function.
    12  type AddFunc func(old, delta interface{}) (new interface{})
    13  
    14  // EqualFunc is a equal function.
    15  type EqualFunc func(old, load interface{}) (equal bool)
    16  
    17  // Value provides an atomic load and store of a consistently typed value.
    18  // The zero value for a Value returns nil from Load.
    19  // Once Store has been called, a Value must not be copied.
    20  //
    21  // A Value must not be copied after first use.
    22  type Value struct {
    23  	v         atomic.Value
    24  	EqualFunc EqualFunc
    25  	AddFunc   AddFunc
    26  }
    27  
    28  // ifaceWords is interface{} internal representation.
    29  type ifaceWords struct {
    30  	typ  unsafe.Pointer
    31  	data unsafe.Pointer
    32  }
    33  
    34  // NewValue returns a new Value.
    35  func NewValue(val interface{}, equalFunc EqualFunc, addFunc AddFunc) *Value {
    36  	addr := &Value{EqualFunc: equalFunc, AddFunc: addFunc}
    37  	addr.Store(val)
    38  	return addr
    39  }
    40  
    41  // Swap atomically stores new into *addr and returns the previous *addr value.
    42  func (v *Value) Swap(new interface{}) (old interface{}) {
    43  	for {
    44  		old = v.Load()
    45  		if v.compareAndSwap(old, new) {
    46  			return
    47  		}
    48  	}
    49  }
    50  
    51  // CompareAndSwap executes the compare-and-swap operation for an interface{} value.
    52  func (v *Value) CompareAndSwap(old, new interface{}) (swapped bool) {
    53  	if v.EqualFunc == nil {
    54  		panic("EqualFunc is nil")
    55  	}
    56  	load := v.Load()
    57  	if !v.EqualFunc(old, load) {
    58  		return false
    59  	}
    60  	return v.compareAndSwap(load, new)
    61  }
    62  
    63  // compareAndSwap executes the compare-and-swap operation for an interface{} value.
    64  func (v *Value) compareAndSwap(old, new interface{}) (swapped bool) {
    65  	if new == nil {
    66  		panic("github.com/hslam/atomic: new is nil")
    67  	}
    68  	vp := (*ifaceWords)(unsafe.Pointer(&v.v))
    69  	np := (*ifaceWords)(unsafe.Pointer(&new))
    70  	typ := LoadPointer(&vp.typ)
    71  	if typ == nil {
    72  		// Attempt to start first store.
    73  		if !CompareAndSwapPointer(&vp.typ, nil, unsafe.Pointer(^uintptr(0))) {
    74  			return false
    75  		}
    76  		// Complete first store.
    77  		StorePointer(&vp.data, np.data)
    78  		StorePointer(&vp.typ, np.typ)
    79  		return
    80  	}
    81  	if uintptr(typ) == ^uintptr(0) {
    82  		// First store in progress.
    83  		return false
    84  	}
    85  	if old == nil {
    86  		panic("github.com/hslam/atomic: old is nil")
    87  	}
    88  	// First store completed. Check type.
    89  	op := (*ifaceWords)(unsafe.Pointer(&old))
    90  	if typ != op.typ {
    91  		panic("github.com/hslam/atomic: old is inconsistently typed value")
    92  	}
    93  	if typ != np.typ {
    94  		panic("github.com/hslam/atomic: new is inconsistently typed value")
    95  	}
    96  	return atomic.CompareAndSwapPointer(&vp.data, op.data, np.data)
    97  }
    98  
    99  // Add atomically adds delta to *addr and returns the new value.
   100  func (v *Value) Add(delta interface{}) (new interface{}) {
   101  	if v.AddFunc == nil {
   102  		panic("AddFunc is nil")
   103  	}
   104  	for {
   105  		old := v.Load()
   106  		new = v.AddFunc(old, delta)
   107  		if v.compareAndSwap(old, new) {
   108  			return
   109  		}
   110  	}
   111  }
   112  
   113  // Load returns the value set by the most recent Store.
   114  // It returns nil if there has been no call to Store for this Value.
   115  func (v *Value) Load() (x interface{}) {
   116  	return v.v.Load()
   117  }
   118  
   119  // Store sets the value of the Value to x.
   120  // All calls to Store for a given Value must use values of the same concrete type.
   121  // Store of an inconsistent type panics, as does Store(nil).
   122  func (v *Value) Store(x interface{}) {
   123  	v.v.Store(x)
   124  }