Rewrite: improve process handling and restoration

This commit is contained in:
2026-08-08 14:08:38 +03:00
parent bf37dd6974
commit 4f8f6218ad
3 changed files with 486 additions and 347 deletions
+366 -293
View File
@@ -5,9 +5,11 @@ package main
import (
"flag"
"fmt"
"math"
"os"
"os/signal"
"path/filepath"
"sort"
"strconv"
"strings"
"syscall"
@@ -17,7 +19,6 @@ import (
const (
CreateToolhelp32SnapshotProcess = 0x00000002
ProcessTerminate = 0x00000001
ProcessSetInformation = 0x00000200
ProcessQueryLimitedInformation = 0x00001000
PriorityClassIdle = 0x00000040
@@ -27,6 +28,7 @@ const (
PriorityClassRealtime = 0x00000100
PriorityClassBelowNormal = 0x00004000
ErrorNoMoreFiles = 18
ErrorAlreadyExists = 183
)
type processEntry32 struct {
@@ -43,43 +45,61 @@ type processEntry32 struct {
}
type processMemoryCounters struct {
CBM uint32
PageFaultCount uint32
PeakWorkingSetSize uintptr
WorkingSetSize uintptr
QuotaPeakPagedPoolUsage uintptr
QuotaPagedPoolUsage uintptr
QuotaPeakNonPagedPoolUsage uintptr
QuotaNonPagedPoolUsage uintptr
PeakPagefileUsage uintptr
PagefileUsage uintptr
PrivateUsage uintptr
CBM uint32
PageFaultCount uint32
PeakWorkingSetSize uintptr
WorkingSetSize uintptr
QuotaPeakPagedPoolUsage uintptr
QuotaPagedPoolUsage uintptr
QuotaPeakNonPagedPoolUsage uintptr
QuotaNonPagedPoolUsage uintptr
PeakPagefileUsage uintptr
PagefileUsage uintptr
PrivateUsage uintptr
}
type processKey struct {
PID uint32
Created uint64
}
type procInfo struct {
PID uint32
Name string
RSS uint64
PID uint32
Name string
RSS uint64
Key processKey
Prio uint32
Handle syscall.Handle
CanSet bool
Keep bool
}
type trackedProc struct {
name string
handle syscall.Handle
}
type savedPrio struct {
name string
prio uint32
name string
prio uint32
handle syscall.Handle
}
var (
k32 = syscall.NewLazyDLL("kernel32.dll")
ps = syscall.NewLazyDLL("psapi.dll")
procCreateSnap = k32.NewProc("CreateToolhelp32Snapshot")
procProcess32First = k32.NewProc("Process32FirstW")
procProcess32Next = k32.NewProc("Process32NextW")
procGetMemInfo = ps.NewProc("GetProcessMemoryInfo")
procSetPriority = k32.NewProc("SetPriorityClass")
procGetPriority = k32.NewProc("GetPriorityClass")
procOpenProcess = k32.NewProc("OpenProcess")
procCloseHandle = k32.NewProc("CloseHandle")
procTerminate = k32.NewProc("TerminateProcess")
procCreateSnap = k32.NewProc("CreateToolhelp32Snapshot")
procProcess32First = k32.NewProc("Process32FirstW")
procProcess32Next = k32.NewProc("Process32NextW")
procGetMemInfo = ps.NewProc("GetProcessMemoryInfo")
procSetPriority = k32.NewProc("SetPriorityClass")
procGetPriority = k32.NewProc("GetPriorityClass")
procOpenProcess = k32.NewProc("OpenProcess")
procCloseHandle = k32.NewProc("CloseHandle")
procGetProcessTimes = k32.NewProc("GetProcessTimes")
procQueryImageName = k32.NewProc("QueryFullProcessImageNameW")
procCreateMutex = k32.NewProc("CreateMutexW")
)
func closeH(h syscall.Handle) {
@@ -94,12 +114,7 @@ func openProc(pid uint32, acc uint32) (syscall.Handle, error) {
return syscall.Handle(r), nil
}
func setPrio(pid uint32, cls uint32) error {
h, err := openProc(pid, ProcessSetInformation)
if err != nil {
return err
}
defer closeH(h)
func setPrio(h syscall.Handle, pid uint32, cls uint32) error {
r, _, e := procSetPriority.Call(uintptr(h), uintptr(cls))
if r == 0 {
return fmt.Errorf("SetPriorityClass(%d) failed: %w", pid, e)
@@ -107,13 +122,44 @@ func setPrio(pid uint32, cls uint32) error {
return nil
}
func utf16Trim(p []uint16) string {
for i, v := range p {
if v == 0 {
return syscall.UTF16ToString(p[:i])
}
func processCreated(h syscall.Handle) (uint64, error) {
var created, exited, kernel, user syscall.Filetime
r, _, e := procGetProcessTimes.Call(
uintptr(h),
uintptr(unsafe.Pointer(&created)),
uintptr(unsafe.Pointer(&exited)),
uintptr(unsafe.Pointer(&kernel)),
uintptr(unsafe.Pointer(&user)),
)
if r == 0 {
return 0, fmt.Errorf("GetProcessTimes failed: %w", e)
}
return syscall.UTF16ToString(p)
return uint64(created.HighDateTime)<<32 | uint64(created.LowDateTime), nil
}
func processName(h syscall.Handle) (string, error) {
buf := make([]uint16, 32768)
size := uint32(len(buf))
r, _, e := procQueryImageName.Call(uintptr(h), 0, uintptr(unsafe.Pointer(&buf[0])), uintptr(unsafe.Pointer(&size)))
if r == 0 {
return "", fmt.Errorf("QueryFullProcessImageName failed: %w", e)
}
return filepath.Base(syscall.UTF16ToString(buf[:size])), nil
}
func processAlive(h syscall.Handle) (bool, error) {
var created, exited, kernel, user syscall.Filetime
r, _, e := procGetProcessTimes.Call(
uintptr(h),
uintptr(unsafe.Pointer(&created)),
uintptr(unsafe.Pointer(&exited)),
uintptr(unsafe.Pointer(&kernel)),
uintptr(unsafe.Pointer(&user)),
)
if r == 0 {
return false, fmt.Errorf("GetProcessTimes failed: %w", e)
}
return exited.HighDateTime == 0 && exited.LowDateTime == 0, nil
}
func parseMemSize(s string) (uint64, error) {
@@ -121,32 +167,26 @@ func parseMemSize(s string) (uint64, error) {
if s == "" {
return 0, fmt.Errorf("empty value")
}
var suffix byte
multiplier := uint64(1)
switch s[len(s)-1] {
case 'k', 'K':
suffix = 'K'
multiplier = 1024
s = s[:len(s)-1]
case 'm', 'M':
suffix = 'M'
multiplier = 1024 * 1024
s = s[:len(s)-1]
case 'g', 'G':
suffix = 'G'
multiplier = 1024 * 1024 * 1024
s = s[:len(s)-1]
}
n, err := strconv.ParseUint(s, 10, 64)
if err != nil {
return 0, err
}
switch suffix {
case 'K':
return n * 1024, nil
case 'M':
return n * 1024 * 1024, nil
case 'G':
return n * 1024 * 1024 * 1024, nil
default:
return n, nil
if n > math.MaxUint64/multiplier {
return 0, fmt.Errorf("value overflows uint64")
}
return n * multiplier, nil
}
func formatMemSize(b uint64) string {
@@ -165,6 +205,22 @@ func isAboveNormal(c uint32) bool {
c == PriorityClassRealtime
}
func desiredPriority(rss, mem uint64, current uint32, gameMode, isGame bool) uint32 {
if gameMode {
if isGame {
return PriorityClassHigh
}
return PriorityClassIdle
}
if rss >= mem {
return PriorityClassHigh
}
if isAboveNormal(current) {
return PriorityClassNormal
}
return 0
}
func prioName(c uint32) string {
switch c {
case PriorityClassIdle:
@@ -186,7 +242,7 @@ func prioName(c uint32) string {
func allProcs() ([]procInfo, error) {
snap, _, e := procCreateSnap.Call(CreateToolhelp32SnapshotProcess, 0)
if snap == 0 {
if snap == ^uintptr(0) {
return nil, fmt.Errorf("CreateToolhelp32Snapshot failed: %w", e)
}
defer closeH(syscall.Handle(snap))
@@ -199,7 +255,7 @@ func allProcs() ([]procInfo, error) {
var out []procInfo
for {
name := utf16Trim(pe.ExeFile[:])
name := syscall.UTF16ToString(pe.ExeFile[:])
if name != "" {
out = append(out, procInfo{PID: pe.PID, Name: name})
}
@@ -215,22 +271,16 @@ func allProcs() ([]procInfo, error) {
return out, nil
}
func killOtherInstances() {
myPID := os.Getpid()
procs, err := allProcs()
func singleInstance() (syscall.Handle, bool, error) {
name, err := syscall.UTF16PtrFromString(`Local\autoPriority`)
if err != nil {
return
return 0, false, err
}
myName := filepath.Base(os.Args[0])
for _, p := range procs {
if p.PID != uint32(myPID) && filepath.Base(p.Name) == myName {
h, err := openProc(p.PID, ProcessTerminate)
if err == nil {
procTerminate.Call(uintptr(h), 0)
closeH(h)
}
}
r, _, e := procCreateMutex.Call(0, 0, uintptr(unsafe.Pointer(name)))
if r == 0 {
return 0, false, fmt.Errorf("CreateMutex failed: %w", e)
}
return syscall.Handle(r), e == syscall.Errno(ErrorAlreadyExists), nil
}
func main() {
@@ -245,7 +295,10 @@ func main() {
}
mem, err := parseMemSize(*memStr)
if err != nil {
if err != nil || mem == 0 {
if err == nil {
err = fmt.Errorf("must be greater than zero")
}
fmt.Fprintf(os.Stderr, "autoPriority: invalid -mem value: %v\n", err)
os.Exit(1)
}
@@ -260,72 +313,195 @@ func main() {
os.Exit(1)
}
killOtherInstances()
time.Sleep(100 * time.Millisecond)
mutex, exists, err := singleInstance()
if err != nil {
fmt.Fprintf(os.Stderr, "autoPriority: %v\n", err)
os.Exit(1)
}
if exists {
closeH(mutex)
fmt.Fprintln(os.Stderr, "autoPriority: already running")
return
}
defer closeH(mutex)
var logFile *os.File
logPath := filepath.Join(os.TempDir(), "autopriority.log")
os.Remove(logPath)
f, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_EXCL, 0644)
if err == nil {
logFile = f
defer logFile.Close()
}
ts := func() string {
return time.Now().Format("02.01.2006 15:04:05")
logFile, err := os.Create(logPath)
if err != nil {
fmt.Fprintf(os.Stderr, "autoPriority: cannot create log: %v\n", err)
os.Exit(1)
}
defer logFile.Close()
logf := func(format string, a ...any) {
if logFile == nil {
return
}
fmt.Fprintf(logFile, "[%s] ", ts())
fmt.Fprintf(logFile, "[%s] ", time.Now().Format("02.01.2006 15:04:05"))
fmt.Fprintf(logFile, format+"\n", a...)
logFile.Sync()
}
if gameMem > 0 {
logf("autoPriority started (mem=%s, game-mem=%s, interval=%s, dry-run=%v)",
formatMemSize(mem), formatMemSize(gameMem), *interval, *dryRun)
} else {
logf("autoPriority started (mem=%s, interval=%s, dry-run=%v)",
formatMemSize(mem), *interval, *dryRun)
logf("autoPriority started (mem=%s, game-mem=%s, interval=%s, dry-run=%v)",
formatMemSize(mem), formatMemSize(gameMem), *interval, *dryRun)
if !*dryRun {
pid := uint32(os.Getpid())
h, err := openProc(pid, ProcessSetInformation)
if err == nil {
err = setPrio(h, pid, PriorityClassIdle)
closeH(h)
}
if err != nil {
logf("warning: could not set own priority to IDLE: %v", err)
} else {
logf("own priority set to IDLE")
}
}
if err := setPrio(uint32(os.Getpid()), PriorityClassIdle); err != nil {
logf("warning: could not set own priority to IDLE: %v", err)
} else {
logf("own priority set to IDLE")
}
blocked := make(map[uint32]string)
gameProcs := make(map[uint32]string)
gameSaved := make(map[uint32]savedPrio)
blocked := make(map[processKey]string)
unreadable := make(map[uint32]string)
gameProcs := make(map[processKey]trackedProc)
gameSaved := make(map[processKey]savedPrio)
gameMode := false
myPID := uint32(os.Getpid())
defer func() {
if gameMode {
restoreProcs, _ := allProcs()
rm := make(map[uint32]string, len(restoreProcs))
for _, p := range restoreProcs {
rm[p.PID] = p.Name
readProcs := func() ([]procInfo, error) {
procs, err := allProcs()
if err != nil {
return nil, err
}
live := make(map[uint32]string, len(procs))
list := make([]procInfo, 0, len(procs))
for i := range procs {
p := &procs[i]
live[p.PID] = p.Name
if p.PID == myPID || p.PID == 0 {
continue
}
for pid, sv := range gameSaved {
if cur, ok := rm[pid]; !ok || cur != sv.name {
h, setErr := openProc(p.PID, ProcessQueryLimitedInformation|ProcessSetInformation)
p.CanSet = setErr == nil
openErr := error(nil)
if !p.CanSet {
h, openErr = openProc(p.PID, ProcessQueryLimitedInformation)
}
if openErr != nil {
if unreadable[p.PID] != p.Name {
unreadable[p.PID] = p.Name
logf("SKIP %s (PID %d): %v", p.Name, p.PID, openErr)
}
continue
}
name, readErr := processName(h)
if readErr == nil {
p.Name = name
}
created := uint64(0)
if readErr == nil {
created, readErr = processCreated(h)
}
var m processMemoryCounters
if readErr == nil {
m.CBM = uint32(unsafe.Sizeof(m))
r, _, e := procGetMemInfo.Call(uintptr(h), uintptr(unsafe.Pointer(&m)), uintptr(unsafe.Sizeof(m)))
if r == 0 {
readErr = fmt.Errorf("GetProcessMemoryInfo failed: %w", e)
}
}
var cur uintptr
if readErr == nil {
r, _, e := procGetPriority.Call(uintptr(h))
cur = r
if cur == 0 {
readErr = fmt.Errorf("GetPriorityClass failed: %w", e)
}
}
if readErr != nil {
closeH(h)
if unreadable[p.PID] != p.Name {
unreadable[p.PID] = p.Name
logf("SKIP %s (PID %d): %v", p.Name, p.PID, readErr)
}
continue
}
delete(unreadable, p.PID)
p.Key = processKey{PID: p.PID, Created: created}
p.RSS = uint64(m.WorkingSetSize)
p.Prio = uint32(cur)
p.Handle = h
if !p.CanSet {
if _, ok := blocked[p.Key]; !ok {
blocked[p.Key] = p.Name
logf("BLOCK %s (PID %d): %v (priority changes disabled)", p.Name, p.PID, setErr)
}
}
list = append(list, *p)
}
for pid, name := range unreadable {
if live[pid] != name {
delete(unreadable, pid)
}
}
return list, nil
}
restore := func(list []procInfo) {
current := make(map[processKey]*procInfo, len(list))
for i := range list {
current[list[i].Key] = &list[i]
}
for key, saved := range gameSaved {
alive, err := processAlive(saved.handle)
if err != nil {
logf("RESTORE %s (PID %d) status error: %v", saved.name, key.PID, err)
continue
}
if !alive {
closeH(saved.handle)
delete(gameSaved, key)
continue
}
cur, _, e := procGetPriority.Call(uintptr(saved.handle))
if cur == 0 {
logf("RESTORE %s (PID %d) priority error: %v", saved.name, key.PID, e)
continue
}
if uint32(cur) != saved.prio {
if err := setPrio(saved.handle, key.PID, saved.prio); err != nil {
logf("RESTORE %s (PID %d) -> %s error: %v", saved.name, key.PID, prioName(saved.prio), err)
continue
}
if err := setPrio(pid, sv.prio); err != nil {
logf("RESTORE %s (PID %d) -> %s error: %v", sv.name, pid, prioName(sv.prio), err)
} else {
logf("RESTORE %s (PID %d) -> %s", sv.name, pid, prioName(sv.prio))
}
logf("RESTORE %s (PID %d) -> %s", saved.name, key.PID, prioName(saved.prio))
}
if p := current[key]; p != nil {
p.Prio = saved.prio
}
closeH(saved.handle)
delete(gameSaved, key)
}
}
defer func() {
for attempt := 0; attempt < 3 && len(gameSaved) > 0; attempt++ {
restore(nil)
if len(gameSaved) > 0 {
time.Sleep(50 * time.Millisecond)
}
}
if len(gameSaved) > 0 {
logf("warning: %d process priorities could not be restored", len(gameSaved))
for _, saved := range gameSaved {
closeH(saved.handle)
}
}
for _, game := range gameProcs {
closeH(game.handle)
}
logf("autoPriority stopped")
logFile.Sync()
}()
myPID := uint32(os.Getpid())
ticker := time.NewTicker(*interval)
defer ticker.Stop()
@@ -333,219 +509,116 @@ func main() {
signal.Notify(stop, os.Interrupt, syscall.SIGTERM)
scan := func() {
procs, err := allProcs()
list, err := readProcs()
if err != nil {
logf("allProcs error: %v", err)
logf("process scan error: %v", err)
return
}
defer func() {
for _, p := range list {
if !p.Keep {
closeH(p.Handle)
}
}
}()
pidMap := make(map[uint32]string, len(procs))
for _, p := range procs {
pidMap[p.PID] = p.Name
current := make(map[processKey]string, len(list))
for _, p := range list {
current[p.Key] = p.Name
}
for pid := range blocked {
if _, ok := pidMap[pid]; !ok {
delete(blocked, pid)
for key, name := range blocked {
if current[key] != name {
delete(blocked, key)
}
}
for pid, name := range gameProcs {
if cur, ok := pidMap[pid]; !ok || cur != name {
delete(gameProcs, pid)
}
}
for pid, sv := range gameSaved {
if cur, ok := pidMap[pid]; !ok || cur != sv.name {
delete(gameSaved, pid)
}
}
var list []procInfo
for i := range procs {
p := &procs[i]
if p.PID == myPID || p.PID == 0 {
continue
}
h, err := openProc(p.PID, ProcessQueryLimitedInformation)
for key, game := range gameProcs {
alive, err := processAlive(game.handle)
if err != nil {
if _, ok := blocked[p.PID]; !ok {
blocked[p.PID] = p.Name
logf("BLOCK %s (PID %d): %v (added to exclusion list)", p.Name, p.PID, err)
}
logf("GAME %s (PID %d) status error: %v", game.name, key.PID, err)
continue
}
var m processMemoryCounters
m.CBM = uint32(unsafe.Sizeof(m))
r, _, e := procGetMemInfo.Call(uintptr(h), uintptr(unsafe.Pointer(&m)), uintptr(unsafe.Sizeof(m)))
closeH(h)
if r == 0 {
if _, ok := blocked[p.PID]; !ok {
logf("GetProcessMemoryInfo(%d) error: %v", p.PID, e)
}
continue
if !alive {
closeH(game.handle)
delete(gameProcs, key)
}
}
for key, saved := range gameSaved {
alive, err := processAlive(saved.handle)
if err == nil && !alive {
closeH(saved.handle)
delete(gameSaved, key)
}
}
p.RSS = uint64(m.WorkingSetSize)
list = append(list, *p)
for i := range list {
p := &list[i]
if gameMem > 0 && p.RSS >= gameMem {
if _, ok := gameProcs[p.PID]; !ok {
gameProcs[p.PID] = p.Name
if _, ok := gameProcs[p.Key]; !ok {
gameProcs[p.Key] = trackedProc{name: p.Name, handle: p.Handle}
p.Keep = true
logf("GAME DETECT %s (PID %d) RSS=%s", p.Name, p.PID, formatMemSize(p.RSS))
}
}
}
hasGame := gameMem > 0 && len(gameProcs) > 0
enteringGame := hasGame && !gameMode
if enteringGame {
gameMode = true
}
if !hasGame && gameMode {
restore(list)
if len(gameSaved) == 0 {
gameMode = false
logf("GAME MODE OFF")
} else {
return
}
}
if hasGame {
sort.SliceStable(list, func(i, j int) bool {
_, iGame := gameProcs[list[i].Key]
_, jGame := gameProcs[list[j].Key]
return iGame && !jGame
})
}
for i := range list {
p := &list[i]
_, isBlocked := blocked[p.PID]
_, isGame := gameProcs[p.PID]
if hasGame && isGame {
if isBlocked {
continue
}
if *dryRun {
logf("[DRY-RUN] %s (PID %d) RSS=%s — would set HIGH (game)", p.Name, p.PID, formatMemSize(p.RSS))
continue
}
h, err := openProc(p.PID, ProcessQueryLimitedInformation)
if err != nil {
continue
}
cur, _, _ := procGetPriority.Call(uintptr(h))
closeH(h)
if cur != 0 && uint32(cur) == PriorityClassHigh {
continue
}
if err := setPrio(p.PID, PriorityClassHigh); err != nil {
blocked[p.PID] = p.Name
logf("BLOCK %s (PID %d): %v (added to exclusion list)", p.Name, p.PID, err)
logf("GAME %s (PID %d) RSS=%s — tracked as game (priority change failed)", p.Name, p.PID, formatMemSize(p.RSS))
} else {
logf("GAME %s (PID %d) RSS=%s -> HIGH", p.Name, p.PID, formatMemSize(p.RSS))
time.Sleep(100 * time.Millisecond)
}
continue
_, isBlocked := blocked[p.Key]
_, isGame := gameProcs[p.Key]
target := desiredPriority(p.RSS, mem, p.Prio, hasGame, isGame)
action := "GAME"
if !hasGame && target == PriorityClassHigh {
action = "PROMOTE"
} else if !hasGame {
action = "DEMOTE"
}
if hasGame {
if isBlocked {
continue
}
if *dryRun {
logf("[DRY-RUN] %s (PID %d) RSS=%s — would set IDLE (game)", p.Name, p.PID, formatMemSize(p.RSS))
continue
}
h, err := openProc(p.PID, ProcessQueryLimitedInformation)
if err != nil {
continue
}
cur, _, _ := procGetPriority.Call(uintptr(h))
closeH(h)
if cur == 0 || uint32(cur) == PriorityClassIdle {
continue
}
if err := setPrio(p.PID, PriorityClassIdle); err != nil {
blocked[p.PID] = p.Name
logf("BLOCK %s (PID %d): %v (added to exclusion list)", p.Name, p.PID, err)
} else {
if _, saved := gameSaved[p.PID]; !saved {
gameSaved[p.PID] = savedPrio{name: p.Name, prio: uint32(cur)}
}
logf("GAME %s (PID %d) RSS=%s, %s -> IDLE", p.Name, p.PID, formatMemSize(p.RSS), prioName(uint32(cur)))
time.Sleep(100 * time.Millisecond)
}
if target == 0 || target == p.Prio || isBlocked {
continue
}
if p.RSS < mem {
if isBlocked {
continue
}
h, err := openProc(p.PID, ProcessQueryLimitedInformation)
if err != nil {
continue
}
cur, _, _ := procGetPriority.Call(uintptr(h))
closeH(h)
if cur == 0 || !isAboveNormal(uint32(cur)) {
continue
}
if *dryRun {
logf("[DRY-RUN] %s (PID %d) RSS=%s, priority=%s — would set NORMAL", p.Name, p.PID, formatMemSize(p.RSS), prioName(uint32(cur)))
continue
}
if err := setPrio(p.PID, PriorityClassNormal); err != nil {
blocked[p.PID] = p.Name
logf("BLOCK %s (PID %d): %v (added to exclusion list)", p.Name, p.PID, err)
} else {
logf("DEMOTE %s (PID %d) RSS=%s, %s -> NORMAL", p.Name, p.PID, formatMemSize(p.RSS), prioName(uint32(cur)))
time.Sleep(100 * time.Millisecond)
}
continue
}
if isBlocked {
continue
}
h, err := openProc(p.PID, ProcessQueryLimitedInformation)
if err != nil {
continue
}
cur, _, _ := procGetPriority.Call(uintptr(h))
closeH(h)
if cur != 0 && uint32(cur) == PriorityClassHigh {
continue
}
origStr := ""
if cur != 0 {
origStr = prioName(uint32(cur))
}
if *dryRun {
logf("[DRY-RUN] %s (PID %d) RSS=%s, priority=%s — would set HIGH", p.Name, p.PID, formatMemSize(p.RSS), origStr)
logf("[DRY-RUN] %s %s (PID %d) RSS=%s, %s -> %s", action, p.Name, p.PID, formatMemSize(p.RSS), prioName(p.Prio), prioName(target))
continue
}
if err := setPrio(p.PID, PriorityClassHigh); err != nil {
blocked[p.PID] = p.Name
logf("BLOCK %s (PID %d): %v (added to exclusion list)", p.Name, p.PID, err)
} else {
logf("PROMOTE %s (PID %d) RSS=%s, %s -> HIGH", p.Name, p.PID, formatMemSize(p.RSS), origStr)
time.Sleep(100 * time.Millisecond)
if err := setPrio(p.Handle, p.PID, target); err != nil {
blocked[p.Key] = p.Name
logf("BLOCK %s (PID %d): %v (priority changes disabled)", p.Name, p.PID, err)
continue
}
}
if gameMem > 0 {
newHasGame := len(gameProcs) > 0
if newHasGame && !gameMode {
gameMode = true
logf("GAME MODE ON")
}
if !newHasGame && gameMode {
gameMode = false
logf("GAME MODE OFF")
for pid, sv := range gameSaved {
if cur, ok := pidMap[pid]; !ok || cur != sv.name {
delete(gameSaved, pid)
continue
}
if *dryRun {
logf("[DRY-RUN] RESTORE %s (PID %d) -> %s", sv.name, pid, prioName(sv.prio))
} else {
if err := setPrio(pid, sv.prio); err != nil {
logf("RESTORE %s (PID %d) -> %s error: %v", sv.name, pid, prioName(sv.prio), err)
} else {
logf("RESTORE %s (PID %d) -> %s", sv.name, pid, prioName(sv.prio))
time.Sleep(100 * time.Millisecond)
}
}
delete(gameSaved, pid)
if hasGame && !isGame && target == PriorityClassIdle {
if _, saved := gameSaved[p.Key]; !saved {
gameSaved[p.Key] = savedPrio{name: p.Name, prio: p.Prio, handle: p.Handle}
p.Keep = true
}
}
logf("%s %s (PID %d) RSS=%s, %s -> %s", action, p.Name, p.PID, formatMemSize(p.RSS), prioName(p.Prio), prioName(target))
p.Prio = target
}
if enteringGame {
logf("GAME MODE ON")
}
}