[PATCH v2] Cygwin: proc: Cache system process information
Takashi Yano
takashi.yano@nifty.ne.jp
Mon Sep 7 11:54:46 GMT 2026
On Sun, 6 Sep 2026 22:28:36 -0700
Mark Geisert <mark@maxrnd.com> wrote:
> Hi Takashi,
>
> This v2 patch LGTM. Thanks for investigating!
Thanks! Pushed.
> Regards,
>
> ..mark
>
> On 9/6/2026 4:10 AM, Takashi Yano wrote:
> > If a process reads /proc/<pid>/stat for multiple PIDs, current code
> > every time calls NtQuerySystemInformation(SystemProcessInformation).
> > In addition, the buffer size is estimated by calling that function
> > with increasing buffer size. This takes 15 - 20 msec. This looks
> > short time, however it is not negligible in some situation. If the
> > number of processes is 100, reading /proc/<pid>/stat takes 1.5 - 2.0
> > sec. Due to this behaviour, process pane of the btop is very heavy.
> >
> > With this patch, system process information is cached per process
> > for a short time (20 msec) to improve efficiency. The buffer is
> > allocated first time when the corresponding /proc file system is
> > read, and held until process exits. If the buffer size needed by
> > NtQuerySystemInformation() is increased, the buffer will be re-
> > allocated, however never freed. The buffer pointer and the buffer
> > size are stored in the static area, so that the number of the
> > function call is reduced much. As a result, the process pane of
> > btop becomes significantly light. It takes only ~15 msec for 100
> > processes.
> >
> > Fixes: 9ba913a56dd6 ("* fhandler_process.cc (process_listing): Add 'stat' and 'statm'.")
> > Signed-off-by: Takashi Yano <takashi.yano@nifty.ne.jp>
> > Reviewed-by: Mark Geisert <mark@maxrnd.com>
> > ---
> > winsup/cygwin/fhandler/process.cc | 22 +++++++++++++++++-----
> > 1 file changed, 17 insertions(+), 5 deletions(-)
> >
> > diff --git a/winsup/cygwin/fhandler/process.cc b/winsup/cygwin/fhandler/process.cc
> > index 537a4b370..404668661 100644
> > --- a/winsup/cygwin/fhandler/process.cc
> > +++ b/winsup/cygwin/fhandler/process.cc
> > @@ -1524,14 +1524,24 @@ get_process_state (DWORD dwProcessId, DWORD *num_threads)
> > {
> > /* This isn't really heavy magic - just go through the processes' threads
> > one by one and return a value accordingly. Errors are silently ignored. */
> > + const ULONGLONG cache_time = 20;
> > + static NO_COPY muto cache_guard;
> > NTSTATUS status;
> > - PSYSTEM_PROCESS_INFORMATION p, sp;
> > - ULONG n = 0x4000;
> > + static PSYSTEM_PROCESS_INFORMATION p = NULL;
> > + static ULONGLONG t0 = 0;
> > + PSYSTEM_PROCESS_INFORMATION sp;
> > + static ULONG n = 0x4000;
> > int state =' ';
> >
> > - p = (PSYSTEM_PROCESS_INFORMATION) malloc (n);
> > + cache_guard.init ("get_process_state")->acquire ();
> > +
> > + if (GetTickCount64 () - t0 < cache_time && p)
> > + goto reuse_query;
> > +
> > + if (!p)
> > + p = (PSYSTEM_PROCESS_INFORMATION) malloc (n);
> > if (!p)
> > - return state;
> > + goto out;
> > while (true)
> > {
> > status = NtQuerySystemInformation (SystemProcessInformation,
> > @@ -1550,6 +1560,8 @@ get_process_state (DWORD dwProcessId, DWORD *num_threads)
> > status, RtlNtStatusToDosError (status));
> > goto out;
> > }
> > + t0 = GetTickCount64 ();
> > +reuse_query:
> > state = 'Z';
> > sp = p;
> > for (;;)
> > @@ -1579,7 +1591,7 @@ get_process_state (DWORD dwProcessId, DWORD *num_threads)
> > sp = (PSYSTEM_PROCESS_INFORMATION) ((char *) sp + sp->NextEntryOffset);
> > }
> > out:
> > - free (p);
> > + cache_guard.release ();
> > return state;
> > }
> >
>
--
Takashi Yano <takashi.yano@nifty.ne.jp>
More information about the Cygwin-patches
mailing list