go.ligato.io/vpp-agent/v3@v3.5.0/plugins/kvscheduler/internal/utils/dependencies.go (about)

     1  // Copyright (c) 2018 Cisco and/or its affiliates.
     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 utils
    16  
    17  import (
    18  	"sort"
    19  )
    20  
    21  // DependsOn returns true if k1 depends on k2 based on dependencies from <deps>.
    22  func DependsOn(k1, k2 string, deps map[string]KeySet, visited KeySet) bool {
    23  	// check direct dependencies
    24  	k1Deps := deps[k1]
    25  	if depends := k1Deps.Has(k2); depends {
    26  		return true
    27  	}
    28  
    29  	// continue transitively
    30  	visited.Add(k1)
    31  	for _, dep := range k1Deps.Iterate() {
    32  		if wasVisited := visited.Has(dep); wasVisited {
    33  			continue
    34  		}
    35  		if DependsOn(dep, k2, deps, visited) {
    36  			return true
    37  		}
    38  	}
    39  	return false
    40  }
    41  
    42  // TopologicalOrder orders keys topologically by Kahn's algorithm to respect
    43  // the given dependencies.
    44  // deps = map{ key -> <set of keys the given key depends on> }
    45  func TopologicalOrder(keys KeySet, deps map[string]KeySet, depFirst bool, handleCycle bool) (sorted []string) {
    46  	// copy input arguments so that they are not returned to the caller changed
    47  	remains := keys.CopyOnWrite()
    48  	remainsDeps := make(map[string]KeySet)
    49  	for key, keyDeps := range deps {
    50  		if !keys.Has(key) {
    51  			continue
    52  		}
    53  		remainsDeps[key] = keyDeps.CopyOnWrite()
    54  		remainsDeps[key].Intersect(keys)
    55  	}
    56  
    57  	// Kahn's algorithm (except for the cycle handling part):
    58  	for remains.Length() > 0 {
    59  		// find candidate keys - keys that could follow in the order
    60  		var candidates []string
    61  		for _, key := range remains.Iterate() {
    62  			// if depFirst, select keys that do not depend on anything in the remaining set
    63  			candidate := depFirst && remainsDeps[key].Length() == 0
    64  			if !depFirst {
    65  				candidate = true
    66  				// is there any other key depending on this one?
    67  				for _, key2Deps := range remainsDeps {
    68  					if key2Deps.Has(key) {
    69  						candidate = false
    70  						break
    71  					}
    72  				}
    73  			}
    74  			if candidate {
    75  				candidates = append(candidates, key)
    76  			}
    77  		}
    78  
    79  		// handle cycles
    80  		var cycle bool
    81  		if len(candidates) == 0 {
    82  			cycle = true
    83  			if !handleCycle {
    84  				panic("Dependency cycle!")
    85  			}
    86  			// select keys that depend on themselves
    87  			for _, key := range remains.Iterate() {
    88  				if DependsOn(key, key, deps, NewMapBasedKeySet()) {
    89  					candidates = append(candidates, key)
    90  				}
    91  			}
    92  		}
    93  
    94  		// to make the algorithm deterministic (for simplified testing),
    95  		// order the candidates
    96  		sort.Strings(candidates)
    97  
    98  		// in case of cycle output all the keys from the cycle, otherwise just the first candidate
    99  		var selected []string
   100  		if cycle {
   101  			selected = candidates
   102  		} else {
   103  			selected = append(selected, candidates[0])
   104  		}
   105  		sorted = append(sorted, selected...)
   106  
   107  		// remove selected key(s) from the set of remaining keys
   108  		for _, key := range selected {
   109  			remains.Del(key)
   110  			delete(remainsDeps, key)
   111  			// remove dependency edges going to this key
   112  			for _, key2Deps := range remainsDeps {
   113  				key2Deps.Del(key)
   114  			}
   115  		}
   116  	}
   117  	return sorted
   118  }