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?

\$\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.

\$\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\$
    – rolfl
    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\$
    – austin_y
    Commented Feb 26, 2019 at 17:37

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.