I'm working on a project where a spinlock is more appropriate than a mutex, and after few tries I came up with:
type SpinLock uint32
func (sl *SpinLock) Lock() {
for !atomic.CompareAndSwapUint32((*uint32)(sl), 0, 1) {
runtime.Gosched() //without this it locks up on GOMAXPROCS > 1
}
}
func (sl *SpinLock) Unlock() {
atomic.StoreUint32((*uint32)(sl), 0)
}
It works fine, and it's even a little bit faster than sync.Mutex
, and 2x the speed of sync.RWMutex
.
➜ go test -bench=. -benchmem -v -cpu 4
BenchmarkSpinL-4 2000 1078798 ns/op 33923 B/op 2006 allocs/op
BenchmarkMutex-4 2000 1195814 ns/op 32781 B/op 2002 allocs/o
BenchmarkRWMutex-4 1000 2352117 ns/op 78253 B/op 2147 allocs/op
The test uses multi readers / writers to a map[int]*struct{int, int}
. Running it with -race
doesn't detect any data races, either.
But I have that nagging feeling that I forgot something, so I'm wondering if my implementation is correct?
1 Answer 1
The only weak point is that the implementation is not copy safe nor there exist mechanism for ensuring copy protection. I would hide its underlying type and return as sync.Locker
, so it can't be mis-used:
type spinLock uint32
func (sl *spinLock) Lock() {
for !atomic.CompareAndSwapUint32((*uint32)(sl), 0, 1) {
runtime.Gosched() //without this it locks up on GOMAXPROCS > 1
}
}
func (sl *spinLock) Unlock() {
atomic.StoreUint32((*uint32)(sl), 0)
}
func SpinLock() sync.Locker {
return &spinLock{}
}
An alternative I've seen in sync.Cond
is to embed an auxiliary type for copy-protection, although it would complicate the implementation unnecessary.
-
1\$\begingroup\$ Hi, and welcome to Code Review, and congratulations on passing through the 'Late Answer' review queue. Nice answer to an older question. \$\endgroup\$rolfl– rolfl2014年09月27日 14:14:46 +00:00Commented Sep 27, 2014 at 14:14
-
\$\begingroup\$ you use the
SpinLock
function instantiate and return thespinLock
(async.Locker
) using composite literal notation (return &spinLock{}
), but this inappropriate for the underlying type and so [the compiler throws an error]( play.golang.org/p/qtxa5zYD1b_N). If thespinLock
is instead instantiated via another method, such asreturn new(spinLock)
, then the compiler is happy. Here's a link to an example that compiles. \$\endgroup\$austin_y– austin_y2019年02月26日 17:37:48 +00:00Commented Feb 26, 2019 at 17:37