-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathlocks_amd64.go
176 lines (146 loc) · 4.61 KB
/
locks_amd64.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
// +build 386 amd64
package safetyfast
import (
"runtime"
"sync/atomic"
)
// Pause executes the PAUSE x86 instruction.
func Pause()
// Mfence executes the MFENCE x86 instruction.
func Mfence()
// SetAndFence32 writes a 1 to val and asserts an MFENCE.
func SetAndFence32(val *int32)
// Lock1XCHG8 will atomically write 1 to val while returning the old value.
// The size of val must be 8 bits.
func Lock1XCHG8(val *int8) (old int8)
// Lock1XCHG32 will atomically write 1 to val while returning the old value.
// The size of val must be 32 bits.
func Lock1XCHG32(val *int32) (old int32)
// Lock1XCHG64 will atomically write 1 to val while returning the old value.
// The size of val must be 64 bits.
func Lock1XCHG64(val *int64) (old int64)
// SpinLock implements a basic spin lock that waits forever until the lock
// can be acquired.
// It assumes the lock has been acquired when it successfully writes a 1 to val,
// while the original value was 0.
// This implementation spins on a read only and then uses the XCHG instruction
// in order to claim the lock. The loop makes use of the PAUSE hint instruction.
func SpinLock(val *int32)
// SpinCountLock implements a basic spin lock that tries to acquire the lock
// only attempts times.
// It assumes the lock has been acquired when it successfully writes a 1 to val,
// while the original value was 0.
// This implementation spins on a read only and then uses the XCHG instruction
// in order to claim the lock. The loop makes use of the PAUSE hint instruction.
func SpinCountLock(val, attempts *int32)
// HLETryLock attempts only once to acquire the lock by writing a 1 to val
// using HLE primitives. This function returns a 0 if the lock was acquired.
func HLETryLock(val *int32) int32
// HLESpinLock repeatedly tries to set val to 1 using Intel HLE and XCHG.
// It is implemented as a spin lock that makes use of the PAUSE and
// LOCK XCHG instructions.
// Please note that this function will never return unless the lock is acquired.
// This means a deadlock will occur if the holder of the lock is descheduled
// by the goruntime.
// Please use HLESpinCountLock to limit the spins and manually invoke
// runtime.Gosched periodically, insted.
func HLESpinLock(val *int32)
// HLESpinCountLock tries to set val to 1 at most attempts times using Intel HLE.
// It is implemented as a spin lock that decrements attempts for each attempt.
// The spin operation makes use of the PAUSE and XACQUIRE LOCK XCHG instructions.
// If attempts is 0 when the function returns, the lock was not acquired and
// the spin lock gave up.
// Please note that attempts must be greater 0 when called.
func HLESpinCountLock(val, attempts *int32)
// HLEUnlock writes a 0 to val to indicate the lock has been released
// using HLE primitives
func HLEUnlock(val *int32)
// LockAttempts sets how many times the spin loop is willing to try to
// fetching the lock.
const LockAttempts = int32(200)
// SpinLockAtomics implements a very basic spin forever style lock that
// uses the built in atomics.SwapInt32 function in order to claim the lock.
func SpinLockAtomics(val *int32) {
for {
// Spin on simple read
for *val != 0 {
// ASM hint for spin loop
Pause()
}
if atomic.SwapInt32(val, 1) == 0 {
break
}
}
}
type SpinMutexBasic struct {
val int32
}
func (m *SpinMutexBasic) Lock() {
for !atomic.CompareAndSwapInt32(&m.val, 0, 1) {
Pause()
}
}
func (m *SpinMutexBasic) Unlock() {
m.val = 0
}
type SpinMutex int32
// Fastest
func (m *SpinMutex) Lock() {
for {
var attempts int32 = LockAttempts
SpinCountLock((*int32)(m), &attempts)
if attempts > 0 {
// We acquired the lock before attempts was exceeded
return
}
// Invoke scheduler to allow other to run
runtime.Gosched()
}
}
func (m *SpinMutex) Unlock() {
*m = 0
}
func (m *SpinMutex) IsLocked() bool {
return *m == 1
}
type SpinMutexASM int32
func (m *SpinMutexASM) Lock() {
for {
// Spin on simple read
for *m != 0 {
// ASM hint for spin loop
Pause()
}
if Lock1XCHG32((*int32)(m)) == 0 {
break
}
}
// for atomic.SwapInt32(&m.val, 1) != 0 {
// // Spin on simple read
// for m.val != 0 {
// // ASM hint for spin loop
// Pause()
// }
// }
}
func (m *SpinMutexASM) Unlock() {
*m = 0
}
// SpinHLEMutex is sync.Mutex replacement that uses HLE
type SpinHLEMutex int32
func (m *SpinHLEMutex) Lock() {
// HLESpinLock((*int32)(m))
for {
var attempts int32 = LockAttempts
HLESpinCountLock((*int32)(m), &attempts)
if attempts > 0 {
// We acquired the lock before attempts was exceeded
return
}
// Invoke scheduler to allow other to run
runtime.Gosched()
}
}
func (m *SpinHLEMutex) Unlock() {
HLEUnlock((*int32)(m))
}