github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/amutex/amutex.go (about) 1 // Copyright 2018 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package amutex provides the implementation of an abortable mutex. It allows 16 // the Lock() function to be canceled while it waits to acquire the mutex. 17 package amutex 18 19 import ( 20 "sync/atomic" 21 22 "github.com/SagerNet/gvisor/pkg/context" 23 "github.com/SagerNet/gvisor/pkg/syserror" 24 ) 25 26 // Sleeper must be implemented by users of the abortable mutex to allow for 27 // cancellation of waits. 28 type Sleeper = context.ChannelSleeper 29 30 // NoopSleeper is a stateless no-op implementation of Sleeper for anonymous 31 // embedding in other types that do not support cancelation. 32 type NoopSleeper = context.Context 33 34 // Block blocks until either receiving from ch succeeds (in which case it 35 // returns nil) or sleeper is interrupted (in which case it returns 36 // syserror.ErrInterrupted). 37 func Block(sleeper Sleeper, ch <-chan struct{}) error { 38 cancel := sleeper.SleepStart() 39 select { 40 case <-ch: 41 sleeper.SleepFinish(true) 42 return nil 43 case <-cancel: 44 sleeper.SleepFinish(false) 45 return syserror.ErrInterrupted 46 } 47 } 48 49 // AbortableMutex is an abortable mutex. It allows Lock() to be aborted while it 50 // waits to acquire the mutex. 51 type AbortableMutex struct { 52 v int32 53 ch chan struct{} 54 } 55 56 // Init initializes the abortable mutex. 57 func (m *AbortableMutex) Init() { 58 m.v = 1 59 m.ch = make(chan struct{}, 1) 60 } 61 62 // Lock attempts to acquire the mutex, returning true on success. If something 63 // is written to the "c" while Lock waits, the wait is aborted and false is 64 // returned instead. 65 func (m *AbortableMutex) Lock(s Sleeper) bool { 66 // Uncontended case. 67 if atomic.AddInt32(&m.v, -1) == 0 { 68 return true 69 } 70 71 var c <-chan struct{} 72 if s != nil { 73 c = s.SleepStart() 74 } 75 76 for { 77 // Try to acquire the mutex again, at the same time making sure 78 // that m.v is negative, which indicates to the owner of the 79 // lock that it is contended, which ill force it to try to wake 80 // someone up when it releases the mutex. 81 if v := atomic.LoadInt32(&m.v); v >= 0 && atomic.SwapInt32(&m.v, -1) == 1 { 82 if s != nil { 83 s.SleepFinish(true) 84 } 85 return true 86 } 87 88 // Wait for the owner to wake us up before trying again, or for 89 // the wait to be aborted by the provided channel. 90 select { 91 case <-m.ch: 92 case <-c: 93 // s must be non-nil, otherwise c would be nil and we'd 94 // never reach this path. 95 s.SleepFinish(false) 96 return false 97 } 98 } 99 } 100 101 // Unlock releases the mutex. 102 func (m *AbortableMutex) Unlock() { 103 if atomic.SwapInt32(&m.v, 1) == 0 { 104 // There were no pending waiters. 105 return 106 } 107 108 // Wake some waiter up. 109 select { 110 case m.ch <- struct{}{}: 111 default: 112 } 113 }