diff --git a/lib/network/util/process_info.dart b/lib/network/util/process_info.dart index 62d1496..088d6fc 100644 --- a/lib/network/util/process_info.dart +++ b/lib/network/util/process_info.dart @@ -38,6 +38,13 @@ void main() async { class ProcessInfoUtils { static final processInfoCache = ExpiringCache(const Duration(minutes: 5)); + // (host:port) -> pid short cache. Keeps the FFI / Process.run lookup off + // the request hot path for the typical HTTP keep-alive case where many + // requests share a single client TCP connection (and thus a single + // remote socket address). Greatly reduces how often the synchronous + // libproc scan runs on the main isolate. + static final _pidCache = ExpiringCache(const Duration(seconds: 30)); + static Future getProcessByPort(InetSocketAddress socketAddress, String cacheKeyPre) async { try { if (Platform.isAndroid) { @@ -51,8 +58,13 @@ class ProcessInfoUtils { return null; } - var pid = await _getPid(socketAddress); - if (pid == null) return null; + var addrKey = "${socketAddress.host}:${socketAddress.port}"; + var pid = _pidCache.get(addrKey); + if (pid == null) { + pid = await _getPid(socketAddress); + if (pid == null) return null; + _pidCache.set(addrKey, pid); + } String cacheKey = "$cacheKeyPre:$pid"; var processInfo = processInfoCache.get(cacheKey); diff --git a/lib/network/util/process_info_macos.dart b/lib/network/util/process_info_macos.dart index b65ce3d..7ee02f8 100644 --- a/lib/network/util/process_info_macos.dart +++ b/lib/network/util/process_info_macos.dart @@ -98,6 +98,16 @@ class MacosProcessInfo { final pidBuf = calloc(pidBufSize); final sockBuf = calloc(_kSizeofSocketFdInfo); + // Reuse the same ByteData view across all fds; the underlying native + // buffer is overwritten in place by each proc_pidfdinfo call. + final sockView = ByteData.sublistView(sockBuf.asTypedList(_kSizeofSocketFdInfo)); + + // If proc_pidfdinfo keeps returning a size smaller than expected, the + // struct layout has changed (e.g. a future macOS bumped the size) and + // continuing the scan can't possibly find anything. Bail out early + // after enough consecutive mismatches to avoid wasting syscalls. + var consecutiveLayoutMismatch = 0; + const layoutMismatchThreshold = 16; try { final actual = _procListPids(_kProcAllPids, 0, pidBuf.cast(), pidBufSize); @@ -107,6 +117,7 @@ class MacosProcessInfo { for (final pid in pidView) { if (pid <= 0) continue; + if (consecutiveLayoutMismatch >= layoutMismatchThreshold) break; final fdSize = _procPidInfo(pid, _kProcPidListFds, 0, nullptr, 0); if (fdSize <= 0) continue; @@ -127,9 +138,12 @@ class MacosProcessInfo { final fd = fdView.getInt32(entryOff + _kOffProcFd, Endian.host); final n = _procPidFdInfo(pid, fd, _kProcPidFdSocketInfo, sockBuf.cast(), _kSizeofSocketFdInfo); - if (n < _kSizeofSocketFdInfo) continue; + if (n < _kSizeofSocketFdInfo) { + consecutiveLayoutMismatch++; + continue; + } + consecutiveLayoutMismatch = 0; - final sockView = ByteData.sublistView(sockBuf.asTypedList(_kSizeofSocketFdInfo)); final soiKind = sockView.getInt32(_kOffSoiKind, Endian.host); if (soiKind != _kSockInfoTcp) continue;