mirror of
https://github.com/wanghongenpin/proxypin.git
synced 2026-06-01 17:15:48 +08:00
Replace Process.run('lsof') and Process.run('ps') in the request hot
path with direct libproc syscalls (proc_listpids, proc_pidinfo,
proc_pidfdinfo, proc_pidpath) via dart:ffi.
Each Process.run on macOS goes through fork()+execvp(). In a
multi-threaded Dart VM the forked child can deadlock before exec on
mutexes that were held by other threads at fork time (POSIX fork()
only clones the calling thread, leaving cloned mutex state with no
owners in the child). Under proxy load (~500 concurrent requests),
roughly 2% of the fork()s deadlock; the orphaned children inherit the
listening proxy fd and pin it for the lifetime of the system, so the
port stays bound even after the parent app exits.
libproc syscalls do not spawn child processes, so they avoid the fork
problem entirely. They are also about an order of magnitude faster
than spawning lsof per request.
Windows and Linux paths are unchanged (independent follow-up).
Verified end-to-end on macOS 26.4 / Darwin 25.4:
- 500 concurrent curl through the proxy: 0 fork leaks (was ~2% before)
- Process icons still resolve correctly for .app bundles (Chrome,
Safari, Lark, etc.)
- 9099 frees immediately on app exit (no orphan listeners)
Fixes #763