Browse Source

cmd/syncthing, lib/config, lib/osutil: Lower process priority (fixes #4628)

GitHub-Pull-Request: https://github.com/syncthing/syncthing/pull/4675
Jakob Borg 7 năm trước cách đây
mục cha
commit
c554ffccc9

+ 6 - 0
cmd/syncthing/main.go

@@ -915,6 +915,12 @@ func syncthingMain(runtimeOptions RuntimeOptions) {
 
 	cleanConfigDirectory()
 
+	if cfg.Options().SetLowPriority {
+		if err := osutil.SetLowPriority(); err != nil {
+			l.Warnln("Failed to lower process priority:", err)
+		}
+	}
+
 	code := <-stop
 
 	mainService.Stop()

+ 2 - 0
lib/config/config_test.go

@@ -76,6 +76,7 @@ func TestDefaultValues(t *testing.T) {
 		KCPUpdateIntervalMs:     25,
 		KCPFastResend:           false,
 		DefaultFolderPath:       "~",
+		SetLowPriority:          true,
 	}
 
 	cfg := New(device1)
@@ -224,6 +225,7 @@ func TestOverriddenValues(t *testing.T) {
 		KCPUpdateIntervalMs:     1000,
 		KCPFastResend:           true,
 		DefaultFolderPath:       "/media/syncthing",
+		SetLowPriority:          false,
 	}
 
 	os.Unsetenv("STNOUPGRADE")

+ 1 - 0
lib/config/optionsconfiguration.go

@@ -143,6 +143,7 @@ type OptionsConfiguration struct {
 	KCPSendWindowSize       int                     `xml:"kcpSendWindowSize" json:"kcpSendWindowSize" default:"128"`
 	KCPReceiveWindowSize    int                     `xml:"kcpReceiveWindowSize" json:"kcpReceiveWindowSize" default:"128"`
 	DefaultFolderPath       string                  `xml:"defaultFolderPath" json:"defaultFolderPath" default:"~"`
+	SetLowPriority          bool                    `xml:"setLowPriority" json:"setLowPriority" default:"true"`
 
 	DeprecatedUPnPEnabled        bool     `xml:"upnpEnabled,omitempty" json:"-"`
 	DeprecatedUPnPLeaseM         int      `xml:"upnpLeaseMinutes,omitempty" json:"-"`

+ 1 - 0
lib/config/testdata/overridenvalues.xml

@@ -44,5 +44,6 @@
         <kcpUpdateIntervalMs>1000</kcpUpdateIntervalMs>
         <kcpFastResend>true</kcpFastResend>
         <defaultFolderPath>/media/syncthing</defaultFolderPath>
+        <setLowPriority>false</setLowPriority>
     </options>
 </configuration>

+ 61 - 0
lib/osutil/lowprio_linux.go

@@ -0,0 +1,61 @@
+// Copyright (C) 2018 The Syncthing Authors.
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this file,
+// You can obtain one at https://mozilla.org/MPL/2.0/.
+
+package osutil
+
+import (
+	"syscall"
+
+	"github.com/pkg/errors"
+)
+
+const ioprioClassShift = 13
+
+type ioprioClass int
+
+const (
+	ioprioClassRT ioprioClass = iota + 1
+	ioprioClassBE
+	ioprioClassIdle
+)
+
+const (
+	ioprioWhoProcess = iota + 1
+	ioprioWhoPGRP
+	ioprioWhoUser
+)
+
+func ioprioSet(class ioprioClass, value int) error {
+	res, _, err := syscall.Syscall(syscall.SYS_IOPRIO_SET,
+		uintptr(ioprioWhoProcess), 0,
+		uintptr(class)<<ioprioClassShift|uintptr(value))
+	if res == 0 {
+		return nil
+	}
+	return err
+}
+
+// SetLowPriority lowers the process CPU scheduling priority, and possibly
+// I/O priority depending on the platform and OS.
+func SetLowPriority() error {
+	// Move ourselves to a new process group so that we can use the process
+	// group variants of Setpriority etc to affect all of our threads in one
+	// go. If this fails, bail, so that we don't affect things we shouldn't.
+	if err := syscall.Setpgid(0, 0); err != nil {
+		return errors.Wrap(err, "set process group")
+	}
+
+	// Process zero is "self", niceness value 9 is something between 0
+	// (default) and 19 (worst priority).
+	if err := syscall.Setpriority(syscall.PRIO_PGRP, 0, 9); err != nil {
+		return errors.Wrap(err, "set niceness")
+	}
+
+	// Best effort, somewhere to the end of the scale (0 through 7 being the
+	// range).
+	err := ioprioSet(ioprioClassBE, 5)
+	return errors.Wrap(err, "set I/O priority") // wraps nil as nil
+}

+ 24 - 0
lib/osutil/lowprio_unix.go

@@ -0,0 +1,24 @@
+// Copyright (C) 2018 The Syncthing Authors.
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this file,
+// You can obtain one at https://mozilla.org/MPL/2.0/.
+
+// +build !windows,!linux
+
+package osutil
+
+import (
+	"syscall"
+
+	"github.com/pkg/errors"
+)
+
+// SetLowPriority lowers the process CPU scheduling priority, and possibly
+// I/O priority depending on the platform and OS.
+func SetLowPriority() error {
+	// Process zero is "self", niceness value 9 is something between 0
+	// (default) and 19 (worst priority).
+	err := syscall.Setpriority(syscall.PRIO_PROCESS, 0, 9)
+	return errors.Wrap(err, "set niceness") // wraps nil as nil
+}

+ 47 - 0
lib/osutil/lowprio_windows.go

@@ -0,0 +1,47 @@
+// Copyright (C) 2018 The Syncthing Authors.
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this file,
+// You can obtain one at https://mozilla.org/MPL/2.0/.
+
+package osutil
+
+import (
+	"syscall"
+
+	"github.com/pkg/errors"
+)
+
+var (
+	kernel32, _         = syscall.LoadLibrary("kernel32.dll")
+	setPriorityClass, _ = syscall.GetProcAddress(kernel32, "SetPriorityClass")
+)
+
+const (
+	// https://msdn.microsoft.com/en-us/library/windows/desktop/ms686219(v=vs.85).aspx
+	aboveNormalPriorityClass   = 0x00008000
+	belowNormalPriorityClass   = 0x00004000
+	highPriorityClass          = 0x00000080
+	idlePriorityClass          = 0x00000040
+	normalPriorityClass        = 0x00000020
+	processModeBackgroundBegin = 0x00100000
+	processModeBackgroundEnd   = 0x00200000
+	realtimePriorityClass      = 0x00000100
+)
+
+// SetLowPriority lowers the process CPU scheduling priority, and possibly
+// I/O priority depending on the platform and OS.
+func SetLowPriority() error {
+	handle, err := syscall.GetCurrentProcess()
+	if err != nil {
+		return errors.Wrap(err, "get process handler")
+	}
+	defer syscall.CloseHandle(handle)
+
+	res, _, err := syscall.Syscall(uintptr(setPriorityClass), uintptr(handle), belowNormalPriorityClass, 0, 0)
+	if res != 0 {
+		// "If the function succeeds, the return value is nonzero."
+		return nil
+	}
+	return errors.Wrap(err, "set priority class") // wraps nil as nil
+}