Cygwin: (mostly) drop NT4 and Samba < 3.0 support
[newlib-cygwin.git] / winsup / cygwin / loadavg.cc
blob127591a2e1f5ed2f64e0b53389624264aba72c15
1 /* loadavg.cc: load average support.
3 This file is part of Cygwin.
5 This software is a copyrighted work licensed under the terms of the
6 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
7 details. */
9 /*
10 Emulate load average
12 There's a fair amount of approximation done here, so don't try to use this to
13 actually measure anything, but it should be good enough for programs to
14 throttle their activity based on load.
16 A global load average estimate is maintained in shared memory. Access to that
17 is guarded by a mutex. This estimate is only updated at most every 5 seconds.
19 We attempt to count running and runnable processes, but unlike linux we don't
20 count processes in uninterruptible sleep (blocked on I/O).
22 The number of running processes is estimated as (NumberOfProcessors) * (%
23 Processor Time). The number of runnable processes is estimated as
24 ProcessorQueueLength.
26 Note that PDH will only return data for '% Processor Time' afer the second
27 call to PdhCollectQueryData(), as it's computed over an interval, so the first
28 attempt to estimate load will fail and 0.0 will be returned.
30 We also assume that '% Processor Time' averaged over the interval since the
31 last time getloadavg() was called is a good approximation of the instantaneous
32 '% Processor Time'.
35 #include "winsup.h"
36 #include "shared_info.h"
37 #include "loadavg.h"
39 #include <math.h>
40 #include <time.h>
41 #include <sys/strace.h>
42 #include <pdh.h>
44 static PDH_HQUERY query;
45 static PDH_HCOUNTER counter1;
46 static PDH_HCOUNTER counter2;
47 static HANDLE mutex;
49 static bool load_init (void)
51 static NO_COPY bool tried = false;
52 static NO_COPY bool initialized = false;
54 if (!tried) {
55 tried = true;
57 PDH_STATUS status;
59 status = PdhOpenQueryW (NULL, 0, &query);
60 if (status != STATUS_SUCCESS)
62 debug_printf ("PdhOpenQueryW, status %y", status);
63 return false;
65 status = PdhAddEnglishCounterW (query,
66 L"\\Processor(_Total)\\% Processor Time",
67 0, &counter1);
68 if (status != STATUS_SUCCESS)
70 debug_printf ("PdhAddEnglishCounterW(time), status %y", status);
71 return false;
73 status = PdhAddEnglishCounterW (query,
74 L"\\System\\Processor Queue Length",
75 0, &counter2);
77 if (status != STATUS_SUCCESS)
79 debug_printf ("PdhAddEnglishCounterW(queue length), status %y", status);
80 return false;
83 mutex = CreateMutex(&sec_all_nih, FALSE, "cyg.loadavg.mutex");
84 if (!mutex) {
85 debug_printf("opening loadavg mutexfailed\n");
86 return false;
89 initialized = true;
92 return initialized;
95 /* estimate the current load */
96 static bool get_load (double *load)
98 *load = 0.0;
100 PDH_STATUS ret = PdhCollectQueryData (query);
101 if (ret != ERROR_SUCCESS)
102 return false;
104 /* Estimate the number of running processes as (NumberOfProcessors) * (%
105 Processor Time) */
106 PDH_FMT_COUNTERVALUE fmtvalue1;
107 ret = PdhGetFormattedCounterValue (counter1, PDH_FMT_DOUBLE, NULL, &fmtvalue1);
108 if (ret != ERROR_SUCCESS)
109 return false;
111 double running = fmtvalue1.doubleValue * wincap.cpu_count () / 100;
113 /* Estimate the number of runnable processes using ProcessorQueueLength */
114 PDH_FMT_COUNTERVALUE fmtvalue2;
115 ret = PdhGetFormattedCounterValue (counter2, PDH_FMT_LONG, NULL, &fmtvalue2);
116 if (ret != ERROR_SUCCESS)
117 return false;
119 LONG rql = fmtvalue2.longValue;
121 *load = rql + running;
122 return true;
126 loadavginfo shared-memory object
129 void loadavginfo::initialize ()
131 for (int i = 0; i < 3; i++)
132 loadavg[i] = 0.0;
134 last_time = 0;
137 void loadavginfo::calc_load (int index, int delta_time, int decay_time, double n)
139 double df = 1.0 / exp ((double)delta_time/decay_time);
140 loadavg[index] = (loadavg[index] * df) + (n * (1.0 - df));
143 void loadavginfo::update_loadavg ()
145 double active_tasks;
147 if (!get_load (&active_tasks))
148 return;
150 /* Don't recalculate the load average if less than 5 seconds has elapsed since
151 the last time it was calculated */
152 time_t curr_time = time (NULL);
153 int delta_time = curr_time - last_time;
154 if (delta_time < 5) {
155 return;
158 if (last_time == 0) {
159 /* Initialize the load average to the current load */
160 for (int i = 0; i < 3; i++) {
161 loadavg[i] = active_tasks;
163 } else {
164 /* Compute the exponentially weighted moving average over ... */
165 calc_load (0, delta_time, 60, active_tasks); /* ... 1 min */
166 calc_load (1, delta_time, 300, active_tasks); /* ... 5 min */
167 calc_load (2, delta_time, 900, active_tasks); /* ... 15 min */
170 last_time = curr_time;
173 int loadavginfo::fetch (double _loadavg[], int nelem)
175 if (!load_init ())
176 return 0;
178 WaitForSingleObject(mutex, INFINITE);
180 update_loadavg ();
182 memcpy (_loadavg, loadavg, nelem * sizeof(double));
184 ReleaseMutex(mutex);
186 return nelem;
189 /* getloadavg: BSD */
190 extern "C" int
191 getloadavg (double loadavg[], int nelem)
193 /* The maximum number of samples is 3 */
194 if (nelem > 3)
195 nelem = 3;
197 /* Return the samples and number of samples retrieved */
198 return cygwin_shared->loadavg.fetch(loadavg, nelem);