Skip to content

Commit ffe48e0

Browse files
committed
sync/atomic: add typed atomic values
These implementations will inline to the lower-level primitives, but they hide the underlying values so that all accesses are forced to use the atomic APIs. They also allow the use of shorter names (methods instead of functions) at call sites, making code more readable. Pointer[T] also avoids conversions using unsafe.Pointer at call sites. Discussed on #47141. See also https://research.swtch.com/gomm for background. Fixes #50860. Change-Id: I0b178ee0c7747fa8985f8e48cd7b01063feb7dcc Reviewed-on: https://go-review.googlesource.com/c/go/+/381317 Reviewed-by: Michael Pratt <[email protected]> Run-TryBot: Russ Cox <[email protected]> TryBot-Result: Gopher Robot <[email protected]>
1 parent cf69725 commit ffe48e0

File tree

5 files changed

+1415
-12
lines changed

5 files changed

+1415
-12
lines changed

api/next/50860.txt

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
pkg sync/atomic, method (*Bool) CompareAndSwap(bool, bool) bool #50860
2+
pkg sync/atomic, method (*Bool) Load() bool #50860
3+
pkg sync/atomic, method (*Bool) Store(bool) #50860
4+
pkg sync/atomic, method (*Bool) Swap(bool) bool #50860
5+
pkg sync/atomic, method (*Int32) Add(int32) int32 #50860
6+
pkg sync/atomic, method (*Int32) CompareAndSwap(int32, int32) bool #50860
7+
pkg sync/atomic, method (*Int32) Load() int32 #50860
8+
pkg sync/atomic, method (*Int32) Store(int32) #50860
9+
pkg sync/atomic, method (*Int32) Swap(int32) int32 #50860
10+
pkg sync/atomic, method (*Int64) Add(int64) int64 #50860
11+
pkg sync/atomic, method (*Int64) CompareAndSwap(int64, int64) bool #50860
12+
pkg sync/atomic, method (*Int64) Load() int64 #50860
13+
pkg sync/atomic, method (*Int64) Store(int64) #50860
14+
pkg sync/atomic, method (*Int64) Swap(int64) int64 #50860
15+
pkg sync/atomic, method (*Pointer[$0]) CompareAndSwap(*$0, *$0) bool #50860
16+
pkg sync/atomic, method (*Pointer[$0]) Load() *$0 #50860
17+
pkg sync/atomic, method (*Pointer[$0]) Store(*$0) #50860
18+
pkg sync/atomic, method (*Pointer[$0]) Swap(*$0) *$0 #50860
19+
pkg sync/atomic, method (*Uint32) Add(uint32) uint32 #50860
20+
pkg sync/atomic, method (*Uint32) CompareAndSwap(uint32, uint32) bool #50860
21+
pkg sync/atomic, method (*Uint32) Load() uint32 #50860
22+
pkg sync/atomic, method (*Uint32) Store(uint32) #50860
23+
pkg sync/atomic, method (*Uint32) Swap(uint32) uint32 #50860
24+
pkg sync/atomic, method (*Uint64) Add(uint64) uint64 #50860
25+
pkg sync/atomic, method (*Uint64) CompareAndSwap(uint64, uint64) bool #50860
26+
pkg sync/atomic, method (*Uint64) Load() uint64 #50860
27+
pkg sync/atomic, method (*Uint64) Store(uint64) #50860
28+
pkg sync/atomic, method (*Uint64) Swap(uint64) uint64 #50860
29+
pkg sync/atomic, method (*Uintptr) Add(uintptr) uintptr #50860
30+
pkg sync/atomic, method (*Uintptr) CompareAndSwap(uintptr, uintptr) bool #50860
31+
pkg sync/atomic, method (*Uintptr) Load() uintptr #50860
32+
pkg sync/atomic, method (*Uintptr) Store(uintptr) #50860
33+
pkg sync/atomic, method (*Uintptr) Swap(uintptr) uintptr #50860
34+
pkg sync/atomic, type Bool struct #50860
35+
pkg sync/atomic, type Int32 struct #50860
36+
pkg sync/atomic, type Int64 struct #50860
37+
pkg sync/atomic, type Pointer[$0 interface{}] struct #50860
38+
pkg sync/atomic, type Uint32 struct #50860
39+
pkg sync/atomic, type Uint64 struct #50860
40+
pkg sync/atomic, type Uintptr struct #50860

