Skip to content

Commit 4f446f3

Browse files
committed
Remove compatibility code for Go versions prior to 1.20
1 parent faeaff3 commit 4f446f3

File tree

5 files changed

+43
-231
lines changed

5 files changed

+43
-231
lines changed

common/atomic/typed.go

Lines changed: 20 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,45 +6,41 @@ import (
66
"github.com/sagernet/sing/common"
77
)
88

9-
type TypedValue[T any] struct {
10-
value atomic.Value
11-
}
12-
13-
// typedValue is a struct with determined type to resolve atomic.Value usages with interface types
14-
// https://github.com/golang/go/issues/22550
15-
//
16-
// The intention to have an atomic value store for errors. However, running this code panics:
17-
// panic: sync/atomic: store of inconsistently typed value into Value
18-
// This is because atomic.Value requires that the underlying concrete type be the same (which is a reasonable expectation for its implementation).
19-
// When going through the atomic.Value.Store method call, the fact that both these are of the error interface is lost.
20-
type typedValue[T any] struct {
21-
value T
22-
}
9+
type TypedValue[T any] atomic.Pointer[T]
2310

2411
func (t *TypedValue[T]) Load() T {
25-
value := t.value.Load()
12+
value := (*atomic.Pointer[T])(t).Load()
2613
if value == nil {
2714
return common.DefaultValue[T]()
2815
}
29-
return value.(typedValue[T]).value
16+
return *value
3017
}
3118

3219
func (t *TypedValue[T]) Store(value T) {
33-
t.value.Store(typedValue[T]{value})
20+
(*atomic.Pointer[T])(t).Store(&value)
3421
}
3522

3623
func (t *TypedValue[T]) Swap(new T) T {
37-
old := t.value.Swap(typedValue[T]{new})
24+
old := (*atomic.Pointer[T])(t).Swap(&new)
3825
if old == nil {
3926
return common.DefaultValue[T]()
4027
}
41-
return old.(typedValue[T]).value
28+
return *old
4229
}
4330

4431
func (t *TypedValue[T]) CompareAndSwap(old, new T) bool {
45-
return t.value.CompareAndSwap(typedValue[T]{old}, typedValue[T]{new}) ||
46-
// In the edge-case where [atomic.Value.Store] is uninitialized
47-
// and trying to compare with the zero value of T,
48-
// then compare-and-swap with the nil any value.
49-
(any(old) == any(common.DefaultValue[T]()) && t.value.CompareAndSwap(any(nil), typedValue[T]{new}))
32+
for {
33+
currentP := (*atomic.Pointer[T])(t).Load()
34+
currentValue := common.DefaultValue[T]()
35+
if currentP != nil {
36+
currentValue = *currentP
37+
}
38+
// Compare old and current via runtime equality check.
39+
if any(currentValue) != any(old) {
40+
return false
41+
}
42+
if (*atomic.Pointer[T])(t).CompareAndSwap(currentP, &new) {
43+
return true
44+
}
45+
}
5046
}

common/atomic/types.go

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,16 @@
1-
//go:build go1.19
1+
//go:build go1.24
22

33
package atomic
44

55
import "sync/atomic"
66

77
type (
8-
Bool = atomic.Bool
9-
Int32 = atomic.Int32
10-
Int64 = atomic.Int64
11-
Uint32 = atomic.Uint32
12-
Uint64 = atomic.Uint64
13-
Uintptr = atomic.Uintptr
14-
Value = atomic.Value
8+
Bool = atomic.Bool
9+
Int32 = atomic.Int32
10+
Int64 = atomic.Int64
11+
Uint32 = atomic.Uint32
12+
Uint64 = atomic.Uint64
13+
Uintptr = atomic.Uintptr
14+
Value = atomic.Value
15+
Pointer[T any] = atomic.Pointer[T]
1516
)
16-
17-
type Pointer[T any] struct {
18-
atomic.Pointer[T]
19-
}

common/atomic/types_compat.go

Lines changed: 12 additions & 193 deletions
Original file line numberDiff line numberDiff line change
@@ -1,198 +1,17 @@
1-
// Copyright 2022 The Go Authors. All rights reserved.
2-
// Use of this source code is governed by a BSD-style
3-
// license that can be found in the LICENSE file.
4-
5-
//go:build !go1.19
1+
//go:build !go1.24
62

73
package atomic
84

