1

fix: ErrorAlreadyExists 1832->183, wrap syscall errors, signal handling, mutex cleanup, log sync

This commit is contained in:
2026-06-20 23:41:59 +03:00
parent 33dfd4b4c5
commit d4da33ca5e
5 changed files with 74 additions and 29 deletions
+9
View File
@@ -1,2 +1,11 @@
*.exe *.exe
*.log *.log
*.exe~
*.swp
*.swo
*~
.env
.idea/
.vscode/
.DS_Store
Thumbs.db
+20 -9
View File
@@ -1,9 +1,13 @@
# autoPriority # autoPriority
Automatically sets high CPU priority for memory-heavy processes. Automatically sets high CPU priority for processes exceeding a memory threshold.
**Windows only.**
## Build ## Build
Requires Go 1.26+.
``` ```
# Standard build (with console window) # Standard build (with console window)
go build -o autopriority.exe . go build -o autopriority.exe .
@@ -20,18 +24,25 @@ autopriority [flags]
| Flag | Default | Description | | Flag | Default | Description |
|-------------|---------------|----------------------------------| |-------------|---------------|----------------------------------|
| `-mem` | 536870912 | Memory threshold in bytes | | `-mem` | 536870912 | Memory threshold in bytes (512 MB) |
| `-interval` | 1m0s | Scan interval (min 10s) | | `-interval` | 1 minute | Scan interval (min 10s) |
| `-dry-run` | false | Log only, don't change priority | | `-dry-run` | false | Log only, don't change priority |
| `-log` | true | Write `autopriority.log` to `%TEMP%` |
Examples:
```
# Run with 1 GB memory threshold, scanning every 30 seconds
autopriority -mem=1073741824 -interval=30s
# Dry run — log decisions without changing anything
autopriority -dry-run
```
Log is always written to `%TEMP%\autopriority.log`.
## Auto-start ## Auto-start
Place a shortcut to `autopriority.exe` in: Open **Win+R**, type `shell:startup`, and press Enter. Then place a shortcut to `autopriority.exe` in the folder that opens.
```
shell:startup
```
## Dependencies ## Dependencies
+1 -1
View File
@@ -1,3 +1,3 @@
module autopriority module autopriority
go 1.25.0 go 1.26
View File
+42 -17
View File
@@ -6,6 +6,7 @@ import (
"flag" "flag"
"fmt" "fmt"
"os" "os"
"os/signal"
"path/filepath" "path/filepath"
"syscall" "syscall"
"time" "time"
@@ -20,7 +21,7 @@ const (
PriorityClassNormal = 0x00000020 PriorityClassNormal = 0x00000020
PriorityClassHigh = 0x00000080 PriorityClassHigh = 0x00000080
MessageBoxIconWarning = 0x00000030 MessageBoxIconWarning = 0x00000030
ErrorAlreadyExists = 1832 ErrorAlreadyExists = 183
) )
type processEntry32 struct { type processEntry32 struct {
@@ -78,7 +79,7 @@ func closeH(h syscall.Handle) {
func openProc(pid uint32, acc uint32) (syscall.Handle, error) { func openProc(pid uint32, acc uint32) (syscall.Handle, error) {
r, _, e := procOpenProcess.Call(uintptr(acc), 0, uintptr(pid)) r, _, e := procOpenProcess.Call(uintptr(acc), 0, uintptr(pid))
if r == 0 { if r == 0 {
return 0, e return 0, fmt.Errorf("OpenProcess(%d) failed: %w", pid, e)
} }
return syscall.Handle(r), nil return syscall.Handle(r), nil
} }
@@ -91,7 +92,7 @@ func setPrio(pid uint32, cls uint32) error {
defer closeH(h) defer closeH(h)
r, _, e := procSetPriority.Call(uintptr(h), uintptr(cls)) r, _, e := procSetPriority.Call(uintptr(h), uintptr(cls))
if r == 0 { if r == 0 {
return e return fmt.Errorf("SetPriorityClass(%d) failed: %w", pid, e)
} }
return nil return nil
} }
@@ -104,7 +105,7 @@ func curPrio(pid uint32) (uint32, error) {
defer closeH(h) defer closeH(h)
r, _, e := procGetPriority.Call(uintptr(h)) r, _, e := procGetPriority.Call(uintptr(h))
if r == 0 { if r == 0 {
return 0, e return 0, fmt.Errorf("GetPriorityClass(%d) failed: %w", pid, e)
} }
return uint32(r), nil return uint32(r), nil
} }
@@ -117,9 +118,10 @@ func rss(pid uint32) (uint64, error) {
defer closeH(h) defer closeH(h)
var m processMemoryCounters var m processMemoryCounters
m.CBM = uint32(unsafe.Sizeof(m))
r, _, e := procGetMemInfo.Call(uintptr(h), uintptr(unsafe.Pointer(&m)), uintptr(unsafe.Sizeof(m))) r, _, e := procGetMemInfo.Call(uintptr(h), uintptr(unsafe.Pointer(&m)), uintptr(unsafe.Sizeof(m)))
if r == 0 { if r == 0 {
return 0, e return 0, fmt.Errorf("GetProcessMemoryInfo(%d) failed: %w", pid, e)
} }
return uint64(m.WorkingSetSize), nil return uint64(m.WorkingSetSize), nil
} }
@@ -136,14 +138,14 @@ func utf16Trim(p []uint16) string {
func allProcs() ([]processInfo, error) { func allProcs() ([]processInfo, error) {
snap, _, e := procCreateSnap.Call(CreateToolhelp32SnapshotProcess, 0) snap, _, e := procCreateSnap.Call(CreateToolhelp32SnapshotProcess, 0)
if snap == 0 { if snap == 0 {
return nil, e return nil, fmt.Errorf("CreateToolhelp32Snapshot failed: %w", e)
} }
defer closeH(syscall.Handle(snap)) defer closeH(syscall.Handle(snap))
pe := processEntry32{Size: uint32(unsafe.Sizeof(processEntry32{}))} pe := processEntry32{Size: uint32(unsafe.Sizeof(processEntry32{}))}
r, _, e := procProcess32First.Call(snap, uintptr(unsafe.Pointer(&pe))) r, _, e := procProcess32First.Call(snap, uintptr(unsafe.Pointer(&pe)))
if r == 0 { if r == 0 {
return nil, e return nil, fmt.Errorf("Process32First failed: %w", e)
} }
var out []processInfo var out []processInfo
@@ -152,10 +154,15 @@ func allProcs() ([]processInfo, error) {
if name != "" { if name != "" {
out = append(out, processInfo{PID: pe.PID, Name: name}) out = append(out, processInfo{PID: pe.PID, Name: name})
} }
pe.Size = uint32(unsafe.Sizeof(processEntry32{}))
r, _, e = procProcess32Next.Call(snap, uintptr(unsafe.Pointer(&pe))) r, _, e = procProcess32Next.Call(snap, uintptr(unsafe.Pointer(&pe)))
if r == 0 { if r == 0 {
le, _, _ := procGetLastError.Call()
if le == 18 {
break break
} }
return nil, fmt.Errorf("Process32Next failed: %w", e)
}
} }
return out, nil return out, nil
} }
@@ -174,19 +181,22 @@ func prioName(c uint32) string {
} }
func isAlive(pid uint32) bool { func isAlive(pid uint32) bool {
h, _ := openProc(pid, ProcessQueryInformation) h, err := openProc(pid, ProcessQueryInformation)
if h == 0 { if err != nil || h == 0 {
return false return false
} }
closeH(h) closeH(h)
return true return true
} }
func ensureSingleInstance() { func ensureSingleInstance() syscall.Handle {
h, _, _ := procCreateMutex.Call(0, 0, uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr("Global\\autopriority_v1")))) h, _, err := procCreateMutex.Call(0, 0, uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr("Global\\autopriority_v1"))))
if h == 0 {
fmt.Fprintf(os.Stderr, "autoPriority: failed to create mutex: %v\n", err)
os.Exit(1)
}
le, _, _ := procGetLastError.Call() le, _, _ := procGetLastError.Call()
if uint32(le) == ErrorAlreadyExists { if uint32(le) == ErrorAlreadyExists {
closeH(syscall.Handle(h))
procMessageBox.Call(0, procMessageBox.Call(0,
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr("autoPriority is already running."))), uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr("autoPriority is already running."))),
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr("autoPriority"))), uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr("autoPriority"))),
@@ -194,6 +204,7 @@ func ensureSingleInstance() {
) )
os.Exit(1) os.Exit(1)
} }
return syscall.Handle(h)
} }
func main() { func main() {
@@ -213,6 +224,7 @@ func main() {
os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644) os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err == nil { if err == nil {
logFile = f logFile = f
defer logFile.Close()
} }
} }
@@ -226,11 +238,13 @@ func main() {
} }
fmt.Fprintf(logFile, "[%s] ", ts()) fmt.Fprintf(logFile, "[%s] ", ts())
fmt.Fprintf(logFile, format+"\n", a...) fmt.Fprintf(logFile, format+"\n", a...)
logFile.Sync()
} }
logf("autoPriority started (mem=%d bytes, interval=%s, dry-run=%v)", *mem, *interval, *dryRun) logf("autoPriority started (mem=%d bytes, interval=%s, dry-run=%v)", *mem, *interval, *dryRun)
ensureSingleInstance() mutex := ensureSingleInstance()
defer closeH(mutex)
if err := setPrio(uint32(os.Getpid()), PriorityClassIdle); err != nil { if err := setPrio(uint32(os.Getpid()), PriorityClassIdle); err != nil {
logf("warning: could not set own priority to IDLE: %v", err) logf("warning: could not set own priority to IDLE: %v", err)
@@ -240,9 +254,6 @@ func main() {
promoted := make(map[uint32]string) promoted := make(map[uint32]string)
if logFile != nil {
defer logFile.Close()
}
defer func() { defer func() {
for pid, name := range promoted { for pid, name := range promoted {
if err := setPrio(pid, PriorityClassNormal); err == nil { if err := setPrio(pid, PriorityClassNormal); err == nil {
@@ -256,6 +267,9 @@ func main() {
ticker := time.NewTicker(*interval) ticker := time.NewTicker(*interval)
defer ticker.Stop() defer ticker.Stop()
stop := make(chan os.Signal, 1)
signal.Notify(stop, os.Interrupt, syscall.SIGTERM)
scan := func() { scan := func() {
procs, err := allProcs() procs, err := allProcs()
if err != nil { if err != nil {
@@ -311,7 +325,18 @@ func main() {
} }
scan() scan()
for range ticker.C { select {
case <-stop:
logf("received shutdown signal")
case <-ticker.C:
for {
scan() scan()
select {
case <-stop:
logf("received shutdown signal")
return
case <-ticker.C:
}
}
} }
} }