├── README.md ├── locker.go ├── LICENSE └── locker_test.go /README.md: -------------------------------------------------------------------------------- 1 | # spinlock 2 | 3 | [![GoDoc](https://img.shields.io/badge/api-reference-blue.svg?style=flat-square)](https://godoc.org/github.com/tidwall/spinlock) 4 | 5 | 6 | A [spinlock](https://en.wikipedia.org/wiki/Spinlock) implementation for Go. 7 | 8 | It shares the same interface as [sync.Mutex](https://golang.org/pkg/sync/#Mutex), and is intended to be used to synchronize exceptionally short lived operations. 9 | 10 | ## Install 11 | 12 | ``` 13 | go get -u github.com/tidwall/spinlock 14 | ``` 15 | 16 | ## Contact 17 | 18 | Josh Baker [@tidwall](http://twitter.com/tidwall) 19 | 20 | ## License 21 | 22 | spinlock source code is available under the MIT [License](/LICENSE). 23 | -------------------------------------------------------------------------------- /locker.go: -------------------------------------------------------------------------------- 1 | package spinlock 2 | 3 | import ( 4 | "runtime" 5 | "sync" 6 | "sync/atomic" 7 | ) 8 | 9 | // Locker is a spinlock implementation. 10 | // 11 | // A Locker must not be copied after first use. 12 | type Locker struct { 13 | _ sync.Mutex // for copy protection compiler warning 14 | lock uintptr 15 | } 16 | 17 | // Lock locks l. 18 | // If the lock is already in use, the calling goroutine 19 | // blocks until the locker is available. 20 | func (l *Locker) Lock() { 21 | loop: 22 | if !atomic.CompareAndSwapUintptr(&l.lock, 0, 1) { 23 | runtime.Gosched() 24 | goto loop 25 | } 26 | } 27 | 28 | // Unlock unlocks l. 29 | func (l *Locker) Unlock() { 30 | atomic.StoreUintptr(&l.lock, 0) 31 | } 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Josh Baker 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /locker_test.go: -------------------------------------------------------------------------------- 1 | package spinlock 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "testing" 7 | "time" 8 | ) 9 | 10 | func testLock(threads, n int, l sync.Locker) time.Duration { 11 | var wg sync.WaitGroup 12 | wg.Add(threads) 13 | 14 | var count1 int 15 | var count2 int 16 | 17 | start := time.Now() 18 | for i := 0; i < threads; i++ { 19 | go func() { 20 | for i := 0; i < n; i++ { 21 | l.Lock() 22 | count1++ 23 | count2 += 2 24 | l.Unlock() 25 | } 26 | wg.Done() 27 | }() 28 | } 29 | wg.Wait() 30 | dur := time.Since(start) 31 | if count1 != threads*n { 32 | panic("mismatch") 33 | } 34 | if count2 != threads*n*2 { 35 | panic("mismatch") 36 | } 37 | return dur 38 | } 39 | 40 | func TestSpinLock(t *testing.T) { 41 | fmt.Printf("[1] spinlock %4.0fms\n", testLock(1, 1000000, &Locker{}).Seconds()*1000) 42 | fmt.Printf("[1] mutex %4.0fms\n", testLock(1, 1000000, &sync.Mutex{}).Seconds()*1000) 43 | fmt.Printf("[4] spinlock %4.0fms\n", testLock(4, 1000000, &Locker{}).Seconds()*1000) 44 | fmt.Printf("[4] mutex %4.0fms\n", testLock(4, 1000000, &sync.Mutex{}).Seconds()*1000) 45 | fmt.Printf("[8] spinlock %4.0fms\n", testLock(8, 1000000, &Locker{}).Seconds()*1000) 46 | fmt.Printf("[8] mutex %4.0fms\n", testLock(8, 1000000, &sync.Mutex{}).Seconds()*1000) 47 | } 48 | --------------------------------------------------------------------------------