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  }