Quantcast

[PATCH] Implement getloadavg()

classic Classic list List threaded Threaded
12 messages Options
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

[PATCH] Implement getloadavg()

Jon TURNEY
Signed-off-by: Jon Turney <[hidden email]>
---
 winsup/cygwin/Makefile.in              |   5 +-
 winsup/cygwin/common.din               |   1 +
 winsup/cygwin/fhandler_proc.cc         |  10 ++-
 winsup/cygwin/include/cygwin/stdlib.h  |   4 +
 winsup/cygwin/include/cygwin/version.h |   3 +-
 winsup/cygwin/loadavg.cc               | 135 +++++++++++++++++++++++++++++++++
 winsup/doc/posix.xml                   |   1 +
 7 files changed, 154 insertions(+), 5 deletions(-)
 create mode 100644 winsup/cygwin/loadavg.cc

diff --git a/winsup/cygwin/Makefile.in b/winsup/cygwin/Makefile.in
index c8652b0..5e719a6 100644
--- a/winsup/cygwin/Makefile.in
+++ b/winsup/cygwin/Makefile.in
@@ -147,7 +147,9 @@ EXTRA_OFILES:=
 
 MALLOC_OFILES:=malloc.o
 
-DLL_IMPORTS:=${shell $(CC) -print-file-name=w32api/libkernel32.a} ${shell $(CC) -print-file-name=w32api/libntdll.a}
+DLL_IMPORTS:=${shell $(CC) -print-file-name=w32api/libkernel32.a} \
+ ${shell $(CC) -print-file-name=w32api/libntdll.a} \
+ ${shell $(CC) -print-file-name=w32api/libpdh.a}
 
 MT_SAFE_OBJECTS:=
 #
@@ -323,6 +325,7 @@ DLL_OFILES:= \
  kernel32.o \
  ldap.o \
  libstdcxx_wrapper.o \
+ loadavg.o \
  localtime.o \
  lsearch.o \
  malloc_wrapper.o \
diff --git a/winsup/cygwin/common.din b/winsup/cygwin/common.din
index f236813..8e54a7d 100644
--- a/winsup/cygwin/common.din
+++ b/winsup/cygwin/common.din
@@ -624,6 +624,7 @@ gethostname = cygwin_gethostname SIGFE
 getifaddrs SIGFE
 getitimer SIGFE
 getline = __getline SIGFE
+getloadavg SIGFE
 getlogin NOSIGFE
 getlogin_r NOSIGFE
 getmntent SIGFE
diff --git a/winsup/cygwin/fhandler_proc.cc b/winsup/cygwin/fhandler_proc.cc
index 2a8cf14..a7e816f 100644
--- a/winsup/cygwin/fhandler_proc.cc
+++ b/winsup/cygwin/fhandler_proc.cc
@@ -418,7 +418,7 @@ static off_t
 format_proc_loadavg (void *, char *&destbuf)
 {
   extern int get_process_state (DWORD dwProcessId);
-  unsigned running = 0;
+  unsigned int running = 0;
   winpids pids ((DWORD) 0);
 
   for (unsigned i = 0; i < pids.npids; i++)
@@ -429,9 +429,13 @@ format_proc_loadavg (void *, char *&destbuf)
  break;
     }
 
+  double loadavg[3] = { 0.0, 0.0, 0.0 };
+  getloadavg (loadavg, 3);
+
   destbuf = (char *) crealloc_abort (destbuf, 48);
-  return __small_sprintf (destbuf, "%u.%02u %u.%02u %u.%02u %u/%u\n",
-    0, 0, 0, 0, 0, 0, running, pids.npids);
+  return sprintf (destbuf, "%.2f %.2f %.2f %u/%u\n",
+  loadavg[0], loadavg[1], loadavg[2], running,
+  (unsigned int)pids.npids);
 }
 
 static off_t
diff --git a/winsup/cygwin/include/cygwin/stdlib.h b/winsup/cygwin/include/cygwin/stdlib.h
index 744a08d..a8eb4de 100644
--- a/winsup/cygwin/include/cygwin/stdlib.h
+++ b/winsup/cygwin/include/cygwin/stdlib.h
@@ -77,6 +77,10 @@ extern _PTR valloc _PARAMS ((size_t));
 #undef _mstats_r
 #define _mstats_r(r, p) mstats (p)
 
+#if __BSD_VISIBLE
+int getloadavg(double loadavg[], int nelem);
+#endif
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/winsup/cygwin/include/cygwin/version.h b/winsup/cygwin/include/cygwin/version.h
index 6ca3079..298741a 100644
--- a/winsup/cygwin/include/cygwin/version.h
+++ b/winsup/cygwin/include/cygwin/version.h
@@ -473,12 +473,13 @@ details. */
   306: Export getentropy, getrandom.
   307: Export timingsafe_bcmp, timingsafe_memcmp.
   308: Export dladdr.
+  309: Export getloadavg.
 
   Note that we forgot to bump the api for ualarm, strtoll, strtoull,
   sigaltstack, sethostname. */
 
 #define CYGWIN_VERSION_API_MAJOR 0
