├── .travis.yml ├── LICENSE ├── README.md ├── example_test.go ├── trylock.go └── trylock_test.go /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | sudo: false 3 | go: 4 | - 1.x 5 | - master 6 | install: 7 | - go get -t ./... 8 | - go get golang.org/x/lint/golint 9 | script: 10 | - go vet ./... 11 | - test -z "$(golint ./... | tee /dev/stderr)" 12 | - test -z "$(gofmt -s -l . | tee /dev/stderr)" 13 | - go test -race -v ./... 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Alexander Morozov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, 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, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | trylock - TryLock implementation for Go 2 | ======================================= 3 | 4 | [![Build Status](https://travis-ci.org/LK4D4/trylock.svg?branch=master)](https://travis-ci.org/LK4D4/trylock) 5 | [![GoDoc](https://godoc.org/github.com/LK4D4/trylock?status.svg)](https://godoc.org/github.com/LK4D4/trylock) 6 | 7 | trylock uses unsafe, which is sorta "unsafe", but should work until `sync.Mutex` 8 | will change its layout (I hope it never will). 9 | 10 | # Usage 11 | 12 | ```go 13 | type LockedStruct struct { 14 | mu trylock.Mutex 15 | } 16 | 17 | storage := &LockedStruct{} 18 | 19 | if storage.mu.TryLock() { 20 | // do something with storage 21 | } else { 22 | // return busy or use some logic for unavailable storage 23 | } 24 | ``` 25 | -------------------------------------------------------------------------------- /example_test.go: -------------------------------------------------------------------------------- 1 | package trylock_test 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/LK4D4/trylock" 7 | ) 8 | 9 | func Example() { 10 | var mu trylock.Mutex 11 | fmt.Println(mu.TryLock()) 12 | fmt.Println(mu.TryLock()) 13 | mu.Unlock() 14 | fmt.Println(mu.TryLock()) 15 | // Output: 16 | // true 17 | // false 18 | // true 19 | } 20 | -------------------------------------------------------------------------------- /trylock.go: -------------------------------------------------------------------------------- 1 | package trylock 2 | 3 | import ( 4 | "sync" 5 | "sync/atomic" 6 | "unsafe" 7 | ) 8 | 9 | const mutexLocked = 1 << iota 10 | 11 | // Mutex is simple sync.Mutex + ability to try to Lock. 12 | type Mutex struct { 13 | in sync.Mutex 14 | } 15 | 16 | // Lock locks m. 17 | // If the lock is already in use, the calling goroutine 18 | // blocks until the mutex is available. 19 | func (m *Mutex) Lock() { 20 | m.in.Lock() 21 | } 22 | 23 | // Unlock unlocks m. 24 | // It is a run-time error if m is not locked on entry to Unlock. 25 | // 26 | // A locked Mutex is not associated with a particular goroutine. 27 | // It is allowed for one goroutine to lock a Mutex and then 28 | // arrange for another goroutine to unlock it. 29 | func (m *Mutex) Unlock() { 30 | m.in.Unlock() 31 | } 32 | 33 | // TryLock tries to lock m. It returns true in case of success, false otherwise. 34 | func (m *Mutex) TryLock() bool { 35 | return atomic.CompareAndSwapInt32((*int32)(unsafe.Pointer(&m.in)), 0, mutexLocked) 36 | } 37 | -------------------------------------------------------------------------------- /trylock_test.go: -------------------------------------------------------------------------------- 1 | package trylock 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func TestMutexLayout(t *testing.T) { 9 | sf := reflect.TypeOf((*Mutex)(nil)).Elem().FieldByIndex([]int{0, 0}) 10 | if sf.Name != "state" { 11 | t.Fatal("sync.Mutex first field should have name state") 12 | } 13 | if sf.Offset != uintptr(0) { 14 | t.Fatal("sync.Mutex state field should have zero offset") 15 | } 16 | if sf.Type != reflect.TypeOf(int32(1)) { 17 | t.Fatal("sync.Mutex state field type should be int32") 18 | } 19 | } 20 | 21 | func TestTryLock(t *testing.T) { 22 | var mu Mutex 23 | if !mu.TryLock() { 24 | t.Fatal("mutex must be unlocked") 25 | } 26 | if mu.TryLock() { 27 | t.Fatal("mutex must be locked") 28 | } 29 | 30 | mu.Unlock() 31 | if !mu.TryLock() { 32 | t.Fatal("mutex must be unlocked") 33 | } 34 | if mu.TryLock() { 35 | t.Fatal("mutex must be locked") 36 | } 37 | 38 | mu.Unlock() 39 | mu.Lock() 40 | if mu.TryLock() { 41 | t.Fatal("mutex must be locked") 42 | } 43 | if mu.TryLock() { 44 | t.Fatal("mutex must be locked") 45 | } 46 | mu.Unlock() 47 | } 48 | 49 | func TestTryLockPointer(t *testing.T) { 50 | mu := &Mutex{} 51 | if !mu.TryLock() { 52 | t.Fatal("mutex must be unlocked") 53 | } 54 | if mu.TryLock() { 55 | t.Fatal("mutex must be locked") 56 | } 57 | 58 | mu.Unlock() 59 | if !mu.TryLock() { 60 | t.Fatal("mutex must be unlocked") 61 | } 62 | if mu.TryLock() { 63 | t.Fatal("mutex must be locked") 64 | } 65 | 66 | mu.Unlock() 67 | mu.Lock() 68 | if mu.TryLock() { 69 | t.Fatal("mutex must be locked") 70 | } 71 | if mu.TryLock() { 72 | t.Fatal("mutex must be locked") 73 | } 74 | mu.Unlock() 75 | } 76 | 77 | func TestRace(t *testing.T) { 78 | var mu Mutex 79 | var x int 80 | for i := 0; i < 1024; i++ { 81 | if i%2 == 0 { 82 | go func() { 83 | if mu.TryLock() { 84 | x++ 85 | mu.Unlock() 86 | } 87 | }() 88 | continue 89 | } 90 | go func() { 91 | mu.Lock() 92 | x++ 93 | mu.Unlock() 94 | }() 95 | } 96 | } 97 | --------------------------------------------------------------------------------