diff --git a/config/config.go b/config/config.go index 548ca2660..8a9a4f187 100644 --- a/config/config.go +++ b/config/config.go @@ -47,9 +47,6 @@ type Config struct { // these are executed in response to SIGINT and are blocking 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 ConfigFile string } diff --git a/config/directives.go b/config/directives.go index 162d56bf2..687738327 100644 --- a/config/directives.go +++ b/config/directives.go @@ -3,9 +3,6 @@ package config import ( "os" "os/exec" - "runtime" - "strconv" - "strings" "github.com/mholt/caddy/middleware" ) @@ -74,46 +71,6 @@ func init() { p.cfg.TLS = tls 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 { // TODO: This code is duplicated with the shutdown directive below diff --git a/main.go b/main.go index d3faa9d93..60fe00967 100644 --- a/main.go +++ b/main.go @@ -1,10 +1,14 @@ package main import ( + "errors" "flag" "fmt" "log" "net" + "runtime" + "strconv" + "strings" "sync" "github.com/mholt/caddy/config" @@ -15,18 +19,26 @@ var ( conf string http2 bool // TODO: temporary flag until http2 is standard quiet bool + cpu string ) func init() { 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(&quiet, "quiet", false, "quiet mode (no initialization output)") + flag.StringVar(&cpu, "cpu", "100%", "CPU cap") flag.Parse() } func main() { var wg sync.WaitGroup + // Set CPU cap + err := setCPU(cpu) + if err != nil { + log.Fatal(err) + } + // Load config from file allConfigs, err := config.Load(conf) if err != nil { @@ -109,3 +121,38 @@ func arrangeBindings(allConfigs []config.Config) (map[string][]config.Config, er 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 +} diff --git a/server/server.go b/server/server.go index 76880eca5..1f9d40e35 100644 --- a/server/server.go +++ b/server/server.go @@ -11,7 +11,6 @@ import ( "net/http" "os" "os/signal" - "runtime" "github.com/bradfitz/http2" "github.com/mholt/caddy/config" @@ -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) } - // Use all CPUs (if needed) by default - if conf.MaxCPU == 0 { - conf.MaxCPU = runtime.NumCPU() - } - vh := virtualHost{config: conf} // Build middleware stack @@ -73,7 +67,7 @@ func (s *Server) Serve() error { } for _, vh := range s.vhosts { - // Execute startup functions + // Execute startup functions now for _, start := range vh.config.Startup { err := start() if err != nil { @@ -81,13 +75,8 @@ func (s *Server) Serve() error { } } - // Use highest procs value across all configurations - if vh.config.MaxCPU > 0 && vh.config.MaxCPU > runtime.GOMAXPROCS(0) { - runtime.GOMAXPROCS(vh.config.MaxCPU) - } - + // Execute shutdown commands on exit if len(vh.config.Shutdown) > 0 { - // Execute shutdown commands on exit go func() { interrupt := make(chan os.Signal, 1) signal.Notify(interrupt, os.Interrupt, os.Kill) // TODO: syscall.SIGQUIT? (Ctrl+\, Unix-only)