1// Copyright (C) 2018 The Syncthing Authors. 2// 3// This Source Code Form is subject to the terms of the Mozilla Public 4// License, v. 2.0. If a copy of the MPL was not distributed with this file, 5// You can obtain one at https://mozilla.org/MPL/2.0/. 6 7// +build !android 8 9package osutil 10 11import ( 12 "os" 13 "syscall" 14 15 "github.com/pkg/errors" 16) 17 18const ioprioClassShift = 13 19 20type ioprioClass int 21 22const ( 23 ioprioClassRT ioprioClass = iota + 1 24 ioprioClassBE 25 ioprioClassIdle 26) 27 28const ( 29 ioprioWhoProcess = iota + 1 30 ioprioWhoPGRP 31 ioprioWhoUser 32) 33 34func ioprioSet(class ioprioClass, value int) error { 35 res, _, err := syscall.Syscall(syscall.SYS_IOPRIO_SET, 36 uintptr(ioprioWhoProcess), 0, 37 uintptr(class)<<ioprioClassShift|uintptr(value)) 38 if res == 0 { 39 return nil 40 } 41 return err 42} 43 44// SetLowPriority lowers the process CPU scheduling priority, and possibly 45// I/O priority depending on the platform and OS. 46func SetLowPriority() error { 47 // Process zero is "self", niceness value 9 is something between 0 48 // (default) and 19 (worst priority). But then, this is Linux, so of 49 // course we get this to take care of as well: 50 // 51 // "C library/kernel differences 52 // 53 // Within the kernel, nice values are actually represented using the 54 // range 40..1 (since negative numbers are error codes) and these are 55 // the values employed by the setpriority() and getpriority() system 56 // calls. The glibc wrapper functions for these system calls handle the 57 // translations between the user-land and kernel representations of the 58 // nice value according to the formula unice = 20 - knice. (Thus, the 59 // kernel's 40..1 range corresponds to the range -20..19 as seen by user 60 // space.)" 61 62 const ( 63 pidSelf = 0 64 wantNiceLevel = 20 - 9 65 ) 66 67 // Remember Linux kernel nice levels are upside down. 68 if cur, err := syscall.Getpriority(syscall.PRIO_PROCESS, 0); err == nil && cur <= wantNiceLevel { 69 // We're done here. 70 return nil 71 } 72 73 // Move ourselves to a new process group so that we can use the process 74 // group variants of Setpriority etc to affect all of our threads in one 75 // go. If this fails, bail, so that we don't affect things we shouldn't. 76 // If we are already the leader of our own process group, do nothing. 77 // 78 // Oh and this is because Linux doesn't follow the POSIX threading model 79 // where setting the niceness of the process would actually set the 80 // niceness of the process, instead it just affects the current thread 81 // so we need this workaround... 82 if pgid, err := syscall.Getpgid(pidSelf); err != nil { 83 // This error really shouldn't happen 84 return errors.Wrap(err, "get process group") 85 } else if pgid != os.Getpid() { 86 // We are not process group leader. Elevate! 87 if err := syscall.Setpgid(pidSelf, 0); err != nil { 88 return errors.Wrap(err, "set process group") 89 } 90 } 91 92 if err := syscall.Setpriority(syscall.PRIO_PGRP, pidSelf, wantNiceLevel); err != nil { 93 return errors.Wrap(err, "set niceness") 94 } 95 96 // Best effort, somewhere to the end of the scale (0 through 7 being the 97 // range). 98 err := ioprioSet(ioprioClassBE, 5) 99 return errors.Wrap(err, "set I/O priority") // wraps nil as nil 100} 101