Emulation of `getloadavg' on MS-Windows:

Include float.h
(g_b_init_get_native_system_info, g_b_init_get_system_times)
(GetNativeSystemInfo_Proc, GetSystemTimes_Proc): Declare.
(get_native_system_info, get_system_times): New functions.
(buf_next, buf_prev, sample_system_load, getavg): New subroutines.
(getloadavg): Rewrite using GetSystemTimes and GetNativeSystemInfo.
(globals_of_w32): Initialize g_b_init_get_native_system_info,
g_b_init_get_system_times, and num_of_processors.
This commit is contained in:
Eli Zaretskii 2009-07-04 10:43:10 +00:00
parent 7df24305ed
commit ad9e2d54dd
2 changed files with 213 additions and 5 deletions

View file

@ -1,3 +1,15 @@
2009-07-04 Eli Zaretskii <eliz@gnu.org>
Emulation of `getloadavg' on MS-Windows.
* w32.c: Include float.h
(g_b_init_get_native_system_info, g_b_init_get_system_times)
(GetNativeSystemInfo_Proc, GetSystemTimes_Proc): Declare.
(get_native_system_info, get_system_times): New functions.
(buf_next, buf_prev, sample_system_load, getavg): New subroutines.
(getloadavg): Rewrite using GetSystemTimes and GetNativeSystemInfo.
(globals_of_w32): Initialize g_b_init_get_native_system_info,
g_b_init_get_system_times, and num_of_processors.
2009-07-03 Jason Rumney <jasonr@gnu.org>
* w32term.c (w32_initialize): Use standard types.

206
src/w32.c
View file

