[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