# Spin-lock implementation

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?

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.

• Hi, and welcome to Code Review, and congratulations on passing through the 'Late Answer' review queue. Nice answer to an older question. – rolfl Sep 27 '14 at 14:14
• you use the SpinLock function instantiate and return the spinLock (a sync.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 the spinLock is instead instantiated via another method, such as return new(spinLock), then the compiler is happy. Here's a link to an example that compiles. – austin_y Feb 26 '19 at 17:37