9-
import (
10-
"sync/atomic"
11-
"unsafe"
12-
)
13-
14-
// A Bool is an atomic boolean value.
15-
// The zero value is false.
16-
type Bool struct {
17-
_ noCopy
18-
v uint32
19-
}
20-
21-
// Load atomically loads and returns the value stored in x.
22-
func (x *Bool) Load() bool { return atomic.LoadUint32(&x.v) != 0 }
23-
24-
// Store atomically stores val into x.
25-
func (x *Bool) Store(val bool) { atomic.StoreUint32(&x.v, b32(val)) }
26-
27-
// Swap atomically stores new into x and returns the previous value.
28-
func (x *Bool) Swap(new bool) (old bool) { return atomic.SwapUint32(&x.v, b32(new)) != 0 }
29-
30-
// CompareAndSwap executes the compare-and-swap operation for the boolean value x.
31-
func (x *Bool) CompareAndSwap(old, new bool) (swapped bool) {
32-
return atomic.CompareAndSwapUint32(&x.v, b32(old), b32(new))
33-
}
34-
35-
// b32 returns a uint32 0 or 1 representing b.
36-
func b32(b bool) uint32 {
37-
if b {
38-
return 1
39-
}
40-
return 0
41-
}
42-
43-
// A Pointer is an atomic pointer of type *T. The zero value is a nil *T.
44-
type Pointer[T any] struct {
45-
// Mention *T in a field to disallow conversion between Pointer types.
46-
// See go.dev/issue/56603 for more details.
47-
// Use *T, not T, to avoid spurious recursive type definition errors.
48-
_ [0]*T
49-
50-
_ noCopy
51-
v unsafe.Pointer
52-
}
53-
54-
// Load atomically loads and returns the value stored in x.
55-
func (x *Pointer[T]) Load() *T { return (*T)(atomic.LoadPointer(&x.v)) }
56-
57-
// Store atomically stores val into x.
58-
func (x *Pointer[T]) Store(val *T) { atomic.StorePointer(&x.v, unsafe.Pointer(val)) }
59-
60-
// Swap atomically stores new into x and returns the previous value.
61-
func (x *Pointer[T]) Swap(new *T) (old *T) {
62-
return (*T)(atomic.SwapPointer(&x.v, unsafe.Pointer(new)))
63-
}
64-
65-
// CompareAndSwap executes the compare-and-swap operation for x.
66-
func (x *Pointer[T]) CompareAndSwap(old, new *T) (swapped bool) {
67-
return atomic.CompareAndSwapPointer(&x.v, unsafe.Pointer(old), unsafe.Pointer(new))
68-
}
69-
70-
// An Int32 is an atomic int32. The zero value is zero.
71-
type Int32 struct {
72-
_ noCopy
73-
v int32
74-
}
75-
76-
// Load atomically loads and returns the value stored in x.
77-
func (x *Int32) Load() int32 { return atomic.LoadInt32(&x.v) }
78-
79-
// Store atomically stores val into x.
80-
func (x *Int32) Store(val int32) { atomic.StoreInt32(&x.v, val) }
81-
82-
// Swap atomically stores new into x and returns the previous value.
83-
func (x *Int32) Swap(new int32) (old int32) { return atomic.SwapInt32(&x.v, new) }
84-
85-
// CompareAndSwap executes the compare-and-swap operation for x.
86-
func (x *Int32) CompareAndSwap(old, new int32) (swapped bool) {
87-
return atomic.CompareAndSwapInt32(&x.v, old, new)
88-
}
89-
90-
// Add atomically adds delta to x and returns the new value.
91-
func (x *Int32) Add(delta int32) (new int32) { return atomic.AddInt32(&x.v, delta) }
92-
93-
// An Int64 is an atomic int64. The zero value is zero.
94-
type Int64 struct {
95-
_ noCopy
96-
v int64
97-
}
98-
99-
// Load atomically loads and returns the value stored in x.
100-
func (x *Int64) Load() int64 { return atomic.LoadInt64(&x.v) }
101-
102-
// Store atomically stores val into x.
103-
func (x *Int64) Store(val int64) { atomic.StoreInt64(&x.v, val) }
5+
import "sync/atomic"
1046

