github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/lib/cpuset/cpuset.go (about) 1 package cpuset 2 3 import ( 4 "fmt" 5 "math" 6 "reflect" 7 "sort" 8 "strconv" 9 "strings" 10 ) 11 12 // CPUSet is a set like object that provides methods helpful when working with cpus with systems 13 // such as the Linux cpuset cgroup subsystem. A CPUSet is immutable and can be safely accessed concurrently. 14 type CPUSet struct { 15 cpus map[uint16]struct{} 16 } 17 18 // New initializes a new CPUSet with 0 or more containing cpus 19 func New(cpus ...uint16) CPUSet { 20 cpuset := CPUSet{ 21 cpus: make(map[uint16]struct{}), 22 } 23 24 for _, v := range cpus { 25 cpuset.cpus[v] = struct{}{} 26 } 27 28 return cpuset 29 } 30 31 // Copy returns a deep copy of CPUSet c. 32 func (c CPUSet) Copy() CPUSet { 33 cpus := make(map[uint16]struct{}, len(c.cpus)) 34 for k := range c.cpus { 35 cpus[k] = struct{}{} 36 } 37 return CPUSet{ 38 cpus: cpus, 39 } 40 } 41 42 // String returns the cpuset as a comma delimited set of core values and ranged 43 func (c CPUSet) String() string { 44 if c.Size() == 0 { 45 return "" 46 } 47 cores := c.ToSlice() 48 cpusetStrs := []string{} 49 cur := [2]uint16{cores[0], cores[0]} 50 for i := 1; i < len(cores); i++ { 51 if cores[i] == cur[1]+1 { 52 cur[1] = cores[i] 53 continue 54 } 55 56 if cur[0] == cur[1] { 57 cpusetStrs = append(cpusetStrs, fmt.Sprintf("%d", cur[0])) 58 } else { 59 cpusetStrs = append(cpusetStrs, fmt.Sprintf("%d-%d", cur[0], cur[1])) 60 } 61 62 // new range 63 cur = [2]uint16{cores[i], cores[i]} 64 } 65 if cur[0] == cur[1] { 66 cpusetStrs = append(cpusetStrs, fmt.Sprintf("%d", cur[0])) 67 } else { 68 cpusetStrs = append(cpusetStrs, fmt.Sprintf("%d-%d", cur[0], cur[1])) 69 } 70 71 return strings.Join(cpusetStrs, ",") 72 } 73 74 // Size returns to the number of cpus contained in the CPUSet 75 func (c CPUSet) Size() int { 76 return len(c.cpus) 77 } 78 79 // ToSlice returns a sorted slice of uint16 CPU IDs contained in the CPUSet. 80 func (c CPUSet) ToSlice() []uint16 { 81 cpus := []uint16{} 82 for k := range c.cpus { 83 cpus = append(cpus, k) 84 } 85 sort.Slice(cpus, func(i, j int) bool { return cpus[i] < cpus[j] }) 86 return cpus 87 } 88 89 // Union returns a new set that is the union of this CPUSet and the supplied other. 90 // Ex. [0,1,2,3].Union([2,3,4,5]) = [0,1,2,3,4,5] 91 func (c CPUSet) Union(other CPUSet) CPUSet { 92 s := New() 93 for k := range c.cpus { 94 s.cpus[k] = struct{}{} 95 } 96 for k := range other.cpus { 97 s.cpus[k] = struct{}{} 98 } 99 return s 100 } 101 102 // Difference returns a new set that is the difference of this CPUSet and the supplied other. 103 // [0,1,2,3].Difference([2,3,4]) = [0,1] 104 func (c CPUSet) Difference(other CPUSet) CPUSet { 105 s := New() 106 for k := range c.cpus { 107 s.cpus[k] = struct{}{} 108 } 109 for k := range other.cpus { 110 delete(s.cpus, k) 111 } 112 return s 113 114 } 115 116 // IsSubsetOf returns true if all cpus of the this CPUSet are present in the other CPUSet. 117 func (c CPUSet) IsSubsetOf(other CPUSet) bool { 118 for cpu := range c.cpus { 119 if _, ok := other.cpus[cpu]; !ok { 120 return false 121 } 122 } 123 return true 124 } 125 126 func (c CPUSet) IsSupersetOf(other CPUSet) bool { 127 for cpu := range other.cpus { 128 if _, ok := c.cpus[cpu]; !ok { 129 return false 130 } 131 } 132 return true 133 } 134 135 // ContainsAny returns true if any cpus in other CPUSet are present 136 func (c CPUSet) ContainsAny(other CPUSet) bool { 137 for cpu := range other.cpus { 138 if _, ok := c.cpus[cpu]; ok { 139 return true 140 } 141 } 142 return false 143 } 144 145 // Equal tests the equality of the elements in the CPUSet 146 func (c CPUSet) Equal(other CPUSet) bool { 147 return reflect.DeepEqual(c.cpus, other.cpus) 148 } 149 150 // Parse parses the Linux cpuset format into a CPUSet 151 // 152 // Ref: http://man7.org/linux/man-pages/man7/cpuset.7.html#FORMATS 153 func Parse(s string) (CPUSet, error) { 154 cpuset := New() 155 s = strings.TrimSpace(s) 156 if s == "" { 157 return cpuset, nil 158 } 159 sets := strings.Split(s, ",") 160 for _, set := range sets { 161 bounds := strings.Split(set, "-") 162 if len(bounds) == 1 { 163 v, err := strconv.Atoi(bounds[0]) 164 if err != nil { 165 return New(), err 166 } 167 168 if v > math.MaxUint16 { 169 return New(), fmt.Errorf("failed to parse element %s, more than max allowed cores", set) 170 } 171 cpuset.cpus[uint16(v)] = struct{}{} 172 continue 173 } 174 if len(bounds) > 2 { 175 return New(), fmt.Errorf("failed to parse element %s, more than 1 '-' found", set) 176 } 177 178 lower, err := strconv.Atoi(bounds[0]) 179 if err != nil { 180 return New(), err 181 } 182 upper, err := strconv.Atoi(bounds[1]) 183 if err != nil { 184 return New(), err 185 } 186 187 for v := lower; v <= upper; v++ { 188 if v > math.MaxUint16 { 189 return New(), fmt.Errorf("failed to parse element %s, more than max allowed cores", set) 190 } 191 cpuset.cpus[uint16(v)] = struct{}{} 192 } 193 } 194 195 return cpuset, nil 196 }