fix: ErrorAlreadyExists 1832->183, wrap syscall errors, signal handling, mutex cleanup, log sync
This commit is contained in:
@@ -1,2 +1,11 @@
|
||||
*.exe
|
||||
*.log
|
||||
*.exe~
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
.env
|
||||
.idea/
|
||||
.vscode/
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
# autoPriority
|
||||
|
||||
Automatically sets high CPU priority for memory-heavy processes.
|
||||
Automatically sets high CPU priority for processes exceeding a memory threshold.
|
||||
|
||||
**Windows only.**
|
||||
|
||||
## Build
|
||||
|
||||
Requires Go 1.26+.
|
||||
|
||||
```
|
||||
# Standard build (with console window)
|
||||
go build -o autopriority.exe .
|
||||
@@ -20,18 +24,25 @@ autopriority [flags]
|
||||
|
||||
| Flag | Default | Description |
|
||||
|-------------|---------------|----------------------------------|
|
||||
| `-mem` | 536870912 | Memory threshold in bytes |
|
||||
| `-interval` | 1m0s | Scan interval (min 10s) |
|
||||
| `-mem` | 536870912 | Memory threshold in bytes (512 MB) |
|
||||
| `-interval` | 1 minute | Scan interval (min 10s) |
|
||||
| `-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
|
||||
|
||||
Place a shortcut to `autopriority.exe` in:
|
||||
|
||||
```
|
||||
shell:startup
|
||||
```
|
||||
Open **Win+R**, type `shell:startup`, and press Enter. Then place a shortcut to `autopriority.exe` in the folder that opens.
|
||||
|
||||
## Dependencies
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
"time"
|
||||
@@ -20,7 +21,7 @@ const (
|
||||
PriorityClassNormal = 0x00000020
|
||||
PriorityClassHigh = 0x00000080
|
||||
MessageBoxIconWarning = 0x00000030
|
||||
ErrorAlreadyExists = 1832
|
||||
ErrorAlreadyExists = 183
|
||||
)
|
||||
|
||||
type processEntry32 struct {
|
||||
@@ -78,7 +79,7 @@ func closeH(h syscall.Handle) {
|
||||
func openProc(pid uint32, acc uint32) (syscall.Handle, error) {
|
||||
r, _, e := procOpenProcess.Call(uintptr(acc), 0, uintptr(pid))
|
||||
if r == 0 {
|
||||
return 0, e
|
||||
return 0, fmt.Errorf("OpenProcess(%d) failed: %w", pid, e)
|
||||
}
|
||||
return syscall.Handle(r), nil
|
||||
}
|
||||
@@ -91,7 +92,7 @@ func setPrio(pid uint32, cls uint32) error {
|
||||
defer closeH(h)
|
||||
r, _, e := procSetPriority.Call(uintptr(h), uintptr(cls))
|
||||
if r == 0 {
|
||||
return e
|
||||
return fmt.Errorf("SetPriorityClass(%d) failed: %w", pid, e)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -104,7 +105,7 @@ func curPrio(pid uint32) (uint32, error) {
|
||||
defer closeH(h)
|
||||
r, _, e := procGetPriority.Call(uintptr(h))
|
||||
if r == 0 {
|
||||
return 0, e
|
||||
return 0, fmt.Errorf("GetPriorityClass(%d) failed: %w", pid, e)
|
||||
}
|
||||
return uint32(r), nil
|
||||
}
|
||||
@@ -117,9 +118,10 @@ func rss(pid uint32) (uint64, error) {
|
||||
defer closeH(h)
|
||||
|
||||
var m processMemoryCounters
|
||||
m.CBM = uint32(unsafe.Sizeof(m))
|
||||
r, _, e := procGetMemInfo.Call(uintptr(h), uintptr(unsafe.Pointer(&m)), uintptr(unsafe.Sizeof(m)))
|
||||
if r == 0 {
|
||||
return 0, e
|
||||
return 0, fmt.Errorf("GetProcessMemoryInfo(%d) failed: %w", pid, e)
|
||||
}
|
||||
return uint64(m.WorkingSetSize), nil
|
||||
}
|
||||
@@ -136,14 +138,14 @@ func utf16Trim(p []uint16) string {
|
||||
func allProcs() ([]processInfo, error) {
|
||||
snap, _, e := procCreateSnap.Call(CreateToolhelp32SnapshotProcess, 0)
|
||||
if snap == 0 {
|
||||
return nil, e
|
||||
return nil, fmt.Errorf("CreateToolhelp32Snapshot failed: %w", e)
|
||||
}
|
||||
defer closeH(syscall.Handle(snap))
|
||||
|
||||
pe := processEntry32{Size: uint32(unsafe.Sizeof(processEntry32{}))}
|
||||
r, _, e := procProcess32First.Call(snap, uintptr(unsafe.Pointer(&pe)))
|
||||
if r == 0 {
|
||||
return nil, e
|
||||
return nil, fmt.Errorf("Process32First failed: %w", e)
|
||||
}
|
||||
|
||||
var out []processInfo
|
||||
@@ -152,9 +154,14 @@ func allProcs() ([]processInfo, error) {
|
||||
if 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)))
|
||||
if r == 0 {
|
||||
break
|
||||
le, _, _ := procGetLastError.Call()
|
||||
if le == 18 {
|
||||
break
|
||||
}
|
||||
return nil, fmt.Errorf("Process32Next failed: %w", e)
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
@@ -174,19 +181,22 @@ func prioName(c uint32) string {
|
||||
}
|
||||
|
||||
func isAlive(pid uint32) bool {
|
||||
h, _ := openProc(pid, ProcessQueryInformation)
|
||||
if h == 0 {
|
||||
h, err := openProc(pid, ProcessQueryInformation)
|
||||
if err != nil || h == 0 {
|
||||
return false
|
||||
}
|
||||
closeH(h)
|
||||
return true
|
||||
}
|
||||
|
||||
func ensureSingleInstance() {
|
||||
h, _, _ := procCreateMutex.Call(0, 0, uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr("Global\\autopriority_v1"))))
|
||||
func ensureSingleInstance() syscall.Handle {
|
||||
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()
|
||||
if uint32(le) == ErrorAlreadyExists {
|
||||
closeH(syscall.Handle(h))
|
||||
procMessageBox.Call(0,
|
||||
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr("autoPriority is already running."))),
|
||||
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr("autoPriority"))),
|
||||
@@ -194,6 +204,7 @@ func ensureSingleInstance() {
|
||||
)
|
||||
os.Exit(1)
|
||||
}
|
||||
return syscall.Handle(h)
|
||||
}
|
||||
|
||||
func main() {
|
||||
@@ -213,6 +224,7 @@ func main() {
|
||||
os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
|
||||
if err == nil {
|
||||
logFile = f
|
||||
defer logFile.Close()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,11 +238,13 @@ func main() {
|
||||
}
|
||||
fmt.Fprintf(logFile, "[%s] ", ts())
|
||||
fmt.Fprintf(logFile, format+"\n", a...)
|
||||
logFile.Sync()
|
||||
}
|
||||
|
||||
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 {
|
||||
logf("warning: could not set own priority to IDLE: %v", err)
|
||||
@@ -240,9 +254,6 @@ func main() {
|
||||
|
||||
promoted := make(map[uint32]string)
|
||||
|
||||
if logFile != nil {
|
||||
defer logFile.Close()
|
||||
}
|
||||
defer func() {
|
||||
for pid, name := range promoted {
|
||||
if err := setPrio(pid, PriorityClassNormal); err == nil {
|
||||
@@ -256,6 +267,9 @@ func main() {
|
||||
ticker := time.NewTicker(*interval)
|
||||
defer ticker.Stop()
|
||||
|
||||
stop := make(chan os.Signal, 1)
|
||||
signal.Notify(stop, os.Interrupt, syscall.SIGTERM)
|
||||
|
||||
scan := func() {
|
||||
procs, err := allProcs()
|
||||
if err != nil {
|
||||
@@ -311,7 +325,18 @@ func main() {
|
||||
}
|
||||
|
||||
scan()
|
||||
for range ticker.C {
|
||||
scan()
|
||||
select {
|
||||
case <-stop:
|
||||
logf("received shutdown signal")
|
||||
case <-ticker.C:
|
||||
for {
|
||||
scan()
|
||||
select {
|
||||
case <-stop:
|
||||
logf("received shutdown signal")
|
||||
return
|
||||
case <-ticker.C:
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user