Skip to content

Commit 00360a2

Browse files
Fix group priority (#339)
* fix group priority on Linux * also fix on mac os * fix typo Co-authored-by: Tim Weber <scy@scy.name> * refactoring of the priority package improve tests * remove group priority test * try creating a new group when inside a container --------- Co-authored-by: Tim Weber <scy@scy.name>
1 parent 022f9ee commit 00360a2

File tree

8 files changed

+137
-92
lines changed

8 files changed

+137
-92
lines changed

priority/check/main_linux.go

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,64 @@ package main
55
import (
66
"fmt"
77
"os"
8+
"sync"
89

910
"github.com/creativeprojects/resticprofile/priority"
11+
"golang.org/x/sys/unix"
1012
)
1113

12-
// This is only displaying the priority of the current process (for testing)
14+
const selfPID = 0
15+
1316
func main() {
14-
getPriority()
15-
getIOPriority()
17+
// run it in a go routine in case it would make a difference
18+
wg := sync.WaitGroup{}
19+
wg.Add(1)
20+
go func() {
21+
defer wg.Done()
22+
displayProcessAndGroup()
23+
displayPriority()
24+
displayIOPriority()
25+
}()
26+
wg.Wait()
27+
}
28+
29+
func displayProcessAndGroup() {
30+
fmt.Printf("Process ID: %d, Group ID: %d\n", unix.Getpid(), unix.Getpgrp())
1631
}
1732

18-
func getPriority() {
19-
pri, err := priority.GetNice()
33+
func displayPriority() {
34+
processNice, err := getProcessNice()
35+
if err != nil {
36+
fmt.Println(err)
37+
os.Exit(1)
38+
}
39+
groupNice, err := getGroupNice()
2040
if err != nil {
2141
fmt.Println(err)
2242
os.Exit(1)
2343
}
24-
fmt.Printf("Priority: %d\n", pri)
44+
fmt.Printf("Process Priority: %d, Group Priority: %d\n", processNice, groupNice)
45+
}
46+
47+
// getProcessNice returns the nice value of the current process
48+
func getProcessNice() (int, error) {
49+
pri, err := unix.Getpriority(unix.PRIO_PROCESS, selfPID)
50+
if err != nil {
51+
return 0, err
52+
}
53+
return 20 - pri, nil
54+
}
55+
56+
// GetProcessNice returns the nice value of the current process group
57+
func getGroupNice() (int, error) {
58+
pri, err := unix.Getpriority(unix.PRIO_PGRP, selfPID)
59+
if err != nil {
60+
return 0, err
61+
}
62+
return 20 - pri, nil
2563
}
2664

27-
func getIOPriority() {
65+
func displayIOPriority() {
2866
class, value, err := priority.GetIONice()
2967
if err != nil {
3068
fmt.Println(err)

priority/check/main_unix.go

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,62 @@
1-
//+build !windows,!linux
1+
//go:build !windows && !linux
22

33
package main
44

55
import (
66
"fmt"
77
"os"
8+
"sync"
89

9-
"github.com/creativeprojects/resticprofile/priority"
10+
"golang.org/x/sys/unix"
1011
)
1112

13+
const selfPID = 0
14+
1215
// This is only displaying the priority of the current process (for testing)
1316
func main() {
14-
pri, err := priority.GetNice()
17+
// run it in a go routine in case it would make a difference
18+
wg := sync.WaitGroup{}
19+
wg.Add(1)
20+
go func() {
21+
defer wg.Done()
22+
displayProcessAndGroup()
23+
displayPriority()
24+
}()
25+
wg.Wait()
26+
}
27+
28+
func displayProcessAndGroup() {
29+
fmt.Printf("Process ID: %d, Group ID: %d\n", unix.Getpid(), unix.Getpgrp())
30+
}
31+
32+
func displayPriority() {
33+
processNice, err := getProcessNice()
34+
if err != nil {
35+
fmt.Println(err)
36+
os.Exit(1)
37+
}
38+
groupNice, err := getGroupNice()
1539
if err != nil {
1640
fmt.Println(err)
1741
os.Exit(1)
1842
}
19-
fmt.Printf("Priority: %d\n", pri)
43+
fmt.Printf("Process Priority: %d, Group Priority: %d\n", processNice, groupNice)
44+
}
45+
46+
// getProcessNice returns the nice value of the current process
47+
func getProcessNice() (int, error) {
48+
pri, err := unix.Getpriority(unix.PRIO_PROCESS, selfPID)
49+
if err != nil {
50+
return 0, err
51+
}
52+
return pri, nil
53+
}
54+
55+
// GetProcessNice returns the nice value of the current process group
56+
func getGroupNice() (int, error) {
57+
pri, err := unix.Getpriority(unix.PRIO_PGRP, selfPID)
58+
if err != nil {
59+
return 0, err
60+
}
61+
return pri, nil
2062
}

priority/check/main_windows.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//+build windows
1+
//go:build windows
22

33
package main
44

priority/ioprio.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
//go:build !linux
2+
3+
package priority
4+
5+
import (
6+
"errors"
7+
)
8+
9+
// SetIONice does nothing in non-linux OS
10+
func SetIONice(class, value int) error {
11+
return errors.New("IONice is only supported on Linux")
12+
}

priority/linux.go renamed to priority/ioprio_linux.go

Lines changed: 0 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -34,50 +34,6 @@ const (
3434
IOPrioWhoUser
3535
)
3636

37-
// SetNice sets the unix "nice" value of the current process
38-
func SetNice(priority int) error {
39-
var err error
40-
// pid 0 means "self"
41-
pid := 0
42-
43-
if priority < -20 || priority > 19 {
44-
return fmt.Errorf("unexpected priority value %d", priority)
45-
}
46-
47-
currentPriority, _ := unix.Getpriority(unix.PRIO_PROCESS, 0)
48-
if currentPriority == priority {
49-
return nil
50-
}
51-
52-
// Move ourselves to a new process group so that we can use the process
53-
// group variants of Setpriority etc to affect all of our threads in one go
54-
err = unix.Setpgid(pid, 0)
55-
if err != nil {
56-
return fmt.Errorf("cannot set process group priority, restic will run with the default priority: %w", err)
57-
}
58-
59-
clog.Debugf("setting process priority to %d", priority)
60-
err = unix.Setpriority(unix.PRIO_PROCESS, pid, priority)
61-
if err != nil {
62-
return fmt.Errorf("cannot set process priority, restic will run with the default priority: %w", err)
63-
}
64-
return nil
65-
}
66-
67-
// SetClass sets the priority class of the current process
68-
func SetClass(class int) error {
69-
return SetNice(class)
70-
}
71-
72-
// GetNice returns the current nice value
73-
func GetNice() (int, error) {
74-
pri, err := unix.Getpriority(unix.PRIO_PROCESS, 0)
75-
if err != nil {
76-
return 0, err
77-
}
78-
return 20 - pri, nil
79-
}
80-
8137
// SetIONice sets the io_prio class and value
8238
func SetIONice(class, value int) error {
8339
if class < 1 || class > 3 {
@@ -106,7 +62,6 @@ func GetIONice() (IOPrioClass, int, error) {
10662
}
10763

10864
func getIOPrio(who IOPrioWho) (IOPrioClass, int, error) {
109-
11065
r1, _, errno := unix.Syscall(
11166
unix.SYS_IOPRIO_GET,
11267
uintptr(who),
@@ -122,7 +77,6 @@ func getIOPrio(who IOPrioWho) (IOPrioClass, int, error) {
12277
}
12378

12479
func setIOPrio(who IOPrioWho, class IOPrioClass, value int) error {
125-
12680
_, _, errno := unix.Syscall(
12781
unix.SYS_IOPRIO_SET,
12882
uintptr(who),

priority/prority_test.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ import (
44
"bytes"
55
"io"
66
"os/exec"
7-
"runtime"
87
"testing"
98
"time"
109

10+
"github.com/creativeprojects/resticprofile/platform"
1111
"github.com/stretchr/testify/assert"
1212
)
1313

@@ -25,10 +25,10 @@ func TestStartProcessWithPriority(t *testing.T) {
2525
t.Fatal(err)
2626
}
2727
t.Log(output)
28-
if runtime.GOOS == "windows" {
28+
if platform.IsWindows() {
2929
assert.Contains(t, output, "Priority class: NORMAL")
3030
} else {
31-
assert.Contains(t, output, "Priority: 0")
31+
assert.Contains(t, output, "Process Priority: 0")
3232
}
3333
})
3434
time.Sleep(30 * time.Millisecond)
@@ -44,10 +44,10 @@ func TestStartProcessWithPriority(t *testing.T) {
4444
t.Fatal(err)
4545
}
4646
t.Log(output)
47-
if runtime.GOOS == "windows" {
47+
if platform.IsWindows() {
4848
assert.Contains(t, output, "Priority class: BELOW_NORMAL")
4949
} else {
50-
assert.Contains(t, output, "Priority: 10")
50+
assert.Contains(t, output, "Process Priority: 10")
5151
}
5252
})
5353
time.Sleep(30 * time.Millisecond)
@@ -63,10 +63,10 @@ func TestStartProcessWithPriority(t *testing.T) {
6363
t.Fatal(err)
6464
}
6565
t.Log(output)
66-
if runtime.GOOS == "windows" {
66+
if platform.IsWindows() {
6767
assert.Contains(t, output, "Priority class: IDLE")
6868
} else {
69-
assert.Contains(t, output, "Priority: 15")
69+
assert.Contains(t, output, "Process Priority: 15")
7070
}
7171
})
7272
time.Sleep(30 * time.Millisecond)

priority/unix.go

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,53 @@
1-
//+build !windows,!linux
1+
//go:build !windows
22

33
package priority
44

55
import (
6-
"errors"
76
"fmt"
87

98
"github.com/creativeprojects/clog"
109
"golang.org/x/sys/unix"
1110
)
1211

12+
const (
13+
selfPID = 0
14+
errorMessage = "cannot set process group priority, restic will run with the default priority: %w"
15+
)
16+
1317
// SetNice sets the unix "nice" value of the current process
1418
func SetNice(priority int) error {
1519
var err error
16-
// pid 0 means "self"
17-
pid := 0
1820

1921
if priority < -20 || priority > 19 {
2022
return fmt.Errorf("unexpected priority value %d", priority)
2123
}
2224

23-
clog.Debugf("setting process priority to %d", priority)
24-
err = unix.Setpriority(unix.PRIO_PROCESS, pid, priority)
25+
clog.Debugf("setting process group priority to %d", priority)
26+
err = unix.Setpriority(unix.PRIO_PGRP, selfPID, priority)
2527
if err != nil {
26-
return fmt.Errorf("error setting process priority: %v", err)
28+
if err.Error() == "operation not permitted" {
29+
// try again after creating a new process group
30+
return setNewProcessGroup(priority)
31+
}
32+
return fmt.Errorf(errorMessage, err)
2733
}
28-
return nil
29-
}
3034

31-
// SetClass sets the priority class of the current process
32-
func SetClass(class int) error {
33-
return SetNice(class)
35+
return nil
3436
}
3537

36-
// GetNice returns the scheduler priority of the current process
37-
func GetNice() (int, error) {
38-
pri, err := unix.Getpriority(unix.PRIO_PROCESS, 0)
38+
func setNewProcessGroup(priority int) error {
39+
err := unix.Setpgid(selfPID, 0)
40+
if err != nil {
41+
return fmt.Errorf(errorMessage, err)
42+
}
43+
err = unix.Setpriority(unix.PRIO_PGRP, selfPID, priority)
3944
if err != nil {
40-
return 0, err
45+
return fmt.Errorf(errorMessage, err)
4146
}
42-
return pri, nil
47+
return nil
4348
}
4449

45-
// SetIONice does nothing in non-linux OS
46-
func SetIONice(class, value int) error {
47-
return errors.New("IONice is only supported on Linux")
50+
// SetClass sets the priority class of the current process
51+
func SetClass(class int) error {
52+
return SetNice(class)
4853
}

priority/windows.go

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
//+build windows
1+
//go:build windows
22

33
package priority
44

55
import (
6-
"errors"
76
"fmt"
87

98
"github.com/creativeprojects/clog"
@@ -83,8 +82,3 @@ func GetPriorityClassName(class uint32) string {
8382
}
8483
return fmt.Sprintf("0x%x", class)
8584
}
86-
87-
// SetIONice does nothing in Windows
88-
func SetIONice(class, value int) error {
89-
return errors.New("IONice is only supported on Linux")
90-
}

0 commit comments

Comments
 (0)