9
\$\begingroup\$

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?

Jamal
35.2k13 gold badges134 silver badges238 bronze badges
asked Aug 18, 2014 at 0:08
\$\endgroup\$
0

1 Answer 1

7
\$\begingroup\$

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.

answered Sep 27, 2014 at 13:44
\$\endgroup\$
2
  • 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\$ Commented Sep 27, 2014 at 14:14
  • \$\begingroup\$ 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. \$\endgroup\$ Commented Feb 26, 2019 at 17:37

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.