-#define CYGWIN_VERSION_API_MINOR 308
+#define CYGWIN_VERSION_API_MINOR 309
 
 /* There is also a compatibity version number associated with the shared memory
    regions.  It is incremented when incompatible changes are made to the shared
diff --git a/winsup/cygwin/loadavg.cc b/winsup/cygwin/loadavg.cc
new file mode 100644
index 0000000..63d8b3e
--- /dev/null
+++ b/winsup/cygwin/loadavg.cc
@@ -0,0 +1,135 @@
+/* loadavg.cc: load average support.
+
+  This file is part of Cygwin.
+
+  This software is a copyrighted work licensed under the terms of the
+  Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
+  details. */
+
+/*
+  Emulate load average
+
+  There's a fair amount of approximation done here, so don't try to use this to
+  actually measure anything, but it should be good enough for programs to
+  throttle their activity based on load.
+
+  A per-process load average estimate is maintained.  This estimate is only
+  updated at most every 5 seconds.
+
+  We attempt to count running and runnable processes, but unlike linux we don't
+  count processes in uninterruptible sleep (blocked on I/O).
+
+  The number of running processes is estimated as (NumberOfProcessors) * (%
+  Processor Time).  The number of runnable processes is estimated as
+  ProcessorQueueLength.
+
+  Note that PDH will only return data for '% Processor Time' afer the second
+  call to PdhCollectQueryData(), as it's computed over an interval, so the first
+  load estimate will always be 0.
+*/
+
+#include <math.h>
+#include <time.h>
+#include <sys/strace.h>
+
+#define _WIN32_WINNT 0x0600
+#include <pdh.h>
+
+static double _loadavg[3] = { 0.0, 0.0, 0.0 };
+static PDH_HQUERY query;
+static PDH_HCOUNTER counter1;
+static PDH_HCOUNTER counter2;
+
+static bool load_init (void)
+{
+  static bool tried = false;
+  static bool initialized = false;
+
+  if (!tried) {
+    tried = true;
+
+    if ((PdhOpenQueryA (NULL, 0, &query) == ERROR_SUCCESS) &&
+ (PdhAddEnglishCounterA (query, "\\Processor(_Total)\\% Processor Time",
+ 0, &counter1) == ERROR_SUCCESS) &&
+ (PdhAddEnglishCounterA (query, "\\System\\Processor Queue Length",
+ 0, &counter2) == ERROR_SUCCESS)) {
+      initialized = true;
+    } else {
+      debug_printf("loadavg PDH initialization failed\n");
+    }
+  }
+
+  return initialized;
+}
+
+/* estimate the current load */
+static double load (void)
+{
+  PDH_STATUS ret = PdhCollectQueryData (query);
+  if (ret != ERROR_SUCCESS)
+    return 0.0;
+
+  /* Estimate the number of running processes as (NumberOfProcessors) * (%
+     Processor Time) */
+  PDH_FMT_COUNTERVALUE fmtvalue1;
+  ret = PdhGetFormattedCounterValue (counter1, PDH_FMT_DOUBLE, NULL, &fmtvalue1);
+  if (ret != ERROR_SUCCESS)
+    return 0.0;
+
+  SYSTEM_INFO sysinfo;
+  GetSystemInfo (&sysinfo);
+
+  double running = fmtvalue1.doubleValue * sysinfo.dwNumberOfProcessors / 100;
+
+  /* Estimate the number of runnable processes using ProcessorQueueLength */
+  PDH_FMT_COUNTERVALUE fmtvalue2;
+  ret = PdhGetFormattedCounterValue (counter2, PDH_FMT_LONG, NULL, &fmtvalue2);
+  if (ret != ERROR_SUCCESS)
+    return 0.0;
+
+  LONG rql = fmtvalue2.longValue;
+
+  return rql + running;
+}
+
+static void calc_load (int index, int delta_time, int decay_time, double n)
+{
+  double df = 1.0 / exp ((double)delta_time/decay_time);
+  _loadavg[index] = (_loadavg[index] * df) + (n * (1.0 - df));
+}
+
+static void update_loadavg (int delta_time)
+{
+  if (!load_init ())
+    return;
+
+  double active_tasks = load ();
+
+  /* Compute the exponentially weighted moving average over ... */
+  calc_load (0, delta_time, 60,  active_tasks); /* ... 1 min */
+  calc_load (1, delta_time, 300, active_tasks); /* ... 5 min */
+  calc_load (2, delta_time, 900, active_tasks); /* ... 15 min */
+}
+
+/* getloadavg: BSD */
+extern "C" int
+getloadavg (double loadavg[], int nelem)
+{
+  /* Don't recalculate the load average if less than 5 seconds has elapsed since
+     the last time it was calculated */
+  static time_t last_time = 0;
+  time_t curr_time = time (NULL);
+  int delta_time = curr_time - last_time;
+  if (delta_time >= 5) {
+    last_time = curr_time;
+    update_loadavg (delta_time);
+  }
+
+  /* The maximum number of samples is 3 */
+  if (nelem > 3)
+    nelem = 3;
+
+  /* Return the samples and number of samples retrieved */
+  memcpy (loadavg, _loadavg, nelem * sizeof(double));
+  return nelem;
+}
diff --git a/winsup/doc/posix.xml b/winsup/doc/posix.xml
index 03d168d..a3b2e9f 100644
--- a/winsup/doc/posix.xml
+++ b/winsup/doc/posix.xml
@@ -1173,6 +1173,7 @@ also IEEE Std 1003.1-2008 (POSIX.1-2008).</para>
     getdtablesize
     getgrouplist
     getifaddrs
+    getloadavg
     getpagesize
     getpeereid
     getprogname
--
2.8.3

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: [PATCH] Implement getloadavg()

Jon TURNEY
On 17/03/2017 17:50, Jon Turney wrote:
>  winsup/cygwin/Makefile.in              |   5 +-
>  winsup/cygwin/common.din               |   1 +
>  winsup/cygwin/fhandler_proc.cc         |  10 ++-
>  winsup/cygwin/include/cygwin/stdlib.h  |   4 +
>  winsup/cygwin/include/cygwin/version.h |   3 +-
>  winsup/cygwin/loadavg.cc               | 135 +++++++++++++++++++++++++++++++++
>  winsup/doc/posix.xml                   |   1 +
>  7 files changed, 154 insertions(+), 5 deletions(-)
>  create mode 100644 winsup/cygwin/loadavg.cc

Note that this doesn't build for x86 at the moment, due to an issue with
pdh.h in w32api (See [1])

Furthermore, there are a few problems with this implementation:

