forked from tuna/tunasync
-
Notifications
You must be signed in to change notification settings - Fork 0
/
cgroup.go
127 lines (113 loc) · 2.46 KB
/
cgroup.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
package worker
import (
"bufio"
"errors"
"fmt"
"os"
"path/filepath"
"strconv"
"syscall"
"time"
"golang.org/x/sys/unix"
"github.com/codeskyblue/go-sh"
)
type cgroupHook struct {
emptyHook
basePath string
baseGroup string
created bool
subsystem string
memLimit string
}
func newCgroupHook(p mirrorProvider, basePath, baseGroup, subsystem, memLimit string) *cgroupHook {
if basePath == "" {
basePath = "/sys/fs/cgroup"
}
if baseGroup == "" {
baseGroup = "tunasync"
}
if subsystem == "" {
subsystem = "cpu"
}
return &cgroupHook{
emptyHook: emptyHook{
provider: p,
},
basePath: basePath,
baseGroup: baseGroup,
subsystem: subsystem,
}
}
func (c *cgroupHook) preExec() error {
c.created = true
if err := sh.Command("cgcreate", "-g", c.Cgroup()).Run(); err != nil {
return err
}
if c.subsystem != "memory" {
return nil
}
if c.memLimit != "" {
gname := fmt.Sprintf("%s/%s", c.baseGroup, c.provider.Name())
return sh.Command(
"cgset", "-r",
fmt.Sprintf("memory.limit_in_bytes=%s", c.memLimit),
gname,
).Run()
}
return nil
}
func (c *cgroupHook) postExec() error {
err := c.killAll()
if err != nil {
logger.Errorf("Error killing tasks: %s", err.Error())
}
c.created = false
return sh.Command("cgdelete", c.Cgroup()).Run()
}
func (c *cgroupHook) Cgroup() string {
name := c.provider.Name()
return fmt.Sprintf("%s:%s/%s", c.subsystem, c.baseGroup, name)
}
func (c *cgroupHook) killAll() error {
if !c.created {
return nil
}
name := c.provider.Name()
readTaskList := func() ([]int, error) {
taskList := []int{}
taskFile, err := os.Open(filepath.Join(c.basePath, c.subsystem, c.baseGroup, name, "tasks"))
if err != nil {
return taskList, err
}
defer taskFile.Close()
scanner := bufio.NewScanner(taskFile)
for scanner.Scan() {
pid, err := strconv.Atoi(scanner.Text())
if err != nil {
return taskList, err
}
taskList = append(taskList, pid)
}
return taskList, nil
}
for i := 0; i < 4; i++ {
if i == 3 {
return errors.New("Unable to kill all child tasks")
}
taskList, err := readTaskList()
if err != nil {
return err
}
if len(taskList) == 0 {
return nil
}
for _, pid := range taskList {
// TODO: deal with defunct processes
logger.Debugf("Killing process: %d", pid)
unix.Kill(pid, syscall.SIGKILL)
}
// sleep 10ms for the first round, and 1.01s, 2.01s, 3.01s for the rest
time.Sleep(time.Duration(i)*time.Second + 10*time.Millisecond)
}
return nil
}