105-
// Swap atomically stores new into x and returns the previous value.
106-
func (x *Int64) Swap(new int64) (old int64) { return atomic.SwapInt64(&x.v, new) }
107-
108-
// CompareAndSwap executes the compare-and-swap operation for x.
109-
func (x *Int64) CompareAndSwap(old, new int64) (swapped bool) {
110-
return atomic.CompareAndSwapInt64(&x.v, old, new)
111-
}
112-
113-
// Add atomically adds delta to x and returns the new value.
114-
func (x *Int64) Add(delta int64) (new int64) { return atomic.AddInt64(&x.v, delta) }
115-
116-
// An Uint32 is an atomic uint32. The zero value is zero.
117-
type Uint32 struct {
118-
_ noCopy
119-
v uint32
120-
}
121-
122-
// Load atomically loads and returns the value stored in x.
123-
func (x *Uint32) Load() uint32 { return atomic.LoadUint32(&x.v) }
124-
125-
// Store atomically stores val into x.
126-
func (x *Uint32) Store(val uint32) { atomic.StoreUint32(&x.v, val) }
127-
128-
// Swap atomically stores new into x and returns the previous value.
129-
func (x *Uint32) Swap(new uint32) (old uint32) { return atomic.SwapUint32(&x.v, new) }
130-
131-
// CompareAndSwap executes the compare-and-swap operation for x.
132-
func (x *Uint32) CompareAndSwap(old, new uint32) (swapped bool) {
133-
return atomic.CompareAndSwapUint32(&x.v, old, new)
134-
}
135-
136-
// Add atomically adds delta to x and returns the new value.
137-
func (x *Uint32) Add(delta uint32) (new uint32) { return atomic.AddUint32(&x.v, delta) }
138-
139-
// An Uint64 is an atomic uint64. The zero value is zero.
140-
type Uint64 struct {
141-
_ noCopy
142-
v uint64
143-
}
144-
145-
// Load atomically loads and returns the value stored in x.
146-
func (x *Uint64) Load() uint64 { return atomic.LoadUint64(&x.v) }
147-
148-
// Store atomically stores val into x.
149-
func (x *Uint64) Store(val uint64) { atomic.StoreUint64(&x.v, val) }
150-
151-
// Swap atomically stores new into x and returns the previous value.
152-
func (x *Uint64) Swap(new uint64) (old uint64) { return atomic.SwapUint64(&x.v, new) }
153-
154-
// CompareAndSwap executes the compare-and-swap operation for x.
155-
func (x *Uint64) CompareAndSwap(old, new uint64) (swapped bool) {
156-
return atomic.CompareAndSwapUint64(&x.v, old, new)
157-
}
158-
159-
// Add atomically adds delta to x and returns the new value.
160-
func (x *Uint64) Add(delta uint64) (new uint64) { return atomic.AddUint64(&x.v, delta) }
161-
162-
// An Uintptr is an atomic uintptr. The zero value is zero.
163-
type Uintptr struct {
164-
_ noCopy
165-
v uintptr
166-
}
167-
168-
// Load atomically loads and returns the value stored in x.
169-
func (x *Uintptr) Load() uintptr { return atomic.LoadUintptr(&x.v) }
170-
171-
// Store atomically stores val into x.
172-
func (x *Uintptr) Store(val uintptr) { atomic.StoreUintptr(&x.v, val) }
173-
174-
// Swap atomically stores new into x and returns the previous value.
175-
func (x *Uintptr) Swap(new uintptr) (old uintptr) { return atomic.SwapUintptr(&x.v, new) }
176-
177-
// CompareAndSwap executes the compare-and-swap operation for x.
178-
func (x *Uintptr) CompareAndSwap(old, new uintptr) (swapped bool) {
179-
return atomic.CompareAndSwapUintptr(&x.v, old, new)
180-
}
181-
182-
// Add atomically adds delta to x and returns the new value.
183-
func (x *Uintptr) Add(delta uintptr) (new uintptr) { return atomic.AddUintptr(&x.v, delta) }
184-
185-
// noCopy may be added to structs which must not be copied
186-
// after the first use.
187-
//
188-
// See https://golang.org/issues/8005#issuecomment-190753527
189-
// for details.
190-
//
191-
// Note that it must not be embedded, due to the Lock and Unlock methods.
192-
type noCopy struct{}
193-
194-
// Lock is a no-op used by -copylocks checker from `go vet`.
195-
func (*noCopy) Lock() {}
196-
func (*noCopy) Unlock() {}
7+
type (
8+
Bool = atomic.Bool
9+
Int32 = atomic.Int32
10+
Int64 = atomic.Int64
11+
Uint32 = atomic.Uint32
12+
Uint64 = atomic.Uint64
13+
Uintptr = atomic.Uintptr
14+
Value = atomic.Value
15+
)
19716

198-
type Value = atomic.Value
17+
type Pointer[T any] atomic.Pointer[T]

common/control/protect_other.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//go:build (go1.19 && !unix) || (!go1.19 && !(linux || darwin))
1+
//go:build !unix
22

33
package control
44

common/control/protect_unix.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//go:build (go1.19 && unix) || (!go1.19 && (linux || darwin))
1+
//go:build unix
22

33
package control
44

0 commit comments

Comments
 (0)