Add task pool, minor changes
- Add task pool to smooth impact when list in config is pretty long - Update dependencies - Update config
This commit is contained in:
@@ -1,14 +1,25 @@
|
||||
# Fork and run as real unix daemon.
|
||||
daemon: true
|
||||
|
||||
# PID file for daemon.
|
||||
pidfile: /var/run/reniced.pid
|
||||
# Delay between subsequent renices or kills, to lower cpu load peaks.
|
||||
cmd_delay: 33
|
||||
|
||||
# Delay between addition and execution of tasks to task pool, to lower cpu load peaks.
|
||||
cmd_delay: 20
|
||||
|
||||
# Delay among iterations.
|
||||
loop_delay: 5000
|
||||
# Wheather to print debug messages to stdout.
|
||||
|
||||
# Whether to print debug messages to stdout.
|
||||
debug: true
|
||||
|
||||
# Maximum worker in command pool.
|
||||
max_workers: 3
|
||||
|
||||
# Capacity of non-blocking tasks. All tasks that exceeding this capacity will block submission of new tasks until queue
|
||||
# has vacant place for new task.
|
||||
nb_capacity: 30
|
||||
|
||||
kill:
|
||||
- sig: stop
|
||||
name:
|
||||
|
||||
5
go.mod
5
go.mod
@@ -3,8 +3,9 @@ module reniced
|
||||
go 1.21.4
|
||||
|
||||
require (
|
||||
github.com/alitto/pond v1.8.3
|
||||
github.com/sevlyar/go-daemon v0.1.6
|
||||
github.com/shirou/gopsutil/v3 v3.24.2
|
||||
github.com/shirou/gopsutil/v3 v3.24.3
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
@@ -17,5 +18,5 @@ require (
|
||||
github.com/tklauser/go-sysconf v0.3.13 // indirect
|
||||
github.com/tklauser/numcpus v0.7.0 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
golang.org/x/sys v0.18.0 // indirect
|
||||
golang.org/x/sys v0.19.0 // indirect
|
||||
)
|
||||
|
||||
14
go.sum
14
go.sum
@@ -1,3 +1,5 @@
|
||||
github.com/alitto/pond v1.8.3 h1:ydIqygCLVPqIX/USe5EaV/aSRXTRXDEI9JwuDdu+/xs=
|
||||
github.com/alitto/pond v1.8.3/go.mod h1:CmvIIGd5jKLasGI3D87qDkQxjzChdKMmnXMg3fG6M6Q=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@@ -20,8 +22,8 @@ github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/sevlyar/go-daemon v0.1.6 h1:EUh1MDjEM4BI109Jign0EaknA2izkOyi0LV3ro3QQGs=
|
||||
github.com/sevlyar/go-daemon v0.1.6/go.mod h1:6dJpPatBT9eUwM5VCw9Bt6CdX9Tk6UWvhW3MebLDRKE=
|
||||
github.com/shirou/gopsutil/v3 v3.24.2 h1:kcR0erMbLg5/3LcInpw0X/rrPSqq4CDPyI6A6ZRC18Y=
|
||||
github.com/shirou/gopsutil/v3 v3.24.2/go.mod h1:tSg/594BcA+8UdQU2XcW803GWYgdtauFFPgJCJKZlVk=
|
||||
github.com/shirou/gopsutil/v3 v3.24.3 h1:eoUGJSmdfLzJ3mxIhmOAhgKEKgQkeOwKpz1NbhVnuPE=
|
||||
github.com/shirou/gopsutil/v3 v3.24.3/go.mod h1:JpND7O217xa72ewWz9zN2eIIkPWsDN/3pl0H8Qt0uwg=
|
||||
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
|
||||
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
|
||||
github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
|
||||
@@ -29,10 +31,12 @@ github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnj
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
|
||||
github.com/tklauser/go-sysconf v0.3.13 h1:GBUpcahXSpR2xN01jhkNAbTLRk2Yzgggk8IM08lq3r4=
|
||||
github.com/tklauser/go-sysconf v0.3.13/go.mod h1:zwleP4Q4OehZHGn4CYZDipCgg9usW5IJePewFCGVEa0=
|
||||
@@ -46,9 +50,9 @@ golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
|
||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
|
||||
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
66
main.go
66
main.go
@@ -3,21 +3,54 @@ package main
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"os/user"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/alitto/pond"
|
||||
"github.com/sevlyar/go-daemon"
|
||||
proc "github.com/shirou/gopsutil/v3/process"
|
||||
)
|
||||
|
||||
// Пул воркеров.
|
||||
var pool *pond.WorkerPool
|
||||
|
||||
// Канал, в который приходят уведомления для хэндлера сигналов от траппера сигналов.
|
||||
var sigChan = make(chan os.Signal, 1)
|
||||
|
||||
// main основная функция программы.
|
||||
func main() {
|
||||
// Самое время поставить траппер сигналов.
|
||||
signal.Notify(sigChan,
|
||||
syscall.SIGINT,
|
||||
syscall.SIGTERM,
|
||||
syscall.SIGQUIT)
|
||||
|
||||
go sigHandler()
|
||||
|
||||
var (
|
||||
cnf Config
|
||||
err error
|
||||
)
|
||||
|
||||
u, err := user.Current()
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to get user info for current user: %s", err)
|
||||
}
|
||||
|
||||
// Для рута и для других пользователей конфиги находятся в разных местах.
|
||||
if u.Uid != "0" {
|
||||
if os.Getenv("HOME") == "" {
|
||||
log.Fatalln("Unable to get HOME environment variable value")
|
||||
}
|
||||
|
||||
cnf, err := readConf(os.Getenv("HOME") + "/.reniced.yaml")
|
||||
cnf, err = readConf(os.Getenv("HOME") + "/.reniced.yaml")
|
||||
} else {
|
||||
cnf, err = readConf("/etc/reniced.yaml")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("unable to parse config: %s", err)
|
||||
@@ -50,6 +83,8 @@ func main() {
|
||||
|
||||
// reniced основная логика.
|
||||
func reniced(cnf Config) {
|
||||
pool = pond.New(cnf.MaxWorkers, cnf.NbCapacity)
|
||||
|
||||
for {
|
||||
processList, err := proc.Processes()
|
||||
|
||||
@@ -59,6 +94,7 @@ func reniced(cnf Config) {
|
||||
|
||||
// Не самый эффективный, зато работающий способ - простой перебор массивов.
|
||||
for _, p := range processList {
|
||||
ok := pool.TrySubmit(func() {
|
||||
if processName, err := p.Name(); err == nil {
|
||||
for _, k := range cnf.Kill {
|
||||
for _, killProcessName := range k.Name {
|
||||
@@ -197,6 +233,11 @@ func reniced(cnf Config) {
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if !ok {
|
||||
log.Printf("Unable to add task to pool")
|
||||
}
|
||||
|
||||
time.Sleep(time.Millisecond * time.Duration(cnf.CmdDelay))
|
||||
}
|
||||
@@ -205,4 +246,27 @@ func reniced(cnf Config) {
|
||||
}
|
||||
}
|
||||
|
||||
// sigHandler хэндлер сигналов. Работает на выходе приложения. Держит INT, TERM, QUIT.
|
||||
func sigHandler() {
|
||||
for {
|
||||
var s = <-sigChan
|
||||
switch s {
|
||||
case syscall.SIGINT:
|
||||
log.Print("Got SIGINT, quitting")
|
||||
case syscall.SIGTERM:
|
||||
log.Print("Got SIGTERM, quitting")
|
||||
case syscall.SIGQUIT:
|
||||
log.Print("Got SIGQUIT, quitting")
|
||||
|
||||
// Заходим на новую итерацию, если у нас "неинтересный" сигнал
|
||||
default:
|
||||
continue
|
||||
}
|
||||
|
||||
pool.StopAndWait()
|
||||
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
|
||||
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */
|
||||
|
||||
7
types.go
7
types.go
@@ -16,6 +16,13 @@ type Config struct {
|
||||
// Whether to print debug info on stdout.
|
||||
Debug bool `yaml:"debug,omitempty"`
|
||||
|
||||
// Maximum workers in pool.
|
||||
MaxWorkers int `yaml:"max_workers,omitempty"`
|
||||
|
||||
// Capacity of non-blocking tasks. All tasks that exceeding this capacity will block submission of new tasks until
|
||||
// queue has vacant place for new task.
|
||||
NbCapacity int `yaml:"nb_capacity,omitempty"`
|
||||
|
||||
// В гошке yaml не позволяет сделать удобный парсер для конструкций, аналогичных json-овскому
|
||||
// {kill:{"stop":["proc1","proc2"], "term":["proc3","proc4"]}}
|
||||
// Поэтому мы просто возьмём массив и каждому элементу добавим тип сигнала.
|
||||
|
||||
25
util.go
25
util.go
@@ -22,7 +22,7 @@ func readConf(path string) (Config, error) {
|
||||
|
||||
// Конфиг-файл длинноват для конфига, попробуем следующего кандидата.
|
||||
if fileInfo.Size() > 65535 {
|
||||
err := fmt.Errorf("Config file %s is too long for config, skipping", path)
|
||||
err := fmt.Errorf("config file %s is too long for config, skipping", path)
|
||||
|
||||
return Config{}, err
|
||||
}
|
||||
@@ -39,21 +39,34 @@ func readConf(path string) (Config, error) {
|
||||
}
|
||||
|
||||
if cfg.CmdDelay == 0 {
|
||||
log.Println("cmd_delay can not be 0, setting to 200 milliseconds")
|
||||
|
||||
cfg.CmdDelay = 200
|
||||
cfg.CmdDelay = 20
|
||||
log.Println("cmd_delay can not be 0, setting to %d milliseconds", cfg.CmdDelay)
|
||||
}
|
||||
|
||||
if cfg.LoopDelay == 0 {
|
||||
log.Println("loop_delay can not be 0, setting to 2000 milliseconds")
|
||||
|
||||
cfg.LoopDelay = 2000
|
||||
log.Printf("loop_delay set to %d milliseconds", cfg.LoopDelay)
|
||||
}
|
||||
|
||||
if cfg.Debug {
|
||||
log.Println("debug set to true")
|
||||
}
|
||||
|
||||
if cfg.MaxWorkers == 0 {
|
||||
cfg.MaxWorkers = 5
|
||||
log.Printf("max_workers set to %d", cfg.MaxWorkers)
|
||||
}
|
||||
|
||||
if cfg.NbCapacity == 0 {
|
||||
cfg.NbCapacity = 100
|
||||
log.Printf("nb_capacity set to %d", cfg.NbCapacity)
|
||||
}
|
||||
|
||||
if cfg.NbCapacity < cfg.MaxWorkers {
|
||||
cfg.NbCapacity = cfg.MaxWorkers
|
||||
log.Printf("nb_capacity set to same value as max_workers: %d", cfg.MaxWorkers)
|
||||
}
|
||||
|
||||
newPrio := make(Prio, 0)
|
||||
|
||||
// Обычный юзер не может задрать nice больше 0, только руту такое можно.
|
||||
|
||||
12
vendor/github.com/alitto/pond/.gitignore
generated
vendored
Normal file
12
vendor/github.com/alitto/pond/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
21
vendor/github.com/alitto/pond/LICENSE
generated
vendored
Normal file
21
vendor/github.com/alitto/pond/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 Alejandro Durante
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
5
vendor/github.com/alitto/pond/Makefile
generated
vendored
Normal file
5
vendor/github.com/alitto/pond/Makefile
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
test:
|
||||
go test -race -v ./
|
||||
|
||||
coverage:
|
||||
go test -race -v -coverprofile=coverage.out -covermode=atomic ./
|
||||
293
vendor/github.com/alitto/pond/README.md
generated
vendored
Normal file
293
vendor/github.com/alitto/pond/README.md
generated
vendored
Normal file
@@ -0,0 +1,293 @@
|
||||
<a title="Codecov" target="_blank" href="https://github.com/alitto/pond/actions"><img alt="Build status" src="https://github.com/alitto/pond/actions/workflows/main.yml/badge.svg?branch=master&event=push"/></a>
|
||||
<a title="Codecov" target="_blank" href="https://codecov.io/gh/alitto/pond"><img src="https://codecov.io/gh/alitto/pond/branch/master/graph/badge.svg"/></a>
|
||||
<a title="Release" target="_blank" href="https://github.com/alitto/pond/releases"><img src="https://img.shields.io/github/v/release/alitto/pond"/></a>
|
||||
<a title="Go Report Card" target="_blank" href="https://goreportcard.com/report/github.com/alitto/pond"><img src="https://goreportcard.com/badge/github.com/alitto/pond"/></a>
|
||||
|
||||
# pond
|
||||
Minimalistic and High-performance goroutine worker pool written in Go
|
||||
|
||||
## Motivation
|
||||
|
||||
This library is meant to provide a simple way to limit concurrency when executing some function over a limited resource or service.
|
||||
|
||||
Some common scenarios include:
|
||||
|
||||
- Executing queries against a Database with a limited no. of connections
|
||||
- Sending HTTP requests to a a rate/concurrency limited API
|
||||
|
||||
## Features:
|
||||
|
||||
- Zero dependencies
|
||||
- Create pools with fixed or dynamic size
|
||||
- Worker goroutines are only created when needed (backpressure detection) and automatically purged after being idle for some time (configurable)
|
||||
- Minimalistic APIs for:
|
||||
- Creating worker pools with fixed or dynamic size
|
||||
- Submitting tasks to a pool in a fire-and-forget fashion
|
||||
- Submitting tasks to a pool and waiting for them to complete
|
||||
- Submitting tasks to a pool with a deadline
|
||||
- Submitting a group of tasks and waiting for them to complete
|
||||
- Submitting a group of tasks associated to a Context
|
||||
- Getting the number of running workers (goroutines)
|
||||
- Stopping a worker pool
|
||||
- Task panics are handled gracefully (configurable panic handler)
|
||||
- Supports Non-blocking and Blocking task submission modes (buffered / unbuffered)
|
||||
- Very high performance and efficient resource usage under heavy workloads, even outperforming unbounded goroutines in some scenarios (See [benchmarks](https://github.com/alitto/pond-benchmarks))
|
||||
- Configurable pool resizing strategy, with 3 presets for common scenarios: Eager, Balanced and Lazy.
|
||||
- Complete pool metrics such as number of running workers, tasks waiting in the queue [and more](#metrics--monitoring).
|
||||
- **New (since v1.7.0)**: configurable parent context and graceful shutdown with deadline.
|
||||
- [API reference](https://pkg.go.dev/github.com/alitto/pond)
|
||||
|
||||
## How to install
|
||||
|
||||
```bash
|
||||
go get -u github.com/alitto/pond
|
||||
```
|
||||
|
||||
## How to use
|
||||
|
||||
### Worker pool with dynamic size
|
||||
|
||||
``` go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/alitto/pond"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
// Create a buffered (non-blocking) pool that can scale up to 100 workers
|
||||
// and has a buffer capacity of 1000 tasks
|
||||
pool := pond.New(100, 1000)
|
||||
|
||||
// Submit 1000 tasks
|
||||
for i := 0; i < 1000; i++ {
|
||||
n := i
|
||||
pool.Submit(func() {
|
||||
fmt.Printf("Running task #%d\n", n)
|
||||
})
|
||||
}
|
||||
|
||||
// Stop the pool and wait for all submitted tasks to complete
|
||||
pool.StopAndWait()
|
||||
}
|
||||
```
|
||||
|
||||
### Worker pool with fixed size
|
||||
|
||||
``` go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/alitto/pond"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
// Create an unbuffered (blocking) pool with a fixed
|
||||
// number of workers
|
||||
pool := pond.New(10, 0, pond.MinWorkers(10))
|
||||
|
||||
// Submit 1000 tasks
|
||||
for i := 0; i < 1000; i++ {
|
||||
n := i
|
||||
pool.Submit(func() {
|
||||
fmt.Printf("Running task #%d\n", n)
|
||||
})
|
||||
}
|
||||
|
||||
// Stop the pool and wait for all submitted tasks to complete
|
||||
pool.StopAndWait()
|
||||
}
|
||||
```
|
||||
|
||||
### Submitting a group of tasks
|
||||
|
||||
``` go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/alitto/pond"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
// Create a pool
|
||||
pool := pond.New(10, 1000)
|
||||
defer pool.StopAndWait()
|
||||
|
||||
// Create a task group
|
||||
group := pool.Group()
|
||||
|
||||
// Submit a group of tasks
|
||||
for i := 0; i < 20; i++ {
|
||||
n := i
|
||||
group.Submit(func() {
|
||||
fmt.Printf("Running group task #%d\n", n)
|
||||
})
|
||||
}
|
||||
|
||||
// Wait for all tasks in the group to complete
|
||||
group.Wait()
|
||||
}
|
||||
```
|
||||
|
||||
### Submitting a group of tasks associated to a context (**since v1.8.0**)
|
||||
|
||||
This feature provides synchronization, error propagation, and Context cancelation for subtasks of a common task. Similar to `errgroup.Group` from [`golang.org/x/sync/errgroup`](https://pkg.go.dev/golang.org/x/sync/errgroup) package with concurrency bounded by the worker pool.
|
||||
|
||||
``` go
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/alitto/pond"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
// Create a worker pool
|
||||
pool := pond.New(10, 1000)
|
||||
defer pool.StopAndWait()
|
||||
|
||||
// Create a task group associated to a context
|
||||
group, ctx := pool.GroupContext(context.Background())
|
||||
|
||||
var urls = []string{
|
||||
"https://www.golang.org/",
|
||||
"https://www.google.com/",
|
||||
"https://www.github.com/",
|
||||
}
|
||||
|
||||
// Submit tasks to fetch each URL
|
||||
for _, url := range urls {
|
||||
url := url
|
||||
group.Submit(func() error {
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err == nil {
|
||||
resp.Body.Close()
|
||||
}
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
// Wait for all HTTP requests to complete.
|
||||
err := group.Wait()
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to fetch URLs: %v", err)
|
||||
} else {
|
||||
fmt.Println("Successfully fetched all URLs")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Pool Configuration Options
|
||||
|
||||
- **MinWorkers**: Specifies the minimum number of worker goroutines that must be running at any given time. These goroutines are started when the pool is created. The default value is 0. Example:
|
||||
``` go
|
||||
// This will create a pool with 5 running worker goroutines
|
||||
pool := pond.New(10, 1000, pond.MinWorkers(5))
|
||||
```
|
||||
- **IdleTimeout**: Defines how long to wait before removing idle worker goroutines from the pool. The default value is 5 seconds. Example:
|
||||
``` go
|
||||
// This will create a pool that will remove workers 100ms after they become idle
|
||||
pool := pond.New(10, 1000, pond.IdleTimeout(100 * time.Millisecond))
|
||||
```
|
||||
- **PanicHandler**: Allows to configure a custom function to handle panics thrown by tasks submitted to the pool. The default handler just writes a message to standard output using `fmt.Printf` with the following contents: `Worker exits from a panic: [panic] \n Stack trace: [stack trace]`). Example:
|
||||
```go
|
||||
// Custom panic handler function
|
||||
panicHandler := func(p interface{}) {
|
||||
fmt.Printf("Task panicked: %v", p)
|
||||
}
|
||||
|
||||
// This will create a pool that will handle panics using a custom panic handler
|
||||
pool := pond.New(10, 1000, pond.PanicHandler(panicHandler)))
|
||||
```
|
||||
- **Strategy**: Configures the strategy used to resize the pool when backpressure is detected. You can create a custom strategy by implementing the `pond.ResizingStrategy` interface or choose one of the 3 presets:
|
||||
- **Eager**: maximizes responsiveness at the expense of higher resource usage, which can reduce throughput under certain conditions. This strategy is meant for worker pools that will operate at a small percentage of their capacity most of the time and may occasionally receive bursts of tasks. This is the default strategy.
|
||||
- **Balanced**: tries to find a balance between responsiveness and throughput. It's suitable for general purpose worker pools or those that will operate close to 50% of their capacity most of the time.
|
||||
- **Lazy**: maximizes throughput at the expense of responsiveness. This strategy is meant for worker pools that will operate close to their max. capacity most of the time.
|
||||
``` go
|
||||
// Example: create pools with different resizing strategies
|
||||
eagerPool := pond.New(10, 1000, pond.Strategy(pond.Eager()))
|
||||
balancedPool := pond.New(10, 1000, pond.Strategy(pond.Balanced()))
|
||||
lazyPool := pond.New(10, 1000, pond.Strategy(pond.Lazy()))
|
||||
```
|
||||
- **Context**: Configures a parent context on this pool to stop all workers when it is cancelled. The default value `context.Background()`. Example:
|
||||
``` go
|
||||
// This creates a pool that is stopped when myCtx is cancelled
|
||||
pool := pond.New(10, 1000, pond.Context(myCtx))
|
||||
```
|
||||
|
||||
### Resizing strategies
|
||||
|
||||
The following chart illustrates the behaviour of the different pool resizing strategies as the number of submitted tasks increases. Each line represents the number of worker goroutines in the pool (pool size) and the x-axis reflects the number of submitted tasks (cumulative).
|
||||
|
||||

|
||||
|
||||
As the name suggests, the "Eager" strategy always spawns an extra worker when there are no idles, which causes the pool to grow almost linearly with the number of submitted tasks. On the other end, the "Lazy" strategy creates one worker every N submitted tasks, where N is the maximum number of available CPUs ([GOMAXPROCS](https://golang.org/pkg/runtime/#GOMAXPROCS)). The "Balanced" strategy represents a middle ground between the previous two because it creates a worker every N/2 submitted tasks.
|
||||
|
||||
### Stopping a pool
|
||||
|
||||
There are 3 methods available to stop a pool and release associated resources:
|
||||
- `pool.Stop()`: stop accepting new tasks and signal all workers to stop processing new tasks. Tasks being processed by workers will continue until completion unless the process is terminated.
|
||||
- `pool.StopAndWait()`: stop accepting new tasks and wait until all running and queued tasks have completed before returning.
|
||||
- `pool.StopAndWaitFor(deadline time.Duration)`: similar to `StopAndWait` but with a deadline to prevent waiting indefinitely.
|
||||
|
||||
### Metrics & monitoring
|
||||
|
||||
Each worker pool instance exposes useful metrics that can be queried through the following methods:
|
||||
|
||||
- `pool.RunningWorkers() int`: Current number of running workers
|
||||
- `pool.IdleWorkers() int`: Current number of idle workers
|
||||
- `pool.MinWorkers() int`: Minimum number of worker goroutines
|
||||
- `pool.MaxWorkers() int`: Maxmimum number of worker goroutines
|
||||
- `pool.MaxCapacity() int`: Maximum number of tasks that can be waiting in the queue at any given time (queue capacity)
|
||||
- `pool.SubmittedTasks() uint64`: Total number of tasks submitted since the pool was created
|
||||
- `pool.WaitingTasks() uint64`: Current number of tasks in the queue that are waiting to be executed
|
||||
- `pool.SuccessfulTasks() uint64`: Total number of tasks that have successfully completed their exection since the pool was created
|
||||
- `pool.FailedTasks() uint64`: Total number of tasks that completed with panic since the pool was created
|
||||
- `pool.CompletedTasks() uint64`: Total number of tasks that have completed their exection either successfully or with panic since the pool was created
|
||||
|
||||
In our [Prometheus example](./examples/prometheus/prometheus.go) we showcase how to configure collectors for these metrics and expose them to Prometheus.
|
||||
|
||||
## Examples
|
||||
|
||||
- [Creating a worker pool with dynamic size](./examples/dynamic_size/dynamic_size.go)
|
||||
- [Creating a worker pool with fixed size](./examples/fixed_size/fixed_size.go)
|
||||
- [Creating a worker pool with a Context](./examples/pool_context/pool_context.go)
|
||||
- [Exporting worker pool metrics to Prometheus](./examples/prometheus/prometheus.go)
|
||||
- [Submitting a group of tasks](./examples/task_group/task_group.go)
|
||||
- [Submitting a group of tasks associated to a context](./examples/group_context/group_context.go)
|
||||
|
||||
## API Reference
|
||||
|
||||
Full API reference is available at https://pkg.go.dev/github.com/alitto/pond
|
||||
|
||||
## Benchmarks
|
||||
|
||||
See [Benchmarks](https://github.com/alitto/pond-benchmarks).
|
||||
|
||||
## Resources
|
||||
|
||||
Here are some of the resources which have served as inspiration when writing this library:
|
||||
|
||||
- http://marcio.io/2015/07/handling-1-million-requests-per-minute-with-golang/
|
||||
- https://brandur.org/go-worker-pool
|
||||
- https://gobyexample.com/worker-pools
|
||||
- https://github.com/panjf2000/ants
|
||||
- https://github.com/gammazero/workerpool
|
||||
|
||||
## Contribution & Support
|
||||
|
||||
Feel free to send a pull request if you consider there's something which can be improved. Also, please open up an issue if you run into a problem when using this library or just have a question.
|
||||
106
vendor/github.com/alitto/pond/group.go
generated
vendored
Normal file
106
vendor/github.com/alitto/pond/group.go
generated
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
package pond
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// TaskGroup represents a group of related tasks
|
||||
type TaskGroup struct {
|
||||
pool *WorkerPool
|
||||
waitGroup sync.WaitGroup
|
||||
}
|
||||
|
||||
// Submit adds a task to this group and sends it to the worker pool to be executed
|
||||
func (g *TaskGroup) Submit(task func()) {
|
||||
g.waitGroup.Add(1)
|
||||
|
||||
g.pool.Submit(func() {
|
||||
defer g.waitGroup.Done()
|
||||
|
||||
task()
|
||||
})
|
||||
}
|
||||
|
||||
// Wait waits until all the tasks in this group have completed
|
||||
func (g *TaskGroup) Wait() {
|
||||
|
||||
// Wait for all tasks to complete
|
||||
g.waitGroup.Wait()
|
||||
}
|
||||
|
||||
// TaskGroupWithContext represents a group of related tasks associated to a context
|
||||
type TaskGroupWithContext struct {
|
||||
TaskGroup
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
|
||||
errSync struct {
|
||||
once sync.Once
|
||||
guard sync.RWMutex
|
||||
}
|
||||
err error
|
||||
}
|
||||
|
||||
// Submit adds a task to this group and sends it to the worker pool to be executed
|
||||
func (g *TaskGroupWithContext) Submit(task func() error) {
|
||||
g.waitGroup.Add(1)
|
||||
|
||||
g.pool.Submit(func() {
|
||||
defer g.waitGroup.Done()
|
||||
|
||||
// If context has already been cancelled, skip task execution
|
||||
select {
|
||||
case <-g.ctx.Done():
|
||||
return
|
||||
default:
|
||||
}
|
||||
|
||||
// don't actually ignore errors
|
||||
err := task()
|
||||
if err != nil {
|
||||
g.setError(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Wait blocks until either all the tasks submitted to this group have completed,
|
||||
// one of them returned a non-nil error or the context associated to this group
|
||||
// was canceled.
|
||||
func (g *TaskGroupWithContext) Wait() error {
|
||||
|
||||
// Wait for all tasks to complete
|
||||
tasksCompleted := make(chan struct{})
|
||||
go func() {
|
||||
g.waitGroup.Wait()
|
||||
tasksCompleted <- struct{}{}
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-tasksCompleted:
|
||||
// If context was provided, cancel it to signal all running tasks to stop
|
||||
g.cancel()
|
||||
case <-g.ctx.Done():
|
||||
g.setError(g.ctx.Err())
|
||||
}
|
||||
|
||||
return g.getError()
|
||||
}
|
||||
|
||||
func (g *TaskGroupWithContext) getError() error {
|
||||
g.errSync.guard.RLock()
|
||||
err := g.err
|
||||
g.errSync.guard.RUnlock()
|
||||
return err
|
||||
}
|
||||
|
||||
func (g *TaskGroupWithContext) setError(err error) {
|
||||
g.errSync.once.Do(func() {
|
||||
g.errSync.guard.Lock()
|
||||
g.err = err
|
||||
g.errSync.guard.Unlock()
|
||||
|
||||
// Cancel execution of any pending task in this group
|
||||
g.cancel()
|
||||
})
|
||||
}
|
||||
549
vendor/github.com/alitto/pond/pond.go
generated
vendored
Normal file
549
vendor/github.com/alitto/pond/pond.go
generated
vendored
Normal file
@@ -0,0 +1,549 @@
|
||||
package pond
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"runtime/debug"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// defaultIdleTimeout defines the default idle timeout to use when not explicitly specified
|
||||
// via the IdleTimeout() option
|
||||
defaultIdleTimeout = 5 * time.Second
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrSubmitOnStoppedPool is thrown when attempting to submit a task to a pool that has been stopped
|
||||
ErrSubmitOnStoppedPool = errors.New("worker pool has been stopped and is no longer accepting tasks")
|
||||
)
|
||||
|
||||
// defaultPanicHandler is the default panic handler
|
||||
func defaultPanicHandler(panic interface{}) {
|
||||
fmt.Printf("Worker exits from a panic: %v\nStack trace: %s\n", panic, string(debug.Stack()))
|
||||
}
|
||||
|
||||
// ResizingStrategy represents a pool resizing strategy
|
||||
type ResizingStrategy interface {
|
||||
Resize(runningWorkers, minWorkers, maxWorkers int) bool
|
||||
}
|
||||
|
||||
// Option represents an option that can be passed when instantiating a worker pool to customize it
|
||||
type Option func(*WorkerPool)
|
||||
|
||||
// IdleTimeout allows to change the idle timeout for a worker pool
|
||||
func IdleTimeout(idleTimeout time.Duration) Option {
|
||||
return func(pool *WorkerPool) {
|
||||
pool.idleTimeout = idleTimeout
|
||||
}
|
||||
}
|
||||
|
||||
// MinWorkers allows to change the minimum number of workers of a worker pool
|
||||
func MinWorkers(minWorkers int) Option {
|
||||
return func(pool *WorkerPool) {
|
||||
pool.minWorkers = minWorkers
|
||||
}
|
||||
}
|
||||
|
||||
// Strategy allows to change the strategy used to resize the pool
|
||||
func Strategy(strategy ResizingStrategy) Option {
|
||||
return func(pool *WorkerPool) {
|
||||
pool.strategy = strategy
|
||||
}
|
||||
}
|
||||
|
||||
// PanicHandler allows to change the panic handler function of a worker pool
|
||||
func PanicHandler(panicHandler func(interface{})) Option {
|
||||
return func(pool *WorkerPool) {
|
||||
pool.panicHandler = panicHandler
|
||||
}
|
||||
}
|
||||
|
||||
// Context configures a parent context on a worker pool to stop all workers when it is cancelled
|
||||
func Context(parentCtx context.Context) Option {
|
||||
return func(pool *WorkerPool) {
|
||||
pool.context, pool.contextCancel = context.WithCancel(parentCtx)
|
||||
}
|
||||
}
|
||||
|
||||
// WorkerPool models a pool of workers
|
||||
type WorkerPool struct {
|
||||
// Atomic counters, should be placed first so alignment is guaranteed
|
||||
// for atomic operations.
|
||||
workerCount int32
|
||||
idleWorkerCount int32
|
||||
waitingTaskCount uint64
|
||||
submittedTaskCount uint64
|
||||
successfulTaskCount uint64
|
||||
failedTaskCount uint64
|
||||
// Configurable settings
|
||||
maxWorkers int
|
||||
maxCapacity int
|
||||
minWorkers int
|
||||
idleTimeout time.Duration
|
||||
strategy ResizingStrategy
|
||||
panicHandler func(interface{})
|
||||
context context.Context
|
||||
contextCancel context.CancelFunc
|
||||
// Private properties
|
||||
tasks chan func()
|
||||
tasksCloseOnce sync.Once
|
||||
workersWaitGroup sync.WaitGroup
|
||||
tasksWaitGroup sync.WaitGroup
|
||||
mutex sync.Mutex
|
||||
stopped int32
|
||||
}
|
||||
|
||||
// New creates a worker pool with that can scale up to the given maximum number of workers (maxWorkers).
|
||||
// The maxCapacity parameter determines the number of tasks that can be submitted to this pool without blocking,
|
||||
// because it defines the size of the buffered channel used to receive tasks.
|
||||
// The options parameter can take a list of functions to customize configuration values on this worker pool.
|
||||
func New(maxWorkers, maxCapacity int, options ...Option) *WorkerPool {
|
||||
|
||||
// Instantiate the pool
|
||||
pool := &WorkerPool{
|
||||
maxWorkers: maxWorkers,
|
||||
maxCapacity: maxCapacity,
|
||||
idleTimeout: defaultIdleTimeout,
|
||||
strategy: Eager(),
|
||||
panicHandler: defaultPanicHandler,
|
||||
}
|
||||
|
||||
// Apply all options
|
||||
for _, opt := range options {
|
||||
opt(pool)
|
||||
}
|
||||
|
||||
// Make sure options are consistent
|
||||
if pool.maxWorkers <= 0 {
|
||||
pool.maxWorkers = 1
|
||||
}
|
||||
if pool.minWorkers > pool.maxWorkers {
|
||||
pool.minWorkers = pool.maxWorkers
|
||||
}
|
||||
if pool.maxCapacity < 0 {
|
||||
pool.maxCapacity = 0
|
||||
}
|
||||
if pool.idleTimeout < 0 {
|
||||
pool.idleTimeout = defaultIdleTimeout
|
||||
}
|
||||
|
||||
// Initialize base context (if not already set)
|
||||
if pool.context == nil {
|
||||
Context(context.Background())(pool)
|
||||
}
|
||||
|
||||
// Create tasks channel
|
||||
pool.tasks = make(chan func(), pool.maxCapacity)
|
||||
|
||||
// Start purger goroutine
|
||||
pool.workersWaitGroup.Add(1)
|
||||
go pool.purge()
|
||||
|
||||
// Start minWorkers workers
|
||||
if pool.minWorkers > 0 {
|
||||
for i := 0; i < pool.minWorkers; i++ {
|
||||
pool.maybeStartWorker(nil)
|
||||
}
|
||||
}
|
||||
|
||||
return pool
|
||||
}
|
||||
|
||||
// RunningWorkers returns the current number of running workers
|
||||
func (p *WorkerPool) RunningWorkers() int {
|
||||
return int(atomic.LoadInt32(&p.workerCount))
|
||||
}
|
||||
|
||||
// IdleWorkers returns the current number of idle workers
|
||||
func (p *WorkerPool) IdleWorkers() int {
|
||||
return int(atomic.LoadInt32(&p.idleWorkerCount))
|
||||
}
|
||||
|
||||
// MinWorkers returns the minimum number of worker goroutines
|
||||
func (p *WorkerPool) MinWorkers() int {
|
||||
return p.minWorkers
|
||||
}
|
||||
|
||||
// MaxWorkers returns the maximum number of worker goroutines
|
||||
func (p *WorkerPool) MaxWorkers() int {
|
||||
return p.maxWorkers
|
||||
}
|
||||
|
||||
// MaxCapacity returns the maximum number of tasks that can be waiting in the queue
|
||||
// at any given time (queue size)
|
||||
func (p *WorkerPool) MaxCapacity() int {
|
||||
return p.maxCapacity
|
||||
}
|
||||
|
||||
// Strategy returns the configured pool resizing strategy
|
||||
func (p *WorkerPool) Strategy() ResizingStrategy {
|
||||
return p.strategy
|
||||
}
|
||||
|
||||
// SubmittedTasks returns the total number of tasks submitted since the pool was created
|
||||
func (p *WorkerPool) SubmittedTasks() uint64 {
|
||||
return atomic.LoadUint64(&p.submittedTaskCount)
|
||||
}
|
||||
|
||||
// WaitingTasks returns the current number of tasks in the queue that are waiting to be executed
|
||||
func (p *WorkerPool) WaitingTasks() uint64 {
|
||||
return atomic.LoadUint64(&p.waitingTaskCount)
|
||||
}
|
||||
|
||||
// SuccessfulTasks returns the total number of tasks that have successfully completed their exection
|
||||
// since the pool was created
|
||||
func (p *WorkerPool) SuccessfulTasks() uint64 {
|
||||
return atomic.LoadUint64(&p.successfulTaskCount)
|
||||
}
|
||||
|
||||
// FailedTasks returns the total number of tasks that completed with panic since the pool was created
|
||||
func (p *WorkerPool) FailedTasks() uint64 {
|
||||
return atomic.LoadUint64(&p.failedTaskCount)
|
||||
}
|
||||
|
||||
// CompletedTasks returns the total number of tasks that have completed their exection either successfully
|
||||
// or with panic since the pool was created
|
||||
func (p *WorkerPool) CompletedTasks() uint64 {
|
||||
return p.SuccessfulTasks() + p.FailedTasks()
|
||||
}
|
||||
|
||||
// Stopped returns true if the pool has been stopped and is no longer accepting tasks, and false otherwise.
|
||||
func (p *WorkerPool) Stopped() bool {
|
||||
return atomic.LoadInt32(&p.stopped) == 1
|
||||
}
|
||||
|
||||
// Submit sends a task to this worker pool for execution. If the queue is full,
|
||||
// it will wait until the task is dispatched to a worker goroutine.
|
||||
func (p *WorkerPool) Submit(task func()) {
|
||||
p.submit(task, true)
|
||||
}
|
||||
|
||||
// TrySubmit attempts to send a task to this worker pool for execution. If the queue is full,
|
||||
// it will not wait for a worker to become idle. It returns true if it was able to dispatch
|
||||
// the task and false otherwise.
|
||||
func (p *WorkerPool) TrySubmit(task func()) bool {
|
||||
return p.submit(task, false)
|
||||
}
|
||||
|
||||
func (p *WorkerPool) submit(task func(), mustSubmit bool) (submitted bool) {
|
||||
if task == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if p.Stopped() {
|
||||
// Pool is stopped and caller must submit the task
|
||||
if mustSubmit {
|
||||
panic(ErrSubmitOnStoppedPool)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Increment submitted and waiting task counters as soon as we receive a task
|
||||
atomic.AddUint64(&p.submittedTaskCount, 1)
|
||||
atomic.AddUint64(&p.waitingTaskCount, 1)
|
||||
p.tasksWaitGroup.Add(1)
|
||||
|
||||
defer func() {
|
||||
if !submitted {
|
||||
// Task was not sumitted to the pool, decrement submitted and waiting task counters
|
||||
atomic.AddUint64(&p.submittedTaskCount, ^uint64(0))
|
||||
atomic.AddUint64(&p.waitingTaskCount, ^uint64(0))
|
||||
p.tasksWaitGroup.Done()
|
||||
}
|
||||
}()
|
||||
|
||||
// Start a worker as long as we haven't reached the limit
|
||||
if submitted = p.maybeStartWorker(task); submitted {
|
||||
return
|
||||
}
|
||||
|
||||
if !mustSubmit {
|
||||
// Attempt to dispatch to an idle worker without blocking
|
||||
select {
|
||||
case p.tasks <- task:
|
||||
submitted = true
|
||||
return
|
||||
default:
|
||||
// Channel is full and can't wait for an idle worker, so need to exit
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Submit the task to the tasks channel and wait for it to be picked up by a worker
|
||||
p.tasks <- task
|
||||
submitted = true
|
||||
return
|
||||
}
|
||||
|
||||
// SubmitAndWait sends a task to this worker pool for execution and waits for it to complete
|
||||
// before returning
|
||||
func (p *WorkerPool) SubmitAndWait(task func()) {
|
||||
if task == nil {
|
||||
return
|
||||
}
|
||||
|
||||
done := make(chan struct{})
|
||||
p.Submit(func() {
|
||||
defer close(done)
|
||||
task()
|
||||
})
|
||||
<-done
|
||||
}
|
||||
|
||||
// SubmitBefore attempts to send a task for execution to this worker pool but aborts it
|
||||
// if the task did not start before the given deadline.
|
||||
func (p *WorkerPool) SubmitBefore(task func(), deadline time.Duration) {
|
||||
if task == nil {
|
||||
return
|
||||
}
|
||||
|
||||
timer := time.NewTimer(deadline)
|
||||
p.Submit(func() {
|
||||
select {
|
||||
case <-timer.C:
|
||||
// Deadline was reached, abort the task
|
||||
default:
|
||||
// Deadline not reached, execute the task
|
||||
defer timer.Stop()
|
||||
|
||||
task()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Stop causes this pool to stop accepting new tasks and signals all workers to exit.
|
||||
// Tasks being executed by workers will continue until completion (unless the process is terminated).
|
||||
// Tasks in the queue will not be executed.
|
||||
func (p *WorkerPool) Stop() {
|
||||
go p.stop(false)
|
||||
}
|
||||
|
||||
// StopAndWait causes this pool to stop accepting new tasks and then waits for all tasks in the queue
|
||||
// to complete before returning.
|
||||
func (p *WorkerPool) StopAndWait() {
|
||||
p.stop(true)
|
||||
}
|
||||
|
||||
// StopAndWaitFor stops this pool and waits until either all tasks in the queue are completed
|
||||
// or the given deadline is reached, whichever comes first.
|
||||
func (p *WorkerPool) StopAndWaitFor(deadline time.Duration) {
|
||||
|
||||
// Launch goroutine to detect when worker pool has stopped gracefully
|
||||
workersDone := make(chan struct{})
|
||||
go func() {
|
||||
p.stop(true)
|
||||
workersDone <- struct{}{}
|
||||
}()
|
||||
|
||||
// Wait until either all workers have exited or the deadline is reached
|
||||
select {
|
||||
case <-workersDone:
|
||||
return
|
||||
case <-time.After(deadline):
|
||||
p.contextCancel()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (p *WorkerPool) stop(waitForQueuedTasksToComplete bool) {
|
||||
// Mark pool as stopped
|
||||
atomic.StoreInt32(&p.stopped, 1)
|
||||
|
||||
if waitForQueuedTasksToComplete {
|
||||
// Wait for all queued tasks to complete
|
||||
p.tasksWaitGroup.Wait()
|
||||
}
|
||||
|
||||
// Reset worker count
|
||||
p.resetWorkerCount()
|
||||
|
||||
// Terminate all workers & purger goroutine
|
||||
p.contextCancel()
|
||||
|
||||
// Wait for all workers & purger goroutine to exit
|
||||
p.workersWaitGroup.Wait()
|
||||
|
||||
// close tasks channel (only once, in case multiple concurrent calls to StopAndWait are made)
|
||||
p.tasksCloseOnce.Do(func() {
|
||||
close(p.tasks)
|
||||
})
|
||||
}
|
||||
|
||||
// purge represents the work done by the purger goroutine
|
||||
func (p *WorkerPool) purge() {
|
||||
defer p.workersWaitGroup.Done()
|
||||
|
||||
idleTicker := time.NewTicker(p.idleTimeout)
|
||||
defer idleTicker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
// Timed out waiting for any activity to happen, attempt to stop an idle worker
|
||||
case <-idleTicker.C:
|
||||
p.maybeStopIdleWorker()
|
||||
// Pool context was cancelled, exit
|
||||
case <-p.context.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// maybeStopIdleWorker attempts to stop an idle worker by sending it a nil task
|
||||
func (p *WorkerPool) maybeStopIdleWorker() bool {
|
||||
|
||||
if decremented := p.decrementWorkerCount(); !decremented {
|
||||
return false
|
||||
}
|
||||
|
||||
// Send a nil task to stop an idle worker
|
||||
p.tasks <- nil
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// maybeStartWorker attempts to create a new worker goroutine to run the given task.
|
||||
// If the worker pool has reached the maximum number of workers or there are idle workers,
|
||||
// it will not create a new one.
|
||||
func (p *WorkerPool) maybeStartWorker(firstTask func()) bool {
|
||||
|
||||
if incremented := p.incrementWorkerCount(); !incremented {
|
||||
return false
|
||||
}
|
||||
|
||||
if firstTask == nil {
|
||||
// Worker starts idle
|
||||
atomic.AddInt32(&p.idleWorkerCount, 1)
|
||||
}
|
||||
|
||||
// Launch worker goroutine
|
||||
go worker(p.context, &p.workersWaitGroup, firstTask, p.tasks, p.executeTask)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// executeTask executes the given task and updates task-related counters
|
||||
func (p *WorkerPool) executeTask(task func(), isFirstTask bool) {
|
||||
|
||||
defer func() {
|
||||
if panic := recover(); panic != nil {
|
||||
// Increment failed task count
|
||||
atomic.AddUint64(&p.failedTaskCount, 1)
|
||||
|
||||
// Invoke panic handler
|
||||
p.panicHandler(panic)
|
||||
|
||||
// Increment idle count
|
||||
atomic.AddInt32(&p.idleWorkerCount, 1)
|
||||
}
|
||||
p.tasksWaitGroup.Done()
|
||||
}()
|
||||
|
||||
// Decrement idle count
|
||||
if !isFirstTask {
|
||||
atomic.AddInt32(&p.idleWorkerCount, -1)
|
||||
}
|
||||
|
||||
// Decrement waiting task count
|
||||
atomic.AddUint64(&p.waitingTaskCount, ^uint64(0))
|
||||
|
||||
// Execute task
|
||||
task()
|
||||
|
||||
// Increment successful task count
|
||||
atomic.AddUint64(&p.successfulTaskCount, 1)
|
||||
|
||||
// Increment idle count
|
||||
atomic.AddInt32(&p.idleWorkerCount, 1)
|
||||
}
|
||||
|
||||
func (p *WorkerPool) incrementWorkerCount() bool {
|
||||
|
||||
p.mutex.Lock()
|
||||
defer p.mutex.Unlock()
|
||||
|
||||
runningWorkerCount := p.RunningWorkers()
|
||||
|
||||
// Reached max workers, do not create a new one
|
||||
if runningWorkerCount >= p.maxWorkers {
|
||||
return false
|
||||
}
|
||||
|
||||
// Idle workers available, do not create a new one
|
||||
if runningWorkerCount >= p.minWorkers && runningWorkerCount > 0 && p.IdleWorkers() > 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
// Execute the resizing strategy to determine if we should create more workers
|
||||
if resize := p.strategy.Resize(runningWorkerCount, p.minWorkers, p.maxWorkers); !resize {
|
||||
return false
|
||||
}
|
||||
|
||||
// Increment worker count
|
||||
atomic.AddInt32(&p.workerCount, 1)
|
||||
|
||||
// Increment wait group
|
||||
p.workersWaitGroup.Add(1)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (p *WorkerPool) decrementWorkerCount() bool {
|
||||
|
||||
p.mutex.Lock()
|
||||
defer p.mutex.Unlock()
|
||||
|
||||
if p.IdleWorkers() <= 0 || p.RunningWorkers() <= p.minWorkers || p.Stopped() {
|
||||
return false
|
||||
}
|
||||
|
||||
// Decrement worker count
|
||||
atomic.AddInt32(&p.workerCount, -1)
|
||||
|
||||
// Decrement idle count
|
||||
atomic.AddInt32(&p.idleWorkerCount, -1)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (p *WorkerPool) resetWorkerCount() {
|
||||
|
||||
p.mutex.Lock()
|
||||
defer p.mutex.Unlock()
|
||||
|
||||
// Reset worker count
|
||||
atomic.StoreInt32(&p.workerCount, 0)
|
||||
|
||||
// Reset idle count
|
||||
atomic.StoreInt32(&p.idleWorkerCount, 0)
|
||||
}
|
||||
|
||||
// Group creates a new task group
|
||||
func (p *WorkerPool) Group() *TaskGroup {
|
||||
return &TaskGroup{
|
||||
pool: p,
|
||||
}
|
||||
}
|
||||
|
||||
// GroupContext creates a new task group and an associated Context derived from ctx.
|
||||
//
|
||||
// The derived Context is canceled the first time a function submitted to the group
|
||||
// returns a non-nil error or the first time Wait returns, whichever occurs first.
|
||||
func (p *WorkerPool) GroupContext(ctx context.Context) (*TaskGroupWithContext, context.Context) {
|
||||
|
||||
if ctx == nil {
|
||||
panic("a non-nil context needs to be specified when using GroupContext")
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
return &TaskGroupWithContext{
|
||||
TaskGroup: TaskGroup{
|
||||
pool: p,
|
||||
},
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
}, ctx
|
||||
}
|
||||
55
vendor/github.com/alitto/pond/resizer.go
generated
vendored
Normal file
55
vendor/github.com/alitto/pond/resizer.go
generated
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
package pond
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
)
|
||||
|
||||
var maxProcs = runtime.GOMAXPROCS(0)
|
||||
|
||||
// Preset pool resizing strategies
|
||||
var (
|
||||
// Eager maximizes responsiveness at the expense of higher resource usage,
|
||||
// which can reduce throughput under certain conditions.
|
||||
// This strategy is meant for worker pools that will operate at a small percentage of their capacity
|
||||
// most of the time and may occasionally receive bursts of tasks. It's the default strategy.
|
||||
Eager = func() ResizingStrategy { return RatedResizer(1) }
|
||||
// Balanced tries to find a balance between responsiveness and throughput.
|
||||
// It's suitable for general purpose worker pools or those
|
||||
// that will operate close to 50% of their capacity most of the time.
|
||||
Balanced = func() ResizingStrategy { return RatedResizer(maxProcs / 2) }
|
||||
// Lazy maximizes throughput at the expense of responsiveness.
|
||||
// This strategy is meant for worker pools that will operate close to their max. capacity most of the time.
|
||||
Lazy = func() ResizingStrategy { return RatedResizer(maxProcs) }
|
||||
)
|
||||
|
||||
// ratedResizer implements a rated resizing strategy
|
||||
type ratedResizer struct {
|
||||
rate uint64
|
||||
hits uint64
|
||||
}
|
||||
|
||||
// RatedResizer creates a resizing strategy which can be configured
|
||||
// to create workers at a specific rate when the pool has no idle workers.
|
||||
// rate: determines the number of tasks to receive before creating an extra worker.
|
||||
// A value of 3 can be interpreted as: "Create a new worker every 3 tasks".
|
||||
func RatedResizer(rate int) ResizingStrategy {
|
||||
|
||||
if rate < 1 {
|
||||
rate = 1
|
||||
}
|
||||
|
||||
return &ratedResizer{
|
||||
rate: uint64(rate),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *ratedResizer) Resize(runningWorkers, minWorkers, maxWorkers int) bool {
|
||||
|
||||
if r.rate == 1 || runningWorkers == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
r.hits++
|
||||
|
||||
return r.hits%r.rate == 1
|
||||
}
|
||||
35
vendor/github.com/alitto/pond/worker.go
generated
vendored
Normal file
35
vendor/github.com/alitto/pond/worker.go
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
package pond
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// worker represents a worker goroutine
|
||||
func worker(context context.Context, waitGroup *sync.WaitGroup, firstTask func(), tasks <-chan func(), taskExecutor func(func(), bool)) {
|
||||
|
||||
// If provided, execute the first task immediately, before listening to the tasks channel
|
||||
if firstTask != nil {
|
||||
taskExecutor(firstTask, true)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
waitGroup.Done()
|
||||
}()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-context.Done():
|
||||
// Pool context was cancelled, exit
|
||||
return
|
||||
case task, ok := <-tasks:
|
||||
if task == nil || !ok {
|
||||
// We have received a signal to exit
|
||||
return
|
||||
}
|
||||
|
||||
// We have received a task, execute it
|
||||
taskExecutor(task, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
34
vendor/github.com/shirou/gopsutil/v3/process/process_freebsd_arm64.go
generated
vendored
34
vendor/github.com/shirou/gopsutil/v3/process/process_freebsd_arm64.go
generated
vendored
@@ -1,8 +1,7 @@
|
||||
//go:build freebsd && arm64
|
||||
// +build freebsd,arm64
|
||||
|
||||
// Code generated by cmd/cgo -godefs; DO NOT EDIT.
|
||||
// cgo -godefs process/types_freebsd.go
|
||||
// cgo -godefs types_freebsd.go
|
||||
|
||||
package process
|
||||
|
||||
@@ -82,14 +81,14 @@ type Rlimit struct {
|
||||
type KinfoProc struct {
|
||||
Structsize int32
|
||||
Layout int32
|
||||
Args *int64 /* pargs */
|
||||
Paddr *int64 /* proc */
|
||||
Addr *int64 /* user */
|
||||
Tracep *int64 /* vnode */
|
||||
Textvp *int64 /* vnode */
|
||||
Fd *int64 /* filedesc */
|
||||
Vmspace *int64 /* vmspace */
|
||||
Wchan *byte
|
||||
Args int64 /* pargs */
|
||||
Paddr int64 /* proc */
|
||||
Addr int64 /* user */
|
||||
Tracep int64 /* vnode */
|
||||
Textvp int64 /* vnode */
|
||||
Fd int64 /* filedesc */
|
||||
Vmspace int64 /* vmspace */
|
||||
Wchan int64
|
||||
Pid int32
|
||||
Ppid int32
|
||||
Pgid int32
|
||||
@@ -140,7 +139,7 @@ type KinfoProc struct {
|
||||
Wmesg [9]uint8
|
||||
Login [18]uint8
|
||||
Lockname [9]uint8
|
||||
Comm [20]int8
|
||||
Comm [20]int8 // changed from uint8 by hand
|
||||
Emul [17]uint8
|
||||
Loginclass [18]uint8
|
||||
Moretdname [4]uint8
|
||||
@@ -159,11 +158,12 @@ type KinfoProc struct {
|
||||
Pri Priority
|
||||
Rusage Rusage
|
||||
Rusage_ch Rusage
|
||||
Pcb *int64 /* pcb */
|
||||
Kstack *byte
|
||||
Udata *byte
|
||||
Tdaddr *int64 /* thread */
|
||||
Spareptrs [6]*byte
|
||||
Pcb int64 /* pcb */
|
||||
Kstack int64
|
||||
Udata int64
|
||||
Tdaddr int64 /* thread */
|
||||
Pd int64 /* pwddesc, not accurate */
|
||||
Spareptrs [5]int64
|
||||
Sparelongs [12]int64
|
||||
Sflag int64
|
||||
Tdflags int64
|
||||
@@ -195,7 +195,7 @@ type KinfoVmentry struct {
|
||||
Vn_rdev_freebsd11 uint32
|
||||
Vn_mode uint16
|
||||
Status uint16
|
||||
Vn_fsid uint64
|
||||
Type_spec [8]byte
|
||||
Vn_rdev uint64
|
||||
X_kve_ispare [8]int32
|
||||
Path [1024]uint8
|
||||
|
||||
2
vendor/golang.org/x/sys/unix/mmap_nomremap.go
generated
vendored
2
vendor/golang.org/x/sys/unix/mmap_nomremap.go
generated
vendored
@@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build aix || darwin || dragonfly || freebsd || openbsd || solaris
|
||||
//go:build aix || darwin || dragonfly || freebsd || openbsd || solaris || zos
|
||||
|
||||
package unix
|
||||
|
||||
|
||||
8
vendor/golang.org/x/sys/unix/syscall_zos_s390x.go
generated
vendored
8
vendor/golang.org/x/sys/unix/syscall_zos_s390x.go
generated
vendored
@@ -1520,6 +1520,14 @@ func (m *mmapper) Munmap(data []byte) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func Mmap(fd int, offset int64, length int, prot int, flags int) (data []byte, err error) {
|
||||
return mapper.Mmap(fd, offset, length, prot, flags)
|
||||
}
|
||||
|
||||
func Munmap(b []byte) (err error) {
|
||||
return mapper.Munmap(b)
|
||||
}
|
||||
|
||||
func Read(fd int, p []byte) (n int, err error) {
|
||||
n, err = read(fd, p)
|
||||
if raceenabled {
|
||||
|
||||
82
vendor/golang.org/x/sys/windows/syscall_windows.go
generated
vendored
82
vendor/golang.org/x/sys/windows/syscall_windows.go
generated
vendored
@@ -165,6 +165,7 @@ func NewCallbackCDecl(fn interface{}) uintptr {
|
||||
//sys CreateFile(name *uint16, access uint32, mode uint32, sa *SecurityAttributes, createmode uint32, attrs uint32, templatefile Handle) (handle Handle, err error) [failretval==InvalidHandle] = CreateFileW
|
||||
//sys CreateNamedPipe(name *uint16, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *SecurityAttributes) (handle Handle, err error) [failretval==InvalidHandle] = CreateNamedPipeW
|
||||
//sys ConnectNamedPipe(pipe Handle, overlapped *Overlapped) (err error)
|
||||
//sys DisconnectNamedPipe(pipe Handle) (err error)
|
||||
//sys GetNamedPipeInfo(pipe Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error)
|
||||
//sys GetNamedPipeHandleState(pipe Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) = GetNamedPipeHandleStateW
|
||||
//sys SetNamedPipeHandleState(pipe Handle, state *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32) (err error) = SetNamedPipeHandleState
|
||||
@@ -348,8 +349,19 @@ func NewCallbackCDecl(fn interface{}) uintptr {
|
||||
//sys SetProcessPriorityBoost(process Handle, disable bool) (err error) = kernel32.SetProcessPriorityBoost
|
||||
//sys GetProcessWorkingSetSizeEx(hProcess Handle, lpMinimumWorkingSetSize *uintptr, lpMaximumWorkingSetSize *uintptr, flags *uint32)
|
||||
//sys SetProcessWorkingSetSizeEx(hProcess Handle, dwMinimumWorkingSetSize uintptr, dwMaximumWorkingSetSize uintptr, flags uint32) (err error)
|
||||
//sys ClearCommBreak(handle Handle) (err error)
|
||||
//sys ClearCommError(handle Handle, lpErrors *uint32, lpStat *ComStat) (err error)
|
||||
//sys EscapeCommFunction(handle Handle, dwFunc uint32) (err error)
|
||||
//sys GetCommState(handle Handle, lpDCB *DCB) (err error)
|
||||
//sys GetCommModemStatus(handle Handle, lpModemStat *uint32) (err error)
|
||||
//sys GetCommTimeouts(handle Handle, timeouts *CommTimeouts) (err error)
|
||||
//sys PurgeComm(handle Handle, dwFlags uint32) (err error)
|
||||
//sys SetCommBreak(handle Handle) (err error)
|
||||
//sys SetCommMask(handle Handle, dwEvtMask uint32) (err error)
|
||||
//sys SetCommState(handle Handle, lpDCB *DCB) (err error)
|
||||
//sys SetCommTimeouts(handle Handle, timeouts *CommTimeouts) (err error)
|
||||
//sys SetupComm(handle Handle, dwInQueue uint32, dwOutQueue uint32) (err error)
|
||||
//sys WaitCommEvent(handle Handle, lpEvtMask *uint32, lpOverlapped *Overlapped) (err error)
|
||||
//sys GetActiveProcessorCount(groupNumber uint16) (ret uint32)
|
||||
//sys GetMaximumProcessorCount(groupNumber uint16) (ret uint32)
|
||||
//sys EnumWindows(enumFunc uintptr, param unsafe.Pointer) (err error) = user32.EnumWindows
|
||||
@@ -1834,3 +1846,73 @@ func ResizePseudoConsole(pconsole Handle, size Coord) error {
|
||||
// accept arguments that can be casted to uintptr, and Coord can't.
|
||||
return resizePseudoConsole(pconsole, *((*uint32)(unsafe.Pointer(&size))))
|
||||
}
|
||||
|
||||
// DCB constants. See https://learn.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-dcb.
|
||||
const (
|
||||
CBR_110 = 110
|
||||
CBR_300 = 300
|
||||
CBR_600 = 600
|
||||
CBR_1200 = 1200
|
||||
CBR_2400 = 2400
|
||||
CBR_4800 = 4800
|
||||
CBR_9600 = 9600
|
||||
CBR_14400 = 14400
|
||||
CBR_19200 = 19200
|
||||
CBR_38400 = 38400
|
||||
CBR_57600 = 57600
|
||||
CBR_115200 = 115200
|
||||
CBR_128000 = 128000
|
||||
CBR_256000 = 256000
|
||||
|
||||
DTR_CONTROL_DISABLE = 0x00000000
|
||||
DTR_CONTROL_ENABLE = 0x00000010
|
||||
DTR_CONTROL_HANDSHAKE = 0x00000020
|
||||
|
||||
RTS_CONTROL_DISABLE = 0x00000000
|
||||
RTS_CONTROL_ENABLE = 0x00001000
|
||||
RTS_CONTROL_HANDSHAKE = 0x00002000
|
||||
RTS_CONTROL_TOGGLE = 0x00003000
|
||||
|
||||
NOPARITY = 0
|
||||
ODDPARITY = 1
|
||||
EVENPARITY = 2
|
||||
MARKPARITY = 3
|
||||
SPACEPARITY = 4
|
||||
|
||||
ONESTOPBIT = 0
|
||||
ONE5STOPBITS = 1
|
||||
TWOSTOPBITS = 2
|
||||
)
|
||||
|
||||
// EscapeCommFunction constants. See https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-escapecommfunction.
|
||||
const (
|
||||
SETXOFF = 1
|
||||
SETXON = 2
|
||||
SETRTS = 3
|
||||
CLRRTS = 4
|
||||
SETDTR = 5
|
||||
CLRDTR = 6
|
||||
SETBREAK = 8
|
||||
CLRBREAK = 9
|
||||
)
|
||||
|
||||
// PurgeComm constants. See https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-purgecomm.
|
||||
const (
|
||||
PURGE_TXABORT = 0x0001
|
||||
PURGE_RXABORT = 0x0002
|
||||
PURGE_TXCLEAR = 0x0004
|
||||
PURGE_RXCLEAR = 0x0008
|
||||
)
|
||||
|
||||
// SetCommMask constants. See https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-setcommmask.
|
||||
const (
|
||||
EV_RXCHAR = 0x0001
|
||||
EV_RXFLAG = 0x0002
|
||||
EV_TXEMPTY = 0x0004
|
||||
EV_CTS = 0x0008
|
||||
EV_DSR = 0x0010
|
||||
EV_RLSD = 0x0020
|
||||
EV_BREAK = 0x0040
|
||||
EV_ERR = 0x0080
|
||||
EV_RING = 0x0100
|
||||
)
|
||||
|
||||
24
vendor/golang.org/x/sys/windows/types_windows.go
generated
vendored
24
vendor/golang.org/x/sys/windows/types_windows.go
generated
vendored
@@ -3380,3 +3380,27 @@ type BLOB struct {
|
||||
Size uint32
|
||||
BlobData *byte
|
||||
}
|
||||
|
||||
type ComStat struct {
|
||||
Flags uint32
|
||||
CBInQue uint32
|
||||
CBOutQue uint32
|
||||
}
|
||||
|
||||
type DCB struct {
|
||||
DCBlength uint32
|
||||
BaudRate uint32
|
||||
Flags uint32
|
||||
wReserved uint16
|
||||
XonLim uint16
|
||||
XoffLim uint16
|
||||
ByteSize uint8
|
||||
Parity uint8
|
||||
StopBits uint8
|
||||
XonChar byte
|
||||
XoffChar byte
|
||||
ErrorChar byte
|
||||
EofChar byte
|
||||
EvtChar byte
|
||||
wReserved1 uint16
|
||||
}
|
||||
|
||||
126
vendor/golang.org/x/sys/windows/zsyscall_windows.go
generated
vendored
126
vendor/golang.org/x/sys/windows/zsyscall_windows.go
generated
vendored
@@ -188,6 +188,8 @@ var (
|
||||
procAssignProcessToJobObject = modkernel32.NewProc("AssignProcessToJobObject")
|
||||
procCancelIo = modkernel32.NewProc("CancelIo")
|
||||
procCancelIoEx = modkernel32.NewProc("CancelIoEx")
|
||||
procClearCommBreak = modkernel32.NewProc("ClearCommBreak")
|
||||
procClearCommError = modkernel32.NewProc("ClearCommError")
|
||||
procCloseHandle = modkernel32.NewProc("CloseHandle")
|
||||
procClosePseudoConsole = modkernel32.NewProc("ClosePseudoConsole")
|
||||
procConnectNamedPipe = modkernel32.NewProc("ConnectNamedPipe")
|
||||
@@ -212,7 +214,9 @@ var (
|
||||
procDeleteProcThreadAttributeList = modkernel32.NewProc("DeleteProcThreadAttributeList")
|
||||
procDeleteVolumeMountPointW = modkernel32.NewProc("DeleteVolumeMountPointW")
|
||||
procDeviceIoControl = modkernel32.NewProc("DeviceIoControl")
|
||||
procDisconnectNamedPipe = modkernel32.NewProc("DisconnectNamedPipe")
|
||||
procDuplicateHandle = modkernel32.NewProc("DuplicateHandle")
|
||||
procEscapeCommFunction = modkernel32.NewProc("EscapeCommFunction")
|
||||
procExitProcess = modkernel32.NewProc("ExitProcess")
|
||||
procExpandEnvironmentStringsW = modkernel32.NewProc("ExpandEnvironmentStringsW")
|
||||
procFindClose = modkernel32.NewProc("FindClose")
|
||||
@@ -236,6 +240,8 @@ var (
|
||||
procGenerateConsoleCtrlEvent = modkernel32.NewProc("GenerateConsoleCtrlEvent")
|
||||
procGetACP = modkernel32.NewProc("GetACP")
|
||||
procGetActiveProcessorCount = modkernel32.NewProc("GetActiveProcessorCount")
|
||||
procGetCommModemStatus = modkernel32.NewProc("GetCommModemStatus")
|
||||
procGetCommState = modkernel32.NewProc("GetCommState")
|
||||
procGetCommTimeouts = modkernel32.NewProc("GetCommTimeouts")
|
||||
procGetCommandLineW = modkernel32.NewProc("GetCommandLineW")
|
||||
procGetComputerNameExW = modkernel32.NewProc("GetComputerNameExW")
|
||||
@@ -322,6 +328,7 @@ var (
|
||||
procProcess32NextW = modkernel32.NewProc("Process32NextW")
|
||||
procProcessIdToSessionId = modkernel32.NewProc("ProcessIdToSessionId")
|
||||
procPulseEvent = modkernel32.NewProc("PulseEvent")
|
||||
procPurgeComm = modkernel32.NewProc("PurgeComm")
|
||||
procQueryDosDeviceW = modkernel32.NewProc("QueryDosDeviceW")
|
||||
procQueryFullProcessImageNameW = modkernel32.NewProc("QueryFullProcessImageNameW")
|
||||
procQueryInformationJobObject = modkernel32.NewProc("QueryInformationJobObject")
|
||||
@@ -335,6 +342,9 @@ var (
|
||||
procResetEvent = modkernel32.NewProc("ResetEvent")
|
||||
procResizePseudoConsole = modkernel32.NewProc("ResizePseudoConsole")
|
||||
procResumeThread = modkernel32.NewProc("ResumeThread")
|
||||
procSetCommBreak = modkernel32.NewProc("SetCommBreak")
|
||||
procSetCommMask = modkernel32.NewProc("SetCommMask")
|
||||
procSetCommState = modkernel32.NewProc("SetCommState")
|
||||
procSetCommTimeouts = modkernel32.NewProc("SetCommTimeouts")
|
||||
procSetConsoleCursorPosition = modkernel32.NewProc("SetConsoleCursorPosition")
|
||||
procSetConsoleMode = modkernel32.NewProc("SetConsoleMode")
|
||||
@@ -342,7 +352,6 @@ var (
|
||||
procSetDefaultDllDirectories = modkernel32.NewProc("SetDefaultDllDirectories")
|
||||
procSetDllDirectoryW = modkernel32.NewProc("SetDllDirectoryW")
|
||||
procSetEndOfFile = modkernel32.NewProc("SetEndOfFile")
|
||||
procSetFileValidData = modkernel32.NewProc("SetFileValidData")
|
||||
procSetEnvironmentVariableW = modkernel32.NewProc("SetEnvironmentVariableW")
|
||||
procSetErrorMode = modkernel32.NewProc("SetErrorMode")
|
||||
procSetEvent = modkernel32.NewProc("SetEvent")
|
||||
@@ -351,6 +360,7 @@ var (
|
||||
procSetFileInformationByHandle = modkernel32.NewProc("SetFileInformationByHandle")
|
||||
procSetFilePointer = modkernel32.NewProc("SetFilePointer")
|
||||
procSetFileTime = modkernel32.NewProc("SetFileTime")
|
||||
procSetFileValidData = modkernel32.NewProc("SetFileValidData")
|
||||
procSetHandleInformation = modkernel32.NewProc("SetHandleInformation")
|
||||
procSetInformationJobObject = modkernel32.NewProc("SetInformationJobObject")
|
||||
procSetNamedPipeHandleState = modkernel32.NewProc("SetNamedPipeHandleState")
|
||||
@@ -361,6 +371,7 @@ var (
|
||||
procSetStdHandle = modkernel32.NewProc("SetStdHandle")
|
||||
procSetVolumeLabelW = modkernel32.NewProc("SetVolumeLabelW")
|
||||
procSetVolumeMountPointW = modkernel32.NewProc("SetVolumeMountPointW")
|
||||
procSetupComm = modkernel32.NewProc("SetupComm")
|
||||
procSizeofResource = modkernel32.NewProc("SizeofResource")
|
||||
procSleepEx = modkernel32.NewProc("SleepEx")
|
||||
procTerminateJobObject = modkernel32.NewProc("TerminateJobObject")
|
||||
@@ -379,6 +390,7 @@ var (
|
||||
procVirtualQueryEx = modkernel32.NewProc("VirtualQueryEx")
|
||||
procVirtualUnlock = modkernel32.NewProc("VirtualUnlock")
|
||||
procWTSGetActiveConsoleSessionId = modkernel32.NewProc("WTSGetActiveConsoleSessionId")
|
||||
procWaitCommEvent = modkernel32.NewProc("WaitCommEvent")
|
||||
procWaitForMultipleObjects = modkernel32.NewProc("WaitForMultipleObjects")
|
||||
procWaitForSingleObject = modkernel32.NewProc("WaitForSingleObject")
|
||||
procWriteConsoleW = modkernel32.NewProc("WriteConsoleW")
|
||||
@@ -1641,6 +1653,22 @@ func CancelIoEx(s Handle, o *Overlapped) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func ClearCommBreak(handle Handle) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procClearCommBreak.Addr(), 1, uintptr(handle), 0, 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func ClearCommError(handle Handle, lpErrors *uint32, lpStat *ComStat) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procClearCommError.Addr(), 3, uintptr(handle), uintptr(unsafe.Pointer(lpErrors)), uintptr(unsafe.Pointer(lpStat)))
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func CloseHandle(handle Handle) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procCloseHandle.Addr(), 1, uintptr(handle), 0, 0)
|
||||
if r1 == 0 {
|
||||
@@ -1845,6 +1873,14 @@ func DeviceIoControl(handle Handle, ioControlCode uint32, inBuffer *byte, inBuff
|
||||
return
|
||||
}
|
||||
|
||||
func DisconnectNamedPipe(pipe Handle) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procDisconnectNamedPipe.Addr(), 1, uintptr(pipe), 0, 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func DuplicateHandle(hSourceProcessHandle Handle, hSourceHandle Handle, hTargetProcessHandle Handle, lpTargetHandle *Handle, dwDesiredAccess uint32, bInheritHandle bool, dwOptions uint32) (err error) {
|
||||
var _p0 uint32
|
||||
if bInheritHandle {
|
||||
@@ -1857,6 +1893,14 @@ func DuplicateHandle(hSourceProcessHandle Handle, hSourceHandle Handle, hTargetP
|
||||
return
|
||||
}
|
||||
|
||||
func EscapeCommFunction(handle Handle, dwFunc uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procEscapeCommFunction.Addr(), 2, uintptr(handle), uintptr(dwFunc), 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func ExitProcess(exitcode uint32) {
|
||||
syscall.Syscall(procExitProcess.Addr(), 1, uintptr(exitcode), 0, 0)
|
||||
return
|
||||
@@ -2058,6 +2102,22 @@ func GetActiveProcessorCount(groupNumber uint16) (ret uint32) {
|
||||
return
|
||||
}
|
||||
|
||||
func GetCommModemStatus(handle Handle, lpModemStat *uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procGetCommModemStatus.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(lpModemStat)), 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func GetCommState(handle Handle, lpDCB *DCB) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procGetCommState.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(lpDCB)), 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func GetCommTimeouts(handle Handle, timeouts *CommTimeouts) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procGetCommTimeouts.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(timeouts)), 0)
|
||||
if r1 == 0 {
|
||||
@@ -2810,6 +2870,14 @@ func PulseEvent(event Handle) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func PurgeComm(handle Handle, dwFlags uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procPurgeComm.Addr(), 2, uintptr(handle), uintptr(dwFlags), 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func QueryDosDevice(deviceName *uint16, targetPath *uint16, max uint32) (n uint32, err error) {
|
||||
r0, _, e1 := syscall.Syscall(procQueryDosDeviceW.Addr(), 3, uintptr(unsafe.Pointer(deviceName)), uintptr(unsafe.Pointer(targetPath)), uintptr(max))
|
||||
n = uint32(r0)
|
||||
@@ -2924,6 +2992,30 @@ func ResumeThread(thread Handle) (ret uint32, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func SetCommBreak(handle Handle) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procSetCommBreak.Addr(), 1, uintptr(handle), 0, 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func SetCommMask(handle Handle, dwEvtMask uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procSetCommMask.Addr(), 2, uintptr(handle), uintptr(dwEvtMask), 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func SetCommState(handle Handle, lpDCB *DCB) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procSetCommState.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(lpDCB)), 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func SetCommTimeouts(handle Handle, timeouts *CommTimeouts) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procSetCommTimeouts.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(timeouts)), 0)
|
||||
if r1 == 0 {
|
||||
@@ -2989,14 +3081,6 @@ func SetEndOfFile(handle Handle) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func SetFileValidData(handle Handle, validDataLength int64) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procSetFileValidData.Addr(), 2, uintptr(handle), uintptr(validDataLength), 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func SetEnvironmentVariable(name *uint16, value *uint16) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procSetEnvironmentVariableW.Addr(), 2, uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(value)), 0)
|
||||
if r1 == 0 {
|
||||
@@ -3060,6 +3144,14 @@ func SetFileTime(handle Handle, ctime *Filetime, atime *Filetime, wtime *Filetim
|
||||
return
|
||||
}
|
||||
|
||||
func SetFileValidData(handle Handle, validDataLength int64) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procSetFileValidData.Addr(), 2, uintptr(handle), uintptr(validDataLength), 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func SetHandleInformation(handle Handle, mask uint32, flags uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procSetHandleInformation.Addr(), 3, uintptr(handle), uintptr(mask), uintptr(flags))
|
||||
if r1 == 0 {
|
||||
@@ -3145,6 +3237,14 @@ func SetVolumeMountPoint(volumeMountPoint *uint16, volumeName *uint16) (err erro
|
||||
return
|
||||
}
|
||||
|
||||
func SetupComm(handle Handle, dwInQueue uint32, dwOutQueue uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procSetupComm.Addr(), 3, uintptr(handle), uintptr(dwInQueue), uintptr(dwOutQueue))
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func SizeofResource(module Handle, resInfo Handle) (size uint32, err error) {
|
||||
r0, _, e1 := syscall.Syscall(procSizeofResource.Addr(), 2, uintptr(module), uintptr(resInfo), 0)
|
||||
size = uint32(r0)
|
||||
@@ -3291,6 +3391,14 @@ func WTSGetActiveConsoleSessionId() (sessionID uint32) {
|
||||
return
|
||||
}
|
||||
|
||||
func WaitCommEvent(handle Handle, lpEvtMask *uint32, lpOverlapped *Overlapped) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procWaitCommEvent.Addr(), 3, uintptr(handle), uintptr(unsafe.Pointer(lpEvtMask)), uintptr(unsafe.Pointer(lpOverlapped)))
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func waitForMultipleObjects(count uint32, handles uintptr, waitAll bool, waitMilliseconds uint32) (event uint32, err error) {
|
||||
var _p0 uint32
|
||||
if waitAll {
|
||||
|
||||
7
vendor/modules.txt
vendored
7
vendor/modules.txt
vendored
@@ -1,3 +1,6 @@
|
||||
# github.com/alitto/pond v1.8.3
|
||||
## explicit; go 1.19
|
||||
github.com/alitto/pond
|
||||
# github.com/go-ole/go-ole v1.3.0
|
||||
## explicit; go 1.12
|
||||
github.com/go-ole/go-ole
|
||||
@@ -14,7 +17,7 @@ github.com/power-devops/perfstat
|
||||
# github.com/sevlyar/go-daemon v0.1.6
|
||||
## explicit
|
||||
github.com/sevlyar/go-daemon
|
||||
# github.com/shirou/gopsutil/v3 v3.24.2
|
||||
# github.com/shirou/gopsutil/v3 v3.24.3
|
||||
## explicit; go 1.15
|
||||
github.com/shirou/gopsutil/v3/common
|
||||
github.com/shirou/gopsutil/v3/cpu
|
||||
@@ -34,7 +37,7 @@ github.com/tklauser/numcpus
|
||||
# github.com/yusufpapurcu/wmi v1.2.4
|
||||
## explicit; go 1.16
|
||||
github.com/yusufpapurcu/wmi
|
||||
# golang.org/x/sys v0.18.0
|
||||
# golang.org/x/sys v0.19.0
|
||||
## explicit; go 1.18
|
||||
golang.org/x/sys/unix
|
||||
golang.org/x/sys/windows
|
||||
|
||||
Reference in New Issue
Block a user