changeset 103718:9a76dfa56921

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.
author Eli Zaretskii <eliz@gnu.org>
date Sat, 04 Jul 2009 10:42:45 +0000
parents 7bebd9bb90fc
children ef4a24ec292c
files src/w32.c
diffstat 1 files changed, 204 insertions(+), 8 deletions(-) [+]
line wrap: on
line diff
--- a/src/w32.c	Sat Jul 04 08:12:10 2009 +0000
+++ b/src/w32.c	Sat Jul 04 10:42:45 2009 +0000
@@ -23,6 +23,7 @@
 #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_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 @@
     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 @@
   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 @@
 #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;
-
-  /* A faithful emulation is going to have to be saved for a rainy day.  */
-  for (i = 0; i < nelem; i++)
-    {
-      loadavg[i] = 0.0;
-    }
-  return i;
+  int elem;
+  ULONGLONG idle, kernel, user;
+  time_t now = time (NULL);
+
+  /* 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)
+    {
+      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);
+    }
+
+  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 @@
   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