@ -23,6 +23,7 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
#include <stddef.h> /* for offsetof */
#include <stdlib.h>
#include <stdio.h>
#include <float.h> /* for DBL_EPSILON */
#include <io.h>
#include <errno.h>
#include <fcntl.h>
@ -191,6 +192,8 @@ static BOOL g_b_init_global_memory_status_ex;
static BOOL g_b_init_get_length_sid;
static BOOL g_b_init_equal_sid;
static BOOL g_b_init_copy_sid;
static BOOL g_b_init_get_native_system_info;
static BOOL g_b_init_get_system_times;
/*
BEGIN: Wrapper functions around OpenProcessToken
@ -293,6 +296,12 @@ typedef BOOL (WINAPI * EqualSid_Proc) (
PSID pSid2);
typedef DWORD (WINAPI * GetLengthSid_Proc) (
PSID pSid);
typedef void (WINAPI * GetNativeSystemInfo_Proc) (
LPSYSTEM_INFO lpSystemInfo);
typedef BOOL (WINAPI * GetSystemTimes_Proc) (
LPFILETIME lpIdleTime,
LPFILETIME lpKernelTime,
LPFILETIME lpUserTime);
@ -723,6 +732,47 @@ BOOL WINAPI copy_sid (
supported in Windows NT / 2k / XP
*/
void WINAPI get_native_system_info (
LPSYSTEM_INFO lpSystemInfo)
{
static GetNativeSystemInfo_Proc s_pfn_Get_Native_System_Info = NULL;
if (is_windows_9x () != TRUE)
{
if (g_b_init_get_native_system_info == 0)
{
g_b_init_get_native_system_info = 1;
s_pfn_Get_Native_System_Info =
(GetNativeSystemInfo_Proc)GetProcAddress (GetModuleHandle ("kernel32.dll"),
"GetNativeSystemInfo");
}
if (s_pfn_Get_Native_System_Info != NULL)
s_pfn_Get_Native_System_Info (lpSystemInfo);
}
else
lpSystemInfo->dwNumberOfProcessors = -1;
}
BOOL WINAPI get_system_times(
LPFILETIME lpIdleTime,
LPFILETIME lpKernelTime,
LPFILETIME lpUserTime)
{
static GetSystemTimes_Proc s_pfn_Get_System_times = NULL;
if (is_windows_9x () == TRUE)
{
return FALSE;
}
if (g_b_init_get_system_times == 0)
{
g_b_init_get_system_times = 1;
s_pfn_Get_System_times =
(GetSystemTimes_Proc)GetProcAddress (GetModuleHandle ("kernel32.dll"),
"GetSystemTimes");
}
if (s_pfn_Get_System_times == NULL)
return FALSE;
return (s_pfn_Get_System_times (lpIdleTime, lpKernelTime, lpUserTime));
}
/* Equivalent of strerror for W32 error codes. */
char *
@ -795,17 +845,160 @@ gethostname (char *buffer, int size)
#endif /* HAVE_SOCKETS */
/* Emulate getloadavg. */
struct load_sample {
time_t sample_time;
ULONGLONG idle;
ULONGLONG kernel;
ULONGLONG user;
};
/* Number of processors on this machine. */
static unsigned num_of_processors;
/* We maintain 1-sec samples for the last 16 minutes in a circular buffer. */
static struct load_sample samples[16*60];
static int first_idx = -1, last_idx = -1;
static int max_idx = sizeof (samples) / sizeof (samples[0]);
static int
buf_next (int from)
{
int next_idx = from + 1;
if (next_idx >= max_idx)
next_idx = 0;
return next_idx;
}
static int
buf_prev (int from)
{
int prev_idx = from - 1;
if (prev_idx < 0)
prev_idx = max_idx - 1;
return prev_idx;
}
static void
sample_system_load (ULONGLONG *idle, ULONGLONG *kernel, ULONGLONG *user)
{
SYSTEM_INFO sysinfo;
FILETIME ft_idle, ft_user, ft_kernel;
/* Initialize the number of processors on this machine. */
if (num_of_processors <= 0)
{
get_native_system_info (&sysinfo);
num_of_processors = sysinfo.dwNumberOfProcessors;
if (num_of_processors <= 0)
{
GetSystemInfo (&sysinfo);
num_of_processors = sysinfo.dwNumberOfProcessors;
}
if (num_of_processors <= 0)
num_of_processors = 1;
}
/* TODO: Take into account threads that are ready to run, by
sampling the "\System\Processor Queue Length" performance
counter. The code below accounts only for threads that are
actually running. */
if (get_system_times (&ft_idle, &ft_kernel, &ft_user))
{
ULARGE_INTEGER uidle, ukernel, uuser;
memcpy (&uidle, &ft_idle, sizeof (ft_idle));
memcpy (&ukernel, &ft_kernel, sizeof (ft_kernel));
memcpy (&uuser, &ft_user, sizeof (ft_user));
*idle = uidle.QuadPart;
*kernel = ukernel.QuadPart;
*user = uuser.QuadPart;
}
else
{
*idle = 0;
*kernel = 0;
*user = 0;
}
}
/* Produce the load average for a given time interval, using the
samples in the samples[] array. WHICH can be 0, 1, or 2, meaning
1-minute, 5-minute, or 15-minute average, respectively. */
static double
getavg (int which)
{
double retval = -1.0;
double tdiff;
int idx;
double span = (which == 0 ? 1.0 : (which == 1 ? 5.0 : 15.0)) * 60;
time_t now = samples[last_idx].sample_time;
if (first_idx != last_idx)
{
for (idx = buf_prev (last_idx); ; idx = buf_prev (idx))
{
tdiff = difftime (now, samples[idx].sample_time);
if (tdiff >= span - 2*DBL_EPSILON*now)
{
long double sys =
samples[last_idx].kernel + samples[last_idx].user
- (samples[idx].kernel + samples[idx].user);
long double idl = samples[last_idx].idle - samples[idx].idle;
retval = (1.0 - idl / sys) * num_of_processors;
break;
}
if (idx == first_idx)
break;
}
}
return retval;
}
int
getloadavg (double loadavg[], int nelem)
{
int i;
int elem;
ULONGLONG idle, kernel, user;
time_t now = time (NULL);
/* A faithful emulation is going to have to be saved for a rainy day. */
for (i = 0; i < nelem; i++)
/* Store another sample. We ignore samples that are less than 1 sec
apart. */
if (difftime (now, samples[last_idx].sample_time) >= 1.0 - 2*DBL_EPSILON*now)
{
loadavg[i] = 0.0;
sample_system_load (&idle, &kernel, &user);
last_idx = buf_next (last_idx);
samples[last_idx].sample_time = now;
samples[last_idx].idle = idle;
samples[last_idx].kernel = kernel;
samples[last_idx].user = user;
/* If the buffer has more that 15 min worth of samples, discard
the old ones. */
if (first_idx == -1)
first_idx = last_idx;
while (first_idx != last_idx
&& (difftime (now, samples[first_idx].sample_time)
>= 15.0*60 + 2*DBL_EPSILON*now))
first_idx = buf_next (first_idx);
}
return i;
for (elem = 0; elem < nelem; elem++)
{
double avg = getavg (elem);
if (avg < 0)
break;
loadavg[elem] = avg;
}
return elem;
}
/* Emulate getpwuid, getpwnam and others. */
@ -5693,6 +5886,9 @@ globals_of_w32 ()
g_b_init_equal_sid = 0;
g_b_init_copy_sid = 0;
g_b_init_get_length_sid = 0;
g_b_init_get_native_system_info = 0;
g_b_init_get_system_times = 0;
num_of_processors = 0;
/* The following sets a handler for shutdown notifications for
console apps. This actually applies to Emacs in both console and
GUI modes, since we had to fool windows into thinking emacs is a