summaryrefslogtreecommitdiff
path: root/internal/cpu/stresser.go
blob: 62ae3b70373590305d09e17b3c711569cfc7c969 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
// Package cpu provides CPU stress testing with multiple algorithms and chaos modes.
package cpu

import (
	"context"
	"math/rand"
	"sync"
	"time"
)

// stresser.go contains the main Stresser type, constructor, and lifecycle methods.

// Stresser uses pointer receivers because it contains a sync.Mutex
// and shared mutable state that must not be copied.
type Stresser struct {
	config      Config
	cancel      context.CancelFunc
	wg          sync.WaitGroup
	mu          sync.Mutex
	current     Mode
	currentCPUs int
	rand        *rand.Rand
}

func New(cfg Config) *Stresser {
	if cfg.Workers <= 0 {
		cfg.Workers = 1
	}
	if cfg.IntensityMin <= 0 {
		cfg.IntensityMin = 100
	}
	if cfg.IntensityMax <= 0 {
		cfg.IntensityMax = 1000
	}
	if cfg.Mode == "" {
		cfg.Mode = ModePrime
	}
	if !cfg.ChaosEnabled && cfg.MinCPUs <= 0 {
		cfg.MinCPUs = cfg.Workers
		cfg.MaxCPUs = cfg.Workers
	}
	cfg.normalize()
	return &Stresser{
		config: cfg,
		rand:   rand.New(rand.NewSource(time.Now().UnixNano())),
	}
}

func (s *Stresser) Start() {
	ctx, cancel := context.WithCancel(context.Background())
	s.cancel = cancel

	s.mu.Lock()
	s.currentCPUs = s.determineCPUCount()
	s.mu.Unlock()

	s.wg.Add(s.currentCPUs)
	for i := 0; i < s.currentCPUs; i++ {
		go s.worker(ctx, i)
	}

	if s.config.ChaosEnabled {
		go s.chaosController(ctx)
	}
}

func (s *Stresser) Stop() {
	if s.cancel != nil {
		s.cancel()
	}
	s.wg.Wait()
}