github.com/argoproj/argo-cd/v2@v2.10.9/reposerver/repository/lock.go (about) 1 package repository 2 3 import ( 4 "fmt" 5 "io" 6 "sync" 7 8 ioutil "github.com/argoproj/argo-cd/v2/util/io" 9 ) 10 11 func NewRepositoryLock() *repositoryLock { 12 return &repositoryLock{stateByKey: map[string]*repositoryState{}} 13 } 14 15 type repositoryLock struct { 16 lock sync.Mutex 17 stateByKey map[string]*repositoryState 18 } 19 20 // Lock acquires lock unless lock is already acquired with the same commit and allowConcurrent is set to true 21 func (r *repositoryLock) Lock(path string, revision string, allowConcurrent bool, init func() (io.Closer, error)) (io.Closer, error) { 22 r.lock.Lock() 23 state, ok := r.stateByKey[path] 24 if !ok { 25 state = &repositoryState{cond: &sync.Cond{L: &sync.Mutex{}}} 26 r.stateByKey[path] = state 27 } 28 r.lock.Unlock() 29 30 closer := ioutil.NewCloser(func() error { 31 state.cond.L.Lock() 32 notify := false 33 state.processCount-- 34 var err error 35 if state.processCount == 0 { 36 notify = true 37 state.revision = "" 38 err = state.initCloser.Close() 39 } 40 41 state.cond.L.Unlock() 42 if notify { 43 state.cond.Broadcast() 44 } 45 if err != nil { 46 return fmt.Errorf("init closer failed: %w", err) 47 } 48 return nil 49 }) 50 51 for { 52 state.cond.L.Lock() 53 if state.revision == "" { 54 // no in progress operation for that repo. Go ahead. 55 initCloser, err := init() 56 if err != nil { 57 state.cond.L.Unlock() 58 return nil, fmt.Errorf("failed to initialize repository resources: %w", err) 59 } 60 state.initCloser = initCloser 61 state.revision = revision 62 state.processCount = 1 63 state.allowConcurrent = allowConcurrent 64 state.cond.L.Unlock() 65 return closer, nil 66 } else if state.revision == revision && state.allowConcurrent && allowConcurrent { 67 // same revision already processing and concurrent processing allowed. Increment process count and go ahead. 68 state.processCount++ 69 state.cond.L.Unlock() 70 return closer, nil 71 } else { 72 state.cond.Wait() 73 // wait when all in-flight processes of this revision complete and try again 74 state.cond.L.Unlock() 75 } 76 } 77 } 78 79 type repositoryState struct { 80 cond *sync.Cond 81 revision string 82 initCloser io.Closer 83 processCount int 84 allowConcurrent bool 85 }