runtime/internal/atomic: add atomic types for all functions

Change-Id: I74f365316484feb819c31c77fbffd78fadfe32a9
Reviewed-on: https://go-review.googlesource.com/c/go/+/356169
Trust: Michael Knyszek <mknyszek@google.com>
Run-TryBot: Michael Knyszek <mknyszek@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Austin Clements <austin@google.com>
Reviewed-by: Michael Pratt <mpratt@google.com>
This commit is contained in:
Michael Anthony Knyszek 2021-09-24 16:01:03 +00:00 committed by Michael Knyszek
parent 3ff39c5eda
commit 3ec8d4b5ed
3 changed files with 443 additions and 0 deletions

View File

@ -0,0 +1,18 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
/*
Package atomic provides atomic operations, independent of sync/atomic,
to the runtime.
On most platforms, the compiler is aware of the functions defined
in this package, and they're replaced with platform-specific intrinsics.
On other platforms, generic implementations are made available.
Unless otherwise noted, operations defined in this package are sequentially
consistent across threads with respect to the values they manipulate. More
specifically, operations that happen in a specific order on one thread,
will always be observed to happen in exactly that order by another thread.
*/
package atomic

View File

@ -0,0 +1,395 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package atomic
import "unsafe"
// Int32 is an atomically accessed int32 value.
//
// An Int32 must not be copied.
type Int32 struct {
noCopy noCopy
value int32
}
// Load accesses and returns the value atomically.
func (i *Int32) Load() int32 {
return Loadint32(&i.value)
}
// Store updates the value atomically.
func (i *Int32) Store(value int32) {
Storeint32(&i.value, value)
}
// CompareAndSwap atomically compares i's value with old,
// and if they're equal, swaps i's value with new.
//
// Returns true if the operation succeeded.
func (i *Int32) CompareAndSwap(old, new int32) bool {
return Casint32(&i.value, old, new)
}
// Swap replaces i's value with new, returning
// i's value before the replacement.
func (i *Int32) Swap(new int32) int32 {
return Xchgint32(&i.value, new)
}
// Add adds delta to i atomically, returning
// the new updated value.
//
// This operation wraps around in the usual
// two's-complement way.
func (i *Int32) Add(delta int32) int32 {
return Xaddint32(&i.value, delta)
}
// Int64 is an atomically accessed int64 value.
//
// An Int64 must not be copied.
type Int64 struct {
noCopy noCopy
value int64
}
// Load accesses and returns the value atomically.
func (i *Int64) Load() int64 {
return Loadint64(&i.value)
}
// Store updates the value atomically.
func (i *Int64) Store(value int64) {
Storeint64(&i.value, value)
}
// CompareAndSwap atomically compares i's value with old,
// and if they're equal, swaps i's value with new.
//
// Returns true if the operation succeeded.
func (i *Int64) CompareAndSwap(old, new int64) bool {
return Casint64(&i.value, old, new)
}
// Swap replaces i's value with new, returning
// i's value before the replacement.
func (i *Int64) Swap(new int64) int64 {
return Xchgint64(&i.value, new)
}
// Add adds delta to i atomically, returning
// the new updated value.
//
// This operation wraps around in the usual
// two's-complement way.
func (i *Int64) Add(delta int64) int64 {
return Xaddint64(&i.value, delta)
}
// Uint8 is an atomically accessed uint8 value.
//
// A Uint8 must not be copied.
type Uint8 struct {
noCopy noCopy
value uint8
}
// Load accesses and returns the value atomically.
func (u *Uint8) Load() uint8 {
return Load8(&u.value)
}
// Store updates the value atomically.
func (u *Uint8) Store(value uint8) {
Store8(&u.value, value)
}
// And takes value and performs a bit-wise
// "and" operation with the value of u, storing
// the result into u.
//
// The full process is performed atomically.
func (u *Uint8) And(value uint8) {
And8(&u.value, value)
}
// Or takes value and performs a bit-wise
// "or" operation with the value of u, storing
// the result into u.
//
// The full process is performed atomically.
func (u *Uint8) Or(value uint8) {
Or8(&u.value, value)
}
// Uint32 is an atomically accessed uint32 value.
//
// A Uint32 must not be copied.
type Uint32 struct {
noCopy noCopy
value uint32
}
// Load accesses and returns the value atomically.
func (u *Uint32) Load() uint32 {
return Load(&u.value)
}
// LoadAcquire is a partially unsynchronized version
// of Load that relaxes ordering constraints. Other threads
// may observe operations that precede this operation to
// occur after it, but no operation that occurs after it
// on this thread can be observed to occur before it.
//
// WARNING: Use sparingly and with great care.
func (u *Uint32) LoadAcquire() uint32 {
return LoadAcq(&u.value)
}
// Store updates the value atomically.
func (u *Uint32) Store(value uint32) {
Store(&u.value, value)
}
// StoreRelease is a partially unsynchronized version
// of Store that relaxes ordering constraints. Other threads
// may observe operations that occur after this operation to
// precede it, but no operation that precedes it
// on this thread can be observed to occur after it.
//
// WARNING: Use sparingly and with great care.
func (u *Uint32) StoreRelease(value uint32) {
StoreRel(&u.value, value)
}
// CompareAndSwap atomically compares u's value with old,
// and if they're equal, swaps u's value with new.
//
// Returns true if the operation succeeded.
func (u *Uint32) CompareAndSwap(old, new uint32) bool {
return Cas(&u.value, old, new)
}
// CompareAndSwapRelease is a partially unsynchronized version
// of Cas that relaxes ordering constraints. Other threads
// may observe operations that occur after this operation to
// precede it, but no operation that precedes it
// on this thread can be observed to occur after it.
//
// Returns true if the operation succeeded.
//
// WARNING: Use sparingly and with great care.
func (u *Uint32) CompareAndSwapRelease(old, new uint32) bool {
return CasRel(&u.value, old, new)
}
// Swap replaces u's value with new, returning
// u's value before the replacement.
func (u *Uint32) Swap(value uint32) uint32 {
return Xchg(&u.value, value)
}
// And takes value and performs a bit-wise
// "and" operation with the value of u, storing
// the result into u.
//
// The full process is performed atomically.
func (u *Uint32) And(value uint32) {
And(&u.value, value)
}
// Or takes value and performs a bit-wise
// "or" operation with the value of u, storing
// the result into u.
//
// The full process is performed atomically.
func (u *Uint32) Or(value uint32) {
Or(&u.value, value)
}
// Add adds delta to u atomically, returning
// the new updated value.
//
// This operation wraps around in the usual
// two's-complement way.
func (u *Uint32) Add(delta int32) uint32 {
return Xadd(&u.value, delta)
}
// Uint64 is an atomically accessed uint64 value.
//
// A Uint64 must not be copied.
type Uint64 struct {
noCopy noCopy
value uint64
}
// Load accesses and returns the value atomically.
func (u *Uint64) Load() uint64 {
return Load64(&u.value)
}
// Store updates the value atomically.
func (u *Uint64) Store(value uint64) {
Store64(&u.value, value)
}
// CompareAndSwap atomically compares u's value with old,
// and if they're equal, swaps u's value with new.
//
// Returns true if the operation succeeded.
func (u *Uint64) CompareAndSwap(old, new uint64) bool {
return Cas64(&u.value, old, new)
}
// Swap replaces u's value with new, returning
// u's value before the replacement.
func (u *Uint64) Swap(value uint64) uint64 {
return Xchg64(&u.value, value)
}
// Add adds delta to u atomically, returning
// the new updated value.
//
// This operation wraps around in the usual
// two's-complement way.
func (u *Uint64) Add(delta int64) uint64 {
return Xadd64(&u.value, delta)
}
// Uintptr is an atomically accessed uintptr value.
//
// A Uintptr must not be copied.
type Uintptr struct {
noCopy noCopy
value uintptr
}
// Load accesses and returns the value atomically.
func (u *Uintptr) Load() uintptr {
return Loaduintptr(&u.value)
}
// LoadAcquire is a partially unsynchronized version
// of Load that relaxes ordering constraints. Other threads
// may observe operations that precede this operation to
// occur after it, but no operation that occurs after it
// on this thread can be observed to occur before it.
//
// WARNING: Use sparingly and with great care.
func (u *Uintptr) LoadAcquire() uintptr {
return LoadAcquintptr(&u.value)
}
// Store updates the value atomically.
func (u *Uintptr) Store(value uintptr) {
Storeuintptr(&u.value, value)
}
// StoreRelease is a partially unsynchronized version
// of Store that relaxes ordering constraints. Other threads
// may observe operations that occur after this operation to
// precede it, but no operation that precedes it
// on this thread can be observed to occur after it.
//
// WARNING: Use sparingly and with great care.
func (u *Uintptr) StoreRelease(value uintptr) {
StoreReluintptr(&u.value, value)
}
// CompareAndSwap atomically compares u's value with old,
// and if they're equal, swaps u's value with new.
//
// Returns true if the operation succeeded.
func (u *Uintptr) CompareAndSwap(old, new uintptr) bool {
return Casuintptr(&u.value, old, new)
}
// Swap replaces u's value with new, returning
// u's value before the replacement.
func (u *Uintptr) Swap(value uintptr) uintptr {
return Xchguintptr(&u.value, value)
}
// Add adds delta to u atomically, returning
// the new updated value.
//
// This operation wraps around in the usual
// two's-complement way.
func (u *Uintptr) Add(delta uintptr) uintptr {
return Xadduintptr(&u.value, delta)
}
// Float64 is an atomically accessed float64 value.
//
// A Float64 must not be copied.
type Float64 struct {
u Uint64
}
// Load accesses and returns the value atomically.
func (f *Float64) Load() float64 {
r := f.u.Load()
return *(*float64)(unsafe.Pointer(&r))
}
// Store updates the value atomically.
func (f *Float64) Store(value float64) {
f.u.Store(*(*uint64)(unsafe.Pointer(&value)))
}
// UnsafePointer is an atomically accessed unsafe.Pointer value.
//
// Note that because of the atomicity guarantees, stores to values
// of this type never trigger a write barrier, and the relevant
// methods are suffixed with "NoWB" to indicate that explicitly.
// As a result, this type should be used carefully, and sparingly,
// mostly with values that do not live in the Go heap anyway.
//
// An UnsafePointer must not be copied.
type UnsafePointer struct {
noCopy noCopy
value unsafe.Pointer
}
// Load accesses and returns the value atomically.
func (u *UnsafePointer) Load() unsafe.Pointer {
return Loadp(unsafe.Pointer(&u.value))
}
// StoreNoWB updates the value atomically.
//
// WARNING: As the name implies this operation does *not*
// perform a write barrier on value, and so this operation may
// hide pointers from the GC. Use with care and sparingly.
// It is safe to use with values not found in the Go heap.
func (u *UnsafePointer) StoreNoWB(value unsafe.Pointer) {
StorepNoWB(unsafe.Pointer(&u.value), value)
}
// CompareAndSwapNoWB atomically (with respect to other methods)
// compares u's value with old, and if they're equal,
// swaps u's value with new.
//
// Returns true if the operation succeeded.
//
// WARNING: As the name implies this operation does *not*
// perform a write barrier on value, and so this operation may
// hide pointers from the GC. Use with care and sparingly.
// It is safe to use with values not found in the Go heap.
func (u *UnsafePointer) CompareAndSwapNoWB(old, new unsafe.Pointer) bool {
return Casp1(&u.value, old, new)
}
// noCopy may be embedded into structs which must not be copied
// after the first use.
//
// See https://golang.org/issues/8005#issuecomment-190753527
// for details.
type noCopy struct{}
// Lock is a no-op used by -copylocks checker from `go vet`.
func (*noCopy) Lock() {}
func (*noCopy) Unlock() {}

View File

@ -0,0 +1,30 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build amd64 || arm64 || mips64 || mips64le || ppc64 || ppc64le || riscv64 || s390x || wasm
// +build amd64 arm64 mips64 mips64le ppc64 ppc64le riscv64 s390x wasm
package atomic
// LoadAcquire is a partially unsynchronized version
// of Load that relaxes ordering constraints. Other threads
// may observe operations that precede this operation to
// occur after it, but no operation that occurs after it
// on this thread can be observed to occur before it.
//
// WARNING: Use sparingly and with great care.
func (u *Uint64) LoadAcquire() uint64 {
return LoadAcq64(&u.value)
}
// StoreRelease is a partially unsynchronized version
// of Store that relaxes ordering constraints. Other threads
// may observe operations that occur after this operation to
// precede it, but no operation that precedes it
// on this thread can be observed to occur after it.
//
// WARNING: Use sparingly and with great care.
func (u *Uint64) StoreRelease(value uint64) {
StoreRel64(&u.value, value)
}