Commit aa89b950 authored by Matthew Holt's avatar Matthew Holt

Replaced cpu directive with command line flag

parent 27fc1672
...@@ -47,9 +47,6 @@ type Config struct { ...@@ -47,9 +47,6 @@ type Config struct {
// these are executed in response to SIGINT and are blocking // these are executed in response to SIGINT and are blocking
Shutdown []func() error Shutdown []func() error
// MaxCPU is the maximum number of cores for the whole process to use
MaxCPU int
// The path to the configuration file from which this was loaded // The path to the configuration file from which this was loaded
ConfigFile string ConfigFile string
} }
......
...@@ -3,9 +3,6 @@ package config ...@@ -3,9 +3,6 @@ package config
import ( import (
"os" "os"
"os/exec" "os/exec"
"runtime"
"strconv"
"strings"
"github.com/mholt/caddy/middleware" "github.com/mholt/caddy/middleware"
) )
...@@ -74,46 +71,6 @@ func init() { ...@@ -74,46 +71,6 @@ func init() {
p.cfg.TLS = tls p.cfg.TLS = tls
return nil return nil
}, },
"cpu": func(p *parser) error {
sysCores := runtime.NumCPU()
if !p.nextArg() {
return p.argErr()
}
strNum := p.tkn()
setCPU := func(val int) {
if val < 1 {
val = 1
}
if val > sysCores {
val = sysCores
}
if val > p.cfg.MaxCPU {
p.cfg.MaxCPU = val
}
}
if strings.HasSuffix(strNum, "%") {
// Percent
var percent float32
pctStr := strNum[:len(strNum)-1]
pctInt, err := strconv.Atoi(pctStr)
if err != nil || pctInt < 1 || pctInt > 100 {
return p.err("Parse", "Invalid number '"+strNum+"' (must be a positive percentage between 1 and 100)")
}
percent = float32(pctInt) / 100
setCPU(int(float32(sysCores) * percent))
} else {
// Number
num, err := strconv.Atoi(strNum)
if err != nil || num < 0 {
return p.err("Parse", "Invalid number '"+strNum+"' (requires positive integer or percent)")
}
setCPU(num)
}
return nil
},
"startup": func(p *parser) error { "startup": func(p *parser) error {
// TODO: This code is duplicated with the shutdown directive below // TODO: This code is duplicated with the shutdown directive below
......
package main package main
import ( import (
"errors"
"flag" "flag"
"fmt" "fmt"
"log" "log"
"net" "net"
"runtime"
"strconv"
"strings"
"sync" "sync"
"github.com/mholt/caddy/config" "github.com/mholt/caddy/config"
...@@ -15,18 +19,26 @@ var ( ...@@ -15,18 +19,26 @@ var (
conf string conf string
http2 bool // TODO: temporary flag until http2 is standard http2 bool // TODO: temporary flag until http2 is standard
quiet bool quiet bool
cpu string
) )
func init() { func init() {
flag.StringVar(&conf, "conf", config.DefaultConfigFile, "the configuration file to use") flag.StringVar(&conf, "conf", config.DefaultConfigFile, "the configuration file to use")
flag.BoolVar(&http2, "http2", true, "enable HTTP/2 support") // TODO: temporary flag until http2 merged into std lib flag.BoolVar(&http2, "http2", true, "enable HTTP/2 support") // TODO: temporary flag until http2 merged into std lib
flag.BoolVar(&quiet, "quiet", false, "quiet mode (no initialization output)") flag.BoolVar(&quiet, "quiet", false, "quiet mode (no initialization output)")
flag.StringVar(&cpu, "cpu", "100%", "CPU cap")
flag.Parse() flag.Parse()
} }
func main() { func main() {
var wg sync.WaitGroup var wg sync.WaitGroup
// Set CPU cap
err := setCPU(cpu)
if err != nil {
log.Fatal(err)
}
// Load config from file // Load config from file
allConfigs, err := config.Load(conf) allConfigs, err := config.Load(conf)
if err != nil { if err != nil {
...@@ -109,3 +121,38 @@ func arrangeBindings(allConfigs []config.Config) (map[string][]config.Config, er ...@@ -109,3 +121,38 @@ func arrangeBindings(allConfigs []config.Config) (map[string][]config.Config, er
return addresses, nil return addresses, nil
} }
// setCPU parses string cpu and sets GOMAXPROCS
// according to its value. It accepts either
// a number (e.g. 3) or a percent (e.g. 50%).
func setCPU(cpu string) error {
var numCPU int
availCPU := runtime.NumCPU()
if strings.HasSuffix(cpu, "%") {
// Percent
var percent float32
pctStr := cpu[:len(cpu)-1]
pctInt, err := strconv.Atoi(pctStr)
if err != nil || pctInt < 1 || pctInt > 100 {
return errors.New("Invalid CPU value: percentage must be between 1-100")
}
percent = float32(pctInt) / 100
numCPU = int(float32(availCPU) * percent)
} else {
// Number
num, err := strconv.Atoi(cpu)
if err != nil || num < 1 {
return errors.New("Invalid CPU value: provide a number or percent greater than 0")
}
numCPU = num
}
if numCPU > availCPU {
numCPU = availCPU
}
runtime.GOMAXPROCS(numCPU)
return nil
}
...@@ -11,7 +11,6 @@ import ( ...@@ -11,7 +11,6 @@ import (
"net/http" "net/http"
"os" "os"
"os/signal" "os/signal"
"runtime"
"github.com/bradfitz/http2" "github.com/bradfitz/http2"
"github.com/mholt/caddy/config" "github.com/mholt/caddy/config"
...@@ -41,11 +40,6 @@ func New(addr string, configs []config.Config, tls bool) (*Server, error) { ...@@ -41,11 +40,6 @@ func New(addr string, configs []config.Config, tls bool) (*Server, error) {
return nil, fmt.Errorf("Cannot serve %s - host already defined for address %s", conf.Address(), s.address) return nil, fmt.Errorf("Cannot serve %s - host already defined for address %s", conf.Address(), s.address)
} }
// Use all CPUs (if needed) by default
if conf.MaxCPU == 0 {
conf.MaxCPU = runtime.NumCPU()
}
vh := virtualHost{config: conf} vh := virtualHost{config: conf}
// Build middleware stack // Build middleware stack
...@@ -73,7 +67,7 @@ func (s *Server) Serve() error { ...@@ -73,7 +67,7 @@ func (s *Server) Serve() error {
} }
for _, vh := range s.vhosts { for _, vh := range s.vhosts {
// Execute startup functions // Execute startup functions now
for _, start := range vh.config.Startup { for _, start := range vh.config.Startup {
err := start() err := start()
if err != nil { if err != nil {
...@@ -81,13 +75,8 @@ func (s *Server) Serve() error { ...@@ -81,13 +75,8 @@ func (s *Server) Serve() error {
} }
} }
// Use highest procs value across all configurations // Execute shutdown commands on exit
if vh.config.MaxCPU > 0 && vh.config.MaxCPU > runtime.GOMAXPROCS(0) {
runtime.GOMAXPROCS(vh.config.MaxCPU)
}
if len(vh.config.Shutdown) > 0 { if len(vh.config.Shutdown) > 0 {
// Execute shutdown commands on exit
go func() { go func() {
interrupt := make(chan os.Signal, 1) interrupt := make(chan os.Signal, 1)
signal.Notify(interrupt, os.Interrupt, os.Kill) // TODO: syscall.SIGQUIT? (Ctrl+\, Unix-only) signal.Notify(interrupt, os.Interrupt, os.Kill) // TODO: syscall.SIGQUIT? (Ctrl+\, Unix-only)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment