github.com/argoproj/argo-cd@v1.8.7/reposerver/repository/lock.go (about)

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