github.com/tickoalcantara12/micro/v3@v3.0.0-20221007104245-9d75b9bcbab9/util/sync/memory/memory.go (about) 1 // Licensed under the Apache License, Version 2.0 (the "License"); 2 // you may not use this file except in compliance with the License. 3 // You may obtain a copy of the License at 4 // 5 // https://www.apache.org/licenses/LICENSE-2.0 6 // 7 // Unless required by applicable law or agreed to in writing, software 8 // distributed under the License is distributed on an "AS IS" BASIS, 9 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 // See the License for the specific language governing permissions and 11 // limitations under the License. 12 // 13 // Original source: github.com/micro/go-micro/v3/sync/memory/memory.go 14 15 // Package memory provides a sync.Mutex implementation of the lock for local use 16 package memory 17 18 import ( 19 gosync "sync" 20 "time" 21 22 "github.com/tickoalcantara12/micro/v3/util/sync" 23 ) 24 25 type memorySync struct { 26 options sync.Options 27 28 mtx gosync.RWMutex 29 locks map[string]*memoryLock 30 } 31 32 type memoryLock struct { 33 id string 34 time time.Time 35 ttl time.Duration 36 release chan bool 37 } 38 39 type memoryLeader struct { 40 opts sync.LeaderOptions 41 id string 42 resign func(id string) error 43 status chan bool 44 } 45 46 func (m *memoryLeader) Resign() error { 47 return m.resign(m.id) 48 } 49 50 func (m *memoryLeader) Status() chan bool { 51 return m.status 52 } 53 54 func (m *memorySync) Leader(id string, opts ...sync.LeaderOption) (sync.Leader, error) { 55 var once gosync.Once 56 var options sync.LeaderOptions 57 for _, o := range opts { 58 o(&options) 59 } 60 61 // acquire a lock for the id 62 if err := m.Lock(id); err != nil { 63 return nil, err 64 } 65 66 // return the leader 67 return &memoryLeader{ 68 opts: options, 69 id: id, 70 resign: func(id string) error { 71 once.Do(func() { 72 m.Unlock(id) 73 }) 74 return nil 75 }, 76 // TODO: signal when Unlock is called 77 status: make(chan bool, 1), 78 }, nil 79 } 80 81 func (m *memorySync) Init(opts ...sync.Option) error { 82 for _, o := range opts { 83 o(&m.options) 84 } 85 return nil 86 } 87 88 func (m *memorySync) Options() sync.Options { 89 return m.options 90 } 91 92 func (m *memorySync) Lock(id string, opts ...sync.LockOption) error { 93 // lock our access 94 m.mtx.Lock() 95 96 var options sync.LockOptions 97 for _, o := range opts { 98 o(&options) 99 } 100 101 lk, ok := m.locks[id] 102 if !ok { 103 m.locks[id] = &memoryLock{ 104 id: id, 105 time: time.Now(), 106 ttl: options.TTL, 107 release: make(chan bool), 108 } 109 // unlock 110 m.mtx.Unlock() 111 return nil 112 } 113 114 m.mtx.Unlock() 115 116 // set wait time 117 var wait <-chan time.Time 118 var ttl <-chan time.Time 119 120 // decide if we should wait 121 if options.Wait > time.Duration(0) { 122 wait = time.After(options.Wait) 123 } 124 125 // check the ttl of the lock 126 if lk.ttl > time.Duration(0) { 127 // time lived for the lock 128 live := time.Since(lk.time) 129 130 // set a timer for the leftover ttl 131 if live > lk.ttl { 132 // release the lock if it expired 133 _ = m.Unlock(id) 134 } else { 135 ttl = time.After(live) 136 } 137 } 138 139 lockLoop: 140 for { 141 // wait for the lock to be released 142 select { 143 case <-lk.release: 144 m.mtx.Lock() 145 146 // someone locked before us 147 lk, ok = m.locks[id] 148 if ok { 149 m.mtx.Unlock() 150 continue 151 } 152 153 // got chance to lock 154 m.locks[id] = &memoryLock{ 155 id: id, 156 time: time.Now(), 157 ttl: options.TTL, 158 release: make(chan bool), 159 } 160 161 m.mtx.Unlock() 162 163 break lockLoop 164 case <-ttl: 165 // ttl exceeded 166 _ = m.Unlock(id) 167 // TODO: check the ttl again above 168 ttl = nil 169 // try acquire 170 continue 171 case <-wait: 172 return sync.ErrLockTimeout 173 } 174 } 175 176 return nil 177 } 178 179 func (m *memorySync) Unlock(id string) error { 180 m.mtx.Lock() 181 defer m.mtx.Unlock() 182 183 lk, ok := m.locks[id] 184 // no lock exists 185 if !ok { 186 return nil 187 } 188 189 // delete the lock 190 delete(m.locks, id) 191 192 select { 193 case <-lk.release: 194 return nil 195 default: 196 close(lk.release) 197 } 198 199 return nil 200 } 201 202 func (m *memorySync) String() string { 203 return "memory" 204 } 205 206 func NewSync(opts ...sync.Option) sync.Sync { 207 var options sync.Options 208 for _, o := range opts { 209 o(&options) 210 } 211 212 return &memorySync{ 213 options: options, 214 locks: make(map[string]*memoryLock), 215 } 216 }