github.com/gitbundle/modules@v0.0.0-20231025071548-85b91c5c3b01/sync/exclusive_pool.go (about) 1 // Copyright 2023 The GitBundle Inc. All rights reserved. 2 // Copyright 2017 The Gitea Authors. All rights reserved. 3 // Use of this source code is governed by a MIT-style 4 // license that can be found in the LICENSE file. 5 6 // Copyright 2016 The Gogs Authors. All rights reserved. 7 // Use of this source code is governed by a MIT-style 8 // license that can be found in the LICENSE file. 9 10 package sync 11 12 import ( 13 "sync" 14 ) 15 16 // ExclusivePool is a pool of non-identical instances 17 // that only one instance with same identity is in the pool at a time. 18 // In other words, only instances with different identities can be in 19 // the pool the same time. If another instance with same identity tries 20 // to get into the pool, it hangs until previous instance left the pool. 21 // 22 // This pool is particularly useful for performing tasks on same resource 23 // on the file system in different goroutines. 24 type ExclusivePool struct { 25 lock sync.Mutex 26 27 // pool maintains locks for each instance in the pool. 28 pool map[string]*sync.Mutex 29 30 // count maintains the number of times an instance with same identity checks in 31 // to the pool, and should be reduced to 0 (removed from map) by checking out 32 // with same number of times. 33 // The purpose of count is to delete lock when count down to 0 and recycle memory 34 // from map object. 35 count map[string]int 36 } 37 38 // NewExclusivePool initializes and returns a new ExclusivePool object. 39 func NewExclusivePool() *ExclusivePool { 40 return &ExclusivePool{ 41 pool: make(map[string]*sync.Mutex), 42 count: make(map[string]int), 43 } 44 } 45 46 // CheckIn checks in an instance to the pool and hangs while instance 47 // with same identity is using the lock. 48 func (p *ExclusivePool) CheckIn(identity string) { 49 p.lock.Lock() 50 51 lock, has := p.pool[identity] 52 if !has { 53 lock = &sync.Mutex{} 54 p.pool[identity] = lock 55 } 56 p.count[identity]++ 57 58 p.lock.Unlock() 59 lock.Lock() 60 } 61 62 // CheckOut checks out an instance from the pool and releases the lock 63 // to let other instances with same identity to grab the lock. 64 func (p *ExclusivePool) CheckOut(identity string) { 65 p.lock.Lock() 66 defer p.lock.Unlock() 67 68 p.pool[identity].Unlock() 69 if p.count[identity] == 1 { 70 delete(p.pool, identity) 71 delete(p.count, identity) 72 } else { 73 p.count[identity]-- 74 } 75 }