src/cmd/compile/internal/test/inl_test.go

+38
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,42 @@ func TestIntendedInlining(t *testing.T) {
180180
"net": {
181181
"(*UDPConn).ReadFromUDP",
182182
},
183+
"sync/atomic": {
184+
// (*Bool).CompareAndSwap handled below.
185+
"(*Bool).Load",
186+
"(*Bool).Store",
187+
"(*Bool).Swap",
188+
"(*Int32).Add",
189+
"(*Int32).CompareAndSwap",
190+
"(*Int32).Load",
191+
"(*Int32).Store",
192+
"(*Int32).Swap",
193+
"(*Int64).Add",
194+
"(*Int64).CompareAndSwap",
195+
"(*Int64).Load",
196+
"(*Int64).Store",
197+
"(*Int64).Swap",
198+
"(*Uint32).Add",
199+
"(*Uint32).CompareAndSwap",
200+
"(*Uint32).Load",
201+
"(*Uint32).Store",
202+
"(*Uint32).Swap",
203+
"(*Uint64).Add",
204+
"(*Uint64).CompareAndSwap",
205+
"(*Uint64).Load",
206+
"(*Uint64).Store",
207+
"(*Uint64).Swap",
208+
"(*Uintptr).Add",
209+
"(*Uintptr).CompareAndSwap",
210+
"(*Uintptr).Load",
211+
"(*Uintptr).Store",
212+
"(*Uintptr).Swap",
213+
// TODO(rsc): Why are these not reported as inlined?
214+
// "(*Pointer[T]).CompareAndSwap",
215+
// "(*Pointer[T]).Load",
216+
// "(*Pointer[T]).Store",
217+
// "(*Pointer[T]).Swap",
218+
},
183219
}
184220

185221
if runtime.GOARCH != "386" && runtime.GOARCH != "mips64" && runtime.GOARCH != "mips64le" && runtime.GOARCH != "riscv64" {
@@ -199,6 +235,8 @@ func TestIntendedInlining(t *testing.T) {
199235
if bits.UintSize == 64 {
200236
// mix is only defined on 64-bit architectures
201237
want["runtime"] = append(want["runtime"], "mix")
238+
// (*Bool).CompareAndSwap is just over budget on 32-bit systems (386, arm).
239+
want["sync/atomic"] = append(want["sync/atomic"], "(*Bool).CompareAndSwap")
202240
}
203241

204242
switch runtime.GOARCH {

src/cmd/compile/internal/types/size.go

+9
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,11 @@ func calcStructOffset(errtype *Type, t *Type, o int64, flag int) int64 {
167167
if maxalign < 1 {
168168
maxalign = 1
169169
}
170+
// Special case: sync/atomic.align64 is an empty struct we recognize
171+
// as a signal that the struct it contains must be 64-bit-aligned.
172+
if isStruct && t.NumFields() == 0 && t.Sym() != nil && t.Sym().Name == "align64" && isSyncAtomic(t.Sym().Pkg) {
173+
maxalign = 8
174+
}
170175
lastzero := int64(0)
171176
for _, f := range t.Fields().Slice() {
172177
if f.Type == nil {
@@ -226,6 +231,10 @@ func calcStructOffset(errtype *Type, t *Type, o int64, flag int) int64 {
226231
return o
227232
}
228233

234+
func isSyncAtomic(p *Pkg) bool {
235+
return p.Prefix == "sync/atomic" || p.Prefix == `""` && base.Ctxt.Pkgpath == "sync/atomic"
236+
}
237+
229238
// CalcSize calculates and stores the size and alignment for t.
230239
// If CalcSizeDisabled is set, and the size/alignment
231240
// have not already been calculated, it calls Fatal.

0 commit comments

Comments
 (0)