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
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
    Sep 27 '14 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
    Feb 26 '19 at 17:37

This site is temporarily in read only mode and not accepting new answers.

Not the answer you're looking for? Browse other questions tagged .