The first time a process calls getloadavg(), it always gets 0 (so 'cat
/proc/loadavg' always shows a load of 0).  This is a consequence of
having no global state, and the fact that we can't measure %CPU
instantaneously, only over an interval.

I'm not the only person on the internet who thinks that
(%CPU*#cores)+PQL is they way to calculate a load value, and while this
gives reasonable values when when the load is between 0 and (number of
cores), the PQL seems to get unexpectedly large when CPU is saturated on
an otherwise idle system.

e.g. if I saturate the CPU with tasks of "normal" priority, PQL goes to
~20; saturate with tasks of "below normal" priority, PQL goes to ~5, but
there isn't actually any real demand there; if the CPU saturating tasks
are killed, load drops close to 0.  I don't know how to interpret that...

[1] https://sourceforge.net/p/mingw-w64/mailman/message/35727667/
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: [PATCH] Implement getloadavg()

Corinna Vinschen-2
In reply to this post by Jon TURNEY
Hi Jon,

neat!  But...

On Mar 17 17:50, Jon Turney wrote:

> Signed-off-by: Jon Turney <[hidden email]>
> ---
>  winsup/cygwin/Makefile.in              |   5 +-
>  winsup/cygwin/common.din               |   1 +
>  winsup/cygwin/fhandler_proc.cc         |  10 ++-
>  winsup/cygwin/include/cygwin/stdlib.h  |   4 +
>  winsup/cygwin/include/cygwin/version.h |   3 +-
>  winsup/cygwin/loadavg.cc               | 135 +++++++++++++++++++++++++++++++++
>  winsup/doc/posix.xml                   |   1 +
>  7 files changed, 154 insertions(+), 5 deletions(-)
>  create mode 100644 winsup/cygwin/loadavg.cc
>
> diff --git a/winsup/cygwin/Makefile.in b/winsup/cygwin/Makefile.in
> index c8652b0..5e719a6 100644
> --- a/winsup/cygwin/Makefile.in
> +++ b/winsup/cygwin/Makefile.in
> @@ -147,7 +147,9 @@ EXTRA_OFILES:=
>  
>  MALLOC_OFILES:=malloc.o
>  
> -DLL_IMPORTS:=${shell $(CC) -print-file-name=w32api/libkernel32.a} ${shell $(CC) -print-file-name=w32api/libntdll.a}
> +DLL_IMPORTS:=${shell $(CC) -print-file-name=w32api/libkernel32.a} \
> + ${shell $(CC) -print-file-name=w32api/libntdll.a} \
> + ${shell $(CC) -print-file-name=w32api/libpdh.a}
No, that's not right.  Please add the new functions to autoload.cc and
drop static linking to libpdh.a.

> +static double _loadavg[3] = { 0.0, 0.0, 0.0 };

The load average is global, non-critical data.  So what about storing it
in shared_info instead?  This way, only the first call of the first
Cygwin process returns all zero.

> +static bool load_init (void)
> +{
> +  static bool tried = false;
> +  static bool initialized = false;
> +
> +  if (!tried) {
> +    tried = true;
> +
> +    if ((PdhOpenQueryA (NULL, 0, &query) == ERROR_SUCCESS) &&
> + (PdhAddEnglishCounterA (query, "\\Processor(_Total)\\% Processor Time",
> + 0, &counter1) == ERROR_SUCCESS) &&
> + (PdhAddEnglishCounterA (query, "\\System\\Processor Queue Length",
> + 0, &counter2) == ERROR_SUCCESS)) {
> +      initialized = true;
> +    } else {
> +      debug_printf("loadavg PDH initialization failed\n");
> +    }
> +  }
> +
> +  return initialized;
> +}
How slow is that initialization?  Would it {make sense|hurt} to call it
once in the initalization of Cygwin's shared mem in shared_info::initialize?

As for the declaration problem on x86, what about moving the declarations
to the start of loadavg.cc, until we get a new w32api-headers package?

JonY, any chance for a quick update of w32api-headers to include the
patch from https://sourceforge.net/p/mingw-w64/mailman/message/35727667/?


Thanks,
Corinna

--
Corinna Vinschen                  Please, send mails regarding Cygwin to
Cygwin Maintainer                 cygwin AT cygwin DOT com
Red Hat

signature.asc (836 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: [PATCH] Implement getloadavg()

JonY
On 03/20/2017 10:37 AM, Corinna Vinschen wrote:
>
> JonY, any chance for a quick update of w32api-headers to include the
> patch from https://sourceforge.net/p/mingw-w64/mailman/message/35727667/?
>

Sure, I'll try to churn out 5.0.2 with this fix by next week.




signature.asc (875 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: [PATCH] Implement getloadavg()

Corinna Vinschen-2
On Mar 20 13:05, JonY wrote:
> On 03/20/2017 10:37 AM, Corinna Vinschen wrote:
> >
> > JonY, any chance for a quick update of w32api-headers to include the
> > patch from https://sourceforge.net/p/mingw-w64/mailman/message/35727667/?
> >
>
> Sure, I'll try to churn out 5.0.2 with this fix by next week.

Thanks!


Corinna

--
Corinna Vinschen                  Please, send mails regarding Cygwin to
Cygwin Maintainer                 cygwin AT cygwin DOT com
Red Hat

signature.asc (836 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: [PATCH] Implement getloadavg()

Jon TURNEY
In reply to this post by Corinna Vinschen-2
On 20/03/2017 10:37, Corinna Vinschen wrote:

> Hi Jon,
>
> neat!  But...
>
> On Mar 17 17:50, Jon Turney wrote:
>> Signed-off-by: Jon Turney <[hidden email]>
>> ---
>>  winsup/cygwin/Makefile.in              |   5 +-
>>  winsup/cygwin/common.din               |   1 +
>>  winsup/cygwin/fhandler_proc.cc         |  10 ++-
>>  winsup/cygwin/include/cygwin/stdlib.h  |   4 +
>>  winsup/cygwin/include/cygwin/version.h |   3 +-
>>  winsup/cygwin/loadavg.cc               | 135 +++++++++++++++++++++++++++++++++
>>  winsup/doc/posix.xml                   |   1 +
>>  7 files changed, 154 insertions(+), 5 deletions(-)
>>  create mode 100644 winsup/cygwin/loadavg.cc
>>
>> diff --git a/winsup/cygwin/Makefile.in b/winsup/cygwin/Makefile.in
>> index c8652b0..5e719a6 100644
>> --- a/winsup/cygwin/Makefile.in
>> +++ b/winsup/cygwin/Makefile.in
>> @@ -147,7 +147,9 @@ EXTRA_OFILES:=
>>
>>  MALLOC_OFILES:=malloc.o
>>
>> -DLL_IMPORTS:=${shell $(CC) -print-file-name=w32api/libkernel32.a} ${shell $(CC) -print-file-name=w32api/libntdll.a}
>> +DLL_IMPORTS:=${shell $(CC) -print-file-name=w32api/libkernel32.a} \
>> + ${shell $(CC) -print-file-name=w32api/libntdll.a} \
>> + ${shell $(CC) -print-file-name=w32api/libpdh.a}
>
> No, that's not right.  Please add the new functions to autoload.cc and
> drop static linking to libpdh.a.

Ah, I see.  Ok.

>> +static double _loadavg[3] = { 0.0, 0.0, 0.0 };
>
> The load average is global, non-critical data.  So what about storing it
> in shared_info instead?  This way, only the first call of the first
> Cygwin process returns all zero.

Ok.

>> +static bool load_init (void)
>> +{
>> +  static bool tried = false;
>> +  static bool initialized = false;
>> +
>> +  if (!tried) {
>> +    tried = true;
>> +
>> +    if ((PdhOpenQueryA (NULL, 0, &query) == ERROR_SUCCESS) &&
>> + (PdhAddEnglishCounterA (query, "\\Processor(_Total)\\% Processor Time",
>> + 0, &counter1) == ERROR_SUCCESS) &&
>> + (PdhAddEnglishCounterA (query, "\\System\\Processor Queue Length",
>> + 0, &counter2) == ERROR_SUCCESS)) {
>> +      initialized = true;
>> +    } else {
>> +      debug_printf("loadavg PDH initialization failed\n");
>> +    }
>> +  }
>> +
>> +  return initialized;
>> +}
>
> How slow is that initialization?  Would it {make sense|hurt} to call it
> once in the initalization of Cygwin's shared mem in shared_info::initialize?

I don't think that's particularly heavyweight, and I didn't see anything
to suggest that PDH query handles can be shared between processes, but
I'll look into it.

> As for the declaration problem on x86, what about moving the declarations
> to the start of loadavg.cc, until we get a new w32api-headers package?

It's only one prototype, so that should be ok.

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: [PATCH] Implement getloadavg()

Corinna Vinschen-2
On Mar 20 15:04, Jon Turney wrote:

> On 20/03/2017 10:37, Corinna Vinschen wrote:
> > On Mar 17 17:50, Jon Turney wrote:
> > The load average is global, non-critical data.  So what about storing it
> > in shared_info instead?  This way, only the first call of the first
> > Cygwin process returns all zero.
>
> Ok.
>
> > > +static bool load_init (void)
> > > +{
> > > +  static bool tried = false;
> > > +  static bool initialized = false;
> > > +
> > > +  if (!tried) {
> > > +    tried = true;
> > > +
> > > +    if ((PdhOpenQueryA (NULL, 0, &query) == ERROR_SUCCESS) &&
> > > + (PdhAddEnglishCounterA (query, "\\Processor(_Total)\\% Processor Time",
> > > + 0, &counter1) == ERROR_SUCCESS) &&
> > > + (PdhAddEnglishCounterA (query, "\\System\\Processor Queue Length",
> > > + 0, &counter2) == ERROR_SUCCESS)) {
> > > +      initialized = true;
> > > +    } else {
> > > +      debug_printf("loadavg PDH initialization failed\n");
> > > +    }
> > > +  }
> > > +
> > > +  return initialized;
> > > +}
> >
> > How slow is that initialization?  Would it {make sense|hurt} to call it
> > once in the initalization of Cygwin's shared mem in shared_info::initialize?
>
> I don't think that's particularly heavyweight, and I didn't see anything to
> suggest that PDH query handles can be shared between processes, but I'll
> look into it.
Oh, right, that might pose a problem.

But even then:

The first process creating shared_info could call this and prime the values
with a first call to getloadavg.  Each other process would have to init its
own pointers.  You just have to make sure that access to the loadavg values
from shared_info is atomic.


Corinna

--
Corinna Vinschen                  Please, send mails regarding Cygwin to
Cygwin Maintainer                 cygwin AT cygwin DOT com
Red Hat

signature.asc (836 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: [PATCH] Implement getloadavg()

Achim Gratz
In reply to this post by Jon TURNEY
Jon Turney writes:
> I don't think that's particularly heavyweight, and I didn't see
> anything to suggest that PDH query handles can be shared between
> processes, but I'll look into it.

So maybe something to chuck to cygserver when it's running?


Regards,
Achim.
--
+<[Q+ Matrix-12 WAVE#46+305 Neuron microQkb Andromeda XTk Blofeld]>+

SD adaptation for Waldorf Blofeld V1.15B11:
http://Synth.Stromeko.net/Downloads.html#WaldorfSDada
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

[PATCH] Implement getloadavg() [v2]

Jon TURNEY
In reply to this post by Jon TURNEY
v2:
autoload PerfDataHelper functions
Keep loadavg in shared memory
Guard loadavg access by a mutex
Initialize loadavg to the current load

Signed-off-by: Jon Turney <[hidden email]>
---
 winsup/cygwin/Makefile.in              |   1 +
 winsup/cygwin/autoload.cc              |   6 ++
 winsup/cygwin/common.din               |   1 +
 winsup/cygwin/fhandler_proc.cc         |  10 +-
 winsup/cygwin/include/cygwin/stdlib.h  |   4 +
 winsup/cygwin/include/cygwin/version.h |   5 +-
 winsup/cygwin/loadavg.cc               | 192 +++++++++++++++++++++++++++++++++
 winsup/cygwin/loadavg.h                |  24 +++++
 winsup/cygwin/shared.cc                |   1 +
 winsup/cygwin/shared_info.h            |   2 +
 winsup/doc/posix.xml                   |   1 +
 11 files changed, 242 insertions(+), 5 deletions(-)
 create mode 100644 winsup/cygwin/loadavg.cc
 create mode 100644 winsup/cygwin/loadavg.h

diff --git a/winsup/cygwin/Makefile.in b/winsup/cygwin/Makefile.in
index c8652b0..10e6b1f 100644
--- a/winsup/cygwin/Makefile.in
+++ b/winsup/cygwin/Makefile.in
@@ -323,6 +323,7 @@ DLL_OFILES:= \
  kernel32.o \
  ldap.o \
  libstdcxx_wrapper.o \
+ loadavg.o \
  localtime.o \
  lsearch.o \
  malloc_wrapper.o \
diff --git a/winsup/cygwin/autoload.cc b/winsup/cygwin/autoload.cc
index df06013..785c0b7 100644
--- a/winsup/cygwin/autoload.cc
+++ b/winsup/cygwin/autoload.cc
@@ -730,4 +730,10 @@ LoadDLLfunc (WSASetLastError, 4, ws2_32)
 LoadDLLfunc (WSASocketW, 24, ws2_32)
 // LoadDLLfunc (WSAStartup, 8, ws2_32)
 LoadDLLfunc (WSAWaitForMultipleEvents, 20, ws2_32)
+
+LoadDLLfunc (PdhAddCounterA, 16, pdh)
+LoadDLLfunc (PdhAddEnglishCounterA, 16, pdh)
+LoadDLLfunc (PdhCollectQueryData, 4, pdh)
+LoadDLLfunc (PdhGetFormattedCounterValue, 16, pdh)
+LoadDLLfunc (PdhOpenQueryA, 12, pdh)
 }
diff --git a/winsup/cygwin/common.din b/winsup/cygwin/common.din
index f236813..8e54a7d 100644
--- a/winsup/cygwin/common.din
+++ b/winsup/cygwin/common.din
@@ -624,6 +624,7 @@ gethostname = cygwin_gethostname SIGFE
 getifaddrs SIGFE
 getitimer SIGFE
 getline = __getline SIGFE
+getloadavg SIGFE
 getlogin NOSIGFE
 getlogin_r NOSIGFE
 getmntent SIGFE
diff --git a/winsup/cygwin/fhandler_proc.cc b/winsup/cygwin/fhandler_proc.cc
index 2a8cf14..a7e816f 100644
--- a/winsup/cygwin/fhandler_proc.cc
+++ b/winsup/cygwin/fhandler_proc.cc
@@ -418,7 +418,7 @@ static off_t
 format_proc_loadavg (void *, char *&destbuf)
 {
   extern int get_process_state (DWORD dwProcessId);
-  unsigned running = 0;
+  unsigned int running = 0;
   winpids pids ((DWORD) 0);
 
   for (unsigned i = 0; i < pids.npids; i++)
@@ -429,9 +429,13 @@ format_proc_loadavg (void *, char *&destbuf)
  break;
     }
 
+  double loadavg[3] = { 0.0, 0.0, 0.0 };
+  getloadavg (loadavg, 3);
+
   destbuf = (char *) crealloc_abort (destbuf, 48);
-  return __small_sprintf (destbuf, "%u.%02u %u.%02u %u.%02u %u/%u\n",
-    0, 0, 0, 0, 0, 0, running, pids.npids);
+  return sprintf (destbuf, "%.2f %.2f %.2f %u/%u\n",
+  loadavg[0], loadavg[1], loadavg[2], running,
+  (unsigned int)pids.npids);
 }
 
 static off_t
diff --git a/winsup/cygwin/include/cygwin/stdlib.h b/winsup/cygwin/include/cygwin/stdlib.h
index 744a08d..a8eb4de 100644
--- a/winsup/cygwin/include/cygwin/stdlib.h
+++ b/winsup/cygwin/include/cygwin/stdlib.h
@@ -77,6 +77,10 @@ extern _PTR valloc _PARAMS ((size_t));
 #undef _mstats_r
 #define _mstats_r(r, p) mstats (p)
 
+#if __BSD_VISIBLE
+int getloadavg(double loadavg[], int nelem);
+#endif
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/winsup/cygwin/include/cygwin/version.h b/winsup/cygwin/include/cygwin/version.h
index 6ca3079..b6e176b 100644
--- a/winsup/cygwin/include/cygwin/version.h
+++ b/winsup/cygwin/include/cygwin/version.h
@@ -473,18 +473,19 @@ details. */
   306: Export getentropy, getrandom.
   307: Export timingsafe_bcmp, timingsafe_memcmp.
   308: Export dladdr.
+  309: Export getloadavg.
 
   Note that we forgot to bump the api for ualarm, strtoll, strtoull,
   sigaltstack, sethostname. */
 
 #define CYGWIN_VERSION_API_MAJOR 0
-#define CYGWIN_VERSION_API_MINOR 308
+#define CYGWIN_VERSION_API_MINOR 309
 
 /* There is also a compatibity version number associated with the shared memory
    regions.  It is incremented when incompatible changes are made to the shared
    memory region *or* to any named shared mutexes, semaphores, etc. */
 
-#define CYGWIN_VERSION_SHARED_DATA 5
+#define CYGWIN_VERSION_SHARED_DATA 6
 
 /* An identifier used in the names used to create shared objects.  The full
    names include the CYGWIN_VERSION_SHARED_DATA version as well as this
diff --git a/winsup/cygwin/loadavg.cc b/winsup/cygwin/loadavg.cc
new file mode 100644
index 0000000..cfcba40
--- /dev/null
+++ b/winsup/cygwin/loadavg.cc
@@ -0,0 +1,192 @@
+/* loadavg.cc: load average support.
+
+  This file is part of Cygwin.
+
+  This software is a copyrighted work licensed under the terms of the
+  Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
+  details. */
+
+/*
+  Emulate load average
+
+  There's a fair amount of approximation done here, so don't try to use this to
+  actually measure anything, but it should be good enough for programs to
+  throttle their activity based on load.
+
+  A global load average estimate is maintained in shared memory.  Access to that
+  is guarded by a mutex.  This estimate is only updated at most every 5 seconds.
+
+  We attempt to count running and runnable processes, but unlike linux we don't
+  count processes in uninterruptible sleep (blocked on I/O).
+
+  The number of running processes is estimated as (NumberOfProcessors) * (%
+  Processor Time).  The number of runnable processes is estimated as
+  ProcessorQueueLength.
+
+  Note that PDH will only return data for '% Processor Time' afer the second
+  call to PdhCollectQueryData(), as it's computed over an interval, so the first
+  attempt to estimate load will fail and 0.0 will be returned.
+
+  We also assume that '% Processor Time' averaged over the interval since the
+  last time getloadavg() was called is a good approximation of the instantaneous
+  '% Processor Time'.
+*/
+
+#include "winsup.h"
+#include "shared_info.h"
+#include "loadavg.h"
+
+#include <math.h>
+#include <time.h>
+#include <sys/strace.h>
+
+/* Prototype for PdhAddEnglishCounterA in pdh.h under _WIN32_WINNT >= 0x0600 is
+   missing WINAPI */
+#undef _WIN32_WINNT
+#include <pdh.h>
+extern "C"
+PDH_FUNCTION PdhAddEnglishCounterA(PDH_HQUERY hQuery, LPCSTR szFullCounterPath,
+   DWORD_PTR dwUserData, PDH_HCOUNTER *phCounter);
+
+static PDH_HQUERY query;
+static PDH_HCOUNTER counter1;
+static PDH_HCOUNTER counter2;
+static HANDLE mutex;
+
+static bool load_init (void)
+{
+  static bool tried = false;
+  static bool initialized = false;
+
+  if (!tried) {
+    tried = true;
+
+    if (!((PdhOpenQueryA (NULL, 0, &query) == ERROR_SUCCESS) &&
+  (PdhAddEnglishCounterA (query, "\\Processor(_Total)\\% Processor Time",
+  0, &counter1) == ERROR_SUCCESS) &&
+  (PdhAddEnglishCounterA (query, "\\System\\Processor Queue Length",
+  0, &counter2) == ERROR_SUCCESS))) {
+      debug_printf("loadavg PDH initialization failed\n");
+      return false;
+    }
+
+    mutex = CreateMutex(&sec_all_nih, FALSE, "cyg.loadavg.mutex");
+    if (!mutex) {
+      debug_printf("opening loadavg mutexfailed\n");
+      return false;
+    }
+
+    initialized = true;
+  }
+
+  return initialized;
+}
+
+/* estimate the current load */
+static bool get_load (double *load)
+{
+  *load = 0.0;
+
+  PDH_STATUS ret = PdhCollectQueryData (query);
+  if (ret != ERROR_SUCCESS)
+    return false;
+
+  /* Estimate the number of running processes as (NumberOfProcessors) * (%
+     Processor Time) */
+  PDH_FMT_COUNTERVALUE fmtvalue1;
+  ret = PdhGetFormattedCounterValue (counter1, PDH_FMT_DOUBLE, NULL, &fmtvalue1);
+  if (ret != ERROR_SUCCESS)
+    return false;
+
+  SYSTEM_INFO sysinfo;
+  GetSystemInfo (&sysinfo);
+
+  double running = fmtvalue1.doubleValue * sysinfo.dwNumberOfProcessors / 100;
+
+  /* Estimate the number of runnable processes using ProcessorQueueLength */
+  PDH_FMT_COUNTERVALUE fmtvalue2;
+  ret = PdhGetFormattedCounterValue (counter2, PDH_FMT_LONG, NULL, &fmtvalue2);
+  if (ret != ERROR_SUCCESS)
+    return false;
+
+  LONG rql = fmtvalue2.longValue;
+
+  *load = rql + running;
+  return true;
+}
+
+/*
+  loadavginfo shared-memory object
+*/
+
+void loadavginfo::initialize ()
+{
+  for (int i = 0; i < 3; i++)
+    loadavg[i] = 0.0;
+
+  last_time = 0;
+}
+
+void loadavginfo::calc_load (int index, int delta_time, int decay_time, double n)
+{
+  double df = 1.0 / exp ((double)delta_time/decay_time);
+  loadavg[index] = (loadavg[index] * df) + (n * (1.0 - df));
+}
+
+void loadavginfo::update_loadavg ()
+{
+  double active_tasks;
+
+  if (!get_load (&active_tasks))
+    return;
+
+  /* Don't recalculate the load average if less than 5 seconds has elapsed since
+     the last time it was calculated */
+  time_t curr_time = time (NULL);
+  int delta_time = curr_time - last_time;
+  if (delta_time < 5) {
+    return;
+  }
+
+  if (last_time == 0) {
+    /* Initialize the load average to the current load */
+    for (int i = 0; i < 3; i++) {
+      loadavg[i] = active_tasks;
+    }
+  } else {
+    /* Compute the exponentially weighted moving average over ... */
+    calc_load (0, delta_time, 60,  active_tasks); /* ... 1 min */
+    calc_load (1, delta_time, 300, active_tasks); /* ... 5 min */
+    calc_load (2, delta_time, 900, active_tasks); /* ... 15 min */
+  }
+
+  last_time = curr_time;
+}
+
+int loadavginfo::fetch (double _loadavg[], int nelem)
+{
+  if (!load_init ())
+    return 0;
+
+  WaitForSingleObject(mutex, INFINITE);
+
+  update_loadavg ();
+
+  memcpy (_loadavg, loadavg, nelem * sizeof(double));
+
+  ReleaseMutex(mutex);
+
+  return nelem;
+}
+
+/* getloadavg: BSD */
+extern "C" int
+getloadavg (double loadavg[], int nelem)
+{
+  /* The maximum number of samples is 3 */
+  if (nelem > 3)
+    nelem = 3;
+
+  /* Return the samples and number of samples retrieved */
+  return cygwin_shared->loadavg.fetch(loadavg, nelem);
+}
diff --git a/winsup/cygwin/loadavg.h b/winsup/cygwin/loadavg.h
new file mode 100644
index 0000000..e6fb594
--- /dev/null
+++ b/winsup/cygwin/loadavg.h
@@ -0,0 +1,24 @@
+/* loadavg.h: load average support.
+
+  This file is part of Cygwin.
+
+  This software is a copyrighted work licensed under the terms of the
+  Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
+  details. */
+
+#ifndef LOADAVG_H
+#define LOADAVG_H
+
+class loadavginfo
+{
+  double loadavg[3];
+  time_t last_time;
+
+ public:
+  void initialize ();
+  int fetch (double _loadavg[], int nelem);
+  void update_loadavg ();
+  void calc_load (int index, int delta_time, int decay_time, double n);
+};
+
+#endif /* LOADAVG_H */
diff --git a/winsup/cygwin/shared.cc b/winsup/cygwin/shared.cc
index 4ed4c11..dd16f14 100644
--- a/winsup/cygwin/shared.cc
+++ b/winsup/cygwin/shared.cc
@@ -328,6 +328,7 @@ shared_info::initialize ()
       init_obcaseinsensitive (); /* Initialize obcaseinsensitive */
       tty.init (); /* Initialize tty table  */
       mt.initialize (); /* Initialize shared tape information */
+      loadavg.initialize (); /* Initialize loadavg information */
       /* Defer debug output printing the installation root and installation key
  up to this point.  Debug output except for system_printf requires
  the global shared memory to exist. */
diff --git a/winsup/cygwin/shared_info.h b/winsup/cygwin/shared_info.h
index ce17c15..d03844d 100644
--- a/winsup/cygwin/shared_info.h
+++ b/winsup/cygwin/shared_info.h
@@ -11,6 +11,7 @@ details. */
 #include "mtinfo.h"
 #include "limits.h"
 #include "mount.h"
+#include "loadavg.h"
 
 #define CURR_USER_MAGIC 0xab1fcce8U
 
@@ -48,6 +49,7 @@ class shared_info
   LONG last_used_bindresvport;
   DWORD obcaseinsensitive;
   mtinfo mt;
+  loadavginfo loadavg;
 
   void initialize ();
   void init_obcaseinsensitive ();
diff --git a/winsup/doc/posix.xml b/winsup/doc/posix.xml
index 03d168d..a3b2e9f 100644
--- a/winsup/doc/posix.xml
+++ b/winsup/doc/posix.xml
@@ -1173,6 +1173,7 @@ also IEEE Std 1003.1-2008 (POSIX.1-2008).</para>
     getdtablesize
     getgrouplist
     getifaddrs
+    getloadavg
     getpagesize
     getpeereid
     getprogname
--
2.8.3

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: [PATCH] Implement getloadavg()

Jon TURNEY
In reply to this post by Corinna Vinschen-2
On 20/03/2017 15:40, Corinna Vinschen wrote:

> On Mar 20 15:04, Jon Turney wrote:
>> On 20/03/2017 10:37, Corinna Vinschen wrote:
>>> On Mar 17 17:50, Jon Turney wrote:
>>> The load average is global, non-critical data.  So what about storing it
>>> in shared_info instead?  This way, only the first call of the first
>>> Cygwin process returns all zero.
>>
>> Ok.
>>
>>>> +static bool load_init (void)
>>>> +{
>>>> +  static bool tried = false;
>>>> +  static bool initialized = false;
>>>> +
>>>> +  if (!tried) {
>>>> +    tried = true;
>>>> +
>>>> +    if ((PdhOpenQueryA (NULL, 0, &query) == ERROR_SUCCESS) &&
>>>> + (PdhAddEnglishCounterA (query, "\\Processor(_Total)\\% Processor Time",
>>>> + 0, &counter1) == ERROR_SUCCESS) &&
>>>> + (PdhAddEnglishCounterA (query, "\\System\\Processor Queue Length",
>>>> + 0, &counter2) == ERROR_SUCCESS)) {
>>>> +      initialized = true;
>>>> +    } else {
>>>> +      debug_printf("loadavg PDH initialization failed\n");
>>>> +    }
>>>> +  }
>>>> +
>>>> +  return initialized;
>>>> +}
>>>
>>> How slow is that initialization?  Would it {make sense|hurt} to call it
>>> once in the initalization of Cygwin's shared mem in shared_info::initialize?
>>
>> I don't think that's particularly heavyweight, and I didn't see anything to
>> suggest that PDH query handles can be shared between processes, but I'll
>> look into it.
>
> Oh, right, that might pose a problem.

I can't find anything which documents these handles as shareable.

In practise, they seem to randomly stop working after a while after the
process which created the handle exits, or something like that. :S

> But even then:
>
> The first process creating shared_info could call this and prime the values
> with a first call to getloadavg.  Each other process would have to init its

We cannot determine an initial value of load when shared_info is
created, as the %CPU is not measured instantaneously, but over an
interval.  This means that we can't have a load estimate until the 2nd
time a process calls PdhCollectQueryData()

I've tweaked things slightly in v2 so the loadavg is initialized to the
current load, rather than converging on it from 0.0.

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

[PATCH] Implement getloadavg() [v3]

Jon TURNEY
In reply to this post by Jon TURNEY
v2:
autoload PerfDataHelper functions
Keep loadavg in shared memory
Guard loadavg access by a mutex
Initialize loadavg to the current load

v3:
Shared memory version bump isn't needed if we are only extending it
Remove unused autoload
Mark inititalized flags as NO_COPY for correct behaviour in fork child

Signed-off-by: Jon Turney <[hidden email]>
---
 winsup/cygwin/Makefile.in              |   1 +
 winsup/cygwin/autoload.cc              |   5 +
 winsup/cygwin/common.din               |   1 +
 winsup/cygwin/fhandler_proc.cc         |  10 +-
 winsup/cygwin/include/cygwin/stdlib.h  |   4 +
 winsup/cygwin/include/cygwin/version.h |   3 +-
 winsup/cygwin/loadavg.cc               | 192 +++++++++++++++++++++++++++++++++
 winsup/cygwin/loadavg.h                |  24 +++++
 winsup/cygwin/shared.cc                |   1 +
 winsup/cygwin/shared_info.h            |   2 +
 winsup/doc/posix.xml                   |   1 +
 11 files changed, 240 insertions(+), 4 deletions(-)
 create mode 100644 winsup/cygwin/loadavg.cc
 create mode 100644 winsup/cygwin/loadavg.h

diff --git a/winsup/cygwin/Makefile.in b/winsup/cygwin/Makefile.in
index c8652b0..10e6b1f 100644
--- a/winsup/cygwin/Makefile.in
+++ b/winsup/cygwin/Makefile.in
@@ -323,6 +323,7 @@ DLL_OFILES:= \
  kernel32.o \
  ldap.o \
  libstdcxx_wrapper.o \
+ loadavg.o \
  localtime.o \
  lsearch.o \
  malloc_wrapper.o \
diff --git a/winsup/cygwin/autoload.cc b/winsup/cygwin/autoload.cc
index df06013..02d2e67 100644
--- a/winsup/cygwin/autoload.cc
+++ b/winsup/cygwin/autoload.cc
@@ -730,4 +730,9 @@ LoadDLLfunc (WSASetLastError, 4, ws2_32)
 LoadDLLfunc (WSASocketW, 24, ws2_32)
 // LoadDLLfunc (WSAStartup, 8, ws2_32)
 LoadDLLfunc (WSAWaitForMultipleEvents, 20, ws2_32)
+
+LoadDLLfunc (PdhAddEnglishCounterA, 16, pdh)
+LoadDLLfunc (PdhCollectQueryData, 4, pdh)
+LoadDLLfunc (PdhGetFormattedCounterValue, 16, pdh)
+LoadDLLfunc (PdhOpenQueryA, 12, pdh)
 }
diff --git a/winsup/cygwin/common.din b/winsup/cygwin/common.din
index f236813..8e54a7d 100644
--- a/winsup/cygwin/common.din
+++ b/winsup/cygwin/common.din
@@ -624,6 +624,7 @@ gethostname = cygwin_gethostname SIGFE
 getifaddrs SIGFE
 getitimer SIGFE
 getline = __getline SIGFE
+getloadavg SIGFE
 getlogin NOSIGFE
 getlogin_r NOSIGFE
 getmntent SIGFE
diff --git a/winsup/cygwin/fhandler_proc.cc b/winsup/cygwin/fhandler_proc.cc
index 2a8cf14..a7e816f 100644
--- a/winsup/cygwin/fhandler_proc.cc
+++ b/winsup/cygwin/fhandler_proc.cc
@@ -418,7 +418,7 @@ static off_t
 format_proc_loadavg (void *, char *&destbuf)
 {
   extern int get_process_state (DWORD dwProcessId);
-  unsigned running = 0;
+  unsigned int running = 0;
   winpids pids ((DWORD) 0);
 
   for (unsigned i = 0; i < pids.npids; i++)
@@ -429,9 +429,13 @@ format_proc_loadavg (void *, char *&destbuf)
  break;
     }
 
+  double loadavg[3] = { 0.0, 0.0, 0.0 };
+  getloadavg (loadavg, 3);
+
   destbuf = (char *) crealloc_abort (destbuf, 48);
-  return __small_sprintf (destbuf, "%u.%02u %u.%02u %u.%02u %u/%u\n",
-    0, 0, 0, 0, 0, 0, running, pids.npids);
+  return sprintf (destbuf, "%.2f %.2f %.2f %u/%u\n",
+  loadavg[0], loadavg[1], loadavg[2], running,
+  (unsigned int)pids.npids);
 }
 
 static off_t
diff --git a/winsup/cygwin/include/cygwin/stdlib.h b/winsup/cygwin/include/cygwin/stdlib.h
index 744a08d..a8eb4de 100644
--- a/winsup/cygwin/include/cygwin/stdlib.h
+++ b/winsup/cygwin/include/cygwin/stdlib.h
@@ -77,6 +77,10 @@ extern _PTR valloc _PARAMS ((size_t));
 #undef _mstats_r
 #define _mstats_r(r, p) mstats (p)
 
+#if __BSD_VISIBLE
+int getloadavg(double loadavg[], int nelem);
+#endif
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/winsup/cygwin/include/cygwin/version.h b/winsup/cygwin/include/cygwin/version.h
index 6ca3079..298741a 100644
--- a/winsup/cygwin/include/cygwin/version.h
+++ b/winsup/cygwin/include/cygwin/version.h
@@ -473,12 +473,13 @@ details. */
   306: Export getentropy, getrandom.
   307: Export timingsafe_bcmp, timingsafe_memcmp.
   308: Export dladdr.
+  309: Export getloadavg.
 
   Note that we forgot to bump the api for ualarm, strtoll, strtoull,
   sigaltstack, sethostname. */
 
 #define CYGWIN_VERSION_API_MAJOR 0
-#define CYGWIN_VERSION_API_MINOR 308
+#define CYGWIN_VERSION_API_MINOR 309
 
 /* There is also a compatibity version number associated with the shared memory
    regions.  It is incremented when incompatible changes are made to the shared
diff --git a/winsup/cygwin/loadavg.cc b/winsup/cygwin/loadavg.cc
new file mode 100644
index 0000000..a7e5f61
--- /dev/null
+++ b/winsup/cygwin/loadavg.cc
@@ -0,0 +1,192 @@
+/* loadavg.cc: load average support.
+
+  This file is part of Cygwin.
+
+  This software is a copyrighted work licensed under the terms of the
+  Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
+  details. */
+
+/*
+  Emulate load average
+
+  There's a fair amount of approximation done here, so don't try to use this to
+  actually measure anything, but it should be good enough for programs to
+  throttle their activity based on load.
+
+  A global load average estimate is maintained in shared memory.  Access to that
+  is guarded by a mutex.  This estimate is only updated at most every 5 seconds.
+
+  We attempt to count running and runnable processes, but unlike linux we don't
+  count processes in uninterruptible sleep (blocked on I/O).
+
+  The number of running processes is estimated as (NumberOfProcessors) * (%
+  Processor Time).  The number of runnable processes is estimated as
+  ProcessorQueueLength.
+
+  Note that PDH will only return data for '% Processor Time' afer the second
+  call to PdhCollectQueryData(), as it's computed over an interval, so the first
+  attempt to estimate load will fail and 0.0 will be returned.
+
+  We also assume that '% Processor Time' averaged over the interval since the
+  last time getloadavg() was called is a good approximation of the instantaneous
+  '% Processor Time'.
+*/
+
+#include "winsup.h"
+#include "shared_info.h"
+#include "loadavg.h"
+
+#include <math.h>
+#include <time.h>
+#include <sys/strace.h>
+
+/* Prototype for PdhAddEnglishCounterA in pdh.h under _WIN32_WINNT >= 0x0600 is
+   missing WINAPI */
+#undef _WIN32_WINNT
+#include <pdh.h>
+extern "C"
+PDH_FUNCTION PdhAddEnglishCounterA(PDH_HQUERY hQuery, LPCSTR szFullCounterPath,
+   DWORD_PTR dwUserData, PDH_HCOUNTER *phCounter);
+
+static PDH_HQUERY query;
+static PDH_HCOUNTER counter1;
+static PDH_HCOUNTER counter2;
+static HANDLE mutex;
+
+static bool load_init (void)
+{
+  static NO_COPY bool tried = false;
+  static NO_COPY bool initialized = false;
+
+  if (!tried) {
+    tried = true;
+
+    if (!((PdhOpenQueryA (NULL, 0, &query) == ERROR_SUCCESS) &&
+  (PdhAddEnglishCounterA (query, "\\Processor(_Total)\\% Processor Time",
+  0, &counter1) == ERROR_SUCCESS) &&
+  (PdhAddEnglishCounterA (query, "\\System\\Processor Queue Length",
+  0, &counter2) == ERROR_SUCCESS))) {
+      debug_printf("loadavg PDH initialization failed\n");
+      return false;
+    }
+
+    mutex = CreateMutex(&sec_all_nih, FALSE, "cyg.loadavg.mutex");
+    if (!mutex) {
+      debug_printf("opening loadavg mutexfailed\n");
+      return false;
+    }
+
+    initialized = true;
+  }
+
+  return initialized;
+}
+
+/* estimate the current load */
+static bool get_load (double *load)
+{
+  *load = 0.0;
+
+  PDH_STATUS ret = PdhCollectQueryData (query);
+  if (ret != ERROR_SUCCESS)
+    return false;
+
+  /* Estimate the number of running processes as (NumberOfProcessors) * (%
+     Processor Time) */
+  PDH_FMT_COUNTERVALUE fmtvalue1;
+  ret = PdhGetFormattedCounterValue (counter1, PDH_FMT_DOUBLE, NULL, &fmtvalue1);
+  if (ret != ERROR_SUCCESS)
+    return false;
+
+  SYSTEM_INFO sysinfo;
+  GetSystemInfo (&sysinfo);
+
+  double running = fmtvalue1.doubleValue * sysinfo.dwNumberOfProcessors / 100;
+
+  /* Estimate the number of runnable processes using ProcessorQueueLength */
+  PDH_FMT_COUNTERVALUE fmtvalue2;
+  ret = PdhGetFormattedCounterValue (counter2, PDH_FMT_LONG, NULL, &fmtvalue2);
+  if (ret != ERROR_SUCCESS)
+    return false;
+
+  LONG rql = fmtvalue2.longValue;
+
+  *load = rql + running;
+  return true;
+}
+
+/*
+  loadavginfo shared-memory object
+*/
+
+void loadavginfo::initialize ()
+{
+  for (int i = 0; i < 3; i++)
+    loadavg[i] = 0.0;
+
+  last_time = 0;
+}
+
+void loadavginfo::calc_load (int index, int delta_time, int decay_time, double n)
+{
+  double df = 1.0 / exp ((double)delta_time/decay_time);
+  loadavg[index] = (loadavg[index] * df) + (n * (1.0 - df));
+}
+
+void loadavginfo::update_loadavg ()
+{
+  double active_tasks;
+
+  if (!get_load (&active_tasks))
+    return;
+
+  /* Don't recalculate the load average if less than 5 seconds has elapsed since
+     the last time it was calculated */
+  time_t curr_time = time (NULL);
+  int delta_time = curr_time - last_time;
+  if (delta_time < 5) {
+    return;
+  }
+
+  if (last_time == 0) {
+    /* Initialize the load average to the current load */
+    for (int i = 0; i < 3; i++) {
+      loadavg[i] = active_tasks;
+    }
+  } else {
+    /* Compute the exponentially weighted moving average over ... */
+    calc_load (0, delta_time, 60,  active_tasks); /* ... 1 min */
+    calc_load (1, delta_time, 300, active_tasks); /* ... 5 min */
+    calc_load (2, delta_time, 900, active_tasks); /* ... 15 min */
+  }
+
+  last_time = curr_time;
+}
+
+int loadavginfo::fetch (double _loadavg[], int nelem)
+{
+  if (!load_init ())
+    return 0;
+
+  WaitForSingleObject(mutex, INFINITE);
+
+  update_loadavg ();
+
+  memcpy (_loadavg, loadavg, nelem * sizeof(double));
+
+  ReleaseMutex(mutex);
+
+  return nelem;
+}
+
+/* getloadavg: BSD */
+extern "C" int
+getloadavg (double loadavg[], int nelem)
+{
+  /* The maximum number of samples is 3 */
+  if (nelem > 3)
+    nelem = 3;
+
+  /* Return the samples and number of samples retrieved */
+  return cygwin_shared->loadavg.fetch(loadavg, nelem);
+}
diff --git a/winsup/cygwin/loadavg.h b/winsup/cygwin/loadavg.h
new file mode 100644
index 0000000..e6fb594
--- /dev/null
+++ b/winsup/cygwin/loadavg.h
@@ -0,0 +1,24 @@
+/* loadavg.h: load average support.
+
+  This file is part of Cygwin.
+
+  This software is a copyrighted work licensed under the terms of the
+  Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
+  details. */
+
+#ifndef LOADAVG_H
+#define LOADAVG_H
+
+class loadavginfo
+{
+  double loadavg[3];
+  time_t last_time;
+
+ public:
+  void initialize ();
+  int fetch (double _loadavg[], int nelem);
+  void update_loadavg ();
+  void calc_load (int index, int delta_time, int decay_time, double n);
+};
+
+#endif /* LOADAVG_H */
diff --git a/winsup/cygwin/shared.cc b/winsup/cygwin/shared.cc
index 4ed4c11..dd16f14 100644
--- a/winsup/cygwin/shared.cc
+++ b/winsup/cygwin/shared.cc
@@ -328,6 +328,7 @@ shared_info::initialize ()
       init_obcaseinsensitive (); /* Initialize obcaseinsensitive */
       tty.init (); /* Initialize tty table  */
       mt.initialize (); /* Initialize shared tape information */
+      loadavg.initialize (); /* Initialize loadavg information */
       /* Defer debug output printing the installation root and installation key
  up to this point.  Debug output except for system_printf requires
  the global shared memory to exist. */
diff --git a/winsup/cygwin/shared_info.h b/winsup/cygwin/shared_info.h
index ce17c15..d03844d 100644
--- a/winsup/cygwin/shared_info.h
+++ b/winsup/cygwin/shared_info.h
@@ -11,6 +11,7 @@ details. */
 #include "mtinfo.h"
 #include "limits.h"
 #include "mount.h"
+#include "loadavg.h"
 
 #define CURR_USER_MAGIC 0xab1fcce8U
 
@@ -48,6 +49,7 @@ class shared_info
   LONG last_used_bindresvport;
   DWORD obcaseinsensitive;
   mtinfo mt;
+  loadavginfo loadavg;
 
   void initialize ();
   void init_obcaseinsensitive ();
diff --git a/winsup/doc/posix.xml b/winsup/doc/posix.xml
index 03d168d..a3b2e9f 100644
--- a/winsup/doc/posix.xml
+++ b/winsup/doc/posix.xml
@@ -1173,6 +1173,7 @@ also IEEE Std 1003.1-2008 (POSIX.1-2008).</para>
     getdtablesize
     getgrouplist
     getifaddrs
+    getloadavg
     getpagesize
     getpeereid
     getprogname
--
2.8.3

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: [PATCH] Implement getloadavg() [v3]

Corinna Vinschen-2
On Mar 27 16:10, Jon Turney wrote:

> v2:
> autoload PerfDataHelper functions
> Keep loadavg in shared memory
> Guard loadavg access by a mutex
> Initialize loadavg to the current load
>
> v3:
> Shared memory version bump isn't needed if we are only extending it
> Remove unused autoload
> Mark inititalized flags as NO_COPY for correct behaviour in fork child
>
> Signed-off-by: Jon Turney <[hidden email]>
> ---
Looks good, pleae apply.


Thanks,
Corinna

--
Corinna Vinschen                  Please, send mails regarding Cygwin to
Cygwin Maintainer                 cygwin AT cygwin DOT com
Red Hat

signature.asc (836 bytes) Download Attachment
Loading...